from collections.abc import Generator, Sequence
from typing import Any, NamedTuple, Optional, Union
import numpy as np
import numpy.typing as npt
[docs]
class RandomStartPoint:
"""Class containing all the information to guide the random generation of this
point.
Parameters
----------
method : str
Name of the method of :class:`numpy.random.Generator` that must be used to
generate random start locations for this point, e.g., ``"unform"`` for
:meth:`numpy.random.Generator.uniform`, ``"normal"`` for
:meth:`numpy.random.Generator.normal`, etc.
args, kwargs
Args and kwargs with which to call the above method.
"""
def __init__(self, method: str, *args: Any, **kwargs: Any) -> None:
self.method = method
self.args = args
self.kwargs = kwargs
[docs]
class RandomStartPoints:
"""Class that can be iterated to yield a set of random start points for a multistart
NLP optimization problem (see :class:`csnlp.multistart.MultistartNlp` and its
subclasses).
Parameters
----------
points : dict of (str, RandomStartPoint)
Dictionary containing the name of each variable, and how to generate random
starting points for it (in the form of a :class:`RandomStartPoint` object).
multistarts : int, optional
The number of multiple start points. Default is ``10``.
biases : dict of (str, array_like), optional
Biases to add to the generated random points under the same name. If ``None``,
no bias is added.
scales : float, or array of floats
Scales to multiplty the generated random points with, under the same name. If
``None``, no scale is multiplied.
seed : None, int, array_like of ints, SeedSequence, BitGenerator, Generator
RNG seed.
"""
def __init__(
self,
points: dict[str, RandomStartPoint],
multistarts: int = 10,
biases: Optional[dict[str, npt.ArrayLike]] = None,
scales: Optional[dict[str, npt.ArrayLike]] = None,
seed: Union[
None,
int,
Sequence[int],
np.random.SeedSequence,
np.random.BitGenerator,
np.random.Generator,
] = None,
) -> None:
self.points = points
self.multistarts = multistarts
self.biases = biases or {}
self.scales = scales or {}
self.np_random = np.random.default_rng(seed)
def __iter__(self) -> Generator[dict[str, npt.ArrayLike], None, None]:
"""Iterates over the random start points, yielding each time a different set."""
biases = self.biases
scales = self.scales
points = self.points
out = {}
for _ in range(self.multistarts):
out.clear()
for name, point in points.items():
val = getattr(self.np_random, point.method)(*point.args, **point.kwargs)
if name in scales:
val = np.multiply(val, scales[name])
if name in biases:
val = np.add(val, biases[name])
out[name] = val
yield out.copy()
[docs]
class StructuredStartPoint(NamedTuple):
"""Class containing all the information to guide the structured generation of this
point."""
lb: npt.ArrayLike
ub: npt.ArrayLike
[docs]
class StructuredStartPoints:
"""Class that can be iterated to yield a set of structured (deterministic) start
points for a multistart NLP optimization problem (see
:class:`csnlp.multistart.MultistartNlp` and its subclasses). The points are linearly
spaced between upper- and lower-bounds.
Parameters
----------
points : dict of (str, StructuredStartPoint)
Dictionary containing the name of each variable, and how to generate structured
starting points for it (in the form of a :class:`StructuredStartPoint` object).
multistarts : int, optional
The number of multiple start points. Default is ``10``.
"""
def __init__(
self, points: dict[str, StructuredStartPoint], multistarts: int = 10
) -> None:
self.points = points
self.multistarts = multistarts
def __iter__(self) -> Generator[dict[str, npt.ArrayLike], None, None]:
"""Iterates over the structured start points."""
data = {
n: iter(np.linspace(p.lb, p.ub, self.multistarts))
for n, p in self.points.items()
}.items()
yield from ({n: next(v) for n, v in data} for _ in range(self.multistarts))