Evaluation and explanation#

The fg.eval namespace, its result envelope (EvaluateResult / EvaluateRow), and the explanation surface (Explanation and the EvidenceGraph it carries).

fg.eval is a read-only namespace on a FactGraph / SDKStore instance. Assigning to it raises FrozenSnapshotError. It never writes to the ledger.

fg.eval.evaluate(rule_or_expr, *, head, engine=None, config=None) -> EvaluateResult#

Evaluates an application Rule or a RuleExpr against the current visible view and returns an EvaluateResult envelope. (The same method also accepts an Inference object or a structured derivation dict; those legacy derivation inputs are not covered here.)

Parameters:

  • rule_or_expr (positional, required): a single application Rule or RuleExpr. Exactly one positional input is accepted. A str first argument raises SDKStoreError (“string derivation DSL is not supported in SDK v1”). For a Rule/RuleExpr input, head= is required.
  • head (keyword, required for Rule/RuleExpr input): the closing application Rule. Must be an instance of the application protocol Rule (for example Rule.projection("user")). If absent, raises SDKStoreError (“evaluate(rule_expr, …) requires head= Rule”). If not a Rule, raises SDKStoreError (“evaluate(rule_expr, …) head= must be Rule; …”).
  • engine (keyword, default None): one of "native", "problog", "pyreason", "souffle". None resolves to "native". Any other value raises SDKStoreError (“engine= must be one of: native, problog, pyreason, souffle”).
  • config (keyword, default None): a SemanticsProfile, or an SDK public semantics object (ProbLogConfig, PyReasonConfig). Only the "problog" and "pyreason" engines consume semantics; pairing a profile with another engine raises SDKStoreError (“engine=’…’ does not consume SemanticsProfile”). A SemanticsProfile whose engine does not match the resolved engine raises SDKStoreError.

Returns: EvaluateResult.

Raises: SDKStoreError for unsupported keywords. view=, policy=, semantics_profile=, mode=, temporal_view=, registry=, and engine_options= are each rejected with a specific message (for example view= directs you to FactGraph.attach(db, view=view); see persistence.md). Any other unexpected keyword raises SDKStoreError (“unknown evaluate(rule_expr, …) keyword(s): …”). RuleExprError is raised when the head foundation is invalid.

Example:

result = fg.eval.evaluate(rule, head=rule, engine="native")
row = result[0]

fg.eval.explain(rule_or_expr, *, head, engine=None, config=None) -> Explanation#

Replays a closed-head evaluation and returns an Explanation. The first matching result row is explained; if no row matches the closed head, a "failed" explanation is returned.

Parameters:

  • rule_or_expr (positional, required): a single RuleExpr or application Rule. Exactly one positional input; otherwise raises SDKStoreError (“eval.explain(expr, …) accepts exactly one RuleExpr or Rule input”).
  • head (keyword, required): a closed application Rule. Required (SDKStoreError “eval.explain(expr, …) requires head= closed Rule” when absent). Must be a Rule (SDKStoreError “eval.explain(…) head= must be Rule”). If the head has unbound ports, raises RuleExprError (“manual explain head must be closed; unbound ports: …”).
  • engine (keyword, default None): same accepted values as evaluate.
  • config (keyword, default None): same accepted values as evaluate.

Returns: Explanation. When a matching row is found, the explanation mirrors the row’s own explain() result. When no row matches, Explanation(status="failed", failure_class="closed_head_false", ...) is returned with row=None.

Raises: SDKStoreError for view= (directs to FactGraph.attach) and for any other unknown keyword (“unknown eval.explain(…) keyword(s): …”). RuleExprError when head is not closed.

Example:

explanation = fg.eval.explain(rule, head=closed_head, engine="native")
print("\n".join(explanation.narrate()))

For the cell-level fg.audit.explain (chosen-policy state for a stored assertion), see audit.md. Choosing between engines is covered in engines-and-semantics.md.

EvaluateResult#

Frozen dataclass envelope returned by fg.eval.evaluate. It holds the result rows and a deterministic fingerprint of the evaluation.

Fields:

  • result_id: str: identifier prefixed evalr_v1:.
  • rows: tuple[EvaluateRow, ...]: the result rows. Row ids are unique within a result; duplicates raise ProtocolShapeError.
  • head: Rule: the closing application protocol Rule.
  • engine: str: the engine that produced the result.
  • evaluated_at: object: evaluation timestamp.
  • fingerprint: ResultFingerprint: the digests identifying this evaluation (see below).
  • engine_meta: Mapping[str, Any]: engine and adapter metadata.

Methods and operators:

  • first() -> EvaluateRow | None: the first row, or None if there are no rows.
  • exists() -> bool: True if there is at least one row.
  • count() -> int: number of rows.
  • __iter__() -> Iterator[EvaluateRow]: iterate the rows (for row in result).
  • __len__() -> int: len(result), equal to count().
  • __getitem__(index: int) -> EvaluateRow: index access (result[0]).

Deprecated properties (each emits a DeprecationWarning and forwards to fingerprint or engine_meta): run_id, engine_version, adapter_version, expr_digest, rule_set_digest, view_snapshot_digest, config_digest, result_digest. Read these from result.fingerprint.* and result.engine_meta[...] instead.

Example:

result = fg.eval.evaluate(expr, head=head, engine="native")
if result.exists():
    print(result.count(), "rows")
    for row in result:
        ...

ResultFingerprint#

Frozen dataclass of the digests that identify an evaluation. Carried by EvaluateResult.fingerprint. All digest fields are sha256:-prefixed tokens; run_id is run_v1:-prefixed.

Fields:

  • expr_digest: str
  • rule_set_digest: str
  • view_snapshot_digest: str
  • config_digest: str | None: None when no semantics profile was applied.
  • result_digest: str
  • run_id: str

EvaluateRow#

Frozen dataclass for one row of an EvaluateResult. Rows are bound to their owning result; calling a live-only method on a detached row raises DetachedRowError.

Fields:

  • row_id: str: non-empty row identifier.
  • bindings: Mapping[str, Any]: the variable bindings for this row (frozen mapping).
  • kind: ClaimKind: one of "fact_triple", "rule_head", "aggregate_result", "projection".
  • digest: str: sha256:-prefixed row digest.
  • closed_head_digest: str: sha256:-prefixed digest of the closed head.
  • certainty: Certainty | None: the row’s certainty, or None.

Methods:

  • explain() -> Explanation: explain this row against its owning result. Returns Explanation(status="passed", evidence=..., row=self, ...) on success. If the row is not present in the current result, returns status="unsupported" with error code ROW_NOT_IN_RESULT; if the row no longer matches the result anchor, status="unsupported" with code STALE_ROW; if the evidence graph fails validation, status="unsupported" with code GRAPH_VALIDATION_FAILED. Raises DetachedRowError if the row has no owning result.
  • close() -> Rule: build a closed application Rule from this row’s bindings. Raises DetachedRowError if the row is detached, stale, or does not belong to its result.

Example:

row = result.first()
if row is not None:
    print(row.bindings)
    explanation = row.explain()

Certainty#

Frozen dataclass attached to a row or evidence node, expressing a confidence interval.

Fields:

  • lo: float: lower bound, in [0.0, 1.0].
  • hi: float: upper bound, in [0.0, 1.0].
  • kind: CertaintyKind: one of "boolean", "probabilistic", "possibilistic". Default "boolean".

Raises: ProtocolShapeError if kind is not one of the three kinds, if a bound is not a finite number in [0.0, 1.0], or if lo > hi.

The module constant BOOLEAN_CERTAINTY is Certainty(1.0, 1.0, "boolean").

Explanation#

Frozen dataclass returned by fg.eval.explain and EvaluateRow.explain. It carries the outcome status, the evidence graph (when present), and the rendered output accessors.

Fields:

  • status: ExplanationStatus: one of "passed", "failed", "unsupported", "invalid_request". evidence is non-None if and only if status is "passed" or "failed".
  • evidence: EvidenceGraph | None: the evidence graph (see below). None for "unsupported" and "invalid_request".
  • row: EvaluateRow | None: the explained row, or None. Required when status is "passed".
  • result_id: str | None: evalr_v1:-prefixed result id; required (non-empty) when status is "passed" or "failed".
  • failure_class: ExplanationFailureClass | None: one of "no_matching_row", "closed_head_false", "stale_row", "row_not_in_result", "insufficient_closed_bindings". Required when status is "failed"; must be None otherwise.
  • checked_scope: Mapping[str, Any] | None: the digests checked during the replay (frozen mapping when provided).
  • suggested_next_steps: tuple[str, ...]: default ().
  • errors: tuple[ErrorDTO, ...]: default (). Non-empty when status is "unsupported" or "invalid_request".
  • warnings: tuple[WarningDTO, ...]: default ().

Rendered output:

  • repr -> tuple[str, ...] | None (property): deterministic structured lines walked from the evidence graph. Returns None when status is "unsupported" or "invalid_request". Cached after first access.
  • narrate() -> tuple[str, ...] | None: a human-readable narrative of the evidence graph. Returns None when status is "unsupported" or "invalid_request". Cached after first access.

Raises: ProtocolShapeError on construction when the field invariants above are violated (for example a "passed" status without a row, a "failed" status without a failure_class, or an "unsupported" / "invalid_request" status with an empty errors tuple).

Example:

explanation = row.explain()
lines = explanation.narrate()
if lines is not None:
    print("\n".join(lines))

EvidenceGraph#

Frozen dataclass carried by Explanation.evidence. It is a paths-model graph: each path is an EvidenceTree (or EvidenceTimeline) and each tree holds rules, which hold atoms with per-atom verdicts.

Fields:

  • graph_id: str
  • engine: str: the engine that produced the evidence.
  • layout_hint: LayoutHint: one of "tree" (constant LAYOUT_TREE) or "timeline" (constant LAYOUT_TIMELINE).
  • subject_binding: Mapping[str, Any]: frozen mapping of the subject’s bindings.
  • paths: tuple[EvidenceTree | EvidenceTimeline, ...]
  • certainty: Certainty | None: default BOOLEAN_CERTAINTY.
  • metadata: Mapping[str, Any]: frozen mapping; default empty.

Example:

evidence = explanation.evidence
for path in evidence.paths:
    for rule in path.rules:
        for atom in rule.atoms:
            print(rule.rule_id, atom.repr_text, atom.verdict)

EvidenceTree#

Frozen dataclass for one path in EvidenceGraph.paths.

Fields:

  • tree_id: str
  • status: TreeStatus: one of "holds", "fails", "not_reached".
  • rules: tuple[EvidenceRule, ...]
  • joins: tuple[EvidenceJoin, ...]: default ().
  • certainty: Certainty | None: default BOOLEAN_CERTAINTY.
  • metadata: Mapping[str, Any]: frozen mapping; default empty.

EvidenceRule#

Frozen dataclass for a rule occurrence within an EvidenceTree.

Fields:

  • occurrence_alias: str
  • rule_id: str
  • role: RuleRole: one of "head", "body".
  • status: TreeStatus: one of "holds", "fails", "not_reached".
  • repr_text: str | None: default None.
  • ports: Mapping[str, Any]: frozen mapping; default empty.
  • atoms: tuple[EvidenceAtom, ...]: default ().

EvidenceAtom#

Frozen dataclass for a single atom within an EvidenceRule, with its verdict.

Fields:

  • form: AtomForm: one of Fact, Compare, Builtin, Aggregate.
  • verdict: Verdict: one of Holds, Fails, NotReached (see below).
  • atom_id: str
  • repr_text: str | None: default None.
  • negated: bool: default False.
  • timestep: int | None: default None.

Verdict types (Verdict = Holds | Fails | NotReached):

  • Holds: fields certainty: Certainty (default BOOLEAN_CERTAINTY), support: tuple[Source, ...] (default ()).
  • Fails: field certainty: Certainty (default BOOLEAN_CERTAINTY).
  • NotReached: field blocked_by: str | None (default None).

Each Source (in Holds.support) has fields ref: str, field: str | None, value: Any (default None), and meta: Mapping[str, Any] (frozen mapping; default empty), tracing the supporting fact back to the ledger. For ledger-level provenance and audit, see audit.md and the explanation in reasoning-evaluation-and-evidence.md.

Legacy surface#

fg.eval.evaluate_candidates (raw CandidateSet output) and CandidateSet are part of the legacy candidate/derivation flow and are not covered here.

comments powered by Disqus