Thanks for illuminating that for me! I was thinking that you could parallelize the "brute force" part of the search, and periodically have the supervisor pick the best result to inform the next phase of search. The fact that this would have to be built much deeper in the stack makes this approach less feasible, of course.
I'll have to think about how to full extract a working example, as this is part of a larger simulation codebase, so the model implementations you would need to be able to run this would require a few hundred lines of code to be extracted (the model itself, the curve objects, the data loading), and I didn't want to overwhelm with a bunch of information.
To answer your question about "g_curve": in this code, t is a numpy array of time values for which we want to integrate the model. I thought about caching, and indeed I do cache the output of these curves (they're splines/step functions under the hood), but I wasn't sure the *same* time values would be used in different calls to this residual function. I didn't know if there was some kind of adaptive step size where different iterations might use different times to sample, so I keep the (somewhat expensive) calculation in the hot loop to ensure correctness rather than save computation time. Because the same curve object does cache results for different time values, `observed_g` amortizes to a fairly cheap list copy.
I'm actually fitting for many more parameters than the sample code implies, here's my fit report:
[[Fit Statistics]]
# fitting method = leastsq
# function evals = 381
# data points = 2000
# variables = 10
chi-square = 3033802.20
reduced chi-square = 1524.52372
Akaike info crit = 14668.8496
Bayesian info crit = 14724.8586
[[Variables]]
h: 111.762032 +/- 1508.53479 (1349.77%) (init = 136)
n: 4.33066010 +/- 30.5492149 (705.42%) (init = 0.13)
gb: 120.976891 +/- 1.62835388 (1.35%) (init = 120)
ib: 49.7449559 +/- 1123.46722 (2258.45%) (init = 10)
p1: -0.09994449 +/- 0.01059817 (10.60%) (init = -0.0131)
p2: -0.09120352 +/- 1.78702475 (1959.38%) (init = -0.0135)
p3: 2.9656e-06 +/- 6.9307e-05 (2337.06%) (init = 2.9e-06)
x_0: 1.0143e-04 +/- 0.00511144 (5039.62%) (init = 0.01)
i1_0: 1.86116644 +/- 2736.46694 (147029.67%) (init = 10)
i2_0: -99.4873902 +/- 2285.40105 (2297.18%) (init = 0)
[[Correlations]] (unreported correlations are < 0.100)
C(gb, p3) = +0.4926
C(gb, p2) = +0.3764
C(p2, i1_0) = -0.3742
C(gb, p1) = -0.3442
C(p3, i1_0) = +0.3419
C(h, p2) = +0.3405
C(p2, p3) = -0.3273
C(h, p3) = -0.2888
C(ib, i2_0) = +0.2689
C(i1_0, i2_0) = -0.2334
C(n, p3) = +0.2137
C(n, ib) = +0.2101
C(h, n) = +0.2012
C(gb, i1_0) = +0.1885
C(n, i2_0) = -0.1779
C(h, p1) = +0.1670
C(ib, p1) = +0.1621
C(p2, i2_0) = +0.1584
C(n, p2) = -0.1556
C(n, x_0) = -0.1539
C(p2, x_0) = -0.1511
C(n, gb) = -0.1487
C(p3, x_0) = +0.1471
C(n, i1_0) = +0.1293
C(h, i2_0) = +0.1207
C(p1, x_0) = -0.1170
C(p1, p2) = -0.1055
And the actual parameter values from the final fit:
Parameters([
('h', <Parameter 'h', value=111.76203180426232 +/- 1.51e+03, bounds=[80:200]>),
('n', <Parameter 'n', value=4.330660103060304 +/- 30.5, bounds=[0.01:5.0]>),
('gb', <Parameter 'gb', value=120.97689107965567 +/- 1.63, bounds=[80:200]>),
('ib', <Parameter 'ib', value=49.74495592655311 +/- 1.12e+03, bounds=[1:50]>),
('p1', <Parameter 'p1', value=-0.0999444948667807 +/- 0.0106, bounds=[-0.1:-0.001]>),
('p2', <Parameter 'p2', value=-0.09120351550969862 +/- 1.79, bounds=[-0.1:-0.001]>),
('p3', <Parameter 'p3', value=2.9655792256020028e-06 +/- 6.93e-05, bounds=[5e-07:1e-05]>),
('x_0', <Parameter 'x_0', value=0.00010142508073466625 +/- 0.00511, bounds=[0.0001:0.5]>),
('i1_0', <Parameter 'i1_0', value=1.8611664435517237 +/- 2.74e+03, bounds=[1:50]>),
('i2_0', <Parameter 'i2_0', value=-99.48739015035827 +/- 2.29e+03, bounds=[-100:100]>)])
The model is a fairly simple glucose model for patients in the ICU, and consists of a system of 4 ODEs. I'm trying to solve for constant parameters that are roughly the same for a given patient, as well as the initial conditions for the ODEs that do not have a direct observation we can seed with.
I'll try to extract a minimal example that demonstrates the slowdown I saw with this model, it may take a bit though.