Skip to content

Pareto

Multi-objective Pareto front analysis.

trade_study.extract_front(scores, directions, weights=None)

Extract Pareto-optimal indices from a score matrix.

Parameters:

Name Type Description Default
scores NDArray[floating[Any]]

Array of shape (n_trials, n_objectives).

required
directions list[Direction]

Optimization direction for each objective.

required
weights list[float] | None

Optional per-objective weights. Larger weight increases the importance of that objective during non-dominated sorting.

None

Returns:

Type Description
NDArray[intp]

Integer array of row indices on the Pareto front.

Source code in src/trade_study/_pareto.py
def extract_front(
    scores: NDArray[np.floating[Any]],
    directions: list[Direction],
    weights: list[float] | None = None,
) -> NDArray[np.intp]:
    """Extract Pareto-optimal indices from a score matrix.

    Args:
        scores: Array of shape (n_trials, n_objectives).
        directions: Optimization direction for each objective.
        weights: Optional per-objective weights.  Larger weight increases
            the importance of that objective during non-dominated sorting.

    Returns:
        Integer array of row indices on the Pareto front.
    """
    from pymoo.util.nds.non_dominated_sorting import (  # type: ignore[import-untyped]
        NonDominatedSorting,
    )

    obj = _normalize_objectives(scores, directions, weights)
    nds = NonDominatedSorting()
    fronts = nds.do(obj)
    return np.asarray(fronts[0], dtype=np.intp)

trade_study.pareto_rank(scores, directions, weights=None)

Assign Pareto rank to each trial (0 = front, 1 = next layer, ...).

Parameters:

Name Type Description Default
scores NDArray[floating[Any]]

Array of shape (n_trials, n_objectives).

required
directions list[Direction]

Optimization direction for each objective.

required
weights list[float] | None

Optional per-objective weights.

None

Returns:

Type Description
NDArray[intp]

Integer array of ranks, shape (n_trials,).

Source code in src/trade_study/_pareto.py
def pareto_rank(
    scores: NDArray[np.floating[Any]],
    directions: list[Direction],
    weights: list[float] | None = None,
) -> NDArray[np.intp]:
    """Assign Pareto rank to each trial (0 = front, 1 = next layer, ...).

    Args:
        scores: Array of shape (n_trials, n_objectives).
        directions: Optimization direction for each objective.
        weights: Optional per-objective weights.

    Returns:
        Integer array of ranks, shape (n_trials,).
    """
    from pymoo.util.nds.non_dominated_sorting import (
        NonDominatedSorting,
    )

    obj = _normalize_objectives(scores, directions, weights)
    nds = NonDominatedSorting()
    fronts = nds.do(obj)
    ranks = np.empty(len(scores), dtype=np.intp)
    for rank, front in enumerate(fronts):
        ranks[front] = rank
    return ranks

trade_study.hypervolume(front, ref_point, directions=None, weights=None)

Compute hypervolume indicator for a Pareto front.

Parameters:

Name Type Description Default
front NDArray[floating[Any]]

Array of shape (n_points, n_objectives) on the front.

required
ref_point NDArray[floating[Any]]

Reference point (should dominate all front points after direction normalization).

required
directions list[Direction] | None

If provided, flips maximize objectives before computing.

None
weights list[float] | None

Optional per-objective weights applied after direction flip.

None

Returns:

Type Description
float

Hypervolume value.

Source code in src/trade_study/_pareto.py
def hypervolume(
    front: NDArray[np.floating[Any]],
    ref_point: NDArray[np.floating[Any]],
    directions: list[Direction] | None = None,
    weights: list[float] | None = None,
) -> float:
    """Compute hypervolume indicator for a Pareto front.

    Args:
        front: Array of shape (n_points, n_objectives) on the front.
        ref_point: Reference point (should dominate all front points after
            direction normalization).
        directions: If provided, flips maximize objectives before computing.
        weights: Optional per-objective weights applied after direction flip.

    Returns:
        Hypervolume value.
    """
    from pymoo.indicators.hv import HV  # type: ignore[import-untyped]

    obj = front.copy()
    rp = ref_point.copy()
    if directions is not None:
        for j, d in enumerate(directions):
            if d == Direction.MAXIMIZE:
                obj[:, j] = -obj[:, j]
                rp[j] = -rp[j]
    if weights is not None:
        for j, w in enumerate(weights):
            obj[:, j] *= w
            rp[j] *= w
    return float(HV(ref_point=rp)(obj))

trade_study.igd_plus(front, reference, directions=None, weights=None)

Compute IGD+ indicator.

Parameters:

Name Type Description Default
front NDArray[floating[Any]]

Obtained Pareto front.

required
reference NDArray[floating[Any]]

Reference Pareto front.

required
directions list[Direction] | None

Optimization directions.

None
weights list[float] | None

Optional per-objective weights applied after direction flip.

None

Returns:

Type Description
float

IGD+ value (lower is better).

Source code in src/trade_study/_pareto.py
def igd_plus(
    front: NDArray[np.floating[Any]],
    reference: NDArray[np.floating[Any]],
    directions: list[Direction] | None = None,
    weights: list[float] | None = None,
) -> float:
    """Compute IGD+ indicator.

    Args:
        front: Obtained Pareto front.
        reference: Reference Pareto front.
        directions: Optimization directions.
        weights: Optional per-objective weights applied after direction flip.

    Returns:
        IGD+ value (lower is better).
    """
    from pymoo.indicators.igd_plus import IGDPlus  # type: ignore[import-untyped]

    obj = front.copy()
    ref = reference.copy()
    if directions is not None:
        for j, d in enumerate(directions):
            if d == Direction.MAXIMIZE:
                obj[:, j] = -obj[:, j]
                ref[:, j] = -ref[:, j]
    if weights is not None:
        for j, w in enumerate(weights):
            obj[:, j] *= w
            ref[:, j] *= w
    return float(IGDPlus(ref)(obj))