Open-World vs Closed-World Reasoning
When the inference engine cannot prove something, what does that mean? The answer depends on whether you’re using closed-world or open-world reasoning — and choosing the right one is critical for getting correct results.
The core distinction
Section titled “The core distinction”Closed-world assumption (default)
Section titled “Closed-world assumption (default)”“If I can’t prove it, it’s false.”
Under closed-world reasoning, the knowledge base is assumed to be complete. If a fact isn’t present, it’s treated as definitively not true. This is how SQL databases and most programming languages work — if a row doesn’t exist, the query returns no results.
// Closed-world (default)const result = await client.inference.backwardChain({ goal: psi('has_allergy', { patient: 'Alice', allergen: '?Allergen', }),});// If no allergy facts exist for Alice → 0 solutions// Interpretation: "Alice has no allergies"Open-world assumption
Section titled “Open-world assumption”“If I can’t prove it, it’s unknown.”
Under open-world reasoning, the knowledge base is assumed to be incomplete. Missing information doesn’t mean “false” — it means “we don’t know yet.” The engine tracks what information is missing and reports it alongside whatever it can prove.
// Open-worldconst result = await client.inference.backwardChain({ goal: psi('has_allergy', { patient: 'Alice', allergen: '?Allergen', }), openWorld: true,});// If no allergy facts exist for Alice → may still return solutions// with evidenceRatio < 1.0 and residuatedSorts listing what's missing// Interpretation: "We can't confirm or deny allergies — data is incomplete"When to use which
Section titled “When to use which”| Scenario | Use | Reason |
|---|---|---|
| Database-like queries | Closed | Your data is complete — what’s stored is what exists |
| Medical diagnosis | Open | Absence of a symptom doesn’t mean the symptom doesn’t exist — it might not have been checked |
| Compliance checking | Closed | If a required document isn’t present, the check fails |
| Scientific research | Open | Incomplete data is the norm — you want to know what’s proven vs. unknown |
| Inventory lookup | Closed | If an item isn’t in the system, it’s not in stock |
| Incident investigation | Open | Root cause analysis shouldn’t dismiss possibilities just because some data is missing |
How open-world reasoning works in practice
Section titled “How open-world reasoning works in practice”Evidence ratio
Section titled “Evidence ratio”Open-world solutions include an evidence ratio — the fraction of a rule’s antecedents that were actually matched. This tells you how much evidence supports the conclusion.
const result = await client.inference.backwardChain({ goal: psi('diagnosis', { condition: '?Condition', }), openWorld: true,});
for (const solution of result.solutions) { console.log(`Condition: ${solution.substitution.bindings[0]?.boundToDisplay}`); console.log(`Evidence ratio: ${solution.evidenceRatio}`); // 1.0 = all antecedents matched (fully proven) // 0.75 = 3 of 4 antecedents matched (partially proven) // 0.5 = half the evidence is missing}Residuated sorts
Section titled “Residuated sorts”When the engine encounters a sort it can’t resolve (no matching facts), it residuates — suspends that part of the proof and reports which sorts couldn’t be resolved.
for (const solution of result.solutions) { if (solution.residuatedSorts?.length) { console.log('Missing information about:', solution.residuatedSorts); // e.g., ["blood_test", "imaging_result"] // These sorts had no matching facts — the proof is incomplete }}This is directly connected to the concept of residuation in Psi-terms — the system’s ability to say “I cannot determine this yet” rather than failing outright.
Connection to residuation
Section titled “Connection to residuation”Residuation is the mechanism that makes open-world reasoning possible. When the engine encounters missing information:
- In closed-world: it treats the missing info as “false” and the proof fails at that branch
- In open-world: it residuates — suspends that branch, records what’s missing, and continues with whatever can be proved
This means open-world solutions are partial proofs. They tell you:
- What was proved (the bindings and certainty)
- What couldn’t be proved (the residuated sorts)
- How strong the evidence is (the evidence ratio)
Practical example
Section titled “Practical example”Consider a rule: “A patient is at risk if they have high blood pressure AND a family history of heart disease AND are over 50.”
// Rule: at_risk :- high_bp AND family_history AND age > 50await client.inference.addRule({ term: psi('at_risk', { patient: '?Patient', }), antecedents: [ psi('blood_pressure', { patient: '?Patient', level: 'high', }), psi('family_history', { patient: '?Patient', condition: 'heart_disease', }), psi('patient_info', { patient: '?Patient', age: constrained('?Age', guard('gt', 50)), }), ],});
// We only know Alice has high blood pressure and is 65await client.inference.addFact({ term: psi('blood_pressure', { patient: 'Alice', level: 'high', }),});await client.inference.addFact({ term: psi('patient_info', { patient: 'Alice', age: 65, }),});// Note: no family_history fact for AliceClosed-world result: 0 solutions. Alice is not at risk (because family history isn’t recorded, which is treated as “no family history”).
Open-world result: 1 solution with evidenceRatio: 0.67 and residuatedSorts: ["family_history"]. Alice might be at risk — 2 of 3 conditions are met, but family history data is missing.
The open-world result is much more useful for a clinical application — it flags the incomplete data rather than silently concluding “no risk.”