Struct MoReport
pub struct MoReport {
pub label: String,
pub directions: Vec<Direction>,
pub values: Vec<Vec<f64>>,
pub constraints: Vec<Vec<f64>>,
pub front: Vec<Vec<f64>>,
pub front_constraints: Vec<Vec<f64>>,
}Expand description
One searcher’s run over one multi-objective problem, and the front metrics computed from it.
The front is the one the framework itself reports
(Study::pareto_front) — copied verbatim, not recomputed and not
filtered here, so an assertion about it is an assertion about what a user
would actually get. In particular
front_is_feasible is a statement about
atune’s constrained ranking rather than about this module’s bookkeeping.
Fields§
§label: StringThe searcher’s label.
directions: Vec<Direction>The study directions the objectives were ranked under.
values: Vec<Vec<f64>>Every completed trial’s objective vector, in trial-number order.
constraints: Vec<Vec<f64>>Each trial’s constraint values, parallel to values;
an empty entry means the trial declared none.
front: Vec<Vec<f64>>The Pareto front the study reports, in trial-number order.
front_constraints: Vec<Vec<f64>>Each front member’s constraint values, parallel to
front.
Implementations§
§impl MoReport
impl MoReport
pub fn evaluations(&self) -> usize
pub fn evaluations(&self) -> usize
How many trials the run completed.
pub fn front_size(&self) -> usize
pub fn front_size(&self) -> usize
How many points the reported front holds.
pub fn front_is_feasible(&self) -> bool
pub fn front_is_feasible(&self) -> bool
true if every member of the reported front is feasible.
The feasibility-first claim, stated on the framework’s own answer: because
constrained_dominates ranks
any feasible solution above any infeasible one, a study that evaluated at
least one feasible point must report a wholly feasible front. Trivially
true for an unconstrained problem, where nothing declared feasibility
either way.
pub fn hypervolume(&self, reference: &[f64]) -> Result<f64>
pub fn hypervolume(&self, reference: &[f64]) -> Result<f64>
The hypervolume the front covers against reference — the coverage
axis.
Computed over the front’s feasible members, exactly as
StudyView::hypervolume
does: an infeasible solution has not earned the objective space it appears
to cover. (It reaches the front at all only when nothing feasible exists,
which none of the fixtures here produce — see
front_is_feasible.)
§Errors
Everything hypervolume reports.
pub fn span(&self, objective: usize) -> f64
pub fn span(&self, objective: usize) -> f64
The front’s span in one objective: max − min over its members.
0 for an empty or single-point front — which is the honest answer, and
the reason a clone farm fails spread.
pub fn spread(&self, problem: &MoProblem) -> f64
pub fn spread(&self, problem: &MoProblem) -> f64
The diversity axis: the smallest fraction of the true front’s span that the reported front covers, over all objectives.
1.0 means the front reaches from one extreme of the true front to the
other in every objective; 0.0 means it is a single point. The minimum
over objectives — not the mean — because a front that spreads along one
axis and collapses along another has not covered the trade-off, and a mean
would hide exactly that.
This is the measurement crowding distance exists to move: a sampler that
finds one good point and clones it scores 0 here while still scoring
respectably on hypervolume, which is why the oracle asserts both.
pub fn spacing(&self) -> f64
pub fn spacing(&self) -> f64
Schott’s spacing metric: the standard deviation of each front member’s distance to its nearest neighbour on the front.
Lower is more evenly distributed. Reported rather than gated, and
deliberately so: spacing measures evenness, not extent, so a front
collapsed onto a single cluster scores a perfect 0. It is the classic
example of a diversity metric that a degenerate front games, which is why
spread is the one the assertions use.
0 for a front of fewer than two points.
pub fn convergence(&self, problem: &MoProblem) -> f64
pub fn convergence(&self, problem: &MoProblem) -> f64
The convergence axis: the mean distance from the reported front to the true one (the generational distance).
f64::INFINITY for an empty front — nothing to measure, never
accidentally good.
pub fn inverted_generational_distance(&self, problem: &MoProblem) -> f64
pub fn inverted_generational_distance(&self, problem: &MoProblem) -> f64
The inverted generational distance: the mean, over a dense sample of the true front, of the distance to the nearest reported front member.
The metric that measures convergence and coverage in one number, and
the one this oracle leads with. convergence
(the plain generational distance) asks “is what I found near the front”,
which a run can score well on while covering a tenth of it — and which an
archive front is unfairly punished by, since a study’s reported front
keeps every historical extreme, including the first trial’s stray minimum
in one objective. IGD asks the complementary question, “is every part of
the front near something I found”, so a gap costs and a stray does not.
f64::INFINITY for an empty front.
pub fn tail_convergence(&self, problem: &MoProblem, divisor: usize) -> f64
pub fn tail_convergence(&self, problem: &MoProblem, divisor: usize) -> f64
The mean distance to the true front of the last 1/divisor of the
evaluated points — where the search ended up, as opposed to what it
archived.
The complement to the two front metrics, and the sharpest statement of what a population-based sampler does: NSGA-II’s last generation is its answer, while a study’s reported front is an archive that keeps every historical extreme, and uniform random search’s “front” is the lucky tail of a stationary cloud. Measuring the tail of the trial sequence compares the searchers where they actually differ.
tail_convergence(problem, 4) is the last quarter. f64::INFINITY for
an empty run.
pub fn feasible_rate(&self) -> f64
pub fn feasible_rate(&self) -> f64
The fraction of completed trials that were feasible.
1.0 for an unconstrained problem (a trial that declared no constraints
is not infeasible — it said nothing about feasibility).
pub fn tail_feasible_rate(&self, divisor: usize) -> f64
pub fn tail_feasible_rate(&self, divisor: usize) -> f64
The feasible fraction of the last 1/divisor of the run.
Where the population ended up, as opposed to where it started: the first
generation is a uniform random draw whatever the sampler, so a whole-run
feasibility rate is diluted by it. tail_feasible_rate(4) is the last
quarter.
pub fn infeasible_dominators(&self) -> usize
pub fn infeasible_dominators(&self) -> usize
How many evaluated points were infeasible yet dominate a member of the reported front on the raw objectives alone.
The feasibility-first claim, stated so it can fail: if this is 0 the
constrained problem never actually tempted the sampler, and asserting that
the front is feasible would prove nothing. A positive count means the run
really did evaluate better-looking illegal points and the front declined
them.
Trait Implementations§
impl StructuralPartialEq for MoReport
Auto Trait Implementations§
impl Freeze for MoReport
impl RefUnwindSafe for MoReport
impl Send for MoReport
impl Sync for MoReport
impl Unpin for MoReport
impl UnsafeUnpin for MoReport
impl UnwindSafe for MoReport
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T, U> Imply<T> for U
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more