Skip to main content
Docs

Search guides and API endpoints, for example “Idempotency-Key” or “submit job”.

    Tools · Custom Modeling

    Active Learning

    Pick the next batch of compounds to make from your measurements and a candidate pool, balancing predicted value, uncertainty and diversity.

    Updated October 15, 2026

    On this page

    Prices, workflows, and method papersOpen in the app

    Active Learning answers "which of these candidates should we make next?". You give it the compounds you have measured and a pool of candidates, such as an enumerated library, Free-Wilson proposals or matched-pair analogs. A surrogate model predicts each candidate's value and how uncertain that prediction is. An acquisition function turns the two into a score, and a diversity step spreads the batch across chemical space. Each design–make–test–learn round is a new run: add the new measurements and run it again.

    The surrogate

    • Gaussian process (default). Fitted to your measurements every round, with a Tanimoto (MinMax) kernel on Morgan count fingerprints. It reports both a prediction and a standard deviation, and the round summary includes its cross-validated accuracy on your data.
    • A saved model. An MPNN or QSAR model from MPNN Models or QSAR Models is used as trained. Acquisitions other than greedy need a model that reports uncertainty: an ensemble, an uncertainty head, a random forest, kNN or Gaussian process.

    Acquisition

    AcquisitionPicks
    Expected improvement (EI)The largest expected gain over your best measurement
    Upper confidence bound (UCB)The best prediction (closest to a target range) plus a multiple of its uncertainty
    Probability of improvement (PI)The best chance of beating your best measurement, or of landing in a target range
    Thompson samplingThe best values in a draw from the surrogate
    GreedyThe highest prediction, ignoring uncertainty

    You can maximize, minimize, or aim for a target range. Compounds you have already measured are never suggested again, matched by InChIKey, even when their value is censored and left out of the fit.

    Diversity

    • MaxMin: among the best-scoring candidates, start from the best and keep adding the one farthest from every pick so far.
    • Cluster: cluster the best-scoring candidates, then take each cluster's best in turn.
    • Kriging believer (Gaussian process only): take the best, pretend it has been measured at its predicted value, rescore, repeat.
    • None: the top scores.

    Outputs

    A ranked table of suggestions with each pick's predicted value and uncertainty, acquisition score, nearest measured compound and cluster. You also get the picks as SMILES for docking or other workflow steps, every candidate's score, and a round summary with the surrogate's accuracy and your best value so far.

    Limits

    • One objective per run. Apply constraints such as ADMET thresholds with a filter step before the candidates reach this tool.
    • The Gaussian process uses at most a few thousand measurements, and the candidate pool is capped. The exact limits are in the reference below.
    • Suggestions are a ranking from a model, not measurements.
    • Thompson Sampling searches combinatorial reagent spaces too large to enumerate, with a fixed evaluator. Use Active Learning when you already have the candidate list and want the model to learn from each round's results.
    • Free Wilson Analysis proposes R-group combinations you have not made. The ready-made workflow Propose R-group combinations with Free-Wilson, then pick the next batch to make chains it into Active Learning.

    Run it from the API

    Submit with Submit a job and the job_type below. Price it first with Estimate job reservation cost: submitting reserves that amount from your wallet, and the charge settles at the actual runtime.

    Active Learning active-learning

    Job type
    active-learning
    Hardware
    cpu (default)
    Typical runtime
    5 min on CPU

    Payload

    Provide exactly one of: candidates_data, candidates_table.

    Provide at least one of: measured_table, measured_smiles + measured_values, model_id.

    Payload fields
    FieldTypeDescription
    measured_tablestring | object[] | artifactRef
    smiles_columnstring

    Default: "smiles"Limits: min length 1

    value_columnstring

    Default: "p_activity"Limits: min length 1

    measured_smiles[]string[]

    Limits: min items 1, max items 5000

    measured_values[]number[]

    Limits: min items 1, max items 5000

    measured_censored[]boolean[]

    Limits: min items 1, max items 5000

    censored_policystring

    Default: "drop"One of: "drop", "as_threshold"

    model_idstring

    Limits: pattern ^mdl-[0-9A-HJKMNP-TV-Z]{26}$

    score_targetstring

    Limits: min length 1, max length 200

    score_classstring | integer
    candidates_data[](string | artifactRef)[]

    Limits: min items 1, max items 10000

    candidates_tablestring | object[] | artifactRef
    candidates_smiles_columnstring

    Default: "smiles"Limits: min length 1

    objectiveobject
    objective.directionstring

    Default: "maximize"One of: "maximize", "minimize", "target"

    objective.lownumber
    objective.highnumber
    batch_sizeinteger

    Default: 12Limits: ≥ 1, ≤ 96

    acquisitionstring

    Default: "ucb"One of: "greedy", "ucb", "ei", "pi", "thompson"

    ucb_betanumber

    Default: 2Limits: ≥ 0, ≤ 100

    xinumber

    Default: 0.01Limits: ≥ 0

    diversitystring

    One of: "none", "maxmin", "cluster", "kriging_believer"

    cluster_cutoffnumber

    Default: 0.4Limits: > 0, ≤ 1

    campaignstring

    Limits: min length 1, max length 100

    roundinteger

    Default: 1Limits: ≥ 1, ≤ 10000

    seedinteger

    Default: 0Limits: ≥ 0, ≤ 2147483647

    Example

    from cognichem_client import CogniChem
    
    client = CogniChem.from_env()  # reads COGNICHEM_API_KEY
    payload = {
        "measured_table": [
            {"smiles": "CCO", "logS": -0.2},
            {"smiles": "CCCO", "logS": -0.5},
            {"smiles": "CCCCO", "logS": -0.9},
            {"smiles": "CCCCCO", "logS": -1.4},
            {"smiles": "c1ccccc1", "logS": -1.6},
            "… 7 more",
        ],
        "value_column": "logS",
        "candidates_data": [
            "CCCCCCO",
            "Cc1ccccc1O",
            "Brc1ccccc1",
            "OCCO",
            "CC(C)(C)O",
            "… 6 more",
        ],
        "objective": {"direction": "maximize"},
        "batch_size": 4,
        "acquisition": "ucb",
        "ucb_beta": 2,
        "diversity": "kriging_believer",
        "campaign": "fixture",
        "round": 2,
        "seed": 0,
    }
    
    estimate = client.jobs.estimate(job_type="active-learning", payload=payload, resource="cpu")
    print(f"Reserves ${estimate.cost:.2f}")
    
    job = client.jobs.submit(
        job_name="my-active-learning-run",
        job_type="active-learning",
        payload=payload,
        resource="cpu",
    )
    status = client.jobs.wait(job.process_id)
    if status.status == "completed":
        client.jobs.result(job.process_id, save_path=".")

    Sample data from the job catalog; long values are shortened here. Each job_name must be unique among your jobs.

    Workflow inputs

    • Molecules (list) · optionalSMILES
    • Table (list) · optionalCSV, TSV, JSON
    • Table (list) · optionalCSV, TSV, JSON
    • Trained model · optionalOPAQUE

    Workflow outputs

    • ArchiveZIP
    • MoleculesSMILES
    • TableCSV