From 67c6493361ab6df1f9830ef6d6e2cbffdce94641 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Mon, 8 Jan 2024 04:33:06 +0000 Subject: [PATCH 1/2] Add Latexify extension --- Project.toml | 6 +++- ext/SymbolicRegressionLatexifyExt.jl | 50 ++++++++++++++++++++++++++++ test/test_latexify.jl | 39 ++++++++++++++++++++++ test/unittest.jl | 4 +++ 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 ext/SymbolicRegressionLatexifyExt.jl create mode 100644 test/test_latexify.jl diff --git a/Project.toml b/Project.toml index 3d8334072..aac58f4e7 100644 --- a/Project.toml +++ b/Project.toml @@ -28,10 +28,12 @@ Tricks = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775" [weakdeps] JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" +Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b" [extensions] SymbolicRegressionJSON3Ext = "JSON3" +SymbolicRegressionLatexifyExt = "Latexify" SymbolicRegressionSymbolicUtilsExt = "SymbolicUtils" [compat] @@ -40,6 +42,7 @@ Compat = "^4.2" DynamicExpressions = "0.13" DynamicQuantities = "0.10" JSON3 = "1" +Latexify = "0.16" LineSearches = "7" LossFunctions = "0.10, 0.11" MLJModelInterface = "1.5, 1.6, 1.7, 1.8" @@ -60,6 +63,7 @@ julia = "1.6" Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" +Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MLJBase = "a7f614a8-145f-11e9-1d2a-a57a1082229d" MLJTestInterface = "72560011-54dd-4dc2-94f3-c5de45b75ecd" @@ -70,4 +74,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [targets] -test = ["Test", "SafeTestsets", "Aqua", "ForwardDiff", "LinearAlgebra", "JSON3", "MLJBase", "MLJTestInterface", "Suppressor", "SymbolicUtils", "Zygote"] +test = ["Test", "SafeTestsets", "Aqua", "ForwardDiff", "Latexify", "LinearAlgebra", "JSON3", "MLJBase", "MLJTestInterface", "Suppressor", "SymbolicUtils", "Zygote"] diff --git a/ext/SymbolicRegressionLatexifyExt.jl b/ext/SymbolicRegressionLatexifyExt.jl new file mode 100644 index 000000000..34f4a8513 --- /dev/null +++ b/ext/SymbolicRegressionLatexifyExt.jl @@ -0,0 +1,50 @@ +module SymbolicRegressionLatexifyExt + +using SymbolicRegression: + Options, + Node, + string_tree, + plus, + sub, + mult, + square, + cube, + pow, + safe_pow, + safe_log, + safe_log2, + safe_log10, + safe_log1p, + safe_sqrt, + safe_acosh, + neg, + greater, + cond, + relu, + logical_or, + logical_and, + gamma, + erf, + erfc, + atanh_clip +using Latexify + +@latexrecipe function latexify_node(::Node) + throw( + ArgumentError( + "You must pass an Options object to latexify in addition to the `Node` object" + ), + ) +end + +@latexrecipe function latexify_node(tree::Node, options::Options; variable_names=:default) + s = string_tree( + tree, + options; + raw=false, + variable_names=(variable_names == :default ? nothing : variable_names), + ) + return s +end + +end diff --git a/test/test_latexify.jl b/test/test_latexify.jl new file mode 100644 index 000000000..0189dd417 --- /dev/null +++ b/test/test_latexify.jl @@ -0,0 +1,39 @@ +using SymbolicRegression +using Latexify: latexify, @L_str + +options = Options(; binary_operators=(+, -, *, /), unary_operators=(cos, sin)) +x1, x2, x3 = (i -> Node(Float64; feature=i)).(1:3) +tree = cos(x1 - 0.9) * x2 - 0.9 / x3 + +latex_output = latexify(tree, options) +@test latex_output == L"$\cos\left( x_1 - 0.9 \right) \cdot x_2 - \frac{0.9}{x_3}$" + +latex_output_diff_names = latexify(tree, options; variable_names=["a", "b", "c"]) +@test latex_output_diff_names == L"$\cos\left( a - 0.9 \right) \cdot b - \frac{0.9}{c}$" + +@test_throws ArgumentError latexify(tree) +VERSION >= v"1.9" && + @test_throws "You must pass an Options object to latexify" latexify(tree) + +# With weird operators: +options = Options(; binary_operators=(-,), unary_operators=(erf,)) +@extend_operators options +x1 = Node(; feature=1) +tree = erf(x1 - 0.9) +@test latexify(tree, options) == L"$\mathrm{erf}\left( x_1 - 0.9 \right)$" + +# With user-defined operator: +myop(x, y) = x + y +options = Options(; binary_operators=(myop,)) +@extend_operators options +x1 = Node(; feature=1) +tree = myop(x1, 0.9) +@test latexify(tree, options) == L"$\mathrm{myop}\left( x_1, 0.9 \right)$" + +# Issue with operators that have underscores: +my_op(x, y) = x + y +options = Options(; binary_operators=(my_op,)) +@extend_operators options +x1 = Node(; feature=1) +tree = my_op(x1, 0.9) +@test_broken latexify(tree, options) == L"$\mathrm{myop}\left( x_1, 0.9 \right)$" diff --git a/test/unittest.jl b/test/unittest.jl index 54ad83dc3..48ccd30b6 100644 --- a/test/unittest.jl +++ b/test/unittest.jl @@ -103,3 +103,7 @@ end @safetestset "Dataset" begin include("test_dataset.jl") end + +@safetestset "Latexify" begin + include("test_latexify.jl") +end From 8927c6bb34fe7a7005e7b5fd95454754d78f7ffb Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Mon, 8 Jan 2024 04:34:00 +0000 Subject: [PATCH 2/2] Remove unused operators in latexify ext --- ext/SymbolicRegressionLatexifyExt.jl | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/ext/SymbolicRegressionLatexifyExt.jl b/ext/SymbolicRegressionLatexifyExt.jl index 34f4a8513..9da304c48 100644 --- a/ext/SymbolicRegressionLatexifyExt.jl +++ b/ext/SymbolicRegressionLatexifyExt.jl @@ -1,32 +1,6 @@ module SymbolicRegressionLatexifyExt -using SymbolicRegression: - Options, - Node, - string_tree, - plus, - sub, - mult, - square, - cube, - pow, - safe_pow, - safe_log, - safe_log2, - safe_log10, - safe_log1p, - safe_sqrt, - safe_acosh, - neg, - greater, - cond, - relu, - logical_or, - logical_and, - gamma, - erf, - erfc, - atanh_clip +using SymbolicRegression: Options, Node, string_tree using Latexify @latexrecipe function latexify_node(::Node)