Module sprime.hill_fitting
Hill curve fitting module for sprime.
Implements a linear-x four-parameter logistic (linear-x 4PL) model: the independent
variable is concentration x on a linear scale, entering as (x / EC50) in the denominator.
This matches forms such as AAT Bioquest and generic (x/C)^B calculators.
Industry note: "Hill equation" and "4PL" are not standardized. Many tools (e.g. SigmaPlot,
GraphPad-style log-dose forms) use a log-x 4PL where the dose axis is log10(concentration),
which yields different Hill slope sign conventions for the same curve. Our slope values are
numerically consistent with the linear-x parameterization; compare log-x vs linear-x in
docs/background/README_4PL_Dose_Response.md#linear-x-vs-log-x-4pl-hill-slope.
Naming: The exponent n in (x/EC50)^n is exposed as steepness_coefficient (and
initial_steepness_coefficient for guesses). That is the same role as the classical Hill
coefficient n in linear-x Hill formulations; we avoid the name hill_coefficient here
so it is not confused with log-x "Hill slope" outputs from other packages.
Adapted to work with sprime's domain entities.
Functions
def fit_hill_curve(concentrations: List[float],
responses: List[float],
*,
initial_zero_asymptote: float | None = None,
initial_inf_asymptote: float | None = None,
initial_ec50: float | None = None,
initial_steepness_coefficient: float | None = None,
curve_direction: str | None = None,
maxfev: int = 3000000,
zero_replacement: float = 1e-24,
bounds: Tuple[List[float], List[float]] | None = None,
**curve_fit_kwargs)-
Expand source code
def fit_hill_curve( concentrations: List[float], responses: List[float], *, # Initial parameter guesses (all optional with defaults) initial_zero_asymptote: Optional[float] = None, initial_inf_asymptote: Optional[float] = None, initial_ec50: Optional[float] = None, initial_steepness_coefficient: Optional[float] = None, # Curve direction curve_direction: Optional[str] = None, # "up", "down", or None for auto-detect # Optimization parameters maxfev: int = 3000000, # Zero concentration handling zero_replacement: float = 1e-24, # Parameter bounds (optional) bounds: Optional[Tuple[List[float], List[float]]] = None, # Additional scipy.optimize.curve_fit parameters **curve_fit_kwargs, ): """ Fit four-parameter Hill equation to dose-response data. Fits a sigmoidal curve to concentration-response data and returns HillCurveParams with fitted parameters. Args: concentrations: List of concentration values responses: List of response values (must match length of concentrations) initial_zero_asymptote: Initial guess for zero asymptote (default: auto-estimated) initial_inf_asymptote: Initial guess for inf asymptote (default: auto-estimated) initial_ec50: Initial guess for EC50 (default: auto-estimated) initial_steepness_coefficient: Initial guess for steepness coefficient n (default: auto-estimated) curve_direction: Curve direction - "up" (increasing), "down" (decreasing), or None for auto-detect (tries both, selects best r-squared) maxfev: Maximum function evaluations for optimization (default: 3,000,000) zero_replacement: Value to replace zero concentrations (default: 1e-24) bounds: Optional parameter bounds as (lower_bounds, upper_bounds) tuples Format: ([zero_asymptote_min, steepness_min, ec50_min, inf_asymptote_min], [zero_asymptote_max, steepness_max, ec50_max, inf_asymptote_max]) **curve_fit_kwargs: Additional arguments passed to scipy.optimize.curve_fit Returns: HillCurveParams: Fitted curve parameters with r-squared Raises: ValueError: If inputs are invalid RuntimeError: If curve fitting fails ImportError: If numpy/scipy are not installed """ if np is None or curve_fit is None: raise ImportError("Hill curve fitting requires scipy. " "Install with: pip install scipy") # Validate inputs if len(concentrations) != len(responses): raise ValueError("Concentrations and responses must have same length") if len(concentrations) < 4: raise ValueError("Need at least 4 data points to fit 4-parameter Hill equation") # Convert to numpy arrays (make copies to avoid modifying originals) concentrations = list(concentrations) responses = list(responses) # Sort data if needed (ascending concentrations) if concentrations[0] > concentrations[-1]: concentrations.reverse() responses.reverse() # Handle zero concentrations if concentrations[0] == 0: concentrations[0] = zero_replacement x_data = np.array(concentrations) y_data = np.array(responses) # Auto-detect or use specified curve direction if curve_direction is None: # Try both directions, return best fit return _fit_with_auto_direction( x_data, y_data, initial_zero_asymptote, initial_inf_asymptote, initial_ec50, initial_steepness_coefficient, maxfev, bounds, **curve_fit_kwargs, ) else: # Fit with specified direction return _fit_single_direction( x_data, y_data, curve_direction, initial_zero_asymptote, initial_inf_asymptote, initial_ec50, initial_steepness_coefficient, maxfev, bounds, **curve_fit_kwargs, )Fit four-parameter Hill equation to dose-response data.
Fits a sigmoidal curve to concentration-response data and returns HillCurveParams with fitted parameters.
Args
concentrations- List of concentration values
responses- List of response values (must match length of concentrations)
initial_zero_asymptote- Initial guess for zero asymptote (default: auto-estimated)
initial_inf_asymptote- Initial guess for inf asymptote (default: auto-estimated)
initial_ec50- Initial guess for EC50 (default: auto-estimated)
initial_steepness_coefficient- Initial guess for steepness coefficient n (default: auto-estimated)
curve_direction- Curve direction - "up" (increasing), "down" (decreasing), or None for auto-detect (tries both, selects best r-squared)
maxfev- Maximum function evaluations for optimization (default: 3,000,000)
zero_replacement- Value to replace zero concentrations (default: 1e-24)
bounds- Optional parameter bounds as (lower_bounds, upper_bounds) tuples Format: ([zero_asymptote_min, steepness_min, ec50_min, inf_asymptote_min], [zero_asymptote_max, steepness_max, ec50_max, inf_asymptote_max])
**curve_fit_kwargs- Additional arguments passed to scipy.optimize.curve_fit
Returns
HillCurveParams- Fitted curve parameters with r-squared
Raises
ValueError- If inputs are invalid
RuntimeError- If curve fitting fails
ImportError- If numpy/scipy are not installed
def hill_equation(x,
zero_asymptote: float,
steepness_coefficient: float,
ec50: float,
inf_asymptote: float)-
Expand source code
def hill_equation( x, zero_asymptote: float, steepness_coefficient: float, ec50: float, inf_asymptote: float ): """ Linear-x four-parameter logistic (linear-x 4PL) Hill form. Response vs concentration x (linear scale, same units as EC50): y = inf_asymptote + (zero_asymptote - inf_asymptote) / (1 + (x / C)^n) At concentration approaching zero, y approaches zero_asymptote; at saturating concentration, y approaches inf_asymptote. C = ec50, n = steepness_coefficient. Signed n encodes curve direction together with asymptote ordering; this is not interchangeable with log-x 4PL slope reporting. Args: x: Concentration values (linear scale) zero_asymptote: Response as concentration -> 0 (left side of dose axis) steepness_coefficient: Exponent n in (x/C)^n. Conceptually the same quantity often called the **Hill coefficient** in linear-x dose-response formulations (cooperativity exponent); we use ``steepness_coefficient`` here to avoid confusion with log-x tools that report a different "Hill slope" (see module docstring). ec50: Half-maximal concentration C inf_asymptote: Response at saturating concentration (right of dose axis) Returns: Response values """ return inf_asymptote + (zero_asymptote - inf_asymptote) / ( 1 + (x / ec50) ** steepness_coefficient )Linear-x four-parameter logistic (linear-x 4PL) Hill form.
Response vs concentration x (linear scale, same units as EC50):
y = inf_asymptote + (zero_asymptote - inf_asymptote) / (1 + (x / C)^n)At concentration approaching zero, y approaches zero_asymptote; at saturating concentration, y approaches inf_asymptote. C = ec50, n = steepness_coefficient. Signed n encodes curve direction together with asymptote ordering; this is not interchangeable with log-x 4PL slope reporting.
Args
x- Concentration values (linear scale)
zero_asymptote- Response as concentration -> 0 (left side of dose axis)
steepness_coefficient- Exponent n in (x/C)^n. Conceptually the same quantity often called
the Hill coefficient in linear-x dose-response formulations (cooperativity exponent);
we use
steepness_coefficienthere to avoid confusion with log-x tools that report a different "Hill slope" (see module docstring). ec50- Half-maximal concentration C
inf_asymptote- Response at saturating concentration (right of dose axis)
Returns
Response values