File size: 3,327 Bytes
3f98d52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
"""A synthetic engineering fixture. These data are not cellular measurements."""
from pathlib import Path
import numpy as np
import pandas as pd
from .chemistry import canonical_smiles, molecule_id, fingerprints
from .splits import make_splits
from .io import ResponseData, write_json
from .models import train
from .pipeline import calibrate, evaluate
from .plotting import plot_summary

def synthetic_data(seed=0, molecules=48, dimensions=8):
    rng = np.random.default_rng(seed)
    smiles = []
    for chain in range(1, 9):
        for ring in ["c1ccccc1", "c1ccncc1", "c1ccoc1", "c1ccsc1", "C1CCCCC1", "N1CCCCC1"]:
            smiles.append(canonical_smiles("C"*chain+ring))
    smiles = sorted(set(smiles))[:molecules]
    splits = make_splits(pd.DataFrame({"smiles": smiles}), seed, (.55,.1,.2,.15))
    fp = fingerprints(smiles)
    projection = rng.normal(size=(fp.shape[1], dimensions))/np.sqrt(fp.shape[1])
    effects = fp@projection
    rows, response, context, tv, cv = [], [], [], [], []
    for c in ["context_A", "context_B"]:
        baseline = rng.normal(size=dimensions)*.2
        for block in ["rep1", "rep2"]:
            ctrl = baseline+rng.normal(size=dimensions)*.015
            for m, s in enumerate(smiles):
                for dose in [.05,.5,5.]:
                    effect = effects[m]*(1+.25*np.log(dose))+.05*baseline
                    observed = effect+rng.normal(size=dimensions)*.02-(ctrl-baseline)
                    rows.append({"molecule_id": molecule_id(s), "smiles": s, "drug": f"synthetic_{m}",
                                 "dose_um": dose, "context": c, "block": block, "control_id": c+"|"+block,
                                 "split": splits.set_index("smiles").loc[s,"split"], "n_cells": 64,
                                 "n_control_cells": 128})
                    response.append(observed); context.append(ctrl)
                    tv.append(np.full(dimensions,.02**2)); cv.append(np.full(dimensions,.015**2))
    return ResponseData(pd.DataFrame(rows), np.array(response), np.array(context), np.array(tv),
                         np.array(cv), {"source": "synthetic engineering fixture", "feature_space_id": "synthetic-v1",
                                        "cell_features": {"representation": "synthetic", "dimensions": dimensions},
                                        "biological_data": False}).validate()

def run_demo(output, seed=0, max_queries=8):
    output = Path(output); output.mkdir(parents=True, exist_ok=True)
    data = synthetic_data(seed)
    data.save(output/"data")
    model = train(data, output/"model", ensemble=3, folds=3, seed=seed)
    cal = calibrate(data, model, output/"calibration", panel_molecules=5, scenarios=16, seed=seed)
    frame = evaluate(data, model, cal, output/"evaluation", scenarios=16, seed=seed,
                      max_queries=max_queries, methods=["mean","gibbs","marginal","finite-minimax","remedi"])
    plot_summary(output/"evaluation/summary.csv", output/"plots", title="Synthetic engineering check")
    write_json(output/"run.json", {"source": "synthetic", "biological_validation": False,
               "queries": int(frame.query_id.nunique()), "seed": seed,
               "purpose": "Verify serialization, grouped training, calibration, optimization, evaluation, and plots"})
    return output