Skip to main content

Discovering physical laws with parallel symbolic enumeration

Official implementation of PSE with its core PSRN (Parallel Symbolic Regression Network) module

Authors: Kai Ruan, Yilong Xu, Ze-Feng Gao, Yike Guo, Hao Sun, Ji-Rong Wen, Yang Liu

Article DeepWiki

AI4Science License Stars Forks

PSRN.jpg

This repository contains the official PyTorch implementation of PSE (Parallel Symbolic Enumeration): A fast and efficient symbolic expression discovery method powered by PSRN (Parallel Symbolic Regression Network). PSRN evaluates millions of symbolic expressions simultaneously on GPU with automated subtree reuse.

News

[2026/01/30] 🎉🎉🎉 Our paper has been selected as the cover article for Nature Computational Science, Volume 6, Issue 1, January 2026!

Nature Computational Science Cover

📜 Docs

DeepWiki

https://deepwiki.com/x66ccff/PSRN

📥 Installation

Prerequisite: Python >=3.9, <=3.12

pip install psrn

Or build from source (https://github.com/x66ccff/PSRN):

pip install git+https://github.com/x66ccff/PSRN

🚀 Quickstart with Custom Data

Now, you can use psrn-run with custom data, use the following arguments:

Options

  -q, --csvpath TEXT           path to custom csv file

  -l, --operators         operator library (e.g., "['Add','Mul','Identity','Tanh','Abs']")
  --n_symbol_layers            number of symbol layers (default=3)
  -i, --n_inputs INTEGER       PSRN input size (n variables + n constants)
  -c, --use_constant BOOLEAN   use const in PSE

  --use_cpu BOOLEAN            use cpu
  -g, --gpu_index INTEGER      gpu index used

  --time_limit INTEGER         time limit (s)
  
  -d, --n_down_sample INTEGER  n sample to downsample in PSRN for speeding up
  -s, --seed INTEGER           seed
  -k, --topk INTEGER           number of best expressions to take from PSRN to
                               fit
  
  -o, --probe TEXT             expression probe, string, PSE will stop if
                               probe is in pf

  --experiment_name TEXT       experiment_name
  --help                       Show this message and exit.
Examples
========
>>> #   input data
>>> #  ____^______
>>> # /           |
>>> # ['x',      'y',   'x+x',        '1.5',      '3']
>>> # <-n_variables->
>>> #                 <-n_cross->
>>> #                            <-trying_const_num->
>>> # <-----------------n_inputs--------------------->

For more detailed parameter settings, please use psrn-run --help

📝 CLI Examples

To run the script with build-in custom data with an expression probe (the algorithm will stop when it finds the expression or its symbolic equivalents):

psrn-run --csvpath ./your_data.csv -g 0 -i 5 -c False --probe "(exp(x0)-exp(-x0))/2"

Without an expression probe:

psrn-run --csvpath ./your_data.csv -g 0 -i 5 -c False

For limited VRAM (or when the ground truth expression is expected to be simple):

psrn-run --csvpath ./your_data.csv -g 0 -i 3 -c False --probe "(exp(x0)-exp(-x0))/2"

To customize the operator library:

psrn-run --csvpath ./your_data.csv -g 0 -i 5 -c False --probe "(exp(x0)-exp(-x0))/2" -l "['Add','Mul','Identity','Tanh','Abs']"

For custom data paths and operators:

psrn-run --csvpath ./your_data.csv -g 0 -i 5 -c False -l "['Add','Mul','SemiSub','SemiDiv','Identity']" 

You can also reduce the number of layers to save VRAM (default is 3) so that you can use more inputs (e.g. 70 input PSRN, the rest 20 will be cross subtrees)

psrn-run --csvpath ./50_cols_of_x_data.csv --n_symbol_layers 2 -g 0 -i 70 -c False -l "['Add','Mul','SemiSub','SemiDiv','Identity']" 

If you want to change the source code, please use the following steps:

clone the repo first

git clone https://github.com/x66ccff/PSRN

then install the repo with edit mode

cd PSRN
pip install -e .

The cli.py is the entrance of the code, and you can change the PSRN_Regressior in regressor.py

📝 Python Examples

Show Code
import os
import click
import time
import numpy as np
import sympy as sp
import pandas as pd

default_csv = os.path.join(os.path.dirname(__file__), 'data', 'custom_data.csv')

@click.command()
@click.option("--experiment_name", default="_", type=str, help="experiment_name")
@click.option("--gpu_index", "-g", default=0, type=int, help="gpu index used")
@click.option("--operators","-l",default="['Add','Mul','Sub','Div','Identity','Sin','Cos','Exp','Log']",help="operator library")
@click.option("--n_symbol_layers",default=3,type=int,help="number of symbol layers (default=3)")
@click.option("--n_down_sample","-d",default=100,type=int,help="n sample to downsample in PSRN for speeding up")
@click.option("--n_inputs","-i",default=5,type=int,help="PSRN input size (n variables + n constants)")
@click.option("--seed", "-s", default=0, type=int, help="seed")
@click.option("--topk","-k",default=10,type=int,help="number of best expressions to take from PSRN to fit")
@click.option("--use_constant", "-c", default=False, type=bool, help="use const in PSE")
@click.option("--probe","-o",default=None,type=str,help="expression probe, string, PSE will stop if probe is in pf")
@click.option("--csvpath","-q",default=default_csv,type=str,help="path to custom csv file")
@click.option("--use_cpu",default=False,type=bool,help="use cpu")
@click.option("--time_limit", default=3600, type=int, help="time limit (s)")
def main(experiment_name, gpu_index, operators, n_down_sample, n_inputs, seed, topk, use_constant, probe, csvpath, use_cpu, time_limit):
    if not use_cpu:
        os.environ["CUDA_VISIBLE_DEVICES"] = str(gpu_index)

    import torch
    from psrn import PSRN_Regressor

    if not use_cpu:
        device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    else:
        device = torch.device("cpu")

    operators = eval(operators)
    df = pd.read_csv(csvpath, header=None)

    Input = df.values[:, :-1].reshape(len(df), -1)
    Output = df.values[:, -1].reshape(len(df), 1)

    variables_name = [f"x{i}" for i in range(Input.shape[1])]

    regressor = PSRN_Regressor(
        variables=variables_name,
        use_const=use_constant,
        n_symbol_layers=n_symbol_layers,
        device=device,
        token_generator_config={
            "base": {
                "has_const": use_constant,
                "tokens": operators
            }
        },
        stage_config={
            "default": {
                "operators": operators,
                "time_limit": time_limit,
                "n_psrn_inputs": n_inputs,
                "n_sample_variables": 3,
            },
            "stages": [
                {},
            ],
        },
    )

    start = time.time()
    flag, pareto_ls = regressor.fit(
        Input,
        Output,
        n_down_sample=n_down_sample,
        use_threshold=False,
        threshold=1e-20,
        probe=probe,
        prun_const=True,
        prun_ndigit=6,
        top_k=topk,
    )
    end = time.time()
    time_cost = end - start

    pareto_ls = regressor.display_expr_table(sort_by='mse') # or 'reward'

    expr_str, reward, loss, complexity = pareto_ls[0]
    print('Found:', expr_str, 'time_cost', time_cost)

📚 Citation

If you use this work, please cite:

@article{Ruan2026Discovering,
  title     = {Discovering physical laws with parallel symbolic enumeration},
  author    = {Ruan, Kai and Xu, Yilong and Gao, Ze-Feng and Liu, Yang and Guo, Yike and Wen, Ji-Rong and Sun, Hao},
  journal   = {Nature Computational Science},
  volume    = {6},
  number    = {1},
  pages     = {53--66},
  year      = {2026},
  month     = {jan},
  publisher = {Springer Nature},
  doi       = {10.1038/s43588-025-00904-8},
  url       = {https://doi.org/10.1038/s43588-025-00904-8}
}

Metadata

Release files for psrn 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for psrn 0.1.3
File Size Uploaded
psrn-0.1.3.tar.gz 195.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for psrn 0.1.3
File Interpreter ABI Platform
psrn-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 423.3 kB

Release files / psrn-0.1.3.tar.gz

Download URL psrn-0.1.3.tar.gz
Size 195.2 kB
Tags Source
SHA-256 checksum
How to use checksums
84c1b3eecf592577ec639b1232b6ab16570633477ad8eda99564da895b3b9212
BLAKE2b-256 checksum
How to use checksums
6485769f1c3e23658b9c08035a6f44283cc62da051ad7e84b3b459979abec8a1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / psrn-0.1.3-py3-none-any.whl

Download URL psrn-0.1.3-py3-none-any.whl
Size 228.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
45e17d507834557978bc85f7d6713267ac627dc0467e1aeb43dbe134c84a8f7e
BLAKE2b-256 checksum
How to use checksums
1ac89e32527d605042c318174c6bca572f01be955033003876bf40651cbb7fdb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

0.1.3 This release

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page