Skip to main content

MoProblem

Struct MoProblem 

pub struct MoProblem { /* private fields */ }
Expand description

A multi-objective benchmark problem: a boxed continuous domain, a vector objective, optional constraints, and a known Pareto front.

Every objective is minimized (the convention the whole crate drives), and every problem’s true front is analytic — which is what makes the convergence axis measurable at all rather than a comparison of two searchers against each other.

Build one with a named constructor and hand it to run.

Implementations§

§

impl MoProblem

pub fn name(&self) -> &str

The problem’s label.

pub const fn schema(&self) -> &SpaceSchema

The declared search space.

pub const fn n_objectives(&self) -> usize

How many objectives the problem reports.

pub fn directions(&self) -> Vec<Direction>

The study directions: minimize, one per objective.

pub const fn is_constrained(&self) -> bool

true if the problem declares inequality constraints.

pub fn reference(&self) -> &[f64]

The fixed hypervolume reference point this oracle measures against.

Fixed, and a property of the problem rather than of a run, because a reference recomputed from each run’s own nadir makes two runs’ hypervolumes incomparable — exactly the trap nadir_point documents. Each one is the nadir of the true front plus a 10 % margin, which is the convention the ZDT/DTLZ literature uses: a solution only earns hypervolume by getting inside the box the true front spans, so a scattered cloud of far-from-front points scores nothing rather than being credited for its spread.

pub fn wide_reference(&self) -> Vec<f64>

A generous hypervolume reference: far enough out that even a completely unconverged cloud of points encloses volume.

Kept because a reference point is a choice, and a claim that only holds under one choice is not a claim. Under reference a searcher that never reaches the front scores exactly zero, which can make a margin look total when it merely means “the loser scored nothing”; under this one every evaluated point encloses volume, so the comparison is a ratio of two positive numbers. Measured at the oracle’s budget the ranking is the same either way — NSGA-II beats random search on 0.969 (ZDT1) and 1.000 (ZDT2) of 64 seeds under this generous reference, against 0.953 / 0.969 under the tight one — so the headline does not depend on the choice. It is the less converged regimes where a generous reference flatters a scattered sample (docs/design/STATUS.md, the NSGA-II notes), which is why the choice is documented rather than incidental.

pub fn front_span(&self) -> Vec<f64>

The span of the true front in each objective — the denominator MoReport::spread normalizes by.

“The front is spread” only means something against the range there is to spread over, and that range is a property of the problem, not of the run.

pub fn true_front(&self) -> &[Vec<f64>]

A discretization of the true Pareto front, in objective space.

pub fn reference_set(&self) -> &[Vec<f64>]

An evenly-distributed subsample of the true front, about 128 points — the target set MoReport::inverted_generational_distance measures coverage against.

Built at construction rather than strided out of true_front, because striding a lattice (DTLZ2’s front is a surface, sampled row-major) would silently collapse the set onto a couple of meridians and make IGD blind to a whole axis.

pub fn eval(&self, point: &Assignment) -> Vec<f64>

The objective vector at point.

A parameter that is missing or not a float yields NaN for that coordinate rather than panicking — a NaN objective is never admitted to an NSGA-II population and can never win a Pareto comparison, so a malformed assignment degrades instead of aborting a run.

pub fn constraints(&self, point: &Assignment) -> Vec<f64>

The constraint values at point — ≤ 0 is satisfied, atune’s (and Optuna’s) sign convention.

Empty for an unconstrained problem, which is a declaration of nothing rather than a declaration of feasibility (Point::new).

pub fn distance_to_front(&self, values: &[f64]) -> f64

The Euclidean distance from an objective vector to the true Pareto front.

The convergence axis, and the reason every problem here has an analytic front. For dtlz2 the answer is closed-form (|‖f‖ − 1|, since the front is the unit sphere and the nearest point on a sphere lies along the ray); for the others it is a scan of 1024 curve points followed by 40 ternary-search steps between the winner’s neighbours, so the answer is a property of the curve rather than of the sampling density.

A malformed vector (wrong arity, non-finite) yields f64::INFINITY — unmeasurably far, never accidentally good.

pub fn zdt1(dim: usize) -> Result<Self>

ZDT1 in dim dimensions over [0, 1]^dim.

f₁ = x₀, g = 1 + 9·mean(x₁..x_{d−1}), f₂ = g·(1 − √(f₁/g)). Every Pareto-optimal solution has x₁.. = 0, i.e. g = 1, so the true front is the convex curve f₂ = 1 − √f₁ for f₁ ∈ [0, 1].

The standard multi-objective baseline: unimodal, separable, and with a knowable answer, so “did the front converge” is a measurement rather than an opinion.

§Errors

Error::InvalidSpace if the schema is malformed — never for dim ≥ 2.

pub fn zdt2(dim: usize) -> Result<Self>

ZDT2 in dim dimensions over [0, 1]^dim.

As zdt1 but f₂ = g·(1 − (f₁/g)²), so the true front is the concave curve f₂ = 1 − f₁².

Concavity is the point: a weighted-sum scalarization can only ever return points on the convex hull of the front, so on ZDT2 it reaches the two extremes and nothing between them. Only a dominance-based ranking — which is what NSGA-II is — covers the interior, and this is the problem that shows it.

§Errors

Error::InvalidSpace if the schema is malformed — never for dim ≥ 2.

pub fn dtlz2(dim: usize) -> Result<Self>

DTLZ2 with three objectives in dim dimensions over [0, 1]^dim.

g = Σ_{i≥2} (xᵢ − ½)², and with r = 1 + g

f₁ = r·cos(x₀π/2)·cos(x₁π/2)
f₂ = r·cos(x₀π/2)·sin(x₁π/2)
f₃ = r·sin(x₀π/2)

The true front is g = 0 — the positive octant of the unit sphere, Σ fᵢ² = 1 — which is why the distance to it is closed-form here.

Three objectives are not two: the crowding distance sums over one more axis, the hypervolume switches from the O(N log N) staircase sweep to the O(N² log N) dimension sweep, and a front is a surface rather than a curve. A two-objective-only oracle would leave all of that unmeasured.

§Errors

Error::InvalidSpace if the schema is malformed — never for dim ≥ 3.

pub fn constr_ex() -> Result<Self>

Deb’s CONSTR: the constrained two-objective problem whose unconstrained optimum is illegal.

x₀ ∈ [0.1, 1], x₁ ∈ [0, 5], minimize f₁ = x₀ and f₂ = (1 + x₁)/x₀ subject to

g₁:  x₁ + 9x₀ ≥ 6        →   6 − x₁ − 9x₀ ≤ 0
g₂: −x₁ + 9x₀ ≥ 1        →   1 + x₁ − 9x₀ ≤ 0

The unconstrained front is x₁ = 0, f₂ = 1/f₁ over the whole f₁ ∈ [0.1, 1] — and its left half violates g₁. The constrained front is therefore f₂ = (7 − 9f₁)/f₁ on the g₁ boundary for f₁ ∈ [7/18, 2/3] and f₂ = 1/f₁ for f₁ ∈ [2/3, 1].

That geometry is exactly what makes the problem a feasibility test with teeth: an infeasible point such as (x₀, x₁) = (0.2, 0) has objectives (0.2, 5), which dominates most of the legal front. A sampler that ranks by objectives alone will fill its population with it; a feasibility-first one will not (constrained_dominates).

§Errors

Error::InvalidSpace if the schema is malformed — never, for these fixed bounds.

Trait Implementations§

§

impl Clone for MoProblem

§

fn clone(&self) -> MoProblem

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
§

impl Debug for MoProblem

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> ByRef<T> for T

§

fn by_ref(&self) -> &T

§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T, U> Imply<T> for U
where T: ?Sized, U: ?Sized,

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more