The model is the 4-parameter Hill (logistic) equation — the standard shape for a
competitive dose-response:
response(x) = bottom + (top - bottom) / (1 + (x / IC50) ^ hill)
Fitting. Least squares on the four parameters, minimised by a Nelder-Mead simplex
written from scratch in numpy — no scipy, no solver library. IC50 is fitted in log10 space
(that is the scale the data lives on) and the model is evaluated as
1/(1+exp(hill·ln10·(log10 x − log10 IC50)))
so it never overflows. Three starting guesses (hill = 0.5, 1, 2) are tried and the best kept.
Uncertainty. Residual bootstrap: the residuals from the best fit are resampled with
replacement, added back onto the fitted curve, and the whole fit is re-run — a few hundred times.
The 2.5th and 97.5th percentiles of those refits give the CI and the shaded band. Residual
resampling (rather than resampling points) is used because an 8-point dilution series is a
designed set — dropping a point at random would destroy the design.
Why it is fast. Running hundreds of simplexes one after another in Python takes seconds.
Instead every resample gets its own simplex and they all march in lockstep: one reflection, one
expansion and two contractions for the entire batch, as four numpy calls per iteration on a
B × 5 × 4 array. Same algorithm, one pass, and the
whole bootstrap lands in a fraction of a second.
Why it matters. A single IC50 number hides how much the data actually pins it down.
Push the curve so its bottom plateau is off the edge of the tested range and watch the CI blow up
while R² stays comfortably high.