Coverage for moptipy / spaces / intspace.py: 95%

37 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-24 08:49 +0000

1"""An implementation of an integer string based search space.""" 

2 

3from typing import Final 

4 

5import numpy as np 

6from pycommons.types import type_error 

7 

8from moptipy.spaces.nparrayspace import NPArraySpace 

9from moptipy.utils.logger import KeyValueLogSection 

10from moptipy.utils.nputils import int_range_to_dtype 

11from moptipy.utils.strings import num_to_str_for_name 

12 

13#: the log key for the minimum value 

14KEY_MIN: Final[str] = "min" 

15#: the log key for the maximum value 

16KEY_MAX: Final[str] = "max" 

17 

18 

19class IntSpace(NPArraySpace): 

20 """ 

21 A space where each element is a one-dimensional numpy integer array. 

22 

23 Such spaces can serve as basis for implementing combinatorial 

24 optimization and can be extended to host permutations. Their elements are 

25 instances of :class:`numpy.ndarray`. 

26 

27 >>> s = IntSpace(5, -4, 99) 

28 >>> print(s.dimension) 

29 5 

30 >>> print(s.min_value) 

31 -4 

32 >>> print(s.max_value) 

33 99 

34 >>> print(s.dtype) 

35 int8 

36 >>> s = IntSpace(5, 2, 200) 

37 >>> print(s.dtype) 

38 uint8 

39 >>> s = IntSpace(5, 2, 202340) 

40 >>> print(s.dtype) 

41 int32 

42 """ 

43 

44 def __init__(self, dimension: int, 

45 min_value: int, max_value: int) -> None: 

46 """ 

47 Create the integer-based search space. 

48 

49 :param dimension: The dimension of the search space, 

50 i.e., the number of decision variables. 

51 :param min_value: the minimum value 

52 :param max_value: the maximum value 

53 """ 

54 if not isinstance(min_value, int): 

55 raise type_error(min_value, "min_value", int) 

56 if not isinstance(max_value, int): 

57 raise type_error(max_value, "max_value", int) 

58 if min_value >= max_value: 

59 raise ValueError( 

60 f"max_value > min_value must hold, but got " 

61 f"min_value={min_value} and max_value={max_value}.") 

62 super().__init__(dimension, int_range_to_dtype( 

63 min_value=min_value, max_value=max_value)) 

64 #: the lower bound, i.e., the minimum permitted value 

65 self.min_value: Final[int] = min_value 

66 #: the upper bound, i.e., the maximum permitted value 

67 self.max_value: Final[int] = max_value 

68 

69 def create(self) -> np.ndarray: 

70 """ 

71 Create an integer vector filled with the minimal value. 

72 

73 :return: the vector 

74 

75 >>> from moptipy.spaces.intspace import IntSpace 

76 >>> s = IntSpace(dimension=12, min_value=5, max_value=332) 

77 >>> v = s.create() 

78 >>> print(s.to_str(v)) 

79 5;5;5;5;5;5;5;5;5;5;5;5 

80 >>> print(v.dtype) 

81 int16 

82 """ 

83 return np.full(shape=self.dimension, fill_value=self.min_value, 

84 dtype=self.dtype) 

85 

86 def validate(self, x: np.ndarray) -> None: 

87 """ 

88 Validate an integer string. 

89 

90 :param x: the integer string 

91 :raises TypeError: if the string is not an :class:`numpy.ndarray`. 

92 :raises ValueError: if the shape or data type of the vector is wrong 

93 or any of its element is not finite or if an element is out of 

94 the bounds. 

95 """ 

96 super().validate(x) 

97 

98 miv: Final[int] = self.min_value 

99 mav: Final[int] = self.max_value 

100 for index, item in enumerate(x): 

101 if not (miv <= item <= mav): 

102 raise ValueError( 

103 f"x[{index}]={item}, but should be in {miv}..{mav}.") 

104 

105 def n_points(self) -> int: 

106 """ 

107 Get the number of possible different integer strings. 

108 

109 :return: (max_value - min_value + 1) ** dimension 

110 

111 >>> print(IntSpace(4, -1, 3).n_points()) 

112 625 

113 """ 

114 return (self.max_value - self.min_value + 1) ** self.dimension 

115 

116 def __str__(self) -> str: 

117 """ 

118 Get the name of this integer space. 

119 

120 :return: "ints" + dimension + dtype.char + min_value + "-" + max_value 

121 

122 >>> print(IntSpace(4, -1, 3)) 

123 ints4bm1to3 

124 """ 

125 return (f"ints{self.dimension}{self.dtype.char}" 

126 f"{num_to_str_for_name(self.min_value)}to" 

127 f"{num_to_str_for_name(self.max_value)}") 

128 

129 def log_parameters_to(self, logger: KeyValueLogSection) -> None: 

130 """ 

131 Log the parameters of this space to the given logger. 

132 

133 :param logger: the logger for the parameters 

134 

135 >>> from moptipy.utils.logger import InMemoryLogger 

136 >>> space = IntSpace(7, -5, 5) 

137 >>> space.dimension 

138 7 

139 >>> with InMemoryLogger() as l: 

140 ... with l.key_values("C") as kv: 

141 ... space.log_parameters_to(kv) 

142 ... text = l.get_log() 

143 >>> text[-2] 

144 'max: 5' 

145 >>> text[-3] 

146 'min: -5' 

147 >>> text[-4] 

148 'dtype: b' 

149 >>> text[-5] 

150 'nvars: 7' 

151 >>> text[-6] 

152 'class: moptipy.spaces.intspace.IntSpace' 

153 >>> text[-7] 

154 'name: ints7bm5to5' 

155 >>> len(text) 

156 8 

157 """ 

158 super().log_parameters_to(logger) 

159 logger.key_value(KEY_MIN, self.min_value) 

160 logger.key_value(KEY_MAX, self.max_value)