Skip to content

Commit be2ef26

Browse files
committed
.
0 parents  commit be2ef26

8 files changed

Lines changed: 1335 additions & 0 deletions

File tree

.fz/algorithms/Algorithm.py

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
#title: Random Sampling Algorithm (Template)
2+
#author: Funz Contributors
3+
#type: sampling
4+
#options: nvalues=10;seed=42
5+
6+
"""
7+
Template algorithm for fz plugin system.
8+
9+
This is a minimal algorithm that performs random sampling.
10+
Customize it to implement your own algorithm.
11+
12+
Algorithm Interface:
13+
__init__(**options): Constructor with algorithm-specific options
14+
get_initial_design(input_vars, output_vars): Return initial design of experiments
15+
get_next_design(X, Y): Return next design based on previous results, or [] when finished
16+
get_analysis(X, Y): Return analysis results dict
17+
get_analysis_tmp(X, Y): [OPTIONAL] Return intermediate analysis at each iteration
18+
"""
19+
20+
import random
21+
22+
23+
class Algorithm:
24+
"""
25+
Template algorithm: Random Sampling
26+
27+
This is a one-shot sampling algorithm that generates random points
28+
uniformly distributed within the input variable bounds.
29+
30+
Replace this with your own algorithm implementation.
31+
32+
Options:
33+
nvalues (int): Number of random samples to generate (default: 10)
34+
seed (int): Random seed for reproducibility (default: 42)
35+
"""
36+
37+
def __init__(self, **options):
38+
"""
39+
Initialize algorithm with options.
40+
41+
Options are parsed from the #options header comment and/or
42+
passed explicitly via fzd(..., algorithm_options={...}).
43+
Explicit options take precedence over header defaults.
44+
"""
45+
self.nvalues = int(options.get("nvalues", 10))
46+
seed = options.get("seed", None)
47+
if seed is not None:
48+
random.seed(int(seed))
49+
50+
def get_initial_design(self, input_vars, output_vars):
51+
"""
52+
Generate initial design of experiments.
53+
54+
Args:
55+
input_vars: Dict[str, Tuple[float, float]] - {var_name: (min, max)}
56+
e.g., {"x": (0.0, 1.0), "y": (-5.0, 5.0)}
57+
output_vars: List[str] - Output variable names
58+
e.g., ["pressure", "temperature"]
59+
60+
Returns:
61+
List[Dict[str, float]] - List of input variable combinations to evaluate
62+
e.g., [{"x": 0.5, "y": 0.0}, {"x": 0.7, "y": 2.3}]
63+
"""
64+
samples = []
65+
for _ in range(self.nvalues):
66+
sample = {}
67+
for var_name, (min_val, max_val) in input_vars.items():
68+
sample[var_name] = random.uniform(min_val, max_val)
69+
samples.append(sample)
70+
return samples
71+
72+
def get_next_design(self, X, Y):
73+
"""
74+
Generate next design based on previous results.
75+
76+
For this one-shot algorithm, always returns [] (finished).
77+
For iterative algorithms, return new points to evaluate,
78+
or [] when converged.
79+
80+
Args:
81+
X: List[Dict[str, float]] - Previous input combinations
82+
Y: List[float] - Corresponding output values (may contain None for failed cases)
83+
84+
Returns:
85+
List[Dict[str, float]] - Next points to evaluate, or [] if finished
86+
"""
87+
return [] # One-shot algorithm: no further iterations
88+
89+
def get_analysis(self, X, Y):
90+
"""
91+
Return final analysis results.
92+
93+
Args:
94+
X: List[Dict[str, float]] - All evaluated input combinations
95+
Y: List[float] - All corresponding output values (may contain None)
96+
97+
Returns:
98+
Dict with analysis information. Common keys:
99+
'text': Human-readable summary string
100+
'data': Dict with numerical results
101+
'html': Optional HTML output (e.g., plots encoded as base64 images)
102+
"""
103+
# Filter out None values (failed evaluations)
104+
valid_results = [(inp, out) for inp, out in zip(X, Y) if out is not None]
105+
106+
if not valid_results:
107+
return {
108+
"text": "No valid results",
109+
"data": {"samples": len(X), "valid_samples": 0},
110+
}
111+
112+
valid_outputs = [out for _, out in valid_results]
113+
best_input, best_output = min(valid_results, key=lambda r: r[1])
114+
worst_input, worst_output = max(valid_results, key=lambda r: r[1])
115+
mean_output = sum(valid_outputs) / len(valid_outputs)
116+
117+
result_text = f"""Random Sampling Results:
118+
Total samples: {len(X)}
119+
Valid samples: {len(valid_results)}
120+
Best output: {best_output:.6g}
121+
Best input: {best_input}
122+
Worst output: {worst_output:.6g}
123+
Mean output: {mean_output:.6g}
124+
"""
125+
126+
return {
127+
"text": result_text,
128+
"data": {
129+
"samples": len(X),
130+
"valid_samples": len(valid_results),
131+
"best_output": best_output,
132+
"best_input": best_input,
133+
"worst_output": worst_output,
134+
"mean_output": mean_output,
135+
},
136+
}
137+
138+
def get_analysis_tmp(self, X, Y):
139+
"""
140+
[OPTIONAL] Return intermediate analysis at each iteration.
141+
142+
Called after each batch of evaluations to show progress.
143+
If this method is not present, no intermediate results are displayed.
144+
145+
Args:
146+
X: List[Dict[str, float]] - All evaluated inputs so far
147+
Y: List[float] - All outputs so far (may contain None)
148+
149+
Returns:
150+
Dict with intermediate analysis information (typically 'text' and 'data')
151+
"""
152+
valid_count = sum(1 for y in Y if y is not None)
153+
return {
154+
"text": f" Progress: {valid_count}/{len(Y)} valid samples collected",
155+
"data": {"valid_samples": valid_count, "total_samples": len(Y)},
156+
}

.github/workflows/test.yml

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
name: Test Algorithm Plugin
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test-loading:
11+
name: Test Algorithm Loading
12+
runs-on: ${{ matrix.os }}
13+
strategy:
14+
matrix:
15+
os: [ubuntu-latest, macos-latest, windows-latest]
16+
python-version: ["3.10", "3.11", "3.12"]
17+
18+
steps:
19+
- name: Checkout repository
20+
uses: actions/checkout@v4
21+
22+
- name: Set up Python ${{ matrix.python-version }}
23+
uses: actions/setup-python@v5
24+
with:
25+
python-version: ${{ matrix.python-version }}
26+
27+
- name: Install fz framework
28+
run: pip install git+https://github.com/Funz/fz.git
29+
30+
- name: Install test dependencies
31+
run: pip install pytest
32+
33+
- name: Test algorithm file structure
34+
run: python -m pytest tests/test_plugin.py::test_algorithm_file_exists tests/test_plugin.py::test_algorithm_metadata tests/test_plugin.py::test_algorithm_class_structure -v
35+
36+
- name: Test algorithm loading
37+
run: python -m pytest tests/test_plugin.py::test_algorithm_loading tests/test_plugin.py::test_with_fz_loading -v
38+
39+
test-running:
40+
name: Test Algorithm Running
41+
runs-on: ${{ matrix.os }}
42+
strategy:
43+
matrix:
44+
os: [ubuntu-latest, macos-latest, windows-latest]
45+
python-version: ["3.10", "3.11", "3.12"]
46+
47+
steps:
48+
- name: Checkout repository
49+
uses: actions/checkout@v4
50+
51+
- name: Set up Python ${{ matrix.python-version }}
52+
uses: actions/setup-python@v5
53+
with:
54+
python-version: ${{ matrix.python-version }}
55+
56+
- name: Install fz framework
57+
run: pip install git+https://github.com/Funz/fz.git
58+
59+
- name: Install test dependencies
60+
run: pip install pytest
61+
62+
- name: Test algorithm execution
63+
run: python -m pytest tests/test_plugin.py::test_algorithm_execution -v
64+
65+
- name: Run full test suite
66+
run: python -m pytest tests/test_plugin.py -v

.gitignore

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Python
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
*.egg-info/
6+
dist/
7+
build/
8+
9+
# Jupyter
10+
.ipynb_checkpoints/
11+
12+
# fz results
13+
analysis_*/
14+
results/
15+
16+
# OS
17+
.DS_Store
18+
Thumbs.db
19+
20+
# IDE
21+
.vscode/
22+
.idea/

LICENSE

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
BSD 3-Clause License
2+
3+
Copyright (c) 2025, Funz Contributors
4+
All rights reserved.
5+
6+
Redistribution and use in source and binary forms, with or without
7+
modification, are permitted provided that the following conditions are met:
8+
9+
1. Redistributions of source code must retain the above copyright notice, this
10+
list of conditions and the following disclaimer.
11+
12+
2. Redistributions in binary form must reproduce the above copyright notice,
13+
this list of conditions and the following disclaimer in the documentation
14+
and/or other materials provided with the distribution.
15+
16+
3. Neither the name of the copyright holder nor the names of its
17+
contributors may be used to endorse or promote products derived from
18+
this software without specific prior written permission.
19+
20+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

0 commit comments

Comments
 (0)