nnodely 1.5.0__tar.gz → 1.5.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. {nnodely-1.5.0/nnodely.egg-info → nnodely-1.5.2}/PKG-INFO +25 -8
  2. {nnodely-1.5.0 → nnodely-1.5.2}/README.md +25 -8
  3. {nnodely-1.5.0 → nnodely-1.5.2}/mplplots/plots.py +66 -22
  4. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/__init__.py +5 -3
  5. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/basic/model.py +6 -7
  6. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/basic/modeldef.py +43 -32
  7. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/basic/relation.py +2 -2
  8. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/exporter/export.py +23 -10
  9. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/exporter/reporter.py +4 -1
  10. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/layers/arithmetic.py +2 -1
  11. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/layers/fir.py +7 -6
  12. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/layers/fuzzify.py +0 -3
  13. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/layers/input.py +8 -8
  14. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/layers/interpolation.py +0 -3
  15. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/layers/output.py +1 -1
  16. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/layers/parameter.py +1 -1
  17. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/layers/part.py +2 -1
  18. nnodely-1.5.2/nnodely/layers/rungekutta.py +143 -0
  19. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/layers/timeoperation.py +6 -6
  20. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/nnodely.py +9 -10
  21. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/operators/composer.py +36 -30
  22. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/operators/exporter.py +15 -1
  23. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/operators/loader.py +25 -8
  24. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/operators/network.py +44 -22
  25. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/operators/trainer.py +21 -12
  26. nnodely-1.5.2/nnodely/operators/validator.py +304 -0
  27. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/support/jsonutils.py +20 -3
  28. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/support/utils.py +3 -4
  29. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/visualizer/dynamicmpl/resultsplot.py +7 -2
  30. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/visualizer/dynamicmpl/trainingplot.py +6 -1
  31. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/visualizer/mplnotebookvisualizer.py +23 -3
  32. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/visualizer/mplvisualizer.py +28 -16
  33. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/visualizer/textvisualizer.py +4 -4
  34. {nnodely-1.5.0 → nnodely-1.5.2/nnodely.egg-info}/PKG-INFO +25 -8
  35. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely.egg-info/SOURCES.txt +1 -0
  36. {nnodely-1.5.0 → nnodely-1.5.2}/pyproject.toml +1 -1
  37. {nnodely-1.5.0 → nnodely-1.5.2}/tests/test_dataset.py +94 -13
  38. {nnodely-1.5.0 → nnodely-1.5.2}/tests/test_export_recurrent.py +35 -3
  39. {nnodely-1.5.0 → nnodely-1.5.2}/tests/test_json.py +25 -1
  40. {nnodely-1.5.0 → nnodely-1.5.2}/tests/test_model_predict.py +12 -11
  41. {nnodely-1.5.0 → nnodely-1.5.2}/tests/test_model_predict_recurrent.py +207 -53
  42. {nnodely-1.5.0 → nnodely-1.5.2}/tests/test_network_element.py +38 -2
  43. {nnodely-1.5.0 → nnodely-1.5.2}/tests/test_parameters_of_train.py +118 -61
  44. {nnodely-1.5.0 → nnodely-1.5.2}/tests/test_results.py +31 -16
  45. {nnodely-1.5.0 → nnodely-1.5.2}/tests/test_train.py +2 -2
  46. {nnodely-1.5.0 → nnodely-1.5.2}/tests/test_train_recurrent.py +131 -57
  47. {nnodely-1.5.0 → nnodely-1.5.2}/tests/test_visualizer.py +53 -6
  48. nnodely-1.5.0/nnodely/operators/validator.py +0 -176
  49. {nnodely-1.5.0 → nnodely-1.5.2}/LICENSE +0 -0
  50. {nnodely-1.5.0 → nnodely-1.5.2}/mplplots/__init__.py +0 -0
  51. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/basic/__init__.py +0 -0
  52. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/basic/loss.py +0 -0
  53. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/basic/optimizer.py +0 -0
  54. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/exporter/__init__.py +0 -0
  55. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/exporter/emptyexporter.py +0 -0
  56. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/exporter/standardexporter.py +0 -0
  57. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/layers/__init__.py +0 -0
  58. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/layers/activation.py +0 -0
  59. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/layers/equationlearner.py +0 -0
  60. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/layers/linear.py +0 -0
  61. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/layers/localmodel.py +0 -0
  62. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/layers/parametricfunction.py +0 -0
  63. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/layers/trigonometric.py +0 -0
  64. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/operators/__init__.py +0 -0
  65. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/support/__init__.py +0 -0
  66. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/support/earlystopping.py +0 -0
  67. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/support/fixstepsolver.py +0 -0
  68. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/support/initializer.py +0 -0
  69. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/support/logger.py +0 -0
  70. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/support/mathutils.py +0 -0
  71. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/visualizer/__init__.py +0 -0
  72. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/visualizer/dynamicmpl/functionplot.py +0 -0
  73. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/visualizer/dynamicmpl/fuzzyplot.py +0 -0
  74. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely/visualizer/emptyvisualizer.py +0 -0
  75. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely.egg-info/dependency_links.txt +0 -0
  76. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely.egg-info/requires.txt +0 -0
  77. {nnodely-1.5.0 → nnodely-1.5.2}/nnodely.egg-info/top_level.txt +0 -0
  78. {nnodely-1.5.0 → nnodely-1.5.2}/setup.cfg +0 -0
  79. {nnodely-1.5.0 → nnodely-1.5.2}/setup.py +0 -0
  80. {nnodely-1.5.0 → nnodely-1.5.2}/tests/test_documentation.py +0 -0
  81. {nnodely-1.5.0 → nnodely-1.5.2}/tests/test_export.py +0 -0
  82. {nnodely-1.5.0 → nnodely-1.5.2}/tests/test_input_dimensions.py +0 -0
  83. {nnodely-1.5.0 → nnodely-1.5.2}/tests/test_losses.py +0 -0
  84. {nnodely-1.5.0 → nnodely-1.5.2}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nnodely
3
- Version: 1.5.0
3
+ Version: 1.5.2
4
4
  Summary: Model-structured neural network framework for the modeling and control of physical systems
5
5
  Author-email: Gastone Pietro Rosati Papini <tonegas@gmail.com>
6
6
  License: MIT License
@@ -138,7 +138,6 @@ Download the source code and install the dependencies using the following comman
138
138
  To contribute to the nnodely framework, you can:
139
139
  - Open a pull request, if you have a new feature or bug fix.
140
140
  - Open an issue, if you have a question or suggestion.
141
- - Contact us via email at [gastone.rosatipapini@unitn.it](mailto:gastone.rosatipapini@unitn.it) or [mattia.piccinini@tum.de](mailto:mattia.piccinini@tum.de) to directly **collaborate** with us **on your project**!
142
141
 
143
142
  We are very happy to collaborate with you!
144
143
 
@@ -190,7 +189,7 @@ where $x[1]$ is the next position of the mass, $F[0]$ is the last sample of the
190
189
 
191
190
  For the input variable `x`, we are using a time window $T_w = 1$ second, which means that we are using the last $N_x$ samples of the variable `x` to estimate the next position of the mass. The value of $N_x$ is equal to $T_w/T_s$, where $T_s$ is the sampling time used to sample the input variable `x`.
192
191
 
193
- In a particular case, our MS-NN formulation becomes equivalent to the discrete-time response of the mass-spring-damper system. This happens when we choose the following values: $N_x = 3$, $h_x$ equal to the characteristic polynomial of the system, and $h_f = T_s^2/m$, where $T_s$ is the sampling time and $m$ is the mass of the system.
192
+ In a particular case, our MS-NN formulation becomes equivalent to the discrete-time response (discretize with Forward-Euler) of the mass-spring-damper system. This happens when we choose the following values: $N_x = 3$, $h_x$ equal to the characteristic polynomial of the system, and $h_f = T_s^2/m$, where $T_s$ is the sampling time and $m$ is the mass of the system.
194
193
 
195
194
  However, our formulation is more general and can take better adapt to model mismatches and noise levels in the measured variables. This improved learning potential can be achieved by using a larger number of samples $N_x$ in the time window of the input variable `x`.
196
195
 
@@ -286,11 +285,29 @@ print(results)
286
285
  ### nnodely Folder
287
286
  This folder contains all the nnodely library files with relative references.
288
287
 
289
- The nnodely main library files are:
290
- 1. __nnodely.py__ the main file for create the structured network.
291
- 2. __model.py__ containts the pytorch template model for the structured network.
292
-
293
- The model structured NN Inputs Outputs and Parameters:
288
+ The `Moldey` main class defined in __nnodely.py__, it contains all the main properties of the nnodely object and it derives from five main operators:
289
+ 1. __composer.py__ contains all the functions to build the networks: `addModel`, `neuralizeModel`, `addConnection`, `addClosedLool` etc..
290
+ 2. __loader.py__ contains the function for managing the dataset, the main function is `dataLoad`.
291
+ 3. __trainer.py__ contains the function for train the network as the `trainModel`.
292
+ 4. __exporter.py__ contains all the function for import and export: `saveModel`, `loadModel`, `exportONNX` etc..
293
+ 5. __validator.py__ contains all the function for validate the model ad the `resultsAnalysis`.
294
+ All the operators derive from `Network`defined in __network.py__, that contains the shared support functions for all the operators.
295
+
296
+ The folder basic contatins the main classes for the low level functionalities:
297
+ 1. __model.py__ containts the pytorch template model for the structured network.
298
+ 2. __modeldef.py__ containts the operation for work with the json model definition.
299
+ 3. __loss.py__ contatins the loss functions.
300
+ 4. __optimizer.py__ contains the optimizer calss.
301
+ 6. __relation.py__ contains all the main classes from which all the layers are derived.
302
+
303
+ The other folders are:
304
+ 1. exporter that contains the classes for the export functions.
305
+ 2. support for the support functions.
306
+ 3. visualizer that contains all the classes related to the visualization.
307
+ 4. And finally the layers folder.
308
+
309
+ The layers folder contains all the layers that can be used in the MSNN.
310
+ In particular, the model structured NN is defined by `Inputs`, `Outputs` and `Parameters`:
294
311
  1. __input.py__ contains the Input class used for create an input for the network.
295
312
  2. __ouptut.py__ contains the Output class used for create an output for the network.
296
313
  3. __parameter.py__ contains the logic for create a generic parameters and constants.
@@ -92,7 +92,6 @@ Download the source code and install the dependencies using the following comman
92
92
  To contribute to the nnodely framework, you can:
93
93
  - Open a pull request, if you have a new feature or bug fix.
94
94
  - Open an issue, if you have a question or suggestion.
95
- - Contact us via email at [gastone.rosatipapini@unitn.it](mailto:gastone.rosatipapini@unitn.it) or [mattia.piccinini@tum.de](mailto:mattia.piccinini@tum.de) to directly **collaborate** with us **on your project**!
96
95
 
97
96
  We are very happy to collaborate with you!
98
97
 
@@ -144,7 +143,7 @@ where $x[1]$ is the next position of the mass, $F[0]$ is the last sample of the
144
143
 
145
144
  For the input variable `x`, we are using a time window $T_w = 1$ second, which means that we are using the last $N_x$ samples of the variable `x` to estimate the next position of the mass. The value of $N_x$ is equal to $T_w/T_s$, where $T_s$ is the sampling time used to sample the input variable `x`.
146
145
 
147
- In a particular case, our MS-NN formulation becomes equivalent to the discrete-time response of the mass-spring-damper system. This happens when we choose the following values: $N_x = 3$, $h_x$ equal to the characteristic polynomial of the system, and $h_f = T_s^2/m$, where $T_s$ is the sampling time and $m$ is the mass of the system.
146
+ In a particular case, our MS-NN formulation becomes equivalent to the discrete-time response (discretize with Forward-Euler) of the mass-spring-damper system. This happens when we choose the following values: $N_x = 3$, $h_x$ equal to the characteristic polynomial of the system, and $h_f = T_s^2/m$, where $T_s$ is the sampling time and $m$ is the mass of the system.
148
147
 
149
148
  However, our formulation is more general and can take better adapt to model mismatches and noise levels in the measured variables. This improved learning potential can be achieved by using a larger number of samples $N_x$ in the time window of the input variable `x`.
150
149
 
@@ -240,11 +239,29 @@ print(results)
240
239
  ### nnodely Folder
241
240
  This folder contains all the nnodely library files with relative references.
242
241
 
243
- The nnodely main library files are:
244
- 1. __nnodely.py__ the main file for create the structured network.
245
- 2. __model.py__ containts the pytorch template model for the structured network.
246
-
247
- The model structured NN Inputs Outputs and Parameters:
242
+ The `Moldey` main class defined in __nnodely.py__, it contains all the main properties of the nnodely object and it derives from five main operators:
243
+ 1. __composer.py__ contains all the functions to build the networks: `addModel`, `neuralizeModel`, `addConnection`, `addClosedLool` etc..
244
+ 2. __loader.py__ contains the function for managing the dataset, the main function is `dataLoad`.
245
+ 3. __trainer.py__ contains the function for train the network as the `trainModel`.
246
+ 4. __exporter.py__ contains all the function for import and export: `saveModel`, `loadModel`, `exportONNX` etc..
247
+ 5. __validator.py__ contains all the function for validate the model ad the `resultsAnalysis`.
248
+ All the operators derive from `Network`defined in __network.py__, that contains the shared support functions for all the operators.
249
+
250
+ The folder basic contatins the main classes for the low level functionalities:
251
+ 1. __model.py__ containts the pytorch template model for the structured network.
252
+ 2. __modeldef.py__ containts the operation for work with the json model definition.
253
+ 3. __loss.py__ contatins the loss functions.
254
+ 4. __optimizer.py__ contains the optimizer calss.
255
+ 6. __relation.py__ contains all the main classes from which all the layers are derived.
256
+
257
+ The other folders are:
258
+ 1. exporter that contains the classes for the export functions.
259
+ 2. support for the support functions.
260
+ 3. visualizer that contains all the classes related to the visualization.
261
+ 4. And finally the layers folder.
262
+
263
+ The layers folder contains all the layers that can be used in the MSNN.
264
+ In particular, the model structured NN is defined by `Inputs`, `Outputs` and `Parameters`:
248
265
  1. __input.py__ contains the Input class used for create an input for the network.
249
266
  2. __ouptut.py__ contains the Output class used for create an output for the network.
250
267
  3. __parameter.py__ contains the logic for create a generic parameters and constants.
@@ -340,4 +357,4 @@ Robust and Sample-Efficient Estimation of Vehicle Lateral Velocity Using Neural
340
357
  IEEE Transactions on Intelligent Transportation Systems. https://doi.org/10.1109/TITS.2023.3303776
341
358
 
342
359
 
343
- <p align="right">(<a href="#readme-top">back to top</a>)</p>
360
+ <p align="right">(<a href="#readme-top">back to top</a>)</p>
@@ -30,36 +30,80 @@ def plot_training(ax, title, key, data_train, data_val = None, last = None):
30
30
  ax.set_ylim(min_val - min_val / 10, max_val + max_val / 10)
31
31
 
32
32
 
33
- def plot_results(ax, name_data, key, A, B, sample_time):
33
+ def plot_results(ax, name_data, key, A, B, data_idxs, sample_time):
34
34
  # Plot data
35
35
  ax.set_title(f'{key} on the dataset {name_data}')
36
36
  A_t = np.transpose(np.array(A))
37
37
  B_t = np.transpose(np.array(B))
38
- for ind_win in range(A_t.shape[0]):
39
- for ind_dim in range(A_t.shape[1]):
40
- if len(A_t.shape) == 3:
41
- num_samples = len(A_t[ind_win, ind_dim])
42
- time_array = np.linspace(0, (num_samples - 1) * sample_time, num_samples)
43
- ax.plot(time_array, A_t[ind_win, ind_dim],
44
- label=f'real')
45
- ax.plot(time_array, B_t[ind_win, ind_dim], '-.',
46
- label=f'prediction')
47
- correlation = np.corrcoef(A_t[ind_win, ind_dim],B_t[ind_win, ind_dim])[0, 1]
48
- ax.text(0.05, 0.95, f'Correlation: {correlation:.2f}', transform=ax.transAxes, verticalalignment='top')
38
+ idxs_t = np.transpose(np.array(data_idxs))
39
+ color_A = 'tab:blue'
40
+ rgb_A = mcolors.to_rgb(color_A)
41
+ color_B = 'tab:orange'
42
+ rgb_B = mcolors.to_rgb(color_B)
43
+ delta = 0.1
44
+
45
+ if len(A_t.shape) == 3:
46
+ # Print without prediction samples
47
+ time_array = []
48
+ num_samples = A_t.shape[2]
49
+ for o in range(A_t.shape[1]):
50
+ time_array.append(np.linspace(0, (num_samples - 1) * sample_time, num_samples) + sample_time * o)
51
+ time_array = np.array(time_array)
52
+ for ind_dim in range(A_t.shape[0]):
53
+ # Print the marker only if the output have a time window
54
+ ax.plot(time_array[0,:], A_t[ind_dim, 0, :],
55
+ color=tuple((x + delta * ind_dim) % 1.0001 for x in rgb_A),
56
+ marker='s' if A_t.shape[1] > 1 else None, markersize=2,
57
+ label=f'A_{ind_dim}')
58
+ ax.plot(time_array[0,:], B_t[ind_dim, 0, :], '-.',
59
+ color=tuple((x + delta * ind_dim) % 1.0001 for x in rgb_B),
60
+ marker='o' if A_t.shape[1] > 1 else None, markersize=2,
61
+ label=f'B_{ind_dim}')
62
+ if A_t.shape[1] > 1 :
63
+ correlation = np.empty((A_t.shape[0],A_t.shape[2]))
64
+ for ind_el in range(A_t.shape[2]):
65
+ ax.plot(time_array[:,ind_el], A_t[ind_dim,:,ind_el], color=tuple((x + delta*ind_dim)%1.0001 for x in rgb_A))
66
+ ax.plot(time_array[:,ind_el], B_t[ind_dim,:,ind_el], '-.', color=tuple((x + delta*ind_dim)%1.0001 for x in rgb_B))
67
+ correlation[ind_dim, ind_el] = np.corrcoef(A_t[ind_dim,:,ind_el], B_t[ind_dim,:,ind_el])[0, 1]
68
+ ax.text(0.05, 0.95 - 0.05 * ind_dim,
69
+ f'Correlation A_{ind_dim} - B_{ind_dim}: {np.mean(correlation, axis=1)[ind_dim]:.2f}',
70
+ transform=ax.transAxes, verticalalignment='top')
49
71
  else:
50
- num_samples = A_t.shape[3]
51
- for idx in range(A_t.shape[2]):
52
- time_array = np.linspace(idx * sample_time, (idx + num_samples - 1) * sample_time, num_samples)
53
- ax.plot(time_array, A_t[ind_win, ind_dim, idx],
54
- label=f'real')
55
- ax.plot(time_array, B_t[ind_win, ind_dim, idx], '-.',
56
- label=f'prediction')
57
- correlation = np.corrcoef(A_t[ind_win, ind_dim, idx],B_t[ind_win, ind_dim, idx])[0, 1]
58
- ax.text(0.05, 0.95, f'Correlation: {correlation:.2f}', transform=ax.transAxes, verticalalignment='top')
72
+ correlation = np.empty((A_t.shape[0],))
73
+ correlation[ind_dim] = np.corrcoef(A_t[ind_dim, 0], B_t[ind_dim, 0])[0, 1]
74
+ ax.text(0.05, 0.95-0.05*ind_dim, f'Correlation A_{ind_dim} - B_{ind_dim}: {correlation[ind_dim]:.2f}', transform=ax.transAxes, verticalalignment='top')
75
+ else:
76
+ correlation = np.empty(A_t.shape)
77
+ for ind_dim in range(A_t.shape[0]):
78
+ first = True
79
+ ax.scatter(idxs_t[:,0] * sample_time, A_t[ind_dim, 0, :, 0], marker='s', s=6,
80
+ color=tuple((x + delta * ind_dim) % 1.0001 for x in rgb_A))
81
+ ax.scatter(idxs_t[:,0] * sample_time, B_t[ind_dim, 0, :, 0], marker='o', s=6,
82
+ color=tuple((x + delta * ind_dim) % 1.0001 for x in rgb_B))
83
+ for ind_el in range(A_t.shape[2]):
84
+ time_array = idxs_t[ind_el] * sample_time
85
+ # Print the marker only if the output have a time window
86
+ ax.plot(time_array, A_t[ind_dim, 0, ind_el], marker = 's' if A_t.shape[1] > 1 else None, markersize=2,
87
+ color=tuple((x + delta * ind_dim) % 1.0001 for x in rgb_A),
88
+ label=f'A_{ind_dim}' if first else None)
89
+ ax.plot(time_array, B_t[ind_dim, 0, ind_el], '-.', marker = 'o' if A_t.shape[1] > 1 else None, markersize=2,
90
+ color=tuple((x + delta * ind_dim) % 1.0001 for x in rgb_B),
91
+ label=f'B_{ind_dim}' if first else None)
92
+ for ind_pred in range(A_t.shape[3]):
93
+ time_array = idxs_t[ind_el,ind_pred] * sample_time + np.linspace(0, (A_t.shape[1] - 1) * sample_time, A_t.shape[1])
94
+ ax.plot(time_array, A_t[ind_dim, :, ind_el,ind_pred],
95
+ color=tuple((x + delta * ind_dim) % 1.0001 for x in rgb_A))
96
+ ax.plot(time_array, B_t[ind_dim, :, ind_el,ind_pred], '-.',
97
+ color=tuple((x + delta * ind_dim) % 1.0001 for x in rgb_B))
98
+ first = False
99
+ for ind_win in range(A_t.shape[1]):
100
+ correlation[ind_dim,ind_win,ind_el] = np.corrcoef(A_t[ind_dim,ind_win,ind_el], B_t[ind_dim,ind_win,ind_el])[0, 1]
101
+ ax.text(0.05, 0.95-0.05*ind_dim, f'Correlation A_{ind_dim} - B_{ind_dim}: {np.mean(correlation, axis=(1, 2, 3))[ind_dim]:.2f}', transform=ax.transAxes, verticalalignment='top')
102
+
59
103
 
60
104
  ax.grid(True)
61
105
  ax.legend(loc='best')
62
- ax.set_xlabel('Time (s)')
106
+ ax.set_xlabel('Time [s]')
63
107
  ax.set_ylabel(f'Value {key}')
64
108
 
65
109
  # min_val = min([min(A), min(B)])
@@ -17,8 +17,9 @@ from nnodely.layers.fuzzify import Fuzzify
17
17
  from nnodely.layers.part import Part, Select, Concatenate, SamplePart, SampleSelect, TimePart, TimeConcatenate
18
18
  from nnodely.layers.localmodel import LocalModel
19
19
  from nnodely.layers.equationlearner import EquationLearner
20
- from nnodely.layers.timeoperation import Integrate, Derivate
20
+ from nnodely.layers.timeoperation import Integrate, Differentiate
21
21
  from nnodely.layers.interpolation import Interpolation
22
+ from nnodely.layers.rungekutta import ForwardEuler, RK2, RK4
22
23
 
23
24
  # Main nnodely classes
24
25
  from nnodely.nnodely import nnodely, Modely, clearNames
@@ -35,7 +36,7 @@ from nnodely.support import logger
35
36
  major, minor = sys.version_info.major, sys.version_info.minor
36
37
  logger.LOG_LEVEL = logging.INFO
37
38
 
38
- __version__ = '1.5.0'
39
+ __version__ = '1.5.2'
39
40
 
40
41
  if major < 3:
41
42
  sys.exit("Sorry, Python 2 is not supported. You need Python >= 3.10 for "+__package__+".")
@@ -64,8 +65,9 @@ __all__ = [
64
65
  'TimePart', 'TimeConcatenate',
65
66
  'LocalModel',
66
67
  'EquationLearner',
67
- 'Integrate', 'Derivate',
68
+ 'Integrate', 'Differentiate',
68
69
  'Interpolation',
70
+ 'ForwardEuler', 'RK2', 'RK4',
69
71
  'TextVisualizer', 'MPLVisualizer', 'MPLNotebookVisualizer',
70
72
  'StandardExporter',
71
73
  'SGD', 'Adam', 'Optimizer',
@@ -9,14 +9,13 @@ from itertools import product
9
9
  from nnodely.support.utils import TORCH_DTYPE
10
10
  from nnodely.support import initializer
11
11
 
12
-
13
-
14
12
  @torch.fx.wrap
15
- def connect(data_in, rel):
16
- virtual = torch.roll(data_in, shifts=-1, dims=1)
13
+ def update_state(data_in, rel):
14
+ #virtual = torch.roll(data_in, shifts=-1, dims=1)
17
15
  max_dim = min(rel.size(1), data_in.size(1))
18
- virtual[:, -max_dim:, :] = rel[:, -max_dim:, :]
19
- return virtual
16
+ data_out = data_in.clone()
17
+ data_out[:, -max_dim:, :] = rel[:, -max_dim:, :]
18
+ return data_out
20
19
 
21
20
  class Model(nn.Module):
22
21
  def __init__(self, model_def):
@@ -184,7 +183,7 @@ class Model(nn.Module):
184
183
  ## Check if the relation is inside the connect
185
184
  for connect_input, connect_rel in self.connect_update.items():
186
185
  if relation == connect_rel:
187
- result_dict[connect_input] = connect(kwargs[connect_input], result_dict[relation])
186
+ result_dict[connect_input] = update_state(kwargs[connect_input], result_dict[relation])
188
187
  available_keys.add(connect_input)
189
188
 
190
189
  ## Return a dictionary with all the connected inputs
@@ -3,9 +3,10 @@ import copy
3
3
  import numpy as np
4
4
 
5
5
  from nnodely.support.utils import check, check_and_get_list
6
- from nnodely.support.jsonutils import merge, subjson_from_model,subjson_from_relation, check_model, get_models_json
6
+ from nnodely.support.jsonutils import merge, subjson_from_model, subjson_from_minimize, check_model, get_models_json
7
7
  from nnodely.basic.relation import MAIN_JSON, Stream, check_names
8
8
  from nnodely.layers.output import Output
9
+ from nnodely.layers.input import Input
9
10
 
10
11
  from nnodely.support.logger import logging, nnLogger
11
12
  log = nnLogger(__name__, logging.INFO)
@@ -36,17 +37,11 @@ class ModelDef:
36
37
  self.__json[key] = value
37
38
 
38
39
  def __rebuild_json(self, models_names, minimizers):
39
- new_json = subjson_from_model(self.__json, list(models_names))
40
-
41
- if 'Minimizers' in self.__json:
42
- rel_A = [self.__json['Minimizers'][key]['A'] for key in minimizers]
43
- rel_B = [self.__json['Minimizers'][key]['B'] for key in minimizers]
44
- relations_name = set(rel_A) | set(rel_B)
45
- if len(relations_name) != 0:
46
- minimizers_json = subjson_from_relation(self.__json, list(relations_name))
47
- new_json = merge(new_json, minimizers_json)
48
-
49
- return copy.deepcopy(new_json)
40
+ models_json = subjson_from_model(self.__json, list(models_names))
41
+ if 'Minimizers' in self.__json and len(minimizers) > 0:
42
+ minimizers_json = subjson_from_minimize(self.__json, list(minimizers))
43
+ models_json = merge(models_json, minimizers_json)
44
+ return copy.deepcopy(models_json)
50
45
 
51
46
  def recurrentInputs(self):
52
47
  return {key:value for key, value in self.__json['Inputs'].items() if ('closedLoop' in value.keys() or 'connect' in value.keys())}
@@ -66,24 +61,30 @@ class ModelDef:
66
61
  def isDefined(self):
67
62
  return self.__json is not None
68
63
 
69
- def addConnect(self, stream_name:str, input_name:str, local:bool = False):
70
- input_name = check_and_get_list(input_name, set(self.__json['Inputs'].keys()),
71
- lambda name: f"The name {name} is not part of the available inputs")[0]
72
- stream_name = check_and_get_list(stream_name, set(self.__json['Relations'].keys()),
73
- lambda name: f"The name {name} is not part of the available relations")[0]
74
- self.__json['Inputs'][input_name]['connect'] = stream_name
75
- self.__json['Inputs'][input_name]['local'] = int(local)
64
+ def addConnection(self, stream_out:str|Output|Stream, input_in:str|Input, type:str, local:bool = False):
65
+ outputs = self.__json['Outputs']
66
+
67
+ if isinstance(stream_out, (Output, Stream)):
68
+ stream_name = outputs[stream_out.name] if stream_out.name in outputs.keys() else stream_out.name
69
+ else:
70
+ output_name = check_and_get_list(stream_out, set(outputs.keys()),
71
+ lambda name: f"The name {name} is not part of the available Outputs")[0]
72
+ stream_name = outputs[output_name]
73
+
74
+ if isinstance(input_in, Input):
75
+ input_name = input_in.name
76
+ else:
77
+ input_name = input_in #TODO Add tests
76
78
 
77
- def addClosedLoop(self, stream_name:str, input_name:str, local:bool = False):
78
79
  input_name = check_and_get_list(input_name, set(self.__json['Inputs'].keys()),
79
- lambda name: f"The name {name} is not part of the available inputs")[0]
80
+ lambda name: f"The name {name} is not part of the available Inputs")[0]
80
81
  stream_name = check_and_get_list(stream_name, set(self.__json['Relations'].keys()),
81
- lambda name: f"The name {name} is not part of the available relations")[0]
82
- self.__json['Inputs'][input_name]['closedLoop'] = stream_name
82
+ lambda name: f"The name {name} is not part of the available Relations")[0]
83
+ self.__json['Inputs'][input_name][type] = stream_name
83
84
  self.__json['Inputs'][input_name]['local'] = int(local)
84
85
 
85
86
  def removeConnection(self, name_list:str|list[str]):
86
- name_list = check_and_get_list(name_list, set(self.__json['Inputs'].keys()), lambda name: f"The name {name} is not part of the available inputs")
87
+ name_list = check_and_get_list(name_list, set(self.__json['Inputs'].keys()), lambda name: f"The name {name} is not part of the available Inputs")
87
88
  for input_in in name_list:
88
89
  if 'closedLoop' in self.__json['Inputs'][input_in].keys():
89
90
  del self.__json['Inputs'][input_in]['closedLoop']
@@ -124,17 +125,25 @@ class ModelDef:
124
125
  self.__json = self.__rebuild_json(models_names, minimizers)
125
126
 
126
127
  def addMinimize(self, name, streamA, streamB, loss_function='mse'):
127
- check(isinstance(streamA, (Output, Stream)), TypeError, 'streamA must be an instance of Output or Stream')
128
- check(isinstance(streamB, (Output, Stream)), TypeError, 'streamA must be an instance of Output or Stream')
129
- # check(streamA.dim == streamB.dim, ValueError, f'Dimension of streamA={streamA.dim} and streamB={streamB.dim} are not equal.')
130
128
  if 'Minimizers' not in self.__json:
131
129
  self.__json['Minimizers'] = {}
132
-
133
130
  check_names(name, set(self.__json['Minimizers'].keys()), 'Minimizers')
134
- streams = merge(streamA.json, streamB.json)
135
- streamA_name = streamA.json['Outputs'][streamA.name] if isinstance(streamA, Output) else streamA.name
136
- streamB_name = streamB.json['Outputs'][streamB.name] if isinstance(streamB, Output) else streamB.name
137
- self.__json = merge(self.__json, streams)
131
+
132
+ if isinstance(streamA, str):
133
+ streamA_name = streamA
134
+ else:
135
+ check(isinstance(streamA, (Output, Stream)), TypeError, 'streamA must be an instance of Output or Stream')
136
+ streamA_name = streamA.json['Outputs'][streamA.name] if isinstance(streamA, Output) else streamA.name
137
+ self.__json = merge(self.__json, streamA.json)
138
+
139
+ if isinstance(streamB, str):
140
+ streamB_name = streamB
141
+ else:
142
+ check(isinstance(streamB, (Output, Stream)), TypeError, 'streamA must be an instance of Output or Stream')
143
+ streamB_name = streamB.json['Outputs'][streamB.name] if isinstance(streamB, Output) else streamB.name
144
+ self.__json = merge(self.__json, streamB.json)
145
+ #check(streamA.dim == streamB.dim, ValueError, f'Dimension of streamA={streamA.dim} and streamB={streamB.dim} are not equal.')
146
+
138
147
  self.__json['Minimizers'][name] = {}
139
148
  self.__json['Minimizers'][name]['A'] = streamA_name
140
149
  self.__json['Minimizers'][name]['B'] = streamB_name
@@ -158,6 +167,8 @@ class ModelDef:
158
167
  self.__sample_time = 1
159
168
 
160
169
  self.__json['Info'] = {"SampleTime": self.__sample_time}
170
+ if 'SampleTime' in self.__json['Constants']:
171
+ self.__json['Constants']['SampleTime'] = {'dim': 1, 'values': self.__sample_time}
161
172
 
162
173
  check(self.__json['Inputs'] != {}, RuntimeError, "No model is defined!")
163
174
  json_inputs = self.__json['Inputs']
@@ -261,11 +261,11 @@ class Stream(Relation):
261
261
  Stream
262
262
  A Stream of the signal represents the integral or derivation operation.
263
263
  """
264
- from nnodely.layers.timeoperation import Derivate, Integrate
264
+ from nnodely.layers.timeoperation import Differentiate, Integrate
265
265
  check(order != 0, ValueError, "The order must be a positive or negative integer not a zero")
266
266
  if order > 0:
267
267
  for i in range(order):
268
- o = Derivate(self, der_name = der_name, int_name = int_name, method = method)
268
+ o = Differentiate(self, der_name = der_name, int_name = int_name, method = method)
269
269
  elif order < 0:
270
270
  for i in range(-order):
271
271
  o = Integrate(self, der_name = der_name, int_name = int_name, method = method)
@@ -1,4 +1,4 @@
1
- import sys, os, torch, importlib
1
+ import sys, os, torch, importlib, json
2
2
 
3
3
  from torch.fx import symbolic_trace
4
4
 
@@ -54,11 +54,19 @@ def export_python_model(model_def, model, model_path):
54
54
  file.write("import torch\n\n")
55
55
 
56
56
  ## write the connect wrap function
57
- file.write(f"def {package_name}_basic_model_connect(data_in, rel):\n")
58
- file.write(" virtual = torch.cat((data_in[:, 1:, :], data_in[:, :1, :]), dim=1)\n")
57
+ # file.write(f"def {package_name}_basic_model_update_state(data_in, rel):\n")
58
+ # file.write(" virtual = torch.cat((data_in[:, 1:, :], data_in[:, :1, :]), dim=1)\n")
59
+ # file.write(" max_dim = min(rel.size(1), data_in.size(1))\n")
60
+ # file.write(" virtual[:, -max_dim:, :] = rel[:, -max_dim:, :]\n")
61
+ # file.write(" return virtual\n\n")
62
+ file.write(f"def {package_name}_basic_model_update_state(data_in, rel):\n")
63
+ file.write(" data_out = data_in.clone()\n")
59
64
  file.write(" max_dim = min(rel.size(1), data_in.size(1))\n")
60
- file.write(" virtual[:, -max_dim:, :] = rel[:, -max_dim:, :]\n")
61
- file.write(" return virtual\n\n")
65
+ file.write(" data_out[:, -max_dim:, :] = rel[:, -max_dim:, :]\n")
66
+ file.write(" return data_out\n\n")
67
+
68
+ file.write(f"def {package_name}_basic_model_timeshift(data_in):\n")
69
+ file.write(" return torch.cat((data_in[:, 1:, :], data_in[:, :1, :]), dim=1)\n\n")
62
70
 
63
71
  for name in model_def['Functions'].keys():
64
72
  if 'Fuzzify' in name:
@@ -126,8 +134,8 @@ def export_python_model(model_def, model, model_path):
126
134
  # file.write(f" self.all_parameters[\"{param}\"] = torch.nn.Parameter(torch.{value}, requires_grad=True)\n")
127
135
  elif 'Part' in key or 'Select' in key: # any(element in key for element in ['Part', 'Select']):
128
136
  value = model.relation_forward[key].W
129
- temp_value = str(value).replace(')',', requires_grad=False)')
130
- file.write(f" self.all_constants[\"{key}\"] = torch.{temp_value}\n")
137
+ temp_value = json.dumps(value.tolist())
138
+ file.write(f" self.all_constants[\"{key}\"] = torch.tensor({temp_value}, requires_grad=True)\n")
131
139
  elif 'all_parameters' in attr:
132
140
  key = attr.split('.')[-1]
133
141
  file.write(f" self.all_parameters[\"{key}\"] = torch.nn.Parameter(torch.tensor({model.all_parameters[key].tolist()}), requires_grad=True)\n")
@@ -194,7 +202,10 @@ def export_python_model(model_def, model, model_path):
194
202
  file.write(" for key, value in results.items():\n")
195
203
  file.write(" results[key].append(out[key])\n")
196
204
  file.write(" for key, val in closed_loop.items():\n")
197
- file.write(" self.states[key] = nnodely_basic_model_connect(self.states[key], val)\n")
205
+ file.write(" self.states[key] = nnodely_basic_model_timeshift(self.states[key])\n")
206
+ file.write(" self.states[key] = nnodely_basic_model_update_state(self.states[key], val)\n")
207
+ file.write(" for key, val in connect.items():\n")
208
+ file.write(" self.states[key] = nnodely_basic_model_timeshift(val)\n")
198
209
  file.write(" return results\n")
199
210
 
200
211
  def export_pythononnx_model(model_def, model_path, model_onnx_path, input_order=None, outputs_order=None):
@@ -289,9 +300,11 @@ def export_pythononnx_model(model_def, model_path, model_onnx_path, input_order=
289
300
  for idx, key in enumerate(model_outputs):
290
301
  file.write(f" results_{key}.append(out[{idx}])\n")
291
302
  for idx, key in enumerate(closed_loop_states):
292
- file.write(f" {key} = nnodely_basic_model_connect({key}, closed_loop[{idx}])\n")
303
+ file.write(f" {key} = nnodely_basic_model_timeshift({key})\n")
304
+ file.write(f" {key} = nnodely_basic_model_update_state({key}, closed_loop[{idx}])\n")
293
305
  for idx, key in enumerate(connect_states):
294
- file.write(f" {key} = connect[{idx}]\n")
306
+ file.write(f" {key} = nnodely_basic_model_timeshift(connect[{idx}])\n")
307
+ #file.write(f" {key} = connect[{idx}]\n")
295
308
  for idx, key in enumerate(model_outputs):
296
309
  file.write(f" results_{key} = torch.stack(results_{key}, dim=0)\n")
297
310
  return_str = " return "
@@ -38,8 +38,11 @@ class Reporter:
38
38
  for ind, name_data in enumerate(self.modely.prediction.keys()):
39
39
  fig = plt.figure(figsize=(10, 5))
40
40
  ax = fig.add_subplot(111)
41
+ idxs = None
42
+ if 'idxs' in self.modely.prediction[name_data]:
43
+ idxs = self.modely.prediction[name_data]['idxs']
41
44
  plots.plot_results(ax, name_data, key, self.modely.prediction[name_data][key]['A'],
42
- self.modely.prediction[name_data][key]['B'], self.modely._model_def['Info']["SampleTime"])
45
+ self.modely.prediction[name_data][key]['B'], idxs, self.modely._model_def['Info']["SampleTime"])
43
46
  # Add a text box with correlation coefficient
44
47
  results = io.BytesIO()
45
48
  plt.savefig(results, format='png')
@@ -178,6 +178,7 @@ class Sum(Stream, ToStream):
178
178
  obj = toStream(obj)
179
179
  check(type(obj) is Stream, TypeError,
180
180
  f"The type of {obj} is {type(obj)} and is not supported for sum operation.")
181
+ obj.dim['dim'] = 1
181
182
  super().__init__(sum_relation_name + str(Stream.count),obj.json,obj.dim)
182
183
  self.json['Relations'][self.name] = [sum_relation_name,[obj.name]]
183
184
 
@@ -313,7 +314,7 @@ class Sum_Layer(nn.Module):
313
314
  super(Sum_Layer, self).__init__()
314
315
 
315
316
  def forward(self, inputs):
316
- return torch.sum(inputs, dim = 2)
317
+ return torch.sum(inputs, dim = 2, keepdim = True)
317
318
 
318
319
  def createSum(name, *inputs):
319
320
  """
@@ -29,11 +29,11 @@ class Fir(NeuObj, AutoToStream):
29
29
  ----------
30
30
  output_dimension : int, optional
31
31
  The output dimension of the FIR relation.
32
- W_init : Callable, optional
32
+ W_init : Callable, str, optional
33
33
  A callable for initializing the parameters.
34
34
  W_init_params : dict, optional
35
35
  A dictionary of parameters for the parameter initializer.
36
- b_init : Callable, optional
36
+ b_init : Callable, str, optional
37
37
  A callable for initializing the bias.
38
38
  b_init_params : dict, optional
39
39
  A dictionary of parameters for the bias initializer.
@@ -83,14 +83,14 @@ class Fir(NeuObj, AutoToStream):
83
83
 
84
84
  Example - passing a parameter:
85
85
  >>> input = Input('in')
86
- >>> par = Parameter('par', dimensions=3, sw=2, init=init_constant)
86
+ >>> par = Parameter('par', dimensions=3, sw=2, init='init_constant')
87
87
  >>> relation = Fir(W=par)(input.sw(2))
88
88
 
89
89
  Example - parameters initialization:
90
90
  >>> x = Input('x')
91
91
  >>> F = Input('F')
92
- >>> fir_x = Fir(W_init=init_negexp)(x.tw(0.2))
93
- >>> fir_F = Fir(W_init=init_constant, W_init_params={'value':1})(F.last())
92
+ >>> fir_x = Fir(W_init='init_negexp')(x.tw(0.2))
93
+ >>> fir_F = Fir(W_init='init_constant', W_init_params={'value':1})(F.last())
94
94
 
95
95
  """
96
96
  @enforce_types
@@ -112,7 +112,8 @@ class Fir(NeuObj, AutoToStream):
112
112
  super().__init__('P'+fir_relation_name + str(NeuObj.count))
113
113
 
114
114
  if type(self.W) is Parameter:
115
- check(len(self.W.dim) == 2,ValueError,f"The values of the parameters must have two dimensions (tw/sample_rate or sw,output_dimension).")
115
+ check('tw' in self.W.dim or 'sw' in self.W.dim, TypeError, f'The "W" Parameter must have a time dimension or a sample dimension but got {self.W.dim}.')
116
+ #check(len(self.W.dim) == 2,ValueError,f"The values of the parameters must have two dimensions [tw/sample_rate,output_dimension] or [sw,output_dimension].")
116
117
  if output_dimension is None:
117
118
  check(type(self.W.dim['dim']) is int, TypeError, 'Dimension of the parameter must be an integer for the Fir')
118
119
  self.output_dimension = self.W.dim['dim']
@@ -10,9 +10,6 @@ from nnodely.basic.model import Model
10
10
  from nnodely.support.utils import check, enforce_types
11
11
  from nnodely.support.jsonutils import merge
12
12
 
13
- from nnodely.support.logger import logging, nnLogger
14
- log = nnLogger(__name__, logging.CRITICAL)
15
-
16
13
  fuzzify_relation_name = 'Fuzzify'
17
14
 
18
15
  class Fuzzify(NeuObj):
@@ -4,7 +4,7 @@ from nnodely.basic.relation import NeuObj, Stream, ToStream
4
4
  from nnodely.support.utils import check, enforce_types
5
5
  from nnodely.support.jsonutils import merge, stream_to_str
6
6
  from nnodely.layers.part import SamplePart, TimePart
7
- from nnodely.layers.timeoperation import Derivate, Integrate
7
+ from nnodely.layers.timeoperation import Differentiate, Integrate
8
8
 
9
9
  class Input(NeuObj):
10
10
  """
@@ -119,16 +119,16 @@ class Input(NeuObj):
119
119
  Examples
120
120
  --------
121
121
  Select a sample window considering a signal T = [-3,-2,-1,0,1,2] where the time vector 0 represent the last passed instant. If sw is an integer #1 represent the number of step in the past
122
- >>> T.s(2) #= [-1, 0] represents two sample step in the past
122
+ >>> T.sw(2) #= [-1, 0] represents two sample step in the past
123
123
 
124
124
  If sw is a list [#1,#2] the numbers represent the sample indexes in the vector with the second element excluded
125
- >>> T.s([-2,0]) #= [-1, 0] represents two time step in the past zero in the future
126
- >>> T.s([0,1]) #= [1] the first time in the future
127
- >>> T.s([-4,-2]) #= [-3,-2]
125
+ >>> T.sw([-2,0]) #= [-1, 0] represents two time step in the past zero in the future
126
+ >>> T.sw([0,1]) #= [1] the first time in the future
127
+ >>> T.sw([-4,-2]) #= [-3,-2]
128
128
 
129
129
  The total number of samples can be computed #2-#1. The offset represent the index of the vector that need to be used to offset the window
130
- >>> T.s(2,offset=-2) #= [0, 1] the value of the window is [-1,0]
131
- >>> T.s([-2,2],offset=-1) #= [-1,0,1,2] the value of the window is [-1,0,1,2]
130
+ >>> T.sw(2,offset=-2) #= [0, 1] the value of the window is [-1,0]
131
+ >>> T.sw([-2,2],offset=-1) #= [-1,0,1,2] the value of the window is [-1,0,1,2]
132
132
  """
133
133
  dim = copy.deepcopy(self.dim)
134
134
  json = copy.deepcopy(self.json)
@@ -224,7 +224,7 @@ class Input(NeuObj):
224
224
  if order > 0:
225
225
  o = self.last()
226
226
  for i in range(order):
227
- o = Derivate(o, der_name = der_name, int_name = int_name, method = method)
227
+ o = Differentiate(o, der_name = der_name, int_name = int_name, method = method)
228
228
  elif order < 0:
229
229
  o = self.last()
230
230
  for i in range(-order):
@@ -7,9 +7,6 @@ from nnodely.basic.model import Model
7
7
  from nnodely.support.utils import check, enforce_types
8
8
  from nnodely.support.jsonutils import merge
9
9
 
10
- from nnodely.support.logger import logging, nnLogger
11
- log = nnLogger(__name__, logging.CRITICAL)
12
-
13
10
  interpolation_relation_name = 'Interpolation'
14
11
  class Interpolation(NeuObj):
15
12
  """