Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Drug-interactions walkthrough

A worked safety knowledge base: pharmacokinetic drug-drug interaction (DDI) reasoning — “should this co-prescription raise a safety alert?” — decided by deduction over an explicit mechanism instead of a statistical guess.

Source of truth: drug-interactions.nibli at the repo root. Every verdict quoted below is pinned by the ddi_* regression tests in nibli-engine/tests/integration.rs. The same corpus ships as the playground preset “Drug interactions (Ch 20)” — an example KB, not a chapter of any third-party book.

The scenario

The warfarin + fluconazole interaction, mediated by the CYP2C9 enzyme: fluconazole inhibits CYP2C9; warfarin (narrow therapeutic index) is metabolised by CYP2C9, so its concentration rises → toxicity risk → safety alert. Apixaban is the negative control: metabolised by CYP3A4, which fluconazole does not inhibit → no alert, as a real deduced FALSE. Phenytoin is the second control: pharmacologically at risk, but not on the patient’s chart.

The corpus has no native pharmacology vocabulary, so it maps onto the nearest committed relations and discloses the mapping in its header:

PredicateReads as
chemical(d)d is a drug
uses(p, d)patient p takes drug d
prevents(d, e)drug d inhibits enzyme e
metabolized_by(d, e)drug d is metabolised by enzyme e
thin(d)narrow therapeutic index
increases(d)blood concentration is raised
dangerous(d)at toxicity risk
warns(d)warrants a safety alert

Enzymes are opaque rigid Names: Siptucin = CYP2C9, Sipcivon = CYP3A4.

The three-step mechanism

Step 1 — concentration rise. Grounded conditionals per affected substrate (the fully general join rule all $a, $b, $e: prevents($a,$e) & metabolized_by($b,$e) -> increases($b). also compiles and reasons correctly — the grounded form is an encoding choice, not a limitation):

prevents(Flukonazol, Siptucin) & metabolized_by(Varfarin, Siptucin) -> increases(Varfarin).

Step 2 — toxicity risk. One general rule with a conjunctive restrictor; both conditions are required:

dangerous(every chemical where increases where thin).

A wide-margin drug whose concentration rises is not flagged, and a narrow-index drug with no interaction is not flagged (both negative controls are pinned by ddi_toxicity_requires_both_conditions).

Step 3 — the alert is patient-gated. Risk is drug-level pharmacology; the actionable alert only fires for an at-risk drug this patient actually takes:

all $da: dangerous($da) & uses(Adam, $da) -> warns($da).

Load it in the REPL

:load drug-interactions.nibli
[Load] Done: 16 asserted, 78 skipped, 0 errors

Two fact ids matter for the belief-revision demos below (assigned in file order, pinned by ddi_corpus_transcript_pins): the inhibition fact prevents(Flukonazol, Siptucin). is #4, and the regimen fact uses(Adam, Varfarin). is #10.

Engine-checked queries

ClaimVerdictWhy
? increases(Varfarin).TRUEStep 1: inhibited enzyme + substrate
? dangerous(Varfarin).TRUEStep 2: raised concentration + narrow index
? warns(Varfarin).TRUEStep 3: at risk and on Adam’s chart — a 3-hop proof
? increases(Apiksaban).FALSECYP3A4 is not inhibited by fluconazole
? warns(Apiksaban).FALSEThe negative control: a deduced FALSE, not unknown
? increases(Fenitoin).TRUESame shared inhibitor, same general rules
? dangerous(Fenitoin).TRUERisk is drug-level — no per-drug rule needed
? warns(Fenitoin).FALSEBut Adam does not take it: the regimen gate

The phenytoin pair is the point of step 3: pharmacological risk is general, the actionable alert is patient-specific.

Witness extraction (??) enumerates bindings instead of checking one claim — “which drugs are CYP2C9 substrates?”:

?? metabolized_by($da, Siptucin).

lists warfarin and phenytoin as witnesses for $da; apixaban (a CYP3A4 substrate) does not appear (pinned by ddi_witness_cyp2c9_substrates).

Belief revision: two clinical moves

Alerts are never baked in — they are re-derived from current facts, so the two canonical chart edits are single retractions (see Belief revision):

Discontinue the inhibitor (retract prevents(Flukonazol, Siptucin)., #4): the mechanism’s entry premise disappears, so the concentration rise, the toxicity risk, and the alert all dissolve in one step — for both substrates, since they share the inhibitor:

:retract 4
[Retract] Fact #4 retracted. KB rebuilt.

? warns(Varfarin).
[Query] FALSE

? dangerous(Fenitoin).
[Query] FALSE

Discontinue the drug (retract uses(Adam, Varfarin)., #10): the alert is withdrawn while the drug-level risk stays derivable — the alert is gated on the regimen, the risk is not:

:retract 10
[Retract] Fact #10 retracted. KB rebuilt.

? dangerous(Varfarin).
[Query] TRUE

? warns(Varfarin).
[Query] FALSE

Both moves are pinned by ddi_belief_revision_discontinue_inhibitor and ddi_belief_revision_discontinue_drug.

Try it in the playground

Select “Drug interactions (Ch 20)” in the playground header dropdown. Its presets are the headline chain plus the negative control: concentration rising? · toxicity risk? · safety alert?—a 3-hop proof · negative control—no alert. Proofs render with the curated pharmacology overlay (“fluconazole inhibits CYP2C9”, “warfarin is at toxicity risk”), never a bare variable or a raw transliterated name.