functioneer 0.2.1__tar.gz → 0.3.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.2.1/src/functioneer.egg-info → functioneer-0.3.0}/PKG-INFO +60 -46
- {functioneer-0.2.1 → functioneer-0.3.0}/README.md +54 -41
- {functioneer-0.2.1 → functioneer-0.3.0}/pyproject.toml +4 -4
- {functioneer-0.2.1 → functioneer-0.3.0}/src/functioneer/__init__.py +1 -3
- functioneer-0.3.0/src/functioneer/analysis.py +372 -0
- {functioneer-0.2.1 → functioneer-0.3.0}/src/functioneer/parameter.py +52 -7
- functioneer-0.3.0/src/functioneer/steps.py +669 -0
- {functioneer-0.2.1 → functioneer-0.3.0/src/functioneer.egg-info}/PKG-INFO +60 -46
- {functioneer-0.2.1 → functioneer-0.3.0}/src/functioneer.egg-info/SOURCES.txt +1 -0
- functioneer-0.2.1/src/functioneer/analysis.py +0 -503
- {functioneer-0.2.1 → functioneer-0.3.0}/LICENSE +0 -0
- {functioneer-0.2.1 → functioneer-0.3.0}/setup.cfg +0 -0
- {functioneer-0.2.1 → functioneer-0.3.0}/src/functioneer/util.py +0 -0
- {functioneer-0.2.1 → functioneer-0.3.0}/src/functioneer.egg-info/dependency_links.txt +0 -0
- {functioneer-0.2.1 → functioneer-0.3.0}/src/functioneer.egg-info/requires.txt +0 -0
- {functioneer-0.2.1 → functioneer-0.3.0}/src/functioneer.egg-info/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: functioneer
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Effortlessly explore function behavior with automated batch analysis.
|
|
5
5
|
Author-email: Quinn Marsh <quinnmarsh@hotmail.com>
|
|
6
6
|
Maintainer-email: Quinn Marsh <quinnmarsh@hotmail.com>
|
|
7
7
|
License: MIT
|
|
@@ -9,8 +9,8 @@ Project-URL: Homepage, https://github.com/qthedoc/functioneer
|
|
|
9
9
|
Project-URL: Issues, https://github.com/qthedoc/functioneer/issues
|
|
10
10
|
Project-URL: Funding, https://donate.pypi.org
|
|
11
11
|
Project-URL: Say Thanks!, http://quinnmarsh.com
|
|
12
|
-
Keywords: functioneer,analysis,automation,autorun,trade space,digital twin
|
|
13
|
-
Classifier: Development Status ::
|
|
12
|
+
Keywords: functioneer,analysis,batch run,automation,autorun,trade space,digital twin
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
14
|
Classifier: Intended Audience :: Science/Research
|
|
15
15
|
Classifier: Topic :: Scientific/Engineering
|
|
16
16
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -27,13 +27,15 @@ License-File: LICENSE
|
|
|
27
27
|
Requires-Dist: numpy>=1.18.5
|
|
28
28
|
Requires-Dist: scipy>=1.5.2
|
|
29
29
|
Requires-Dist: pandas>=1.0.5
|
|
30
|
+
Dynamic: license-file
|
|
30
31
|
|
|
31
32
|
# Functioneer
|
|
32
33
|
|
|
33
34
|
**Author**: Quinn Marsh
|
|
34
|
-
**Date**: February 02, 2025
|
|
35
|
+
**Date**: February 02, 2025\
|
|
36
|
+
**PyPI**: https://pypi.org/project/functioneer/
|
|
35
37
|
|
|
36
|
-
Functioneer
|
|
38
|
+
Functioneer lets you effortlessly explore function behavior with automated batch analysis. With just a few lines of code, you can queue up thousands or even millions of function evaluations, with various parameter combinations and/or optimizations. Retrieve structured results in formats like pandas for seamless integration into your workflows. Perfect for parameter sweeps, engineering simulations, and digital twin optimization.
|
|
37
39
|
|
|
38
40
|
## Use cases
|
|
39
41
|
|
|
@@ -43,9 +45,19 @@ Functioneer is a Python package that automates the analysis of ANY function, ena
|
|
|
43
45
|
|
|
44
46
|
## How Functioneer Works
|
|
45
47
|
|
|
46
|
-
|
|
48
|
+
At its core, functioneer organizes analyses as tree where a *set of parameters* starts at the trunk and moves out towards the leaves. Along the way, the *set of parameters* 'flows' through a series of *analysis steps* (each of which can be defined in a single line of code). Each *analysis step* can modify or use the parameters in various ways, such as defining new parameters, modifying parameters, or using the parameters to evaluate or even optimize any function of your choice. One key feature of functioneer is the ability to introduce *forks*: a type of analysis step that splits the analysis into multiple parallel *branches*, each exploring different values for a specific parameter. Using many *Forks* in series allows you to queue up thousands or even millions of parameter combinations with only a few lines of code. This structured approach enables highly flexible and dynamic analyses, suitable for a wide range of applications.
|
|
49
|
+
|
|
50
|
+
Summary of most useful types of *analysis steps*:
|
|
51
|
+
- Define: Adds a new parameter to the analysis
|
|
52
|
+
- Fork: Splits the analysis into multiple parallel *branches*, each exploring different values for a specific parameter
|
|
53
|
+
- Execute: Calls a provided function using the parameters
|
|
54
|
+
- Optimize: Quickly set up an optimization by providing a function and defining which parameters are going to be optimized
|
|
55
|
+
|
|
56
|
+
<details>
|
|
57
|
+
<summary>
|
|
58
|
+
<span style="font-size:1.5em;">Important Terms</span>
|
|
59
|
+
</summary>
|
|
47
60
|
|
|
48
|
-
### Terms
|
|
49
61
|
* AnalysisModule
|
|
50
62
|
* Definition: The central container for an analysis pipeline.
|
|
51
63
|
* Function: Holds a sequence of analysis steps and manages a set of parameters that flow through the pipeline.
|
|
@@ -69,6 +81,7 @@ Functioneer is a powerful system for defining and executing complex analysis pip
|
|
|
69
81
|
* Leaf
|
|
70
82
|
* Definition: The endpoint of a branch after all analysis steps have been executed.
|
|
71
83
|
* Function: Represents the final state of parameters for that branch. Each leaf corresponds to a specific combination of parameter values and results. When results are tabulated, each row corresponds to a leaf.
|
|
84
|
+
</details>
|
|
72
85
|
|
|
73
86
|
## Installation
|
|
74
87
|
|
|
@@ -81,17 +94,19 @@ pip install functioneer
|
|
|
81
94
|
## Getting Started
|
|
82
95
|
Below are a few quick examples of how to use Functioneer. Each example will build on the last, introducing one piece of functionality. By the end you will have witnessed the computational power of this fully armed and fully operational library.
|
|
83
96
|
|
|
84
|
-
###
|
|
85
|
-
Functioneer is designed to analyze ANY function(s) with ANY number of inputs and outputs. For the following examples, the [Rosenbrock Function](https://en.wikipedia.org/wiki/Rosenbrock_function)
|
|
97
|
+
### Choose a Function to Analyze
|
|
98
|
+
Functioneer is designed to analyze ANY function(s) with ANY number of inputs and outputs. For the following examples, we use the [Rosenbrock Function](https://en.wikipedia.org/wiki/Rosenbrock_function) for (1) its relative simplicity, (2) 4 inputs (plenty to play with) and (3) its historical significance as an optimization benchmark.
|
|
86
99
|
|
|
87
100
|
```
|
|
101
|
+
# Example Function
|
|
88
102
|
# Rosenbrock function (known minimum of 0 at: x=1, y=1, a=1, b=100)
|
|
89
103
|
def rosenbrock(x, y, a, b):
|
|
90
104
|
return (a-x)**2 + b*(y-x**2)**2
|
|
91
105
|
```
|
|
92
106
|
|
|
93
107
|
### Example 1: The Basics (Defining Parameters and Executing a Function)
|
|
94
|
-
Set up an *analysis sequence* by defining four parameters
|
|
108
|
+
Set up an *analysis sequence* by defining four parameters to match our function, then executing the function
|
|
109
|
+
Note: Parameter IDs MUST match your function's args, function executions inside functioneer are fully keyword arg based.
|
|
95
110
|
|
|
96
111
|
```
|
|
97
112
|
import functioneer as fn
|
|
@@ -105,7 +120,7 @@ anal.add.define('b', 100) # Define parameter 'b'
|
|
|
105
120
|
anal.add.define('x', 1) # Define parameter 'x'
|
|
106
121
|
anal.add.define('y', 1) # Define parameter 'y'
|
|
107
122
|
|
|
108
|
-
anal.add.execute(func=rosenbrock
|
|
123
|
+
anal.add.execute(func=rosenbrock) # Execute function with parameter ids matched to kwargs
|
|
109
124
|
|
|
110
125
|
# Run the analysis sequence
|
|
111
126
|
results = anal.run()
|
|
@@ -115,32 +130,33 @@ print(results['df'])
|
|
|
115
130
|
|
|
116
131
|
```
|
|
117
132
|
Output:
|
|
118
|
-
runtime a b x y
|
|
119
|
-
0 0.0 1 100 1 1
|
|
133
|
+
runtime a b x y rosenbrock datetime
|
|
134
|
+
0 0.0 1 100 1 1 0 2025-06-24 03:26:48.842824
|
|
120
135
|
```
|
|
121
136
|
|
|
122
|
-
As
|
|
123
|
-
|
|
124
|
-
Note: the `results['df']` is a pandas DataFrame containing all parameters in addition to *runtime* and *datetime* fields
|
|
137
|
+
As we expect, the `rosenbrock` parameter evaluates to 0 when a=1, b=100, x=1, y=1
|
|
125
138
|
|
|
126
|
-
|
|
139
|
+
Note: the `results['df']` is a pandas DataFrame containing all parameters in addition to *runtime* and *datetime* for the given branch
|
|
127
140
|
|
|
128
141
|
### Example 2: Single Parameter Forks (Testing Variations of a Parameter)
|
|
142
|
+
Let's say you want to test a range of values for some parameters...
|
|
129
143
|
If you want to test a set of values for a parameter you can create a *fork* in the *analysis sequence*. This splits the analysis into multiple *branches*, each exploring different values for a the given parameter.
|
|
130
144
|
|
|
131
|
-
Say we want to evaluate and plot the Rosenbrock surface over the x-y domain. Let's evaluate Rosenbrock a grid where x=(0, 1, 2) and y=(1, 10) which should result in 6 final *branches* / *leaves*...
|
|
145
|
+
Say we want to evaluate and plot the Rosenbrock surface over the x-y domain. Let's evaluate Rosenbrock on a grid where x=(0, 1, 2) and y=(1, 10) which should result in 6 final *branches* / *leaves*...
|
|
132
146
|
|
|
147
|
+
Note: the parameter's name is by default set from the function name, but can be overridden using the 'assign_to' arg.
|
|
133
148
|
Note: some boiler plate can be removed by defining initial parameters in the AnalysisModule() declaration
|
|
149
|
+
Note: initial parameter values will be overwritten as needed by parameter steps
|
|
134
150
|
```
|
|
135
151
|
# Create new analysis
|
|
136
|
-
init_params = dict(a=1, b=100, x=1, y=1) # initial parameters
|
|
152
|
+
init_params = dict(a=1, b=100, x=1, y=1) # define initial parameters
|
|
137
153
|
anal = fn.AnalysisModule(init_params)
|
|
138
154
|
|
|
139
155
|
# Define analysis sequence
|
|
140
|
-
anal.add.fork('x',
|
|
141
|
-
anal.add.fork('y',
|
|
156
|
+
anal.add.fork('x', value_set=(0, 1, 2)) # Fork analysis, create a branch for each value of 'x': 0, 1, 2
|
|
157
|
+
anal.add.fork('y', value_set=(1, 10)) # Fork analysis, create a branch for each value of 'y': 1, 10
|
|
142
158
|
|
|
143
|
-
anal.add.execute(func=rosenbrock,
|
|
159
|
+
anal.add.execute(func=rosenbrock, assign_to='brock_purdy') # Execute function (for each branch) with parameters matched to kwargs
|
|
144
160
|
|
|
145
161
|
# Run the analysis sequence
|
|
146
162
|
results = anal.run()
|
|
@@ -148,13 +164,13 @@ print(results['df'].drop(columns='datetime'))
|
|
|
148
164
|
```
|
|
149
165
|
```
|
|
150
166
|
Output:
|
|
151
|
-
runtime a b x y
|
|
152
|
-
0 0.
|
|
153
|
-
1 0.
|
|
154
|
-
2 0.
|
|
155
|
-
3 0.
|
|
156
|
-
4 0.
|
|
157
|
-
5 0.
|
|
167
|
+
runtime a b x y brock_purdy
|
|
168
|
+
0 0.001294 1 100 0 1 101
|
|
169
|
+
1 0.000000 1 100 0 10 10001
|
|
170
|
+
2 0.000000 1 100 1 1 0
|
|
171
|
+
3 0.000000 1 100 1 10 8100
|
|
172
|
+
4 0.000000 1 100 2 1 901
|
|
173
|
+
5 0.000000 1 100 2 10 3601
|
|
158
174
|
```
|
|
159
175
|
The parameters `x` and `y` were given 3 and 2 fork values respectively, this created 6 total *leaves* (end of each branch) in the analysis. `rosen` has been evaluated for each *leaf*. Essentially you have begun to map the Rosenbrock function over the x-y domain.
|
|
160
176
|
|
|
@@ -168,7 +184,7 @@ anal = fn.AnalysisModule(dict(x=0, y=0))
|
|
|
168
184
|
anal.add.fork('a', value_set=(1, 2)) # Fork analysis, create a branch for each value of 'a': 0, 1, 2
|
|
169
185
|
anal.add.fork('b', value_set=(0, 100, 200)) # Fork analysis, create a branch for each value of 'b': 0, 100, 200
|
|
170
186
|
|
|
171
|
-
anal.add.optimize(func=rosenbrock,
|
|
187
|
+
anal.add.optimize(func=rosenbrock, opt_param_ids=('x', 'y'))
|
|
172
188
|
|
|
173
189
|
# Run the analysis sequence
|
|
174
190
|
results = anal.run()
|
|
@@ -186,8 +202,7 @@ Output:
|
|
|
186
202
|
```
|
|
187
203
|
For each branch, the Rosenbrock Function has been minimized and the solution values for `x`, `y` and `rosen` are shown.
|
|
188
204
|
|
|
189
|
-
Note: the initial values used in the optimization are
|
|
190
|
-
|
|
205
|
+
Note: the initial values (`x0`) used in the optimization are simply the existing parameter values (in this case x and y are 0) going into the optimization step.
|
|
191
206
|
Note: due to optimization the runtimes for some of the analyses have gone up.
|
|
192
207
|
|
|
193
208
|
### Example 4: Multi-parameter Forks
|
|
@@ -198,8 +213,7 @@ anal = fn.AnalysisModule(dict(a=1, b=100))
|
|
|
198
213
|
|
|
199
214
|
# Define analysis sequence
|
|
200
215
|
anal.add.fork.multi(('x', 'y'), value_sets=((0, 1, 2), (0, 10, 20))) # Fork analysis, create a branch for each value of 'y': 1, 10
|
|
201
|
-
|
|
202
|
-
anal.add.execute(func=rosenbrock, output_param_ids='rosen') # Execute function (for each branch) with parameters matched to kwargs
|
|
216
|
+
anal.add.execute(func=rosenbrock) # Execute function (for each branch) with parameters matched to kwargs
|
|
203
217
|
|
|
204
218
|
# Run the analysis sequence
|
|
205
219
|
results = anal.run()
|
|
@@ -225,7 +239,7 @@ anal = fn.AnalysisModule(dict(x=0, y=0))
|
|
|
225
239
|
# Define analysis sequence
|
|
226
240
|
anal.add.fork('a', value_set=(1, 2))
|
|
227
241
|
anal.add.fork('b', value_set=(0, 100, 200))
|
|
228
|
-
anal.add.optimize(func=rosenbrock,
|
|
242
|
+
anal.add.optimize(func=rosenbrock, opt_param_ids=('x', 'y'))
|
|
229
243
|
|
|
230
244
|
# Only evaluate 'expensive_func' if the optimized 'y' is above 0.5
|
|
231
245
|
expensive_func = lambda x, y: x+y
|
|
@@ -236,15 +250,15 @@ print(results['df'].drop(columns='datetime'))
|
|
|
236
250
|
```
|
|
237
251
|
```
|
|
238
252
|
Output:
|
|
239
|
-
runtime
|
|
240
|
-
0 0.
|
|
241
|
-
1 0.
|
|
242
|
-
2 0.
|
|
243
|
-
3 0.
|
|
244
|
-
4 0.
|
|
245
|
-
5 0.
|
|
246
|
-
```
|
|
247
|
-
Notice how the evaluation of `expensive_param` has been skipped where the optimized `y` did not meet
|
|
253
|
+
runtime x y a b rosenbrock expensive_param
|
|
254
|
+
0 0.001997 1.000000 0.000000 1 0 4.930381e-32 NaN
|
|
255
|
+
1 0.004206 0.999763 0.999523 1 100 5.772481e-08 1.999286
|
|
256
|
+
2 0.012199 0.999939 0.999873 1 200 8.146869e-09 1.999811
|
|
257
|
+
3 0.000965 2.000000 0.000000 2 0 0.000000e+00 NaN
|
|
258
|
+
4 0.010121 1.999731 3.998866 2 100 4.067518e-07 5.998596
|
|
259
|
+
5 0.012308 1.999554 3.998225 2 200 2.136755e-07 5.997779
|
|
260
|
+
```
|
|
261
|
+
Notice how the evaluation of `expensive_param` has been skipped where the optimized `y` did not meet our criteria `y>0.5`
|
|
248
262
|
|
|
249
263
|
## License
|
|
250
264
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# Functioneer
|
|
2
2
|
|
|
3
3
|
**Author**: Quinn Marsh
|
|
4
|
-
**Date**: February 02, 2025
|
|
4
|
+
**Date**: February 02, 2025\
|
|
5
|
+
**PyPI**: https://pypi.org/project/functioneer/
|
|
5
6
|
|
|
6
|
-
Functioneer
|
|
7
|
+
Functioneer lets you effortlessly explore function behavior with automated batch analysis. With just a few lines of code, you can queue up thousands or even millions of function evaluations, with various parameter combinations and/or optimizations. Retrieve structured results in formats like pandas for seamless integration into your workflows. Perfect for parameter sweeps, engineering simulations, and digital twin optimization.
|
|
7
8
|
|
|
8
9
|
## Use cases
|
|
9
10
|
|
|
@@ -13,9 +14,19 @@ Functioneer is a Python package that automates the analysis of ANY function, ena
|
|
|
13
14
|
|
|
14
15
|
## How Functioneer Works
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
At its core, functioneer organizes analyses as tree where a *set of parameters* starts at the trunk and moves out towards the leaves. Along the way, the *set of parameters* 'flows' through a series of *analysis steps* (each of which can be defined in a single line of code). Each *analysis step* can modify or use the parameters in various ways, such as defining new parameters, modifying parameters, or using the parameters to evaluate or even optimize any function of your choice. One key feature of functioneer is the ability to introduce *forks*: a type of analysis step that splits the analysis into multiple parallel *branches*, each exploring different values for a specific parameter. Using many *Forks* in series allows you to queue up thousands or even millions of parameter combinations with only a few lines of code. This structured approach enables highly flexible and dynamic analyses, suitable for a wide range of applications.
|
|
18
|
+
|
|
19
|
+
Summary of most useful types of *analysis steps*:
|
|
20
|
+
- Define: Adds a new parameter to the analysis
|
|
21
|
+
- Fork: Splits the analysis into multiple parallel *branches*, each exploring different values for a specific parameter
|
|
22
|
+
- Execute: Calls a provided function using the parameters
|
|
23
|
+
- Optimize: Quickly set up an optimization by providing a function and defining which parameters are going to be optimized
|
|
24
|
+
|
|
25
|
+
<details>
|
|
26
|
+
<summary>
|
|
27
|
+
<span style="font-size:1.5em;">Important Terms</span>
|
|
28
|
+
</summary>
|
|
17
29
|
|
|
18
|
-
### Terms
|
|
19
30
|
* AnalysisModule
|
|
20
31
|
* Definition: The central container for an analysis pipeline.
|
|
21
32
|
* Function: Holds a sequence of analysis steps and manages a set of parameters that flow through the pipeline.
|
|
@@ -39,6 +50,7 @@ Functioneer is a powerful system for defining and executing complex analysis pip
|
|
|
39
50
|
* Leaf
|
|
40
51
|
* Definition: The endpoint of a branch after all analysis steps have been executed.
|
|
41
52
|
* Function: Represents the final state of parameters for that branch. Each leaf corresponds to a specific combination of parameter values and results. When results are tabulated, each row corresponds to a leaf.
|
|
53
|
+
</details>
|
|
42
54
|
|
|
43
55
|
## Installation
|
|
44
56
|
|
|
@@ -51,17 +63,19 @@ pip install functioneer
|
|
|
51
63
|
## Getting Started
|
|
52
64
|
Below are a few quick examples of how to use Functioneer. Each example will build on the last, introducing one piece of functionality. By the end you will have witnessed the computational power of this fully armed and fully operational library.
|
|
53
65
|
|
|
54
|
-
###
|
|
55
|
-
Functioneer is designed to analyze ANY function(s) with ANY number of inputs and outputs. For the following examples, the [Rosenbrock Function](https://en.wikipedia.org/wiki/Rosenbrock_function)
|
|
66
|
+
### Choose a Function to Analyze
|
|
67
|
+
Functioneer is designed to analyze ANY function(s) with ANY number of inputs and outputs. For the following examples, we use the [Rosenbrock Function](https://en.wikipedia.org/wiki/Rosenbrock_function) for (1) its relative simplicity, (2) 4 inputs (plenty to play with) and (3) its historical significance as an optimization benchmark.
|
|
56
68
|
|
|
57
69
|
```
|
|
70
|
+
# Example Function
|
|
58
71
|
# Rosenbrock function (known minimum of 0 at: x=1, y=1, a=1, b=100)
|
|
59
72
|
def rosenbrock(x, y, a, b):
|
|
60
73
|
return (a-x)**2 + b*(y-x**2)**2
|
|
61
74
|
```
|
|
62
75
|
|
|
63
76
|
### Example 1: The Basics (Defining Parameters and Executing a Function)
|
|
64
|
-
Set up an *analysis sequence* by defining four parameters
|
|
77
|
+
Set up an *analysis sequence* by defining four parameters to match our function, then executing the function
|
|
78
|
+
Note: Parameter IDs MUST match your function's args, function executions inside functioneer are fully keyword arg based.
|
|
65
79
|
|
|
66
80
|
```
|
|
67
81
|
import functioneer as fn
|
|
@@ -75,7 +89,7 @@ anal.add.define('b', 100) # Define parameter 'b'
|
|
|
75
89
|
anal.add.define('x', 1) # Define parameter 'x'
|
|
76
90
|
anal.add.define('y', 1) # Define parameter 'y'
|
|
77
91
|
|
|
78
|
-
anal.add.execute(func=rosenbrock
|
|
92
|
+
anal.add.execute(func=rosenbrock) # Execute function with parameter ids matched to kwargs
|
|
79
93
|
|
|
80
94
|
# Run the analysis sequence
|
|
81
95
|
results = anal.run()
|
|
@@ -85,32 +99,33 @@ print(results['df'])
|
|
|
85
99
|
|
|
86
100
|
```
|
|
87
101
|
Output:
|
|
88
|
-
runtime a b x y
|
|
89
|
-
0 0.0 1 100 1 1
|
|
102
|
+
runtime a b x y rosenbrock datetime
|
|
103
|
+
0 0.0 1 100 1 1 0 2025-06-24 03:26:48.842824
|
|
90
104
|
```
|
|
91
105
|
|
|
92
|
-
As
|
|
93
|
-
|
|
94
|
-
Note: the `results['df']` is a pandas DataFrame containing all parameters in addition to *runtime* and *datetime* fields
|
|
106
|
+
As we expect, the `rosenbrock` parameter evaluates to 0 when a=1, b=100, x=1, y=1
|
|
95
107
|
|
|
96
|
-
|
|
108
|
+
Note: the `results['df']` is a pandas DataFrame containing all parameters in addition to *runtime* and *datetime* for the given branch
|
|
97
109
|
|
|
98
110
|
### Example 2: Single Parameter Forks (Testing Variations of a Parameter)
|
|
111
|
+
Let's say you want to test a range of values for some parameters...
|
|
99
112
|
If you want to test a set of values for a parameter you can create a *fork* in the *analysis sequence*. This splits the analysis into multiple *branches*, each exploring different values for a the given parameter.
|
|
100
113
|
|
|
101
|
-
Say we want to evaluate and plot the Rosenbrock surface over the x-y domain. Let's evaluate Rosenbrock a grid where x=(0, 1, 2) and y=(1, 10) which should result in 6 final *branches* / *leaves*...
|
|
114
|
+
Say we want to evaluate and plot the Rosenbrock surface over the x-y domain. Let's evaluate Rosenbrock on a grid where x=(0, 1, 2) and y=(1, 10) which should result in 6 final *branches* / *leaves*...
|
|
102
115
|
|
|
116
|
+
Note: the parameter's name is by default set from the function name, but can be overridden using the 'assign_to' arg.
|
|
103
117
|
Note: some boiler plate can be removed by defining initial parameters in the AnalysisModule() declaration
|
|
118
|
+
Note: initial parameter values will be overwritten as needed by parameter steps
|
|
104
119
|
```
|
|
105
120
|
# Create new analysis
|
|
106
|
-
init_params = dict(a=1, b=100, x=1, y=1) # initial parameters
|
|
121
|
+
init_params = dict(a=1, b=100, x=1, y=1) # define initial parameters
|
|
107
122
|
anal = fn.AnalysisModule(init_params)
|
|
108
123
|
|
|
109
124
|
# Define analysis sequence
|
|
110
|
-
anal.add.fork('x',
|
|
111
|
-
anal.add.fork('y',
|
|
125
|
+
anal.add.fork('x', value_set=(0, 1, 2)) # Fork analysis, create a branch for each value of 'x': 0, 1, 2
|
|
126
|
+
anal.add.fork('y', value_set=(1, 10)) # Fork analysis, create a branch for each value of 'y': 1, 10
|
|
112
127
|
|
|
113
|
-
anal.add.execute(func=rosenbrock,
|
|
128
|
+
anal.add.execute(func=rosenbrock, assign_to='brock_purdy') # Execute function (for each branch) with parameters matched to kwargs
|
|
114
129
|
|
|
115
130
|
# Run the analysis sequence
|
|
116
131
|
results = anal.run()
|
|
@@ -118,13 +133,13 @@ print(results['df'].drop(columns='datetime'))
|
|
|
118
133
|
```
|
|
119
134
|
```
|
|
120
135
|
Output:
|
|
121
|
-
runtime a b x y
|
|
122
|
-
0 0.
|
|
123
|
-
1 0.
|
|
124
|
-
2 0.
|
|
125
|
-
3 0.
|
|
126
|
-
4 0.
|
|
127
|
-
5 0.
|
|
136
|
+
runtime a b x y brock_purdy
|
|
137
|
+
0 0.001294 1 100 0 1 101
|
|
138
|
+
1 0.000000 1 100 0 10 10001
|
|
139
|
+
2 0.000000 1 100 1 1 0
|
|
140
|
+
3 0.000000 1 100 1 10 8100
|
|
141
|
+
4 0.000000 1 100 2 1 901
|
|
142
|
+
5 0.000000 1 100 2 10 3601
|
|
128
143
|
```
|
|
129
144
|
The parameters `x` and `y` were given 3 and 2 fork values respectively, this created 6 total *leaves* (end of each branch) in the analysis. `rosen` has been evaluated for each *leaf*. Essentially you have begun to map the Rosenbrock function over the x-y domain.
|
|
130
145
|
|
|
@@ -138,7 +153,7 @@ anal = fn.AnalysisModule(dict(x=0, y=0))
|
|
|
138
153
|
anal.add.fork('a', value_set=(1, 2)) # Fork analysis, create a branch for each value of 'a': 0, 1, 2
|
|
139
154
|
anal.add.fork('b', value_set=(0, 100, 200)) # Fork analysis, create a branch for each value of 'b': 0, 100, 200
|
|
140
155
|
|
|
141
|
-
anal.add.optimize(func=rosenbrock,
|
|
156
|
+
anal.add.optimize(func=rosenbrock, opt_param_ids=('x', 'y'))
|
|
142
157
|
|
|
143
158
|
# Run the analysis sequence
|
|
144
159
|
results = anal.run()
|
|
@@ -156,8 +171,7 @@ Output:
|
|
|
156
171
|
```
|
|
157
172
|
For each branch, the Rosenbrock Function has been minimized and the solution values for `x`, `y` and `rosen` are shown.
|
|
158
173
|
|
|
159
|
-
Note: the initial values used in the optimization are
|
|
160
|
-
|
|
174
|
+
Note: the initial values (`x0`) used in the optimization are simply the existing parameter values (in this case x and y are 0) going into the optimization step.
|
|
161
175
|
Note: due to optimization the runtimes for some of the analyses have gone up.
|
|
162
176
|
|
|
163
177
|
### Example 4: Multi-parameter Forks
|
|
@@ -168,8 +182,7 @@ anal = fn.AnalysisModule(dict(a=1, b=100))
|
|
|
168
182
|
|
|
169
183
|
# Define analysis sequence
|
|
170
184
|
anal.add.fork.multi(('x', 'y'), value_sets=((0, 1, 2), (0, 10, 20))) # Fork analysis, create a branch for each value of 'y': 1, 10
|
|
171
|
-
|
|
172
|
-
anal.add.execute(func=rosenbrock, output_param_ids='rosen') # Execute function (for each branch) with parameters matched to kwargs
|
|
185
|
+
anal.add.execute(func=rosenbrock) # Execute function (for each branch) with parameters matched to kwargs
|
|
173
186
|
|
|
174
187
|
# Run the analysis sequence
|
|
175
188
|
results = anal.run()
|
|
@@ -195,7 +208,7 @@ anal = fn.AnalysisModule(dict(x=0, y=0))
|
|
|
195
208
|
# Define analysis sequence
|
|
196
209
|
anal.add.fork('a', value_set=(1, 2))
|
|
197
210
|
anal.add.fork('b', value_set=(0, 100, 200))
|
|
198
|
-
anal.add.optimize(func=rosenbrock,
|
|
211
|
+
anal.add.optimize(func=rosenbrock, opt_param_ids=('x', 'y'))
|
|
199
212
|
|
|
200
213
|
# Only evaluate 'expensive_func' if the optimized 'y' is above 0.5
|
|
201
214
|
expensive_func = lambda x, y: x+y
|
|
@@ -206,15 +219,15 @@ print(results['df'].drop(columns='datetime'))
|
|
|
206
219
|
```
|
|
207
220
|
```
|
|
208
221
|
Output:
|
|
209
|
-
runtime
|
|
210
|
-
0 0.
|
|
211
|
-
1 0.
|
|
212
|
-
2 0.
|
|
213
|
-
3 0.
|
|
214
|
-
4 0.
|
|
215
|
-
5 0.
|
|
216
|
-
```
|
|
217
|
-
Notice how the evaluation of `expensive_param` has been skipped where the optimized `y` did not meet
|
|
222
|
+
runtime x y a b rosenbrock expensive_param
|
|
223
|
+
0 0.001997 1.000000 0.000000 1 0 4.930381e-32 NaN
|
|
224
|
+
1 0.004206 0.999763 0.999523 1 100 5.772481e-08 1.999286
|
|
225
|
+
2 0.012199 0.999939 0.999873 1 200 8.146869e-09 1.999811
|
|
226
|
+
3 0.000965 2.000000 0.000000 2 0 0.000000e+00 NaN
|
|
227
|
+
4 0.010121 1.999731 3.998866 2 100 4.067518e-07 5.998596
|
|
228
|
+
5 0.012308 1.999554 3.998225 2 200 2.136755e-07 5.997779
|
|
229
|
+
```
|
|
230
|
+
Notice how the evaluation of `expensive_param` has been skipped where the optimized `y` did not meet our criteria `y>0.5`
|
|
218
231
|
|
|
219
232
|
## License
|
|
220
233
|
|
|
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "functioneer"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
authors = [{ name = "Quinn Marsh", email = "quinnmarsh@hotmail.com" }]
|
|
9
9
|
maintainers = [{ name = "Quinn Marsh", email = "quinnmarsh@hotmail.com" }]
|
|
10
|
-
description = "
|
|
10
|
+
description = "Effortlessly explore function behavior with automated batch analysis."
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
license = { text = "MIT" }
|
|
13
|
-
keywords = ["functioneer", "analysis", "automation", "autorun", "trade space", "digital twin"]
|
|
13
|
+
keywords = ["functioneer", "analysis", "batch run", "automation", "autorun", "trade space", "digital twin"]
|
|
14
14
|
requires-python = ">=3.7"
|
|
15
15
|
dependencies = [
|
|
16
16
|
"numpy>=1.18.5",
|
|
@@ -19,7 +19,7 @@ dependencies = [
|
|
|
19
19
|
]
|
|
20
20
|
|
|
21
21
|
classifiers = [
|
|
22
|
-
"Development Status ::
|
|
22
|
+
"Development Status :: 4 - Beta",
|
|
23
23
|
"Intended Audience :: Science/Research",
|
|
24
24
|
"Topic :: Scientific/Engineering",
|
|
25
25
|
"License :: OSI Approved :: MIT License",
|