Custom: Creating a simple module

How to create a simple custom module

In this example it is shown how you can create your own basic module in pymoto that multiplies two values. Both the response (forward) and sensitivity (backward) are implemented and are tested with the pymoto.finite_difference() function.

10 import pymoto as pym
11
12
13 class MyModule(pym.Module):
14     """ Example of a simple module with two inputs and one output """
15     def __call__(self, x1, x2):
16         """ Forward-path calculation is put here """
17         print(f'[{type(self).__name__}] Do my response calculation')
18         print(f'\tInputs are {x1} and {x2}')
19         return x1 * x2
20
21     def _sensitivity(self, df_dy):
22         """ Backward-path sensitivity calculation here.
23         In case df_dy is None, the function will automatically not be called
24         """
25         print(f'[{type(self).__name__}] Do my sensitivity calculation')
26         x1, x2 = self.get_input_states()  # Get the input states if required
27         df_dx1 = df_dy * x2  # Apply chain rule df/dx1 = df/dy * dy/dx1
28         df_dx2 = df_dy * x1
29         return df_dx1, df_dx2  # Return the sensitivities with respect to input signals
30
31
32 if __name__ == "__main__":
33     print(__doc__)
34     print("_" * 80)
35     print("-- Module setup")
36
37     # Create signals for the inputs. The argument is the 'tag' of the signal, which is optional.
38     # The tag of the signal can be seen as its name, which can be useful for printing and debugging
39     x1 = pym.Signal("x1", 2.0)
40
41     # Also create a second input signal (as our module has two inputs)
42     x2 = pym.Signal("x2", 3.0)
43
44     print(f"\nState initialized to {x1.tag} = {x1.state}, {x2.tag} = {x2.state}")
45
46     # The module is instantiated using the constructor. In this case there is not initialization defined, so no
47     # arguments are passed.
48     print("Create Module:")
49     my_module = MyModule()
50
51     # After the module is created, it is connected to the input signals.
52     # It will be run once and an output signal is created.
53     print("\n-- Connect module and run forward analysis:")
54     y = my_module(x1, x2)
55     y.tag = 'y'  # Set a name for the output signal
56
57     # The state of the output signal can be accessed using its `state`
58     print(f"The result: {y.tag} = {y.state}")
59
60     # Calculate sensitivities
61     print("\n-- Sensitivity analysis by back-propagation")
62     print("\nSeed dy/dy = 1.0, so we can calculate dy/dx1 and dy/dx2")
63     # An initial 'seed' sensitivity of the response you're interested in needs to be set. We can do this by setting
64     # the `sensitivity` property
65     y.sensitivity = 1.0
66     my_module.sensitivity()  # Run backpropagation to calculate sensitivities
67     # The sensitivities of the input signals can now be accessed from the `sensitivity` property
68     print(f"dy/d{x1.tag} = {x1.sensitivity}")
69     print(f"dy/d{x2.tag} = {x2.sensitivity}")
70
71     # You can always check your module with finite differencing
72     pym.finite_difference([x1, x2], y, random=False)

Gallery generated by Sphinx-Gallery