2

I have a question similar to this one: How to substitute multiple symbols in an expression in sympy? but in reverse.

I have a sympy expression with numerical values and symbols alike. I would like to substitute all numerical values with symbolic constants. I appreciate that such query is uncommon for sympy. What can I try next?

For example, I have:

-0.5967695*sin(0.15280747*x0 + 0.89256966) + 0.5967695*sin(sin(0.004289882*x0 - 1.5390939)) and would like to replace all numbers with a, b, c etc. ideally in a batch type of way.

The goal is to then apply trig identities to simplify the expression.

halfer
  • 19,824
  • 17
  • 99
  • 186
Hirek
  • 435
  • 3
  • 12

2 Answers2

2

I'm not sure if there is already such a function. If there is not, it's quite easy to build one. For example:

import string

def num2symbols(expr):
    # wild symbol to select all numbers
    w = Wild("w", properties=[lambda t: isinstance(t, Number)])
    # extract the numbers from the expression
    n = expr.find(w)
    # get a lowercase alphabet
    alphabet = list(string.ascii_lowercase)
    # create a symbol for each number
    s = symbols(" ".join(alphabet[:len(n)]))
    # create a dictionary mapping a number to a symbol
    d = {k: v for k, v in zip(n, s)}
    return d, expr.subs(d)

x0 = symbols("x0")
expr = -0.5967695*sin(0.15280747*x0 + 0.89256966) + 0.5967695*sin(sin(0.004289882*x0 - 1.5390939))
d, new_expr = num2symbols(expr)
print(new_expr)
# out: b*sin(c + d*x0) - b*sin(sin(a + f*x0))
print(d):
# {-1.53909390000000: a, -0.596769500000000: b, 0.892569660000000: c, 0.152807470000000: d, 0.596769500000000: e, 0.00428988200000000: f}
Davide_sd
  • 10,578
  • 3
  • 18
  • 30
  • Hi @Davide_sd thank you for your answer. I wanted to edit the function but the queue is full. It should be sympy.Wild and sympy.Number for it to work correctly. If you or anyone else does that, I shall accept the answer. – Hirek Jun 22 '22 at 10:27
  • I just had a closer look, your solution even finds unique numbers, all the better! Thanks again! – Hirek Jun 22 '22 at 10:40
0

I feel like dict.setdefault was made for this purpose in Python :-)

>>> c = numbered_symbols('c',cls=Dummy)
>>> d = {}
>>> econ = expr.replace(lambda x:x.is_Float, lambda x: sign(x)*d.setdefault(abs(x),next(c)))
>>> undo = {v:k for k,v in d.items()}

Do what you want with econ and when done (after saving results to econ)

>>> econ.xreplace(undo) == expr
True

(But if you change econ the exact equivalence may no longer hold.) This uses abs to store symbols so if the expression has constants that differ by a sign they will appear in econ with +/-ci instead of ci and cj.

smichr
  • 16,948
  • 2
  • 27
  • 34