MathExpression: General math expressions

Evaluating general mathematical expressions with automatic symbolic differentiated sensitivities

This example is identical in behavior to Custom: Simple scalar network, but uses the pymoto.MathExpression module instead of custom modules. The pymoto.MathExpression module allows the user to enter a string with a mathematical formula, which is symbolically differentiated using sympy.

Side note: pymoto.MathExpression is able to operate on both scalar data or numpy-array data (vectors and matrices) in an element-wise fashion. This is very useful, for instance for evaluation of SIMP equation \(\rho = x_0 + (1-x_0)x^3\) used in Compliance minimization, or the Heaviside projection functions used in Robust formulation.

15 import pymoto as pym
16
17 if __name__ == '__main__':
18     print(__doc__)
19
20     # --- SETUP ---
21     x = pym.Signal('x')
22     y = pym.Signal('y')
23     z = pym.Signal('z')
24
25     # Initial values
26     use_vector = True
27     if use_vector:
28         print("The initial values are vectors")
29         import numpy as np
30         x.state = np.array([1.0, 2.0, 3.0, 4.0])
31         y.state = np.array([0.8, 1.2, 1.8, 2.6])
32         z.state = np.array([3.4, 8.5, 4.1, 6.3])
33     else:
34         print("The initial values are scalar")
35         x.state = 1.0
36         y.state = 0.8
37         z.state = 3.4
38
39     # Create modules
40     with pym.Network() as func:
41         ordering = 0
42         if ordering == 0:
43             a = pym.MathExpression("inp0 * sin(inp1)")(x, y)
44             # Instead of  'inp0' and 'inp1', also the tags ('y', 'z') can be used (if they are defined!)
45             b = pym.MathExpression("cos(y) * cos(z)")(y, z)
46             # Signals a and b do not have a tag, so inp0 and inp1 are used
47             g = pym.MathExpression("inp0^2 * (1 + inp1)")(a, b)
48         elif ordering == 1:
49             print("Using an alternative module order")
50             a = pym.MathExpression("cos(inp0) * cos(inp1)")(x, y)
51             b = pym.MathExpression("inp0^2 * (1 + inp1)")(y, z)
52             g = pym.MathExpression("inp1 * sin(inp0)")(a, b)
53         else:
54             raise RuntimeError("Unknown ordering")
55
56         if use_vector:
57             # In case of vector, convert to scalar by summing over the vector elements
58             g_vector = g
59             g = pym.EinSum('i->')(g_vector)
60         g.tag = 'g'
61
62     print("\nCurrent Network:")
63     print(" -> ".join([type(m).__name__ for m in func.mods]))
64
65     print(f"\nThe result is  g(x={x.state}, y={y.state}, z={z.state}) = {g.state}")
66
67     # --- FORWARD ANALYSIS ---
68     # Perform forward analysis with updated values
69     x.state *= 2
70     y.state += 0.1
71     func.response()
72     print(f"\nThe updated result is  g(x={x.state}, y={y.state}, z={z.state}) = {g.state}")
73
74     # --- BACKPROPAGATION ---
75     # Seed the response sensitivity
76     g.sensitivity = 1.0
77
78     # Calculate the sensitivities
79     func.sensitivity()
80
81     print("\nThe sensitivities are:")
82     print("d{0}/d{1} = {2}".format(g.tag, x.tag, x.sensitivity))
83     print("d{0}/d{1} = {2}".format(g.tag, y.tag, y.sensitivity))
84     print("d{0}/d{1} = {2}".format(g.tag, z.tag, z.sensitivity))
85
86     # --- Finite difference checks ---
87     # Do a finite difference check on the entire network
88     pym.finite_difference(x, g, function=func, random=False)

Gallery generated by Sphinx-Gallery