Skip to content

Commit c4ccdae

Browse files
committed
improve readme
fix_readme black fix secret fix readme
1 parent 5f8f256 commit c4ccdae

File tree

6 files changed

+532
-334
lines changed

6 files changed

+532
-334
lines changed

examples/symbolic_regression/README.md

Lines changed: 94 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,10 @@ This example leverages **LLM-SRBench**, a benchmark specifically designed for La
1616

1717
Follow these steps to set up and run the symbolic regression benchmark example:
1818

19-
### 1. Configure API Secrets
19+
### 1. Configure API Keys
2020

21-
You'll need to provide your API credentials for the language models used by OpenEvolve.
21+
The API key is read from the environment `OPENAI_API_KEY` by default. The primary and secondary model we used in testing LLM-SRBench is `gpt-4o` and `o3`. You can check `create_config()` in `data_api.py`.
2222

23-
- Create a `secrets.yaml` file in the example directory.
24-
- Add your API key and model preferences:
25-
26-
YAML
27-
28-
```
29-
# secrets.yaml
30-
api_key: <YOUR_OPENAI_API_KEY>
31-
api_base: "https://api.openai.com/v1" # Or your custom endpoint
32-
primary_model: "gpt-4o"
33-
secondary_model: "o3" # Or another preferred model for specific tasks
34-
```
35-
36-
Replace `<YOUR_OPENAI_API_KEY>` with your actual OpenAI API key.
3723

3824
### 2. Load Benchmark Tasks & Generate Initial Programs
3925

@@ -143,19 +129,83 @@ def run_search():
143129

144130
### Evolved Algorithm (Discovered Symbolic Expression)
145131

146-
OpenEvolve will iteratively modify the Python code within the `# EVOLVE-BLOCK-START` and `# EVOLVE-BLOCK-END` markers in `initial_program.py`. The goal is to transform the simple initial model into a more complex and accurate symbolic expression that minimizes the Mean Squared Error (MSE) on the training data.
132+
**OpenEvolve** iteratively modifies Python code segments, delineated by `# EVOLVE-BLOCK-START` and `# EVOLVE-BLOCK-END` markers within an `initial_program.py` file. The primary objective is to evolve a simple initial model into a more complex and accurate symbolic expression that minimizes the Mean Squared Error (MSE) against the training data.
147133

148-
An evolved `func` might, for instance, discover a non-linear expression like:
134+
Below is a symbolic expression discovered by OpenEvolve for the physics task `PO10`:
149135

150136
```python
151-
# Hypothetical example of what OpenEvolve might find:
137+
import numpy as np
138+
152139
def func(x, params):
153-
# Assuming X_train_scaled maps to x and const maps to a parameter in params
154-
predictions = np.sin(x[:, 0]) * x[:, 1]**2 + params[0]
155-
return predictions
140+
"""
141+
Calculates the model output using a linear combination of input variables
142+
or a constant value if no input variables. Operates on a matrix of samples.
143+
144+
Args:
145+
x (np.ndarray): A 2D numpy array of input variable values, shape (n_samples, n_features).
146+
n_features is 2.
147+
If n_features is 0, x should be shape (n_samples, 0).
148+
The order of columns in x must correspond to:
149+
(x, t).
150+
params (np.ndarray): A 1D numpy array of parameters.
151+
Expected length: 10.
152+
153+
Returns:
154+
np.ndarray: A 1D numpy array of predicted output values, shape (n_samples,).
155+
"""
156+
# --------------------------------------------------------------------------
157+
# Allow for flexible parameter count, only padding essential parts.
158+
if len(params) < 10:
159+
required_params = params.shape[0]
160+
params = np.pad(params, (0, 10 - required_params))
161+
162+
# Readable aliases for the two input features
163+
pos = x[:, 0] # position x(t)
164+
t_val = x[:, 1] # time t
165+
166+
# ---------- Internal restoring forces (Duffing-like) ------------------
167+
# −k x −β x³ −γ x⁵ (only odd powers, respecting the usual symmetry)
168+
# Reduced polynomial order (up to cubic) to avoid over-fitting while
169+
# still capturing the essential softening/stiffening behaviour.
170+
restoring = -(params[0] * pos + params[1] * pos**3)
171+
172+
# ---------- Externally forced, periodically driven term --------------
173+
# A e^{-λ t} sin(ω t) + B cos(Ω t) (General form considered)
174+
# Let the optimiser decide whether the envelope should grow
175+
# or decay by keeping the sign of params[4]. The exponent is
176+
# clipped to avoid numerical overflow.
177+
# Simple periodic forcing without exponential envelope. This is
178+
# sufficient for many driven oscillator benchmarks and reduces the
179+
# risk of numerical overflow in exp().
180+
trig1 = params[3] * t_val
181+
trig2 = params[5] * t_val
182+
forcing = params[2] * np.cos(trig1) + params[4] * np.sin(trig2)
183+
184+
# ---------- Weak position–time coupling & constant bias ---------------
185+
interaction = params[8] * pos * t_val
186+
bias = params[9]
187+
188+
return restoring + forcing + interaction + bias
156189
```
157190

158-
*(This is a simplified, hypothetical example to illustrate the transformation.)*
191+
The ground truth for this PO10 task is represented by the equation:
192+
193+
$F_0sin(t)−ω_0^2(γt+1)x(t)−ω_0^2x(t)^3−ω_0^2x(t).$
194+
195+
This can be expanded and simplified to:
196+
197+
$F_0sin(t)−ω_0^2γtx(t)−2ω_0^2x(t)−ω_0^2x(t)^3.$
198+
199+
Notably, the core functional forms present in this ground truth equation are captured by the evolved symbolic expression:
200+
201+
- The $sin(t)$ component can be represented by `params[4] * np.sin(params[5] * t_val)`.
202+
- The linear $x(t)$ term corresponds to `params[0] * pos`.
203+
- The cubic $x(t)^3$ term is `params[1] * pos**3`.
204+
- The interaction term $t⋅x(t)$ is captured by `params[8] * pos * t_val`.
205+
206+
The evolved code also includes terms like `params[2] * np.cos(params[3] * t_val)` (a cosine forcing term) and `params[9]` (a constant bias). These might evolve to have negligible parameter values if not supported by the data, or they could capture secondary effects or noise. The inclusion of the primary terms demonstrates OpenEvolve's strength in identifying the correct underlying structure of the equation.
207+
208+
*Note: Symbolic regression, despite such promising results, remains a very challenging task. This difficulty largely stems from the inherent complexities of inferring precise mathematical models from finite and potentially noisy training data, which provides only a partial observation of the true underlying system.*
159209

160210
------
161211

@@ -177,12 +227,28 @@ The `eval.py` script will help you collect and analyze performance metrics. The
177227

178228
For benchmark-wide comparisons and results from other methods, please refer to the official LLM-SRBench paper.
179229

180-
| **Task Category** | Med. NMSE (Test) | Med. R2 (Test) | **Med. NMSE (OOD Test)** | **Med. R2 (OOD Test)** |
181-
| ----------------------- | ---------------- | -------------- | ------------------------ | ---------------------- |
182-
| Chemistry (36 tasks) | 2.3419e-06 | 1.000 | 3.1384e-02 | 0.9686 |
183-
| Physics (44 tasks) | 1.8548e-05 | 1.000 | 7.9255e-04 | 0.9992 |
230+
*Note: Below we extract the approximate results of baselines in Fig.5 from LLMSR-Bench paper.*
231+
232+
**Median NMSE (Test Set)**
233+
234+
| **Domain** | **Direct** | **LLMSR** | **LaSR** | **SGA** | **OpenEvolve** |
235+
| ---------------- | ----------- | --------------- | ----------- | ----------- | -------------- |
236+
| Chemistry | ~6.0 × 10⁻¹ | **~1.5 × 10⁻⁶** | ~1.0 × 10⁻⁴ | ~1.0 × 10⁻² | 2.34 × 10⁻⁶ |
237+
| Biology | ~2.0 × 10⁻² | ~1.0 × 10⁻⁵ | ~1.0 × 10⁻⁴ | ~2.0 × 10⁻⁴ ||
238+
| Physics | ~3.0 × 10⁻¹ | **~2.0 × 10⁻⁷** | ~1.0 × 10⁻³ | ~4.0 × 10⁻³ | 1.85 × 10⁻⁵ |
239+
| Material Science | ~3.0 × 10⁻¹ | ~1.0 × 10⁻⁴ | ~7.0 × 10⁻⁴ | ~3.0 × 10⁻² ||
240+
241+
**Median NMSE (OOD Test Set)**
242+
243+
| **Domain** | **Direct** | **LLMSR** | **LaSR** | **SGA** | **OpenEvolve** |
244+
| ---------------- | ---------- | ----------- | ----------- | ---------- | --------------- |
245+
| Chemistry | ~3.0 × 10² | ~5.0 × 10⁻² | ~1.0 × 10⁰ | ~1.5 × 10⁰ | **3.14 × 10⁻²** |
246+
| Biology | ~1.2 × 10² | ~4.0 × 10⁰ | ~3.0 × 10¹ | ~4.0 × 10¹ ||
247+
| Physics | ~1.0 × 10¹ | ~1.0 × 10⁻³ | ~5.0 × 10⁻² | ~1.0 × 10⁰ | **7.93 × 10⁻⁴** |
248+
| Material Science | ~2.5 × 10¹ | ~3.0 × 10⁰ | ~8.0 × 10⁰ | ~2.5 × 10¹ ||
249+
250+
Current results for OpenEvolve are only for two subsets of LSR-Synth. We will update the comprehensive results soon.
184251

185-
Current results are only for two subset of LSR-Synth. We will update the comprehensive results soon.
186252

187253
------
188254

examples/symbolic_regression/bench/dataclasses.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ class Equation:
1515
lambda_format: Optional[callable] = None
1616
program_format: Optional[str] = None
1717

18+
1819
@dataclass
1920
class SearchResult:
2021
equation: Equation
2122
aux: Any
2223

24+
2325
@dataclass
2426
class SEDTask:
2527
name: str
@@ -29,6 +31,7 @@ class SEDTask:
2931
samples: Any
3032
desc: Optional[str] = None
3133

34+
3235
@dataclass
3336
class Problem:
3437
dataset_identifier: str
@@ -37,20 +40,23 @@ class Problem:
3740
samples: Any
3841

3942
def create_task(self) -> SEDTask:
40-
return SEDTask(name=self.equation_idx,
41-
symbols=self.gt_equation.symbols,
42-
symbol_descs=self.gt_equation.symbol_descs,
43-
symbol_properties=self.gt_equation.symbol_properties,
44-
samples=self.train_samples,
45-
desc=self.gt_equation.desc)
43+
return SEDTask(
44+
name=self.equation_idx,
45+
symbols=self.gt_equation.symbols,
46+
symbol_descs=self.gt_equation.symbol_descs,
47+
symbol_properties=self.gt_equation.symbol_properties,
48+
samples=self.train_samples,
49+
desc=self.gt_equation.desc,
50+
)
51+
4652
@property
4753
def train_samples(self):
48-
return self.samples['train']
49-
54+
return self.samples["train"]
55+
5056
@property
5157
def test_samples(self):
52-
return self.samples['test']
53-
58+
return self.samples["test"]
59+
5460
@property
5561
def ood_test_samples(self):
56-
return self.samples.get('ood_test', None)
62+
return self.samples.get("ood_test", None)

0 commit comments

Comments
 (0)