### Install SymbolicRegression.jl Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/README.md Install the SymbolicRegression.jl package using the Julia package manager. ```julia using Pkg Pkg.add("SymbolicRegression") ``` -------------------------------- ### Seeding Search with Initial Guesses Source: https://context7.com/astroautomata/symbolicregression.jl/llms.txt Provide string expressions as initial guesses to guide the search process. This can accelerate convergence, especially for complex target functions. ```julia using SymbolicRegression using MLJ: machine, fit!, report X = randn(Float32, 6, 2048) y = @. sin(X[1,:] * X[2,:] + 0.1f0) + cos(X[3,:]) * X[4,:] + X[5,:] / (X[6,:]^2 + 1) model = SRRegressor( binary_operators=[+, -, *, /], unary_operators=[sin, cos], maxsize=35, niterations=100, # Provide prior knowledge as a starting point; constants will be optimized guesses=["sin(x1 * x2) + cos(x3) * x4 + x5 / (x6 * x6 + 0.9)"], batching=true, batch_size=64, ) mach = machine(model, X', y) fit!(mach) r = report(mach) # Should converge faster to the correct + 0.1 term and 1.0 constant println("Best: ", r.equations[r.best_idx]) ``` -------------------------------- ### Setup Template Expressions in Julia Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/examples.md Illustrates setting up a template expression specification in Julia for SymbolicRegression.jl. This requires defining the structure of the expression using the @template_spec macro and preparing input data. ```julia using SymbolicRegression using Random: rand, MersenneTwister using MLJBase: machine, fit!, report expression_spec = @template_spec(expressions=(f, g)) do x1, x2, x3 f(x1, x2) + g(x2) - g(x3) end n = 100 rng = MersenneTwister(0) x1 = 10rand(rng, n) x2 = 10rand(rng, n) x3 = 10rand(rng, n) X = (; x1, x2, x3) y = [ 2 * cos(x1[i] + 3.2) + x2[i]^2 - 0.8 * x3[i]^2 for i in eachindex(x1) ] ``` -------------------------------- ### Generate Data for Seeding Search Example Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/examples.md Generates random input data and corresponding target values for a complex function. Requires SymbolicRegression and MLJ packages. ```julia using SymbolicRegression, MLJ X = randn(6, 2048) y = @. sin(X[1, :] * X[2, :] + 0.1f0) + cos(X[3, :]) * X[4, :] + X[5, :] / (X[6, :] * X[6, :] + 1) ``` -------------------------------- ### Example Script for Slurm Cluster Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/slurm.md This script configures SymbolicRegression.jl for distributed mode on a Slurm cluster. It defines a custom loss function and sets up parallel processing using SlurmManager. ```julia # script.jl using SymbolicRegression using Distributed: addprocs using SlurmClusterManager: SlurmManager using MLJ: machine, fit! # Figure out how large a job we are launching num_tasks = parse(Int, ENV["SLURM_NTASKS"]) # Define a custom loss function (this will # be automatically passed to the workers) my_loss(pred, targ) = abs2(pred - targ) # Create a simple dataset X = (; x1=randn(10_000), x2=20rand(10_000)) y = 2 .* cos.(X.x2) .+ X.x1 .^ 2 .- 1 add_slurm(_; kws...) = addprocs(SlurmManager(); kws...) model = SRRegressor(; unary_operators=(), binary_operators=(+, *, -, /, mod), niterations=5000, elementwise_loss=my_loss, ###################################### #= KEY OPTIONS FOR DISTRIBUTED MODE =# parallelism=:multiprocessing, addprocs_function=add_slurm, #numprocs=num_tasks, # <- Not relevant for `SlurmManager`, but some other cluster managers need it ###################################### # Scaled # of populations means workers can stay busy: populations=num_tasks * 3, # Larger ncycles_per_iteration means reduced # communication overhead with head worker node: ncycles_per_iteration=300, # If you need additional modules on workers, pass them to `worker_imports` as symbols: #worker_imports=[:VectorizationBase], ) mach = machine(model, X, y) fit!(mach) ``` -------------------------------- ### Generate Dependency Structure Bash Script Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/README.md Use this bash script to generate a dependency structure from the Julia source files in the 'src' directory. Requires 'vim-stream' to be installed. ```bash echo 'stateDiagram-v2' IFS=$'\n' for f in *.jl; do for line in $(cat $f | grep -e 'import \.\.' -e 'import \.' -e 'using \.' -e 'using \.\.'); do echo $(echo $line | vims -s 'dwf:d$' -t '%s/^\.*//g' '%s/Module//g') $(basename "$f" .jl); done; done | vims -l 'f a--> ' | sort ``` -------------------------------- ### Define and Train SRRegressor with MLJ Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/README.md Example of defining an SRRegressor model with custom operators and training it on a dataset using MLJ's fit! function. Ensure MLJ and SymbolicRegression are imported. ```julia import SymbolicRegression: SRRegressor import MLJ: machine, fit!, predict, report # Dataset with two named features: X = (a = rand(500), b = rand(500)) # and one target: y = @. 2 * cos(X.a * 23.5) - X.b ^ 2 # with some noise: y = y .+ randn(500) .* 1e-3 model = SRRegressor( niterations=50, binary_operators=[+, -, *], unary_operators=[cos], ) ``` ```julia mach = machine(model, X, y) fit!(mach) ``` -------------------------------- ### Define Custom Loss Function Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/losses.md You can define your own loss function that accepts two (unweighted) or three (weighted) scalar arguments. This example shows a weighted custom loss function. ```julia f(x, y, w) = abs(x-y)*w options = Options(elementwise_loss=f) ``` -------------------------------- ### node_to_symbolic Function Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/api.md Converts an internal expression tree node to a SymbolicUtils.jl expression. Requires SymbolicUtils.jl to be installed and loaded. ```julia node_to_symbolic ``` -------------------------------- ### Construct and Evaluate Symbolic Expressions Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/README.md Demonstrates manual construction of symbolic expression trees using operators and variables, then evaluates them on data. Supports type conversion for constants. ```julia using SymbolicRegression: Options, Expression, Node options = Options(; binary_operators=[+, -, *, /], unary_operators=[cos, exp, sin] ) operators = options.operators variable_names = ["x1", "x2", "x3"] x1, x2, x3 = [Expression(Node(Float64; feature=i); operators, variable_names) for i=1:3] tree = cos(x1 - 3.2 * x2) - x1 * x1 float32_tree = convert(Expression{Float32}, tree) X = rand(Float32, 3, 100) tree(X) ``` -------------------------------- ### Get Report from Trained Model Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/README.md Retrieve the report, including discovered expressions, from a trained MLJ machine using the report function. ```julia report(mach) ``` -------------------------------- ### Options and Weights Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/api.md Configuration options for search and mutation weights. ```APIDOC ## Options ```@docs Options MutationWeights ``` ``` -------------------------------- ### Template Expressions with @template_spec Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/api.md Defines custom expression templates for symbolic regression. Use this to guide the search towards specific equation structures. ```julia @template_spec ``` -------------------------------- ### Configure Symbolic Regression Search with Options Source: https://context7.com/astroautomata/symbolicregression.jl/llms.txt The `Options` struct centralizes configuration for the search process. Customize operator sets, search space dimensions, population sizes, objective functions (loss), complexity penalties, stopping criteria, mutation weights, constant optimization, batching, and output settings. ```julia using SymbolicRegression options = Options( # Operator set binary_operators=[+, -, *, /], unary_operators=[cos, exp, log], # Search space maxsize=30, # Maximum expression tree size maxdepth=10, # Maximum tree depth (defaults to maxsize) # Search size populations=31, # Number of independent populations population_size=27, # Individuals per population ncycles_per_iteration=380, # Objective elementwise_loss=L2DistLoss(), # Mean-squared error (default) # OR custom loss: # elementwise_loss=(pred, target) -> abs(pred - target), # OR full-dataset loss: # loss_function=(tree, dataset, options) -> sum((eval_tree_array(tree, dataset.X, options)[1] .- dataset.y).^2), # Complexity penalties parsimony=0.0032, complexity_of_operators=[(^) => 3, sin => 2, cos => 2], complexity_of_constants=1, complexity_of_variables=1, # Stopping criteria niterations=50, # (passed to equation_search, not Options) early_stop_condition=(loss, complexity) -> loss < 1e-6 && complexity <= 10, timeout_in_seconds=60.0, max_evals=Int(1e6), # Mutation control mutation_weights=MutationWeights( mutate_constant=0.05, mutate_operator=0.5, add_node=0.1, delete_node=0.1, do_nothing=0.3, ), crossover_probability=0.066, # Constant optimization should_optimize_constants=true, optimizer_algorithm=Optim.BFGS(linesearch=LineSearches.BackTracking()), optimizer_nrestarts=2, optimizer_probability=0.14, # Batching (for large datasets) batching=true, batch_size=256, # Reproducibility seed=42, deterministic=false, # Output verbosity=1, save_to_file=true, output_directory="./outputs", ) ``` -------------------------------- ### Using Higher-Arity Operators with OperatorEnum Source: https://context7.com/astroautomata/symbolicregression.jl/llms.txt Define custom operators with arity greater than two by passing an explicit OperatorEnum. This example uses a ternary conditional operator. ```julia using SymbolicRegression using DynamicExpressions: OperatorEnum using MLJ: machine, fit!, report # A ternary conditional: ifelse(a > 0, b, c) scalar_ifelse(a, b, c) = a > 0 ? b : c X = randn(3, 100) y = [X[1, i] > 0 ? 2*X[2, i] : X[3, i] for i in 1:100] model = SRRegressor( operators=OperatorEnum( 1 => (), # No unary operators 2 => (+, -, *, /), # Standard binary 3 => (scalar_ifelse,), # Ternary ), niterations=35, maxsize=15, ) mach = machine(model, X', y) fit!(mach) r = report(mach) println("Best equation: ", r.equations[r.best_idx]) # e.g.: "scalar_ifelse(x1, 2 * x2, x3)" ``` -------------------------------- ### Set Up and Fit SRRegressor with Initial Guesses Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/examples.md Configures and fits an SRRegressor model with specified operators, max size, iterations, and initial guesses. Requires MLJ and SymbolicRegression packages. ```julia model = SRRegressor( binary_operators=[+, -, *, /], unary_operators=[sin, cos], maxsize=35, niterations=35, guesses=["sin(x1 * x2) + cos(x3) * x4 + x5 / (x6 * x6 + 0.9)", #= can provide additional guesses here =#], batching=true, batch_size=32, ) mach = machine(model, X', y) fit!(mach) ``` -------------------------------- ### Log Symbolic Regression with TensorBoard in Julia Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/examples.md Illustrates how to integrate TensorBoard logging into a symbolic regression search using SymbolicRegression.jl and TensorBoardLogger.jl. This requires initializing an SRLogger with a TBLogger and passing it to the SRRegressor. ```julia using SymbolicRegression using TensorBoardLogger using MLJ logger = SRLogger(TBLogger("logs/sr_run")) # Create and fit model with logger model = SRRegressor( binary_operators=[+, -, *], maxsize=40, niterations=100, logger=logger ) X = (a=rand(500), b=rand(500)) y = @. 2 * cos(X.a * 23.5) - X.b^2 mach = machine(model, X, y) fit!(mach) ``` -------------------------------- ### Set Up and Fit SRRegressor Model with Derivative Operator Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/examples.md Sets up and fits an SRRegressor model using a defined expression specification for derivative operators. Requires MLJ and SymbolicRegression packages. ```julia using MLJ model = SRRegressor( binary_operators=(+, -, *, /), unary_operators=(sqrt,), maxsize=20, expression_spec=expression_spec, ) X = (; x=x) mach = machine(model, X, y) fit!(mach) ``` -------------------------------- ### Requesting Resources with salloc Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/slurm.md This bash command requests resources for an interactive Slurm session. Adjust 'YOUR_PARTITION' and '-N 2' based on your cluster's configuration and needs. ```bash salloc -p YOUR_PARTITION -N 2 ``` -------------------------------- ### Options and MutationWeights Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/api.md Configure the behavior and parameters of the symbolic regression search. Options control general settings, while MutationWeights influence the probability of different mutation operators. ```julia Options ``` ```julia MutationWeights ``` -------------------------------- ### Using Float32 Precision Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/examples.md Demonstrates using `Float32` for all calculations, which can affect precision and performance. The output types will reflect the specified precision. ```julia X = 2randn(Float32, 1000, 5) y = @. 2*cos(X[:, 4]) + X[:, 1]^2 - 2 model = SRRegressor(binary_operators=[+, -, *, /], unary_operators=[cos], niterations=30) mach = machine(model, X, y) fit!(mach) ``` ```julia r = report(mach) best = r.equations[r.best_idx] println(typeof(best)) # Expression{Float32,Node{Float32},...} ``` -------------------------------- ### Low-Level API Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/api.md Core functions for equation searching. ```APIDOC ## Low-Level API ```@docs equation_search ``` ``` -------------------------------- ### Train Model with Template Expressions in Julia Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/examples.md Demonstrates training a symbolic regression model using a predefined template expression specification in Julia. This involves configuring the SRRegressor with the expression_spec and fitting it to data. ```julia model = SRRegressor(; binary_operators=(+, -, *, /), unary_operators=(cos,), niterations=500, maxsize=25, expression_spec=expression_spec, ) mach = machine(model, X, y) fit!(mach) ``` -------------------------------- ### Options Configuration Source: https://context7.com/astroautomata/symbolicregression.jl/llms.txt The Options struct is the central configuration object for symbolic regression searches. It allows customization of operators, search hyperparameters, mutation weights, complexity penalties, and stopping criteria. ```APIDOC ## `Options` — Configure the search The `Options` struct is the central configuration object passed to every search function. It controls the operator set, search size, objective, complexity, mutations, stopping criteria, and more. All parameters are keyword arguments with sensible defaults tuned from community benchmarks. ```julia using SymbolicRegression options = Options( # Operator set binary_operators=[+, -, *, /], unary_operators=[cos, exp, log], # Search space maxsize=30, # Maximum expression tree size maxdepth=10, # Maximum tree depth (defaults to maxsize) # Search size populations=31, # Number of independent populations population_size=27, # Individuals per population ncycles_per_iteration=380, # Objective elementwise_loss=L2DistLoss(), # Mean-squared error (default) # OR custom loss: # elementwise_loss=(pred, target) -> abs(pred - target), # OR full-dataset loss: # loss_function=(tree, dataset, options) -> sum((eval_tree_array(tree, dataset.X, options)[1] .- dataset.y).^2), # Complexity penalties parsimony=0.0032, complexity_of_operators=[(^) => 3, sin => 2, cos => 2], complexity_of_constants=1, complexity_of_variables=1, # Stopping criteria niterations=50, # (passed to equation_search, not Options) early_stop_condition=(loss, complexity) -> loss < 1e-6 && complexity <= 10, timeout_in_seconds=60.0, max_evals=Int(1e6), # Mutation control mutation_weights=MutationWeights( mutate_constant=0.05, mutate_operator=0.5, add_node=0.1, delete_node=0.1, do_nothing=0.3, ), crossover_probability=0.066, # Constant optimization should_optimize_constants=true, optimizer_algorithm=Optim.BFGS(linesearch=LineSearches.BackTracking()), optimizer_nrestarts=2, optimizer_probability=0.14, # Batching (for large datasets) batching=true, batch_size=256, # Reproducibility seed=42, deterministic=false, # Output verbosity=1, save_to_file=true, output_directory="./outputs", ) ``` ``` -------------------------------- ### Dimensional Analysis with DynamicQuantities Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/examples.md Applies dimensional analysis to find dimensionally consistent expressions using `DynamicQuantities`. Requires `DynamicQuantities` and `SymbolicRegression`. ```julia using DynamicQuantities using SymbolicRegression M = (rand(100) .+ 0.1) .* Constants.M_sun m = 100 .* (rand(100) .+ 0.1) .* u"kg" r = (rand(100) .+ 0.1) .* Constants.R_earth G = Constants.G F = @. (G * M * m / r^2) ``` ```julia X = (; M=M, m=m, r=r) y = F ``` ```julia function loss_fnc(prediction, target) # Useful loss for large dynamic range scatter_loss = abs(log((abs(prediction)+1e-20) / (abs(target)+1e-20))) sign_loss = 10 * (sign(prediction) - sign(target))^2 return scatter_loss + sign_loss end ``` ```julia model = SRRegressor( binary_operators=[+, -, *, /], unary_operators=[square], elementwise_loss=loss_fnc, complexity_of_constants=2, maxsize=25, niterations=100, populations=50, dimensional_constraint_penalty=10^5, ) mach = machine(model, X, y) fit!(mach) ``` ```julia "y[m s⁻² kg] = (M[kg] * 2.6353e-22[?])" ``` -------------------------------- ### Slurm Batch Script Configuration Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/slurm.md A sample Slurm batch script (`batch.sh`) to submit a job. It specifies partition, number of tasks, CPU cores per task, and job time limit. ```bash #!/usr/bin/env bash #SBATCH -p gen #SBATCH -n 128 #SBATCH --cpus-per-task=1 #SBATCH --time=1-00:00:00 #SBATCH --job-name=sr_example julia --project=. script.jl ``` -------------------------------- ### Custom loss functions for Symbolic Regression in Julia Source: https://context7.com/astroautomata/symbolicregression.jl/llms.txt Illustrates three methods for defining custom loss functions: `elementwise_loss` for scalar losses, `loss_function` for dataset-wide losses, and `loss_function_expression` for template expressions. ```julia using SymbolicRegression using LossFunctions: HuberLoss, L1DistLoss # Option 1: Built-in loss from LossFunctions.jl options1 = Options( binary_operators=[+, *, -], elementwise_loss=HuberLoss(1.0), # Robust to outliers ) # Option 2: Custom elementwise lambda (pred, target) -> scalar options2 = Options( binary_operators=[+, *, -], elementwise_loss=(pred, target) -> abs(log(abs(pred) + 1e-10) - log(abs(target) + 1e-10)), ) # Option 3: Weighted loss (pred, target, weight) -> scalar options3 = Options( binary_operators=[+, *, -], elementwise_loss=(pred, target, w) -> w * abs(pred - target), ) # Option 4: Full-dataset loss function (most flexible) function my_dataset_loss(tree, dataset, options) pred, flag = eval_tree_array(tree, dataset.X, options) !flag && return typeof(dataset.y[1])(Inf) residuals = pred .- dataset.y return sum(abs, residuals) / dataset.n end options4 = Options( binary_operators=[+, *, -, /], loss_function=my_dataset_loss, loss_scale=:linear, # Use :linear for losses that can be negative (e.g., likelihoods) ) X = randn(2, 200) y = @. X[1, :] + X[2, :]^2 hof = equation_search(X, y; options=options4, niterations=20) println(string_tree(calculate_pareto_frontier(hof)[end].tree, options4)) ``` -------------------------------- ### Multi-target Search with Symbolic Regression Source: https://context7.com/astroautomata/symbolicregression.jl/llms.txt Demonstrates setting up and performing a multi-target symbolic regression search where the target variable `y` is a matrix. The `equation_search` function returns a vector of `HallOfFame` objects, one for each target. ```julia y_multi = vcat(y', (X[1, :] .+ X[2, :])') # 2 targets hof_multi = equation_search(X, y_multi; options=options, niterations=30) ``` -------------------------------- ### Logging Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/api.md Tracks the progress of symbolic regression searches using custom loggers. ```APIDOC ## Logging ```@docs SRLogger ``` The `SRLogger` allows you to track the progress of symbolic regression searches. It can wrap any `AbstractLogger` that implements the Julia logging interface, such as from TensorBoardLogger.jl or Wandb.jl. ```julia using TensorBoardLogger logger = SRLogger(TBLogger("logs/run"), log_interval=2) model = SRRegressor(; logger=logger, kws... ) ``` ``` -------------------------------- ### Equation Search Flowchart Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/README.md Visualizes the equation search process, including options, dataset input, the equation search module, and the hall of fame output. Shows parallel processing threads and migration between populations. ```mermaid flowchart TB op([Options]) d([Dataset]) op --> ES d --> ES subgraph ES[equation_search] direction TB IP[sr_spawner] IP --> p1 IP --> p2 subgraph p1[Thread 1] direction LR pop1([Population]) pop1 --> src[s_r_cycle] src --> opt[optimize_and_simplify_population] opt --> pop1 end subgraph p2[Thread 2] direction LR pop2([Population]) pop2 --> src2[s_r_cycle] src2 --> opt2[optimize_and_simplify_population] opt2 --> pop2 end pop1 --> hof pop2 --> hof hof([HallOfFame]) hof --> migration pop1 <-.-> migration pop2 <-.-> migration migration[migrate!] end ES --> output([HallOfFame]) ``` -------------------------------- ### Generate Data for Integrand Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/examples.md Generates sample data for an integrand function. Requires SymbolicRegression and Random packages. ```julia using SymbolicRegression using Random rng = MersenneTwister(42) x = 1 .+ rand(rng, 1000) * 9 # Sampling points in the range [1, 10] y = @. 1 / (x^2 * sqrt(x^2 - 1)) # Values of the integrand ``` -------------------------------- ### Connecting to an Allocated Node Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/slurm.md SSH into one of the nodes allocated by Slurm. Replace 'YOUR_NODE_NAME' with the actual node name from the 'squeue' output. ```bash ssh YOUR_NODE_NAME ``` -------------------------------- ### Viewing Allocated Nodes Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/slurm.md Use this command to view the nodes currently allocated to your user in the Slurm queue. ```bash squeue -u $USER ``` -------------------------------- ### Print Full Pareto Frontier Details Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/README.md Iterates through the Pareto frontier to print detailed information for each expression, including complexity, loss (MSE), and the string representation of the equation. ```julia import SymbolicRegression: compute_complexity, string_tree println("Complexity\tMSE\tEquation") for member in dominating complexity = compute_complexity(member, options) loss = member.loss string = string_tree(member.tree, options) println("$(complexity)\t$(loss)\t$(string)") end ``` -------------------------------- ### Convert Expression Tree to SymbolicUtils.jl Source: https://context7.com/astroautomata/symbolicregression.jl/llms.txt Convert a discovered expression tree into a SymbolicUtils.jl expression for algebraic simplification and LaTeX rendering. Ensure SymbolicUtils.jl is loaded. ```julia using SymbolicRegression using SymbolicUtils # Must be loaded using Latexify # Optional: for LaTeX output using MLJ: machine, fit!, report X = 2randn(1000, 3) .+ 0.1 y = @. 1 / X[:, 1] my_inv(x) = 1/x model = SRRegressor(binary_operators=[+, *], unary_operators=[my_inv], niterations=30) mach = machine(model, X, y) fit!(mach) r = report(mach) best = r.equations[r.best_idx] # Convert to SymbolicUtils expression sym_expr = node_to_symbolic(best) println(sym_expr) # Simplify simplified = simplify(sym_expr * 2 + 1) println(simplified) # Render as LaTeX (requires Latexify) println(latexify(string(sym_expr))) # => "\frac{1}{x_{1}}" ``` -------------------------------- ### Perform Equation Search with Options Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/README.md Initiates a symbolic regression search using custom options for operators and population size. Assumes column-major input data. ```julia import SymbolicRegression: Options, equation_search X = randn(2, 100) y = 2 * cos.(X[2, :]) + X[1, :] .^ 2 .- 2 options = Options( binary_operators=[+, *, /, -], unary_operators=[cos, exp], populations=20 ) hall_of_fame = equation_search( X, y, niterations=40, options=options, parallelism=:multithreading ) ``` -------------------------------- ### Explicit Worker Management with SymbolicRegression.jl Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/slurm.md This script demonstrates explicit worker management by passing a list of processes to SRRegressor. It also shows how to define functions and import modules on workers using `@everywhere`. ```julia using SymbolicRegression using Distributed: addprocs, @everywhere using SlurmClusterManager: SlurmManager using MLJ: machine, fit! num_tasks = parse(Int, ENV["SLURM_NTASKS"]) procs = addprocs(SlurmManager()) @everywhere begin # define functions you need on workers function my_loss(pred, targ) abs2(pred - targ) end # define any other modules you need on workers using VectorizationBase end model = SRRegressor(; #= same as before =# #= ... =# procs=procs, # Pass workers explicitly addprocs_function=nothing, # <- Only used for SR-managed workers ) ``` -------------------------------- ### Calculating Pareto Frontier with `calculate_pareto_frontier` Source: https://context7.com/astroautomata/symbolicregression.jl/llms.txt Filters a `HallOfFame` object to extract only the Pareto-optimal expressions, where no other expression is both simpler and more accurate. Each member in the returned list contains the expression tree and its evaluated loss. ```julia using SymbolicRegression X = randn(Float32, 3, 200) y = @. X[1, :] * cos(X[2, :]) - X[3, :]^2 options = Options(binary_operators=[+, *, -, /], unary_operators=[cos]) hof = equation_search(X, y; options=options, niterations=30) # Get Pareto frontier dominating = calculate_pareto_frontier(hof) # Access individual members for member in dominating expr = member.tree # The expression tree (AbstractExpression) loss = member.loss # Scalar loss value complexity = compute_complexity(member, options) str = string_tree(expr, options) println("Complexity $complexity: loss=$(round(loss, sigdigits=4)), expr=$str") end ``` -------------------------------- ### Dimensional analysis with DynamicQuantities in Symbolic Regression Source: https://context7.com/astroautomata/symbolicregression.jl/llms.txt Performs symbolic regression aware of physical units using `DynamicQuantities`. It penalizes dimensionally inconsistent expressions using `dimensional_constraint_penalty`. ```julia using SymbolicRegression using DynamicQuantities using MLJ: machine, fit!, report # Newton's law of gravitation: F = G * M * m / r^2 M = (rand(100) .+ 0.1) .* Constants.M_sun m = 100 .* (rand(100) .+ 0.1) .* u"kg" r = (rand(100) .+ 0.1) .* Constants.R_earth F = @. Constants.G * M * m / r^2 X = (; M, m, r) y = F # Custom log-space loss for large dynamic range function log_loss(pred, target) scatter = abs(log((abs(pred) + 1e-20) / (abs(target) + 1e-20))) sign_pen = 10 * (sign(pred) - sign(target))^2 return scatter + sign_pen end model = SRRegressor( binary_operators=[+, -, *, /], elementwise_loss=log_loss, complexity_of_constants=2, maxsize=25, niterations=100, populations=50, dimensional_constraint_penalty=1e5, # Penalize unit violations dimensionless_constants_only=false, # Allow constants with inferred units ) mach = machine(model, X, y) fit!(mach) r = report(mach) # Dimensionally consistent expressions have "[unit]" suffixes on constants: println(r.equations[r.best_idx]) # e.g.: "M[kg] * 6.674e-11[m³ kg⁻¹ s⁻²] * m[kg] / r[m]^2" ``` -------------------------------- ### ComposableExpression: Callable sub-expressions in Julia Source: https://context7.com/astroautomata/symbolicregression.jl/llms.txt Demonstrates wrapping symbolic trees into callable Julia functions using `ComposableExpression`. Supports scalar arrays and composition with other expressions. ```julia using SymbolicRegression using DynamicExpressions: OperatorEnum, Node operators = OperatorEnum(; binary_operators=(+, *, /, -), unary_operators=(sin, cos)) variable_names = ["x1", "x2"] x1 = ComposableExpression(Node(Float64; feature=1); operators, variable_names) x2 = ComposableExpression(Node(Float64; feature=2); operators, variable_names) # Build an expression using Julia operators f = x1 * sin(x2) println(f) # x1 * sin(x2) # Call with raw arrays (vectorized) a, b = randn(5), randn(5) out = f(a, b) # == a .* sin.(b) println(out) # Call with other ComposableExpression objects (composition) g = f(x1, x1) # == x1 * sin(x1) println(g) # Self-composition h = f(f, f) # == (x1 * sin(x2)) * sin(x1 * sin(x2)) println(h) ``` -------------------------------- ### Convert Expression Tree to String with `string_tree` Source: https://context7.com/astroautomata/symbolicregression.jl/llms.txt Converts an expression tree to a human-readable mathematical string. Customizes output using `Options` for variable names and print precision. ```julia using SymbolicRegression options = Options( binary_operators=[+, *, /], unary_operators=[cos, exp], ) X = randn(3, 50) y = @. cos(X[1, :]) / (X[2, :] + X[3, :]) hof = equation_search(X, y; options=options, niterations=20, variable_names=["time", "mass", "velocity"]) dominating = calculate_pareto_frontier(hof) for member in dominating str = string_tree(member.tree, options) println(str) end ``` ```julia # Example output: "cos(time) / (mass + velocity)" ``` ```julia # With custom print precision: options2 = Options(binary_operators=[+, *], print_precision=8) hof2 = equation_search(X, y; options=options2, niterations=10) println(string_tree(calculate_pareto_frontier(hof2)[end].tree, options2)) ``` ```julia # Example output: "3.14159265 * x1" ``` -------------------------------- ### Submitting a Slurm Batch Job Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/slurm.md Submit the configured batch script to the Slurm scheduler. ```bash sbatch batch.sh ``` -------------------------------- ### SRLogger: Logging search progress with TensorBoardLogger Source: https://context7.com/astroautomata/symbolicregression.jl/llms.txt Integrates `SRLogger` with `TensorBoardLogger` to log Pareto frontier progress at specified intervals during the symbolic regression search. ```julia using SymbolicRegression using TensorBoardLogger using MLJ: machine, fit! # Wrap any AbstractLogger tb_logger = TBLogger("logs/sr_experiment") logger = SRLogger(tb_logger; log_interval=50) # Log every 50 steps model = SRRegressor( binary_operators=[+, -, *], unary_operators=[cos], maxsize=30, niterations=200, populations=20, logger=logger, ) X = (a=rand(500), b=rand(500)) y = @. 2 * cos(X.a * 23.5) - X.b^2 mach = machine(model, X, y) fit!(mach) # View logs: # $ tensorboard --logdir logs ``` -------------------------------- ### Converting Expression to SymbolicUtils and LaTeX Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/examples.md Converts a found expression to a SymbolicUtils object and then to LaTeX format for visualization. Requires `SymbolicUtils` and `Latexify`. ```julia using SymbolicUtils eqn = node_to_symbolic(r.equations[1][r.best_idx[1]]) ``` ```julia using Latexify latexify(string(eqn)) ``` -------------------------------- ### Manage Populations of Equations Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/types.md A `Population` is an array of `PopMember` objects, where each member represents an equation with associated cost, loss, and birthdate. ```julia ```@docs Population ``` ``` ```julia ```@docs PopMember ``` ``` -------------------------------- ### Define and Evaluate Expressions in Julia Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/examples.md Demonstrates how to define custom operators and variables to construct and evaluate symbolic expressions using the Expression type in SymbolicRegression.jl. Ensure operators and variable names are correctly configured. ```julia using SymbolicRegression # Define options with operators and structure options = Options( binary_operators=[+, -, *], unary_operators=[cos], ) operators = options.operators variable_names = ["x1", "x2"] x1 = Expression( Node{Float64}(feature=1), operators=operators, variable_names=variable_names, ) x2 = Expression( Node{Float64}(feature=2), operators=operators, variable_names=variable_names, ) # Construct and evaluate expression expr = x1 * cos(x2 - 3.2) X = rand(Float64, 2, 100) output = expr(X) ``` -------------------------------- ### Evaluate Template Expression in Julia Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/examples.md Demonstrates evaluating a trained template expression with new input data in SymbolicRegression.jl. The expression can handle multi-dimensional input arrays. ```julia best_expr(randn(3, 20)) ``` -------------------------------- ### Automatic Differentiation with eval_grad_tree_array Source: https://context7.com/astroautomata/symbolicregression.jl/llms.txt Compute gradients of discovered expressions with respect to constants using forward-mode automatic differentiation. Useful for optimizing constants within symbolic expressions. ```julia using SymbolicRegression options = Options(binary_operators=[+, *, -], unary_operators=[cos, sin]) X = randn(Float64, 2, 50) y = @. cos(X[1, :]) + X[1, :] * X[2, :] hof = equation_search(X, y; options=options, niterations=20) best = calculate_pareto_frontier(hof)[end] tree = best.tree # Gradient with respect to constants (df/dC) # Note: This requires constants to be present in the tree. # For this example, we assume no constants are present initially. # If constants were present, you would specify their indices. # For demonstration, let's assume a hypothetical constant at index 0. # grad_output, grad_constants, did_succeed_grad = eval_grad_tree_array(tree, X, options, [0]) # if did_succeed_grad # println("Gradient wrt constants: ", grad_constants) # end ``` -------------------------------- ### SymbolicUtils.jl Interface Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/api.md Converts expression trees to SymbolicUtils.jl expressions. ```APIDOC ## SymbolicUtils.jl interface ```@docs node_to_symbolic ``` Note that use of this function requires `SymbolicUtils.jl` to be installed and loaded. ``` -------------------------------- ### Control Mutation Weights with `MutationWeights` Source: https://context7.com/astroautomata/symbolicregression.jl/llms.txt Configures the relative frequency of mutation types during evolution. Pass `MutationWeights` to `Options` to customize the search. ```julia using SymbolicRegression # Custom weights favoring operator mutation and tree rotation weights = MutationWeights( mutate_constant = 0.05, # Perturb a numeric constant mutate_operator = 0.40, # Replace an operator with another mutate_feature = 0.05, # Change which variable a leaf references swap_operands = 0.10, # Swap left/right arguments of binary op rotate_tree = 0.15, # Rotate tree structure around a node add_node = 0.08, # Append a new node insert_node = 0.02, # Insert node into existing tree delete_node = 0.08, # Remove a node simplify = 0.002, # Algebraic simplification randomize = 0.005, # Replace subtree with random expression do_nothing = 0.013, # Accept current tree unchanged optimize = 0.0, # Optimize constants (as a mutation step) ) options = Options( binary_operators=[+, -, *, /], unary_operators=[cos], mutation_weights=weights, ) X = randn(2, 100) y = @. X[1, :] * cos(X[2, :]) hof = equation_search(X, y; options=options, niterations=30) println(string_tree(calculate_pareto_frontier(hof)[end].tree, options)) ``` -------------------------------- ### Define and Use Higher-Arity Operator Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/examples.md Demonstrates using higher-arity operators by defining a scalar_ifelse function and configuring SRRegressor with a custom operator set. Requires MLJ and SymbolicRegression packages. ```julia using SymbolicRegression, MLJ scalar_ifelse(a, b, c) = a > 0 ? b : c X = randn(3, 100) y = [X[1, i] > 0 ? 2*X[2, i] : X[3, i] for i in 1:100] model = SRRegressor( operators=OperatorEnum( 1 => (), 2 => (+, -, *, /), 3 => (scalar_ifelse,) ), niterations=35, ) mach = machine(model, X', y) fit!(mach) ``` -------------------------------- ### SRLogger for Progress Tracking Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/api.md Integrate custom logging with SymbolicRegression.jl searches. SRLogger wraps any Julia AbstractLogger, enabling tracking with tools like TensorBoardLogger.jl or Wandb.jl. ```julia using TensorBoardLogger logger = SRLogger(TBLogger("logs/run"), log_interval=2) model = SRRegressor(; logger=logger, kws... ) ``` -------------------------------- ### Perform Symbolic Regression with equation_search Source: https://context7.com/astroautomata/symbolicregression.jl/llms.txt The `equation_search` function is the core low-level API for performing symbolic regression. It takes feature matrix `X` and target vector `y`, returning a `HallOfFame` object. Configure search parameters via `Options`, specify parallelism, variable names, and a random seed. ```julia using SymbolicRegression # Data: shape [n_features, n_samples] (column-major, features-first) X = randn(Float32, 5, 100) y = @. 2 * cos(X[4, :]) + X[1, :]^2 - 2 options = Options( binary_operators=[+, *, /, -], unary_operators=[cos, exp], maxsize=25, ) # Single-target search hall_of_fame = equation_search( X, y; options=options, niterations=40, parallelism=:multithreading, # :serial, :multithreading, :multiprocessing numprocs=nothing, # number of worker processes (multiprocessing) variable_names=["a", "b", "c", "d", "e"], seed=0, ) # Inspect the Pareto frontier dominating = calculate_pareto_frontier(hall_of_fame) println("Complexity\tLoss\tEquation") for member in dominating complexity = compute_complexity(member, options) loss = member.loss str = string_tree(member.tree, options) println("$complexity\t$loss\t$str") end # Evaluate the best expression on new data best = dominating[end] output, did_succeed = eval_tree_array(best.tree, X, options) println("Evaluation succeeded: $did_succeed") println("Predictions: ", output[1:5]) ``` -------------------------------- ### MLJ Interface Source: https://github.com/astroautomata/symbolicregression.jl/blob/master/docs/src/api.md Provides access to the MLJ-compatible regression models. ```APIDOC ## MLJ Interface ```@docs SRRegressor MultitargetSRRegressor ``` ```