Skip to content

Commit

Permalink
Variable, vars -> Sym, syms
Browse files Browse the repository at this point in the history
  • Loading branch information
shashi committed Apr 26, 2020
1 parent 70b2909 commit fd31083
Show file tree
Hide file tree
Showing 12 changed files with 78 additions and 77 deletions.
34 changes: 17 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,27 @@ SymbolicUtils.jl provides various utilities for symbolic computing.

[![Build Status](https://travis-ci.org/JuliaSymbolics/SymbolicUtils.jl.svg?branch=master)](https://travis-ci.com/github/JuliaSymbolics/SymbolicUtils.jl) [![Coverage Status](https://coveralls.io/repos/github/JuliaSymbolics/SymbolicUtils.jl/badge.svg?branch=master)](https://coveralls.io/github/JuliaSymbolics/SymbolicUtils.jl?branch=master)

## Variables and expressions
## Symbols and expressions

Symbolic variables can be created using the `@vars` macro:
Symbols can be created using the `@syms` macro:

```julia
julia> using SymbolicUtils

julia> @vars a::Integer b c d x::Real y::Number
julia> @syms a::Integer b c d x::Real y::Number
(a, b, c, d, x, y)
```

This macro also defines the Julia variables of the same name and each is set to the its respective symbolic variable object.
This macro also defines the Julia variables of the same name and each is set to the its respective symbolic object.

The associated type `T` in the `@vars a::T` syntax, called `symtype` of the variable, is the type the value of the variable is supposed to be of. These types may determine the rules of symbolic simplification.
The associated type `T` in the `@syms a::T` syntax, called `symtype` of the symbol, is the type the value of the symbol is supposed to be of. These types may determine the rules of symbolic simplification.

Arithmetic and math functions are defined on variables and return `Term` objects which represent function call expressions.
Arithmetic and math functions are defined on symbols and return `Term` objects which represent function call expressions.

Variables can be defined to behave like functions. Both the input and output types for the function can be specified. Any application to that function will only admit either values of those types or symbolic variables of the same `symtype`.
Symbols can be defined to behave like functions. Both the input and output types for the function can be specified. Any application to that function will only admit either values of those types or symbols of the same `symtype`.

```julia
julia> @vars f(x) g(x::Real, y::Real)::Real
julia> @syms f(x) g(x::Real, y::Real)::Real
(f(::Number)::Number, g(::Real, ::Real)::Real)

julia> f(c)
Expand Down Expand Up @@ -67,7 +67,7 @@ _Example:_
Simple rule to turn any `sin` into `cos`:

```julia
julia> @vars a b c
julia> @syms a b c
(a, b, c)

julia> r = @rule sin(~x) => cos(~x)
Expand Down Expand Up @@ -153,7 +153,7 @@ is equivalent to `f(x, y, z, u, v, w)` and commutative if the order of arguments
SymbolicUtils has a special `@acrule` macro meant for rules on functions which are associate
and commutative such as addition and multiplication of real and complex numbers.
```julia
julia> @vars x y
julia> @syms x y
(x, y)

julia> acr = @acrule((~y)^(~n) * ~y => (~y)^(~n+1))
Expand All @@ -169,7 +169,7 @@ Rules are applied to an entire term, they do not see sub-terms
```julia
julia> using SymbolicUtils

julia> @vars x y
julia> @syms x y
(x, y)

julia> r = @rule sin(~x) => cos(~x)
Expand Down Expand Up @@ -233,15 +233,15 @@ Called only if `istree(x)` is `true`. Part of the API required
for `simplify` to work. Other required methods are `operation` and `istree`

#### `to_symbolic(x::S)`
Convert your variable type to a `SymbolicUtils.Variable`. Suppose you have
Convert your variable type to a `SymbolicUtils.Sym`. Suppose you have
```julia
struct MySymbol
s::Symbol
end
```
which could represent any type symbolically, then you would define
```julia
SymbolicUtils.to_symbolic(s::MySymbol) = SymbolicUtils.Variable(s.s)
SymbolicUtils.to_symbolic(s::MySymbol) = SymbolicUtils.Sym(s.s)
```

### Optional
Expand Down Expand Up @@ -283,12 +283,12 @@ julia> ex = 1 + (:x - 2)
How can we use SymbolicUtils.jl to convert `ex` to `(-)(:x, 1)`? We simply implement `istree`,
`operation`, `arguments` and `to_symbolic` and we'll be off to the races:
```julia
using SymbolicUtils: Variable, istree, operation, arguments, to_symbolic
using SymbolicUtils: Sym, istree, operation, arguments, to_symbolic

SymbolicUtils.istree(ex::Expr) = ex.head == :call
SymbolicUtils.operation(ex::Expr) = ex.args[1]
SymbolicUtils.arguments(ex::Expr) = ex.args[2:end]
SymbolicUtils.to_symbolic(s::Symbol) = Variable(s)
SymbolicUtils.to_symbolic(s::Symbol) = Sym(s)

julia> simplify(ex)
(-1 + x)
Expand All @@ -298,7 +298,7 @@ Term{Any}
f: + (function of type typeof(+))
arguments: Array{Any}((2,))
1: Int64 -1
2: Variable{Any}
2: Sym{Any}
name: Symbol x
```
this thing returns a `Term{Any}`, but it's not hard to convert back to `Expr`:
Expand Down Expand Up @@ -327,7 +327,7 @@ Term{Real}
f: + (function of type typeof(+))
arguments: Array{Any}((2,))
1: Int64 -1
2: Variable{Real}
2: Sym{Real}
name: Symbol x
```
and now all our analysis is able to figure out that the `Term`s are `Number`s.
Expand Down
2 changes: 1 addition & 1 deletion src/SymbolicUtils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ else
end


export @vars, term, @fun, showraw
export @syms, term, @fun, showraw
include("types.jl")


Expand Down
4 changes: 2 additions & 2 deletions src/methods.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ for f in diadic
T::Type{<:Number},
S::Type{<:Number}) = promote_type(T, S)

for T in [Variable, Term]
for S in [Variable, Term]
for T in [Sym, Term]
for S in [Sym, Term]
@eval (::$(typeof(f)))(a::$T, b::$S) = term($f, a, b)
end
@eval begin
Expand Down
4 changes: 2 additions & 2 deletions src/rule_dsl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ _Example:_
Simple rule to turn any `sin` into `cos`:
```julia
julia> @vars a b c
julia> @syms a b c
(a, b, c)
julia> r = @rule sin(~x) => cos(~x)
Expand Down Expand Up @@ -176,7 +176,7 @@ Base.show(io::IO, acr::ACRule) = print(io, "ACRule(", acr.rule, ")")

function (acr::ACRule)(term)
r = Rule(acr)
if term isa Variable
if term isa Sym
r(term)
else
f = operation(term)
Expand Down
6 changes: 3 additions & 3 deletions src/simplify.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ _isone(x::Number) = isone(x)
<(a::Number, b::Symbolic) = true

arglength(a) = length(arguments(a))
function <(a::Variable, b::Term)
function <(a::Sym, b::Term)
args = arguments(b)
if length(args) === 2
n1, n2 = !isnumber(args[1]) , !isnumber(args[2])
Expand Down Expand Up @@ -54,7 +54,7 @@ function <ₑ(a::Variable, b::Term)
end
end

<(a::Symbolic, b::Variable) = !(b <ₑ a)
<(a::Symbolic, b::Sym) = !(b <ₑ a)

function <(a::Symbol, b::Symbol)
# Enforce the order [+,-,\,/,^,*]
Expand All @@ -75,7 +75,7 @@ function <ₑ(a::Symbol, b::Symbol)
end
end

<(a::Variable, b::Variable) = a.name < b.name
<(a::Sym, b::Sym) = a.name < b.name
<(a::T, b::S) where {T, S} = T===S ? isless(a, b) : nameof(T) < nameof(S)

function <(a::Term, b::Term)
Expand Down
59 changes: 30 additions & 29 deletions src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -92,56 +92,57 @@ Base.:(==)(a::Symbolic, b::Symbolic) = a === b || isequal(a,b)

#--------------------
#--------------------
#### Variables
#### Syms
#--------------------
"""
Variable{T}(name::Symbol)
Sym{T}(name::Symbol)
A named variable of type `T`. Type `T` can be `FnType{X,Y}` which
means the variable is a function with the type signature X -> Y where
`X` is a tuple type of arguments and `Y` is any type.
"""
struct Variable{T} <: Symbolic{T}
struct Sym{T} <: Symbolic{T}
name::Symbol
end

Variable(x) = Variable{symtype(x)}(x)
const Variable = Sym # old name
Sym(x) = Sym{symtype(x)}(x)

Base.nameof(v::Variable) = v.name
Base.nameof(v::Sym) = v.name

Base.:(==)(a::Variable, b::Variable) = a === b
Base.:(==)(a::Sym, b::Sym) = a === b

Base.:(==)(::Variable, ::Symbolic) = false
Base.:(==)(::Sym, ::Symbolic) = false

Base.:(==)(::Symbolic, ::Variable) = false
Base.:(==)(::Symbolic, ::Sym) = false

Base.isequal(v1::Variable, v2::Variable) = isequal(v1.name, v2.name)
Base.isequal(v1::Sym, v2::Sym) = isequal(v1.name, v2.name)

Base.show(io::IO, v::Variable) = print(io, v.name)
Base.show(io::IO, v::Sym) = print(io, v.name)

#---------------------------
#---------------------------
#### Function-like variables
#---------------------------

# Maybe don't even need a new type, can just use Variable{FnType}
# Maybe don't even need a new type, can just use Sym{FnType}
struct FnType{X<:Tuple,Y} end

(f::Variable{<:FnType})(args...) = term(f, args...)
(f::Sym{<:FnType})(args...) = term(f, args...)

function (f::Variable)(args...)
error("Variable $f of type $F are not callable. " *
"Use @vars $f(var1, var2,...) to create it as a callable. " *
function (f::Sym)(args...)
error("Sym $f of type $F are not callable. " *
"Use @syms $f(var1, var2,...) to create it as a callable. " *
"See ?@fun for more options")
end

"""
`promote_symtype(f::Variable{FnType{X,Y}}, arg_symtypes...)`
`promote_symtype(f::Sym{FnType{X,Y}}, arg_symtypes...)`
The resultant type of applying variable `f` to arugments of symtype `arg_symtypes...`.
if the arguments are of the wrong type then this function will error.
"""
function promote_symtype(f::Variable{FnType{X,Y}}, args...) where {X, Y}
function promote_symtype(f::Sym{FnType{X,Y}}, args...) where {X, Y}
nrequired = fieldcount(X)
ngiven = nfields(args)

Expand All @@ -159,39 +160,39 @@ function promote_symtype(f::Variable{FnType{X,Y}}, args...) where {X, Y}
end

"""
@vars <lhs_expr>[::T1] <lhs_expr>[::T2]...
@syms <lhs_expr>[::T1] <lhs_expr>[::T2]...
For instance:
@vars foo::Real bar baz(x, y::Real)::Complex
@syms foo::Real bar baz(x, y::Real)::Complex
Create one or more variables. `<lhs_expr>` can be just a symbol in which case
it will be the name of the variable, or a function call in which case a function-like
variable which has the same name as the function being called. The Variable type, or
in the case of a function-like Variable, the output type of calling the function
variable which has the same name as the function being called. The Sym type, or
in the case of a function-like Sym, the output type of calling the function
can be set using the `::T` syntax.
# Examples:
- `@vars foo bar::Real baz::Int` will create
- `@syms foo bar::Real baz::Int` will create
variable `foo` of symtype `Number` (the default), `bar` of symtype `Real`
and `baz` of symtype `Int`
- `@vars f(x) g(y::Real, x)::Int h(a::Int, f(b))` creates 1-arg `f` 2-arg `g`
- `@syms f(x) g(y::Real, x)::Int h(a::Int, f(b))` creates 1-arg `f` 2-arg `g`
and 2 arg `f`. The second argument to `h` must be a one argument function-like
variable. So, `h(1, g)` will fail and `h(1, f)` will work.
"""
macro vars(xs...)
macro syms(xs...)
defs = map(xs) do x
n, t = _name_type(x)
:($(esc(n)) = Variable{$(esc(t))}($(Expr(:quote, n))))
:($(esc(n)) = Sym{$(esc(t))}($(Expr(:quote, n))))
end

Expr(:block, defs...,
:(tuple($(map(x->esc(_name_type(x).name), xs)...))))
end

function vars_syntax_error()
error("Incorrect @vars syntax. Try `@vars x::Real y::Complex g(a) f(::Real)::Real` for instance.")
function syms_syntax_error()
error("Incorrect @syms syntax. Try `@syms x::Real y::Complex g(a) f(::Real)::Real` for instance.")
end

function _name_type(x)
Expand All @@ -212,11 +213,11 @@ function _name_type(x)
elseif x isa Expr && x.head === :call
return _name_type(:($x::Number))
else
vars_syntax_error()
syms_syntax_error()
end
end

function Base.show(io::IO, f::Variable{<:FnType{X,Y}}) where {X,Y}
function Base.show(io::IO, f::Sym{<:FnType{X,Y}}) where {X,Y}
print(io, f.name)
argrepr = join(map(t->"::"*string(t), X.parameters), ", ")
print(io, "(", argrepr, ")")
Expand Down
16 changes: 8 additions & 8 deletions test/basics.jl
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
using SymbolicUtils: Variable, FnType, Term, symtype
using SymbolicUtils: Sym, FnType, Term, symtype
using SymbolicUtils
using Test

@testset "@vars" begin
@testset "@syms" begin
let
@vars a b::Float64 f(::Real) g(p, h(q::Real))::Int
@syms a b::Float64 f(::Real) g(p, h(q::Real))::Int

@test a isa Variable{Number}
@test a isa Sym{Number}
@test a.name === :a

@test b isa Variable{Float64}
@test b isa Sym{Float64}
@test b.name === :b

@test f isa Variable{FnType{Tuple{Real}, Number}}
@test f isa Sym{FnType{Tuple{Real}, Number}}
@test f.name === :f

@test g isa Variable{FnType{Tuple{Number, FnType{Tuple{Real}, Number}}, Int}}
@test g isa Sym{FnType{Tuple{Number, FnType{Tuple{Real}, Number}}, Int}}
@test g.name === :g

@test f(b) isa Term
Expand All @@ -30,7 +30,7 @@ using Test
end

@testset "methods test" begin
@vars w::Complex z::Complex a::Real b::Real x
@syms w::Complex z::Complex a::Real b::Real x

@test w + z == Term{Complex}(+, [w, z])
@test z + a == Term{Number}(+, [z, a])
Expand Down
6 changes: 3 additions & 3 deletions test/fuzz.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ using SymbolicUtils: showraw

const leaf_funcs = [()->100*randn(),
()->rand(-100:100),
()->rand(@vars a b c d e f),
()->rand(@vars a b c d e f)]
()->rand(@syms a b c d e f),
()->rand(@syms a b c d e f)]

const fns = vcat(1 .=> SymbolicUtils.monadic,
2 .=> vcat(SymbolicUtils.diadic, fill(+, 5), [-,-], fill(*, 5)),
Expand All @@ -16,7 +16,7 @@ const fns = vcat(1 .=> SymbolicUtils.monadic,
function gen_rand_expr(inputs; leaf_prob=0.92, depth=0, min_depth=1, max_depth=5)
if depth > max_depth || (min_depth <= depth && rand() < leaf_prob)
leaf = rand(leaf_funcs)()
if leaf isa SymbolicUtils.Variable
if leaf isa SymbolicUtils.Sym
push!(inputs, leaf)
end
return leaf
Expand Down
Loading

0 comments on commit fd31083

Please sign in to comment.