# 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 an `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.

Symbolics supports creating variables that denote an array of some size.

```
julia> @variables x[1:3]
1-element Vector{Symbolics.Arr{Num, 1}}:
x[1:3]
julia> @variables y[1:3, 1:6] # support for tensors
1-element Vector{Symbolics.Arr{Num, 2}}:
y[1:3,1:6]
julia> @variables t z(t)[1:3] # also works for dependent variables
2-element Vector{Any}:
t
(z(t))[1:3]
```

A symbol or expression that represents an array can be turned into an array of symbols or expressions using the `scalarize`

function.

```
julia> Symbolics.scalarize(z)
3-element Vector{Num}:
(z(t))[1]
(z(t))[2]
(z(t))[3]
```

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, :value_b, :value_c)
julia> vars = @variables t $a $b(t) $c(t)[1:3]
4-element Vector{Any}:
t
runtime_symbol_value
value_b(t)
(value_c(t))[1:3]
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, rhs) -> Any
```

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
(broadcast(~, A, B))[1:3,1:3]
julia> A .~ 3x
(broadcast(~, A, 3x))[1:3,1:3]
```

## 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 to allow calling functions which are restricted to `Number`

or `Real`

.

```
using Symbolics
@variables t x y z(t);
Symbolics.operation(Symbolics.value(x + y))
```

`+ (generic function with 542 methods)`

`Symbolics.operation(Symbolics.value(z))`

\[ \begin{equation} z \end{equation} \]

`Symbolics.arguments(Symbolics.value(x + y))`

```
2-element Vector{Any}:
x
y
```

Note that Julia converts irrationals — like `π`

and `ℯ`

— to `Float64`

whenever they are involved in arithmetic with other numbers, including integers. An expression like `2π`

will be converted to a float immediately, so an expression like `2π * x`

will leave the symbolic `x`

multiplied by a `Float64`

. It may be preferable to have a symbolic representation of `π`

also, which can be achieved with `Num(π)`

. For generic programming, it may be helpful to simply redefine the variable `π`

to be of the same type as some other argument, as in

```
function f(x)
let π=oftype(x, π)
1 + (2//3 + 4π/5) * x
end
end
f(t)
```

\[ \begin{equation} 1 + t \left( \frac{2}{3} + \frac{4}{5} \mathrm{identity}\left( \pi \right) \right) \end{equation} \]

This will work for any floating-point input, as well as symbolic input.

## 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`

— Functionistree(x)

Returns `true`

if `x`

is a term. If true, `operation`

, `arguments`

must also be defined for `x`

appropriately.

`SymbolicUtils.operation`

— Functionoperation(x)

If `x`

is a term as defined by `istree(x)`

, `operation(x)`

returns the head of the term if `x`

represents a function call, for example, the head is the function being called.

`SymbolicUtils.arguments`

— Functionarguments(x)

Get the arguments of `x`

, must be defined if `istree(x)`

is `true`

.