Parser keep symbolic expression
From the call today we decided it would be best for the parser to not do any eager evaluation and keep symbolic equations. I'm not sure where to stick said code in the parser but this will take some expression string and (hopefully) convert it into a symbolic expression without loss of information.
import regex as re
import finesse
import math
# This dict contains all the mappings for symbols
symbolic_definitions = {
"cos" : lambda x: finesse.element.Operation(math.cos, x),
"sin" : lambda x: finesse.element.Operation(math.sin, x),
"tan" : lambda x: finesse.element.Operation(math.tan, x),
"abs" : lambda x: finesse.element.Operation(math.abs, x),
"sqrt" : lambda x: finesse.element.Operation(math.sqrt, x),
"arcsin" : lambda x: finesse.element.Operation(math.asin, x),
"pi" : finesse.element.Constant(math.pi, name="π"),
# special function for making symbolic constants
"_symconst" : lambda x: finesse.element.Constant(x),
}
NAME = r"[a-zA-Z][a-zA-Z0-9_]*"
REFERENCE = fr"\&({NAME}\.{NAME})"
FUNCTION = fr'\s*({NAME})\s*(?<bracket>\((?:[^\(\)]++|(?&bracket))*\))\s*'
NUMERIC = r'(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][-+]?(?:\d+(?:\.\d*)?|\.\d+)+)?'
OP = r'(?:\+|-|\*|\*\*|/|//)'
NUM_OP_NUM = fr"({NUMERIC})\s*({OP})+\s*({NUMERIC})"
c_nopn = re.compile(NUM_OP_NUM)
c_ref = re.compile(REFERENCE)
def check_for_symbolic_functions(cmd):
for fn, brkt in re.findall(FUNCTION, cmd):
if fn not in symbolic_definitions:
raise Exception(f"No symbolic function for `{fn}` has been defined.")
else:
check_functions(brkt[1:-1])
def eval_symbolic(cmd, model=None):
# Check and make sure a symbolic function exists
# which if not, throws a nice error
check_for_symbolic_functions(cmd)
# Here we identify constants and convert every other one
# into a symbol so that they don't get evaluated too soon
cmd = c_nopn.sub(r"\1\2_symconst(\3)", cmd)
# Subs and eval to get symbolic expression
cmd = c_ref.sub(r'\1.ref', cmd)
return eval(cmd, symbolic_definitions, model.elements)
kat = finesse.Model()
kat.parse("l l1 P=2")
print(eval_symbolic("cos(1e3+20+3*pi) + &l1.P**2 + tan(sin(2+3) * (4/5))", kat))
This should return a symbolic form which (near enough) prints to the same equation