CellModules
LHSIterator.py
Go to the documentation of this file.
1import numpy as np
2import pickle
3import pandas as pd
4from typing import Callable, Dict, List, Any, Union, Iterable
5
7 """
8 Iterator for Latin Hypercube Sampling (LHS) parameter search.
9
10 Parameters
11 ----------
12 - **suggest_fn** : Callable[[Trial], Dict[str, Any]]
13 A function that suggests parameters for each trial. It takes a Trial object as input and returns a dictionary representing the suggested parameters.
14 - **n_samples** : int
15 The number of samples to generate.
16
17 Methods
18 -------
19 - **save** : Saves the LHSIterator object to a file using pickle serialization.
20 - **load** : Loads an LHSIterator object from a file using pickle deserialization.
21
22 Trial Methods
23 -------------
24 ```python
25 suggest_categorical(name: str, choices: List)
26
27 suggest_int(name: str, low: int, high: int, log: bool = False, step: int = None)
28
29 suggest_float(name: str, low: float, high: float, log: bool = False, step: float = None)
30 ```
31
32
33 Example
34 -------
35 ```python
36 def suggest_params(trial: LHSIterator.Trial) -> Dict[str, Any]:
37 return {
38 'input':{
39 "Cell": {
40 "Common": {
41 'hypothesis': trial.suggest_categorical("hypothesis", [False, True]),
42 'cycleDuration': trial.suggest_float("duration", 10.5, 20.3),
43 'startAge': 0
44 ...
45 }
46 }
47 }
48 }
49
50 lhs_iterator = LHSIterator(suggest_params, 10)
51
52 # Save the iterator
53 lhs_iterator.save("lhs_iterator.pkl")
54
55 # Load the iterator
56 loaded_iterator = LHSIterator.load("lhs_iterator.pkl")
57
58 for i, sample in enumerate(loaded_iterator):
59 print(f"Sample {i+1}: {sample}")
60 ```
61 """
62 class Trial:
63 def __init__(self, param_space: Dict[str, Any]):
64 self.param_space = param_space
65 self.params = {}
66 self.modeSuggest = False
67
68 def suggest_categorical(self, name: str, choices: List[Union[str, int, float]]) -> Any:
69 if self.modeSuggest: return self.params[name]
70 else:
71 if name in self.param_space: raise ValueError(f"Parameter name '{name}' already exists in the parameter space.")
72 self.param_space[name] = {"type": "categorical", "choices": choices}
73
74 def suggest_int(self, name: str, low: int, high: int, log: bool = False, step: int = None) -> Any:
75 if self.modeSuggest: return self.params[name]
76 else:
77 if name in self.param_space: raise ValueError(f"Parameter name '{name}' already exists in the parameter space.")
78 self.param_space[name] = {"type": "int", "low": low, "high": high}
79 if log: self.param_space[name]['log'] = log
80 if step is not None: self.param_space[name]['step'] = step
81
82 def suggest_float(self, name: str, low: float, high: float, log: bool = False, step: float = None) -> Any:
83 if self.modeSuggest: return self.params[name]
84 else:
85 if name in self.param_space: raise ValueError(f"Parameter name '{name}' already exists in the parameter space.")
86 self.param_space[name] = {"type": "float", "low": low, "high": high}
87 if log: self.param_space[name]['log'] = log
88 if step is not None: self.param_space[name]['step'] = step
89
90 def __init__(self, suggest_fn: Callable[[Any], Dict[str, Any]], n_samples: int):
91 self.param_space: Dict[str, Any] = {}
92 self.n_samples: int = n_samples
93
94 # Execute the suggestion function with a trial to define the param space
95 self.trial = self.Trial(self.param_space)
96 self.suggest_fn = suggest_fn
97 self.suggest_fn(self.trial)
98 self.trial.modeSuggest=True
99
100 # Generate LHS samples
101 self.lhs_samples = self._lhsclassic(len(self.trial.param_space), self.n_samples)
102
103 # Prepare the iterator
104 self.index = 0
105
106 def __iter__(self) -> 'LHSIterator':
107 self.index = 0
108 return self
109
110 def __len__(self) -> int:
111 return self.n_samples
112
113 def __next__(self) -> Dict[str, Any]:
114 if self.index >= self.n_samples:
115 raise StopIteration
116 sample = self.lhs_samples[self.index]
117 self.trial.params = self._map_sample_to_params(sample)
118 self.index += 1
119 return self.suggest_fn(self.trial)
120
121 def _map_sample_to_params(self, sample: np.ndarray) -> Dict[str, Any]:
122 params = {}
123 for i, (param_name, param_info) in enumerate(self.trial.param_space.items()):
124 if param_info['type'] == 'categorical':
125 params[param_name] = param_info['choices'][int(sample[i] * len(param_info['choices']))]
126 elif param_info['type'] == 'int':
127 if param_info.get('log', False):
128 low = np.log(param_info['low'])
129 high = np.log(param_info['high'])
130 params[param_name] = int(np.exp(sample[i] * (high - low) + low))
131 elif 'step' in param_info:
132 params[param_name] = param_info['low'] + int(sample[i] * ((param_info['high'] - param_info['low']) // param_info['step'])) * param_info['step']
133 else:
134 params[param_name] = param_info['low'] + int(sample[i] * (param_info['high'] - param_info['low'] + 1))
135 elif param_info['type'] == 'float':
136 if param_info.get('log', False):
137 low = np.log(param_info['low'])
138 high = np.log(param_info['high'])
139 params[param_name] = np.exp(sample[i] * (high - low) + low)
140 elif 'step' in param_info:
141 params[param_name] = param_info['low'] + int(sample[i] * ((param_info['high'] - param_info['low']) / param_info['step'])) * param_info['step']
142 else:
143 params[param_name] = param_info['low'] + sample[i] * (param_info['high'] - param_info['low'])
144 return params
145
146 def _lhsclassic(self, n: int, samples: int) -> np.ndarray:
147 # Generate the intervals
148 cut = np.linspace(0, 1, samples + 1)
149
150 # Fill points uniformly in each interval
151 u = np.random.rand(samples, n)
152 a = cut[:samples]
153 b = cut[1:samples + 1]
154 rdpoints = np.zeros_like(u)
155 for j in range(n):
156 rdpoints[:, j] = u[:, j]*(b-a) + a
157
158 # Make the random pairings
159 H = np.zeros_like(rdpoints)
160 for j in range(n):
161 order = np.random.permutation(range(samples))
162 H[:, j] = rdpoints[order, j]
163
164 return H
165
166 def save(self, filename: str) -> None:
167 r"""
168 Saves the LHSIterator object to a file using pickle serialization.
169
170 Parameters
171 ----------
172 - **filename** : str
173 The name of the file to save the object to.
174 """
175 with open(filename, 'wb') as f:
176 pickle.dump(self, f)
177
178 @staticmethod
179 def load(filename: str) -> 'LHSIterator':
180 r"""
181 Loads an LHSIterator object from a file using pickle deserialization.
182
183 Parameters
184 ----------
185 - **filename** : str
186 The name of the file to load the object from.
187
188 Returns
189 -------
190 - **LHSIterator**
191 The loaded LHSIterator object.
192 """
193 with open(filename, 'rb') as f:
194 return pickle.load(f)
195
196 def toDataFrame(self) -> pd.DataFrame:
197 r"""
198 Converts the generated LHS samples into a pandas DataFrame.
199
200 Returns
201 -------
202 - **pd.DataFrame**
203 A pandas DataFrame containing the generated LHS samples, where each row represents a sample and each column represents a parameter.
204 """
205 return pd.DataFrame([self._map_sample_to_params(s) for s in self.lhs_samples])
Any suggest_float(self, str name, float low, float high, bool log=False, float step=None)
Definition: LHSIterator.py:82
Any suggest_int(self, str name, int low, int high, bool log=False, int step=None)
Definition: LHSIterator.py:74
def __init__(self, Dict[str, Any] param_space)
Definition: LHSIterator.py:63
Any suggest_categorical(self, str name, List[Union[str, int, float]] choices)
Definition: LHSIterator.py:68
Dict[str, Any] _map_sample_to_params(self, np.ndarray sample)
Definition: LHSIterator.py:121
Dict[str, Any] __next__(self)
Definition: LHSIterator.py:113
'LHSIterator' __iter__(self)
Definition: LHSIterator.py:106
def __init__(self, Callable[[Any], Dict[str, Any]] suggest_fn, int n_samples)
Definition: LHSIterator.py:90
'LHSIterator' load(str filename)
Definition: LHSIterator.py:179
np.ndarray _lhsclassic(self, int n, int samples)
Definition: LHSIterator.py:146
pd.DataFrame toDataFrame(self)
Definition: LHSIterator.py:196
None save(self, str filename)
Definition: LHSIterator.py:166