import logging
from typing import List
import numpy as np
try:
from pymoo.indicators.hv import HV
except ImportError as e:
logging.debug(e)
EPSILON = 1e-6
[docs]
def default_reference_point(results_array: np.ndarray) -> np.ndarray:
return results_array.max(axis=0) * (1 + EPSILON) + EPSILON
[docs]
def hypervolume(
results_array: np.ndarray,
reference_point: np.ndarray = None,
) -> float:
"""
Compute the hypervolume of all results based on reference points
:param results_array: Array with experiment results ordered by time with
shape ``(npoints, ndimensions)``.
:param reference_point: Reference points for hypervolume calculations.
If ``None``, the maximum values of each dimension of results_array is
used.
:return Hypervolume indicator
"""
if reference_point is None:
reference_point = default_reference_point(results_array)
indicator_fn = HV(ref_point=reference_point)
return indicator_fn(results_array)
[docs]
def linear_interpolate(hv_indicator: np.ndarray, indices: List[int]):
for first, last in zip(indices[:-1], indices[1:]):
num = last - first + 1
v_first = hv_indicator[first]
v_last = hv_indicator[last]
hv_indicator[first : (last + 1)] = np.linspace(v_first, v_last, num=num)
# TODO: Use code for incremental hypervolume (adding one more point). Computation
# here can be slow.
# At least we could check for each new row if it is dominated by rows before. If
# so, the cumulative indicator remains the same.
[docs]
def hypervolume_cumulative(
results_array: np.ndarray,
reference_point: np.ndarray = None,
increment: int = 1,
) -> np.ndarray:
"""
Compute the cumulative hypervolume of all results based on reference points
Returns an array with hypervolumes given by an increasing range of points.
``return_array[idx] = hypervolume(results_array[0 : (idx + 1)])``.
The current implementation is very slow, since the hypervolume index is not
computed incrementally. A solution for now is to use ``increment > 1``,
in which case the HV index is only computed every ``increment`` entry, and
linearly interpolated in between.
:param results_array: Array with experiment results ordered by time with
shape ``(npoints, ndimensions)``.
:param reference_point: Reference points for hypervolume calculations.
If ``None``, the maximum values of each dimension of results_array is
used.
:return: Cumulative hypervolume array, shape ``(npoints,)``
"""
if reference_point is None:
reference_point = default_reference_point(results_array)
indicator_fn = HV(ref_point=reference_point)
hypervolume_indicator = np.zeros(shape=len(results_array))
sz = len(results_array)
indices = list(range(0, sz, increment))
if indices[-1] != sz - 1:
indices.append(sz - 1)
for idx in indices:
hypervolume_indicator[idx] = indicator_fn(results_array[0 : (idx + 1)])
if increment > 1:
linear_interpolate(hypervolume_indicator, indices)
return hypervolume_indicator