"""
Class holding a list of points.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import List, Tuple
import numpy as np
from .point import Point
[docs]
@dataclass
class PointList:
"""
Class holding a list of points. Might be used as optimization run result log
or as a train set for surrogate function.
"""
points: List[Point]
"The list of points"
# alternative constructors
[docs]
@classmethod
def from_list(cls, xs: List[np.ndarray]) -> PointList:
"""
Alternative constructor that takes a list of x values.
Args:
xs: List of x values.
Returns:
Object of class PointList containing points with given x.
"""
return PointList(
points=[Point(x=point, y=None, is_evaluated=False) for point in xs]
)
# adding points to the list
[docs]
def append(self, new_point: Point) -> None:
"""
Add new point to the list.
Args:
new_point: Point to append to this object.
"""
self.points.append(new_point)
[docs]
def extend(self, new_points: PointList) -> None:
"""
Append a list of points to this PointList.
Args:
new_points: A list of point to append to this object.
"""
self.points.extend(new_points.points)
# getting point values
[docs]
def x(self) -> np.ndarray:
"""
Get all x values of points in this list.
Returns:
List containing x values of all points.
"""
return np.array([point.x for point in self.points], dtype=np.float64)
[docs]
def y(self) -> np.ndarray:
"""
Get all y values of points in this list.
Returns:
List of y values of all points.
"""
return np.array([point.y for point in self.points], dtype=np.float64)
[docs]
def pairs(self) -> Tuple[np.ndarray, np.ndarray]:
"""
Return the contents of this point list as list of x and list of y values.
This is potentially useful for quickly accesing point values for training surrofates.
Returns:
Lists of x and y values.
"""
return self.x(), self.y()
[docs]
def only_evaluated(self) -> PointList:
"""
Return list of only those points that have been evaluated.
Returns:
List containing evaluated points.
"""
return PointList(list(filter(lambda point: point.is_evaluated, self.points)))
# magic methods for list abstraction
[docs]
def __getitem__(self, index: int | slice) -> Point | PointList:
"""
Allows indexing and slicing this object like a list.
Args:
index: The index or slice of objects to fetch.
Returns:
A single Point object for integer index,
or a new PointList instance for slicing.
"""
if isinstance(index, slice):
return PointList(self.points[index])
return self.points[index]
[docs]
def __len__(self) -> int:
"""
Return number of points stored in the list.
Returns:
Number of points stored in the list.
"""
return len(self.points)
# fancy methods
[docs]
def rank(self, *, reverse: bool = False) -> None:
"""
Sort points by y value in place ascending.
Args:
reverse: If true, sorting is done descending. Default False.
"""
self.points = list(
sorted(self.points, key=lambda point: point.y, reverse=reverse)
)
[docs]
def x_difference(self, other) -> PointList:
"""
Return list of points in self that do not appear in other based on their x values.
Args:
other: Another PointList to compare against.
Returns:
List of points in self that are not in other.
"""
return PointList(
points=[
point_self
for point_self in self.points
if not point_self in other.points
]
)
[docs]
def remove_x(self) -> None:
"""
Set x values of points to None. This is done to save memory since xs are rarely used.
"""
for point in self:
point.remove_x()
# best value getters and similar methods
[docs]
def best(self) -> Point:
"""
Get the best point by y value from the PointList.
Returns:
The Point with the lowest y value in the list.
"""
return min(self.points, key=lambda point: point.y)
[docs]
def best_index(self) -> int:
"""
Get the index of the best point by y value in the PointList.
Returns:
The index of the point with the lowest y value.
"""
return min(range(len(self.points)), key=lambda i: self.points[i].y)
[docs]
def best_y(self) -> float:
"""
Get the best y value found. If list is empty, infinity is returned.
Returns:
The best y value found.
"""
return min((point.y for point in self.points), default=np.inf)
[docs]
def slice_to_best(self) -> PointList:
"""
Return a list of all points up to the best in the list, including the best.
Returns:
List of points up to the best point.
"""
return self[: self.best_index() + 1]