Skip to content

Multi-objective (mo)

sezgi.mo.*: NSGA-II over the built-in ZDT/DTLZ/WFG problem families, plus hypervolume, IGD, an analytic Pareto-front sampler, and sezgi-moa-format archive logging/reading. sezgi.NSGA2 (Built-in algorithm classes) is a thin class-first skin over mo.nsga2 below.

mo.nsga2

_mo_nsga2(problem, dim, pop_size, budget, m=None, seed=0, eta_c=20.0, eta_m=20.0, p_c=0.9, p_m=None, p_c_bin=0.9, p_m_bin=None, p_c_cat=0.9, p_m_cat=None, k=None, l=None, log_dir=None, label=None)

mo.nsga2(problem, dim, pop_size, budget, m=None, seed=0, eta_c=20.0, eta_m=20.0, p_c=0.9, p_m=None, p_c_bin=0.9, p_m_bin=None, p_c_cat=0.9, p_m_cat=None, k=None, l=None, log_dir=None, label=None) -> dict

Runs NSGA-II (Deb et al. 2002) over one of this module's built-in multi-objective problem families -- see this file's own "Problem strings" note above for problem/dim/m/k/l's per-family validation rules.

Returns a dict with keys individuals (list of float-lists, one per final-population member -- a Binary block's bits are flattened to 0.0/1.0), objectives (list of float-lists, parallel to individuals), front0 (list of ints: indices of the final population's non-dominated set), evals_used (int), and violations (list of floats, <= 0.0, 0.0 = feasible; present ONLY when the problem is constrained -- dtlz8/ dtlz9 today).

eta_c/eta_m/p_c default to the NSGA-II paper's own pinned experimental settings (Deb et al. 2002, Sec. IV.A); p_m=None resolves on the Rust side to 1/n_variables. p_c_bin/p_m_bin are the binary-genotype counterparts (consulted only for zdt5's all-Binary space); p_c_bin defaults to 0.9 (mirroring p_c; the paper gives no verified binary-specific default), p_m_bin=None resolves to 1/l (the paper's own stated binary default). p_c_cat/p_m_cat are the Categorical-genotype counterparts (consulted only for a Mixed space containing a Categorical block -- no problem string in this module's own catalog builds one today, so they are currently validated but inert, exactly like p_c_bin/p_m_bin on every non-zdt5 problem); p_c_cat defaults to 0.9 (mirroring p_c_bin's own reasoning), p_m_cat=None resolves to 1/n_cat (n_cat = the space's total flattened Categorical dimension).

pop_size must be >= 4 AND a multiple of 4 (a KanGAL-faithful tightening of the naive "even, >= 4" rule) or this raises ValueError. When log_dir is given, the run is additionally streamed to /

mo.hypervolume_2d builtin

mo_hypervolume_2d(front, ref_point)

sezgi.mo.hypervolume_2d(front, ref_point) -- binds [sezgi_stats::hypervolume_2d] exactly (see that function's doc for the pinned S-metric definition and reference-point convention). front: a list of [f1, f2] rows (minimization). ref_point: a 2-element [f64; 2]-shaped list.

Errors

ValueError if ref_point does not have exactly 2 values, or for any [sezgi_stats::StatsError] (empty front, a non-2-objective row, or a non-finite value).

mo.hypervolume builtin

mo_hypervolume(front, ref_point)

sezgi.mo.hypervolume(front, ref_point) -- binds [sezgi_stats::hypervolume] (M3-7 Task 8/10), the exact general-M hypervolume via the WFG algorithm (While, Bradstreet & Barone, "A Fast Way of Calculating Exact Hypervolumes", IEEE TEC 2012). Unlike hypervolume_2d, front/ ref_point may have any number M >= 1 of objectives (M == 2 delegates internally to the SAME hypervolume_2d, that module's own "2D shortcut" doc section).

ref_point is REQUIRED, with no default (a plain positional/keyword argument -- calling this with only front raises Python's own TypeError for a missing argument, by signature, not a ValueError this binding raises itself): sezgi_stats::moo_indicators's own module doc, "Choosing a reference point: explicit-always, contested in the literature" section, deliberately never picks one FOR the caller. One common convention from that literature (also the module's own examples/ tests' choice, and the specific one critiqued by Ishibuchi, Imada, Setoguchi & Nojima 2018, "How to Specify a Reference Point in Hypervolume Calculation for Fair Performance Comparison", GECCO Companion) is the analytic front's nadir point (the componentwise worst value across the front) scaled by 1.1 -- a caller-supplied choice, never defaulted here.

Errors

ValueError for any [sezgi_stats::StatsError] (ref_point empty, a front row with a different number of objectives than ref_point, or a non-finite value). An EMPTY front is NOT an error -- it returns 0.0 (the algorithm's own base case, hypervolume's own "empty front" doc section).

mo.igd builtin

mo_igd(front, reference_front)

sezgi.mo.igd(front, reference_front) -- binds [sezgi_stats::igd] exactly (Ishibuchi et al. 2015, eq. 12, p = 1; see that function's doc for the pinned definition). Any (equal, consistent) number of objectives across both front and reference_front.

Errors

ValueError for any [sezgi_stats::StatsError] (an empty front or reference_front, a dimension mismatch, or a non-finite value).

mo.pareto_front

_mo_pareto_front(problem, dim, n, m=None, k=None, l=None)

mo.pareto_front(problem, dim, n, m=None, k=None, l=None) -> list of float-lists, or None

A deterministic n-point sample of the analytic Pareto front in OBJECTIVE space for one of this module's built-in problem families -- same problem/dim/m/k/l mapping and validation as mo.nsga2. Returns None when the problem has no known analytic front sample at this m (e.g. DTLZ5/DTLZ6 with m > 3, or WFG1/WFG2 unconditionally).

mo.evaluate builtin

mo_evaluate(problem, x, dim=None, m=None, k=None, l=None)

sezgi.mo.evaluate(problem, x, dim=None, m=None, k=None, l=None) -> list[float] -- direct, one-shot objective evaluation of a decision vector x against any sezgi.mo problem string, bypassing nsga2's population/budget machinery entirely (problem/dim/m/k/l share mo_nsga2's own mo_problem_from_str mapping).

# sezgi decision: (M3-7 Task 10) added purely so this binding's OWN test suite can pin exact fixture values (zdt5's all-ones/all-zeros hand fixtures, dtlz8/9's hand fixtures, the committed wfg_reference_values.json points) directly from Python -- mirroring the existing cec2022_evaluate/cec2014_evaluate/cec2017_evaluate one-shot-evaluation convention already established in this file. Not literally named in this task's brief (which only names nsga2/pareto_front for the new problem families), but required by the brief's own Test section, since nsga2's randomly-initialized population cannot pin a fixture at a chosen x.

x: for an all-Float problem, its raw decision values; for zdt5 (the only all-Binary problem reachable here), each entry is read as a bit (!= 0.0 -> true) -- see [genotype_from_flat]'s own doc.

Errors

Same problem-construction errors as mo.nsga2/mo.pareto_front, plus a ValueError if len(x) does not match the problem's own dimension.

mo.evaluate_constraints builtin

mo_evaluate_constraints(problem, x, dim=None, m=None, k=None, l=None)

sezgi.mo.evaluate_constraints(problem, x, dim=None, m=None, k=None, l=None) -> Optional[list[float]] -- direct, one-shot constraint-row evaluation, mirroring mo.evaluate's calling convention exactly (same [mo_problem_from_str] mapping, same [genotype_from_flat] decoding). Returns None for an unconstrained problem (every zdt/wfg problem, and dtlz1-7), or the constraint row (g_1..g_ncon, g_j >= 0 meaning SATISFIED -- see [sezgi_core::mo::MoProblem::evaluate_constraints_batch]'s own doc for the pinned sign convention) for a constrained one (dtlz8/ dtlz9).

Errors

Same as mo.evaluate.

mo.read_moa builtin

mo_read_moa(path, at=None)

sezgi.mo.read_moa(path, at=None) -- binds [sezgi_bench::read_moa] (a "sezgi-moa v1" archive file written by mo.nsga2(..., log_dir=..., label=...), M3-7 Task 9/10). Returns a dict: - algo (str): the logging algorithm name -- always "nsga2" today (nsga2_run_logged's own fixed NSGA2_ALGO_NAME). - problem (str): the label mo.nsga2 was called with. Kept as the literal on-disk header key name (crates/bench/src/mo_archive.rs's own format grammar: the header line is problem <label>, not label <label>) rather than renamed here to "label" -- this binding stays a thin, direct mirror of [MoArchiveRun]'s own field names, so a reader cross-checking against the Rust struct (or the R binding, M3-7 Task 11) sees the SAME key everywhere. - m (int), seed (int), budget (int). - kind (str): "float" or "binary". - records (list of dicts, in file/eval order): eval_index (int), objectives (list of float), genotype (list of float for kind="float", list of bool for kind="binary" -- [MoArchiveGenotype]'s own two variants). - archive (list of float-lists): the reconstructed nondominated archive at evaluation budget at, via [MoArchiveRun::archive_at].

# sezgi decision: at=None resolves to the file's own logged budget header field (the full run's final archive) -- archive_at itself takes a REQUIRED evals: u64 with no Rust-side default, and the file's own budget is the one value guaranteed to reconstruct the run's COMPLETE trajectory (nsga2_run_logged's own MoEvaluator budget enforcement means no eval_index beyond it was ever charged) -- a natural, self-contained default rather than requiring every caller to pass the budget back in by hand after already reading it out of the same dict.

Errors

ValueError for any [sezgi_bench::MoArchiveError] (missing file, a malformed header, or a malformed record line).