Skip to content

Commit

Permalink
Merge pull request #360 from MilesCranmer/release-1-changelog
Browse files Browse the repository at this point in the history
Output folder, better TemplateExpression, colored printouts, switch to ProgressMeter
  • Loading branch information
MilesCranmer authored Oct 30, 2024
2 parents 45f1094 + a084404 commit bc9edaf
Show file tree
Hide file tree
Showing 39 changed files with 3,196 additions and 530 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
.dataset*.jl
.hyperparams*.jl
outputs
*.csv
*.bak
*.bkup
performance*txt
*.out
trials*
**/__pycache__
build
dist
Manifest.toml
Manifest*.toml
*.cov
.coveralls.yml
**/*tmp*.jl
Expand Down
1,548 changes: 1,548 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

17 changes: 7 additions & 10 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ Optim = "429524aa-4258-5aef-a3af-852621145aeb"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
ProgressBars = "49802e3a-d2f1-5c88-81d8-b72133a6f568"
ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
StyledStrings = "f489334b-da3d-4c2e-b8f0-e476e12c162b"
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"

[weakdeps]
Expand All @@ -40,13 +41,13 @@ SymbolicRegressionSymbolicUtilsExt = "SymbolicUtils"

[compat]
ADTypes = "^1.4.0"
Compat = "^4.2"
Compat = "^4.16"
ConstructionBase = "<1.5.7"
Dates = "1"
DifferentiationInterface = "0.5, 0.6"
DispatchDoctor = "0.4"
DispatchDoctor = "^0.4.17"
Distributed = "<0.0.1, 1"
DynamicExpressions = "1"
DynamicExpressions = "1.4"
DynamicQuantities = "1"
Enzyme = "0.12"
JSON3 = "1"
Expand All @@ -58,16 +59,12 @@ Optim = "~1.8, ~1.9"
Pkg = "<0.0.1, 1"
PrecompileTools = "1"
Printf = "<0.0.1, 1"
ProgressBars = "~1.4, ~1.5"
ProgressMeter = "1.10"
Random = "<0.0.1, 1"
Reexport = "1"
SpecialFunctions = "0.10.1, 1, 2"
StatsBase = "0.33, 0.34"
StyledStrings = "1"
SymbolicUtils = "0.19, ^1.0.5, 2, 3"
TOML = "<0.0.1, 1"
julia = "1.10"

[extras]
Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9"
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b"
1 change: 1 addition & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
DynamicExpressions = "a40a106e-89c9-4ca8-8020-a735e8728b6b"
Gumbo = "708ec375-b3d6-5a57-a7ce-8257bf98657a"
Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b"

[compat]
Expand Down
26 changes: 17 additions & 9 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@ using SymbolicRegression:
@extend_operators
using DynamicExpressions

DocMeta.setdocmeta!(
SymbolicRegression, :DocTestSetup, :(using LossFunctions); recursive=true
)
DocMeta.setdocmeta!(
SymbolicRegression, :DocTestSetup, :(using DynamicExpressions); recursive=true
)
include("utils.jl")
process_literate_blocks("test")
process_literate_blocks("examples")

readme = open(dirname(@__FILE__) * "/../README.md") do io
read(io, String)
Expand Down Expand Up @@ -88,6 +85,12 @@ open(dirname(@__FILE__) * "/src/index.md", "w") do io
write(io, index_base)
end

DocMeta.setdocmeta!(
SymbolicRegression,
:DocTestSetup,
:(using LossFunctions, DynamicExpressions);
recursive=true,
)
makedocs(;
sitename="SymbolicRegression.jl",
authors="Miles Cranmer",
Expand All @@ -100,7 +103,10 @@ makedocs(;
pages=[
"Contents" => "index_base.md",
"Home" => "index.md",
"Examples" => "examples.md",
"Examples" => [
"Short Examples" => "examples.md",
"Template Expressions" => "examples/template_expression.md",
],
"API" => "api.md",
"Losses" => "losses.md",
"Types" => "types.md",
Expand Down Expand Up @@ -133,9 +139,11 @@ apply_to_a_href!(html.root) do element
element.attributes["href"] = "#LossFunctions." * element.children[1].text
end

# Then, we write the new html to the file:
# Then, we write the new html to the file, only if it has changed:
open("docs/build/losses/index.html", "w") do io
write(io, string(html))
end

deploydocs(; repo="github.com/MilesCranmer/SymbolicRegression.jl.git")
if !haskey(ENV, "JL_LIVERELOAD")
deploydocs(; repo="github.com/MilesCranmer/SymbolicRegression.jl.git")
end
132 changes: 121 additions & 11 deletions docs/src/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,26 +230,39 @@ Note that you can also search for dimensionless units by settings

## 7. Working with Expressions

Expressions in `SymbolicRegression.jl` are represented using the `Expression` type, which combines the raw `Node` type with an `OperatorEnum`. This allows for more flexible and powerful expression manipulation and evaluation.

Here's an example:
Expressions in `SymbolicRegression.jl` are represented using the `Expression{T,Node{T},...}` type, which provides a more robust way to combine structure, operators, and constraints. Here's an example:

```julia
using SymbolicRegression

# Define options with operators
options = Options(; binary_operators=[+, -, *], unary_operators=[cos])
# Define options with operators and structure
options = Options(
binary_operators=[+, -, *],
unary_operators=[cos],
expression_options=(
structure=TemplateStructure(),
variable_constraints=Dict(1 => [1, 2], 2 => [2])
)
)

# Create expression nodes
# Create expression nodes with constraints
operators = options.operators
variable_names = ["x1", "x2"]
x1 = Expression(Node{Float64}(feature=1); operators, variable_names)
x2 = Expression(Node{Float64}(feature=2); operators, variable_names)
x1 = Expression(
Node{Float64}(feature=1),
operators=operators,
variable_names=variable_names,
structure=options.expression_options.structure
)
x2 = Expression(
Node{Float64}(feature=2),
operators=operators,
variable_names=variable_names,
structure=options.expression_options.structure
)

# Construct an expression using the operators from options
# Construct and evaluate expression
expr = x1 * cos(x2 - 3.2)

# Evaluate the expression directly
X = rand(Float64, 2, 100)
output = expr(X)
```
Expand Down Expand Up @@ -330,3 +343,100 @@ to browse the documentation for the Python frontend
[PySR](http://astroautomata.com/PySR), which has additional documentation.
In particular, the [tuning page](http://astroautomata.com/PySR/tuning)
is useful for improving search performance.

## 10. Template Expressions

Template expressions allow you to define structured expressions where different parts can be constrained to use specific variables. In this example, we'll create expressions that output pairs of values.

First, let's set up our basic configuration:

```julia
using SymbolicRegression
using Random: rand
using MLJBase: machine, fit!, report

options = Options(
binary_operators=(+, *, /, -),
unary_operators=(sin, cos)
)
operators = options.operators
variable_names = ["x1", "x2", "x3"]
```

Now we'll create base expressions for each variable:

```julia
x1, x2, x3 = [
Expression(
Node{Float64}(feature=i);
operators=operators,
variable_names=variable_names
)
for i in 1:3
]
```

The key part is defining our template structure. This determines how different parts of the expression combine:

```julia
structure = TemplateStructure{(:f, :g1, :g2)}(;
# Define how to combine vectors of evaluated expressions
combine_vectors=e -> map(
(f, g1, g2) -> (f + g1, f + g2),
e.f, e.g1, e.g2
),
# Define how to combine strings for printing
combine_strings=e -> "( $(e.f) + $(e.g1), $(e.f) + $(e.g2) )",
# Constrain which variables can be used in each part
variable_constraints=(; f=[1, 2], g1=[3], g2=[3])
)
```

Let's generate some example data:

```julia
X = rand(100, 3) .* 10
# Create pairs of target expressions
y = [
(sin(X[i, 1]) + X[i, 3]^2, sin(X[i, 1]) + X[i, 3])
for i in eachindex(axes(X, 1))
]
```

Now we can set up and train our model:

```julia
model = SRRegressor(;
binary_operators=(+, *),
unary_operators=(sin,),
maxsize=25,
expression_type=TemplateExpression,
# Pass options used to instantiate expressions
expression_options=(; structure),
# Our `y` is 2-tuple of values
elementwise_loss=((x1, x2), (y1, y2)) -> (y1 - x1)^2 + (y2 - x2)^2
)

mach = machine(model, X, y)
fit!(mach)
```

After training, we can examine the best expression:

```julia
r = report(mach)
best_expr = r.equations[r.best_idx]

# Access individual parts of the template expression
f_part = get_contents(best_expr).f # Expression using x1 or x2
g1_part = get_contents(best_expr).g1 # Expression using x3
g2_part = get_contents(best_expr).g2 # Expression using x3
```

The above code demonstrates how template expressions can be used to:

- Define structured expressions with multiple components
- Constrains which variables can be used in each component
- Create expressions that can output multiple values

You can even output custom structs - see the more detailed Template Expression example!
2 changes: 1 addition & 1 deletion docs/src/index_base.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Contents

```@contents
Pages = ["examples.md", "api.md", "types.md", "losses.md"]
Pages = ["examples.md", "examples/template_expression.md", "api.md", "types.md", "losses.md"]
```
26 changes: 23 additions & 3 deletions docs/src/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,34 @@ These types allow you to define expressions with parameters that can be tuned to

## Template Expressions

Template expressions are a type of expression that allows you to specify a predefined structure.
This lets you also fit vector expressions, as the custom evaluation structure can simply return
a vector of tuples.
Template expressions allow you to specify predefined structures and constraints for your expressions.
These use the new `TemplateStructure` type to define how expressions should be combined and evaluated.

```@docs
TemplateExpression
TemplateStructure
```

Example usage:

```julia
# Define a template structure
structure = TemplateStructure(
combine=e -> e.f + e.g, # Create normal `Expression`
combine_vectors=e -> (e.f .+ e.g), # Output vector
combine_strings=e -> "($e.f) + ($e.g)", # Output string
variable_constraints=(; f=[1, 2], g=[3]) # Constrain dependencies
)

# Use in options
model = SRRegressor(;
expression_type=TemplateExpression,
expression_options=(; structure=structure)
)
```

The `variable_constraints` field allows you to specify which variables can be used in different parts of the expression.

## Population

Groups of equations are given as a population, which is
Expand Down
Loading

0 comments on commit bc9edaf

Please sign in to comment.