pyCLINE 0.1.7__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.
pycline-0.1.7/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025, Bartosz Prokop, Nikita Frolov, Lendert Gelens
4
+ Laboratory of Dynamics in Biological Systems (DiBS/Gelens lab)
5
+ Department of Cellular and Mollecular Medicine, KU Leuven
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
pycline-0.1.7/PKG-INFO ADDED
@@ -0,0 +1,40 @@
1
+ Metadata-Version: 2.2
2
+ Name: pyCLINE
3
+ Version: 0.1.7
4
+ Summary: This package is the python implementation of the CLINE method introduced by Prokop, Billen, Frolov, Gelens (2025).
5
+ Author-email: Bartosz Prokop <bartosz.prokop@kuleuven.be>, Nikita Frolov <nikita.frolov@kuleuven.be>, Lendert Gelens <lendert.gelens@kuleuven.be>
6
+ Project-URL: Homepage, https://pycline-ec8369.pages.gitlab.kuleuven.be/
7
+ Project-URL: Issues, https://gitlab.kuleuven.be/gelenslab/publications/pycline/-/issues
8
+ Keywords: model,model identification,nullcline,data-driven,machine learning,deep learning,torch,dynamics,oscillator,nonlinear dynamics,complex systems
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: matplotlib>=3.6.2
16
+ Requires-Dist: numpy<1.25.0,>=1.24.1
17
+ Requires-Dist: pandas~=1.5.2
18
+ Requires-Dist: torch>=2.4.1
19
+ Requires-Dist: tqdm>=4.66.1
20
+ Requires-Dist: jitcdde>=1.8.1
21
+ Requires-Dist: scipy>=1.9.3
22
+
23
+ # PyCLINE - python package for CLINE
24
+
25
+ The `pyCLINE` package is the python package based on the CLINE (**C**omputational **L**earning and **I**dentification of **N**ullclin**E**s).
26
+ It can be downloaded from PyPI with pip by using
27
+
28
+ pip install pyCLINE
29
+
30
+ The package allows to recreate all data, models and results shown in XXX, and to apply CLINE to other data sets.
31
+ In order to generate data used in XXX, a set of different models is being provided under `pyCLINE.model`.
32
+ Data from these models can be generated using `pyCLINE.generate_data()`.
33
+ For setting up the data prepartion and adjacent training a neural network, the submodule `pyCLINE.recovery_methods` is used.
34
+ The submodule contains the module for data_preparation `pyCLINE.recovery_methods.data_preparation` and for neural network training `pyCLINE.recovery_methods.nn_training`.
35
+
36
+ For a better understanding, `pyCLINE` also contains the module `pyCLINE.example` which provides four example also found in XXX with step by step instructions on how to setup a CLINE pipeline.
37
+
38
+ The structure of `pyCLINE` is shown here:
39
+
40
+ ![PyCLINE structure](pycline_structure.png)
@@ -0,0 +1,18 @@
1
+ # PyCLINE - python package for CLINE
2
+
3
+ The `pyCLINE` package is the python package based on the CLINE (**C**omputational **L**earning and **I**dentification of **N**ullclin**E**s).
4
+ It can be downloaded from PyPI with pip by using
5
+
6
+ pip install pyCLINE
7
+
8
+ The package allows to recreate all data, models and results shown in XXX, and to apply CLINE to other data sets.
9
+ In order to generate data used in XXX, a set of different models is being provided under `pyCLINE.model`.
10
+ Data from these models can be generated using `pyCLINE.generate_data()`.
11
+ For setting up the data prepartion and adjacent training a neural network, the submodule `pyCLINE.recovery_methods` is used.
12
+ The submodule contains the module for data_preparation `pyCLINE.recovery_methods.data_preparation` and for neural network training `pyCLINE.recovery_methods.nn_training`.
13
+
14
+ For a better understanding, `pyCLINE` also contains the module `pyCLINE.example` which provides four example also found in XXX with step by step instructions on how to setup a CLINE pipeline.
15
+
16
+ The structure of `pyCLINE` is shown here:
17
+
18
+ ![PyCLINE structure](pycline_structure.png)
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = [ "setuptools>=61.0",]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pyCLINE"
7
+ version = "0.1.7"
8
+ description = "This package is the python implementation of the CLINE method introduced by Prokop, Billen, Frolov, Gelens (2025)."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ classifiers = [ "Development Status :: 3 - Alpha", "Programming Language :: Python :: 3", "Operating System :: OS Independent",]
12
+ keywords = [ "model", "model identification", "nullcline", "data-driven", "machine learning", "deep learning", "torch", "dynamics", "oscillator", "nonlinear dynamics", "complex systems",]
13
+ dependencies = [ "matplotlib>=3.6.2", "numpy>=1.24.1,<1.25.0", "pandas~=1.5.2", "torch>=2.4.1", "tqdm>=4.66.1", "jitcdde>=1.8.1", "scipy>=1.9.3",]
14
+ [[project.authors]]
15
+ name = "Bartosz Prokop"
16
+ email = "bartosz.prokop@kuleuven.be"
17
+
18
+ [[project.authors]]
19
+ name = "Nikita Frolov"
20
+ email = "nikita.frolov@kuleuven.be"
21
+
22
+ [[project.authors]]
23
+ name = "Lendert Gelens"
24
+ email = "lendert.gelens@kuleuven.be"
25
+
26
+ [project.license]
27
+ file = "LICEN[CS]E*"
28
+
29
+ [project.urls]
30
+ Homepage = "https://pycline-ec8369.pages.gitlab.kuleuven.be/"
31
+ Issues = "https://gitlab.kuleuven.be/gelenslab/publications/pycline/-/issues"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,22 @@
1
+ """
2
+ pyCLINE: Python implementation of the CLINE method introduced by Prokop, Billen, Frolov and Gelens (2025).
3
+
4
+ Modules:
5
+ recovery_methods: Methods for recovering nullclines.
6
+ model: Model definitions and utilities.
7
+ generate_data: Functions for generating synthetic data.
8
+ example: Example usage and demonstrations.
9
+ """
10
+
11
+ from . import recovery_methods
12
+ from . import model
13
+ from . import generate_data
14
+ from . import example
15
+ from importlib.metadata import version, PackageNotFoundError
16
+
17
+ try:
18
+ __version__ = version("pyCLINE")
19
+ except PackageNotFoundError:
20
+ __version__ = "0.0.0" # Default when package is not installed
21
+
22
+ __all__ = ["recovery_methods", "model", "generate_data", "example"]
@@ -0,0 +1,164 @@
1
+ import sys
2
+ import os
3
+ from . import generate_data
4
+ import pandas as pd
5
+ import matplotlib.pyplot as plt
6
+ from . import recovery_methods
7
+ from . import model
8
+ import torch.nn as nn
9
+ import numpy
10
+ import torch
11
+
12
+ class ExampleCallable:
13
+ def __call__(self, example_model, plot=True):
14
+ example(example_model, plot)
15
+
16
+ sys.modules[__name__]= ExampleCallable()
17
+
18
+ def example(example_model, plot):
19
+ """
20
+ This function runs multiple examples, depending on the choice of the example model.
21
+ It should be used as a guideline how to run pyCLINE for synthetic data.
22
+ For using pyCLINE on real data, the user should adapt the data source accordingly.
23
+
24
+ Args:
25
+ example_model (str): Selection of model to run the example on. Chose from 'FHN', 'Bicubic', 'GeneExpression', 'DelayOscillator'.
26
+ plot (bool): If True, the function will plot the data and the predictions.
27
+
28
+ Raises:
29
+ ValueError: In case no example model string is provided.
30
+ """
31
+
32
+ #provide name of model to run example
33
+ if example_model is None:
34
+ raise ValueError("Error: example_model is None: please provide a model name (FHN, Bicubic, GeneExpression, DelayOscillator).")
35
+
36
+ # check if data already exists
37
+ path='data/synthetic_data/'
38
+ if example_model=='FHN':
39
+ fname='FHN_eps=0.3_a=0.0.csv'
40
+ else:
41
+ fname=example_model+'.csv'
42
+
43
+ print('Running example for model: '+example_model)
44
+ print('Step 1: Load or generate data')
45
+ # generate data if it does not exist
46
+ if os.path.exists(path+fname):
47
+ print('Data already exists: '+path+fname)
48
+ df = pd.read_csv(path+fname)
49
+
50
+ else:
51
+ print('No data saved: generating data for model: '+example_model)
52
+ getattr(generate_data, example_model)()
53
+ print('Data generated saved: '+path+fname)
54
+ df = pd.read_csv(path+fname)
55
+
56
+ # extracting 1 time series and plotting
57
+ if example_model!='DelayOscillator':
58
+ df_sim = df[(df['sim']==1)].copy()
59
+ else:
60
+ df_sim=df.copy()
61
+ df_sim.reset_index(drop=True, inplace=True)
62
+
63
+ if example_model=='DelayOscillator':
64
+ tau=10 # 12,20,35
65
+ v_data=df_sim['u'][df_sim['time']>=tau].values
66
+ df_sim=df_sim[df_sim['time']<=df_sim['time'].max() - tau]
67
+ df_sim['v']=v_data
68
+
69
+ if plot:
70
+ fig,ax = plt.subplots(1,1,figsize=(5,3))
71
+ ax.plot(df_sim['time'], df_sim['u'], label='u')
72
+ ax.plot(df_sim['time'], df_sim['v'], label='v')
73
+ ax.set_xlabel('Time')
74
+ ax.set_ylabel('Amount in arbs. units')
75
+ plt.show()
76
+
77
+ # prepare data for training
78
+ # tmin: minimum time point to consider to avoid transient behavior
79
+ # val_min, val_max: min and max values of the min max normalization
80
+ print('Step 2: Prepare data for training')
81
+ if example_model=='FHN':
82
+ tmin, val_min, val_max=3, -1.0, 1.0
83
+ if example_model=='Bicubic':
84
+ tmin, val_min, val_max=10, 0.0, 1.0
85
+ if example_model=='GeneExpression':
86
+ tmin, val_min, val_max=10, 0.0, 1.0
87
+ if example_model=='DelayOscillator':
88
+ tmin, val_min, val_max=100,0.0,1.0
89
+
90
+
91
+ # normalize data and create additional derivative or delayed variables
92
+ df_sim, df_coef = recovery_methods.data_preparation.prepare_data(df_sim, vars=['u', 'v'], time='time', tmin=tmin, scheme='derivative', value_min=val_min, value_max=val_max)
93
+
94
+ # define input and target variables (can be changed as needed)
95
+ input_vars, target_vars = ['norm u', 'd normu/dt'], ['norm v']
96
+
97
+ # schuffle and split training, test and validation data
98
+ # optimal_thresholding: if True, the amount of samples within the phase space is evenly distributed
99
+ input_train, target_train, input_test, target_test, input_val, target_val = recovery_methods.data_preparation.shuffle_and_split(df_sim, input_vars = input_vars,
100
+ target_var = target_vars,
101
+ optimal_thresholding=False)
102
+
103
+ # set up the model
104
+ # Nin: number of input variables
105
+ # Nout: number of output variables
106
+ # Nlayers: number of layers
107
+ # Nnodes: number of nodes per layer
108
+ # summary: if True, the model summary is printed
109
+ # lr: learning rate
110
+ # activation: activation function can be chosen as needed
111
+ print('Step 3: Set up the model')
112
+ nn_model, optimizer, loss_fn = recovery_methods.nn_training.configure_FFNN_model(Nin=len(input_vars), Nout=len(target_vars),
113
+ Nlayers=3, Nnodes=64, summary=True, lr=1e-4,
114
+ activation=nn.SiLU)
115
+
116
+ # train the model
117
+ print('Step 4: Train the model')
118
+ training_loss, val_loss, test_loss, predictions_evolution, lc_predictions = recovery_methods.nn_training.train_FFNN_model(model=nn_model,
119
+ optimizer=optimizer, loss_fn=loss_fn,
120
+ input_train=input_train,
121
+ target_train=target_train,input_test=input_test,
122
+ target_test=target_test,
123
+ validation_data=(input_val, target_val),
124
+ epochs=3000, batch_size=64, device='cpu',save_evolution=True,
125
+ method='derivative', minimal_value=val_min,maximal_value=val_max)
126
+
127
+ # save model and predictions
128
+ print('Step 5: Save the generated predictions and model')
129
+ if not os.path.exists(f'results/{example_model}'):
130
+ os.makedirs(f'results/{example_model}')
131
+
132
+ torch.save(nn_model.state_dict(), f'results/{example_model}/model.pth')
133
+ numpy.save(f'results/{example_model}/predictions.npy', predictions_evolution)
134
+ numpy.save(f'results/{example_model}/lc_predictions.npy', lc_predictions)
135
+ numpy.save(f'results/{example_model}/training_loss.npy', training_loss)
136
+ numpy.save(f'results/{example_model}/val_loss.npy', val_loss)
137
+ numpy.save(f'results/{example_model}/test_loss.npy', test_loss)
138
+
139
+ print('Example completed: model and predictions saved in results/'+example_model)
140
+
141
+ # plot the predictions
142
+ if plot:
143
+ print('Step 6: Plot the predictions')
144
+ fig,ax = plt.subplots(1,1,figsize=(5,3))
145
+ ax.scatter(df_sim['norm u'], df_sim['norm v'], label='GT LC', c='silver')
146
+ ax.scatter(input_train['norm u'], lc_predictions[-1,:,0],
147
+ c='C2', label='Pred. LC', s=2 )
148
+
149
+ # compute nullcline
150
+ u = numpy.linspace(df_coef['u'].min(), df_coef['u'].max(), predictions_evolution.shape[1])
151
+ sim_model = getattr(model, example_model)()
152
+ gt_nullcline=sim_model.vnull(u)
153
+
154
+ norm_u=recovery_methods.data_preparation.normalize_adjusted(u, df_coef,'u',
155
+ min=val_min, max=val_max)
156
+ norm_gt_nullcline=recovery_methods.data_preparation.normalize_adjusted(gt_nullcline, df_coef,'v',
157
+ min=val_min, max=val_max)
158
+
159
+ ax.plot(norm_u,norm_gt_nullcline, label='GT NC', c='k')
160
+ ax.plot(norm_u,predictions_evolution[-1,:], label='Pred. NC', c='C1')
161
+ ax.set_xlabel('u')
162
+ ax.set_ylabel('v')
163
+ ax.legend()
164
+ plt.show()
@@ -0,0 +1,105 @@
1
+ import numpy as np
2
+ from . import model
3
+
4
+ def FHN(dt=0.1, N=1000000, epsilons=[0.3], n_intiaL_conditions=1):
5
+ """
6
+ Generate data using the FitzHugh-Nagumo model (see src/pyCLINE/model.py)
7
+ with different time scale separation, as used in Prokop, Billen, Frolov, Gelens (2025).
8
+
9
+ Args:
10
+ dt (float): Time step. Defaults to 0.1.
11
+ N (int): Number of time steps. Defaults to 1000000.
12
+ epsilons (list): List of time scale separations. Defaults to [0.3].
13
+
14
+ Returns:
15
+ None
16
+ """
17
+ if dt <= 0:
18
+ raise ValueError("Time step (dt) must be positive.")
19
+ if N <= 0:
20
+ raise ValueError("Number of time steps (N) must be positive.")
21
+ if n_intiaL_conditions <= 0:
22
+ raise ValueError("Number of initial conditions must be positive.")
23
+
24
+ u = np.zeros([2,N])
25
+ for i_eps, eps in enumerate(epsilons):
26
+ u[:,0] = [0.1, 0.1]
27
+ p = [1, 1, eps, 0.5, 0.0]
28
+ fhn=model.FHN(p)
29
+ u0, v0 = np.meshgrid(np.linspace(-1.25,1.75,n_intiaL_conditions),np.linspace(-0.75,1.75,n_intiaL_conditions))
30
+ x0 = np.array([u0,v0])
31
+ fhn.generate_data(x0, dt, N)
32
+ pass
33
+
34
+ def Bicubic(dt=0.1, N=1000000, n_intiaL_conditions=1):
35
+ """
36
+ Generate data using the Bicubic model (see src/pyCLINE/model.py),
37
+ as used in Prokop, Billen, Frolov, Gelens (2025).
38
+
39
+ Args:
40
+ dt (float, optional): Time step. Defaults to 0.1.
41
+ N (int, optional): Number of time steps. Defaults to 1000000.
42
+ """
43
+ if dt <= 0:
44
+ raise ValueError("Time step (dt) must be positive.")
45
+ if N <= 0:
46
+ raise ValueError("Number of time steps (N) must be positive.")
47
+ if n_intiaL_conditions <= 0:
48
+ raise ValueError("Number of initial conditions must be positive.")
49
+
50
+ u = np.zeros([2,N])
51
+
52
+ u[:,0] = [0.1, 0.1]
53
+ p = [-0.5, 0.5, -1/3]
54
+
55
+ bicubic=model.Bicubic(p)
56
+ u0, v0 = np.meshgrid(np.linspace(-1.25,1.75,n_intiaL_conditions),np.linspace(-0.75,1.75,n_intiaL_conditions))
57
+ x0 = np.array([u0,v0])
58
+ bicubic.generate_data(x0, dt, 10000)
59
+ pass
60
+
61
+ def GeneExpression(dt=0.1, N=1000000, n_intiaL_conditions=1):
62
+ """
63
+ Generate data using the Gene Expression model (see src/pyCLINE/model.py),
64
+ as used in Prokop, Billen, Frolov, Gelens (2025).
65
+
66
+ Args:
67
+ dt (float, optional): Time step. Defaults to 0.1.
68
+ N (int, optional): Number of time steps. Defaults to 1000000.
69
+ """
70
+ if dt <= 0:
71
+ raise ValueError("Time step (dt) must be positive.")
72
+ if N <= 0:
73
+ raise ValueError("Number of time steps (N) must be positive.")
74
+ if n_intiaL_conditions <= 0:
75
+ raise ValueError("Number of initial conditions must be positive.")
76
+
77
+ u = np.zeros([2,N])
78
+
79
+ u[:,0] = [0.1, 0.1]
80
+ p=[1, 0.05, 1, 0.05, 1, 0.05, 1, 1, 0.1, 2]
81
+
82
+ gene_expression=model.GeneExpression(p)
83
+ u0, v0 = np.meshgrid(np.linspace(0,1.75,n_intiaL_conditions),np.linspace(0,1.75,n_intiaL_conditions))
84
+ x0 = np.array([u0,v0])
85
+ gene_expression.generate_data(x0, dt, 10000)
86
+ pass
87
+
88
+ def DelayOscillator(N=20000):
89
+ """
90
+ Generate data using the Delay Oscillator model (see src/pyCLINE/model.py),
91
+ as used in Prokop, Billen, Frolov, Gelens (2025).
92
+
93
+ Args:
94
+ N (int, optional): Number of time steps. Defaults to 20000.
95
+ """
96
+ if N <= 0:
97
+ raise ValueError("Number of time steps (N) must be positive.")
98
+
99
+ time = np.linspace(0, 400, N-1)
100
+ dt=time[1]-time[0]
101
+ p=[4, 10, 2]
102
+
103
+ delay_osci=model.DelayOscillator(p)
104
+ delay_osci.generate_data( y_0=0, dt=dt, t_max=time[-1])
105
+ pass