Skip to content

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

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: TermInput.byName('has_allergy', {
patient: FeatureInput.string('Alice'),
allergen: FeatureInput.variable('?Allergen'),
}),
});
// If no allergy facts exist for Alice → 0 solutions
// Interpretation: "Alice has no allergies"

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-world
const result = await client.inference.backwardChain({
goal: TermInput.byName('has_allergy', {
patient: FeatureInput.string('Alice'),
allergen: FeatureInput.variable('?Allergen'),
}),
open_world: true,
});
// If no allergy facts exist for Alice → may still return solutions
// with evidence_ratio < 1.0 and residuated_sorts listing what's missing
// Interpretation: "We can't confirm or deny allergies — data is incomplete"

When to use which

ScenarioUseReason
Database-like queriesClosedYour data is complete — what’s stored is what exists
Medical diagnosisOpenAbsence of a symptom doesn’t mean the symptom doesn’t exist — it might not have been checked
Compliance checkingClosedIf a required document isn’t present, the check fails
Scientific researchOpenIncomplete data is the norm — you want to know what’s proven vs. unknown
Inventory lookupClosedIf an item isn’t in the system, it’s not in stock
Incident investigationOpenRoot cause analysis shouldn’t dismiss possibilities just because some data is missing

How open-world reasoning works in practice

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: TermInput.byName('diagnosis', {
condition: FeatureInput.variable('?Condition'),
}),
open_world: true,
});
for (const solution of result.solutions) {
console.log(`Condition: ${solution.substitution.bindings[0]?.bound_to_display}`);
console.log(`Evidence ratio: ${solution.evidence_ratio}`);
// 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

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.residuated_sorts?.length) {
console.log('Missing information about:', solution.residuated_sorts);
// 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

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:

  1. What was proved (the bindings and certainty)
  2. What couldn’t be proved (the residuated sorts)
  3. How strong the evidence is (the evidence ratio)

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 > 50
await client.inference.addRule({
term: TermInput.byName('at_risk', {
patient: FeatureInput.variable('?Patient'),
}),
antecedents: [
TermInput.byName('blood_pressure', {
patient: FeatureInput.variable('?Patient'),
level: FeatureInput.string('high'),
}),
TermInput.byName('family_history', {
patient: FeatureInput.variable('?Patient'),
condition: FeatureInput.string('heart_disease'),
}),
TermInput.byName('patient_info', {
patient: FeatureInput.variable('?Patient'),
age: FeatureInput.constrainedVar('?Age', guard('gt', 50)),
}),
],
});
// We only know Alice has high blood pressure and is 65
await client.inference.addFact({
term: TermInput.byName('blood_pressure', {
patient: FeatureInput.string('Alice'),
level: FeatureInput.string('high'),
}),
});
await client.inference.addFact({
term: TermInput.byName('patient_info', {
patient: FeatureInput.string('Alice'),
age: FeatureInput.integer(65),
}),
});
// Note: no family_history fact for Alice

Closed-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 evidence_ratio: 0.67 and residuated_sorts: ["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.”