mxlpy 0.21.0__py3-none-any.whl → 0.23.0__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.
mxlpy/__init__.py CHANGED
@@ -52,8 +52,9 @@ from . import (
52
52
  plot,
53
53
  report,
54
54
  sbml,
55
+ units,
55
56
  )
56
- from .integrators import DefaultIntegrator, Scipy
57
+ from .integrators import DefaultIntegrator, Diffrax, Scipy
57
58
  from .label_map import LabelMapper
58
59
  from .linear_label_map import LinearLabelMapper
59
60
  from .mc import Cache
@@ -61,7 +62,14 @@ from .model import Model
61
62
  from .scan import steady_state, time_course, time_course_over_protocol
62
63
  from .simulator import Simulator
63
64
  from .symbolic import SymbolicModel, to_symbolic_model
64
- from .types import Derived, IntegratorProtocol, unwrap
65
+ from .types import (
66
+ Derived,
67
+ InitialAssignment,
68
+ IntegratorProtocol,
69
+ Parameter,
70
+ Variable,
71
+ unwrap,
72
+ )
65
73
 
66
74
  with contextlib.suppress(ImportError):
67
75
  from .integrators import Assimulo
@@ -83,13 +91,17 @@ __all__ = [
83
91
  "Cache",
84
92
  "DefaultIntegrator",
85
93
  "Derived",
94
+ "Diffrax",
95
+ "InitialAssignment",
86
96
  "IntegratorProtocol",
87
97
  "LabelMapper",
88
98
  "LinearLabelMapper",
89
99
  "Model",
100
+ "Parameter",
90
101
  "Scipy",
91
102
  "Simulator",
92
103
  "SymbolicModel",
104
+ "Variable",
93
105
  "cartesian_product",
94
106
  "compare",
95
107
  "distributions",
@@ -110,6 +122,7 @@ __all__ = [
110
122
  "time_course",
111
123
  "time_course_over_protocol",
112
124
  "to_symbolic_model",
125
+ "units",
113
126
  "unwrap",
114
127
  ]
115
128
 
mxlpy/carousel.py CHANGED
@@ -18,7 +18,7 @@ if TYPE_CHECKING:
18
18
  from collections.abc import Iterable, Mapping
19
19
 
20
20
  from mxlpy import Model
21
- from mxlpy.types import Array, IntegratorType, RateFn
21
+ from mxlpy.types import Array, IntegratorType, RateFn, Result
22
22
 
23
23
 
24
24
  @dataclass
@@ -35,11 +35,13 @@ class CarouselSteadyState:
35
35
  """Time course of a carousel simulation."""
36
36
 
37
37
  carousel: list[Model]
38
- results: list[scan.TimePoint]
38
+ results: list[Result]
39
39
 
40
40
  def get_variables_by_model(self) -> pd.DataFrame:
41
41
  """Get the variables of the time course results, indexed by model."""
42
- return pd.DataFrame({i: r.variables for i, r in enumerate(self.results)}).T
42
+ return pd.DataFrame(
43
+ {i: r.variables.iloc[-1] for i, r in enumerate(self.results)}
44
+ ).T
43
45
 
44
46
 
45
47
  @dataclass
@@ -47,7 +49,7 @@ class CarouselTimeCourse:
47
49
  """Time course of a carousel simulation."""
48
50
 
49
51
  carousel: list[Model]
50
- results: list[scan.TimeCourse]
52
+ results: list[Result]
51
53
 
52
54
  def get_variables_by_model(self) -> pd.DataFrame:
53
55
  """Get the variables of the time course results, indexed by model."""
mxlpy/compare.py CHANGED
@@ -8,8 +8,8 @@ from typing import TYPE_CHECKING, cast
8
8
  import pandas as pd
9
9
 
10
10
  from mxlpy import plot
11
- from mxlpy.simulator import Result, Simulator
12
- from mxlpy.types import unwrap
11
+ from mxlpy.simulator import Simulator
12
+ from mxlpy.types import Result, unwrap
13
13
 
14
14
  if TYPE_CHECKING:
15
15
  from mxlpy.model import Model
@@ -230,11 +230,7 @@ def protocol_time_courses(
230
230
  ) -> ProtocolComparison:
231
231
  """Compare the time courses of two models."""
232
232
  return ProtocolComparison(
233
- res1=unwrap(
234
- Simulator(m1).simulate_over_protocol(protocol=protocol).get_result()
235
- ),
236
- res2=unwrap(
237
- Simulator(m2).simulate_over_protocol(protocol=protocol).get_result()
238
- ),
233
+ res1=unwrap(Simulator(m1).simulate_protocol(protocol=protocol).get_result()),
234
+ res2=unwrap(Simulator(m2).simulate_protocol(protocol=protocol).get_result()),
239
235
  protocol=protocol,
240
236
  )
@@ -187,7 +187,7 @@ def model_diff(m1: Model, m2: Model) -> ModelDiff:
187
187
  if (v2 := m2._parameters.get(k)) is None: # noqa: SLF001
188
188
  diff.missing_parameters.add(k)
189
189
  elif v1 != v2:
190
- diff.different_parameters[k] = (v1, v2)
190
+ diff.different_parameters[k] = (v1, v2) # type: ignore
191
191
 
192
192
  for k, v1 in m1._variables.items(): # noqa: SLF001
193
193
  if (v2 := m2._variables.get(k)) is None: # noqa: SLF001
mxlpy/fit.py CHANGED
@@ -32,6 +32,7 @@ if TYPE_CHECKING:
32
32
  LOGGER = logging.getLogger(__name__)
33
33
 
34
34
  __all__ = [
35
+ "Bounds",
35
36
  "CarouselFit",
36
37
  "FitResult",
37
38
  "InitialGuess",
@@ -43,13 +44,13 @@ __all__ = [
43
44
  "ResidualFn",
44
45
  "SteadyStateResidualFn",
45
46
  "TimeSeriesResidualFn",
47
+ "carousel_protocol_time_course",
46
48
  "carousel_steady_state",
47
49
  "carousel_time_course",
48
- "carousel_time_course_over_protocol",
50
+ "protocol_time_course",
49
51
  "rmse",
50
52
  "steady_state",
51
53
  "time_course",
52
- "time_course_over_protocol",
53
54
  ]
54
55
 
55
56
 
@@ -83,7 +84,15 @@ class CarouselFit:
83
84
 
84
85
  type InitialGuess = dict[str, float]
85
86
  type ResidualFn = Callable[[Array], float]
86
- type MinimizeFn = Callable[[ResidualFn, InitialGuess], MinResult | None]
87
+ type Bounds = dict[str, tuple[float | None, float | None]]
88
+ type MinimizeFn = Callable[
89
+ [
90
+ ResidualFn,
91
+ InitialGuess,
92
+ Bounds,
93
+ ],
94
+ MinResult | None,
95
+ ]
87
96
  type LossFn = Callable[
88
97
  [
89
98
  pd.DataFrame | pd.Series,
@@ -151,7 +160,6 @@ class ProtocolResidualFn(Protocol):
151
160
  integrator: IntegratorType,
152
161
  loss_fn: LossFn,
153
162
  protocol: pd.DataFrame,
154
- time_points_per_step: int = 10,
155
163
  ) -> float:
156
164
  """Calculate residual error between model time course and experimental data."""
157
165
  ...
@@ -160,11 +168,12 @@ class ProtocolResidualFn(Protocol):
160
168
  def _default_minimize_fn(
161
169
  residual_fn: ResidualFn,
162
170
  p0: dict[str, float],
171
+ bounds: Bounds,
163
172
  ) -> MinResult | None:
164
173
  res = minimize(
165
174
  residual_fn,
166
175
  x0=list(p0.values()),
167
- bounds=[(0, None) for _ in range(len(p0))],
176
+ bounds=[bounds.get(name, (1e-6, 1e6)) for name in p0],
168
177
  method="L-BFGS-B",
169
178
  )
170
179
  if res.success:
@@ -278,7 +287,7 @@ def _time_course_residual(
278
287
  )
279
288
 
280
289
 
281
- def _protocol_residual(
290
+ def _protocol_time_course_residual(
282
291
  par_values: ArrayLike,
283
292
  # This will be filled out by partial
284
293
  par_names: list[str],
@@ -288,7 +297,6 @@ def _protocol_residual(
288
297
  integrator: IntegratorType,
289
298
  loss_fn: LossFn,
290
299
  protocol: pd.DataFrame,
291
- time_points_per_step: int = 10,
292
300
  ) -> float:
293
301
  """Calculate residual error between model time course and experimental data.
294
302
 
@@ -313,9 +321,9 @@ def _protocol_residual(
313
321
  y0=y0,
314
322
  integrator=integrator,
315
323
  )
316
- .simulate_over_protocol(
324
+ .simulate_protocol_time_course(
317
325
  protocol=protocol,
318
- time_points_per_step=time_points_per_step,
326
+ time_points=data.index,
319
327
  )
320
328
  .get_result()
321
329
  )
@@ -329,6 +337,84 @@ def _protocol_residual(
329
337
  )
330
338
 
331
339
 
340
+ def _carousel_steady_state_worker(
341
+ model: Model,
342
+ p0: dict[str, float],
343
+ data: pd.Series,
344
+ y0: dict[str, float] | None,
345
+ integrator: IntegratorType | None,
346
+ loss_fn: LossFn,
347
+ minimize_fn: MinimizeFn,
348
+ residual_fn: SteadyStateResidualFn,
349
+ bounds: Bounds | None,
350
+ ) -> FitResult | None:
351
+ model_pars = model.get_parameter_values()
352
+
353
+ return steady_state(
354
+ model,
355
+ p0={k: v for k, v in p0.items() if k in model_pars},
356
+ y0=y0,
357
+ data=data,
358
+ minimize_fn=minimize_fn,
359
+ residual_fn=residual_fn,
360
+ integrator=integrator,
361
+ loss_fn=loss_fn,
362
+ bounds=bounds,
363
+ )
364
+
365
+
366
+ def _carousel_time_course_worker(
367
+ model: Model,
368
+ p0: dict[str, float],
369
+ data: pd.DataFrame,
370
+ y0: dict[str, float] | None,
371
+ integrator: IntegratorType | None,
372
+ loss_fn: LossFn,
373
+ minimize_fn: MinimizeFn,
374
+ residual_fn: TimeSeriesResidualFn,
375
+ bounds: Bounds | None,
376
+ ) -> FitResult | None:
377
+ model_pars = model.get_parameter_values()
378
+ return time_course(
379
+ model,
380
+ p0={k: v for k, v in p0.items() if k in model_pars},
381
+ y0=y0,
382
+ data=data,
383
+ minimize_fn=minimize_fn,
384
+ residual_fn=residual_fn,
385
+ integrator=integrator,
386
+ loss_fn=loss_fn,
387
+ bounds=bounds,
388
+ )
389
+
390
+
391
+ def _carousel_protocol_worker(
392
+ model: Model,
393
+ p0: dict[str, float],
394
+ data: pd.DataFrame,
395
+ protocol: pd.DataFrame,
396
+ y0: dict[str, float] | None,
397
+ integrator: IntegratorType | None,
398
+ loss_fn: LossFn,
399
+ minimize_fn: MinimizeFn,
400
+ residual_fn: ProtocolResidualFn,
401
+ bounds: Bounds | None,
402
+ ) -> FitResult | None:
403
+ model_pars = model.get_parameter_values()
404
+ return protocol_time_course(
405
+ model,
406
+ p0={k: v for k, v in p0.items() if k in model_pars},
407
+ y0=y0,
408
+ protocol=protocol,
409
+ data=data,
410
+ minimize_fn=minimize_fn,
411
+ residual_fn=residual_fn,
412
+ integrator=integrator,
413
+ loss_fn=loss_fn,
414
+ bounds=bounds,
415
+ )
416
+
417
+
332
418
  def steady_state(
333
419
  model: Model,
334
420
  *,
@@ -339,6 +425,7 @@ def steady_state(
339
425
  residual_fn: SteadyStateResidualFn = _steady_state_residual,
340
426
  integrator: IntegratorType | None = None,
341
427
  loss_fn: LossFn = rmse,
428
+ bounds: Bounds | None = None,
342
429
  ) -> FitResult | None:
343
430
  """Fit model parameters to steady-state experimental data.
344
431
 
@@ -355,18 +442,19 @@ def steady_state(
355
442
  residual_fn: Function to calculate fitting error
356
443
  integrator: ODE integrator class
357
444
  loss_fn: Loss function to use for residual calculation
445
+ bounds: Mapping of bounds per parameter
358
446
 
359
447
  Returns:
360
448
  dict[str, float]: Fitted parameters as {parameter_name: fitted_value}
361
449
 
362
450
  Note:
363
- Uses L-BFGS-B optimization with bounds [1e-12, 1e6] for all parameters
451
+ Uses L-BFGS-B optimization with bounds [1e-6, 1e6] for all parameters
364
452
 
365
453
  """
366
454
  par_names = list(p0.keys())
367
455
 
368
456
  # Copy to restore
369
- p_orig = model.parameters
457
+ p_orig = model.get_parameter_values()
370
458
 
371
459
  fn = cast(
372
460
  ResidualFn,
@@ -380,7 +468,7 @@ def steady_state(
380
468
  loss_fn=loss_fn,
381
469
  ),
382
470
  )
383
- min_result = minimize_fn(fn, p0)
471
+ min_result = minimize_fn(fn, p0, {} if bounds is None else bounds)
384
472
  # Restore original model
385
473
  model.update_parameters(p_orig)
386
474
  if min_result is None:
@@ -403,6 +491,7 @@ def time_course(
403
491
  residual_fn: TimeSeriesResidualFn = _time_course_residual,
404
492
  integrator: IntegratorType | None = None,
405
493
  loss_fn: LossFn = rmse,
494
+ bounds: Bounds | None = None,
406
495
  ) -> FitResult | None:
407
496
  """Fit model parameters to time course of experimental data.
408
497
 
@@ -419,16 +508,17 @@ def time_course(
419
508
  residual_fn: Function to calculate fitting error
420
509
  integrator: ODE integrator class
421
510
  loss_fn: Loss function to use for residual calculation
511
+ bounds: Mapping of bounds per parameter
422
512
 
423
513
  Returns:
424
514
  dict[str, float]: Fitted parameters as {parameter_name: fitted_value}
425
515
 
426
516
  Note:
427
- Uses L-BFGS-B optimization with bounds [1e-12, 1e6] for all parameters
517
+ Uses L-BFGS-B optimization with bounds [1e-6, 1e6] for all parameters
428
518
 
429
519
  """
430
520
  par_names = list(p0.keys())
431
- p_orig = model.parameters
521
+ p_orig = model.get_parameter_values()
432
522
 
433
523
  fn = cast(
434
524
  ResidualFn,
@@ -443,7 +533,7 @@ def time_course(
443
533
  ),
444
534
  )
445
535
 
446
- min_result = minimize_fn(fn, p0)
536
+ min_result = minimize_fn(fn, p0, {} if bounds is None else bounds)
447
537
  # Restore original model
448
538
  model.update_parameters(p_orig)
449
539
  if min_result is None:
@@ -456,7 +546,7 @@ def time_course(
456
546
  )
457
547
 
458
548
 
459
- def time_course_over_protocol(
549
+ def protocol_time_course(
460
550
  model: Model,
461
551
  *,
462
552
  p0: dict[str, float],
@@ -464,10 +554,10 @@ def time_course_over_protocol(
464
554
  protocol: pd.DataFrame,
465
555
  y0: dict[str, float] | None = None,
466
556
  minimize_fn: MinimizeFn = _default_minimize_fn,
467
- residual_fn: ProtocolResidualFn = _protocol_residual,
557
+ residual_fn: ProtocolResidualFn = _protocol_time_course_residual,
468
558
  integrator: IntegratorType | None = None,
469
559
  loss_fn: LossFn = rmse,
470
- time_points_per_step: int = 10,
560
+ bounds: Bounds | None = None,
471
561
  ) -> FitResult | None:
472
562
  """Fit model parameters to time course of experimental data.
473
563
 
@@ -486,16 +576,17 @@ def time_course_over_protocol(
486
576
  integrator: ODE integrator class
487
577
  loss_fn: Loss function to use for residual calculation
488
578
  time_points_per_step: Number of time points per step in the protocol
579
+ bounds: Mapping of bounds per parameter
489
580
 
490
581
  Returns:
491
582
  dict[str, float]: Fitted parameters as {parameter_name: fitted_value}
492
583
 
493
584
  Note:
494
- Uses L-BFGS-B optimization with bounds [1e-12, 1e6] for all parameters
585
+ Uses L-BFGS-B optimization with bounds [1e-6, 1e6] for all parameters
495
586
 
496
587
  """
497
588
  par_names = list(p0.keys())
498
- p_orig = model.parameters
589
+ p_orig = model.get_parameter_values()
499
590
 
500
591
  fn = cast(
501
592
  ResidualFn,
@@ -508,11 +599,10 @@ def time_course_over_protocol(
508
599
  integrator=integrator,
509
600
  loss_fn=loss_fn,
510
601
  protocol=protocol,
511
- time_points_per_step=time_points_per_step,
512
602
  ),
513
603
  )
514
604
 
515
- min_result = minimize_fn(fn, p0)
605
+ min_result = minimize_fn(fn, p0, {} if bounds is None else bounds)
516
606
  # Restore original model
517
607
  model.update_parameters(p_orig)
518
608
  if min_result is None:
@@ -525,78 +615,6 @@ def time_course_over_protocol(
525
615
  )
526
616
 
527
617
 
528
- def _carousel_steady_state_worker(
529
- model: Model,
530
- p0: dict[str, float],
531
- data: pd.Series,
532
- y0: dict[str, float] | None,
533
- integrator: IntegratorType | None,
534
- loss_fn: LossFn,
535
- minimize_fn: MinimizeFn,
536
- residual_fn: SteadyStateResidualFn,
537
- ) -> FitResult | None:
538
- model_pars = model.parameters
539
-
540
- return steady_state(
541
- model,
542
- p0={k: v for k, v in p0.items() if k in model_pars},
543
- y0=y0,
544
- data=data,
545
- minimize_fn=minimize_fn,
546
- residual_fn=residual_fn,
547
- integrator=integrator,
548
- loss_fn=loss_fn,
549
- )
550
-
551
-
552
- def _carousel_time_course_worker(
553
- model: Model,
554
- p0: dict[str, float],
555
- data: pd.DataFrame,
556
- y0: dict[str, float] | None,
557
- integrator: IntegratorType | None,
558
- loss_fn: LossFn,
559
- minimize_fn: MinimizeFn,
560
- residual_fn: TimeSeriesResidualFn,
561
- ) -> FitResult | None:
562
- model_pars = model.parameters
563
- return time_course(
564
- model,
565
- p0={k: v for k, v in p0.items() if k in model_pars},
566
- y0=y0,
567
- data=data,
568
- minimize_fn=minimize_fn,
569
- residual_fn=residual_fn,
570
- integrator=integrator,
571
- loss_fn=loss_fn,
572
- )
573
-
574
-
575
- def _carousel_protocol_worker(
576
- model: Model,
577
- p0: dict[str, float],
578
- data: pd.DataFrame,
579
- protocol: pd.DataFrame,
580
- y0: dict[str, float] | None,
581
- integrator: IntegratorType | None,
582
- loss_fn: LossFn,
583
- minimize_fn: MinimizeFn,
584
- residual_fn: ProtocolResidualFn,
585
- ) -> FitResult | None:
586
- model_pars = model.parameters
587
- return time_course_over_protocol(
588
- model,
589
- p0={k: v for k, v in p0.items() if k in model_pars},
590
- y0=y0,
591
- protocol=protocol,
592
- data=data,
593
- minimize_fn=minimize_fn,
594
- residual_fn=residual_fn,
595
- integrator=integrator,
596
- loss_fn=loss_fn,
597
- )
598
-
599
-
600
618
  def carousel_steady_state(
601
619
  carousel: Carousel,
602
620
  *,
@@ -607,8 +625,33 @@ def carousel_steady_state(
607
625
  residual_fn: SteadyStateResidualFn = _steady_state_residual,
608
626
  integrator: IntegratorType | None = None,
609
627
  loss_fn: LossFn = rmse,
628
+ bounds: Bounds | None = None,
610
629
  ) -> CarouselFit:
611
- """Fit model parameters to steady-state experimental data over a carousel."""
630
+ """Fit model parameters to steady-state experimental data over a carousel.
631
+
632
+ Examples:
633
+ >>> carousel_steady_state(carousel, p0=p0, data=data)
634
+
635
+ Args:
636
+ carousel: Model carousel to fit
637
+ p0: Initial parameter guesses as {parameter_name: value}
638
+ data: Experimental time course data
639
+ protocol: Experimental protocol
640
+ y0: Initial conditions as {species_name: value}
641
+ minimize_fn: Function to minimize fitting error
642
+ residual_fn: Function to calculate fitting error
643
+ integrator: ODE integrator class
644
+ loss_fn: Loss function to use for residual calculation
645
+ time_points_per_step: Number of time points per step in the protocol
646
+ bounds: Mapping of bounds per parameter
647
+
648
+ Returns:
649
+ dict[str, float]: Fitted parameters as {parameter_name: fitted_value}
650
+
651
+ Note:
652
+ Uses L-BFGS-B optimization with bounds [1e-6, 1e6] for all parameters
653
+
654
+ """
612
655
  return CarouselFit(
613
656
  [
614
657
  fit
@@ -622,6 +665,7 @@ def carousel_steady_state(
622
665
  loss_fn=loss_fn,
623
666
  minimize_fn=minimize_fn,
624
667
  residual_fn=residual_fn,
668
+ bounds=bounds,
625
669
  ),
626
670
  inputs=list(enumerate(carousel.variants)),
627
671
  )
@@ -640,8 +684,33 @@ def carousel_time_course(
640
684
  residual_fn: TimeSeriesResidualFn = _time_course_residual,
641
685
  integrator: IntegratorType | None = None,
642
686
  loss_fn: LossFn = rmse,
687
+ bounds: Bounds | None = None,
643
688
  ) -> CarouselFit:
644
- """Fit model parameters to time course of experimental data over a carousel."""
689
+ """Fit model parameters to time course of experimental data over a carousel.
690
+
691
+ Examples:
692
+ >>> carousel_steady_state(carousel, p0=p0, data=data)
693
+
694
+ Args:
695
+ carousel: Model carousel to fit
696
+ p0: Initial parameter guesses as {parameter_name: value}
697
+ data: Experimental time course data
698
+ protocol: Experimental protocol
699
+ y0: Initial conditions as {species_name: value}
700
+ minimize_fn: Function to minimize fitting error
701
+ residual_fn: Function to calculate fitting error
702
+ integrator: ODE integrator class
703
+ loss_fn: Loss function to use for residual calculation
704
+ time_points_per_step: Number of time points per step in the protocol
705
+ bounds: Mapping of bounds per parameter
706
+
707
+ Returns:
708
+ dict[str, float]: Fitted parameters as {parameter_name: fitted_value}
709
+
710
+ Note:
711
+ Uses L-BFGS-B optimization with bounds [1e-6, 1e6] for all parameters
712
+
713
+ """
645
714
  return CarouselFit(
646
715
  [
647
716
  fit
@@ -655,6 +724,7 @@ def carousel_time_course(
655
724
  loss_fn=loss_fn,
656
725
  minimize_fn=minimize_fn,
657
726
  residual_fn=residual_fn,
727
+ bounds=bounds,
658
728
  ),
659
729
  inputs=list(enumerate(carousel.variants)),
660
730
  )
@@ -663,7 +733,7 @@ def carousel_time_course(
663
733
  )
664
734
 
665
735
 
666
- def carousel_time_course_over_protocol(
736
+ def carousel_protocol_time_course(
667
737
  carousel: Carousel,
668
738
  *,
669
739
  p0: dict[str, float],
@@ -671,11 +741,36 @@ def carousel_time_course_over_protocol(
671
741
  protocol: pd.DataFrame,
672
742
  y0: dict[str, float] | None = None,
673
743
  minimize_fn: MinimizeFn = _default_minimize_fn,
674
- residual_fn: ProtocolResidualFn = _protocol_residual,
744
+ residual_fn: ProtocolResidualFn = _protocol_time_course_residual,
675
745
  integrator: IntegratorType | None = None,
676
746
  loss_fn: LossFn = rmse,
747
+ bounds: Bounds | None = None,
677
748
  ) -> CarouselFit:
678
- """Fit model parameters to time course of experimental data over a protocol."""
749
+ """Fit model parameters to time course of experimental data over a protocol.
750
+
751
+ Examples:
752
+ >>> carousel_steady_state(carousel, p0=p0, data=data)
753
+
754
+ Args:
755
+ carousel: Model carousel to fit
756
+ p0: Initial parameter guesses as {parameter_name: value}
757
+ data: Experimental time course data
758
+ protocol: Experimental protocol
759
+ y0: Initial conditions as {species_name: value}
760
+ minimize_fn: Function to minimize fitting error
761
+ residual_fn: Function to calculate fitting error
762
+ integrator: ODE integrator class
763
+ loss_fn: Loss function to use for residual calculation
764
+ time_points_per_step: Number of time points per step in the protocol
765
+ bounds: Mapping of bounds per parameter
766
+
767
+ Returns:
768
+ dict[str, float]: Fitted parameters as {parameter_name: fitted_value}
769
+
770
+ Note:
771
+ Uses L-BFGS-B optimization with bounds [1e-6, 1e6] for all parameters
772
+
773
+ """
679
774
  return CarouselFit(
680
775
  [
681
776
  fit
@@ -690,6 +785,7 @@ def carousel_time_course_over_protocol(
690
785
  loss_fn=loss_fn,
691
786
  minimize_fn=minimize_fn,
692
787
  residual_fn=residual_fn,
788
+ bounds=bounds,
693
789
  ),
694
790
  inputs=list(enumerate(carousel.variants)),
695
791
  )
mxlpy/identify.py CHANGED
@@ -59,21 +59,26 @@ def profile_likelihood(
59
59
 
60
60
  """
61
61
  parameter_distributions = sample(
62
- {k: LogNormal(np.log(v), sigma=1) for k, v in model.parameters.items()},
62
+ {
63
+ k: LogNormal(np.log(v), sigma=1)
64
+ for k, v in model.get_parameter_values().items()
65
+ },
63
66
  n=n_random,
64
67
  )
65
68
 
66
69
  res = {}
67
70
  for value in tqdm(parameter_values, desc=parameter_name):
68
71
  model.update_parameter(parameter_name, value)
69
- res[value] = parallelise(
70
- partial(
71
- _mc_fit_time_course_worker, model=model, data=data, loss_fn=loss_fn
72
- ),
73
- inputs=list(
74
- parameter_distributions.drop(columns=parameter_name).iterrows()
75
- ),
76
- disable_tqdm=True,
72
+ res[value] = dict(
73
+ parallelise(
74
+ partial(
75
+ _mc_fit_time_course_worker, model=model, data=data, loss_fn=loss_fn
76
+ ),
77
+ inputs=list(
78
+ parameter_distributions.drop(columns=parameter_name).iterrows()
79
+ ),
80
+ disable_tqdm=True,
81
+ )
77
82
  )
78
83
  errors = pd.DataFrame(res, dtype=float).T.abs().mean(axis=1)
79
84
  errors.index.name = "fitting error"
@@ -6,6 +6,7 @@ It includes support for both Assimulo and Scipy integrators, with Assimulo being
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
+ from .int_diffrax import Diffrax
9
10
  from .int_scipy import Scipy
10
11
 
11
12
  try:
@@ -16,5 +17,8 @@ except ImportError:
16
17
  DefaultIntegrator = Scipy
17
18
 
18
19
  __all__ = [
20
+ "Assimulo",
19
21
  "DefaultIntegrator",
22
+ "Diffrax",
23
+ "Scipy",
20
24
  ]