sindy-exp 0.2.0__py3-none-any.whl

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.
sindy_exp/_utils.py ADDED
@@ -0,0 +1,381 @@
1
+ import logging
2
+ from itertools import chain
3
+ from typing import cast
4
+ from warnings import warn
5
+
6
+ import numpy as np
7
+ import pysindy as ps
8
+ import sklearn
9
+ import sklearn.metrics
10
+ import sympy as sp
11
+ from sympy.parsing.sympy_parser import (
12
+ convert_xor,
13
+ implicit_multiplication_application,
14
+ parse_expr,
15
+ standard_transformations,
16
+ )
17
+
18
+ from sindy_exp._typing import SINDyTrialUpdate
19
+
20
+ from ._typing import Float1D, Float2D, _BaseSINDy
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ def _sympy_expr_to_feat_coeff(sp_expr: list[sp.Expr]) -> list[dict[sp.Expr, float]]:
26
+ """Convert symbolic rhs expressions into feature/coeff dictionaries.
27
+
28
+ Each expression is assumed to be a sum of terms; each term is either a
29
+ simple SymPy expression or a product of a numeric coefficient and a
30
+ feature expression. The output is a list of dictionaries, one per
31
+ expression, mapping feature expressions to their numeric coefficients.
32
+ """
33
+
34
+ expressions: list[dict[sp.Expr, float]] = []
35
+
36
+ def kv_term(term: sp.Expr) -> tuple[sp.Expr, float]:
37
+ if not isinstance(term, sp.Mul):
38
+ # Term is either a constant or a feature without a coefficient
39
+ if isinstance(term, sp.Number):
40
+ coeff = float(term)
41
+ feat = sp.Integer(1)
42
+ elif isinstance(term, (sp.Symbol, sp.Pow, sp.Function)):
43
+ coeff = 1.0
44
+ feat = term
45
+ else:
46
+ raise ValueError(f"Unrecognized term format: {term}")
47
+ else:
48
+ try:
49
+ # Assume multiplication is coefficient * feature(s)
50
+ coeff = float(term.args[0])
51
+ args = term.args[1:]
52
+ except TypeError:
53
+ # e.g. x**2 or x * y has no numeric coefficient
54
+ coeff = 1.0
55
+ args = term.args
56
+ if len(args) == 1:
57
+ feat = args[0]
58
+ else:
59
+ feat = sp.Mul(*args)
60
+ return feat, coeff
61
+
62
+ for exp in sp_expr:
63
+ expr_dict: dict[sp.Expr, float] = {}
64
+ if not isinstance(exp, sp.Add):
65
+ feat, coeff = kv_term(exp)
66
+ expr_dict[feat] = coeff
67
+ else:
68
+ for term in exp.args:
69
+ feat, coeff = kv_term(term)
70
+ expr_dict[feat] = coeff
71
+
72
+ expressions.append(expr_dict)
73
+ return expressions
74
+
75
+
76
+ def _sindy_equations_to_sympy(model: _BaseSINDy) -> tuple[list[sp.Expr], list[sp.Expr]]:
77
+ """Convert a SINDy model's string equations to SymPy expressions.
78
+
79
+ Uses sympy's parser with ``convert_xor`` so that terms like ``x^2`` are
80
+ interpreted as powers rather than bitwise XOR.
81
+ """
82
+
83
+ # Use a fixed precision for reproducible string equations.
84
+ input_features = {feat: sp.sympify(feat) for feat in model.feature_names}
85
+ eq_strings = model.equations(10) # type: ignore[call-arg]
86
+ feat_strs = model.feature_library.get_feature_names(
87
+ input_features=model.feature_names
88
+ )
89
+ xforms = standard_transformations + (
90
+ implicit_multiplication_application,
91
+ convert_xor,
92
+ )
93
+ feat_symb = [
94
+ parse_expr(fstr, transformations=xforms, evaluate=False) for fstr in feat_strs
95
+ ]
96
+ eq_symb = [
97
+ parse_expr(
98
+ eq, local_dict=input_features, transformations=xforms, evaluate=False
99
+ )
100
+ for eq in eq_strings
101
+ ]
102
+ return feat_symb, eq_symb
103
+
104
+
105
+ def diff_lookup(kind):
106
+ normalized_kind = kind.lower().replace(" ", "")
107
+ if normalized_kind == "finitedifference":
108
+ return ps.FiniteDifference
109
+ if normalized_kind == "smoothedfinitedifference":
110
+ return ps.SmoothedFiniteDifference
111
+ elif normalized_kind == "sindy":
112
+ return ps.SINDyDerivative
113
+ else:
114
+ raise ValueError
115
+
116
+
117
+ def feature_lookup(kind):
118
+ normalized_kind = kind.lower().replace(" ", "")
119
+ if normalized_kind is None:
120
+ return ps.PolynomialLibrary
121
+ elif normalized_kind == "polynomial":
122
+ return ps.PolynomialLibrary
123
+ elif normalized_kind == "fourier":
124
+ return ps.FourierLibrary
125
+ elif normalized_kind == "weak":
126
+ return ps.WeakPDELibrary
127
+ elif normalized_kind == "pde":
128
+ return ps.PDELibrary
129
+ else:
130
+ raise ValueError
131
+
132
+
133
+ def opt_lookup(kind):
134
+ normalized_kind = kind.lower().replace(" ", "")
135
+ if normalized_kind == "stlsq":
136
+ return ps.STLSQ
137
+ elif normalized_kind == "sr3":
138
+ return ps.SR3
139
+ elif normalized_kind == "miosr":
140
+ return ps.MIOSR
141
+ elif normalized_kind == "trap":
142
+ return ps.TrappingSR3
143
+ elif normalized_kind == "ensemble":
144
+ return ps.EnsembleOptimizer
145
+ else:
146
+ raise ValueError
147
+
148
+
149
+ def coeff_metrics(
150
+ coeff_est_dicts: list[dict[sp.Expr, float]],
151
+ coeff_true_dicts: list[dict[sp.Expr, float]],
152
+ ) -> dict[str, float | np.floating]:
153
+ """Compute coefficient metrics from aligned coefficient dictionaries.
154
+
155
+ Both arguments are expected to be lists of coefficient dictionaries sharing
156
+ the same SymPy-expression keys, such as the output of ``unionize_coeff_dicts``.
157
+ """
158
+
159
+ if not coeff_true_dicts or not coeff_est_dicts:
160
+ raise ValueError("Coefficient dictionaries must be non-empty")
161
+ if len(coeff_true_dicts) != len(coeff_est_dicts):
162
+ raise ValueError("True and estimated coefficients must have same length")
163
+
164
+ features = list(coeff_true_dicts[0].keys())
165
+ n_coord = len(coeff_true_dicts)
166
+ n_feat = len(features)
167
+
168
+ coeff_true = np.zeros((n_coord, n_feat), dtype=float)
169
+ coefficients = np.zeros_like(coeff_true)
170
+
171
+ for row_ind, (true_row, est_row) in enumerate(
172
+ zip(coeff_true_dicts, coeff_est_dicts)
173
+ ):
174
+ if set(true_row.keys()) != set(features) or set(est_row.keys()) != set(
175
+ features
176
+ ):
177
+ raise ValueError(
178
+ "Coefficient dictionaries are not aligned across coordinates"
179
+ )
180
+ for col_ind, feat in enumerate(features):
181
+ coeff_true[row_ind, col_ind] = true_row[feat]
182
+ coefficients[row_ind, col_ind] = est_row[feat]
183
+
184
+ metrics: dict[str, float | np.floating] = {}
185
+ metrics["coeff_precision"] = sklearn.metrics.precision_score(
186
+ coeff_true.flatten() != 0, coefficients.flatten() != 0
187
+ )
188
+ metrics["coeff_recall"] = sklearn.metrics.recall_score(
189
+ coeff_true.flatten() != 0, coefficients.flatten() != 0
190
+ )
191
+ metrics["coeff_f1"] = sklearn.metrics.f1_score(
192
+ coeff_true.flatten() != 0, coefficients.flatten() != 0
193
+ )
194
+ metrics["coeff_mse"] = sklearn.metrics.mean_squared_error(
195
+ coeff_true.flatten(), coefficients.flatten()
196
+ )
197
+ metrics["coeff_mae"] = sklearn.metrics.mean_absolute_error(
198
+ coeff_true.flatten(), coefficients.flatten()
199
+ )
200
+ metrics["main"] = metrics["coeff_f1"]
201
+ return metrics
202
+
203
+
204
+ def pred_metrics(
205
+ model: _BaseSINDy, x_test: np.ndarray, x_dot_test: np.ndarray
206
+ ) -> dict[str, np.ndarray | float | np.floating]:
207
+ preds = model.predict(x_test)
208
+ err = preds - x_dot_test
209
+ return {
210
+ "pred_l2_fro": (np.linalg.norm(err) / np.linalg.norm(x_dot_test)),
211
+ "pred_l2_each": (np.linalg.norm(err) / np.linalg.norm(x_dot_test)),
212
+ "pred_r2": sklearn.metrics.r2_score(x_dot_test, preds),
213
+ }
214
+
215
+
216
+ def integration_metrics(model: _BaseSINDy, x_test, t_train, x_dot_test):
217
+ metrics = {}
218
+ metrics["mse-plot"] = model.score(
219
+ x_test,
220
+ t_train,
221
+ x_dot_test,
222
+ metric=sklearn.metrics.mean_squared_error,
223
+ )
224
+ metrics["mae-plot"] = model.score(
225
+ x_test,
226
+ t_train,
227
+ x_dot_test,
228
+ metric=sklearn.metrics.mean_absolute_error,
229
+ )
230
+ return metrics
231
+
232
+
233
+ def unionize_coeff_dicts(
234
+ model: _BaseSINDy,
235
+ true_equations: list[dict[sp.Expr, float]],
236
+ ) -> tuple[list[dict[sp.Expr, float]], list[dict[sp.Expr, float]]]:
237
+ """Align true and estimated coefficient dictionaries using SymPy expressions.
238
+
239
+ This function compares the symbolic features present in the true
240
+ equations and in the fitted SINDy model's equations and returns two
241
+ lists of coefficient dictionaries with identical feature keys. Missing
242
+ features in either source are filled with a coefficient of 0.0.
243
+
244
+ Args:
245
+ model: Fitted SINDy-like model.
246
+ true_equations: List of coefficient dictionaries mapping SymPy
247
+ expressions to true coefficients, one dict per state coordinate.
248
+
249
+ Returns:
250
+ A pair ``(true_aligned, est_aligned)`` where each element is a list
251
+ of coefficient dictionaries ``dict[sp.Expr, float]`` with identical
252
+ keys across all coordinates.
253
+ """
254
+
255
+ feat_exprs, est_eq_exprs = _sindy_equations_to_sympy(model)
256
+ # est_equations should be unnecessary; all terms are provided by features
257
+ est_equations = _sympy_expr_to_feat_coeff(est_eq_exprs)
258
+
259
+ if len(est_equations) != len(true_equations):
260
+ raise ValueError(
261
+ "True equations and estimated equations must have"
262
+ " the same number of coordinates"
263
+ )
264
+
265
+ true_aligned: list[dict[sp.Expr, float]] = []
266
+ est_aligned: list[dict[sp.Expr, float]] = []
267
+ empty_feats: list[dict[sp.Expr, float]] = [dict.fromkeys(feat_exprs, 0.0)]
268
+
269
+ all_features = [
270
+ expr
271
+ for eq in chain(empty_feats, true_equations, est_equations)
272
+ for expr in eq.keys()
273
+ ]
274
+ all_features = list(dict.fromkeys(all_features)) # deduplicate, preserve order
275
+ for true_eq, est_eq in zip(true_equations, est_equations):
276
+ true_aligned.append({feat: true_eq.get(feat, 0.0) for feat in all_features})
277
+ est_aligned.append({feat: est_eq.get(feat, 0.0) for feat in all_features})
278
+
279
+ return true_aligned, est_aligned
280
+
281
+
282
+ def make_model(
283
+ input_features: list[str],
284
+ dt: float,
285
+ diff_params: dict | ps.BaseDifferentiation,
286
+ feat_params: dict | ps.feature_library.base.BaseFeatureLibrary,
287
+ opt_params: dict | ps.BaseOptimizer,
288
+ ) -> ps.SINDy:
289
+ """Build a model with object parameters dictionaries
290
+
291
+ e.g. {"kind": "finitedifference"} instead of FiniteDifference()
292
+ """
293
+
294
+ def finalize_param(lookup_func, pdict, lookup_key):
295
+ try:
296
+ cls_name = pdict.pop(lookup_key)
297
+ except AttributeError:
298
+ cls_name = pdict.vals.pop(lookup_key)
299
+ pdict = pdict.vals
300
+
301
+ param_cls = lookup_func(cls_name)
302
+ param_final = param_cls(**pdict)
303
+ pdict[lookup_key] = cls_name
304
+ return param_final
305
+
306
+ if isinstance(diff_params, ps.BaseDifferentiation):
307
+ diff = diff_params
308
+ else:
309
+ diff = finalize_param(diff_lookup, diff_params, "diffcls")
310
+ if isinstance(feat_params, ps.feature_library.base.BaseFeatureLibrary):
311
+ features = feat_params
312
+ else:
313
+ features = finalize_param(feature_lookup, feat_params, "featcls")
314
+ if isinstance(opt_params, ps.BaseOptimizer):
315
+ opt = opt_params
316
+ else:
317
+ opt = finalize_param(opt_lookup, opt_params, "optcls")
318
+ return ps.SINDy(
319
+ differentiation_method=diff,
320
+ optimizer=opt,
321
+ t_default=dt, # type: ignore
322
+ feature_library=features,
323
+ feature_names=input_features,
324
+ )
325
+
326
+
327
+ def _simulate_test_data(
328
+ model: _BaseSINDy, dt: float, x_test: Float2D
329
+ ) -> SINDyTrialUpdate:
330
+ """Add simulation data to grid_data
331
+
332
+ This includes the t_sim and x_sim keys. Does not mutate argument.
333
+ Returns:
334
+ Complete GridPointData
335
+ """
336
+ t_test = cast(Float1D, np.arange(0, len(x_test) * dt, step=dt))
337
+ t_sim = t_test
338
+ try:
339
+
340
+ def quit(t, x):
341
+ return np.abs(x).max() - 1000
342
+
343
+ quit.terminal = True # type: ignore
344
+ x_sim = cast(
345
+ Float2D,
346
+ model.simulate(
347
+ x_test[0],
348
+ t_test,
349
+ integrator_kws={
350
+ "method": "LSODA",
351
+ "rtol": 1e-12,
352
+ "atol": 1e-12,
353
+ "events": [quit],
354
+ },
355
+ ),
356
+ )
357
+ except ValueError:
358
+ warn(message="Simulation blew up; returning zeros")
359
+ x_sim = np.zeros_like(x_test)
360
+ # truncate if integration returns wrong number of points
361
+ t_sim = cast(Float1D, t_test[: len(x_sim)])
362
+ return SINDyTrialUpdate(t_sim=t_sim, t_test=t_test, x_sim=x_sim)
363
+
364
+
365
+ def _drop_and_warn(arrs):
366
+ """Drop trajectories that blew up during simulation"""
367
+ maxlen = max(arr.shape[0] for arr in arrs)
368
+
369
+ def _alert_short(arr):
370
+ if arr.shape[0] < maxlen:
371
+ warn(message="Dropping simulation due to blow-up")
372
+ return False
373
+ return True
374
+
375
+ arrs = list(filter(_alert_short, arrs))
376
+ if len(arrs) == 0:
377
+ raise ValueError(
378
+ "Simulations failed due to blow-up. System is too stiff for solver's"
379
+ " numerical tolerance"
380
+ )
381
+ return arrs
@@ -0,0 +1,91 @@
1
+ {
2
+ "LotkaVolterra": {
3
+ "bifurcation_parameter": null,
4
+ "citation": "Lotka, A. J. (1925). Elements of Physical Biology. Williams & Wilkins.; Volterra, V. (1926). Fluctuations in the abundance of a species considered mathematically. Nature, 118, 558-560.",
5
+ "delay": false,
6
+ "description": "Predator-Prey Model.",
7
+ "dt": 0.1,
8
+ "embedding_dimension": 2,
9
+ "initial_conditions": [10.0, 5.0],
10
+ "nonautonomous": false,
11
+ "parameters": {
12
+ "alpha": 1.0,
13
+ "beta": 0.1,
14
+ "gamma": 1.5,
15
+ "delta": 0.075
16
+ }
17
+ },
18
+ "Hopf": {
19
+ "bifurcation_parameter": null,
20
+ "citation": "Standard normal form of a Hopf bifurcation.",
21
+ "delay": false,
22
+ "description": "Planar Hopf normal form with cubic nonlinearity.",
23
+ "dt": 0.1,
24
+ "embedding_dimension": 2,
25
+ "initial_conditions": [1.0, 0.0],
26
+ "nonautonomous": false,
27
+ "parameters": {
28
+ "mu": 0.05,
29
+ "omega": 1.0,
30
+ "A": 1.0
31
+ }
32
+ },
33
+ "SHO": {
34
+ "bifurcation_parameter": null,
35
+ "citation": "Linear damped harmonic oscillator.",
36
+ "delay": false,
37
+ "description": "Two-dimensional linear damped oscillator.",
38
+ "dt": 0.1,
39
+ "embedding_dimension": 2,
40
+ "initial_conditions": [1.0, 0.0],
41
+ "nonautonomous": false,
42
+ "parameters": {
43
+ "a": -0.1,
44
+ "b": 2.0,
45
+ "c": -2.0,
46
+ "d": -0.1
47
+ }
48
+ },
49
+ "CubicHO": {
50
+ "bifurcation_parameter": null,
51
+ "citation": "Cubic damped harmonic oscillator.",
52
+ "delay": false,
53
+ "description": "Two-dimensional cubic damped oscillator.",
54
+ "dt": 0.1,
55
+ "embedding_dimension": 2,
56
+ "initial_conditions": [1.0, 0.0],
57
+ "nonautonomous": false,
58
+ "parameters": {
59
+ "a": -0.1,
60
+ "b": 2.0,
61
+ "c": -2.0,
62
+ "d": -0.1
63
+ }
64
+ },
65
+ "VanDerPol": {
66
+ "bifurcation_parameter": null,
67
+ "citation": "Van der Pol, B. (1926). On relaxation-oscillations. The London, Edinburgh, and Dublin Philosophical Magazine and Journal of Science, 2(11), 978-992.",
68
+ "delay": false,
69
+ "description": "Classic Van der Pol relaxation oscillator.",
70
+ "dt": 0.1,
71
+ "embedding_dimension": 2,
72
+ "initial_conditions": [1.0, 0.0],
73
+ "nonautonomous": false,
74
+ "parameters": {
75
+ "mu": 0.5
76
+ }
77
+ },
78
+ "Kinematics": {
79
+ "bifurcation_parameter": null,
80
+ "citation": "Uniformly accelerated one-dimensional motion.",
81
+ "delay": false,
82
+ "description": "Position and velocity with constant acceleration.",
83
+ "dt": 0.1,
84
+ "embedding_dimension": 2,
85
+ "initial_conditions": [0.0, 0.0],
86
+ "nonautonomous": false,
87
+ "parameters": {
88
+ "a": -1.0
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: sindy-exp
3
+ Version: 0.2.0
4
+ Summary: A basic library for constructing dynamics experiments
5
+ Author-email: Jake Stevens-Haas <jacob.stevens.haas@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2022 Jacob Stevens-Haas
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: homepage, https://github.com/Jake-Stevens-Haas/gen-experiments
29
+ Keywords: Machine Learning,Science,Mathematics,Experiments
30
+ Classifier: Development Status :: 4 - Beta
31
+ Classifier: Programming Language :: Python
32
+ Classifier: Framework :: Jupyter
33
+ Classifier: Intended Audience :: Science/Research
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Natural Language :: English
36
+ Classifier: Operating System :: POSIX :: Linux
37
+ Requires-Python: >=3.10
38
+ Description-Content-Type: text/markdown
39
+ License-File: LICENSE
40
+ Requires-Dist: matplotlib
41
+ Requires-Dist: numpy>=2.0.0
42
+ Requires-Dist: seaborn
43
+ Requires-Dist: scipy
44
+ Requires-Dist: sympy
45
+ Requires-Dist: dysts
46
+ Provides-Extra: jax
47
+ Requires-Dist: jax[cuda12]; extra == "jax"
48
+ Requires-Dist: diffrax; extra == "jax"
49
+ Requires-Dist: sympy2jax; extra == "jax"
50
+ Requires-Dist: typing_extensions; extra == "jax"
51
+ Provides-Extra: dev
52
+ Requires-Dist: ipykernel; extra == "dev"
53
+ Requires-Dist: mypy; extra == "dev"
54
+ Requires-Dist: pytest>=6.0.0; extra == "dev"
55
+ Requires-Dist: pytest-cov; extra == "dev"
56
+ Requires-Dist: flake8; extra == "dev"
57
+ Requires-Dist: flake8-comprehensions>=3.1.0; extra == "dev"
58
+ Requires-Dist: black; extra == "dev"
59
+ Requires-Dist: coverage; extra == "dev"
60
+ Requires-Dist: isort; extra == "dev"
61
+ Requires-Dist: pre-commit; extra == "dev"
62
+ Requires-Dist: codecov; extra == "dev"
63
+ Requires-Dist: tomli; extra == "dev"
64
+ Requires-Dist: pysindy>=2.1.0; extra == "dev"
65
+ Dynamic: license-file
66
+
67
+ # Dynamics Experiments
68
+
69
+ A library for constructing dynamics experiments.
70
+ This includes data generation and plotting/evaluation.
71
+
72
+ ## Getting started
73
+
74
+ It's not yet on PyPI, so install it with `pip install sindy_exp @ git+https://github.com/Jacob-Stevens-Haas/sindy-experiments`
75
+
76
+ Generate data
77
+
78
+ data = sindy_exp.data.gen_data("lorenz", num_trajectories=5, t_end=10.0, dt=0.01)["data]
79
+
80
+ Evaluate your SINDy-like model with:
81
+
82
+ sindy_exp.odes.fit_eval(model, data)
83
+
84
+ ![Coefficient plots](images/coeff.png)
85
+
86
+ A list of available ODE systems can be found in `ODE_CLASSES`, which includes most
87
+ of the systems from the [dysts package](https://pypi.org/project/dysts/) as well as some non-chaotic systems.
88
+
89
+ ## ODE representation
90
+
91
+ We deal primarily with autonomous ODE systems of the form:
92
+
93
+ dx/dt = sum_i f_i(x)
94
+
95
+ Thus, we represent ODE systems as a list of right-hand side expressions.
96
+ Each element is a dictionary mapping a term (Sympy expression) to its coefficient.
97
+
98
+ ## Other useful imports, compatibility, and extensions
99
+
100
+ This is built to be compatible with dynamics learning models that follow the
101
+ pysindy _BaseSINDy interface.
102
+ The experiments are also built to be compatible with the `mitosis` tool,
103
+ an experiment runner.
104
+ To integrate your own experiments or data generation in a way that is compatible,
105
+ see the `ProbData` and `DynamicsTrialData` classes.
106
+ For plotting tools, see `plot_coefficients`, `compare_coefficient_plots_from_dicts`,
107
+ `plot_test_trajectory`, `plot_training_data`, and `COLOR`.
108
+ For metrics, see `coeff_metrics`, `pred_metrics`, and `integration_metrics`.
109
+
110
+ ![3d plot](images/composite.png)
111
+ ![1d plot](images/1d.png)
@@ -0,0 +1,14 @@
1
+ sindy_exp/__init__.py,sha256=F1bJz9Gzk2nPB6DGjEa0qZcaQPTb-Yhh0ZnO9TcQci0,689
2
+ sindy_exp/_data.py,sha256=xEQByYeL2ejPdCpFVP9oH48Faoiz1E3OjRhK06lmkFo,6512
3
+ sindy_exp/_diffrax_solver.py,sha256=YmKF-U1fkxW4z4AbXW_qm1sX6eEy1BGzKbqL63dxo2M,2705
4
+ sindy_exp/_dysts_to_sympy.py,sha256=d_rvnfayOmFcGn4bZRJCfNGFO6yS1mw2QmBbOdWZwxg,15654
5
+ sindy_exp/_odes.py,sha256=RN97nh1Y_6DQCLPfRTyCCJq3exxBq9pfVHVPTxr0na4,8830
6
+ sindy_exp/_plotting.py,sha256=dpcqAXKzb0mSVl0p2WyMadVoGhQi43oL6ZqsbuheEuk,17470
7
+ sindy_exp/_typing.py,sha256=hIOj270YkoITi_qJ8xTO1EaBeoVzcDidIGMXuC0QHzM,4046
8
+ sindy_exp/_utils.py,sha256=RnMoNV_N7ubWVMbgdiJaRGo437DvWUcOIn4fP2rhwJI,12717
9
+ sindy_exp/addl_attractors.json,sha256=KXoHWekFoa4KctjLCqcj_BpLBhXV0zlYrpgxV-uObwE,2928
10
+ sindy_exp-0.2.0.dist-info/licenses/LICENSE,sha256=ubi77tIG3RVrqo0Z8cK91D4KZePQs-W1J-vJ-LkVOmE,1075
11
+ sindy_exp-0.2.0.dist-info/METADATA,sha256=_BRUa1zXAqRJtMWeIcyUDyx74eo_VeAwu7V1NVhbhZM,4514
12
+ sindy_exp-0.2.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
13
+ sindy_exp-0.2.0.dist-info/top_level.txt,sha256=0-tKKdmxHG3IRccz463rOb6xTsVJD-v9c8zSDpTRr5E,10
14
+ sindy_exp-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Jacob Stevens-Haas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ sindy_exp