pypharm 1.6.0__py3-none-any.whl → 1.6.1__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.
PyPharm/models/pbpk.py CHANGED
@@ -1,683 +1,683 @@
1
- import inspect
2
- import matplotlib
3
- import numpy as np
4
- from scipy.integrate import solve_ivp, LSODA
5
- from scipy.optimize import minimize
6
- from PyPharm.algorithms.country_optimization import CountriesAlgorithm
7
- from PyPharm.algorithms.country_optimization_v2 import CountriesAlgorithm_v2
8
- from PyPharm.algorithms.genetic_optimization import GeneticAlgorithm
9
- from PyPharm.constants import MODEL_CONST, ANIMALS
10
- from numba import njit, types, cfunc
11
- from numba.typed import Dict
12
-
13
-
14
- try:
15
- from numbalsoda import lsoda_sig, lsoda
16
- except FileNotFoundError:
17
- available_lsoda = False
18
- lsoda_sig = types.void(
19
- types.double,
20
- types.CPointer(types.double),
21
- types.CPointer(types.double),
22
- types.CPointer(types.double)
23
- )
24
- lsoda = None
25
-
26
-
27
- class PBPKmod:
28
-
29
- _organs = ['lung', 'heart', 'brain', 'muscle', 'adipose', 'skin', 'bone', 'kidney',
30
- 'liver', 'gut', 'spleen', 'stomach', 'pancreas', 'venous_blood', 'arterial_blood']
31
- _cl_organs = ['kidney', 'liver']
32
-
33
- def __init__(self, know_k=None, know_cl=None, numba_option=False, lsoda_option=False):
34
-
35
- self.know_k = know_k if know_k is not None else {}
36
- self.know_cl = know_cl if know_cl is not None else {}
37
- self._optim = False
38
- self.numba_option = numba_option
39
- self.lsoda_option = lsoda_option and available_lsoda
40
- if numba_option or lsoda_option:
41
- self.cnst_v_dict = {}
42
- for key in MODEL_CONST:
43
- cnst_v = Dict.empty(
44
- key_type=types.unicode_type,
45
- value_type=types.float64
46
- )
47
- for k, v in MODEL_CONST[key].items():
48
- cnst_v[k] = v['V']
49
- self.cnst_v_dict[key] = cnst_v
50
- self.cnst_q_dict = {}
51
- for key in MODEL_CONST:
52
- cnst_q = Dict.empty(
53
- key_type=types.unicode_type,
54
- value_type=types.float64
55
- )
56
- for k, v in MODEL_CONST[key].items():
57
- if v.get('Q'):
58
- cnst_q[k] = v['Q']
59
- self.cnst_q_dict[key] = cnst_q
60
-
61
- def load_optimization_data(self, time_exp, dict_c_exp, start_c_in_venous, animal=ANIMALS.MOUSE):
62
- self.time_exp = time_exp
63
- self.dict_c_exp = dict_c_exp
64
- self.start_c_in_venous = start_c_in_venous
65
- self.animal = animal
66
-
67
- def _get_sol_difurs(self):
68
- return self(max(self.time_exp), self.start_c_in_venous, self.animal)
69
-
70
- def fitness(self, k_cl):
71
-
72
- self.k_cl = k_cl
73
-
74
- sol_difurs = self._get_sol_difurs()
75
- # Список для хранения результатов
76
- present_organs_indices = []
77
-
78
- # Проверяем, какие ключи из 'organs' есть в 'dict_n'
79
- for organ in self._organs:
80
- if organ in self.dict_c_exp:
81
- index = self._organs.index(organ) # Получаем индекс органа в списке organs
82
- present_organs_indices.append((organ, index))
83
-
84
- rez_err = 0
85
- for organ, index in present_organs_indices:
86
- mean_y = sum(sol_difurs[index]) / len(sol_difurs[index])
87
- a = [(sol_difurs[index][self.time_exp[i]] - self.dict_c_exp[organ][i]) ** 2 for i in range(len(self.dict_c_exp[organ]))]
88
- a = sum(a)
89
- b = [(mean_y - self.dict_c_exp[organ][i]) ** 2 for i in
90
- range(len(self.dict_c_exp[organ]))]
91
- b = sum(b)
92
- rez_err += a / b
93
- # rez_err += sum([abs(sol_difurs[:, index][self.time_exp[i]] - self.dict_c_exp[organ][i]) for i in
94
- # range(len(self.dict_c_exp[organ]))])
95
-
96
- return rez_err
97
- def _get_result(self, fun, t, max_time, K_CL, animal=ANIMALS.MOUSE):
98
- if not self.lsoda_option:
99
- return solve_ivp(
100
- fun=fun,
101
- t_span=[0, max_time],
102
- y0=self.y0,
103
- t_eval=t,
104
- method=LSODA,
105
- )
106
- else:
107
- return lsoda(
108
- funcptr=fun,
109
- u0=np.array(self.y0, dtype=np.float64),
110
- t_eval=np.array(t, dtype=np.float64),
111
- data=np.array(K_CL, dtype=np.float64),
112
- )
113
-
114
- def _prepare_result(self, t, res):
115
- if not self.lsoda_option:
116
- self._res = res.y
117
- else:
118
- res = res.T
119
- self._res = res
120
- self.last_result = {
121
- 't': t * 60
122
- }
123
- if not self.lsoda_option:
124
- for organ in self._organs:
125
- index = self._organs.index(organ)
126
- self.last_result[organ] = res.y[index]
127
- else:
128
- for organ in self._organs:
129
- index = self._organs.index(organ)
130
- self.last_result[organ] = res[index]
131
-
132
- def __call__(self, max_time, start_c_in_venous, animal=ANIMALS.MOUSE, step=1.0):
133
- self.y0 = [0 for _ in range(15)] # всего в модели 15 органов
134
- self.y0[-2] = start_c_in_venous
135
- t = np.linspace(0, max_time, max_time + 1 if self._optim else int(1 / step * max_time) + 1) / 60
136
-
137
- if not hasattr(self, 'k_cl'):
138
- self.k_cl = []
139
-
140
- full_k = []
141
- i = 0
142
- for name in self._organs:
143
- know_k = self.know_k.get(name)
144
- if know_k is not None:
145
- full_k.append(know_k)
146
- else:
147
- full_k.append(self.k_cl[i])
148
- i += 1
149
- full_cl = []
150
-
151
- for name in self._cl_organs:
152
- know_k = self.know_cl.get(name)
153
- if know_k is not None:
154
- full_cl.append(know_k)
155
- else:
156
- full_cl.append(self.k_cl[i])
157
- i += 1
158
- if not self.numba_option and not self.lsoda_option:
159
- res = self._get_result(
160
- fun=lambda time, y: self.fullPBPKmodel(y, time, [*full_k, *full_cl], animal),
161
- t=t,
162
- max_time=max_time,
163
- K_CL=[*full_k, *full_cl],
164
- animal=animal
165
- )
166
- elif self.lsoda_option:
167
- cnst_v = self.cnst_v_dict[animal]
168
- cnst_q = self.cnst_q_dict[animal]
169
- res, success = self._get_result(
170
- fun=self.lsoda_fullPBPK_for_optimization.address,
171
- t=t,
172
- max_time=max_time,
173
- K_CL=[*full_k, *full_cl, *[cnst_q[key] for key in cnst_q.keys()], *[cnst_v[key] for key in cnst_v.keys()]],
174
- animal=animal
175
- )
176
- else:
177
- k_cl = np.array([*full_k, *full_cl])
178
- cnst_v = self.cnst_v_dict[animal]
179
- cnst_q = self.cnst_q_dict[animal]
180
- function = lambda time, c: self.numba_fullPBPK_for_optimization(
181
- y=c,
182
- t=time,
183
- K_CL=k_cl.astype(np.float64),
184
- cnst_q=cnst_q,
185
- cnst_v=cnst_v
186
- )
187
- res = self._get_result(
188
- fun=function,
189
- t=t,
190
- max_time=max_time,
191
- K_CL=[*full_k, *full_cl],
192
- animal=animal
193
- )
194
- self._prepare_result(t, res)
195
- return self._res
196
-
197
- def plot_last_result(self, organ_names=[], left=None, right=None, user_names={}, theoretic_data={}, y_lims={}):
198
- if hasattr(self, 'last_result'):
199
- for name in organ_names:
200
- if theoretic_data.get(name):
201
- plt.plot(theoretic_data[name]['x'], theoretic_data[name]['y'], '*r')
202
- plt.plot(
203
- self.last_result['t'],
204
- self.last_result.get(name),
205
- )
206
- plt.title(user_names.get(name, name))
207
- plt.xlim(left=left, right=right)
208
- if y_lims.get(name):
209
- plt.ylim(y_lims.get(name))
210
- plt.grid()
211
- plt.show()
212
-
213
- def optimize(self, method=None, user_method=None, method_is_func=True,
214
- optimization_func_name='__call__', **kwargs):
215
- """
216
- Функция оптимизации модели
217
-
218
- Args:
219
- method: Метод оптимизации, любой доступный minimize + 'country_optimization' и 'country_optimization_v2'
220
- max_step: Максимальный шаг при решении СДУ
221
- **kwargs: Дополнительные именованные аргументы
222
-
223
- Returns:
224
- None
225
- """
226
- self._optim = True
227
- f = lambda x: self.fitness(x)
228
- if user_method is not None:
229
- if method_is_func:
230
- x = user_method(f, **kwargs)
231
- else:
232
- optimization_obj = user_method(f, **kwargs)
233
- x = getattr(optimization_obj, optimization_func_name)()
234
- else:
235
- if method == 'country_optimization':
236
- CA = CountriesAlgorithm(
237
- f=f,
238
- memory_list=getattr(self, 'memory', None),
239
- **kwargs
240
- )
241
- CA.start()
242
- x = CA.countries[0].population[0].x
243
- elif method == 'country_optimization_v2':
244
- CA = CountriesAlgorithm_v2(
245
- f=f,
246
- **kwargs
247
- )
248
- CA.start()
249
- x = CA.countries[0].population[0].x
250
- elif method == 'GA':
251
- CA = GeneticAlgorithm(
252
- f=f,
253
- **kwargs
254
- )
255
- x = CA.start()
256
- else:
257
- res = minimize(
258
- fun=f,
259
- method=method,
260
- **kwargs
261
- )
262
- x = res.x
263
- self._optim = False
264
- return x
265
-
266
- def update_know_params(self, k_cl=None):
267
- if k_cl:
268
- i = 0
269
- for name in self._organs:
270
- know_k = self.know_k.get(name)
271
- if know_k is None:
272
- self.know_k[name] = k_cl[i]
273
- i += 1
274
- for name in self._cl_organs:
275
- know_cl = self.know_cl.get(name)
276
- if know_cl is None:
277
- self.know_cl[name] = k_cl[i]
278
- i += 1
279
-
280
- def get_unknown_params(self):
281
- result = []
282
- for name in self._organs:
283
- know_k = self.know_k.get(name)
284
- if know_k is None:
285
- result.append(f"k_{name}")
286
- for name in self._cl_organs:
287
- know_cl = self.know_cl.get(name)
288
- if know_cl is None:
289
- result.append(f"cl_{name}")
290
- return result
291
-
292
- def fullPBPKmodel(self, y, t, K_CL, animal=ANIMALS.MOUSE): # V, Q, K, CL):
293
- # 15 органов
294
- cnst = MODEL_CONST[animal]
295
- C_lung, C_heart, C_brain, C_muscle, C_fat, C_skin, C_bone, \
296
- C_kidney, C_liver, C_gut, C_spleen, C_stomach, C_pancreas, C_V, C_A = y
297
-
298
- K_lung, K_heart, K_brain, K_muscle, K_fat, K_skin, K_bone, \
299
- K_kidney, K_liver, K_gut, K_spleen, K_stomach, K_pancreas, K_liver_cl, K_kidney_cl = K_CL[:15]
300
- CL_kidney, CL_liver = K_CL[15:]
301
-
302
- dC_lung_dt = cnst['lung']['Q'] * (C_V - C_lung / K_lung) / cnst['lung']['V']
303
- dC_heart_dt = cnst['heart']['Q'] * (C_A - C_heart / K_heart) / cnst['heart']['V']
304
- dC_brain_dt = cnst['brain']['Q'] * (C_A - C_brain / K_brain) / cnst['brain']['V']
305
- dC_muscle_dt = cnst['muscle']['Q'] * (C_A - C_muscle / K_muscle) / cnst['muscle']['V']
306
- dC_fat_dt = cnst['adipose']['Q'] * (C_A - C_fat / K_fat) / cnst['adipose']['V']
307
- dC_skin_dt = cnst['skin']['Q'] * (C_A - C_skin / K_skin) / cnst['skin']['V']
308
- dC_bone_dt = cnst['bone']['Q'] * (C_A - C_bone / K_bone) / cnst['bone']['V']
309
- # Kidney V(Kidney)*dC(Kidney)/dt = Q(Kidney)*C(A)-Q(Kidney)*CV(Kidney)-CL(Kidney,int)*CV(Kidney,int)?
310
- dC_kidney_dt = (cnst['kidney']['Q'] * (C_A - C_kidney / K_kidney) - CL_kidney * C_kidney / K_kidney_cl) / \
311
- cnst['kidney']['V'] # ???
312
-
313
- # Liver V(Liver)*dC(Liver)/dt = (Q(Liver)-Q(Spleen)-Q(Gut)-Q(Pancreas)-Q(Stomach))*C(A) + Q(Spleen)*CV(Spleen) +
314
- # + Q(Gut)*CV(Gut) + Q(Pancreas)*CV(Pancreas) + Q(Stomach)*CV(Stomach) -
315
- # - Q(Liver)*CV(Liver) - CL(Liver,int)*CV(Liver,int)? # тут скорее всего нужно вычитать потоки из друг друга дополнительно по крови что бы сохранить массовый баланс
316
- Q_liver_in_from_art = cnst['liver']['Q'] - cnst['gut']['Q'] - cnst['spleen']['Q'] - \
317
- cnst['pancreas']['Q'] - cnst['stomach']['Q']
318
- dC_liver_dt = (
319
- Q_liver_in_from_art * C_A + cnst['gut']['Q'] * C_gut / K_gut
320
- + cnst['spleen']['Q'] * C_spleen / K_spleen
321
- + cnst['stomach']['Q'] * C_stomach / K_stomach
322
- + cnst['pancreas']['Q'] * C_pancreas / K_pancreas
323
- - cnst['liver']['Q'] * C_liver / K_liver
324
- - CL_liver * C_liver / K_liver_cl # ???
325
- ) / cnst['liver']['V']
326
-
327
- dC_gut_dt = cnst['gut']['Q'] * (C_A - C_gut / K_gut) / cnst['gut']['V']
328
- dC_spleen_dt = cnst['spleen']['Q'] * (C_A - C_spleen / K_spleen) / cnst['spleen']['V']
329
- dC_stomach_dt = cnst['stomach']['Q'] * (C_A - C_stomach / K_stomach) / cnst['stomach']['V']
330
- dC_pancreas_dt = cnst['pancreas']['Q'] * (C_A - C_pancreas / K_pancreas) / cnst['pancreas']['V']
331
-
332
- dC_venouse_dt = (
333
- cnst['heart']['Q'] * C_heart / K_heart
334
- + cnst['brain']['Q'] * C_brain / K_brain
335
- + cnst['muscle']['Q'] * C_muscle / K_muscle
336
- + cnst['skin']['Q'] * C_skin / K_skin
337
- + cnst['adipose']['Q'] * C_fat / K_fat
338
- + cnst['bone']['Q'] * C_bone / K_bone
339
- + cnst['kidney']['Q'] * C_kidney / K_kidney
340
- + cnst['liver']['Q'] * C_liver / K_liver
341
- - cnst['lung']['Q'] * C_V
342
- ) / cnst['venous_blood']['V']
343
-
344
- dC_arterial_dt = cnst['lung']['Q'] * (C_lung / K_lung - C_A) / cnst['arterial_blood']['V']
345
-
346
- y_new = [dC_lung_dt, dC_heart_dt, dC_brain_dt, dC_muscle_dt, dC_fat_dt, dC_skin_dt, dC_bone_dt, \
347
- dC_kidney_dt, dC_liver_dt, dC_gut_dt, dC_spleen_dt, dC_stomach_dt, dC_pancreas_dt, dC_venouse_dt,
348
- dC_arterial_dt]
349
- return y_new
350
-
351
- @staticmethod
352
- @njit
353
- def numba_fullPBPK_for_optimization(y, t, K_CL, cnst_q, cnst_v):
354
- C_lung, C_heart, C_brain, C_muscle, C_fat, C_skin, C_bone, \
355
- C_kidney, C_liver, C_gut, C_spleen, C_stomach, C_pancreas, C_V, C_A = y
356
-
357
- K_lung, K_heart, K_brain, K_muscle, K_fat, K_skin, K_bone, \
358
- K_kidney, K_liver, K_gut, K_spleen, K_stomach, K_pancreas, K_liver_cl, K_kidney_cl = K_CL[:15]
359
- CL_kidney, CL_liver = K_CL[15:]
360
-
361
- dC_lung_dt = cnst_q['lung'] * (C_V - C_lung / K_lung) / cnst_v['lung']
362
- dC_heart_dt = cnst_q['heart'] * (C_A - C_heart / K_heart) / cnst_v['heart']
363
- dC_brain_dt = cnst_q['brain'] * (C_A - C_brain / K_brain) / cnst_v['brain']
364
- dC_muscle_dt = cnst_q['muscle'] * (C_A - C_muscle / K_muscle) / cnst_v['muscle']
365
- dC_fat_dt = cnst_q['adipose'] * (C_A - C_fat / K_fat) / cnst_v['adipose']
366
- dC_skin_dt = cnst_q['skin'] * (C_A - C_skin / K_skin) / cnst_v['skin']
367
- dC_bone_dt = cnst_q['bone'] * (C_A - C_bone / K_bone) / cnst_v['bone']
368
- # Kidney V(Kidney)*dC(Kidney)/dt = Q(Kidney)*C(A)-Q(Kidney)*CV(Kidney)-CL(Kidney,int)*CV(Kidney,int)?
369
- dC_kidney_dt = (cnst_q['kidney'] * (C_A - C_kidney / K_kidney) - CL_kidney * C_kidney / K_kidney_cl) / \
370
- cnst_v['kidney'] # ???
371
-
372
- # Liver V(Liver)*dC(Liver)/dt = (Q(Liver)-Q(Spleen)-Q(Gut)-Q(Pancreas)-Q(Stomach))*C(A) + Q(Spleen)*CV(Spleen) +
373
- # + Q(Gut)*CV(Gut) + Q(Pancreas)*CV(Pancreas) + Q(Stomach)*CV(Stomach) -
374
- # - Q(Liver)*CV(Liver) - CL(Liver,int)*CV(Liver,int)? # тут скорее всего нужно вычитать потоки из друг друга дополнительно по крови что бы сохранить массовый баланс
375
- Q_liver_in_from_art = cnst_q['liver'] - cnst_q['gut'] - cnst_q['spleen'] - \
376
- cnst_q['pancreas'] - cnst_q['stomach']
377
- dC_liver_dt = (
378
- Q_liver_in_from_art * C_A + cnst_q['gut'] * C_gut / K_gut
379
- + cnst_q['spleen'] * C_spleen / K_spleen
380
- + cnst_q['stomach'] * C_stomach / K_stomach
381
- + cnst_q['pancreas'] * C_pancreas / K_pancreas
382
- - cnst_q['liver'] * C_liver / K_liver
383
- - CL_liver * C_liver / K_liver_cl # ???
384
- ) / cnst_v['liver']
385
-
386
- dC_gut_dt = cnst_q['gut'] * (C_A - C_gut / K_gut) / cnst_v['gut']
387
- dC_spleen_dt = cnst_q['spleen'] * (C_A - C_spleen / K_spleen) / cnst_v['spleen']
388
- dC_stomach_dt = cnst_q['stomach'] * (C_A - C_stomach / K_stomach) / cnst_v['stomach']
389
- dC_pancreas_dt = cnst_q['pancreas'] * (C_A - C_pancreas / K_pancreas) / cnst_v['pancreas']
390
-
391
- dC_venouse_dt = (
392
- cnst_q['heart'] * C_heart / K_heart
393
- + cnst_q['brain'] * C_brain / K_brain
394
- + cnst_q['muscle'] * C_muscle / K_muscle
395
- + cnst_q['skin'] * C_skin / K_skin
396
- + cnst_q['adipose'] * C_fat / K_fat
397
- + cnst_q['bone'] * C_bone / K_bone
398
- + cnst_q['kidney'] * C_kidney / K_kidney
399
- + cnst_q['liver'] * C_liver / K_liver
400
- - cnst_q['lung'] * C_V
401
- ) / cnst_v['venous_blood']
402
-
403
- dC_arterial_dt = cnst_q['lung'] * (C_lung / K_lung - C_A) / cnst_v['arterial_blood']
404
-
405
- y_new = np.array([dC_lung_dt, dC_heart_dt, dC_brain_dt, dC_muscle_dt, dC_fat_dt, dC_skin_dt, dC_bone_dt, \
406
- dC_kidney_dt, dC_liver_dt, dC_gut_dt, dC_spleen_dt, dC_stomach_dt, dC_pancreas_dt, dC_venouse_dt,
407
- dC_arterial_dt]).astype(np.float64)
408
- return y_new
409
-
410
- @staticmethod
411
- @cfunc(lsoda_sig)
412
- def lsoda_fullPBPK_for_optimization(t, y, y_new, data):
413
-
414
- C_lung = y[0]
415
- C_heart = y[1]
416
- C_brain = y[2]
417
- C_muscle = y[3]
418
- C_fat = y[4]
419
- C_skin = y[5]
420
- C_bone = y[6]
421
- C_kidney = y[7]
422
- C_liver = y[8]
423
- C_gut = y[9]
424
- C_spleen = y[10]
425
- C_stomach = y[11]
426
- C_pancreas = y[12]
427
- C_V = y[13]
428
- C_A = y[14]
429
-
430
- K_lung = data[0]
431
- K_heart = data[1]
432
- K_brain = data[2]
433
- K_muscle = data[3]
434
- K_fat = data[4]
435
- K_skin = data[5]
436
- K_bone = data[6]
437
- K_kidney = data[7]
438
- K_liver = data[8]
439
- K_gut = data[9]
440
- K_spleen = data[10]
441
- K_stomach = data[11]
442
- K_pancreas = data[12]
443
- K_liver_cl = data[13]
444
- K_kidney_cl = data[14]
445
- CL_kidney = data[15]
446
- CL_liver = data[16]
447
-
448
- cnst_q_adipose = data[17]
449
- cnst_q_bone = data[18]
450
- cnst_q_brain = data[19]
451
- cnst_q_gut = data[20]
452
- cnst_q_heart = data[21]
453
- cnst_q_kidney = data[22]
454
- cnst_q_liver = data[23]
455
- cnst_q_lung = data[24]
456
- cnst_q_muscle = data[25]
457
- cnst_q_pancreas = data[26]
458
- cnst_q_skin = data[27]
459
- cnst_q_spleen = data[28]
460
- cnst_q_stomach = data[29]
461
- cnst_q_teaster = data[30]
462
-
463
- cnst_v_adipose = data[31]
464
- cnst_v_bone = data[32]
465
- cnst_v_brain = data[33]
466
- cnst_v_gut = data[34]
467
- cnst_v_heart = data[35]
468
- cnst_v_kidney = data[36]
469
- cnst_v_liver = data[37]
470
- cnst_v_lung = data[38]
471
- cnst_v_muscle = data[39]
472
- cnst_v_pancreas = data[40]
473
- cnst_v_skin = data[41]
474
- cnst_v_spleen = data[42]
475
- cnst_v_stomach = data[43]
476
- cnst_v_teaster = data[44]
477
- cnst_v_arterial_blood = data[45]
478
- cnst_v_venous_blood = data[46]
479
-
480
- dC_lung_dt = cnst_q_lung * (C_V - C_lung / K_lung) / cnst_v_lung
481
- dC_heart_dt = cnst_q_heart * (C_A - C_heart / K_heart) / cnst_v_heart
482
- dC_brain_dt = cnst_q_brain * (C_A - C_brain / K_brain) / cnst_v_brain
483
- dC_muscle_dt = cnst_q_muscle * (C_A - C_muscle / K_muscle) / cnst_v_muscle
484
- dC_fat_dt = cnst_q_adipose * (C_A - C_fat / K_fat) / cnst_v_adipose
485
- dC_skin_dt = cnst_q_skin * (C_A - C_skin / K_skin) / cnst_v_skin
486
- dC_bone_dt = cnst_q_bone * (C_A - C_bone / K_bone) / cnst_v_bone
487
- # Kidney V(Kidney)*dC(Kidney)/dt = Q(Kidney)*C(A)-Q(Kidney)*CV(Kidney)-CL(Kidney,int)*CV(Kidney,int)?
488
- dC_kidney_dt = (cnst_q_kidney * (C_A - C_kidney / K_kidney) - CL_kidney * C_kidney / K_kidney_cl) / \
489
- cnst_v_kidney # ???
490
-
491
- # Liver V(Liver)*dC(Liver)/dt = (Q(Liver)-Q(Spleen)-Q(Gut)-Q(Pancreas)-Q(Stomach))*C(A) + Q(Spleen)*CV(Spleen) +
492
- # + Q(Gut)*CV(Gut) + Q(Pancreas)*CV(Pancreas) + Q(Stomach)*CV(Stomach) -
493
- # - Q(Liver)*CV(Liver) - CL(Liver,int)*CV(Liver,int)? # тут скорее всего нужно вычитать потоки из друг друга дополнительно по крови что бы сохранить массовый баланс
494
- Q_liver_in_from_art = cnst_q_liver - cnst_q_gut - cnst_q_spleen - \
495
- cnst_q_pancreas - cnst_q_stomach
496
- dC_liver_dt = (
497
- Q_liver_in_from_art * C_A + cnst_q_gut * C_gut / K_gut
498
- + cnst_q_spleen * C_spleen / K_spleen
499
- + cnst_q_stomach * C_stomach / K_stomach
500
- + cnst_q_pancreas * C_pancreas / K_pancreas
501
- - cnst_q_liver * C_liver / K_liver
502
- - CL_liver * C_liver / K_liver_cl # ???
503
- ) / cnst_v_liver
504
-
505
- dC_gut_dt = cnst_q_gut * (C_A - C_gut / K_gut) / cnst_v_gut
506
- dC_spleen_dt = cnst_q_spleen * (C_A - C_spleen / K_spleen) / cnst_v_spleen
507
- dC_stomach_dt = cnst_q_stomach * (C_A - C_stomach / K_stomach) / cnst_v_stomach
508
- dC_pancreas_dt = cnst_q_pancreas * (C_A - C_pancreas / K_pancreas) / cnst_v_pancreas
509
-
510
- dC_venouse_dt = (
511
- cnst_q_heart * C_heart / K_heart
512
- + cnst_q_brain * C_brain / K_brain
513
- + cnst_q_muscle * C_muscle / K_muscle
514
- + cnst_q_skin * C_skin / K_skin
515
- + cnst_q_adipose * C_fat / K_fat
516
- + cnst_q_bone * C_bone / K_bone
517
- + cnst_q_kidney * C_kidney / K_kidney
518
- + cnst_q_liver * C_liver / K_liver
519
- - cnst_q_lung * C_V
520
- ) / cnst_v_venous_blood
521
-
522
- dC_arterial_dt = cnst_q_lung * (C_lung / K_lung - C_A) / cnst_v_arterial_blood
523
- y_new[0] = dC_lung_dt
524
- y_new[1] = dC_heart_dt
525
- y_new[2] = dC_brain_dt
526
- y_new[3] = dC_muscle_dt
527
- y_new[4] = dC_fat_dt
528
- y_new[5] = dC_skin_dt
529
- y_new[6] = dC_bone_dt
530
- y_new[7] = dC_kidney_dt
531
- y_new[8] = dC_liver_dt
532
- y_new[9] = dC_gut_dt
533
- y_new[10] = dC_spleen_dt
534
- y_new[11] = dC_stomach_dt
535
- y_new[12] = dC_pancreas_dt
536
- y_new[13] = dC_venouse_dt
537
- y_new[14] = dC_arterial_dt
538
-
539
- # y_new = [dC_lung_dt, dC_heart_dt, dC_brain_dt, dC_muscle_dt, dC_fat_dt, dC_skin_dt, dC_bone_dt, \
540
- # dC_kidney_dt, dC_liver_dt, dC_gut_dt, dC_spleen_dt, dC_stomach_dt, dC_pancreas_dt, dC_venouse_dt,
541
- # dC_arterial_dt]
542
-
543
-
544
- class ReleasePBPKmod(PBPKmod):
545
-
546
- @staticmethod
547
- def ode_release(solver, t, y0, release_function, d, v, is_lsoda=False):
548
- result = []
549
- new_y0 = y0
550
- old_release_correction = 0
551
- for i in range(1, len(t)):
552
- if is_lsoda:
553
- res, _ = solver(new_y0, t[i - 1], t[i])
554
- y = res.T
555
- else:
556
- res = solver(new_y0, t[i - 1], t[i])
557
- y = res.y
558
- release_correction = release_function(t[i], d)
559
- plus_release = release_correction - old_release_correction
560
- all_corrections = plus_release
561
- y[-2][1] += all_corrections / v
562
- old_release_correction = release_correction
563
- if i == 1:
564
- result.append([y[i][0] for i in range(y.shape[0])])
565
- new_y0 = np.array([y[i][1] for i in range(y.shape[0])])
566
- result.append(new_y0)
567
- return np.array(result).T
568
-
569
- def __init__(self, release_parameters: dict=None, release_function: callable=None, know_k=None, know_cl=None, numba_option=False, lsoda_option=False):
570
- super().__init__(
571
- know_k=know_k, know_cl=know_cl, numba_option=numba_option, lsoda_option=lsoda_option
572
- )
573
- self.release_function = release_function
574
- if release_parameters is None:
575
- self.release_parameters = {}
576
- else:
577
- self.release_parameters = release_parameters
578
- self.know_release_parameters = set(self.release_parameters.keys())
579
-
580
- @staticmethod
581
- def _default_release_function(t, d, m, b, c):
582
- """
583
- Функция для поправки на высвобождение
584
- """
585
- return d * c * t ** b / (t ** b + m)
586
-
587
- def get_release_function(self):
588
- return lambda t, d: self._get_release_function()(t, d, **self.release_parameters)
589
-
590
- def _get_release_function(self):
591
- if self.release_function is not None:
592
- return self.release_function
593
- else:
594
- return self._default_release_function
595
-
596
- @property
597
- def _release_parameters_list(self) -> list[str]:
598
- method = self._get_release_function()
599
- arguments = inspect.getfullargspec(method).args
600
- return [arg for arg in arguments if arg not in {'t', 'd'}]
601
-
602
- def get_unknown_params(self):
603
- result = super().get_unknown_params()
604
- arguments = self._release_parameters_list
605
- for arg in arguments:
606
- know_arg = self.release_parameters.get(arg)
607
- if know_arg is None:
608
- result.append(f"release_{arg}")
609
- return result
610
-
611
- def update_know_params(self, k_cl=None, release_parameters=None):
612
- super().update_know_params(k_cl)
613
- if release_parameters is not None:
614
- self.release_parameters = release_parameters
615
- self.know_release_parameters = set(self.release_parameters.keys())
616
-
617
- def _get_sol_difurs(self):
618
- return self(max(self.time_exp), self.d, self.animal)
619
-
620
- def fitness(self, x):
621
-
622
- n = len(self._organs) + len(self._cl_organs) - len(self.know_cl) - len(self.know_k)
623
- self.k_cl = x[:n]
624
- i = 0
625
- for arg in self._release_parameters_list:
626
- if not arg in self.know_release_parameters:
627
- self.release_parameters[arg] = x[n + i]
628
- i += 1
629
- return super().fitness(self.k_cl)
630
-
631
- def _get_result(self, fun, t, max_time, K_CL, animal):
632
- if not self.lsoda_option:
633
- solver = lambda y0, t_left, t_right: solve_ivp(
634
- fun=fun,
635
- t_span=[t_left, t_right],
636
- y0=y0,
637
- t_eval=np.array([t_left, t_right]),
638
- method=LSODA
639
- )
640
- return self.ode_release(solver, t, self.y0, d=self.d, v=self.v, release_function=self.get_release_function())
641
- else:
642
- solver = lambda y0, t_left, t_right: lsoda(
643
- funcptr=fun,
644
- u0=np.array(y0, dtype=np.float64),
645
- t_eval=np.array([t_left, t_right], dtype=np.float64),
646
- data=np.array(K_CL, dtype=np.float64),
647
- )
648
- return self.ode_release(solver, t, self.y0, d=self.d, v=self.v,
649
- release_function=self.get_release_function(), is_lsoda=True), True
650
-
651
- def _prepare_result(self, t, res):
652
- self._res = res
653
- self.last_result = {
654
- 't': t * 60
655
- }
656
- for organ in self._organs:
657
- index = self._organs.index(organ)
658
- self.last_result[organ] = res[index]
659
-
660
- def __call__(self, max_time, d, animal=ANIMALS.MOUSE, step=1.0):
661
- self.d = d
662
- const = MODEL_CONST[animal]
663
- self.v = const['venous_blood']['V']
664
- return super().__call__(
665
- max_time=max_time,
666
- start_c_in_venous=0,
667
- animal=animal,
668
- step=step
669
- )
670
-
671
- def optimize(self, method=None, user_method=None, method_is_func=True,
672
- optimization_func_name='__call__', **kwargs):
673
-
674
- return super().optimize(
675
- method=method, user_method=user_method, method_is_func=method_is_func,
676
- optimization_func_name=optimization_func_name, **kwargs
677
- )
678
-
679
- def load_optimization_data(self, time_exp, dict_c_exp, d, animal=ANIMALS.MOUSE):
680
- self.time_exp = time_exp
681
- self.dict_c_exp = dict_c_exp
682
- self.d = d
683
- self.animal = animal
1
+ import inspect
2
+ import matplotlib
3
+ import numpy as np
4
+ from scipy.integrate import solve_ivp, LSODA
5
+ from scipy.optimize import minimize
6
+ from PyPharm.algorithms.country_optimization import CountriesAlgorithm
7
+ from PyPharm.algorithms.country_optimization_v2 import CountriesAlgorithm_v2
8
+ from PyPharm.algorithms.genetic_optimization import GeneticAlgorithm
9
+ from PyPharm.constants import MODEL_CONST, ANIMALS
10
+ from numba import njit, types, cfunc
11
+ from numba.typed import Dict
12
+
13
+
14
+ try:
15
+ from numbalsoda import lsoda_sig, lsoda
16
+ except FileNotFoundError:
17
+ available_lsoda = False
18
+ lsoda_sig = types.void(
19
+ types.double,
20
+ types.CPointer(types.double),
21
+ types.CPointer(types.double),
22
+ types.CPointer(types.double)
23
+ )
24
+ lsoda = None
25
+
26
+
27
+ class PBPKmod:
28
+
29
+ _organs = ['lung', 'heart', 'brain', 'muscle', 'adipose', 'skin', 'bone', 'kidney',
30
+ 'liver', 'gut', 'spleen', 'stomach', 'pancreas', 'venous_blood', 'arterial_blood']
31
+ _cl_organs = ['kidney', 'liver']
32
+
33
+ def __init__(self, know_k=None, know_cl=None, numba_option=False, lsoda_option=False):
34
+
35
+ self.know_k = know_k if know_k is not None else {}
36
+ self.know_cl = know_cl if know_cl is not None else {}
37
+ self._optim = False
38
+ self.numba_option = numba_option
39
+ self.lsoda_option = lsoda_option and available_lsoda
40
+ if numba_option or lsoda_option:
41
+ self.cnst_v_dict = {}
42
+ for key in MODEL_CONST:
43
+ cnst_v = Dict.empty(
44
+ key_type=types.unicode_type,
45
+ value_type=types.float64
46
+ )
47
+ for k, v in MODEL_CONST[key].items():
48
+ cnst_v[k] = v['V']
49
+ self.cnst_v_dict[key] = cnst_v
50
+ self.cnst_q_dict = {}
51
+ for key in MODEL_CONST:
52
+ cnst_q = Dict.empty(
53
+ key_type=types.unicode_type,
54
+ value_type=types.float64
55
+ )
56
+ for k, v in MODEL_CONST[key].items():
57
+ if v.get('Q'):
58
+ cnst_q[k] = v['Q']
59
+ self.cnst_q_dict[key] = cnst_q
60
+
61
+ def load_optimization_data(self, time_exp, dict_c_exp, start_c_in_venous, animal=ANIMALS.MOUSE):
62
+ self.time_exp = time_exp
63
+ self.dict_c_exp = dict_c_exp
64
+ self.start_c_in_venous = start_c_in_venous
65
+ self.animal = animal
66
+
67
+ def _get_sol_difurs(self):
68
+ return self(max(self.time_exp), self.start_c_in_venous, self.animal)
69
+
70
+ def fitness(self, k_cl):
71
+
72
+ self.k_cl = k_cl
73
+
74
+ sol_difurs = self._get_sol_difurs()
75
+ # Список для хранения результатов
76
+ present_organs_indices = []
77
+
78
+ # Проверяем, какие ключи из 'organs' есть в 'dict_n'
79
+ for organ in self._organs:
80
+ if organ in self.dict_c_exp:
81
+ index = self._organs.index(organ) # Получаем индекс органа в списке organs
82
+ present_organs_indices.append((organ, index))
83
+
84
+ rez_err = 0
85
+ for organ, index in present_organs_indices:
86
+ mean_y = sum(sol_difurs[index]) / len(sol_difurs[index])
87
+ a = [(sol_difurs[index][self.time_exp[i]] - self.dict_c_exp[organ][i]) ** 2 for i in range(len(self.dict_c_exp[organ]))]
88
+ a = sum(a)
89
+ b = [(mean_y - self.dict_c_exp[organ][i]) ** 2 for i in
90
+ range(len(self.dict_c_exp[organ]))]
91
+ b = sum(b)
92
+ rez_err += a / b
93
+ # rez_err += sum([abs(sol_difurs[:, index][self.time_exp[i]] - self.dict_c_exp[organ][i]) for i in
94
+ # range(len(self.dict_c_exp[organ]))])
95
+
96
+ return rez_err
97
+ def _get_result(self, fun, t, max_time, K_CL, animal=ANIMALS.MOUSE):
98
+ if not self.lsoda_option:
99
+ return solve_ivp(
100
+ fun=fun,
101
+ t_span=[0, max_time],
102
+ y0=self.y0,
103
+ t_eval=t,
104
+ method=LSODA,
105
+ )
106
+ else:
107
+ return lsoda(
108
+ funcptr=fun,
109
+ u0=np.array(self.y0, dtype=np.float64),
110
+ t_eval=np.array(t, dtype=np.float64),
111
+ data=np.array(K_CL, dtype=np.float64),
112
+ )
113
+
114
+ def _prepare_result(self, t, res):
115
+ if not self.lsoda_option:
116
+ self._res = res.y
117
+ else:
118
+ res = res.T
119
+ self._res = res
120
+ self.last_result = {
121
+ 't': t * 60
122
+ }
123
+ if not self.lsoda_option:
124
+ for organ in self._organs:
125
+ index = self._organs.index(organ)
126
+ self.last_result[organ] = res.y[index]
127
+ else:
128
+ for organ in self._organs:
129
+ index = self._organs.index(organ)
130
+ self.last_result[organ] = res[index]
131
+
132
+ def __call__(self, max_time, start_c_in_venous, animal=ANIMALS.MOUSE, step=1.0):
133
+ self.y0 = [0 for _ in range(15)] # всего в модели 15 органов
134
+ self.y0[-2] = start_c_in_venous
135
+ t = np.linspace(0, max_time, max_time + 1 if self._optim else int(1 / step * max_time) + 1) / 60
136
+
137
+ if not hasattr(self, 'k_cl'):
138
+ self.k_cl = []
139
+
140
+ full_k = []
141
+ i = 0
142
+ for name in self._organs:
143
+ know_k = self.know_k.get(name)
144
+ if know_k is not None:
145
+ full_k.append(know_k)
146
+ else:
147
+ full_k.append(self.k_cl[i])
148
+ i += 1
149
+ full_cl = []
150
+
151
+ for name in self._cl_organs:
152
+ know_k = self.know_cl.get(name)
153
+ if know_k is not None:
154
+ full_cl.append(know_k)
155
+ else:
156
+ full_cl.append(self.k_cl[i])
157
+ i += 1
158
+ if not self.numba_option and not self.lsoda_option:
159
+ res = self._get_result(
160
+ fun=lambda time, y: self.fullPBPKmodel(y, time, [*full_k, *full_cl], animal),
161
+ t=t,
162
+ max_time=max_time,
163
+ K_CL=[*full_k, *full_cl],
164
+ animal=animal
165
+ )
166
+ elif self.lsoda_option:
167
+ cnst_v = self.cnst_v_dict[animal]
168
+ cnst_q = self.cnst_q_dict[animal]
169
+ res, success = self._get_result(
170
+ fun=self.lsoda_fullPBPK_for_optimization.address,
171
+ t=t,
172
+ max_time=max_time,
173
+ K_CL=[*full_k, *full_cl, *[cnst_q[key] for key in cnst_q.keys()], *[cnst_v[key] for key in cnst_v.keys()]],
174
+ animal=animal
175
+ )
176
+ else:
177
+ k_cl = np.array([*full_k, *full_cl])
178
+ cnst_v = self.cnst_v_dict[animal]
179
+ cnst_q = self.cnst_q_dict[animal]
180
+ function = lambda time, c: self.numba_fullPBPK_for_optimization(
181
+ y=c,
182
+ t=time,
183
+ K_CL=k_cl.astype(np.float64),
184
+ cnst_q=cnst_q,
185
+ cnst_v=cnst_v
186
+ )
187
+ res = self._get_result(
188
+ fun=function,
189
+ t=t,
190
+ max_time=max_time,
191
+ K_CL=[*full_k, *full_cl],
192
+ animal=animal
193
+ )
194
+ self._prepare_result(t, res)
195
+ return self._res
196
+
197
+ def plot_last_result(self, organ_names=[], left=None, right=None, user_names={}, theoretic_data={}, y_lims={}):
198
+ if hasattr(self, 'last_result'):
199
+ for name in organ_names:
200
+ if theoretic_data.get(name):
201
+ plt.plot(theoretic_data[name]['x'], theoretic_data[name]['y'], '*r')
202
+ plt.plot(
203
+ self.last_result['t'],
204
+ self.last_result.get(name),
205
+ )
206
+ plt.title(user_names.get(name, name))
207
+ plt.xlim(left=left, right=right)
208
+ if y_lims.get(name):
209
+ plt.ylim(y_lims.get(name))
210
+ plt.grid()
211
+ plt.show()
212
+
213
+ def optimize(self, method=None, user_method=None, method_is_func=True,
214
+ optimization_func_name='__call__', **kwargs):
215
+ """
216
+ Функция оптимизации модели
217
+
218
+ Args:
219
+ method: Метод оптимизации, любой доступный minimize + 'country_optimization' и 'country_optimization_v2'
220
+ max_step: Максимальный шаг при решении СДУ
221
+ **kwargs: Дополнительные именованные аргументы
222
+
223
+ Returns:
224
+ None
225
+ """
226
+ self._optim = True
227
+ f = lambda x: self.fitness(x)
228
+ if user_method is not None:
229
+ if method_is_func:
230
+ x = user_method(f, **kwargs)
231
+ else:
232
+ optimization_obj = user_method(f, **kwargs)
233
+ x = getattr(optimization_obj, optimization_func_name)()
234
+ else:
235
+ if method == 'country_optimization':
236
+ CA = CountriesAlgorithm(
237
+ f=f,
238
+ memory_list=getattr(self, 'memory', None),
239
+ **kwargs
240
+ )
241
+ CA.start()
242
+ x = CA.countries[0].population[0].x
243
+ elif method == 'country_optimization_v2':
244
+ CA = CountriesAlgorithm_v2(
245
+ f=f,
246
+ **kwargs
247
+ )
248
+ CA.start()
249
+ x = CA.countries[0].population[0].x
250
+ elif method == 'GA':
251
+ CA = GeneticAlgorithm(
252
+ f=f,
253
+ **kwargs
254
+ )
255
+ x = CA.start()
256
+ else:
257
+ res = minimize(
258
+ fun=f,
259
+ method=method,
260
+ **kwargs
261
+ )
262
+ x = res.x
263
+ self._optim = False
264
+ return x
265
+
266
+ def update_know_params(self, k_cl=None):
267
+ if k_cl:
268
+ i = 0
269
+ for name in self._organs:
270
+ know_k = self.know_k.get(name)
271
+ if know_k is None:
272
+ self.know_k[name] = k_cl[i]
273
+ i += 1
274
+ for name in self._cl_organs:
275
+ know_cl = self.know_cl.get(name)
276
+ if know_cl is None:
277
+ self.know_cl[name] = k_cl[i]
278
+ i += 1
279
+
280
+ def get_unknown_params(self):
281
+ result = []
282
+ for name in self._organs:
283
+ know_k = self.know_k.get(name)
284
+ if know_k is None:
285
+ result.append(f"k_{name}")
286
+ for name in self._cl_organs:
287
+ know_cl = self.know_cl.get(name)
288
+ if know_cl is None:
289
+ result.append(f"cl_{name}")
290
+ return result
291
+
292
+ def fullPBPKmodel(self, y, t, K_CL, animal=ANIMALS.MOUSE): # V, Q, K, CL):
293
+ # 15 органов
294
+ cnst = MODEL_CONST[animal]
295
+ C_lung, C_heart, C_brain, C_muscle, C_fat, C_skin, C_bone, \
296
+ C_kidney, C_liver, C_gut, C_spleen, C_stomach, C_pancreas, C_V, C_A = y
297
+
298
+ K_lung, K_heart, K_brain, K_muscle, K_fat, K_skin, K_bone, \
299
+ K_kidney, K_liver, K_gut, K_spleen, K_stomach, K_pancreas, K_liver_cl, K_kidney_cl = K_CL[:15]
300
+ CL_kidney, CL_liver = K_CL[15:]
301
+
302
+ dC_lung_dt = cnst['lung']['Q'] * (C_V - C_lung / K_lung) / cnst['lung']['V']
303
+ dC_heart_dt = cnst['heart']['Q'] * (C_A - C_heart / K_heart) / cnst['heart']['V']
304
+ dC_brain_dt = cnst['brain']['Q'] * (C_A - C_brain / K_brain) / cnst['brain']['V']
305
+ dC_muscle_dt = cnst['muscle']['Q'] * (C_A - C_muscle / K_muscle) / cnst['muscle']['V']
306
+ dC_fat_dt = cnst['adipose']['Q'] * (C_A - C_fat / K_fat) / cnst['adipose']['V']
307
+ dC_skin_dt = cnst['skin']['Q'] * (C_A - C_skin / K_skin) / cnst['skin']['V']
308
+ dC_bone_dt = cnst['bone']['Q'] * (C_A - C_bone / K_bone) / cnst['bone']['V']
309
+ # Kidney V(Kidney)*dC(Kidney)/dt = Q(Kidney)*C(A)-Q(Kidney)*CV(Kidney)-CL(Kidney,int)*CV(Kidney,int)?
310
+ dC_kidney_dt = (cnst['kidney']['Q'] * (C_A - C_kidney / K_kidney) - CL_kidney * C_kidney / K_kidney_cl) / \
311
+ cnst['kidney']['V'] # ???
312
+
313
+ # Liver V(Liver)*dC(Liver)/dt = (Q(Liver)-Q(Spleen)-Q(Gut)-Q(Pancreas)-Q(Stomach))*C(A) + Q(Spleen)*CV(Spleen) +
314
+ # + Q(Gut)*CV(Gut) + Q(Pancreas)*CV(Pancreas) + Q(Stomach)*CV(Stomach) -
315
+ # - Q(Liver)*CV(Liver) - CL(Liver,int)*CV(Liver,int)? # тут скорее всего нужно вычитать потоки из друг друга дополнительно по крови что бы сохранить массовый баланс
316
+ Q_liver_in_from_art = cnst['liver']['Q'] - cnst['gut']['Q'] - cnst['spleen']['Q'] - \
317
+ cnst['pancreas']['Q'] - cnst['stomach']['Q']
318
+ dC_liver_dt = (
319
+ Q_liver_in_from_art * C_A + cnst['gut']['Q'] * C_gut / K_gut
320
+ + cnst['spleen']['Q'] * C_spleen / K_spleen
321
+ + cnst['stomach']['Q'] * C_stomach / K_stomach
322
+ + cnst['pancreas']['Q'] * C_pancreas / K_pancreas
323
+ - cnst['liver']['Q'] * C_liver / K_liver
324
+ - CL_liver * C_liver / K_liver_cl # ???
325
+ ) / cnst['liver']['V']
326
+
327
+ dC_gut_dt = cnst['gut']['Q'] * (C_A - C_gut / K_gut) / cnst['gut']['V']
328
+ dC_spleen_dt = cnst['spleen']['Q'] * (C_A - C_spleen / K_spleen) / cnst['spleen']['V']
329
+ dC_stomach_dt = cnst['stomach']['Q'] * (C_A - C_stomach / K_stomach) / cnst['stomach']['V']
330
+ dC_pancreas_dt = cnst['pancreas']['Q'] * (C_A - C_pancreas / K_pancreas) / cnst['pancreas']['V']
331
+
332
+ dC_venouse_dt = (
333
+ cnst['heart']['Q'] * C_heart / K_heart
334
+ + cnst['brain']['Q'] * C_brain / K_brain
335
+ + cnst['muscle']['Q'] * C_muscle / K_muscle
336
+ + cnst['skin']['Q'] * C_skin / K_skin
337
+ + cnst['adipose']['Q'] * C_fat / K_fat
338
+ + cnst['bone']['Q'] * C_bone / K_bone
339
+ + cnst['kidney']['Q'] * C_kidney / K_kidney
340
+ + cnst['liver']['Q'] * C_liver / K_liver
341
+ - cnst['lung']['Q'] * C_V
342
+ ) / cnst['venous_blood']['V']
343
+
344
+ dC_arterial_dt = cnst['lung']['Q'] * (C_lung / K_lung - C_A) / cnst['arterial_blood']['V']
345
+
346
+ y_new = [dC_lung_dt, dC_heart_dt, dC_brain_dt, dC_muscle_dt, dC_fat_dt, dC_skin_dt, dC_bone_dt, \
347
+ dC_kidney_dt, dC_liver_dt, dC_gut_dt, dC_spleen_dt, dC_stomach_dt, dC_pancreas_dt, dC_venouse_dt,
348
+ dC_arterial_dt]
349
+ return y_new
350
+
351
+ @staticmethod
352
+ @njit
353
+ def numba_fullPBPK_for_optimization(y, t, K_CL, cnst_q, cnst_v):
354
+ C_lung, C_heart, C_brain, C_muscle, C_fat, C_skin, C_bone, \
355
+ C_kidney, C_liver, C_gut, C_spleen, C_stomach, C_pancreas, C_V, C_A = y
356
+
357
+ K_lung, K_heart, K_brain, K_muscle, K_fat, K_skin, K_bone, \
358
+ K_kidney, K_liver, K_gut, K_spleen, K_stomach, K_pancreas, K_liver_cl, K_kidney_cl = K_CL[:15]
359
+ CL_kidney, CL_liver = K_CL[15:]
360
+
361
+ dC_lung_dt = cnst_q['lung'] * (C_V - C_lung / K_lung) / cnst_v['lung']
362
+ dC_heart_dt = cnst_q['heart'] * (C_A - C_heart / K_heart) / cnst_v['heart']
363
+ dC_brain_dt = cnst_q['brain'] * (C_A - C_brain / K_brain) / cnst_v['brain']
364
+ dC_muscle_dt = cnst_q['muscle'] * (C_A - C_muscle / K_muscle) / cnst_v['muscle']
365
+ dC_fat_dt = cnst_q['adipose'] * (C_A - C_fat / K_fat) / cnst_v['adipose']
366
+ dC_skin_dt = cnst_q['skin'] * (C_A - C_skin / K_skin) / cnst_v['skin']
367
+ dC_bone_dt = cnst_q['bone'] * (C_A - C_bone / K_bone) / cnst_v['bone']
368
+ # Kidney V(Kidney)*dC(Kidney)/dt = Q(Kidney)*C(A)-Q(Kidney)*CV(Kidney)-CL(Kidney,int)*CV(Kidney,int)?
369
+ dC_kidney_dt = (cnst_q['kidney'] * (C_A - C_kidney / K_kidney) - CL_kidney * C_kidney / K_kidney_cl) / \
370
+ cnst_v['kidney'] # ???
371
+
372
+ # Liver V(Liver)*dC(Liver)/dt = (Q(Liver)-Q(Spleen)-Q(Gut)-Q(Pancreas)-Q(Stomach))*C(A) + Q(Spleen)*CV(Spleen) +
373
+ # + Q(Gut)*CV(Gut) + Q(Pancreas)*CV(Pancreas) + Q(Stomach)*CV(Stomach) -
374
+ # - Q(Liver)*CV(Liver) - CL(Liver,int)*CV(Liver,int)? # тут скорее всего нужно вычитать потоки из друг друга дополнительно по крови что бы сохранить массовый баланс
375
+ Q_liver_in_from_art = cnst_q['liver'] - cnst_q['gut'] - cnst_q['spleen'] - \
376
+ cnst_q['pancreas'] - cnst_q['stomach']
377
+ dC_liver_dt = (
378
+ Q_liver_in_from_art * C_A + cnst_q['gut'] * C_gut / K_gut
379
+ + cnst_q['spleen'] * C_spleen / K_spleen
380
+ + cnst_q['stomach'] * C_stomach / K_stomach
381
+ + cnst_q['pancreas'] * C_pancreas / K_pancreas
382
+ - cnst_q['liver'] * C_liver / K_liver
383
+ - CL_liver * C_liver / K_liver_cl # ???
384
+ ) / cnst_v['liver']
385
+
386
+ dC_gut_dt = cnst_q['gut'] * (C_A - C_gut / K_gut) / cnst_v['gut']
387
+ dC_spleen_dt = cnst_q['spleen'] * (C_A - C_spleen / K_spleen) / cnst_v['spleen']
388
+ dC_stomach_dt = cnst_q['stomach'] * (C_A - C_stomach / K_stomach) / cnst_v['stomach']
389
+ dC_pancreas_dt = cnst_q['pancreas'] * (C_A - C_pancreas / K_pancreas) / cnst_v['pancreas']
390
+
391
+ dC_venouse_dt = (
392
+ cnst_q['heart'] * C_heart / K_heart
393
+ + cnst_q['brain'] * C_brain / K_brain
394
+ + cnst_q['muscle'] * C_muscle / K_muscle
395
+ + cnst_q['skin'] * C_skin / K_skin
396
+ + cnst_q['adipose'] * C_fat / K_fat
397
+ + cnst_q['bone'] * C_bone / K_bone
398
+ + cnst_q['kidney'] * C_kidney / K_kidney
399
+ + cnst_q['liver'] * C_liver / K_liver
400
+ - cnst_q['lung'] * C_V
401
+ ) / cnst_v['venous_blood']
402
+
403
+ dC_arterial_dt = cnst_q['lung'] * (C_lung / K_lung - C_A) / cnst_v['arterial_blood']
404
+
405
+ y_new = np.array([dC_lung_dt, dC_heart_dt, dC_brain_dt, dC_muscle_dt, dC_fat_dt, dC_skin_dt, dC_bone_dt, \
406
+ dC_kidney_dt, dC_liver_dt, dC_gut_dt, dC_spleen_dt, dC_stomach_dt, dC_pancreas_dt, dC_venouse_dt,
407
+ dC_arterial_dt]).astype(np.float64)
408
+ return y_new
409
+
410
+ @staticmethod
411
+ @cfunc(lsoda_sig)
412
+ def lsoda_fullPBPK_for_optimization(t, y, y_new, data):
413
+
414
+ C_lung = y[0]
415
+ C_heart = y[1]
416
+ C_brain = y[2]
417
+ C_muscle = y[3]
418
+ C_fat = y[4]
419
+ C_skin = y[5]
420
+ C_bone = y[6]
421
+ C_kidney = y[7]
422
+ C_liver = y[8]
423
+ C_gut = y[9]
424
+ C_spleen = y[10]
425
+ C_stomach = y[11]
426
+ C_pancreas = y[12]
427
+ C_V = y[13]
428
+ C_A = y[14]
429
+
430
+ K_lung = data[0]
431
+ K_heart = data[1]
432
+ K_brain = data[2]
433
+ K_muscle = data[3]
434
+ K_fat = data[4]
435
+ K_skin = data[5]
436
+ K_bone = data[6]
437
+ K_kidney = data[7]
438
+ K_liver = data[8]
439
+ K_gut = data[9]
440
+ K_spleen = data[10]
441
+ K_stomach = data[11]
442
+ K_pancreas = data[12]
443
+ K_liver_cl = data[13]
444
+ K_kidney_cl = data[14]
445
+ CL_kidney = data[15]
446
+ CL_liver = data[16]
447
+
448
+ cnst_q_adipose = data[17]
449
+ cnst_q_bone = data[18]
450
+ cnst_q_brain = data[19]
451
+ cnst_q_gut = data[20]
452
+ cnst_q_heart = data[21]
453
+ cnst_q_kidney = data[22]
454
+ cnst_q_liver = data[23]
455
+ cnst_q_lung = data[24]
456
+ cnst_q_muscle = data[25]
457
+ cnst_q_pancreas = data[26]
458
+ cnst_q_skin = data[27]
459
+ cnst_q_spleen = data[28]
460
+ cnst_q_stomach = data[29]
461
+ cnst_q_teaster = data[30]
462
+
463
+ cnst_v_adipose = data[31]
464
+ cnst_v_bone = data[32]
465
+ cnst_v_brain = data[33]
466
+ cnst_v_gut = data[34]
467
+ cnst_v_heart = data[35]
468
+ cnst_v_kidney = data[36]
469
+ cnst_v_liver = data[37]
470
+ cnst_v_lung = data[38]
471
+ cnst_v_muscle = data[39]
472
+ cnst_v_pancreas = data[40]
473
+ cnst_v_skin = data[41]
474
+ cnst_v_spleen = data[42]
475
+ cnst_v_stomach = data[43]
476
+ cnst_v_teaster = data[44]
477
+ cnst_v_arterial_blood = data[45]
478
+ cnst_v_venous_blood = data[46]
479
+
480
+ dC_lung_dt = cnst_q_lung * (C_V - C_lung / K_lung) / cnst_v_lung
481
+ dC_heart_dt = cnst_q_heart * (C_A - C_heart / K_heart) / cnst_v_heart
482
+ dC_brain_dt = cnst_q_brain * (C_A - C_brain / K_brain) / cnst_v_brain
483
+ dC_muscle_dt = cnst_q_muscle * (C_A - C_muscle / K_muscle) / cnst_v_muscle
484
+ dC_fat_dt = cnst_q_adipose * (C_A - C_fat / K_fat) / cnst_v_adipose
485
+ dC_skin_dt = cnst_q_skin * (C_A - C_skin / K_skin) / cnst_v_skin
486
+ dC_bone_dt = cnst_q_bone * (C_A - C_bone / K_bone) / cnst_v_bone
487
+ # Kidney V(Kidney)*dC(Kidney)/dt = Q(Kidney)*C(A)-Q(Kidney)*CV(Kidney)-CL(Kidney,int)*CV(Kidney,int)?
488
+ dC_kidney_dt = (cnst_q_kidney * (C_A - C_kidney / K_kidney) - CL_kidney * C_kidney / K_kidney_cl) / \
489
+ cnst_v_kidney # ???
490
+
491
+ # Liver V(Liver)*dC(Liver)/dt = (Q(Liver)-Q(Spleen)-Q(Gut)-Q(Pancreas)-Q(Stomach))*C(A) + Q(Spleen)*CV(Spleen) +
492
+ # + Q(Gut)*CV(Gut) + Q(Pancreas)*CV(Pancreas) + Q(Stomach)*CV(Stomach) -
493
+ # - Q(Liver)*CV(Liver) - CL(Liver,int)*CV(Liver,int)? # тут скорее всего нужно вычитать потоки из друг друга дополнительно по крови что бы сохранить массовый баланс
494
+ Q_liver_in_from_art = cnst_q_liver - cnst_q_gut - cnst_q_spleen - \
495
+ cnst_q_pancreas - cnst_q_stomach
496
+ dC_liver_dt = (
497
+ Q_liver_in_from_art * C_A + cnst_q_gut * C_gut / K_gut
498
+ + cnst_q_spleen * C_spleen / K_spleen
499
+ + cnst_q_stomach * C_stomach / K_stomach
500
+ + cnst_q_pancreas * C_pancreas / K_pancreas
501
+ - cnst_q_liver * C_liver / K_liver
502
+ - CL_liver * C_liver / K_liver_cl # ???
503
+ ) / cnst_v_liver
504
+
505
+ dC_gut_dt = cnst_q_gut * (C_A - C_gut / K_gut) / cnst_v_gut
506
+ dC_spleen_dt = cnst_q_spleen * (C_A - C_spleen / K_spleen) / cnst_v_spleen
507
+ dC_stomach_dt = cnst_q_stomach * (C_A - C_stomach / K_stomach) / cnst_v_stomach
508
+ dC_pancreas_dt = cnst_q_pancreas * (C_A - C_pancreas / K_pancreas) / cnst_v_pancreas
509
+
510
+ dC_venouse_dt = (
511
+ cnst_q_heart * C_heart / K_heart
512
+ + cnst_q_brain * C_brain / K_brain
513
+ + cnst_q_muscle * C_muscle / K_muscle
514
+ + cnst_q_skin * C_skin / K_skin
515
+ + cnst_q_adipose * C_fat / K_fat
516
+ + cnst_q_bone * C_bone / K_bone
517
+ + cnst_q_kidney * C_kidney / K_kidney
518
+ + cnst_q_liver * C_liver / K_liver
519
+ - cnst_q_lung * C_V
520
+ ) / cnst_v_venous_blood
521
+
522
+ dC_arterial_dt = cnst_q_lung * (C_lung / K_lung - C_A) / cnst_v_arterial_blood
523
+ y_new[0] = dC_lung_dt
524
+ y_new[1] = dC_heart_dt
525
+ y_new[2] = dC_brain_dt
526
+ y_new[3] = dC_muscle_dt
527
+ y_new[4] = dC_fat_dt
528
+ y_new[5] = dC_skin_dt
529
+ y_new[6] = dC_bone_dt
530
+ y_new[7] = dC_kidney_dt
531
+ y_new[8] = dC_liver_dt
532
+ y_new[9] = dC_gut_dt
533
+ y_new[10] = dC_spleen_dt
534
+ y_new[11] = dC_stomach_dt
535
+ y_new[12] = dC_pancreas_dt
536
+ y_new[13] = dC_venouse_dt
537
+ y_new[14] = dC_arterial_dt
538
+
539
+ # y_new = [dC_lung_dt, dC_heart_dt, dC_brain_dt, dC_muscle_dt, dC_fat_dt, dC_skin_dt, dC_bone_dt, \
540
+ # dC_kidney_dt, dC_liver_dt, dC_gut_dt, dC_spleen_dt, dC_stomach_dt, dC_pancreas_dt, dC_venouse_dt,
541
+ # dC_arterial_dt]
542
+
543
+
544
+ class ReleasePBPKmod(PBPKmod):
545
+
546
+ @staticmethod
547
+ def ode_release(solver, t, y0, release_function, d, v, is_lsoda=False):
548
+ result = []
549
+ new_y0 = y0
550
+ old_release_correction = 0
551
+ for i in range(1, len(t)):
552
+ if is_lsoda:
553
+ res, _ = solver(new_y0, t[i - 1], t[i])
554
+ y = res.T
555
+ else:
556
+ res = solver(new_y0, t[i - 1], t[i])
557
+ y = res.y
558
+ release_correction = release_function(t[i], d)
559
+ plus_release = release_correction - old_release_correction
560
+ all_corrections = plus_release
561
+ y[-2][1] += all_corrections / v
562
+ old_release_correction = release_correction
563
+ if i == 1:
564
+ result.append([y[i][0] for i in range(y.shape[0])])
565
+ new_y0 = np.array([y[i][1] for i in range(y.shape[0])])
566
+ result.append(new_y0)
567
+ return np.array(result).T
568
+
569
+ def __init__(self, release_parameters: dict=None, release_function: callable=None, know_k=None, know_cl=None, numba_option=False, lsoda_option=False):
570
+ super().__init__(
571
+ know_k=know_k, know_cl=know_cl, numba_option=numba_option, lsoda_option=lsoda_option
572
+ )
573
+ self.release_function = release_function
574
+ if release_parameters is None:
575
+ self.release_parameters = {}
576
+ else:
577
+ self.release_parameters = release_parameters
578
+ self.know_release_parameters = set(self.release_parameters.keys())
579
+
580
+ @staticmethod
581
+ def _default_release_function(t, d, m, b, c):
582
+ """
583
+ Функция для поправки на высвобождение
584
+ """
585
+ return d * c * t ** b / (t ** b + m)
586
+
587
+ def get_release_function(self):
588
+ return lambda t, d: self._get_release_function()(t, d, **self.release_parameters)
589
+
590
+ def _get_release_function(self):
591
+ if self.release_function is not None:
592
+ return self.release_function
593
+ else:
594
+ return self._default_release_function
595
+
596
+ @property
597
+ def _release_parameters_list(self) -> list[str]:
598
+ method = self._get_release_function()
599
+ arguments = inspect.getfullargspec(method).args
600
+ return [arg for arg in arguments if arg not in {'t', 'd'}]
601
+
602
+ def get_unknown_params(self):
603
+ result = super().get_unknown_params()
604
+ arguments = self._release_parameters_list
605
+ for arg in arguments:
606
+ know_arg = self.release_parameters.get(arg)
607
+ if know_arg is None:
608
+ result.append(f"release_{arg}")
609
+ return result
610
+
611
+ def update_know_params(self, k_cl=None, release_parameters=None):
612
+ super().update_know_params(k_cl)
613
+ if release_parameters is not None:
614
+ self.release_parameters = release_parameters
615
+ self.know_release_parameters = set(self.release_parameters.keys())
616
+
617
+ def _get_sol_difurs(self):
618
+ return self(max(self.time_exp), self.d, self.animal)
619
+
620
+ def fitness(self, x):
621
+
622
+ n = len(self._organs) + len(self._cl_organs) - len(self.know_cl) - len(self.know_k)
623
+ self.k_cl = x[:n]
624
+ i = 0
625
+ for arg in self._release_parameters_list:
626
+ if not arg in self.know_release_parameters:
627
+ self.release_parameters[arg] = x[n + i]
628
+ i += 1
629
+ return super().fitness(self.k_cl)
630
+
631
+ def _get_result(self, fun, t, max_time, K_CL, animal):
632
+ if not self.lsoda_option:
633
+ solver = lambda y0, t_left, t_right: solve_ivp(
634
+ fun=fun,
635
+ t_span=[t_left, t_right],
636
+ y0=y0,
637
+ t_eval=np.array([t_left, t_right]),
638
+ method=LSODA
639
+ )
640
+ return self.ode_release(solver, t, self.y0, d=self.d, v=self.v, release_function=self.get_release_function())
641
+ else:
642
+ solver = lambda y0, t_left, t_right: lsoda(
643
+ funcptr=fun,
644
+ u0=np.array(y0, dtype=np.float64),
645
+ t_eval=np.array([t_left, t_right], dtype=np.float64),
646
+ data=np.array(K_CL, dtype=np.float64),
647
+ )
648
+ return self.ode_release(solver, t, self.y0, d=self.d, v=self.v,
649
+ release_function=self.get_release_function(), is_lsoda=True), True
650
+
651
+ def _prepare_result(self, t, res):
652
+ self._res = res
653
+ self.last_result = {
654
+ 't': t * 60
655
+ }
656
+ for organ in self._organs:
657
+ index = self._organs.index(organ)
658
+ self.last_result[organ] = res[index]
659
+
660
+ def __call__(self, max_time, d, animal=ANIMALS.MOUSE, step=1.0):
661
+ self.d = d
662
+ const = MODEL_CONST[animal]
663
+ self.v = const['venous_blood']['V']
664
+ return super().__call__(
665
+ max_time=max_time,
666
+ start_c_in_venous=0,
667
+ animal=animal,
668
+ step=step
669
+ )
670
+
671
+ def optimize(self, method=None, user_method=None, method_is_func=True,
672
+ optimization_func_name='__call__', **kwargs):
673
+
674
+ return super().optimize(
675
+ method=method, user_method=user_method, method_is_func=method_is_func,
676
+ optimization_func_name=optimization_func_name, **kwargs
677
+ )
678
+
679
+ def load_optimization_data(self, time_exp, dict_c_exp, d, animal=ANIMALS.MOUSE):
680
+ self.time_exp = time_exp
681
+ self.dict_c_exp = dict_c_exp
682
+ self.d = d
683
+ self.animal = animal