Questions about Symbolics: remove small constant terms

I have the following symbolic expression with small terms in Julia:

(2.23606797749979 - 2.220446049250313e-16im)*(x[1] + x[4]) + (-2.23606797749979 + 2.220446049250313e-16im)*x[2] + (-2.23606797749979 + 2.220446049250313e-16im)*x[3]

How do I easily remove real or imaginary components throughout the expression that are absolutely below a certain threshold? I would think this would be commonly needed.

I have been unable to find a successful example for doing this with SymbolicUtils rules. Is there an easy way to walk the entire expression tree looking for small constants, even for complex?

Thanks for any help.

Alex G

Open an issue. I can’t recall a function that’ll do this automatically, though it’s not too hard to come up with an expression walk that does it.

A similar question came up here Set as zero low values in symbolic expressions

Maybe the same solution works for you?

2 Likes

The recommendation by @Sevi works. I have only modified it to make more explicit the tolerance

using Symbolics
using Symbolics.SymbolicUtils.Rewriters

tol = 10.0 * eps(Float64)
rule_too_small = @rule ~x::(xs -> xs isa AbstractFloat && abs(xs) <= tol) => 0
eliminatezeros = Prewalk(PassThrough(rule_too_small))

@variables x[1:4]
expr = (2.23606797749979 - 2.220446049250313e-16im)*(x[1] + x[4]) + (-2.23606797749979 + 2.220446049250313e-16im)*x[2] + (-2.23606797749979 + 2.220446049250313e-16im)*x[3]

simplify(expr; rewriter=eliminatezeros) 

It gives the output

simplify(expr; rewriter=eliminatezeros)
2.23606797749979(x[1] + x[4]) - 2.23606797749979x[2] - 2.23606797749979x[3]

It would be nice to have rules like these into a Metatheory.@theory to eliminate multiplications by near ones and additions of near zeros etc

3 Likes

(post deleted by author)

I ended up doing this (thanks for you suggestions) for now, but I don’t like using applicable:

function remove_small_coefficients(p,tol=1e-12)
    small_float = @rule ~x::(xs -> xs isa AbstractFloat && abs(~x) < tol) => 0   
    small_complex_re = @rule ~x::(xs -> xs isa Complex && applicable(real,xs) && abs(real(xs)) < tol) => im*imag(~x)
    small_complex_im = @rule ~x::(xs -> xs isa Complex && applicable(imag,xs) && abs(imag(xs)) < tol) => real(~x)

    r_sm_float = Prewalk(PassThrough(small_float))
    r_sm_complex_re = Prewalk(PassThrough(small_complex_re))
    r_sm_complex_im = Prewalk(PassThrough(small_complex_im))

    p=simplify(p,r_sm_float)
    p=simplify(p,r_sm_complex_re)
    p=simplify(p,r_sm_complex_im)
    simplify(p)
end

Do you actually need applicable ?

For the example expression you gave, the suggestion from @pitsianis seems to work fine – are there example expressions where it doesn’t do the right thing?

It seems to me that complex numbers are always represented as realpart + im * imaginarypart – and also the symbolic expressions get sorted automatically in such a way that the Prewalk already only consumes real numbers, not complex ones.

But probably there a cases I'm not thinking about right now…