Frequently Asked Questions

Limits of Symbolic Computation

Transforming my function to a symbolic equation has failed. What do I do?

If you see the error:

ERROR: TypeError: non-boolean (Num) used in boolean context

this is likely coming from an algorithm which cannot be traced into a purely symbolic algorithm. Many numerical solvers, for instance, have this property. It shows up when you're doing something like if x < tol. If x is a number, then this is true or false. If x is a symbol, then it's x < tol, so Julia just cannot know how many iterations to do and throws an error.

This shows up in adaptive algorithms, for example:

function factorial(x)
  out = x
  while x > 1
    x -= 1
    out *= x
  end
  out
end
factorial (generic function with 1 method)

The number of iterations this algorithm runs for is dependent on the value of x, and so there is no static representation of the algorithm. If x is 5, then it's out = x*(x-1)*(x-2)*(x-3)*(x-4), while if x is 3, then it's out = x*(x-1)*(x-2). It should thus be no surprise that:

using Symbolics
@variables x
try
    factorial(x)
catch e
    e
end
TypeError(:if, "", Bool, x > 1)

fails. It's not that there is anything wrong with this code, but it's not going to work because fundamentally this is not a symbolically-representable algorithm.

The space of algorithms which can be turned into symbolic algorithms is what we call quasi-static, that is, there is a way to represent the algorithm as static. Loops are allowed, but the amount of loop iterations should not require that you know the value of the symbol x. If the algorithm is quasi-static, then Symbolics.jl tracing will produce the static form of the code, unrolling the operations, and generating a flat representation of the algorithm.

What can be done?

If you need to represent this function f symbolically, then you'll need to make sure it's not traced and instead is directly represented in the underlying computational graph. Just like how sqrt(x) symbolically does not try to represent the underlying algorithm, this must be done to your f. This is done by doing @register_symbolic f(x). If you have to define things like derivatives to f, then the function registration documentation.

Equality and set membership tests

Comparing symbols with == produces a symbolic equality, not a Bool. To produce a Bool, call isequal.

To test if a symbol is part of a collection of symbols, i.e., a vector, either create a Set and use in, e.g.

try
    x in [x]
catch e
    e
end
TypeError(:if, "", Bool, x == x)
x in Set([x])
true
any(isequal(x), [x])
true

If == is used instead, you will receive TypeError: non-boolean (Num) used in boolean context. What this error is telling you is that the symbolic x == y expression is being used where a Bool is required, such as if x == y, and since the symbolic expression is held lazily this will error because the appropriate branch cannot be selected (since x == y is unknown for arbitrary symbolic values!). This is why the check isequal(x,y) is required, since this is a non-lazy check of whether the symbol x is always equal to the symbol y, rather than an expression of whether x and y currently have the same value.