Engines and semantics#
The engine= and config= options of fg.eval.evaluate(...) and fg.eval.explain(...), the supported engine names, the public semantics wrappers ProbLogConfig and PyReasonConfig, the canonical SemanticsProfile, and how certainty is represented per engine.
engine= option#
Selects the runtime adapter that executes a rule, RuleExpr, or inference. Passed as a keyword to fg.eval.evaluate(...), fg.eval.evaluate_candidates(...), and fg.eval.explain(...).
- Type: string or
None - Default:
None, which resolves to"native". - Accepted values:
"native","souffle","problog","pyreason".
The accepted set is fixed by SUPPORTED_ENGINES in factgraph.core.semantics:
SUPPORTED_ENGINES = frozenset({"native", "souffle", "problog", "pyreason"})Each non-native engine is supplied by an adapter package under factgraph.adapters that registers itself on import:
| Engine | Adapter package | Registered evaluator |
|---|---|---|
native | in-process runtime (no adapter package) | default; used when engine=None |
souffle | factgraph.adapters.souffle | evaluate_store_engine |
problog | factgraph.adapters.problog | evaluate_problog |
pyreason | factgraph.adapters.pyreason | pyreason_engine_eval |
Raises: SDKStoreError if engine= is not a string in the accepted set:
<api_path>: engine= must be one of: native, problog, pyreason, souffleevaluate() does not accept mode=; passing it raises SDKStoreError ("evaluate() does not accept mode= in E; use engine=").
Example:
result = fg.eval.evaluate(rule, head=rule, engine="native")
candidates = fg.eval.evaluate(derivation, engine="pyreason")config= option#
Supplies engine semantics. Passed as a keyword to fg.eval.evaluate(...) and fg.eval.explain(...). Accepts a SemanticsProfile, a ProbLogConfig, a PyReasonConfig, or None.
- Type:
SemanticsProfile | ProbLogConfig | PyReasonConfig | None - Default:
None(no semantics; engine runs with its defaults).
Only "problog" and "pyreason" consume a config=. The set is _SEMANTICS_PROFILE_ENGINES = {"problog", "pyreason"}. The "native" and "souffle" engines do not consume a semantics profile.
Engine resolution when config= is supplied:
- If
engine=is omitted, the engine is taken from the config (config.engine). - If both are supplied, they must agree. A mismatch raises
SDKStoreError:SemanticsProfile.engine='<x>' does not match engine='<y>'engine='<x>' does not match semantics.engine='<y>'
- Supplying a
config=for an engine that does not consume one raisesSDKStoreError:engine='<engine>' does not consume SemanticsProfile.
evaluate() does not accept semantics_profile=; passing it raises SDKStoreError ("evaluate() does not accept semantics_profile= in SDK; use config="). evaluate() does not accept engine_options=; passing it raises SDKStoreError.
ProbLogConfig and PyReasonConfig are lowered into a canonical SemanticsProfile before evaluation. Lowering a public wrapper requires a Rule, RuleExpr, or Inference input; otherwise SDKStoreError is raised ("SDK public semantics require Rule, RuleExpr, or Inference object input").
Example:
result = fg.eval.evaluate(
derivation,
config=ProbLogConfig(uncertainty_projection={"probabilistic": {"policy": "midpoint"}}),
)
fg.eval.evaluate(derivation, config=PyReasonConfig(iteration_count=5))ProbLogConfig#
Public ProbLog semantics wrapper. Frozen dataclass in factgraph.sdk. Exposes a read-only engine property that returns "problog", so the SDK derives the engine from the wrapper.
@dataclass(frozen=True)
class ProbLogConfig:
case_probabilities: dict[str, float] = field(default_factory=dict)
rule_params: dict[str, dict[str, Any]] = field(default_factory=dict)
uncertainty_projection: dict[str, Any] = field(default_factory=_default_problog_uncertainty_projection)
name: str | None = None
fallback: str = "reject_unconfigured"| Field | Type | Default | Description |
|---|---|---|---|
case_probabilities | dict[str, float] | {} | Mapping from branch id to probability in (0, 1]. |
rule_params | dict[str, dict[str, Any]] | {} | Per-Rule metadata keyed by application Rule.id. |
uncertainty_projection | dict[str, Any] | {"probabilistic": {"policy": "reject"}, "possibilistic": {"policy": "reject"}, "fallback": "reject_unconfigured"} | Uncertainty projection policy mapping, same schema as SemanticsProfile.uncertainty_projection. |
name | str | None | None | Optional profile name used in the lowered canonical profile. |
fallback | str | "reject_unconfigured" | Policy for unconfigured semantics. |
Property: engine -> str returns "problog".
Raises (SDKStoreError) during construction:
ProbLogConfig.name must be non-empty string when providedifnameis set but not a non-empty string.ProbLogConfig.fallback must be non-empty stringiffallbackis not a non-empty string.case_probabilities[<key>] must be within (0,1]if a probability is<= 0.0or> 1.0.case_probabilities[<key>] must be float in (0,1]if a value is not numeric (booleans rejected).case_probabilities keys must be non-empty branch idsif a key is not a non-empty string.rule_params keys must be non-empty Rule ids/rule_params[<key>] must be an objectfor malformedrule_params.- Any
ValueErrorfrom validatinguncertainty_projectionis re-raised asSDKStoreError.
The uncertainty_projection value is validated by constructing an internal SemanticsProfile; each non-fallback entry must carry a policy drawn from UNCERTAINTY_POLICIES (see SemanticsProfile).
Example:
from factgraph.sdk import ProbLogConfig
config = ProbLogConfig(uncertainty_projection={"probabilistic": {"policy": "midpoint"}})
result = fg.eval.evaluate(derivation, config=config)PyReasonConfig#
Public PyReason semantics wrapper. Frozen dataclass in factgraph.sdk. Exposes a read-only engine property that returns "pyreason", so the SDK derives the engine from the wrapper.
@dataclass(frozen=True)
class PyReasonConfig:
timestep_delay: int = 0
iteration_count: int = 1
derived_bound: tuple[float, float] | None = None
atom_bounds: dict[str, tuple[float, float]] = field(default_factory=dict)
head_bound: tuple[float, float] | None = None
case_bounds: dict[str, tuple[float, float]] = field(default_factory=dict)
rule_params: dict[str, dict[str, Any]] = field(default_factory=dict)
temporal_projection: dict[str, Any] = field(default_factory=lambda: {"mode": "none"})
uncertainty_projection: dict[str, Any] = field(default_factory=dict)
name: str | None = None
fallback: str = "reject_unconfigured"| Field | Type | Default | Description |
|---|---|---|---|
timestep_delay | int | 0 | Non-negative timestep delay for compiled rules. |
iteration_count | int | 1 | Positive global PyReason inference round count. |
derived_bound | tuple[float, float] | None | None | Optional [lower, upper] interval for rule heads. Conflicts with head_bound. |
atom_bounds | dict[str, tuple[float, float]] | {} | Body atom intervals keyed by <rule_id>:atom_<index>. |
head_bound | tuple[float, float] | None | None | Optional global [lower, upper] interval for rule heads. |
case_bounds | dict[str, tuple[float, float]] | {} | Per-branch interval overrides keyed by branch id. |
rule_params | dict[str, dict[str, Any]] | {} | Per-Rule metadata keyed by application Rule.id. |
temporal_projection | dict[str, Any] | {"mode": "none"} | Temporal projection mapping. |
uncertainty_projection | dict[str, Any] | {} | Uncertainty projection mapping. |
name | str | None | None | Optional profile name used in the lowered canonical profile. |
fallback | str | "reject_unconfigured" | Policy for unconfigured semantics. |
Property: engine -> str returns "pyreason".
Each interval value is normalized to a (lower, upper) tuple and must satisfy 0 <= lower <= upper <= 1.
Raises (SDKStoreError) during construction:
PyReasonConfig.name must be non-empty string when providedifnameis set but not a non-empty string.PyReasonConfig.timestep_delay must be intif not an int (booleans rejected);PyReasonConfig.timestep_delay must be >= 0if negative.PyReasonConfig.iteration_count must be intif not an int (booleans rejected);PyReasonConfig.iteration_count must be >= 1if less than 1.PyReasonConfig.fallback must be non-empty stringiffallbackis not a non-empty string.PyReasonConfig.derived_bound conflicts with PyReasonConfig.head_boundif bothderived_boundandhead_boundare set.<field> must be [lower, upper]if an interval is not a 2-element list or tuple;<field> must satisfy 0 <= lower <= upper <= 1if the bounds are out of range.PyReasonConfig.atom_boundskeys must use<rule_id>:atom_<index>with a non-negative integer index.
The interaction between iteration_count and temporal_projection when lowered: when temporal_projection.mode is not "none" and iteration_count is 1, the lowered profile carries iteration_count=None; otherwise it carries the configured iteration_count.
Example:
from factgraph.sdk import PyReasonConfig
config = PyReasonConfig(case_bounds={"sensor_path": [0.8, 1.0]})
fg.eval.evaluate(derivation, config=config)SemanticsProfile#
Canonical, engine-explicit semantics form consumed by runtime adapters. Frozen dataclass in factgraph.core.semantics, re-exported from factgraph.sdk. ProbLogConfig and PyReasonConfig lower into this form. Use it directly when a caller needs explicit control over projections and adapter buckets.
@dataclass(frozen=True)
class SemanticsProfile:
name: str
engine: str
version: str = SUPPORTED_VERSION
engine_options: dict[str, Any] = field(default_factory=dict)
iteration_count: int | None = None
uncertainty_projection: dict[str, Any] = field(default_factory=dict)
temporal_projection: dict[str, Any] = field(default_factory=lambda: {"mode": "none"})
rule_projection: dict[str, list[dict[str, Any]]] = field(default_factory=dict)
certainty_projection: dict[str, Any] = field(default_factory=dict)
output_readback: dict[str, Any] = field(default_factory=dict)
fallback: str = "reject_unconfigured"| Field | Type | Default | Description |
|---|---|---|---|
name | str | required | Stable profile name. Must be a non-empty string. |
engine | str | required | Runtime engine name. Must be in SUPPORTED_ENGINES. |
version | str | "1.0" (SUPPORTED_VERSION) | Profile schema version. Must equal "1.0". |
engine_options | dict[str, Any] | {} | Per-engine option mapping. |
iteration_count | int | None | None | Inference round count. If set, must be >= 1. |
uncertainty_projection | dict[str, Any] | {} | Per-kind policy mapping; see policies below. |
temporal_projection | dict[str, Any] | {"mode": "none"} | Temporal projection; see modes below. |
rule_projection | dict[str, list[dict[str, Any]]] | {} | Per-engine rule annotation buckets. Each entry requires non-empty target and kind strings. |
certainty_projection | dict[str, Any] | {} | Certainty projection mapping. |
output_readback | dict[str, Any] | {} | Output readback mapping. |
fallback | str | "reject_unconfigured" | Fallback policy; must be in FALLBACK_POLICIES. |
Constants (in factgraph.core.semantics):
SUPPORTED_VERSION = "1.0".UNCERTAINTY_POLICIES = frozenset({"identity_probability", "probability_interval", "possibility_interval", "lower", "midpoint", "upper", "reject"}). Eachuncertainty_projectionentry other than thefallbackkey must carry apolicyfrom this set.FALLBACK_POLICIES = frozenset({"reject_unconfigured", "warn_default", "use_default"}). Applies to both the top-levelfallbackfield and theuncertainty_projection.fallbackkey.TIME_BIN_SHORT_FORMS = frozenset({"1d", "1h", "15m", "1m"}).
Supported temporal_projection.mode values: "none", "fixed_timesteps", "valid_time_boundaries", "fact_boundaries", "time_binned". The "fixed_timesteps" mode requires a positive integer timesteps. The "valid_time_boundaries", "fact_boundaries", and "time_binned" modes require a universe of [start, end] with start < end; "time_binned" additionally requires a bin_size matching P<n>D, PT<n>H, PT<n>M, or one of the short forms.
Raises: ValueError during construction:
engine must be one of: native, problog, pyreason, souffleifengineis unsupported.version must be '1.0'ifversionis not"1.0".fallback must be one of: reject_unconfigured, use_default, warn_defaultiffallbackis unsupported.iteration_count must be int or None/iteration_count must be >= 1for invalid counts.uncertainty_projection.<key>.policy must be one of: ...for an unsupported policy.temporal_projection.mode '<mode>' is not supported in D; supported modes: ...for an unsupported temporal mode.
Companion: inspect_semantics_profile(profile) (in factgraph.core.semantics) returns a JSON-like dict reporting engine, profile, version, fallback, a uses map of which sections are populated, and a warnings list.
Example:
from factgraph.sdk import SemanticsProfile
profile = SemanticsProfile(name="audit", engine="problog")
result = fg.eval.evaluate(rule, head=rule, engine="problog", config=profile)Certainty per engine#
How a result row carries certainty depends on the engine.
| Engine | Certainty model | Representation |
|---|---|---|
native | boolean | Derived rows hold; no SemanticsProfile is consumed. |
souffle | boolean | Derived rows hold; no SemanticsProfile is consumed. |
problog | probabilistic | Each derived fact carries a probability annotation (the ProbLog key is "probability"). |
pyreason | possibilistic | Each fact carries a [lower, upper] interval bound. Non-bounded EDB facts enter PyReason with bound [1.0, 1.0]. Confidence is read back from the lower bound of the interval. |
The native and souffle engines evaluate deductively and produce boolean derivations; they reject a config= semantics profile. The problog engine attaches probabilities and reads them under the uncertainty_projection policies. The pyreason engine attaches interval bounds; predicates marked pyreason_bounded use point intervals from their value, and the readback confidence is the interval lower bound.
Related pages#
- Evaluation for the
evaluate/explainresult envelope. - Rules for rule and RuleExpr construction.
- Three-layer API for the
fg.evalnamespace. - Errors for
SDKStoreErrorand related error types.
accept, CandidateSet, Inference, EmitSpec, Pred, and Query are legacy surfaces and are not covered here.