From fa13646df2873f877871e88868f1130eef2f54b7 Mon Sep 17 00:00:00 2001 From: Michael Reed Date: Sun, 15 Apr 2018 17:12:00 -0400 Subject: [PATCH] updated documentation and test coverage --- README.md | 69 ++++++++++++++++++++++++++++----------- docs/make.jl | 2 +- docs/src/index.md | 79 +++++++++++++++++++++++++++++++-------------- docs/src/library.md | 42 ++++++++++++++++++++---- src/parser.jl | 2 +- src/rexpr.jl | 17 +++++++++- test/runtests.jl | 16 +++++++++ 7 files changed, 174 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 2e8c855..ae4945d 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,9 @@ Interface for applying symbolic manipulation on [Julia expressions](https://docs * interface link communicates and interprets via various reduce output modes using `rcall` method; * high-level reduce-julia syntax parser-generator walks arbitrary expression to rewrite mathematical code; * import operators from REDUCE using code generation to apply to arbitrary computational expressions; -* interactive `reduce>` REPL within the Julia terminal window activated by `}` key. +* interactive `reduce>` REPL within the Julia terminal window activated by `}` key; +* extended arithmetic operators `+`,`-`,`*`,`^`,`/`,`//` compute on `Symbol` and `Expr` types; +* provides over 330 internal and external methods each supporting many argument types. ## Setup @@ -53,24 +55,27 @@ View the documentation [stable](https://chakravala.github.io/Reduce.jl/stable) / Reduce expressions encapsulated into `RExpr` objects can be manipulated within julia using the standard syntax. Create an expression object either using the `RExpr("expression")` string constructor or `R"expression"`. Additionally, arbitrary julia expressions can also be parsed directly using the `RExpr(expr)` constructor. Internally `RExpr` objects are represented as an array that can be accessed by calling `*.str[n]` on the object. -Sequences of reduce statements are automatically parsed into julia `quote` blocks using the `RExpr` constructor, which can `parse` back into a julia expression. +When `Reduce` is used in Julia, all of the standard arithmetic operations are now extended to also work on `Symbol` and `Expr` types. ```Julia -julia> :((x+im+π)^2; int(1/(1+x^3),x)) |> RExpr -^(+(x,i,pi),2); -int(/(1,+(1,^(x,3))),x); +julia> 1-1/:n +:((n - 1) // n) -julia> rcall(ans,:expand) |> parse -quote - (((π + 2x) * π + x ^ 2) - 1) + 2 * (π + x) * im - ((2 * sqrt(3) * atan((2x - 1) // sqrt(3)) - log((x ^ 2 - x) + 1)) + 2 * log(x + 1)) // 6 -end -``` -Call `split(::RExpr)` to create a new `RExpr` object with all expressions split into separate array elements. +julia> ans^-:n +:(1 // ((n - 1) // n) ^ n) +julia> limit(ans,:n,Inf) +e = 2.7182818284590... +``` +Julia abstract syntax trees are automatically converted into sequences of reduce statements that are in return parsed into julia `quote` blocks using the `RExpr` constructor. The `rcall` method is used to evaluate any type of expression. ```Julia julia> :(int(sin(im*x+pi)^2-1,x)) |> rcall -:(-(((e ^ (4x) + 4 * e ^ (2x) * x) - 1)) // (8 * e ^ (2x))) +:((1 - (e ^ (4x) + 4 * e ^ (2x) * x)) // (8 * e ^ (2x))) +``` +However, there are often multiple equivalent ways of achieving the same result: +```Julia +julia> int(sin(im*:x+π)^2-1,:x) +:((1 - (e ^ (4x) + 4 * e ^ (2x) * x)) // (8 * e ^ (2x))) ``` The output of `rcall` will be the same as its input type. ```Julia @@ -78,6 +83,18 @@ julia> "int(sin(y)^2, y)" |> rcall "( - cos(y)*sin(y) + y)/2" ``` Use `rcall(expr,switches...)` to evaluate `expr` using REDUCE mode `switches` like `:expand`, `:factor`, and `:latex`. +```Julia +julia> :((x+im+π)^2; int(1/(1+x^3),x)) |> RExpr +^(+(x,i,pi),2); +int(/(1,+(1,^(x,3))),x); + +julia> rcall(ans,:horner) |> parse +quote + ((π + 2x) * π + 2 * (π + x) * im + x ^ 2) - 1 + ((2 * sqrt(3) * atan((2x - 1) // sqrt(3)) - log((x ^ 2 - x) + 1)) + 2 * log(x + 1)) // 6 +end +``` +Call `split(::RExpr)` to create a new `RExpr` object with all expressions internally split into separate array elements. Mathematical operators and REDUCE modes can be applied directly to `Expr` and `RExpr` objects. ```Julia @@ -91,11 +108,23 @@ Although not all language features have been implemented yet, it is possible to julia> Expr(:for,:(i=2:34),:(product(i))) |> rcall :(@big_str "295232799039604140847618609643520000000") ``` +The `squash` function provides a way to reduce full program blocks into simplified functions, +```Julia +julia> Expr(:function,:(example(a,b)),quote + z = 3 + target = z * :a * :b + z -= 1 + target += z*(1-:a)*(1-:b) + end) |> squash |> factor +:(function example(a, b) + (5b - 2) * a - 2 * (b - 1) + end) +``` ### Output mode Various output modes are supported. While in the REPL, the default `nat` output mode will be displayed for `RExpr` objects. - ```Julia - julia> :(sin(x*im) + cos(y*φ)) |> RExpr +```Julia +julia> :(sin(x*im) + cos(y*φ)) |> RExpr (sqrt(5) + 1)*y cos(-----------------) + sinh(x)*i @@ -105,14 +134,14 @@ This same output can also be printed to the screen by calling `print(nat(r))` me It is possible to direclty convert a julia expression object to LaTeX code using the `latex` method. ```Julia -julia> print(@latex sin(x*im) + cos(y*φ)) +julia> print(@latex sin(x) + cos(y*φ)) \begin{displaymath} -\cos \left(\left(\left(\sqrt {5}+1\right) y\right)/2\right)+\sinh \,x\: i +\cos \left(\left(\left(\sqrt {5}+1\right) y\right)/2\right)+\sin \,x \end{displaymath} ``` -Internally, this command essentially expands to `rcall(:(sin(x*im) + cos(y*φ)),:latex) |> print`, which is equivalent. +Internally, this command essentially expands to `rcall(:(sin(x) + cos(y*φ)),:latex) |> print`, which is equivalent. -![latex-equation](https://latex.codecogs.com/svg.latex?\cos&space;\left(\left(\left(\sqrt&space;{5}+1\right)&space;y\right)/2\right)+\sinh&space;x\:&space;i) +![latex-equation](https://latex.codecogs.com/svg.latex?\cos&space;\left(\left(\left(\sqrt&space;{5}+1\right)&space;y\right)/2\right)+\sin&space;x) In `IJulia` the display output of `RExpr` objects will be rendered LaTeX with the `rlfi` REDUCE package in `latex` mode. @@ -145,6 +174,8 @@ Releases of `Reduce.jl` enable the general application of various REDUCE functio If the `reduce>` REPL is not appearing when `}` is pressed or the `Reduce.PSL` pipe is broken, the session can be restored by simply calling `Reduce.Reset()`, without requiring a restart of `julia` or reloading the package. This kills the currently running `redpsl` session and then re-initializes it for new use. +Otherwise, questions can be asked on gitter/discourse or submit your issue or pull-request if you require additional features or noticed some unusual edge-case behavior. + ### OhMyREPL Compatibility Reduce.jl is compatible with the [OhMyREPL.jl](https://github.com/KristofferC/OhMyREPL.jl) package. diff --git a/docs/make.jl b/docs/make.jl index 014153d..75b45fc 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -23,7 +23,7 @@ deploydocs( branch = "gh-pages", latest = "master", osname = "linux", - julia = "0.5", + julia = "0.6", deps = nothing, make = nothing, ) diff --git a/docs/src/index.md b/docs/src/index.md index c3e6aa3..aa5cd5e 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -36,6 +36,8 @@ Interface for applying symbolic manipulation on [Julia expressions](https://docs * high-level reduce-julia syntax parser-generator walks arbitrary expression to rewrite mathematical code; * import operators from REDUCE using code generation to apply to arbitrary computational expressions; * interactive `reduce>` REPL within the Julia terminal window activated by `}` key. +* extended arithmetic operators `+`,`-`,`*`,`^`,`/`,`//` compute on `Symbol` and `Expr` types; +* provides over 330 internal and external methods each supporting many argument types. ## Setup @@ -69,24 +71,27 @@ Releases of `Reduce.jl` enable the general application of various REDUCE functio Reduce expressions encapsulated into `RExpr` objects can be manipulated within julia using the standard syntax. Create an expression object either using the `RExpr("expression")` string constructor or `R"expression"`. Additionally, arbitrary julia expressions can also be parsed directly using the `RExpr(expr)` constructor. Internally `RExpr` objects are represented as an array that can be accessed by calling `*.str[n]` on the object. -Sequences of reduce statements are automatically parsed into julia `quote` blocks using the `RExpr` constructor, which can `parse` back into a julia expression. +When `Reduce` is used in Julia, all of the standard arithmetic operations are now extended to also work on `Symbol` and `Expr` types. ```Julia -julia> :((x+im+π)^2; int(1/(1+x^3),x)) |> RExpr -^(+(x,i,pi),2); -int(/(1,+(1,^(x,3))),x); +julia> 1-1/:n +:((n - 1) // n) -julia> rcall(ans,:expand) |> parse -quote - (((π + 2x) * π + x ^ 2) - 1) + 2 * (π + x) * im - ((2 * sqrt(3) * atan((2x - 1) // sqrt(3)) - log((x ^ 2 - x) + 1)) + 2 * log(x + 1)) // 6 -end -``` -Call `split(::RExpr)` to create a new `RExpr` object with all expressions split into separate array elements. +julia> ans^-:n +:(1 // ((n - 1) // n) ^ n) +julia> limit(ans,:n,Inf) +e = 2.7182818284590... +``` +Julia abstract syntax trees are automatically converted into sequences of reduce statements that are in return parsed into julia `quote` blocks using the `RExpr` constructor. The `rcall` method is used to evaluate any type of expression. ```Julia julia> :(int(sin(im*x+pi)^2-1,x)) |> rcall -:(-(((e ^ (4x) + 4 * e ^ (2x) * x) - 1)) // (8 * e ^ (2x))) +:((1 - (e ^ (4x) + 4 * e ^ (2x) * x)) // (8 * e ^ (2x))) +``` +However, there are often multiple equivalent ways of achieving the same result: +```Julia +julia> int(sin(im*:x+π)^2-1,:x) +:((1 - (e ^ (4x) + 4 * e ^ (2x) * x)) // (8 * e ^ (2x))) ``` The output of `rcall` will be the same as its input type. ```Julia @@ -94,6 +99,18 @@ julia> "int(sin(y)^2, y)" |> rcall "( - cos(y)*sin(y) + y)/2" ``` Use `rcall(expr,switches...)` to evaluate `expr` using REDUCE mode `switches` like `:expand`, `:factor`, and `:latex`. +```Julia +julia> :((x+im+π)^2; int(1/(1+x^3),x)) |> RExpr +^(+(x,i,pi),2); +int(/(1,+(1,^(x,3))),x); + +julia> rcall(ans,:horner) |> parse +quote + ((π + 2x) * π + 2 * (π + x) * im + x ^ 2) - 1 + ((2 * sqrt(3) * atan((2x - 1) // sqrt(3)) - log((x ^ 2 - x) + 1)) + 2 * log(x + 1)) // 6 +end +``` +Call `split(::RExpr)` to create a new `RExpr` object with all expressions internally split into separate array elements. Mathematical operators and REDUCE modes can be applied directly to `Expr` and `RExpr` objects. ```Julia @@ -107,28 +124,40 @@ Although not all language features have been implemented yet, it is possible to julia> Expr(:for,:(i=2:34),:(product(i))) |> rcall :(@big_str "295232799039604140847618609643520000000") ``` +The `squash` function provides a way to reduce full program blocks into simplified functions, +```Julia +julia> Expr(:function,:(example(a,b)),quote + z = 3 + target = z * :a * :b + z -= 1 + target += z*(1-:a)*(1-:b) + end) |> squash |> factor +:(function example(a, b) + (5b - 2) * a - 2 * (b - 1) + end) +``` ### Output mode Various output modes are supported. While in the REPL, the default `nat` output mode will be displayed for `RExpr` objects. ```Julia julia> :(sin(x*im) + cos(y*φ)) |> RExpr - (sqrt(5) + 1)*y - cos(-----------------) + sinh(x)*i - 2 + (sqrt(5) + 1)*y +cos(-----------------) + sinh(x)*i + 2 ``` This same output can also be printed to the screen by calling `print(nat(r))` method. It is possible to direclty convert a julia expression object to LaTeX code using the `latex` method. ```Julia -julia> print(@latex sin(x*im) + cos(y*φ)) +julia> print(@latex sin(x) + cos(y*φ)) \begin{displaymath} -\cos \left(\left(\left(\sqrt {5}+1\right) y\right)/2\right)+\sinh \,x\: i +\cos \left(\left(\left(\sqrt {5}+1\right) y\right)/2\right)+\sin \,x \end{displaymath} ``` -Internally, this command essentially expands to `rcall(:(sin(x*im) + cos(y*φ)),:latex) |> print`, which is equivalent. +Internally, this command essentially expands to `rcall(:(sin(x) + cos(y*φ)),:latex) |> print`, which is equivalent. -![latex-equation](https://latex.codecogs.com/svg.latex?\cos&space;\left(\left(\left(\sqrt&space;{5}+1\right)&space;y\right)/2\right)+\sinh&space;x\:&space;i) +![latex-equation](https://latex.codecogs.com/svg.latex?\cos&space;\left(\left(\left(\sqrt&space;{5}+1\right)&space;y\right)/2\right)+\sin&space;x) In `IJulia` the display output of `RExpr` objects will be rendered LaTeX with the `rlfi` REDUCE package in `latex` mode. @@ -137,17 +166,19 @@ Similar to ? help and ; shell modes in Julia, `Reduce` pro ```Julia reduce> df(atan(golden_ratio*x),x); - 2 2 - sqrt(5)*x + sqrt(5) - x + 1 - ------------------------------- - 4 2 - 2*(x + 3*x + 1) + 2 2 + sqrt(5)*x + sqrt(5) - x + 1 +------------------------------- + 4 2 + 2*(x + 3*x + 1) ``` ## Troubleshooting If the `reduce>` REPL is not appearing when `}` is pressed or the `Reduce.PSL` pipe is broken, the session can be restored by simply calling `Reduce.Reset()`, without requiring a restart of `julia` or reloading the package. This kills the currently running `redpsl` session and then re-initializes it for new use. +Otherwise, questions can be asked on gitter/discourse or submit your issue or pull-request if you require additional features or noticed some unusual edge-case behavior. + ### OhMyREPL Compatibility Reduce.jl is compatible with the [OhMyREPL.jl](https://github.com/KristofferC/OhMyREPL.jl) package. diff --git a/docs/src/library.md b/docs/src/library.md index b8ce935..5f359d1 100644 --- a/docs/src/library.md +++ b/docs/src/library.md @@ -30,22 +30,30 @@ parse load_package ``` +```@docs +sub +``` + +```@docs +squash +``` + ## Imported Operators Reduce `switch` modes callable as functions from Julia -> :expand, :complex, :factor, :expandlog, :combinelog, :precise, :combineexpt, :rounded, :evallhseq, :nat, :latex +> expand, complex, factor, horner, expandlog, combinelog, precise, combineexpt, rounded, evallhseq, nat, latex -Calculus operators -> :df, :int +Reduce operators with multiple arguments +> df, int, limit, sum, prod, +, -, ^, *, /, // Unary operators -> :abs, :conj, :factorial, :floor, :max, :min, :round, :sign, :acos, :acosh, :acot, :acoth, :acsc, :acsch, :asec, :asech, :asin, :asinh, :atan, :atanh, :atan2, :cos, :cosh, :cot, :coth, :csc, :csch, :exp, :hypot, :log, :log10, :sec, :sech, :sin, :sinh, :sqrt, :tan, :tanh, :gamma, :factorize +> abs, conj, factorial, floor, max, min, round, sign, acos, acosh, acot, acoth, acsc, acsch, asec, asech, asin, asinh, atan, atanh, atan2, cos, cosh, cot, coth, csc, csch, exp, hypot, log, log10, sec, sech, sin, sinh, sqrt, tan, tanh, gamma, factorize -> :beta, :besseli, :besselj, :besselk, :bessely, :polygamma, :zeta +> beta, besseli, besselj, besselk, bessely, polygamma, zeta -> :ibeta, :igamma, :ln, :psi, :bernoulli, :continued_fraction, :ci, :dilog, :ei, :si, :airy_ai, :airy_aiprime, :airy_bi, :airy_biprime, :hanekl1, :hankel2, :kummerm, :kummeru, :lommel1, :lommel2, :struveh, :struvel, :whittakerm, :whittakeru, :solidharmonicy, :sphericalharmonicy +> ibeta, igamma, ln, psi, bernoulli, continued_fraction, ci, dilog, ei, si, airy_ai, airy_aiprime, airy_bi, airy_biprime, hanekl1, hankel2, kummerm, kummeru, lommel1, lommel2, struveh, struvel, whittakerm, whittakeru, solidharmonicy, sphericalharmonicy -> :ceiling, :fix, :impart, :repart, :nextprime, :euler, :fibonacci, :motzkin, :random, :random_new_seed +> ceiling, fix, impart, repart, nextprime, euler, fibonacci, motzkin, random, random_new_seed ## Tools & Options @@ -53,6 +61,10 @@ Unary operators Reduce.parsegen ``` +```@docs +Reduce.unfoldgen +``` + ```@docs Reduce.linefilter ``` @@ -64,3 +76,19 @@ Reduce.Rational ```@docs Reduce.SubCall ``` + +```@docs +Reduce.SubHold +``` + +```@docs +Reduce.SubFail +``` + +```@docs +Reduce.ColCheck +``` + +```@docs +Reduce.DisplayLog +``` diff --git a/src/parser.jl b/src/parser.jl index 40ba109..8228994 100644 --- a/src/parser.jl +++ b/src/parser.jl @@ -706,7 +706,7 @@ end """ unfoldgen(::Symbol,::Symbol) -Parser generator that outputs code to walk and manipulate REDUCE expressions +Parser generator that outputs code to walk and manipulate Julia expressions """ function unfoldgen(fun::Symbol,mode::Symbol) modefun = Symbol(:parse,"_",mode) diff --git a/src/rexpr.jl b/src/rexpr.jl index cb33f5b..781a925 100644 --- a/src/rexpr.jl +++ b/src/rexpr.jl @@ -164,13 +164,23 @@ const repjlr = jl_to_r_utf const gexrjl = Regex("($(join(keys(r_to_jl),")|(")))") const gexjlr = Regex("($(join(keys(jl_to_r),")|(")))") +""" + sub(::Union{Dict,Pair},expr) + +Make variable substitutions using Reduce's native sub command +""" sub(syme::String,expr::RExpr) = "sub($syme,$expr)" |> rcall |> RExpr sub(syme::String,expr::T) where T = convert(T,sub(syme,RExpr(expr))) sub(s::Dict{String,String},expr) = sub(_syme(s),expr) sub(s::Dict{<:Any,<:Any},expr) = sub(Dict([=>(string.(RExpr.([b[1],b[2]]))...) for b ∈ collect(s)]...),expr) sub(s::Pair{<:Any,<:Any},expr) = sub(Dict(s),expr) -sub(s::Array{Pair{<:Any,<:Any},1},expr) = sub(Dict(s...),expr) +sub(s::Array{<:Pair{<:Any,<:Any},1},expr) = sub(Dict(s...),expr) + +""" + sub(T::DataType,expr::Expr) +Make a substitution to conver numerical values to type T +""" function sub(T::DataType,ixpr) if typeof(ixpr) == Expr expr = deepcopy(ixpr) @@ -465,6 +475,11 @@ end end end=# +""" + squash(expr) + +Reduces an entire program statement block using symbolic rewriting +""" function squash(expr) typeof(expr) == Expr && if expr.head == :block return @eval $expr diff --git a/test/runtests.jl b/test/runtests.jl index 14b4f0c..509cf78 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -50,3 +50,19 @@ using Base.Test @test :([1 2; 3 4]) |> RExpr |> Reduce.parse |> RExpr == [1 2; 3 4] |> RExpr println() #@test Reduce.repl_init(Base.active_repl)==nothing + +@test nextprime("3") == "5" +@test expand("(x-2)^2") |> RExpr == R"(x-2)^2" +@test nat("x+1") == "\nx + 1\n" +@test macroexpand(@factor(:(x^2+2x+1))) == :((x+1)^2) +@test :x^2 == :(x^2) +@test NaN//NaN |> isnan +@test join(split(R"x+1;x+2"))|> string == "x+1;\nx+2" +@test sub(:x=>7,:x+7) == sub([:x=>7,:z=>21],:z-:x) +@test sub(Float64,prod((:x-:n)^:n,:n,1,7)|>horner) |> typeof == Expr +@test squash(Expr(:function,:(fun(x)),:(z=3;z+=:x))).args[2] == squash(:(y=:x;y+=3)) +@test Expr(:block,:(x+1)) |> RExpr == R"1+x" +@test limit((1-1/:n)^-:n,:n,Inf) == e +@test log(exp(:pi)) == π +@test 2//Inf == 0 +@test Inf//2 == Inf