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:

EngineAdapter packageRegistered evaluator
nativein-process runtime (no adapter package)default; used when engine=None
soufflefactgraph.adapters.souffleevaluate_store_engine
problogfactgraph.adapters.problogevaluate_problog
pyreasonfactgraph.adapters.pyreasonpyreason_engine_eval

Raises: SDKStoreError if engine= is not a string in the accepted set:

<api_path>: engine= must be one of: native, problog, pyreason, souffle

evaluate() 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 raises SDKStoreError: 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"
FieldTypeDefaultDescription
case_probabilitiesdict[str, float]{}Mapping from branch id to probability in (0, 1].
rule_paramsdict[str, dict[str, Any]]{}Per-Rule metadata keyed by application Rule.id.
uncertainty_projectiondict[str, Any]{"probabilistic": {"policy": "reject"}, "possibilistic": {"policy": "reject"}, "fallback": "reject_unconfigured"}Uncertainty projection policy mapping, same schema as SemanticsProfile.uncertainty_projection.
namestr | NoneNoneOptional profile name used in the lowered canonical profile.
fallbackstr"reject_unconfigured"Policy for unconfigured semantics.

Property: engine -> str returns "problog".

Raises (SDKStoreError) during construction:

  • ProbLogConfig.name must be non-empty string when provided if name is set but not a non-empty string.
  • ProbLogConfig.fallback must be non-empty string if fallback is not a non-empty string.
  • case_probabilities[<key>] must be within (0,1] if a probability is <= 0.0 or > 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 ids if a key is not a non-empty string.
  • rule_params keys must be non-empty Rule ids / rule_params[<key>] must be an object for malformed rule_params.
  • Any ValueError from validating uncertainty_projection is re-raised as SDKStoreError.

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"
FieldTypeDefaultDescription
timestep_delayint0Non-negative timestep delay for compiled rules.
iteration_countint1Positive global PyReason inference round count.
derived_boundtuple[float, float] | NoneNoneOptional [lower, upper] interval for rule heads. Conflicts with head_bound.
atom_boundsdict[str, tuple[float, float]]{}Body atom intervals keyed by <rule_id>:atom_<index>.
head_boundtuple[float, float] | NoneNoneOptional global [lower, upper] interval for rule heads.
case_boundsdict[str, tuple[float, float]]{}Per-branch interval overrides keyed by branch id.
rule_paramsdict[str, dict[str, Any]]{}Per-Rule metadata keyed by application Rule.id.
temporal_projectiondict[str, Any]{"mode": "none"}Temporal projection mapping.
uncertainty_projectiondict[str, Any]{}Uncertainty projection mapping.
namestr | NoneNoneOptional profile name used in the lowered canonical profile.
fallbackstr"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 provided if name is set but not a non-empty string.
  • PyReasonConfig.timestep_delay must be int if not an int (booleans rejected); PyReasonConfig.timestep_delay must be >= 0 if negative.
  • PyReasonConfig.iteration_count must be int if not an int (booleans rejected); PyReasonConfig.iteration_count must be >= 1 if less than 1.
  • PyReasonConfig.fallback must be non-empty string if fallback is not a non-empty string.
  • PyReasonConfig.derived_bound conflicts with PyReasonConfig.head_bound if both derived_bound and head_bound are set.
  • <field> must be [lower, upper] if an interval is not a 2-element list or tuple; <field> must satisfy 0 <= lower <= upper <= 1 if the bounds are out of range.
  • PyReasonConfig.atom_bounds keys 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"
FieldTypeDefaultDescription
namestrrequiredStable profile name. Must be a non-empty string.
enginestrrequiredRuntime engine name. Must be in SUPPORTED_ENGINES.
versionstr"1.0" (SUPPORTED_VERSION)Profile schema version. Must equal "1.0".
engine_optionsdict[str, Any]{}Per-engine option mapping.
iteration_countint | NoneNoneInference round count. If set, must be >= 1.
uncertainty_projectiondict[str, Any]{}Per-kind policy mapping; see policies below.
temporal_projectiondict[str, Any]{"mode": "none"}Temporal projection; see modes below.
rule_projectiondict[str, list[dict[str, Any]]]{}Per-engine rule annotation buckets. Each entry requires non-empty target and kind strings.
certainty_projectiondict[str, Any]{}Certainty projection mapping.
output_readbackdict[str, Any]{}Output readback mapping.
fallbackstr"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"}). Each uncertainty_projection entry other than the fallback key must carry a policy from this set.
  • FALLBACK_POLICIES = frozenset({"reject_unconfigured", "warn_default", "use_default"}). Applies to both the top-level fallback field and the uncertainty_projection.fallback key.
  • 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, souffle if engine is unsupported.
  • version must be '1.0' if version is not "1.0".
  • fallback must be one of: reject_unconfigured, use_default, warn_default if fallback is unsupported.
  • iteration_count must be int or None / iteration_count must be >= 1 for 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.

EngineCertainty modelRepresentation
nativebooleanDerived rows hold; no SemanticsProfile is consumed.
soufflebooleanDerived rows hold; no SemanticsProfile is consumed.
problogprobabilisticEach derived fact carries a probability annotation (the ProbLog key is "probability").
pyreasonpossibilisticEach 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.

  • Evaluation for the evaluate/explain result envelope.
  • Rules for rule and RuleExpr construction.
  • Three-layer API for the fg.eval namespace.
  • Errors for SDKStoreError and related error types.

accept, CandidateSet, Inference, EmitSpec, Pred, and Query are legacy surfaces and are not covered here.

comments powered by Disqus