Symbolic

The Symbolic sub-module in exudyn.symbolic allows limited symbolic manipulations in Exudyn and is currently under development In particular, symbolic user functions can be created, which allow significant speedup of Python user functions. However, always veryfy your symbolic expressions or user functions, as behavior may be unexpected in some cases.

symbolic.Real

The symbolic Real type allows to replace Python’s float by a symbolic quantity. The symbolic.Real may be directly set to a float and be evaluated as float. However, turing on recording by using exttt{exudyn.symbolic.SetRecording(True)} (on by default), results are stored as expression trees, which may be evaluated in C++ or Python, in particular in user functions, see the following example:

 1import exudyn as exu
 2esym = exu.symbolic     #abbreviation
 3SymReal = esym.Real     #abbreviation
 4
 5#create some variables
 6a = SymReal('a',42.)    #use named expression
 7b = SymReal(13)         #b is 13
 8c = a+b*7.+1.-3         #c stores expression tree
 9d = c                   #d and c are containing same tree!
10print('a: ',a,' = ',a.Evaluate())
11print('c: ',c,' = ',c.Evaluate())
12
13#use special functions:
14d = a+b*esym.sin(a)+esym.cos(SymReal(7))
15print('d: ',d,' = ',d.Evaluate())
16
17a.SetValue(14)          #variable a set to new value; influences d
18print('d: ',d,' = ',d.Evaluate())
19
20a = SymReal(1000)       #a is now a new variable; not updated in d!
21print('d: ',d,' = ',d.Evaluate())
22
23#compute derivatives (automatic differentiation):
24x = SymReal("x",0.5)
25f = a+b*esym.sin(x)+esym.cos(SymReal(7))+x**4
26print('f=',f.Evaluate(), ', diff=',f.Diff(x))
27
28#turn off recording of trees (globally for all symbolic.Real!):
29esym.SetRecording(False)
30x = SymReal(42) #now, only represents a value
31y = x/3.       #directly evaluates to 14

To create a symbolic Real, use aa=symbolic.Real(1.23) to build a Python object aa with value 1.23. In order to use a named value, use pi=symbolic.Real('pi',3.14). Note that in the following, we use the abbreviation SymReal=exudyn.symbolic.Real. Member functions of SymReal, which are not recorded, are:

The class symbolic.Real has the following functions and structures:

  • __init__(value):
    Construct symbolic.Real from float.
  • __init__(name, value):
    Construct named symbolic.Real from name and float.
  • SetValue(valueInit):
    Set either internal float value or value of named expression; cannot change symbolic expressions.
    Example:
    b = SymReal(13)
    b.SetValue(14) #now b is 14
    #b.SetValue(a+3.) #not possible!
    
  • Evaluate():
    return evaluated expression (prioritized) or stored Real value.
  • Diff(var):
    (UNTESTED!) return derivative of stored expression with respect to given symbolic named variable; NOTE: when defining the expression of the variable which shall be differentiated, the variable may only be changed with the SetValue(…) method hereafter!
    Example:
    x=SymReal('x',2)
    f=3*x+x**2*sin(x)
    f.Diff(x) #evaluate derivative w.r.t. x
    
  • value:
    access to internal float value, which is used in case that symbolic.Real has been built from a float (but without a name and without symbolic expression)
  • operator __float__():
    evaluation of expression and conversion of symbolic.Real to Python float
  • operator __str__():
    conversion of symbolic.Real to string
  • operator __repr__():
    representation of symbolic.Real in Python

The remaining operators and mathematical functions are recorded within expressions. Main mathematical operators for SymReal exist, similar to Python, such as:

 1a = SymReal(1)
 2b = SymReal(2)
 3
 4r1 = a+b
 5r1 = a-b
 6r1 = a*b
 7r1 = a/b
 8r1 = -a
 9r1 = a**b
10
11c = SymReal(3.3)
12c += b
13c -= b
14c *= b
15c /= b
16
17c = (a == b)
18c = (a != b)
19c = (a < b)
20c = (a > b)
21c = (a <= b)
22c = (a >= b)
23
24#in most cases, we can also mix with float:
25c = a*7 + SymReal.sin(8)

Mathematical functions may be called with an SymReal or with a float. Most standard mathematical functions exist for symbolic, e.g., as symbolic.abs. HINT: function names are lower-case for compatibility with Python’s math library. Thus, you can easily exchange math.sin with esym.sin, and you may want to use a generic name, such as myMath=symbolic in order to switch between Python and symbolic user functions. The following functions exist:

The class symbolic has the following functions and structures:

  • isfinite(x):
    according to specification of C++ std::isfinite
  • abs(x):
    according to specification of C++ std::fabs
  • round(x):
    according to specification of C++ std::round
  • ceil(x):
    according to specification of C++ std::ceil
  • floor(x):
    according to specification of C++ std::floor
  • sqrt(x):
    according to specification of C++ std::sqrt
  • exp(x):
    according to specification of C++ std::exp
  • log(x):
    according to specification of C++ std::log
  • sin(x):
    according to specification of C++ std::sin
  • cos(x):
    according to specification of C++ std::cos
  • tan(x):
    according to specification of C++ std::tan
  • asin(x):
    according to specification of C++ std::asin
  • acos(x):
    according to specification of C++ std::acos
  • atan(x):
    according to specification of C++ std::atan
  • sinh(x):
    according to specification of C++ std::sinh
  • cosh(x):
    according to specification of C++ std::cosh
  • tanh(x):
    according to specification of C++ std::tanh
  • asinh(x):
    according to specification of C++ std::asinh
  • acosh(x):
    according to specification of C++ std::acosh
  • atanh(x):
    according to specification of C++ std::atanh

The following table lists special functions for SymReal:

The class symbolic has the following functions and structures:

  • sign(x):
    returns 0 for x=0, -1 for x<0 and 1 for x>1.
  • Not(x):
    returns logical not of expression, equal to Python’s ‘not’. Not(True)=False, Not(0.)=True, Not(-0.1)=False
  • min(x, y):
    return minimum of x and y.
  • max(x, y):
    return maximum of x and y.
  • mod(x, y):
    return floating-point remainder of the division operation x / y. For example, mod(5.1, 3) gives 2.1 as a remainder.
  • pow(x, y):
    return \(x^y\).
  • max(x, y):
    return maximum of x and y.
  • IfThenElse(condition, ifTrue, ifFalse):
    Symbolic function for conditional evaluation. If the condition evaluates to True, the expression ifTrue is evaluated, while otherwise expression ifFalse is evaluated
    Example:
    x=SymReal(-1)
    y=SymReal(2,'y')
    a=SymReal.IfThenElse(x<0, y+1, y-1))
    
  • SetRecording(flag):
    Set current (global / module-wide) status of expression recording. By default, recording is on.
    Example:
    SymReal.SetRecording(True)
    
  • GetRecording():
    Get current (global / module-wide) status of expression recording.
    Example:
    symbolic.Real.GetRecording()
    

symbolic.Vector

A symbolic Vector type to replace Python’s (1D) numpy array in symbolic expressions. The symbolic.Vector may be directly set to a list of floats or (1D) numpy array and be evaluated as array. However, turing on recording by using exttt{exudyn.symbolic.SetRecording(True)} (on by default), results are stored as expression trees, which may be evaluated in C++ or Python, in particular in user functions, see the following example:

 1import exudyn as exu
 2esym = exu.symbolic
 3
 4SymVector = esym.Vector
 5SymReal = esym.Real
 6
 7a = SymReal('a',42.)
 8b = SymReal(13)
 9c = a-3*b
10
11#create from list:
12v1 = SymVector([1,3,2])
13print('v1: ',v1)
14
15#create from numpy array:
16v2 = SymVector(np.array([1,3,2]))
17print('v2 initial: ',v2)
18
19#create from list, mixing symbolic expressions and numbers:
20v2 = SymVector([a,42,c])
21
22print('v2 now: ',v2,"=",v2.Evaluate())
23print('v1+v2: ',v1+v2,"=",(v1+v2).Evaluate()) #evaluate as vector
24
25print('v1*v2: ',v1*v2,"=",(v1*v2).Evaluate()) #evaluate as Real
26
27#access of vector component:
28print('v1[2]: ',v1[2],"=",v1[2].Evaluate())   #evaluate as Real

To create a symbolic Vector, use aa=symbolic.Vector([3,4.2,5] to build a Python object aa with values [3,4.2,5]. In order to use a named vector, use v=symbolic.Vector('myVec',[3,4.2,5]). Vectors can be also created from mixed symbolic expressions and numbers, such as v=symbolic.Vector([x,x**2,3.14]), however, this cannot become a named vector as it contains expressions. There is a significance difference to numpy, such that ‘*’ represents the scalar vector multplication which gives a scalar. Furthermore, the comparison operator ‘==’ gives only True, if all components are equal, and the operator ‘!=’ gives True, if any component is unequal. Note that in the following, we use the abbreviation SymVector=exudyn.symbolic.Vector. Note that only functions are able to be recorded. Member functions of SymVector are:

The class symbolic.Vector has the following functions and structures:

  • __init__(vector):
    Construct symbolic.Vector from vector represented as numpy array or list (which may contain symbolic expressions).
  • __init__(name, vector):
    Construct named symbolic.Vector from name and vector represented as numpy array or list (which may contain symbolic expressions).
  • Evaluate():
    Return evaluated expression (prioritized) or stored vector value. (not recorded)
  • SetVector(vector):
    Set stored vector or named vector expression to new given (non-symbolic) vector. Only works, if SymVector contains no expression. (may lead to inconsistencies in recording)
  • NumberOfItems():
    Get size of Vector (may require to evaluate expression; not recording)
  • operator __setitem__(index):
    bracket [] operator for setting a component of the vector. Only works, if SymVector contains no expression. (may lead to inconsistencies in recording)
  • NormL2():
    return (symbolic) L2-norm of vector.
    Example:
    v1 = SymVector([1,4,8])
    length = v1.NormL2() #gives 9.
    
  • MultComponents(other):
    Perform component-wise multiplication of vector times other vector and return result. This corresponds to the numpy multiplication using ‘*’.
    Example:
    v1 = SymVector([1,2,4])
    v2 = SymVector([1,0.5,0.25])
    v3 = v1.MultComponents(v2)
    
  • operator __getitem__(index):
    bracket [] operator to return (symbolic) component of vector, allowing read-access. Index may also evaluate from an expression.
  • operator __str__():
    conversion of SymVector to string
  • operator __repr__():
    representation of SymVector in Python

Standard vector operators are available for SymVector, see the following examples:

 1v = SymVector([1,3,2])
 2w = SymVector([3.3,2.2,1.1])
 3
 4u = v+w
 5u = v-w
 6u = -v
 7#scalar multiplication; evaluates to SymReal:
 8x = v*w
 9#NOTE: component-wise multiplication, returns SymVector:
10u = v.MultComponents(w)
11
12#inplace operators:
13v += w
14v -= w
15v *= SymReal(0.5)

symbolic.Matrix

A symbolic Matrix type to replace Python’s (2D) numpy array in symbolic expressions. The symbolic.Matrix may be directly set to a list of list of floats or (2D) numpy array and be evaluated as array. However, turing on recording by using exttt{exudyn.symbolic.SetRecording(True)} (on by default), results are stored as expression trees, which may be evaluated in C++ or Python, in particular in user functions, see the following example:

 1import exudyn as exu
 2import numpy as np
 3esym = exu.symbolic
 4
 5SymMatrix = esym.Matrix
 6SymReal = esym.Real
 7
 8a = SymReal('a',42.)
 9b = SymReal(13)
10
11#create matrix from list of lists
12m1 = SymMatrix([[1,3,2],[4,5,6]])
13
14#create symbolic matrix from list of lists
15m3 = SymMatrix([[a,3*b,2],[4,5,6]])
16
17#create from numpy array
18m2 = SymMatrix(np.ones((3,3))-np.eye(3))
19
20m1 += m3
21m1 *= 3
22m1 -= 3*m3
23print('m1: ',m1)
24print('m2: ',m2)

To create a symbolic Matrix, use aa=symbolic.Matrix([[3,4.2],[3.3,1.2]] to build a Python object aa. In order to use a named matrix, use v=symbolic.Matrix('myMat',[3,4.2,5]). Matrixs can be also created from mixed symbolic expressions and numbers, such as v=symbolic.Matrix([x,x**2,3.14]), however, this cannot become a named matrix as it contains expressions. There is a significance difference to numpy, such that ‘*’ represents the matrix multplication (compute components from row times column operations). Note that in the following, we use the abbreviation SymMatrix=exudyn.symbolic.Matrix. Member functions of SymMatrix are:

The class symbolic.Matrix has the following functions and structures:

  • __init__(matrix):
    Construct symbolic.Matrix from vector represented as numpy array or list of lists (which may contain symbolic expressions).
  • __init__(name, matrix):
    Construct named symbolic.Matrix from name and vector represented as numpy array or list of lists (which may contain symbolic expressions).
  • Evaluate():
    Return evaluated expression (prioritized) or stored Matrix value. (not recorded)
  • SetMatrix(matrix):
    Set stored Matrix or named Matrix expression to new given (non-symbolic) Matrix. Only works, if SymMatrix contains no expression. (may lead to inconsistencies in recording)
  • NumberOfRows():
    Get number of rows (may require to evaluate expression; not recording)
  • NumberOfColumns():
    Get number of columns (may require to evaluate expression; not recording)
  • operator __setitem__(row, column):
    bracket [] operator for (symbolic) component of Matrix (write-access). Only works, if SymMatrix contains no expression. (may lead to inconsistencies in recording)
  • operator __getitem__(row, column):
    bracket [] operator for (symbolic) component of Matrix (read-access). Row and column may also evaluate from an expression.
  • operator __str__():
    conversion of SymMatrix to string
  • operator __repr__():
    representation of SymMatrix in Python

Standard Matrix operators are available for SymMatrix, see the following examples:

 1m1 = SymMatrix([[1,7],[4,5]])
 2m2 = SymMatrix([[1,2.2],[4,4.3]])
 3v = SymVector([1.5,3])
 4
 5m3 = m1+m2
 6m3 = m1-m2
 7m3 = m1*m2
 8
 9#multiply with scalar
10m3 = 13*m2
11m3 = m2*3.14
12
13#multiply with vector
14m3 = m2*v
15
16#transposed:
17m3 = v*m2 #equals numpy operation m2.T @ v
18
19#inplace operators:
20m1 += m1
21m1 -= m1
22m1 *= 3.14

symbolic.VariableSet

A container for symbolic variables, in particular for exchange between user functions and the model. For details, see the following example:

 1import exudyn as exu
 2import math
 3SymReal = exu.symbolic.Real
 4
 5#use global variable set:
 6variables = exu.symbolic.variables
 7
 8#create a named Real
 9a = SymReal('a',42.)
10
11#regular way to add variable:
12variables.Add('pi', math.pi)
13
14#add named variable (doesn't need a name):
15variables.Add(a)
16
17#print current variable set
18print(variables)
19
20print('pi=',variables.Get('pi').Evaluate()) #3.14
21print('a=',variables.Get('a')) #prints 'a'
22
23x=variables.Get('a')
24print('x=',x.Evaluate()) #x=42
25
26#override a
27variables.Set('a',3.33)
28
29#x is depending on a:
30print('x:',x,"=",x.Evaluate()) #3.33
31
32#create your own variable set
33mySet = esym.VariableSet()

The class symbolic.VariableSet has the following functions and structures:

  • Add(name, value):
    Add a variable with name and value (name may not exist)
  • Add(namedReal):
    Add a variable with named real (name may not exist)
  • Set(name, value):
    Set a variable with name and value (adds new or overrides existing)
  • Get(name):
    Get a variable by name
  • Exists(name):
    Return True, if variable name exists
  • Reset():
    Erase all variables and reset VariableSet
  • NumberOfItems(name):
    Return True, if variable name exists
  • GetNames():
    Get list of stored variable names
  • data[index]= …name, value:
    bracket [] operator for setting a variable to a specific value
  • … = data[index]name:
    bracket [] operator for getting a specific variable by name
  • operator __str__():
    create string of set of variables
  • operator __repr__():
    representation of SymMatrix in Python

symbolic.UserFunction

A class for creating and handling symbolic user functions in C++. Use these functions for high performance extensions, e.g., of existing objects or loadsFor details, see the following example:

 1import exudyn as exu
 2esym = exu.symbolic
 3from exudyn.utilities import * #advancedUtilities with user function utilities included
 4SymReal = exu.symbolic.Real
 5
 6SC = exu.SystemContainer()
 7mbs = SC.AddSystem()
 8
 9#regular Python user function with esym math functions
10def UFload(mbs, t, load):
11    return load*esym.sin(10*(2*pi)*t)
12
13#create symbolic user function from Python user function:
14symFuncLoad = CreateSymbolicUserFunction(mbs, UFload, load, 'loadUserFunction',verbose=1)
15
16#add ground and mass point:
17oGround = mbs.CreateGround()
18oMassPoint = mbs.CreateMassPoint(referencePosition=[1.+0.05,0,0], physicsMass=1)
19
20#add marker and load:
21mc = mbs.AddMarker(MarkerNodeCoordinate(nodeNumber=mbs.GetObject(oMassPoint)['nodeNumber'], coordinate=0))
22load = mbs.AddLoad(LoadCoordinate(markerNumber=mc, load=10,
23                                  loadUserFunction=symFuncLoad))
24
25#print string of symbolic expression of user function (to check if it looks ok):
26print('load user function: ',symFuncLoad)
27
28#test evaluate user function; requires args of user function:
29print('load user function: ',symFuncLoad.Evaluate(mbs, 0.025, 10.))
30
31#now you could add further items or simulate ...

The class symbolic.UserFunction has the following functions and structures:

  • Evaluate():
    Evaluate symbolic function with test values; requires exactly same args as Python user functions; this is slow and only intended for testing
  • SetUserFunctionFromDict(mainSystem, fcnDict, itemIndex, userFunctionName):
    Create C++ std::function (as requested in C++ item) with symbolic user function as recorded in given dictionary, as created with ConvertFunctionToSymbolic(…).
  • operator __repr__():
    Representation of Symbolic function
  • operator __str__():
    Convert stored symbolic function to string