# Variable and Equation Types

Symbolics IR mirrors the Julia AST but allows for easy mathematical manipulation by itself following mathematical semantics. The base of the IR is the `Sym`

type, which defines a symbolic variable. Registered (mathematical) functions on `Sym`

s (or `istree`

objects) return an expression that `istree`

. For example, `op1 = x+y`

is one symbolic object and `op2 = 2z`

is another, and so `op1*op2`

is another tree object. Then, at the top, an `Equation`

, normally written as `op1 ~ op2`

, defines the symbolic equality between two operations.

## Types

`Sym`

, `Term`

, and `FnType`

are from SymbolicUtils.jl. Note that in Symbolics, we always use `Sym{Real}`

, `Term{Real}`

, and `FnType{Tuple{Any}, Real}`

. To get the arguments of a `istree`

object use `arguments(t::Term)`

, and to get the operation, use `operation(t::Term)`

. However, note that one should never dispatch on `Term`

or test `isa Term`

. Instead, one needs to use `SymbolicUtils.istree`

to check if `arguments`

and `operation`

is defined.

`Symbolics.@variables`

— MacroDefine one or more unknown variables.

```
@variables t α σ(..) β[1:2]
@variables w(..) x(t) y z(t, α, x)
expr = β[1]* x + y^α + σ(3) * (z - t) - β[2] * w(t - 1)
```

`(..)`

signifies that the value should be left uncalled.

Sometimes it is convenient to define arrays of variables to model things like `x₁,…,x₃`

. The `@variables`

macro supports this with the following syntax:

```
julia> @variables x[1:3]
1-element Vector{Vector{Num}}:
[x₁, x₂, x₃]
julia> @variables y[2:3, 1:5:6] # support for arbitrary ranges and tensors
1-element Vector{Matrix{Num}}:
[y₂ˏ₁ y₂ˏ₆; y₃ˏ₁ y₃ˏ₆]
julia> @variables t z[1:3](t) # also works for dependent variables
2-element Vector{Any}:
t
Num[z₁(t), z₂(t), z₃(t)]
```

Note that `@variables`

returns a vector of all the defined variables.

`@variables`

can also take runtime symbol values by the `$`

interpolation operator, and in this case, `@variables`

doesn't automatically assign the value, instead, it only returns a vector of symbolic variables. All the rest of the syntax also applies here.

```
julia> a, b, c = :runtime_symbol_value, :value_b, :value_c
:runtime_symbol_value
julia> vars = @variables t $a $b(t) $c[1:3](t)
3-element Vector{Num}:
t
runtime_symbol_value
value_b(t)
Num[value_c₁(t), value_c₂(t), value_c₃(t)]
julia> (t, a, b, c)
(t, :runtime_symbol_value, :value_b, :value_c)
```

`Symbolics.Equation`

— Type`struct Equation`

An equality relationship between two expressions.

**Fields**

`lhs`

The expression on the left-hand side of the equation.

`rhs`

The expression on the right-hand side of the equation.

`Base.:~`

— Method```
~(lhs::Num, rhs::Num) -> Equation
```

Create an `Equation`

out of two `Num`

instances, or an `Num`

and a `Number`

.

**Examples**

```
julia> using Symbolics
julia> @variables x y;
julia> @variables A[1:3, 1:3] B[1:3, 1:3];
julia> x ~ y
x ~ y
julia> x - y ~ 0
x - y ~ 0
julia> A .~ B
3×3 Array{Equation,2}:
A₁ˏ₁ ~ B₁ˏ₁ A₁ˏ₂ ~ B₁ˏ₂ A₁ˏ₃ ~ B₁ˏ₃
A₂ˏ₁ ~ B₂ˏ₁ A₂ˏ₂ ~ B₂ˏ₂ A₂ˏ₃ ~ B₂ˏ₃
A₃ˏ₁ ~ B₃ˏ₁ A₃ˏ₂ ~ B₃ˏ₂ A₃ˏ₃ ~ B₃ˏ₃
julia> A .~ 3x
3×3 Array{Equation,2}:
A₁ˏ₁ ~ 3x A₁ˏ₂ ~ 3x A₁ˏ₃ ~ 3x
A₂ˏ₁ ~ 3x A₂ˏ₂ ~ 3x A₂ˏ₃ ~ 3x
A₃ˏ₁ ~ 3x A₃ˏ₂ ~ 3x A₃ˏ₃ ~ 3x
```

## A note about functions restricted to `Number`

s

`Sym`

and `Term`

objects are NOT subtypes of `Number`

. Symbolics provides a simple wrapper type called `Num`

which is a subtype of `Real`

. `Num`

wraps either a Sym or a Term or any other object, defines the same set of operations as symbolic expressions and forwards those to the values it wraps. You can use `Symbolics.value`

function to unwrap a `Num`

.

By default, the `@variables`

macros return Num-wrapped objects so as to allow calling functions which are restricted to `Number`

or `Real`

.

```
julia> @variables t x y z(t);
julia> Symbolics.operation(Symbolics.value(x + y))
+ (generic function with 377 methods)
julia> Symbolics.operation(Symbolics.value(z))
z(::Any)::Real
julia> Symbolics.arguments(Symbolics.value(x + y))
2-element Vector{Sym{Real}}:
x
y
```

## Symbolic Control Flow

Control flow can be expressed in Symbolics.jl in the following ways:

`IfElse.ifelse(cond,x,y)`

: this is a dispatch-able version of the`ifelse`

function provided by`IfElse.jl`

which allows for encoding conditionals in the symbolic branches.

## Inspection Functions

`SymbolicUtils.istree`

— Function`istree(x::T)`

Check if `x`

represents an expression tree. If returns true, it will be assumed that `operation(::T)`

and `arguments(::T)`

methods are defined. Definining these three should allow use of `simplify`

on custom types. Optionally `symtype(x)`

can be defined to return the expected type of the symbolic expression.

`SymbolicUtils.operation`

— Function`operation(x::T)`

Returns the operation (a function object) performed by an expression tree. Called only if `istree(::T)`

is true. Part of the API required for `simplify`

to work. Other required methods are `arguments`

and `istree`

`SymbolicUtils.arguments`

— Function`arguments(x::T)`

Returns the arguments (a `Vector`

) for an expression tree. Called only if `istree(x)`

is `true`

. Part of the API required for `simplify`

to work. Other required methods are `operation`

and `istree`