pypharm 1.3.6__py3-none-any.whl → 1.4.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.
@@ -0,0 +1,638 @@
1
+ from multiprocessing import shared_memory
2
+
3
+ import numpy as np
4
+ from scipy.integrate import solve_ivp, RK45
5
+ from scipy.integrate import simps
6
+ from scipy.optimize import minimize
7
+ from ..algorithms.country_optimization import CountriesAlgorithm
8
+ from ..algorithms.country_optimization_v2 import CountriesAlgorithm_v2
9
+ from ..algorithms.genetic_optimization import GeneticAlgorithm
10
+ from numba import njit
11
+ import matplotlib.pyplot as plt
12
+
13
+
14
+ class BaseCompartmentModel:
15
+
16
+ configuration_matrix_target = None
17
+ outputs_target = None
18
+ volumes_target = None
19
+ _optim = False
20
+ numba_option = False
21
+
22
+ def __init__(self, configuration_matrix, outputs, volumes=None, numba_option=False, use_shared_memory=False):
23
+ """
24
+ Базовая камерная модель для описания фармакокинетики системы
25
+
26
+ Неизвестные параметры при необходимости задаются как None
27
+ например configuration_matrix = [[0, 1], [None, 0]]
28
+
29
+ Args:
30
+ configuration_matrix: Настроечная матрица модели, отображающая константы перехода между матрицами
31
+ outputs: Вектор констант перехода во вне камер
32
+ volumes: Объемы камер
33
+ """
34
+ self.configuration_matrix = np.array(configuration_matrix)
35
+ self.configuration_matrix_target_count = 0
36
+ if np.any(self.configuration_matrix == None):
37
+ self.configuration_matrix_target = np.where(self.configuration_matrix == None)
38
+ self.configuration_matrix_target_count = np.sum(self.configuration_matrix == None)
39
+ self.outputs = np.array(outputs)
40
+ self.outputs_target_count = 0
41
+ if np.any(self.outputs == None):
42
+ self.outputs_target = np.where(self.outputs == None)
43
+ self.outputs_target_count = np.sum(self.outputs == None)
44
+ if not volumes:
45
+ self.volumes = np.ones(self.outputs.size)
46
+ else:
47
+ self.volumes = np.array(volumes)
48
+ self.volumes_target_count = 0
49
+ if np.any(self.volumes == None):
50
+ self.volumes_target = np.where(self.volumes == None)
51
+ self.volumes_target_count = np.sum(self.volumes == None)
52
+ self.last_result = None
53
+ self.numba_option = numba_option
54
+ self.use_shared_memory = use_shared_memory
55
+ if self.use_shared_memory:
56
+ self.memory_size = 2 + self.configuration_matrix_target_count + self.outputs_target_count + self.volumes_target_count
57
+ self.memory = shared_memory.ShareableList(self.memory_size * [None])
58
+ self.memory_name = self.memory.shm.name
59
+
60
+ def __del__(self):
61
+ if getattr(self, 'memory', None):
62
+ self.memory.shm.close()
63
+ self.memory.shm.unlink()
64
+
65
+ def _compartment_model(self, t, c):
66
+ """
67
+ Функция для расчета камерной модели
68
+
69
+ Args:
70
+ t: Текущее время
71
+ c: Вектор концентраций
72
+
73
+ Returns:
74
+ Вектор изменений концентраций (c) в момент времени (t)
75
+ """
76
+ dc_dt = (self.configuration_matrix.T @ (c * self.volumes) \
77
+ - self.configuration_matrix.sum(axis=1) * (c * self.volumes)\
78
+ - self.outputs * (c * self.volumes)) / self.volumes
79
+ return dc_dt
80
+
81
+ @staticmethod
82
+ @njit
83
+ def _numba_compartment_model(t, c, configuration_matrix, outputs, volumes):
84
+ """
85
+ Функция для расчета камерной модели
86
+
87
+ Args:
88
+ t: Текущее время
89
+ c: Вектор концентраций
90
+
91
+ Returns:
92
+ Вектор изменений концентраций (c) в момент времени (t)
93
+ """
94
+ dc_dt = (configuration_matrix.T @ (c * volumes) \
95
+ - configuration_matrix.sum(axis=1) * (c * volumes)\
96
+ - outputs * (c * volumes)) / volumes
97
+ return dc_dt
98
+
99
+ def __call__(self, t_max, c0=None, d=None, compartment_number=None, max_step=0.01, t_eval=None):
100
+ """
101
+ Расчет кривых концентраций по фармакокинетической модели
102
+
103
+ Args:
104
+ t_max: Предельное время расчета
105
+ c0: Вектор нулевых концентраций
106
+ d: Вводимая доза
107
+ compartment_number: Номер камеры в которую вводится доза
108
+ max_step: Максимальный шаг при решении СДУ
109
+ t_eval: Временные точки, в которых необходимо молучить решение
110
+
111
+ Returns:
112
+ Результат работы решателя scipy solve_ivp
113
+ """
114
+ if not self._optim:
115
+ assert (not any([self.configuration_matrix_target, self.outputs_target, self.volumes_target])), \
116
+ "It is impossible to make a calculation with unknown parameters"
117
+ assert any([c0 is not None, d, compartment_number is not None]), "Need to set c0 or d and compartment_number"
118
+ if c0 is None:
119
+ assert all([d, compartment_number is not None]), "Need to set d and compartment_number"
120
+ c0 = np.zeros(self.outputs.size)
121
+ c0[compartment_number] = d / self.volumes[compartment_number]
122
+ else:
123
+ c0 = np.array(c0)
124
+ ts = [0, t_max]
125
+ self.last_result = solve_ivp(
126
+ fun=self._compartment_model if not self.numba_option else lambda t, c: self._numba_compartment_model(t, c, self.configuration_matrix.astype(np.float64), self.outputs.astype(np.float64), self.volumes.astype(np.float64)),
127
+ t_span=ts,
128
+ y0=c0,
129
+ max_step=max_step,
130
+ t_eval=t_eval
131
+ )
132
+ return self.last_result
133
+
134
+ def get_kinetic_params(self, t_max, d, compartment_number, max_step=0.01):
135
+ one_hour_result = self(t_max=1, d=d, compartment_number=compartment_number, max_step=max_step)
136
+ auc_1h = simps(one_hour_result.y[compartment_number], one_hour_result.t)
137
+ self(t_max=t_max, d=d, compartment_number=compartment_number, max_step=max_step)
138
+ auc = simps(self.last_result.y[compartment_number], self.last_result.t)
139
+ result_dict = {
140
+ 'c_max': self.last_result.y[compartment_number].max(),
141
+ 'V': self.volumes[compartment_number] if self.volumes is not None else None,
142
+ 'AUC': auc,
143
+ 'AUC_1h': auc_1h,
144
+ 'Cl': d / auc
145
+ }
146
+ return result_dict
147
+
148
+
149
+ def load_data_from_list(self, x):
150
+ if self.configuration_matrix_target:
151
+ self.configuration_matrix[self.configuration_matrix_target] = x[:self.configuration_matrix_target_count]
152
+ if self.outputs_target:
153
+ self.outputs[self.outputs_target] = x[
154
+ self.configuration_matrix_target_count:self.configuration_matrix_target_count + self.outputs_target_count]
155
+ if self.volumes_target:
156
+ self.volumes[self.volumes_target] = x[self.configuration_matrix_target_count + self.outputs_target_count:self.configuration_matrix_target_count + self.outputs_target_count + self.volumes_target_count]
157
+
158
+ def _target_function(self, x, max_step=0.01, metric='R2'):
159
+ """
160
+ Функция расчета значения целевой функции
161
+
162
+ Args:
163
+ x: Значение искомых параметров модели
164
+ max_step: Максимальный шаг при решении СДУ
165
+
166
+ Returns:
167
+ Значение целевой функции, характеризующее отклонение от эксперементальных данных
168
+ """
169
+ self.load_data_from_list(x)
170
+ c0 = self.c0
171
+ if c0 is None:
172
+ c0 = np.zeros(self.outputs.size)
173
+ c0[self.compartment_number] = self.d / self.volumes[self.compartment_number]
174
+ self(
175
+ t_max=np.max(self.teoretic_x),
176
+ c0=c0,
177
+ t_eval=self.teoretic_x,
178
+ max_step=max_step
179
+ )
180
+ target_results = self.last_result.y[tuple(self.know_compartments), :]
181
+ if metric == 'R2':
182
+ return np.sum(np.sum(self.w * ((target_results - self.teoretic_y) ** 2), axis=1) / np.sum((self.teoretic_avg - self.teoretic_y) ** 2, axis=1))
183
+ elif metric == 'norm':
184
+ return np.linalg.norm(target_results - self.teoretic_y)
185
+ else:
186
+ return np.sum(np.sum(self.w * ((target_results - self.teoretic_y) ** 2), axis=1) / np.sum((self.teoretic_avg - self.teoretic_y) ** 2, axis=1))
187
+
188
+
189
+ def load_optimization_data(self, teoretic_x, teoretic_y, know_compartments, w = None, c0=None, d=None, compartment_number=None):
190
+ """
191
+ Функция загрузки в модель эксперементальных данных
192
+
193
+ Args:
194
+ teoretic_x: Вектор временных точек теоретических значений
195
+ teoretic_y: Матрица с теоретическими значениями
196
+ know_compartments: Вектор с номерами камер, по которым есть данные
197
+ c0: Вектор нулевых концентраций
198
+ d: Вводимая доза
199
+ compartment_number: Номер камеры в которую вводится доза
200
+
201
+ Returns:
202
+ None
203
+ """
204
+ self.teoretic_x = np.array(teoretic_x)
205
+ self.teoretic_y = np.array(teoretic_y)
206
+ self.know_compartments = know_compartments
207
+ self.teoretic_avg = np.average(self.teoretic_y, axis=1)
208
+ self.teoretic_avg = np.repeat(self.teoretic_avg, self.teoretic_x.size)
209
+ self.teoretic_avg = np.reshape(self.teoretic_avg, self.teoretic_y.shape)
210
+ assert any([c0, d, compartment_number is not None]), "Need to set c0 or d and compartment_number"
211
+ if not c0:
212
+ assert all([d, compartment_number is not None]), "Need to set d and compartment_number"
213
+ self.d = d
214
+ self.compartment_number = compartment_number
215
+ self.c0 = None
216
+ else:
217
+ self.c0 = np.array(c0)
218
+ self.w = np.ones(self.teoretic_y.shape) if w is None else np.array(w)
219
+
220
+ def optimize(self, method=None, user_method=None, method_is_func=True,
221
+ optimization_func_name='__call__', max_step=0.01, metric='R2', **kwargs):
222
+ """
223
+ Функция оптимизации модели
224
+
225
+ Args:
226
+ method: Метод оптимизации, любой доступный minimize + 'country_optimization' и 'country_optimization_v2'
227
+ max_step: Максимальный шаг при решении СДУ
228
+ **kwargs: Дополнительные именованные аргументы
229
+
230
+ Returns:
231
+ None
232
+ """
233
+ self._optim = True
234
+ f = lambda x: self._target_function(x, max_step=max_step, metric=metric)
235
+ if user_method is not None:
236
+ if method_is_func:
237
+ x = user_method(f, **kwargs)
238
+ else:
239
+ optimization_obj = user_method(f, **kwargs)
240
+ x = getattr(optimization_obj, optimization_func_name)()
241
+ else:
242
+ if method == 'country_optimization':
243
+ CA = CountriesAlgorithm(
244
+ f=f,
245
+ memory_list=getattr(self, 'memory', None),
246
+ **kwargs
247
+ )
248
+ CA.start()
249
+ x = CA.countries[0].population[0].x
250
+ elif method == 'country_optimization_v2':
251
+ CA = CountriesAlgorithm_v2(
252
+ f=f,
253
+ **kwargs
254
+ )
255
+ CA.start()
256
+ x = CA.countries[0].population[0].x
257
+ elif method == 'GA':
258
+ CA = GeneticAlgorithm(
259
+ f=f,
260
+ **kwargs
261
+ )
262
+ x = CA.start()
263
+ else:
264
+ res = minimize(
265
+ fun=f,
266
+ method=method,
267
+ **kwargs
268
+ )
269
+ x = res.x
270
+ if self.configuration_matrix_target:
271
+ self.configuration_matrix[self.configuration_matrix_target] = x[:self.configuration_matrix_target_count]
272
+ if self.outputs_target:
273
+ self.outputs[self.outputs_target] = x[
274
+ self.configuration_matrix_target_count:self.configuration_matrix_target_count + self.outputs_target_count]
275
+ if self.volumes_target:
276
+ self.volumes[self.volumes_target] = x[self.configuration_matrix_target_count + self.outputs_target_count:self.configuration_matrix_target_count + self.outputs_target_count + self.volumes_target_count]
277
+ self.configuration_matrix_target = None
278
+ self.outputs_target = None
279
+ self.volumes_target = None
280
+ self._optim = False
281
+ return x
282
+
283
+ def plot_model(self, compartment_numbers=None, compartment_names={}, left=None, right=None, y_lims={}, **kwargs):
284
+ """
285
+ Функция для построения графиков модели
286
+
287
+ Args:
288
+ compartment_numbers: Камеры, которые нужно отобразить (если не указать, отобразим все)
289
+ compartment_names: Имена камер
290
+ """
291
+ if compartment_numbers:
292
+ compartment_numbers = np.array(compartment_numbers)
293
+ else:
294
+ compartment_numbers = np.arange(self.outputs.size)
295
+ self(**kwargs)
296
+ for i in compartment_numbers:
297
+ if hasattr(self, "teoretic_x") and hasattr(self, "teoretic_y") and i in self.know_compartments:
298
+ plt.plot(self.teoretic_x, self.teoretic_y[self.know_compartments.index(i)], "*r")
299
+ plt.plot(self.last_result.t, self.last_result.y[i])
300
+ plt.title(compartment_names.get(i, i))
301
+ plt.xlim(left=left, right=right)
302
+ if y_lims.get(i):
303
+ plt.ylim(y_lims.get(i))
304
+ plt.grid()
305
+ plt.show()
306
+
307
+
308
+ class MagicCompartmentModel(BaseCompartmentModel):
309
+
310
+ need_magic_optimization = False
311
+
312
+ def __init__(self, configuration_matrix, outputs, volumes=None, magic_coefficient=1, exclude_compartments=[], numba_option=False, use_shared_memory=False):
313
+ super().__init__(configuration_matrix, outputs, volumes, numba_option, use_shared_memory)
314
+ self.magic_coefficient = magic_coefficient
315
+ self.exclude_compartments = np.array(exclude_compartments)
316
+ self.need_magic_optimization = self.magic_coefficient is None
317
+ if getattr(self, "memory", None):
318
+ self.memory.shm.close()
319
+ self.memory.shm.unlink()
320
+ self.memory_size += int(self.need_magic_optimization)
321
+ self.memory = shared_memory.ShareableList(
322
+ sequence=self.memory_size * [None]
323
+ )
324
+ self.memory_name = self.memory.shm.name
325
+
326
+ def __call__(self, t_max, c0=None, d=None, compartment_number=None, max_step=0.01, t_eval=None):
327
+ if not self._optim and not self.magic_coefficient:
328
+ raise Exception("Magic_coefficient parameter not specified")
329
+ res = super().__call__(t_max, c0, d, compartment_number, max_step, t_eval)
330
+ magic_arr = np.ones(self.configuration_matrix.shape[0]) * self.magic_coefficient
331
+ if self.exclude_compartments:
332
+ magic_arr[self.exclude_compartments] = 1
333
+ magic_arr = np.repeat(magic_arr, res.y.shape[1])
334
+ magic_arr = np.reshape(magic_arr, res.y.shape)
335
+ res.y = magic_arr * res.y
336
+ self.last_result = res
337
+ return res
338
+
339
+ def load_data_from_list(self, x):
340
+ super().load_data_from_list(x)
341
+ if self.need_magic_optimization:
342
+ self.magic_coefficient = x[-1]
343
+
344
+ def optimize(self, method=None, user_method=None, method_is_func=True,
345
+ optimization_func_name='__call__', max_step=0.01, **kwargs):
346
+ x = super().optimize(method, user_method, method_is_func, optimization_func_name, max_step, **kwargs)
347
+ if self.need_magic_optimization:
348
+ self.magic_coefficient = x[-1]
349
+ self.need_magic_optimization = False
350
+ return x
351
+
352
+
353
+ class ReleaseCompartmentModel(BaseCompartmentModel):
354
+
355
+ need_v_release_optimization = False
356
+ release_parameters_target = None
357
+ accumulation_parameters_target = None
358
+
359
+ class ReleaseRK45(RK45):
360
+
361
+ def __init__(self, fun, t0, y0, t_bound, release_function, compartment_number, c0, max_step=np.inf,
362
+ with_accumulate=False, accumulation_function=None, accumulation_type=1, rtol=1e-3, atol=1e-6, vectorized=False,
363
+ first_step=None, **extraneous):
364
+ super().__init__(fun, t0, y0, t_bound, max_step=max_step,
365
+ rtol=rtol, atol=atol, vectorized=vectorized,
366
+ first_step=first_step, **extraneous)
367
+ self.release_function = release_function
368
+ self.compartment_number = compartment_number
369
+ self.c0 = c0
370
+ self.old_release_correction = 0
371
+ self.old_accumulation_correction = c0
372
+ self.with_accumulate = with_accumulate
373
+ self.accumulation_function = accumulation_function
374
+ self.accumulation_type = accumulation_type
375
+
376
+ def _step_impl(self):
377
+ result = super()._step_impl()
378
+ release_correction = self.release_function(self.t, self.c0)
379
+ minus_accumulation = 0
380
+ if self.with_accumulate:
381
+ if self.accumulation_type == 1:
382
+ accumulation_correction = self.accumulation_function(self.t, self.c0 )
383
+ minus_accumulation = self.old_accumulation_correction - accumulation_correction
384
+ elif self.accumulation_type == 2:
385
+ coef_accumulation = self.accumulation_function(self.t, self.c0) / self.c0
386
+ plus_release = release_correction - self.old_release_correction
387
+ all_corrections = plus_release
388
+ if self.accumulation_type == 1:
389
+ if plus_release - minus_accumulation > 0:
390
+ all_corrections = plus_release - minus_accumulation
391
+ elif self.accumulation_type == 2:
392
+ all_corrections *= coef_accumulation
393
+ self.y[self.compartment_number] += all_corrections
394
+ self.old_release_correction = release_correction
395
+ if self.with_accumulate and self.accumulation_type == 1:
396
+ self.old_accumulation_correction = accumulation_correction
397
+ return result
398
+
399
+ def __init__(self, v_release, release_parameters, release_compartment,
400
+ release_function=None, with_accumulate=False, accumulation_function=None,
401
+ accumulation_parameters=[None], accumulation_type=1, *args, **kwargs):
402
+ """
403
+ Камерная модель с высвобождением для описания фармакокинетики системы
404
+
405
+ Неизвестные параметры при необходимости задаются как None
406
+ например configuration_matrix = [[0, 1], [None, 0]]
407
+
408
+ Args:
409
+ configuration_matrix: Настроечная матрица модели, отображающая константы перехода между матрицами
410
+ outputs: Вектор констант перехода во вне камер
411
+ volumes: Объемы камер
412
+ v_release: Объем гепотетической камеры из которой происходит высвобождение
413
+ release_parameters: Параметры функции высвобождения
414
+ release_compartment: Номер камеры в которую происходит высвобождение
415
+ release_function: Функция высвобождения по умолчанию f(t,m,b,c) = c0 * c * t ** b / (t ** b + m)
416
+ """
417
+ super().__init__(*args, **kwargs)
418
+ self.release_parameters = np.array(release_parameters)
419
+ self.release_parameters_target_count = 0
420
+ if np.any(self.release_parameters == None):
421
+ self.release_parameters_target = np.where(self.release_parameters == None)
422
+ self.release_parameters_target_count = np.sum(self.release_parameters == None)
423
+ self.v_release = v_release
424
+ if self.v_release is None:
425
+ self.need_v_release_optimization = True
426
+ self.release_compartment = release_compartment
427
+ self.release_function = release_function
428
+ self.with_accumulate = with_accumulate
429
+ self.accumulation_type = accumulation_type
430
+
431
+ if self.with_accumulate:
432
+ self.accumulation_function = accumulation_function
433
+ self.accumulation_parameters = np.array(accumulation_parameters)
434
+ if np.any(self.accumulation_parameters == None):
435
+ self.accumulation_parameters_target = np.where(self.accumulation_parameters == None)
436
+ self.accumulation_parameters_target_count = np.sum(self.accumulation_parameters == None)
437
+
438
+ if getattr(self, "memory", None):
439
+ self.memory.shm.close()
440
+ self.memory.shm.unlink()
441
+ self.memory_size += self.release_parameters_target_count + int(self.need_v_release_optimization)
442
+ self.memory = shared_memory.ShareableList(
443
+ sequence=self.memory_size * [None]
444
+ )
445
+ self.memory_name = self.memory.shm.name
446
+
447
+ def load_data_from_list(self, x):
448
+ super().load_data_from_list(x)
449
+ s = self.configuration_matrix_target_count + self.outputs_target_count + self.volumes_target_count
450
+ if self.release_parameters_target:
451
+ self.release_parameters[self.release_parameters_target] = x[s:s + self.release_parameters_target_count]
452
+ if self.need_v_release_optimization:
453
+ self.v_release = x[s + self.release_parameters_target_count]
454
+ if self.with_accumulate:
455
+ if self.accumulation_parameters_target:
456
+ s += self.release_parameters_target_count + int(self.need_v_release_optimization)
457
+ self.accumulation_parameters[self.accumulation_parameters_target] = x[s:s + self.accumulation_parameters_target_count]
458
+
459
+ def _default_release_function(self, t, c0):
460
+ """
461
+ Функция для поправки на высвобождение
462
+ """
463
+ m, b, c = self.release_parameters
464
+ return c0 * c * t ** b / (t ** b + m)
465
+
466
+ def get_release_function(self):
467
+ if self.release_function is not None:
468
+ return lambda t, c0: self.release_function(t, c0, *self.release_parameters)
469
+ else:
470
+ return self._default_release_function
471
+
472
+ def _default_accumulation_function(self, t, c0):
473
+ """
474
+ Функция для поправки на накопление
475
+ """
476
+ k, = self.accumulation_parameters
477
+ return c0 * np.exp(-k * t)
478
+
479
+ def get_accumulation_function(self):
480
+ if self.accumulation_function is not None:
481
+ return lambda t, c0: self.accumulation_function(t, c0, *self.accumulation_parameters)
482
+ else:
483
+ return self._default_accumulation_function
484
+
485
+ def __call__(self, t_max, c0=None, d=None, max_step=0.01, t_eval=None, **kwargs):
486
+ """
487
+ Расчет кривых концентраций по фармакокинетической модели
488
+
489
+ Args:
490
+ t_max: Предельное время расчета
491
+ c0: Начальная концентрация в камере из которой высвобождается вещество
492
+ d: Вводимая доза
493
+ max_step: Максимальный шаг при решении СДУ
494
+ t_eval: Временные точки, в которых необходимо молучить решение
495
+
496
+ Returns:
497
+ Результат работы решателя scipy solve_ivp
498
+ """
499
+ if not self._optim:
500
+ assert (not any([self.configuration_matrix_target, self.outputs_target, self.volumes_target])), \
501
+ "It is impossible to make a calculation with unknown parameters"
502
+ assert any([c0 is not None, d]), "Need to set c0 or d and compartment_number"
503
+ if c0 is None:
504
+ assert d, "Need to set d"
505
+ c0 = d / self.v_release
506
+ ts = [0, t_max]
507
+ y0 = np.zeros(self.outputs.shape)
508
+ self.last_result = solve_ivp(
509
+ fun=self._compartment_model if
510
+ not self.numba_option
511
+ else lambda t, c: self._numba_compartment_model(t, c,
512
+ self.configuration_matrix.astype(
513
+ np.float64),
514
+ self.outputs.astype(
515
+ np.float64),
516
+ self.volumes.astype(
517
+ np.float64)),
518
+ t_span=ts,
519
+ y0=y0,
520
+ max_step=max_step,
521
+ t_eval=t_eval,
522
+ method=self.ReleaseRK45,
523
+ release_function=self.get_release_function(),
524
+ compartment_number=self.release_compartment,
525
+ with_accumulate=self.with_accumulate,
526
+ accumulation_function=self.get_accumulation_function() if self.with_accumulate else None,
527
+ accumulation_type=self.accumulation_type,
528
+ c0=c0
529
+ )
530
+ self.last_result.model_realized = c0 - self.get_release_function()(self.last_result.t, c0)
531
+ if self.with_accumulate:
532
+ model_accumulation = self.get_accumulation_function()(self.last_result.t, c0)
533
+ if self.accumulation_type == 1:
534
+ self.last_result.model_realized = model_accumulation - self.get_release_function()(self.last_result.t, c0)
535
+ elif self.accumulation_type == 2:
536
+ accumulation_coeffs = model_accumulation / c0
537
+ self.last_result.model_realized= accumulation_coeffs * self.get_release_function()(self.last_result.t, c0)
538
+ self.last_result.model_realized = model_accumulation - self.last_result.model_realized
539
+ return self.last_result
540
+
541
+ def _target_function(self, x, max_step=0.01, metric='R2'):
542
+ """
543
+ Функция расчета значения целевой функции
544
+
545
+ Args:
546
+ x: Значение искомых параметров модели
547
+ max_step: Максимальный шаг при решении СДУ
548
+
549
+ Returns:
550
+ Значение целевой функции, характеризующее отклонение от эксперементальных данных
551
+ """
552
+ self.load_data_from_list(x)
553
+ c0 = self.c0
554
+ if c0 is None:
555
+ c0 = self.d / self.v_release
556
+ self(
557
+ t_max=np.max(self.teoretic_x),
558
+ c0=c0,
559
+ t_eval=self.teoretic_x,
560
+ max_step=max_step
561
+ )
562
+ target_results = self.last_result.y[tuple(self.know_compartments), :]
563
+ if metric == 'R2':
564
+ plus = 0
565
+ if self.teoretic_realized is not None:
566
+ model_realized = self.last_result.model_realized
567
+ plus = np.sum(((model_realized - self.teoretic_realized) ** 2) / ((self.teoretic_realized - self.teoretic_realized_avg) ** 2))
568
+ return plus + np.sum(np.sum(self.w * ((target_results - self.teoretic_y) ** 2), axis=1) / np.sum((self.teoretic_avg - self.teoretic_y) ** 2, axis=1))
569
+ elif metric == 'norm':
570
+ plus = 0
571
+ if self.teoretic_realized is not None:
572
+ model_realized = self.last_result.model_realized
573
+ plus = np.linalg.norm(self.teoretic_realized - model_realized)
574
+ return plus + np.linalg.norm(target_results - self.teoretic_y)
575
+ else:
576
+ return np.sum(np.sum(self.w * ((target_results - self.teoretic_y) ** 2), axis=1) / np.sum((self.teoretic_avg - self.teoretic_y) ** 2, axis=1))
577
+
578
+ def load_optimization_data(self, teoretic_x, teoretic_y, know_compartments,
579
+ w = None, c0=None, d=None, compartment_number=None, teoretic_realized=None):
580
+ """
581
+ Функция загрузки в модель эксперементальных данных
582
+
583
+ Args:
584
+ teoretic_x: Вектор временных точек теоретических значений
585
+ teoretic_y: Матрица с теоретическими значениями
586
+ know_compartments: Вектор с номерами камер, по которым есть данные
587
+ c0: Начальная концентрация в камере из которой высвобождается вещество
588
+ d: Вводимая доза
589
+
590
+ Returns:
591
+ None
592
+ """
593
+ self.teoretic_x = np.array(teoretic_x)
594
+ self.teoretic_y = np.array(teoretic_y)
595
+ self.teoretic_realized = np.array(teoretic_realized) if teoretic_realized is not None else teoretic_realized
596
+ if teoretic_realized is not None:
597
+ self.teoretic_realized_avg = np.average(self.teoretic_realized)
598
+ self.know_compartments = know_compartments
599
+ self.teoretic_avg = np.average(self.teoretic_y, axis=1)
600
+ self.teoretic_avg = np.repeat(self.teoretic_avg, self.teoretic_x.size)
601
+ self.teoretic_avg = np.reshape(self.teoretic_avg, self.teoretic_y.shape)
602
+ assert any([c0, d, compartment_number is not None]), "Need to set c0 or d and compartment_number"
603
+ if not c0:
604
+ assert all([d, compartment_number is not None]), "Need to set d and compartment_number"
605
+ self.d = d
606
+ self.c0 = None
607
+ else:
608
+ self.c0 = np.array(c0)
609
+ self.w = np.ones(self.teoretic_y.shape) if w is None else np.array(w)
610
+
611
+ def optimize(self, method=None, user_method=None, method_is_func=True,
612
+ optimization_func_name='__call__', max_step=0.01, **kwargs):
613
+ x = super().optimize(method, user_method, method_is_func, optimization_func_name, max_step, **kwargs)
614
+ s = self.configuration_matrix_target_count + self.outputs_target_count + self.volumes_target_count
615
+ if self.release_parameters_target:
616
+ self.release_parameters[self.release_parameters_target] = x[s:s + self.release_parameters_target_count]
617
+ if self.need_v_release_optimization:
618
+ self.v_release = x[s:s + self.release_parameters_target_count + 1]
619
+ self.need_v_release_optimization = False
620
+ if self.with_accumulate:
621
+ if self.accumulation_parameters_target:
622
+ s += self.release_parameters_target_count + int(self.need_v_release_optimization)
623
+ self.accumulation_parameters[self.accumulation_parameters_target] = x[s:s + self.accumulation_parameters_target_count]
624
+ return x
625
+
626
+ def plot_model(self, compartment_numbers=None, compartment_names={},
627
+ left=None, right=None, y_lims={}, plot_accumulation=False, **kwargs):
628
+ super().plot_model(compartment_numbers, compartment_names, left, right, y_lims, **kwargs)
629
+ if plot_accumulation:
630
+ if hasattr(self, "teoretic_x") and hasattr(self, "teoretic_realized"):
631
+ plt.plot(self.teoretic_x, self.teoretic_realized, "*r")
632
+ plt.plot(self.last_result.t, self.last_result.model_realized)
633
+ plt.title(compartment_names.get('realized', 'realized'))
634
+ plt.xlim(left=left, right=right)
635
+ if y_lims.get('realized'):
636
+ plt.ylim(y_lims.get('realized'))
637
+ plt.grid()
638
+ plt.show()