Coverage for moptipy / spaces / nparrayspace.py: 88%
40 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-24 08:49 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-24 08:49 +0000
1"""The base class for spaces based on numpy arrays."""
2from typing import Final
4import numpy as np
5from pycommons.types import check_int_range, type_error
7from moptipy.api.logging import KEY_SPACE_NUM_VARS
8from moptipy.api.space import Space
9from moptipy.utils.logger import CSV_SEPARATOR, KeyValueLogSection
10from moptipy.utils.nputils import (
11 KEY_NUMPY_TYPE,
12 array_to_str,
13 numpy_type_to_str,
14)
17class NPArraySpace(Space):
18 """
19 A space where each element is a one-dimensional :class:`numpy.ndarray`.
21 Such spaces can serve as basis for implementing combinatorial
22 optimization and can be extended to host permutations.
24 >>> import numpy as npx
25 >>> s = NPArraySpace(9, npx.dtype(int))
26 >>> print(s.dimension)
27 9
28 >>> print(s.dtype)
29 int64
30 >>> print(s.create())
31 [0 0 0 0 0 0 0 0 0]
32 >>> print(s.to_str(s.create()))
33 0;0;0;0;0;0;0;0;0
34 >>> print(s.from_str(s.to_str(s.create())))
35 [0 0 0 0 0 0 0 0 0]
36 """
38 def __init__(self, dimension: int, dtype: np.dtype) -> None:
39 """
40 Create the numpy array-based search space.
42 :param dimension: The dimension of the search space,
43 i.e., the number of decision variables.
44 :param dtype: the data type
45 """
46 if not isinstance(dtype, np.dtype):
47 raise type_error(dtype, "dtype", np.dtype)
48 if (not isinstance(dtype.char, str)) or (len(dtype.char) != 1):
49 raise ValueError(
50 f"dtype.char must be str of length 1, but is {dtype.char}")
51 #: The basic data type of the vector space elements.
52 self.dtype: Final[np.dtype] = dtype
53 #: The dimension, i.e., the number of elements of the vectors.
54 self.dimension: Final[int] = check_int_range(
55 dimension, "dimension", 1, 100_000_000)
56 # the function forwards
57 self.copy = np.copyto # type: ignore
58 self.is_equal = np.array_equal # type: ignore
60 def to_str(self, x: np.ndarray) -> str:
61 """
62 Convert a np-array to a string.
64 :param x: the array
65 :returns: the string
66 """
67 return array_to_str(x)
69 def create(self) -> np.ndarray:
70 """
71 Create a vector with all zeros.
73 :return: the vector
74 """
75 return np.zeros(shape=self.dimension, dtype=self.dtype)
77 def from_str(self, text: str) -> np.ndarray:
78 """
79 Convert a string to a vector.
81 :param text: the text
82 :return: the vector
83 :raises TypeError: if `text` is not a `str`
84 :raises ValueError: if `text` cannot be converted to a valid vector
85 """
86 if not (isinstance(text, str)):
87 raise type_error(text, "text", str)
88 x = np.fromstring(text, dtype=self.dtype, sep=CSV_SEPARATOR)
89 self.validate(x)
90 return x
92 def validate(self, x: np.ndarray) -> None:
93 """
94 Validate a numpy nd-array.
96 :param x: the numpy vector
97 :raises TypeError: if the string is not an :class:`numpy.ndarray`.
98 :raises ValueError: if the shape or data type of the vector is wrong
99 or any of its element is not finite.
100 """
101 if not isinstance(x, np.ndarray):
102 raise type_error(x, "x", np.ndarray)
103 if x.dtype != self.dtype:
104 raise ValueError(
105 f"x must be of type {self.dtype} but is of type {x.dtype}.")
106 if (len(x.shape) != 1) or (x.shape[0] != self.dimension):
107 raise ValueError(f"x must be of shape ({self.dimension}) but is "
108 f"of shape {x.shape}.")
110 def __str__(self) -> str:
111 """
112 Get the name of this np array space.
114 :return: "ndarray" + dimension + dtype.char
116 >>> import numpy as npx
117 >>> print(NPArraySpace(4, npx.dtype(int)))
118 ndarray4l
119 """
120 return f"ndarray{self.dimension}{self.dtype.char}"
122 def log_parameters_to(self, logger: KeyValueLogSection) -> None:
123 """
124 Log the parameters of this space to the given logger.
126 :param logger: the logger for the parameters
128 >>> from moptipy.utils.logger import InMemoryLogger
129 >>> import numpy as npx
130 >>> dt = npx.dtype(float)
131 >>> dt.char
132 'd'
133 >>> space = NPArraySpace(10, dt)
134 >>> space.dimension
135 10
136 >>> with InMemoryLogger() as l:
137 ... with l.key_values("C") as kv:
138 ... space.log_parameters_to(kv)
139 ... text = l.get_log()
140 >>> text[-2]
141 'dtype: d'
142 >>> text[-3]
143 'nvars: 10'
144 >>> text[-4]
145 'class: moptipy.spaces.nparrayspace.NPArraySpace'
146 >>> text[-5]
147 'name: ndarray10d'
148 >>> len(text)
149 6
150 """
151 super().log_parameters_to(logger)
152 logger.key_value(KEY_SPACE_NUM_VARS, self.dimension)
153 logger.key_value(KEY_NUMPY_TYPE, numpy_type_to_str(self.dtype))