# PyReggression

A Python package for exploring, analyzing, and manipulating symbolic regression results using an e-graph based approach. PyReggression provides a query interface to the r🥚ression engine.

More info [here](https://github.com/folivetti/srtree/tree/main/apps/rEGGression)

## Installation

```bash
pip install pyreggression
```

## Features

- Load and analyze symbolic regression models from e-graph files
- Import expressions from various symbolic regression tools (TIR, HeuristicLab, Operon, etc.)
- Query expressions by patterns, size, parameters, and complexity
- Analyze expression distributions and patterns
- Extract Pareto fronts of accuracy vs. expression size
- Support for different loss functions for various problem types
- Optimization and reporting capabilities

## Usage

You can find a Jupyter Notebook with examples [here](https://github.com/folivetti/pyreggression/tree/main/test)

### Basic Usage

```python
from pyreggression import PyReggression

# Load from an existing e-graph file
egg = PyReggression(
    dataset="train_data.csv", 
    loadFrom="my_models.egraph"
)

# Get the top 10 expressions by fitness
top_models = egg.top(10)
print(top_models)

# Save the e-graph to a new file
egg.save("updated_models.egraph")
```

### Importing from Other Symbolic Regression Tools

```python
from pyreggression import PyReggression

# Import expressions from a CSV file generated by another tool
egg = PyReggression(
    dataset="train_data.csv",
    parseCSV="operon_results.operon",  # File extension indicates the source tool
    parseParams=True
)

# Get the top models
best_models = egg.top(5)
print(best_models)
```

### Pattern Matching and Filtering

```python
from pyreggression import PyReggression

egg = PyReggression(dataset="train_data.csv", loadFrom="models.egraph")

# Find expressions with specific characteristics
filtered = egg.top(
    n=10,
    filters=["size < 15", "parameters <= 3"],
    criteria="fitness",
    pattern="v0 * x0",  # Match any expression multiplied by x0
    isRoot=False
)
print(filtered)

# Count occurrences of a pattern
count = egg.countPattern("sin(v0)")
print(f"Number of expressions containing sine: {count}")
```

### Distribution Analysis

```python
from pyreggression import PyReggression

egg = PyReggression(dataset="train_data.csv", loadFrom="models.egraph")

# Analyze pattern distribution
dist = egg.distribution(
    filters=["size <= 10"],
    limitedAt=20,
    dsc=True,
    byFitness=True,
    atLeast=100,
    fromTop=5000
)
print(dist)
```

### Testing on New Data

```python
from pyreggression import PyReggression

egg = PyReggression(
    dataset="train_data.csv",
    testData="test_data.csv",
    loadFrom="models.egraph"
)

# Get the top models evaluated on test data
test_results = egg.top(10)
print(test_results)
```

## Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `dataset` | str | required | Filename of the training dataset in CSV format |
| `testData` | str | "" | Filename of the test dataset in CSV format |
| `loss` | str | "MSE" | Loss function: "MSE", "Gaussian", "Bernoulli", or "Poisson" |
| `loadFrom` | str | "" | Filename of an e-graph to load |
| `parseCSV` | str | "" | CSV file with expressions from another tool |
| `parseParams` | bool | True | Whether to extract parameter values from expressions |

## Supported File Extensions for parseCSV

PyReggression can import expressions from various symbolic regression tools:

- `.tir` - TIR and ITEA
- `.hl` - HeuristicLab
- `.operon` - Operon
- `.bingo` - BINGO
- `.gomea` - GP-GOMEA
- `.pysr` - PySR
- `.sbp` - SBP
- `.eplex` - EPLEX, FEAT, BRUSH

## Methods

### Query Methods
- `top(n, filters, criteria, pattern, isRoot, negate)`: Returns top expressions by criteria
- `distribution(filters, limitedAt, dsc, byFitness, atLeast, fromTop)`: Returns pattern distribution
- `countPattern(pattern)`: Counts occurrences of a pattern
- `pareto(byFitness)`: Returns the Pareto front of accuracy vs. size

### Analysis Methods
- `report(n)`: Detailed report of e-class n
- `optimize(n)`: Re-optimize parameters for e-class n
- `subtrees(n)`: Return subtrees of e-class n

### Manipulation Methods
- `insert(expr)`: Insert a new expression
- `save(fname)`: Save the e-graph file
- `load(fname)`: Load an e-graph file
- `runQuery(query, df)`: Run a custom query against the e-graph

## Pattern Syntax

Pattern matching uses the following syntax:
- `x0, x1, ...` - Input variables
- `t0, t1, ...` - Model parameters
- `v0, v1, ...` - Pattern variables (match any expression)

Examples:
- `t0 * x0` - Match exactly this expression
- `v0 * x0` - Match any expression multiplied by x0
- `sin(v0)` - Match sine of any expression
- `v0 + v1` - Match any addition
- `v0 + x0 * v1^v0` - Match an expression `v0` added to `x0` multiplied by the expression `v1` to the power of `v0`. E.g., `t0*x1 + x0 * t1^(t0*x1)`

## License

[LICENSE]

## Citation

If you use PyReggression in your research, please cite:

```
@inproceedings{rEGGression,
author = {de Franca, Fabricio Olivetti and Kronberger, Gabriel},
title = {rEGGression: an Interactive and Agnostic Tool for the Exploration of Symbolic Regression Models},
year = {2025},
isbn = {9798400714658},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
url = {https://doi.org/10.1145/3712256.3726385},
doi = {10.1145/3712256.3726385},
booktitle = {Proceedings of the Genetic and Evolutionary Computation Conference},
pages = {},
numpages = {9},
keywords = {Genetic programming, Symbolic regression, Equality saturation, e-graphs},
location = {Malaga, Spain},
series = {GECCO '25},
archivePrefix = {arXiv},
       eprint = {2501.17859},
 primaryClass = {cs.LG},
}
```



The bindings were created following the amazing example written by [wenkokke](https://github.com/wenkokke/example-haskell-wheel)
