functioneer 0.4.2__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {functioneer-0.4.2/src/functioneer.egg-info → functioneer-0.5.0}/PKG-INFO +3 -3
- {functioneer-0.4.2 → functioneer-0.5.0}/README.md +2 -2
- {functioneer-0.4.2 → functioneer-0.5.0}/pyproject.toml +1 -1
- {functioneer-0.4.2 → functioneer-0.5.0}/src/functioneer/__init__.py +2 -2
- {functioneer-0.4.2 → functioneer-0.5.0}/src/functioneer/analysis.py +50 -26
- {functioneer-0.4.2 → functioneer-0.5.0}/src/functioneer/steps.py +57 -71
- {functioneer-0.4.2 → functioneer-0.5.0/src/functioneer.egg-info}/PKG-INFO +3 -3
- {functioneer-0.4.2 → functioneer-0.5.0}/LICENSE +0 -0
- {functioneer-0.4.2 → functioneer-0.5.0}/setup.cfg +0 -0
- {functioneer-0.4.2 → functioneer-0.5.0}/src/functioneer/parameter.py +0 -0
- {functioneer-0.4.2 → functioneer-0.5.0}/src/functioneer/util.py +0 -0
- {functioneer-0.4.2 → functioneer-0.5.0}/src/functioneer.egg-info/SOURCES.txt +0 -0
- {functioneer-0.4.2 → functioneer-0.5.0}/src/functioneer.egg-info/dependency_links.txt +0 -0
- {functioneer-0.4.2 → functioneer-0.5.0}/src/functioneer.egg-info/requires.txt +0 -0
- {functioneer-0.4.2 → functioneer-0.5.0}/src/functioneer.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: functioneer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Effortlessly explore function behavior with the ultimate batch runner.
|
|
5
5
|
Author-email: Quinn Marsh <quinnmarsh@hotmail.com>
|
|
6
6
|
Maintainer-email: Quinn Marsh <quinnmarsh@hotmail.com>
|
|
@@ -71,7 +71,7 @@ analysis = fn.AnalysisModule() # Create new analysis
|
|
|
71
71
|
analysis.add.define({'a': 1, 'b': 100}) # define a and b
|
|
72
72
|
analysis.add.fork('x', (0, 1, 2)) # Fork analysis, create branches for x=0, x=1, x=2
|
|
73
73
|
analysis.add.fork('y', (1, 10))
|
|
74
|
-
analysis.add.
|
|
74
|
+
analysis.add.evaluate(func=rosenbrock) #
|
|
75
75
|
results = analysis.run()
|
|
76
76
|
print('Example 1 Output:')
|
|
77
77
|
print(results['df'][['a', 'b', 'x', 'y', 'rosenbrock']])
|
|
@@ -132,7 +132,7 @@ At its core, functioneer organizes analyses as a tree where a *set of parameters
|
|
|
132
132
|
Summary of most useful types of *analysis steps*:
|
|
133
133
|
- Define: Adds a new parameter to the analysis
|
|
134
134
|
- Fork: Splits the analysis into multiple parallel *branches*, each exploring different values for a specific parameter
|
|
135
|
-
-
|
|
135
|
+
- Evaluate: Calls a provided function using the parameters
|
|
136
136
|
- Optimize: Quickly set up an optimization by providing a function and defining which parameters are going to be optimized
|
|
137
137
|
|
|
138
138
|
<details>
|
|
@@ -40,7 +40,7 @@ analysis = fn.AnalysisModule() # Create new analysis
|
|
|
40
40
|
analysis.add.define({'a': 1, 'b': 100}) # define a and b
|
|
41
41
|
analysis.add.fork('x', (0, 1, 2)) # Fork analysis, create branches for x=0, x=1, x=2
|
|
42
42
|
analysis.add.fork('y', (1, 10))
|
|
43
|
-
analysis.add.
|
|
43
|
+
analysis.add.evaluate(func=rosenbrock) #
|
|
44
44
|
results = analysis.run()
|
|
45
45
|
print('Example 1 Output:')
|
|
46
46
|
print(results['df'][['a', 'b', 'x', 'y', 'rosenbrock']])
|
|
@@ -101,7 +101,7 @@ At its core, functioneer organizes analyses as a tree where a *set of parameters
|
|
|
101
101
|
Summary of most useful types of *analysis steps*:
|
|
102
102
|
- Define: Adds a new parameter to the analysis
|
|
103
103
|
- Fork: Splits the analysis into multiple parallel *branches*, each exploring different values for a specific parameter
|
|
104
|
-
-
|
|
104
|
+
- Evaluate: Calls a provided function using the parameters
|
|
105
105
|
- Optimize: Quickly set up an optimization by providing a function and defining which parameters are going to be optimized
|
|
106
106
|
|
|
107
107
|
<details>
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "functioneer"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.5.0"
|
|
8
8
|
authors = [{ name = "Quinn Marsh", email = "quinnmarsh@hotmail.com" }]
|
|
9
9
|
maintainers = [{ name = "Quinn Marsh", email = "quinnmarsh@hotmail.com" }]
|
|
10
10
|
description = "Effortlessly explore function behavior with the ultimate batch runner."
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
# functioneer/__init__.py
|
|
2
|
-
__version__ = "0.
|
|
3
|
-
from functioneer.analysis import AnalysisModule, AnalysisStep, Define, Fork,
|
|
2
|
+
__version__ = "0.5.0"
|
|
3
|
+
from functioneer.analysis import AnalysisModule, AnalysisStep, Define, Fork, Evaluate, Optimize
|
|
4
4
|
from functioneer.parameter import Parameter
|
|
@@ -20,13 +20,14 @@
|
|
|
20
20
|
# THE SOFTWARE.
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
import itertools
|
|
23
24
|
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
|
24
25
|
import copy
|
|
25
26
|
import pandas as pd
|
|
26
27
|
from datetime import datetime
|
|
27
28
|
import time
|
|
28
29
|
|
|
29
|
-
from functioneer.steps import AnalysisStep, Define, Fork,
|
|
30
|
+
from functioneer.steps import AnalysisStep, Define, Fork, Evaluate, Optimize
|
|
30
31
|
from functioneer.parameter import ParameterSet, Parameter
|
|
31
32
|
from functioneer.util import call_with_matched_kwargs
|
|
32
33
|
|
|
@@ -139,52 +140,75 @@ class AnalysisModule():
|
|
|
139
140
|
else:
|
|
140
141
|
raise ValueError("Expected (param_id, value) or {param_id: value, ...}")
|
|
141
142
|
|
|
142
|
-
def fork(self,
|
|
143
|
-
|
|
143
|
+
def fork(self, param_or_dict_or_configs: Union[str, Dict[str, Tuple[Any, ...]], List[Dict[str, Any]], Tuple[Dict[str, Any], ...]],
|
|
144
|
+
value_list: Optional[Tuple[Any, ...]] = None, condition: Optional[Callable[..., bool]] = None) -> None:
|
|
145
|
+
"""Fork analysis with a single parameter, dictionary of parameter value lists, or list of parameter configurations.
|
|
144
146
|
|
|
145
147
|
Args:
|
|
146
|
-
|
|
147
|
-
|
|
148
|
+
param_or_dict_or_configs: Parameter ID (str), dictionary of parameter IDs to value lists, or list/tuple of parameter configurations.
|
|
149
|
+
value_list: Tuple of values for the parameter (if param_or_dict_or_configs is a string).
|
|
148
150
|
condition: Optional condition function to determine if the step should run.
|
|
149
151
|
|
|
150
152
|
Examples:
|
|
151
153
|
>>> anal.add.fork('x', (0, 1, 2)) # Fork single parameter
|
|
152
|
-
>>> anal.add.fork({'x': (0, 1
|
|
154
|
+
>>> anal.add.fork({'x': (0, 1), 'y': (10, 20)}) # Fork multiple parameters with value lists
|
|
155
|
+
>>> anal.add.fork([{'x': 0, 'y': 0}, {'x': 1, 'y': 10}]) # Fork with parameter configurations
|
|
153
156
|
|
|
154
157
|
Raises:
|
|
155
|
-
ValueError: If inputs are invalid (e.g., wrong types, missing
|
|
158
|
+
ValueError: If inputs are invalid (e.g., wrong types, missing value_list, non-iterable value lists, or inconsistent configurations).
|
|
156
159
|
"""
|
|
157
|
-
if isinstance(
|
|
158
|
-
if
|
|
159
|
-
raise ValueError(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
160
|
+
if isinstance(param_or_dict_or_configs, str):
|
|
161
|
+
if value_list is None:
|
|
162
|
+
raise ValueError("value_list must be provided for single parameter fork")
|
|
163
|
+
if not isinstance(value_list, (list, tuple)):
|
|
164
|
+
raise ValueError("value_list must be a list or tuple of parameter values")
|
|
165
|
+
configurations = [{param_or_dict_or_configs: value} for value in value_list]
|
|
166
|
+
elif isinstance(param_or_dict_or_configs, dict):
|
|
167
|
+
param_ids = list(param_or_dict_or_configs.keys())
|
|
168
|
+
value_lists = list(param_or_dict_or_configs.values())
|
|
169
|
+
# Validate that value lists are iterable and same length
|
|
170
|
+
for vs in value_lists:
|
|
171
|
+
if not isinstance(vs, (list, tuple)):
|
|
172
|
+
raise ValueError("Value lists must be lists or tuples")
|
|
173
|
+
value_lengths = [len(values) for values in value_lists]
|
|
174
|
+
if not value_lengths:
|
|
175
|
+
raise ValueError("param_value_lists dictionary cannot be empty")
|
|
176
|
+
if len(set(value_lengths)) > 1:
|
|
177
|
+
raise ValueError("All value lists must have the same length")
|
|
178
|
+
|
|
179
|
+
# Compute create configurations
|
|
180
|
+
value_configs = zip(*value_lists)
|
|
181
|
+
configurations = [dict(zip(param_ids, values)) for values in value_configs]
|
|
182
|
+
|
|
183
|
+
elif isinstance(param_or_dict_or_configs, (list, tuple)):
|
|
184
|
+
configurations = param_or_dict_or_configs
|
|
185
|
+
# Validate that configurations is a list of dictionaries
|
|
186
|
+
if not all(isinstance(config, dict) for config in configurations):
|
|
187
|
+
raise ValueError("Configurations must be a list or tuple of dictionaries")
|
|
166
188
|
else:
|
|
167
|
-
raise ValueError("
|
|
189
|
+
raise ValueError("Invalid input for fork: expected str, dict, or list/tuple of dicts")
|
|
190
|
+
|
|
191
|
+
self.parent.sequence.append(Fork(configurations, condition))
|
|
168
192
|
|
|
169
|
-
def
|
|
170
|
-
"""
|
|
193
|
+
def evaluate(self, func: Callable[..., Any], assign_to: Optional[Union[str, List[str], Tuple[str, ...]]] = None, unpack_result: bool = False, condition: Optional[Callable[..., bool]] = None) -> None:
|
|
194
|
+
"""Evaluate a function and store its result in the ParameterSet.
|
|
171
195
|
|
|
172
196
|
Args:
|
|
173
|
-
func: Function to
|
|
197
|
+
func: Function to evaluate, taking parameter values as input.
|
|
174
198
|
assign_to: Custom Parameter ID(s) to store the result (str or list/tuple of strings).
|
|
175
199
|
unpack_result: If True, unpacks a dictionary result into multiple parameters.
|
|
176
200
|
condition: Optional condition function to determine if the step should run.
|
|
177
201
|
|
|
178
202
|
Examples:
|
|
179
|
-
>>> anal.add.
|
|
180
|
-
>>> anal.add.
|
|
181
|
-
>>> anal.add.
|
|
182
|
-
>>> anal.add.
|
|
203
|
+
>>> anal.add.evaluate(my_function) # Evaluate function, store result
|
|
204
|
+
>>> anal.add.evaluate(my_function, assign_to='new_param') # Evaluate function, store result to param: 'new_param'
|
|
205
|
+
>>> anal.add.evaluate(my_function_returns_dict, unpack_result=True) # Unpack dict result
|
|
206
|
+
>>> anal.add.evaluate(my_function_returns_dict, assign_to=['out_1', 'out_2'], unpack_result=True) # Unpack only dict keys: out_1, out_2
|
|
183
207
|
|
|
184
208
|
Raises:
|
|
185
209
|
ValueError: If func is not callable or assign_to is invalid.
|
|
186
210
|
"""
|
|
187
|
-
self.parent.sequence.append(
|
|
211
|
+
self.parent.sequence.append(Evaluate(func, assign_to, unpack_result, condition))
|
|
188
212
|
|
|
189
213
|
def optimize(self, func: Callable[..., float], opt_param_ids: Tuple[str, ...], assign_to: Optional[str] = None, direction: str = 'min', optimizer: Union[str, Callable] = 'SLSQP', tol: Optional[float] = None, bounds: Optional[Dict[str, Tuple[float, float]]] = None, options: Optional[Dict[str, Any]] = None, condition: Optional[Callable[..., bool]] = None, **kwargs) -> None:
|
|
190
214
|
"""Optimize a function over specified parameters.
|
|
@@ -214,7 +238,7 @@ class AnalysisModule():
|
|
|
214
238
|
# create_pandas = True,
|
|
215
239
|
# verbose = True
|
|
216
240
|
) -> Dict[str, Any]:
|
|
217
|
-
"""
|
|
241
|
+
"""Evaluate the analysis sequence and return results including a DataFrame of leaf data.
|
|
218
242
|
|
|
219
243
|
Returns:
|
|
220
244
|
Dict[str, Any]: Dictionary containing the results DataFrame, runtime, and number of finished leaves.
|
|
@@ -21,9 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
|
24
|
-
import logging
|
|
25
24
|
import copy
|
|
26
|
-
import warnings
|
|
27
25
|
import numpy as np
|
|
28
26
|
from scipy.optimize import minimize, dual_annealing, basinhopping, OptimizeResult
|
|
29
27
|
|
|
@@ -46,7 +44,7 @@ class AnalysisStep():
|
|
|
46
44
|
|
|
47
45
|
def run(self, paramset: ParameterSet) -> Tuple[ParameterSet, ...]:
|
|
48
46
|
"""
|
|
49
|
-
|
|
47
|
+
Evaluate the analysis step, returning a tuple of modified ParameterSets.
|
|
50
48
|
|
|
51
49
|
Parameters
|
|
52
50
|
----------
|
|
@@ -109,91 +107,79 @@ class Define(AnalysisStep):
|
|
|
109
107
|
|
|
110
108
|
class Fork(AnalysisStep):
|
|
111
109
|
"""
|
|
112
|
-
Fork AnalysisStep: Splits analysis into parallel branches based on provided
|
|
113
|
-
|
|
110
|
+
Fork AnalysisStep: Splits analysis into parallel branches based on provided parameter configurations.
|
|
111
|
+
|
|
112
|
+
Each configuration is a dictionary mapping parameter IDs to values. The analysis creates a new branch
|
|
113
|
+
for each configuration by updating the parameter set with the specified key-value pairs, adding new
|
|
114
|
+
parameters or overwriting existing ones as needed.
|
|
114
115
|
|
|
115
116
|
Parameters
|
|
116
117
|
----------
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
Values to assign to the parameter(s) in each branch. For a single parameter, provide a
|
|
121
|
-
tuple/list of values. For multiple parameters, provide a tuple/list of tuples, where each
|
|
122
|
-
inner tuple contains values for the corresponding parameters. The number of value sets must
|
|
123
|
-
match the number of param_ids, and all inner value sets must have the same length.
|
|
118
|
+
configurations : list of dict
|
|
119
|
+
List of parameter configurations, where each dictionary contains parameter IDs (str) as keys
|
|
120
|
+
and their corresponding values. All configurations must share the same set of parameter IDs.
|
|
124
121
|
condition : callable, optional
|
|
125
122
|
Condition function to determine if the step should run.
|
|
126
123
|
|
|
127
124
|
Raises
|
|
128
125
|
------
|
|
129
126
|
ValueError
|
|
130
|
-
If
|
|
131
|
-
|
|
127
|
+
If configurations is not a list of dictionaries, is empty, contains inconsistent parameter IDs,
|
|
128
|
+
or includes invalid parameter IDs.
|
|
132
129
|
TypeError
|
|
133
|
-
If condition is not callable
|
|
130
|
+
If condition is provided but is not callable.
|
|
134
131
|
|
|
135
132
|
Examples
|
|
136
133
|
--------
|
|
137
|
-
>>> fork = Fork('x',
|
|
138
|
-
>>> fork = Fork(
|
|
139
|
-
>>> fork = Fork(['x', 'y'], [(0, 1), (2, 3)]) # List input
|
|
134
|
+
>>> fork = Fork([{'x': 0}, {'x': 1}, {'x': 2}]) # Single parameter fork
|
|
135
|
+
>>> fork = Fork([{'x': 0, 'y': 10}, {'x': 1, 'y': 20}]) # Multi-parameter fork with configurations
|
|
140
136
|
"""
|
|
141
|
-
def __init__(self,
|
|
137
|
+
def __init__(self, configurations: List[Dict[str, Any]], condition: Callable[..., bool] | None = None):
|
|
142
138
|
super().__init__(condition)
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
for param_id in self.param_value_sets:
|
|
149
|
-
try:
|
|
150
|
-
Parameter.validate_id(param_id)
|
|
151
|
-
except ValueError as e:
|
|
152
|
-
raise ValueError(f"Invalid param_id '{param_id}': {str(e)}") from e
|
|
139
|
+
# Validate that configurations is a non-empty list of dictionaries
|
|
140
|
+
if not isinstance(configurations, (list, tuple)) or not all(isinstance(config, dict) for config in configurations):
|
|
141
|
+
raise ValueError("configurations must be a list or tuple of dictionaries")
|
|
142
|
+
if not configurations:
|
|
143
|
+
raise ValueError("configurations cannot be empty")
|
|
153
144
|
|
|
154
|
-
#
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
# TODO: Provide one level higher of error handling at the analysis level that can provide context like step index.
|
|
145
|
+
self.configurations = list(configurations) # Convert to list for consistency
|
|
146
|
+
|
|
147
|
+
# Validate parameter IDs and ensure consistency across configurations
|
|
148
|
+
# param_ids = set(self.configurations[0].keys())
|
|
149
|
+
for config in self.configurations:
|
|
150
|
+
# if set(config.keys()) != param_ids: # may turn on this optionally
|
|
151
|
+
# raise ValueError("All configurations must have the same parameter IDs")
|
|
152
|
+
for param_id in config:
|
|
153
|
+
try:
|
|
154
|
+
Parameter.validate_id(param_id)
|
|
155
|
+
except ValueError as e:
|
|
156
|
+
raise ValueError(f"Invalid param_id '{param_id}': {str(e)}") from e
|
|
167
157
|
|
|
168
158
|
def run(self, paramset: ParameterSet) -> Tuple[ParameterSet, ...]:
|
|
169
159
|
super().run(paramset)
|
|
170
|
-
if self.value_cnt == 0:
|
|
171
|
-
return (paramset,)
|
|
172
|
-
|
|
173
|
-
# Do forky stuff
|
|
174
160
|
next_paramsets = []
|
|
175
|
-
for
|
|
161
|
+
for config in self.configurations:
|
|
176
162
|
ps = copy.deepcopy(paramset)
|
|
177
|
-
for param_id,
|
|
178
|
-
ps.update_param(param_id,
|
|
163
|
+
for param_id, value in config.items():
|
|
164
|
+
ps.update_param(param_id, value) # Update or add parameter
|
|
179
165
|
next_paramsets.append(ps)
|
|
180
166
|
return tuple(next_paramsets)
|
|
181
|
-
|
|
167
|
+
|
|
182
168
|
def get_details(self) -> Dict[str, Any]:
|
|
183
169
|
details = super().get_details()
|
|
184
170
|
details.update({
|
|
185
|
-
'
|
|
171
|
+
'configurations': self.configurations
|
|
186
172
|
})
|
|
187
173
|
return details
|
|
188
174
|
|
|
189
|
-
class
|
|
175
|
+
class Evaluate(AnalysisStep):
|
|
190
176
|
"""
|
|
191
|
-
|
|
177
|
+
Evaluate AnalysisStep: Evaluates a provided function and updates the ParameterSet with results.
|
|
192
178
|
|
|
193
179
|
Parameters
|
|
194
180
|
----------
|
|
195
181
|
func : callable
|
|
196
|
-
The function to
|
|
182
|
+
The function to evaluate. Output can be any type if unpack_result=False and assign_to is a string,
|
|
197
183
|
or a dictionary if unpack_result=True.
|
|
198
184
|
assign_to : str or iterable of str, optional
|
|
199
185
|
Parameter ID(s) where the function output is stored. If a string and unpack_result=False, stores
|
|
@@ -212,14 +198,14 @@ class Execute(AnalysisStep):
|
|
|
212
198
|
>>> anal = AnalysisModule({'x': 0, 'y': 0})
|
|
213
199
|
>>> def add(x, y):
|
|
214
200
|
... return x + y
|
|
215
|
-
>>> anal.add.
|
|
216
|
-
>>> anal.add.
|
|
201
|
+
>>> anal.add.evaluate(func=add, assign_to='sum') # Stores scalar 0 in 'sum'
|
|
202
|
+
>>> anal.add.evaluate(func=add) # Stores scalar 0 in 'add'
|
|
217
203
|
>>> def dict_func(x, y):
|
|
218
204
|
... return {'sum': x + y, 'product': x * y}
|
|
219
|
-
>>> anal.add.
|
|
220
|
-
>>> anal.add.
|
|
221
|
-
>>> anal.add.
|
|
222
|
-
>>> anal.add.
|
|
205
|
+
>>> anal.add.evaluate(func=dict_func, assign_to='results') # Stores {'sum': 0, 'product': 0} in 'results'
|
|
206
|
+
>>> anal.add.evaluate(func=dict_func, assign_to=['sum', 'product'], unpack_result=True) # Stores sum=0, product=0
|
|
207
|
+
>>> anal.add.evaluate(func=dict_func, unpack_result=True) # Unpacks sum=0, product=0
|
|
208
|
+
>>> anal.add.evaluate(func=lambda x, y: [x, y], assign_to='list_result') # Stores [0, 0] in 'list_result'
|
|
223
209
|
"""
|
|
224
210
|
def __init__(self,
|
|
225
211
|
func: Callable,
|
|
@@ -231,7 +217,7 @@ class Execute(AnalysisStep):
|
|
|
231
217
|
|
|
232
218
|
# Validate func
|
|
233
219
|
if not isinstance(func, Callable):
|
|
234
|
-
raise ValueError("Invalid
|
|
220
|
+
raise ValueError("Invalid Evaluate: 'func' must be a callable")
|
|
235
221
|
self.func = func
|
|
236
222
|
|
|
237
223
|
# Handle assign_to
|
|
@@ -243,34 +229,34 @@ class Execute(AnalysisStep):
|
|
|
243
229
|
# Handle unpack_result
|
|
244
230
|
self.unpack_result = unpack_result if unpack_result is not None else False
|
|
245
231
|
if not isinstance(self.unpack_result, bool):
|
|
246
|
-
raise ValueError(f"Invalid
|
|
232
|
+
raise ValueError(f"Invalid Evaluate: 'unpack_result' must be a boolean, got {type(self.unpack_result)}")
|
|
247
233
|
|
|
248
234
|
# Validate assign_to
|
|
249
235
|
if isinstance(assign_to, str):
|
|
250
236
|
try:
|
|
251
237
|
Parameter.validate_id(assign_to)
|
|
252
238
|
except ValueError as e:
|
|
253
|
-
raise ValueError(f"Invalid
|
|
239
|
+
raise ValueError(f"Invalid Evaluate: 'assign_to' is not a valid param id, got {assign_to}: {str(e)}") from e
|
|
254
240
|
self.assign_to = assign_to
|
|
255
241
|
elif isinstance(assign_to, (list, tuple)) and assign_to:
|
|
256
242
|
try:
|
|
257
243
|
Parameter.validate_id_iterable(assign_to)
|
|
258
244
|
except ValueError as e:
|
|
259
|
-
raise ValueError(f"Invalid
|
|
245
|
+
raise ValueError(f"Invalid Evaluate: 'assign_to' must be a valid iterable of param ids, got {assign_to}: {str(e)}") from e
|
|
260
246
|
self.assign_to = tuple(assign_to)
|
|
261
247
|
else:
|
|
262
|
-
raise ValueError(f"Invalid
|
|
248
|
+
raise ValueError(f"Invalid Evaluate: 'assign_to' must be a string or non-empty iterable of strings, got {assign_to}")
|
|
263
249
|
|
|
264
250
|
# Error if unpack_result is False and assign_to is tuple/list
|
|
265
251
|
if not self.unpack_result and isinstance(self.assign_to, tuple):
|
|
266
252
|
raise ValueError(
|
|
267
|
-
f"Invalid
|
|
253
|
+
f"Invalid Evaluate: assign_to={self.assign_to} is a tuple/list, but unpack_result=False. "
|
|
268
254
|
f"Try setting unpack_result=True to explicitly indicate a dictionary output (which a tuple/list 'assign_to' expects. "
|
|
269
255
|
)
|
|
270
256
|
|
|
271
257
|
def run(self, paramset: ParameterSet) -> Tuple[ParameterSet, ...]:
|
|
272
258
|
"""
|
|
273
|
-
|
|
259
|
+
Evaluates the function and updates the ParameterSet with the output.
|
|
274
260
|
|
|
275
261
|
Parameters
|
|
276
262
|
----------
|
|
@@ -304,18 +290,18 @@ class Execute(AnalysisStep):
|
|
|
304
290
|
if self.unpack_result:
|
|
305
291
|
if not isinstance(output, dict):
|
|
306
292
|
raise ValueError(
|
|
307
|
-
f"
|
|
293
|
+
f"Evaluate: Expected dictionary output for unpack_result=True, "
|
|
308
294
|
f"got {type(output)} with value {output}"
|
|
309
295
|
)
|
|
310
296
|
if not output:
|
|
311
|
-
raise ValueError("
|
|
297
|
+
raise ValueError("Evaluate: Output dictionary is empty for unpack_result=True")
|
|
312
298
|
|
|
313
299
|
# Use specified keys if assign_to is a tuple/list
|
|
314
300
|
if isinstance(self.assign_to, tuple):
|
|
315
301
|
for key in self.assign_to:
|
|
316
302
|
if key not in output:
|
|
317
303
|
raise ValueError(
|
|
318
|
-
f"
|
|
304
|
+
f"Evaluate: Output dictionary missing key '{key}' for assign_to={self.assign_to}"
|
|
319
305
|
)
|
|
320
306
|
next_paramset.update_param(key, output[key])
|
|
321
307
|
|
|
@@ -325,7 +311,7 @@ class Execute(AnalysisStep):
|
|
|
325
311
|
try:
|
|
326
312
|
Parameter.validate_id(key)
|
|
327
313
|
except ValueError as e:
|
|
328
|
-
raise ValueError(f"
|
|
314
|
+
raise ValueError(f"Evaluate: Output key '{key}' is not a valid param id: {str(e)}") from e
|
|
329
315
|
next_paramset.update_param(key, value)
|
|
330
316
|
|
|
331
317
|
# Handle any output type (unpack_result=False, string assign_to)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: functioneer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Effortlessly explore function behavior with the ultimate batch runner.
|
|
5
5
|
Author-email: Quinn Marsh <quinnmarsh@hotmail.com>
|
|
6
6
|
Maintainer-email: Quinn Marsh <quinnmarsh@hotmail.com>
|
|
@@ -71,7 +71,7 @@ analysis = fn.AnalysisModule() # Create new analysis
|
|
|
71
71
|
analysis.add.define({'a': 1, 'b': 100}) # define a and b
|
|
72
72
|
analysis.add.fork('x', (0, 1, 2)) # Fork analysis, create branches for x=0, x=1, x=2
|
|
73
73
|
analysis.add.fork('y', (1, 10))
|
|
74
|
-
analysis.add.
|
|
74
|
+
analysis.add.evaluate(func=rosenbrock) #
|
|
75
75
|
results = analysis.run()
|
|
76
76
|
print('Example 1 Output:')
|
|
77
77
|
print(results['df'][['a', 'b', 'x', 'y', 'rosenbrock']])
|
|
@@ -132,7 +132,7 @@ At its core, functioneer organizes analyses as a tree where a *set of parameters
|
|
|
132
132
|
Summary of most useful types of *analysis steps*:
|
|
133
133
|
- Define: Adds a new parameter to the analysis
|
|
134
134
|
- Fork: Splits the analysis into multiple parallel *branches*, each exploring different values for a specific parameter
|
|
135
|
-
-
|
|
135
|
+
- Evaluate: Calls a provided function using the parameters
|
|
136
136
|
- Optimize: Quickly set up an optimization by providing a function and defining which parameters are going to be optimized
|
|
137
137
|
|
|
138
138
|
<details>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|