in-silico-cancer-cell 0.2.4__cp311-cp311-win_amd64.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.
- in_silico_cancer_cell/__init__.py +30 -0
- in_silico_cancer_cell/_in_rusty_silico.cp311-win_amd64.pyd +0 -0
- in_silico_cancer_cell/_in_rusty_silico.pyi +46 -0
- in_silico_cancer_cell/fit.py +52 -0
- in_silico_cancer_cell/plot.py +42 -0
- in_silico_cancer_cell/py.typed +0 -0
- in_silico_cancer_cell/utils.py +16 -0
- in_silico_cancer_cell-0.2.4.dist-info/METADATA +40 -0
- in_silico_cancer_cell-0.2.4.dist-info/RECORD +10 -0
- in_silico_cancer_cell-0.2.4.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Welcome!
|
|
2
|
+
from in_silico_cancer_cell import _in_rusty_silico # type: ignore reportAttributeAccessIssue
|
|
3
|
+
from ._in_rusty_silico import (
|
|
4
|
+
A549CancerCell,
|
|
5
|
+
CellPhase,
|
|
6
|
+
ChannelCountsProblem,
|
|
7
|
+
InSilicoMethod,
|
|
8
|
+
PatchClampProtocol,
|
|
9
|
+
PatchClampData,
|
|
10
|
+
find_best_fit_for,
|
|
11
|
+
setup_logging,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def useless_python_function():
|
|
16
|
+
print("hello")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
__doc__ = _in_rusty_silico.__doc__
|
|
20
|
+
__all__ = [
|
|
21
|
+
"A549CancerCell",
|
|
22
|
+
"CellPhase",
|
|
23
|
+
"ChannelCountsProblem",
|
|
24
|
+
"InSilicoMethod",
|
|
25
|
+
"PatchClampProtocol",
|
|
26
|
+
"PatchClampData",
|
|
27
|
+
"find_best_fit_for",
|
|
28
|
+
"setup_logging",
|
|
29
|
+
"useless_python_function",
|
|
30
|
+
]
|
|
Binary file
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
|
|
3
|
+
class A549CancerCell:
|
|
4
|
+
@staticmethod
|
|
5
|
+
def new() -> A549CancerCell:
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
def evaluate(self, protocol: PatchClampProtocol, phase: CellPhase) -> float:
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
class PatchClampProtocol(enum.Enum):
|
|
12
|
+
Activation = enum.auto()
|
|
13
|
+
Deactivation = enum.auto()
|
|
14
|
+
Ramp = enum.auto()
|
|
15
|
+
|
|
16
|
+
class CellPhase(enum.Enum):
|
|
17
|
+
G0 = enum.auto()
|
|
18
|
+
G1 = enum.auto()
|
|
19
|
+
|
|
20
|
+
class ChannelCountsProblem:
|
|
21
|
+
@staticmethod
|
|
22
|
+
def new(data: PatchClampData) -> ChannelCountsProblem:
|
|
23
|
+
pass
|
|
24
|
+
def precompute_single_channel_currents(self):
|
|
25
|
+
pass
|
|
26
|
+
def get_current_basis(self) -> list[list[float]]:
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
class InSilicoMethod(enum.Enum):
|
|
30
|
+
Projection = enum.auto()
|
|
31
|
+
ParticleSwarm = enum.auto()
|
|
32
|
+
SteepestDescent = enum.auto()
|
|
33
|
+
LBFGS = enum.auto()
|
|
34
|
+
|
|
35
|
+
class PatchClampData:
|
|
36
|
+
@staticmethod
|
|
37
|
+
def pyload(protocol: PatchClampProtocol, phase: CellPhase) -> PatchClampData:
|
|
38
|
+
pass
|
|
39
|
+
def to_list(self) -> list[float]:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def find_best_fit_for(data: PatchClampData, using: InSilicoMethod) -> list[float]:
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
def setup_logging():
|
|
46
|
+
pass
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import clarabel
|
|
2
|
+
import numpy as np
|
|
3
|
+
from scipy import sparse
|
|
4
|
+
import scipy
|
|
5
|
+
|
|
6
|
+
from . import CellPhase, ChannelCountsProblem, PatchClampData, PatchClampProtocol
|
|
7
|
+
from .utils import moving_average
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def load_problem(n):
|
|
11
|
+
measurements = PatchClampData.pyload(PatchClampProtocol.Activation, CellPhase.G0)
|
|
12
|
+
data = moving_average(np.array(measurements.to_list()), n)
|
|
13
|
+
# data = np.array(measurements.to_list())[::12]
|
|
14
|
+
problem = ChannelCountsProblem.new(measurements)
|
|
15
|
+
problem.precompute_single_channel_currents()
|
|
16
|
+
single_channels = moving_average(np.array(problem.get_current_basis()), n)
|
|
17
|
+
# single_channels = np.array(problem.get_current_basis())[:, (3,)]
|
|
18
|
+
# single_channels = np.concatenate([single_channels, np.ones((single_channels.shape[0], 1))], axis=1)
|
|
19
|
+
return single_channels[: data.shape[0]], data
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def solve_as_quadratic_problem(single_channels, data):
|
|
23
|
+
# LSQ formulation min 1/2 ||Rx - d||^2
|
|
24
|
+
R = single_channels
|
|
25
|
+
d = data
|
|
26
|
+
|
|
27
|
+
# converted into QP formulation
|
|
28
|
+
P = sparse.csc_matrix(R.T @ R)
|
|
29
|
+
q = -R.T @ d
|
|
30
|
+
A = -sparse.identity(11).tocsc()
|
|
31
|
+
b = np.zeros(11)
|
|
32
|
+
cones = [clarabel.NonnegativeConeT(11)]
|
|
33
|
+
|
|
34
|
+
# solve
|
|
35
|
+
settings = clarabel.DefaultSettings()
|
|
36
|
+
solver = clarabel.DefaultSolver(P, q, A, b, cones, settings)
|
|
37
|
+
solution = solver.solve()
|
|
38
|
+
return np.array(solution.x)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def solve_with(single_channels, data, method):
|
|
42
|
+
if method == "lstsq":
|
|
43
|
+
channel_counts, res, rank, s = np.linalg.lstsq(single_channels[: len(data), :], data, rcond=None)
|
|
44
|
+
elif method == "nnls":
|
|
45
|
+
channel_counts, rnorm = scipy.optimize.nnls(single_channels[: len(data), :], data)
|
|
46
|
+
elif method == "qp":
|
|
47
|
+
channel_counts = solve_as_quadratic_problem(single_channels, data)
|
|
48
|
+
elif method == "langthaler":
|
|
49
|
+
channel_counts = np.array([22, 78, 5, 1350, 40, 77, 19, 200, 17, 12, 13])
|
|
50
|
+
channel_counts = channel_counts.round().astype(int)
|
|
51
|
+
print(f"Best fit: {channel_counts}")
|
|
52
|
+
return channel_counts
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import pathlib
|
|
2
|
+
|
|
3
|
+
import matplotlib.axes
|
|
4
|
+
import matplotlib.pyplot as plt
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from . import setup_logging
|
|
8
|
+
from .fit import load_problem, solve_with
|
|
9
|
+
|
|
10
|
+
RESULTS = pathlib.Path.cwd()
|
|
11
|
+
setup_logging()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def plot_measurement():
|
|
15
|
+
fig = plt.figure()
|
|
16
|
+
axes: matplotlib.axes.Axes = fig.add_subplot(1, 1, 1)
|
|
17
|
+
axes.plot()
|
|
18
|
+
axes.set_xlabel("")
|
|
19
|
+
axes.set_ylabel("")
|
|
20
|
+
axes.legend()
|
|
21
|
+
fig.savefig(str(RESULTS / "plot.pdf"))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def plot_full_comparison(method="nnls", n=800):
|
|
25
|
+
single_channels, data = load_problem(n)
|
|
26
|
+
channel_counts = solve_with(single_channels, data, method)
|
|
27
|
+
diff = (single_channels * channel_counts).sum(axis=1) - data
|
|
28
|
+
print(f"Error: {np.sqrt((diff ** 2).sum() / len(diff)):.2f}")
|
|
29
|
+
time = np.linspace(0, 9.901, single_channels.shape[0])
|
|
30
|
+
fig = plt.figure(figsize=(8, 4))
|
|
31
|
+
axes: matplotlib.axes.Axes = fig.add_subplot(1, 1, 1)
|
|
32
|
+
axes.plot(time[: len(data)], data, label="Measurements")
|
|
33
|
+
axes.plot(time, (single_channels * channel_counts).sum(axis=1), label="Simulation")
|
|
34
|
+
axes.set_xlabel("Time $t$ / s")
|
|
35
|
+
axes.set_ylabel("Current $I$ / pA")
|
|
36
|
+
axes.legend()
|
|
37
|
+
fig.savefig(str(RESULTS / "data-vs-simulation.pdf"))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def set_results_folder(path: pathlib.Path):
|
|
41
|
+
global RESULTS
|
|
42
|
+
RESULTS = path
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def moving_average(a: np.ndarray, n=3) -> np.ndarray:
|
|
5
|
+
"""Generate a moving average
|
|
6
|
+
|
|
7
|
+
Args:
|
|
8
|
+
a (np.array): original datum
|
|
9
|
+
n (int, optional): Average stencil order. Defaults to 3.
|
|
10
|
+
|
|
11
|
+
Returns:
|
|
12
|
+
np.ndarray: averaged array
|
|
13
|
+
"""
|
|
14
|
+
ret = np.cumsum(a, dtype=float, axis=0)
|
|
15
|
+
ret[n:] = ret[n:] - ret[:-n]
|
|
16
|
+
return ret[n - 1 :] / n
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: in_silico_cancer_cell
|
|
3
|
+
Version: 0.2.4
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
7
|
+
Requires-Dist: matplotlib>=3.10.8
|
|
8
|
+
Requires-Dist: numpy>=2.4.2
|
|
9
|
+
Requires-Dist: clarabel>=0.11.1
|
|
10
|
+
Summary: Simulation of an electrophysiological A549 cancer cell model using individual ion channels across the membrane
|
|
11
|
+
Author-email: MrP01 <peter@waldert.at>
|
|
12
|
+
License-Expression: MIT
|
|
13
|
+
Requires-Python: >=3.11
|
|
14
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
15
|
+
Project-URL: homepage, https://in-silico-cancer-cell.waldert.at/
|
|
16
|
+
Project-URL: repository, https://github.com/MrP01/InSilicoCancerCell
|
|
17
|
+
|
|
18
|
+
# Electrophysiological Cancer Cell Model
|
|
19
|
+
|
|
20
|
+

|
|
21
|
+
|
|
22
|
+
Attempt to model an A549 cancer cell's ion channels using an HMM (Hidden Markov Model) and simulation of voltage + current development accross the membrane of the cell.
|
|
23
|
+
|
|
24
|
+
This software comes in three flavours:
|
|
25
|
+
|
|
26
|
+
- to run the `main.rs` simulation, do `cargo run`,
|
|
27
|
+
- to compile the Python module, do `maturin develop --features pyo3`,
|
|
28
|
+
- to precompile for the Astro dashboard, do `bun run wasm-pack build frontend`.
|
|
29
|
+
|
|
30
|
+
## A visual to capture your interest:
|
|
31
|
+
|
|
32
|
+

|
|
33
|
+
(Image source: [here](https://doi.org/10.1371/journal.pcbi.1009091.g002)).
|
|
34
|
+
|
|
35
|
+
This computational model is based on [Langthaler et al., 2021](https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1009091): **A549 in-silico 1.0: A first computational model to simulate cell cycle dependent ion current modulation in the human lung adenocarcinoma**.
|
|
36
|
+
|
|
37
|
+
## The Simulation Dashboard
|
|
38
|
+
|
|
39
|
+

|
|
40
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
in_silico_cancer_cell\__init__.py,sha256=KSGw1PIDDXcTpX3HizMCXy0AuWcsCM1BeOZ3oEIr_J8,653
|
|
2
|
+
in_silico_cancer_cell\_in_rusty_silico.cp311-win_amd64.pyd,sha256=3gvzW6qhf6QQkjH299Zhn0uFgF9QacBV0fDf0_XLo2Q,2389504
|
|
3
|
+
in_silico_cancer_cell\_in_rusty_silico.pyi,sha256=FkZk0u5D_4o_fMAA5j7pKou7KAReXhWsyOtBMw1cH6Q,1145
|
|
4
|
+
in_silico_cancer_cell\fit.py,sha256=I1mXIBxkUbS8kmwgEmhHGDC-fvH2kPuuvinb-CcLJWU,2021
|
|
5
|
+
in_silico_cancer_cell\plot.py,sha256=zy11eGa_OBBqr8SXFsLLdgUt2HJIa9rQOIa-vmlzNfI,1307
|
|
6
|
+
in_silico_cancer_cell\py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
in_silico_cancer_cell\utils.py,sha256=HE2irwwNnF0LoSOSCcOk1bT5a1VfJ8JL6oM6CugnyP0,399
|
|
8
|
+
in_silico_cancer_cell-0.2.4.dist-info\METADATA,sha256=DfOLCmnaeW_eKxUI9o1I5sroyHDf9B2kzfJiMh_-uFA,1940
|
|
9
|
+
in_silico_cancer_cell-0.2.4.dist-info\WHEEL,sha256=X79LywvMB9iCuFHu88xBAFTJDhRqJi6Yh9hhoCI9jao,97
|
|
10
|
+
in_silico_cancer_cell-0.2.4.dist-info\RECORD,,
|