"""Independent SciPy fixed-weight IRLS oracle; archival R direct-loss fixtures are unchanged."""
import hashlib, json
from pathlib import Path
import numpy as np
import scipy
from scipy.optimize import least_squares

root = Path(__file__).resolve().parent
source = root / 'fixtures/synthetic-curves.json'
records = []
for curve in json.loads(source.read_text())['curves']:
    if curve['fit']['weighting'] != '1/Y^2':
        continue
    data = [d for d in curve['data'] if not d['excluded']]
    x = np.log10([d['concentration_M'] for d in data]); y = np.array([d['response'] for d in data])
    def predicted(p):
        top, bottom, midpoint, hill = p
        return bottom + (top-bottom)/(1+10.**np.clip((midpoint-x)*hill,-300,300))
    initial = [max(y), min(y), np.median(x), -1]
    span = max(y)-min(y)
    bounds = ([min(y)-20*span,min(y)-20*span,min(x)-5,-50], [max(y)+20*span,max(y)+20*span,max(x)+5,50])
    p = least_squares(lambda q: y-predicted(q), initial, bounds=bounds, x_scale='jac', ftol=1e-13, xtol=1e-13, gtol=1e-12).x
    for iteration in range(100):
        f = predicted(p)
        if np.any(f <= 0): raise ValueError('IRLS requires positive predictions')
        old = p.copy()
        p = least_squares(lambda q: (y-predicted(q))/f, old, bounds=bounds, x_scale='jac', ftol=1e-13, xtol=1e-13, gtol=1e-12).x
        scales=np.array([max(abs(y)),max(abs(y)),max(1,abs(p[2])),max(1,abs(p[3]))])
        if max(abs(p-old)/scales) < 1e-10: break
    else: raise ValueError('IRLS failed to stabilise')
    records.append({'id':curve['id'], 'parameters':dict(zip(['top','bottom','logIC50','hill'],map(float,p))), 'iterations':iteration+1, 'relativeRSS':float(sum(((y-predicted(p))/predicted(p))**2))})
out = {'about': {'method':'Fixed-weight IRLS with independent SciPy least_squares; positive fitted-response SD, no inference', 'scipy':scipy.__version__, 'inputSha256':hashlib.sha256(source.read_bytes()).hexdigest(), 'scriptSha256':hashlib.sha256(Path(__file__).read_bytes()).hexdigest()}, 'curves':records}
(root/'fixtures/relative-irls-reference.json').write_text(json.dumps(out,indent=2)+'\n')
