pypharm 1.5.1__tar.gz → 1.6.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pypharm
3
- Version: 1.5.1
3
+ Version: 1.6.0
4
4
  Summary: Module for solving pharmacokinetic problems
5
5
  Home-page: https://github.com/Krash13/PyPharm
6
6
  Author: Krash13
@@ -10,6 +10,8 @@ from PyPharm.algorithms.genetic_optimization import GeneticAlgorithm
10
10
  from numba import njit
11
11
  import matplotlib.pyplot as plt
12
12
 
13
+ BIG_VALUE = 2 ** 24
14
+
13
15
 
14
16
  class BaseCompartmentModel:
15
17
 
@@ -281,7 +283,7 @@ class BaseCompartmentModel:
281
283
  self._optim = False
282
284
  return x
283
285
 
284
- def plot_model(self, compartment_numbers=None, compartment_names={}, left=None, right=None, y_lims={}, **kwargs):
286
+ def plot_model(self, compartment_numbers=None, compartment_names=None, left=None, right=None, y_lims={}, **kwargs):
285
287
  """
286
288
  Функция для построения графиков модели
287
289
 
@@ -293,6 +295,10 @@ class BaseCompartmentModel:
293
295
  compartment_numbers = np.array(compartment_numbers)
294
296
  else:
295
297
  compartment_numbers = np.arange(self.outputs.size)
298
+ if not compartment_names:
299
+ compartment_names = {}
300
+ if not y_lims:
301
+ y_lims = {}
296
302
  self(**kwargs)
297
303
  for i in compartment_numbers:
298
304
  if hasattr(self, "teoretic_x") and hasattr(self, "teoretic_y") and i in self.know_compartments:
@@ -303,17 +309,22 @@ class BaseCompartmentModel:
303
309
  if y_lims.get(i):
304
310
  plt.ylim(y_lims.get(i))
305
311
  plt.grid()
306
- plt.show()
312
+ try:
313
+ plt.show()
314
+ except AttributeError:
315
+ plt.savefig(f'{compartment_names.get(i, str(i))}.png')
316
+ plt.cla()
317
+
307
318
 
308
319
 
309
320
  class MagicCompartmentModel(BaseCompartmentModel):
310
321
 
311
322
  need_magic_optimization = False
312
323
 
313
- def __init__(self, configuration_matrix, outputs, volumes=None, magic_coefficient=1, exclude_compartments=[], numba_option=False, use_shared_memory=False):
324
+ def __init__(self, configuration_matrix, outputs, volumes=None, magic_coefficient=1, exclude_compartments=None, numba_option=False, use_shared_memory=False):
314
325
  super().__init__(configuration_matrix, outputs, volumes, numba_option, use_shared_memory)
315
326
  self.magic_coefficient = magic_coefficient
316
- self.exclude_compartments = np.array(exclude_compartments)
327
+ self.exclude_compartments = np.array(exclude_compartments) if bool(exclude_compartments) else np.array([])
317
328
  self.need_magic_optimization = self.magic_coefficient is None
318
329
  if getattr(self, "memory", None):
319
330
  self.memory.shm.close()
@@ -637,3 +648,332 @@ class ReleaseCompartmentModel(BaseCompartmentModel):
637
648
  plt.ylim(y_lims.get('realized'))
638
649
  plt.grid()
639
650
  plt.show()
651
+
652
+
653
+ class TwoSubstancesCompartmentModel(MagicCompartmentModel):
654
+
655
+ released_configuration_matrix_target = None
656
+ released_outputs_target = None
657
+ release_parameters_target = None
658
+ has_teoretic_y = False
659
+ has_teoretic_released = False
660
+
661
+ class TwoSubstancesRK45(RK45):
662
+
663
+ def __init__(self, fun, t0, y0, t_bound, release_function, max_step=np.inf,
664
+ rtol=1e-3, atol=1e-6, vectorized=False,
665
+ first_step=None, **extraneous):
666
+ self.old_corrections = 0
667
+ super().__init__(fun, t0, y0, t_bound, max_step=max_step,
668
+ rtol=rtol, atol=atol, vectorized=vectorized,
669
+ first_step=first_step, **extraneous)
670
+ self.release_function = release_function
671
+
672
+ def _step_impl(self):
673
+ result = super()._step_impl()
674
+ release_correction: float = self.release_function(self.y, self.t)
675
+ correction = release_correction - self.old_corrections
676
+ c = self.y[:self.y.size // 2]
677
+ real_release_correction = np.append(-1 * correction * c, correction * c)
678
+ self.y += real_release_correction
679
+ self.old_corrections = release_correction
680
+ return result
681
+
682
+ def __init__(self, configuration_matrix, outputs, released_configuration_matrix, released_outputs,
683
+ release_parameters, release_function=None,
684
+ volumes=None, magic_coefficient=1, exclude_compartments=None,
685
+ numba_option=False, use_shared_memory=False):
686
+ super().__init__(
687
+ configuration_matrix=configuration_matrix,
688
+ outputs=outputs,
689
+ volumes=volumes,
690
+ magic_coefficient=magic_coefficient,
691
+ exclude_compartments=exclude_compartments,
692
+ numba_option=numba_option,
693
+ use_shared_memory=use_shared_memory
694
+ )
695
+ self.released_configuration_matrix = np.array(released_configuration_matrix)
696
+ self.released_configuration_matrix_target_count = 0
697
+ if np.any(self.released_configuration_matrix == None):
698
+ self.released_configuration_matrix_target = np.where(self.released_configuration_matrix == None)
699
+ self.released_configuration_matrix_target_count = np.sum(self.released_configuration_matrix == None)
700
+ self.released_outputs = np.array(released_outputs)
701
+ self.released_outputs_target_count = 0
702
+ if np.any(self.released_outputs == None):
703
+ self.released_outputs_target = np.where(self.released_outputs == None)
704
+ self.released_outputs_target_count = np.sum(self.released_outputs == None)
705
+ self.release_parameters = np.array(release_parameters)
706
+ if np.any(self.release_parameters == None):
707
+ self.release_parameters_target = np.where(self.release_parameters == None)
708
+ self.release_parameters_target_count = np.sum(self.release_parameters == None)
709
+ self.release_function = release_function
710
+ if getattr(self, "memory", None):
711
+ self.memory.shm.close()
712
+ self.memory.shm.unlink()
713
+ self.memory_size += (self.release_parameters_target_count +
714
+ self.released_configuration_matrix_target_count + self.released_outputs_target_count)
715
+ self.memory = shared_memory.ShareableList(
716
+ sequence=self.memory_size * [None]
717
+ )
718
+ self.memory_name = self.memory.shm.name
719
+
720
+ def __call__(self, t_max, c0=None, d=None, compartment_number=None, max_step=0.01, t_eval=None, **kwargs):
721
+ """
722
+ Расчет кривых концентраций по фармакокинетической модели
723
+
724
+ Args:
725
+ t_max: Предельное время расчета
726
+ c0: Начальная концентрация в камере из которой высвобождается вещество
727
+ d: Вводимая доза
728
+ max_step: Максимальный шаг при решении СДУ
729
+ t_eval: Временные точки, в которых необходимо молучить решение
730
+
731
+ Returns:
732
+ Результат работы решателя scipy solve_ivp
733
+ """
734
+ if not self._optim:
735
+ assert (not any([self.configuration_matrix_target, self.outputs_target, self.volumes_target])), \
736
+ "It is impossible to make a calculation with unknown parameters"
737
+ assert any([c0 is not None, d]), "Need to set c0 or d and compartment_number"
738
+
739
+
740
+ if c0 is None:
741
+ assert all([d, compartment_number is not None]), "Need to set d and compartment_number"
742
+ c0 = np.zeros((2, self.outputs.size))
743
+ c0[0][compartment_number] = d / self.volumes[compartment_number]
744
+ else:
745
+ c0 = np.array(c0)
746
+ c0 = c0.reshape((1, c0.size))[0]
747
+ ts = [0, t_max]
748
+ self.last_result = solve_ivp(
749
+ fun=self._compartment_model if
750
+ not self.numba_option
751
+ else lambda t, c: self._numba_compartment_model(t, c,
752
+ self.configuration_matrix.astype(
753
+ np.float64),
754
+ self.released_configuration_matrix.astype(
755
+ np.float64),
756
+ self.outputs.astype(
757
+ np.float64),
758
+ self.released_outputs.astype(
759
+ np.float64),
760
+ self.volumes.astype(
761
+ np.float64)),
762
+ t_span=ts,
763
+ y0=c0,
764
+ max_step=max_step,
765
+ t_eval=t_eval,
766
+ method=self.TwoSubstancesRK45,
767
+ release_function=self.get_release_function()
768
+ )
769
+ return self.last_result
770
+
771
+ def _default_release_function(self, current_c, t):
772
+ """
773
+ Функция для поправки на высвобождение
774
+ """
775
+ # c_resh = c.reshape((2, c.size // 2))
776
+ # k = self.release_parameters[0]
777
+ # return k * c_resh[0]
778
+ m, b, c = self.release_parameters
779
+ return c * t ** b / (t ** b + m)
780
+
781
+ def get_release_function(self):
782
+ if self.release_function is not None:
783
+ return lambda c, t: self.release_function(c, t, *self.release_parameters)
784
+ else:
785
+ return self._default_release_function
786
+
787
+ def _compartment_model(self, t, c):
788
+ """
789
+ Функция для расчета камерной модели
790
+
791
+ Args:
792
+ t: Текущее время
793
+ c: Вектор концентраций
794
+
795
+ Returns:
796
+ Вектор изменений концентраций (c) в момент времени (t)
797
+ """
798
+ c_resh = c.reshape((2, c.size // 2))
799
+ c_primary = c_resh[0]
800
+ dc_dt = (self.configuration_matrix.T @ (c_primary * self.volumes) \
801
+ - self.configuration_matrix.sum(axis=1) * (c_primary * self.volumes)\
802
+ - self.outputs * (c_primary * self.volumes)) / self.volumes
803
+ c_released = c_resh[1]
804
+ dc_dt_released = (self.released_configuration_matrix.T @ (c_released * self.volumes) \
805
+ - self.released_configuration_matrix.sum(axis=1) * (c_released * self.volumes) \
806
+ - self.released_outputs * (c_released * self.volumes)) / self.volumes
807
+ np.append(dc_dt, dc_dt_released)
808
+
809
+ @staticmethod
810
+ @njit
811
+ def _numba_compartment_model(t, c, configuration_matrix, released_configuration_matrix, outputs, released_outputs, volumes):
812
+ """
813
+ Функция для расчета камерной модели
814
+
815
+ Args:
816
+ t: Текущее время
817
+ c: Вектор концентраций
818
+
819
+ Returns:
820
+ Вектор изменений концентраций (c) в момент времени (t)
821
+ """
822
+ c_resh = c.reshape((2, c.size // 2))
823
+ c_primary = c_resh[0]
824
+ dc_dt = (configuration_matrix.T @ (c_primary * volumes) \
825
+ - configuration_matrix.sum(axis=1) * (c_primary * volumes) \
826
+ - outputs * (c_primary * volumes)) / volumes
827
+ c_released = c_resh[1]
828
+ dc_dt_released = (released_configuration_matrix.T @ (c_released * volumes) \
829
+ - released_configuration_matrix.sum(axis=1) * (c_released * volumes) \
830
+ - released_outputs * (c_released * volumes)) / volumes
831
+ return np.append(dc_dt, dc_dt_released)
832
+
833
+ def load_optimization_data(self, teoretic_x, teoretic_y=None, teoretic_released=None, know_compartments=None, know_released_compartments=None,
834
+ w=None, released_w=None, c0=None, d=None, compartment_number=None):
835
+ self.has_teoretic_y = bool(teoretic_y)
836
+ self.has_teoretic_released = bool(teoretic_released)
837
+ self.teoretic_x = np.array(teoretic_x)
838
+ if self.has_teoretic_y:
839
+ self.teoretic_y = np.array(teoretic_y if teoretic_y is not None else [[]])
840
+ self.know_compartments = know_compartments if know_compartments is not None else []
841
+ self.teoretic_avg = np.average(self.teoretic_y, axis=1)
842
+ self.teoretic_avg = np.repeat(self.teoretic_avg, self.teoretic_x.size)
843
+ self.teoretic_avg = np.reshape(self.teoretic_avg, self.teoretic_y.shape)
844
+ self.w = np.ones(self.teoretic_y.shape) if w is None else np.array(w)
845
+ if self.has_teoretic_released:
846
+ self.teoretic_released = np.array(teoretic_released if teoretic_released is not None else [[]])
847
+ self.know_released_compartments = know_released_compartments if know_released_compartments is not None else []
848
+ self.released_avg = np.average(self.teoretic_released, axis=1)
849
+ self.released_avg = np.repeat(self.released_avg, self.teoretic_x.size)
850
+ self.released_avg = np.reshape(self.released_avg, self.teoretic_released.shape)
851
+ self.released_w = np.ones(self.teoretic_released.shape) if released_w is None else np.array(released_w)
852
+
853
+ assert any([c0, d, compartment_number is not None]), "Need to set c0 or d and compartment_number"
854
+ if not c0:
855
+ assert all([d, compartment_number is not None]), "Need to set d and compartment_number"
856
+ self.d = d
857
+ self.compartment_number = compartment_number
858
+ self.c0 = None
859
+ else:
860
+ self.c0 = np.array(c0)
861
+
862
+
863
+ def load_data_from_list(self, x):
864
+ super().load_data_from_list(x)
865
+ n = self.configuration_matrix_target_count + self.outputs_target_count + self.volumes_target_count
866
+ if self.released_configuration_matrix_target:
867
+ self.released_configuration_matrix[self.released_configuration_matrix_target] = x[n:n + self.released_configuration_matrix_target_count]
868
+ n += self.released_configuration_matrix_target_count
869
+ if self.released_outputs_target:
870
+ self.released_outputs[self.released_outputs_target] = x[n:n + self.released_outputs_target_count]
871
+ n += self.released_outputs_target_count
872
+ if self.release_parameters_target:
873
+ self.release_parameters[self.release_parameters_target] = x[n:n + self.release_parameters_target_count]
874
+
875
+ def _target_function(self, x, max_step=0.01, metric='R2'):
876
+ self.load_data_from_list(x)
877
+ c0 = self.c0
878
+ if c0 is None:
879
+ c0 = np.zeros((2, self.outputs.size))
880
+ c0[0][self.compartment_number] = self.d / self.volumes[self.compartment_number]
881
+ self(
882
+ t_max=np.max(self.teoretic_x),
883
+ c0=c0,
884
+ t_eval=self.teoretic_x,
885
+ max_step=max_step
886
+ )
887
+ if not self.last_result.success:
888
+ return BIG_VALUE
889
+ if self.has_teoretic_y:
890
+ target_results = self.last_result.y[tuple(self.know_compartments), :]
891
+ if self.has_teoretic_released:
892
+ t = tuple([self.configuration_matrix.shape[0] + i for i in self.know_released_compartments])
893
+ released_target_results = self.last_result.y[t, :]
894
+ if metric == 'R2':
895
+ if self.has_teoretic_y:
896
+ a = 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))
897
+ else:
898
+ a = 0
899
+ if self.has_teoretic_released:
900
+ b = np.sum(np.sum(self.released_w * ((released_target_results - self.teoretic_released) ** 2), axis=1) / np.sum(
901
+ (self.released_avg - self.teoretic_released) ** 2, axis=1))
902
+ else:
903
+ b = 0
904
+ return a + b
905
+ elif metric == 'norm':
906
+ if self.has_teoretic_y:
907
+ a = np.linalg.norm(target_results - self.teoretic_y)
908
+ else:
909
+ a = 0
910
+ if self.has_teoretic_released:
911
+ b = np.linalg.norm(released_target_results - self.teoretic_released)
912
+ else:
913
+ b = 0
914
+ return a + b
915
+ else:
916
+ if self.has_teoretic_y:
917
+ a = np.sum(np.sum(self.w * ((target_results - self.teoretic_y) ** 2), axis=1) / np.sum(
918
+ (self.teoretic_avg - self.teoretic_y) ** 2, axis=1))
919
+ else:
920
+ a = 0
921
+ if self.has_teoretic_released:
922
+ b = np.sum(np.sum(self.released_w * ((released_target_results - self.teoretic_released) ** 2), axis=1) / np.sum(
923
+ (self.released_avg - self.teoretic_released) ** 2, axis=1))
924
+ else:
925
+ b = 0
926
+ return a + b
927
+
928
+ def optimize(self, method=None, user_method=None, method_is_func=True,
929
+ optimization_func_name='__call__', max_step=0.01, **kwargs):
930
+ x = super().optimize(method, user_method, method_is_func, optimization_func_name, max_step, **kwargs)
931
+ n = self.configuration_matrix_target_count + self.outputs_target_count + self.volumes_target_count + int(
932
+ self.need_magic_optimization)
933
+ if self.released_configuration_matrix_target:
934
+ self.released_configuration_matrix[self.released_configuration_matrix_target] = x[n:n + self.released_configuration_matrix_target_count]
935
+ n += self.released_configuration_matrix_target_count
936
+ if self.released_outputs_target:
937
+ self.released_outputs[self.released_outputs_target] = x[n: n + self.released_outputs_target_count]
938
+ n += self.released_outputs_target_count
939
+ if self.release_parameters_target:
940
+ self.release_parameters[self.release_parameters_target] = x[n:n + self.release_parameters_target_count]
941
+ self.released_configuration_matrix_target = None
942
+ self.released_outputs_target = None
943
+ return x
944
+
945
+ def plot_model(self, compartment_numbers=None, released_compartment_numbers=None,
946
+ released_compartment_names=None, compartment_names=None, left=None, right=None,
947
+ y_lims=None, released_y_lims=None, **kwargs):
948
+ """
949
+ Функция для построения графиков модели
950
+
951
+ Args:
952
+ compartment_numbers: Камеры, которые нужно отобразить (если не указать, отобразим все)
953
+ compartment_names: Имена камер
954
+ """
955
+ super().plot_model(compartment_numbers=compartment_numbers, compartment_names=compartment_names,
956
+ left=left, right=right, y_lims=y_lims, **kwargs)
957
+
958
+ if not released_compartment_numbers:
959
+ released_compartment_numbers = []
960
+ if not released_compartment_names:
961
+ released_compartment_names = {}
962
+ if not released_y_lims:
963
+ released_y_lims = {}
964
+ for i in released_compartment_numbers:
965
+ j = i + self.outputs.size
966
+ if hasattr(self, "teoretic_x") and hasattr(self, "teoretic_released") and i in self.know_released_compartments:
967
+ plt.plot(self.teoretic_x, self.teoretic_released[self.know_compartments.index(i)], "*r")
968
+ plt.plot(self.last_result.t, self.last_result.y[j])
969
+ plt.title(released_compartment_names.get(i, j))
970
+ plt.xlim(left=left, right=right)
971
+ if released_y_lims.get(i):
972
+ plt.ylim(released_y_lims.get(i))
973
+ plt.grid()
974
+ try:
975
+ plt.show()
976
+ except AttributeError:
977
+ plt.savefig(f'{released_compartment_names.get(i, str(j))}.png')
978
+ plt.cla()
979
+
@@ -1,17 +1,28 @@
1
1
  import inspect
2
- from numbalsoda import lsoda_sig, lsoda
2
+ import matplotlib
3
3
  import numpy as np
4
4
  from scipy.integrate import solve_ivp, LSODA
5
5
  from scipy.optimize import minimize
6
6
  from PyPharm.algorithms.country_optimization import CountriesAlgorithm
7
7
  from PyPharm.algorithms.country_optimization_v2 import CountriesAlgorithm_v2
8
8
  from PyPharm.algorithms.genetic_optimization import GeneticAlgorithm
9
- from PyPharm.constants import MODEL_CONST, ORGAN_NAMES, ANIMALS
9
+ from PyPharm.constants import MODEL_CONST, ANIMALS
10
10
  from numba import njit, types, cfunc
11
11
  from numba.typed import Dict
12
- import matplotlib.pyplot as plt
13
12
 
14
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
+
15
26
 
16
27
  class PBPKmod:
17
28
 
@@ -25,7 +36,7 @@ class PBPKmod:
25
36
  self.know_cl = know_cl if know_cl is not None else {}
26
37
  self._optim = False
27
38
  self.numba_option = numba_option
28
- self.lsoda_option = lsoda_option
39
+ self.lsoda_option = lsoda_option and available_lsoda
29
40
  if numba_option or lsoda_option:
30
41
  self.cnst_v_dict = {}
31
42
  for key in MODEL_CONST:
@@ -253,7 +264,7 @@ class PBPKmod:
253
264
  return x
254
265
 
255
266
  def update_know_params(self, k_cl=None):
256
- if k_cl is not None:
267
+ if k_cl:
257
268
  i = 0
258
269
  for name in self._organs:
259
270
  know_k = self.know_k.get(name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pypharm
3
- Version: 1.5.1
3
+ Version: 1.6.0
4
4
  Summary: Module for solving pharmacokinetic problems
5
5
  Home-page: https://github.com/Krash13/PyPharm
6
6
  Author: Krash13
@@ -6,7 +6,7 @@ def readme():
6
6
 
7
7
  setup(
8
8
  name='pypharm',
9
- version='1.5.1',
9
+ version='1.6.0',
10
10
  author='Krash13',
11
11
  author_email='krasheninnikov.r.s@muctr.ru',
12
12
  description='Module for solving pharmacokinetic problems',
File without changes
File without changes
File without changes
File without changes