Skip to main content
Docs

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

    Tools · Custom Modeling

    Reaction Models

    Train models that predict a reaction's yield, selectivity, activation barrier or rate constant from atom-mapped reactions and the conditions they ran under.

    Updated October 12, 2026

    On this page

    Prices, workflows, and method papersOpen in the app

    Reaction Models learns a property of a reaction rather than of a molecule: a yield, an enantiomeric or diastereomeric ratio, an activation barrier or a rate constant. Each reaction is read as a condensed graph of reaction, which combines the reactants and products atom by atom, together with the molecules that change between experiments, such as the ligand, base, additive or solvent.

    Yield models work best on consistent in-house data, such as a high-throughput experimentation plate run under one protocol. Yields pooled from the literature mix different scales, purities and work-ups, and give weak models.

    Inputs

    A table with one atom-mapped reaction per row and its measured value. Map the reactions first with the Reaction Atom Mapper: its mapped table keeps your other columns (yield, ligand, plate position), so it can be used for training as it is. Rows that are not atom-mapped are skipped, rows the mapper rejected are dropped, and low-confidence mappings are excluded unless you keep them. Up to 10,000 reactions.

    ColumnRole
    ReactionAtom-mapped reaction SMILES (mapped_reaction from the mapper)
    TargetThe measured value: yield, ee, a barrier, log k (or a class)
    ConditionsUp to six columns of molecules that vary between reactions (SMILES; an empty cell means none)
    Group (optional)Keeps every reaction with the same value in one fold, for a leave-one-ligand-out test

    Molecules on the left of the reaction that contribute no atoms to the product (reagents written into the reaction) are left out of the graph; give them as condition columns instead.

    How the model is checked

    The model is trained like MPNN Models: a validation set picks the saved checkpoint, and a held-out test set is scored against a mean and a nearest-neighbour baseline. Splits can be random, by group (the honest test for a plate: hold out a ligand or an additive the model never saw), by reaction template or by product scaffold. Ensembles add an uncertainty to every prediction, regression models carry a 90% prediction interval, and each prediction reports how similar the reaction and its conditions are to the training data.

    Set target bounds (for example 0 and 100 for a yield in percent) to keep predictions and intervals inside the possible range.

    Predictions

    Predict up to 1,000 reactions at a time on the tool page or through the API, giving the same condition columns the model was trained with. Score larger sets, or reactions enumerated from a template, in a workflow with Saved Model Scoring: the workflow templates Train a reaction yield model from your reactions and Enumerate products and keep the reactions your model predicts best chain the mapper, training and scoring.

    Outputs

    FileContents
    model/The saved model with its model card: test metrics, baselines, the split, the conditions it reads and the mapping counts (no pickled code)
    training_summary.jsonReactions used and skipped

    The saved model appears in My Models.

    Map and check reactions with the Reaction Atom Mapper, enumerate products with Reaction Enumeration, or model molecules instead with MPNN Models and QSAR Models.

    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.

    Reaction Model Training train-reaction-mpnn

    Job type
    train-reaction-mpnn
    Hardware
    cpu (default)t4l4a10
    Typical runtime
    1 h on CPU

    Payload

    Payload fields
    FieldTypeDescription
    model_namerequiredstring

    Limits: min length 1

    reactions[]string[]

    Limits: min items 1, max items 10000

    componentsmap<string, string[]>
    taskstring

    Default: "regression"One of: "regression", "binary", "multiclass"

    binary_thresholdnumber
    targets[](number | string | (number | null)[])[]

    Limits: min items 1

    target_names[]string[]

    Limits: min items 1, max items 20

    target_units[]string[]

    Limits: min items 1, max items 20

    target_bounds[]number[]

    Limits: min items 2, max items 2

    splits[]string[]

    One of: "train", "training", "val", "valid", "validation", "test"Limits: min items 1

    groups[]string[]

    Limits: min items 1

    reaction_tablestring | object[] | artifactRef
    reaction_columnstring

    Default: "mapped_reaction"Limits: min length 1

    component_columns[]string[]

    Limits: max items 6

    target_columnstring

    Default: "yield"Limits: min length 1

    target_columns[]string[]

    Limits: min items 1, max items 20

    split_columnstring

    Limits: min length 1

    group_columnstring

    Limits: min length 1

    low_confidence_policystring

    Default: "exclude"One of: "exclude", "keep"

    mapping_summarymap<string, integer>
    unitstring
    model_descriptionstring
    tags[]string[]
    model_paramsobject

    Example

    from cognichem_client import CogniChem
    
    client = CogniChem.from_env()  # reads COGNICHEM_API_KEY
    payload = {
        "model_name": "esterification-yield",
        "reactions": [
            "[CH3:1][C:2](=[O:3])[OH:4].[CH3:5][OH:6]>>[CH3:1][C:2](=[O:3])[O:6][CH3:5].[OH2:4]",
            "[CH3:1][C:2](=[O:3])[OH:4].[CH3:5][CH2:7][OH:6]>>[CH3:1][C:2](=[O:3])[O:6][CH2:7][CH3:5].[OH2:4]",
            "[CH3:1][C:2](=[O:3])[OH:4].[CH3:5][CH2:7][CH2:8][OH:6]>>[CH3:1][C:2](=[O:3])[O:6][CH2:7][CH2:8][CH3:5].[OH2:4]",
            "[CH3:1][C:2](=[O:3])[OH:4].[CH3:5][CH:7]([CH3:8])[OH:6]>>[CH3:1][C:2](=[O:3])[O:6][CH:7]([CH3:8])[CH3:5].[OH2:4]",
            "[cH:1]1[cH:9][cH:10][cH:11][cH:12][c:13]1[C:2](=[O:3])[OH:4].[CH3:5][OH:6]>>[cH:1]1[cH:9][cH:10][cH:11][cH:12][c:13]1[C:2](=[O:3])[O:6][CH3:5].[OH2:4]",
            "… 3 more",
        ],
        "components": {
            "catalyst": [
                "OS(=O)(=O)O",
                "",
                "Cc1ccc(S(=O)(=O)O)cc1",
                "OS(=O)(=O)O",
                "OS(=O)(=O)O",
                "… 3 more",
            ],
        },
        "targets": [72, 65.5, 58, 31, 81, "… 3 more"],
        "unit": "%",
        "target_bounds": [0, 100],
        "model_params": {
            "validation_size": 0.25,
            "test_size": 0,
            "mp_hidden_dim": 32,
            "mp_depth": 1,
            "n_ffn_layers": 1,
            "ffn_hidden_dim": 32,
            "learning_rate": 0.001,
            "n_epochs": 6,
            "batch_size": 2,
        },
    }
    
    estimate = client.jobs.estimate(job_type="train-reaction-mpnn", payload=payload, resource="cpu")
    print(f"Reserves ${estimate.cost:.2f}")
    
    job = client.jobs.submit(
        job_name="my-train-reaction-mpnn-run",
        job_type="train-reaction-mpnn",
        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

    • Table (list)CSV, TSV, JSON

    Workflow outputs

    • ArchiveZIP
    • Trained modelOPAQUE