Note
Go to the end to download the full example code.
Custom: Module with initialization
This examples demonstrates how to make a module with an ininitialization function.
Constant values and other parameters can be passed into the __init__() function, allowing more complex
functionality in a module. The values passed into __init__() can be used in the response and sensitivity
calculation.
10 import pymoto as pym
11
12
13 class ModuleWithInit(pym.Module):
14 """ This module takes a parameter during initialization
15 Example usage:
16 >>> import pymoto as pym
17 >>> x = pym.Signal('x', state=3.5)
18 >>> y = pym.Signal('y')
19 >>> y = WithInit(1.2, optional_value='bar')(x, y) # doctest: +ELLIPSIS
20 [WithInit] Prepare my module
21 value = 1.2
22 optional_value = bar
23 [WithInit] Do my response calculation
24 Message = bar, x = 3.5, y=4.2
25 ...
26 >>> y.state
27 4.2
28 """
29 def __init__(self, value, optional_value='foo'):
30 """ This prepare is called during initialization of the module, and can be used for set-up """
31 print(f'[{type(self).__name__}] Initialize my module')
32 print(f'value = {value}')
33 print(f'optional_value = {optional_value}')
34 self.value = value
35 self.optional_value = optional_value
36
37 def __call__(self, x):
38 print(f'[{type(self).__name__}] Do my response calculation')
39 y = x * self.value
40 print(f"Message = {self.optional_value}, x = {x}, y={y}")
41 return y
42
43 def _sensitivity(self, df_dy):
44 print(f'[{type(self).__name__}] Do my sensitivity calculation')
45 return df_dy * self.value
46
47
48 if __name__ == "__main__":
49 print(__doc__)
50 print("_" * 80)
51 print("-- Module setup")
52
53 # Create signals for the inputs. The argument is the 'tag' of the signal, which is optional.
54 # The tag of the signal can be seen as its name, which can be useful for printing and debugging
55 x = pym.Signal("x", 2.0)
56
57 print(f"\nState initialized to {x.tag} = {x.state}")
58
59 # The module is instantiated using the constructor. The values used in the `__init__` function can be passed here.
60 print("Create Module:")
61 my_module = ModuleWithInit(3.14, optional_value='bar') # Module with extra (constant) parameters
62
63 # After the module is created, it is connected to the input signal.
64 # It will be run once and an output signal is created.
65 print("\n-- Connect module and run forward analysis:")
66 y = my_module(x)
67 y.tag = 'y' # Set a name for the output signal
68
69 # The state of the output signal can be accessed using `state` again
70 print(f"The result: {y.tag} = {y.state}")
71
72 print("\n-- Sensitivity analysis by back-propagation")
73 # Calculate sensitivities
74 print("\nSeed dy/dy = 1.0, so we can calculate dy/dx")
75 # An initial 'seed' sensitivity of the response you're interested in needs to be set. We can do this by setting
76 # the `sensitivity` property
77 y.sensitivity = 1.0
78 my_module.sensitivity()
79 # The sensitivities of the input signals can now be accessed by <Signal>.sensitivity
80 print(f"dy/d{x.tag} = {x.sensitivity}")
81
82 # You can always check your module with finite differencing
83 pym.finite_difference([x], y, random=False)