Internals

This page is for engine authors and contributors. It lists the calls an engine makes to run a resolved rule, and the internal helpers a contributor needs.

The engine interface

An engine uses a rule in three steps:

  1. It resolves the rule once, when it sets a node up, with find_message_rule and its siblings.
  2. It checks the rule with check_services and check_reads_logscale.
  3. It runs the rule on every update with execute_rule. execute_rule builds no RuleResult, so it allocates nothing beyond the rule's own work.

These names are public, for engine authors. A rule author never calls them.

MessagePassingRulesBase.execute_rule — Function
execute_rule(spec::RuleSpec, output, algorithm, ctx, args, ann, target)
execute_rule(spec::RuleSpec, output, scratch, algorithm, ctx, args, ann, target)

Run a resolved rule and return its bare result, building no RuleResult: the engine's entry point.

Arguments

  • spec: the rule, as resolution returned it;
  • output: for an in-place rule, the buffer to write into, or nothing to have the rule preallocate one; ignored otherwise;
  • scratch: the rule's working memory, which an engine builds once with rule_scratch and passes on every call; without it, or with nothing, a rule that declares scratch gets a fresh one;
  • algorithm: the value to run under, rule_algorithm(spec, algorithm);
  • ctx: the RuleContext; its services are not checked here, which is check_services's job, once, when the rule is resolved;
  • args: the RuleArgs;
  • ann: the annotations, a RuleAnnotations or a NoAnnotations;
  • target: the Target, IndexedTarget or ClusterTarget, or nothing for an average energy.

Nothing here catches exceptions: whatever a rule throws propagates to the caller. The rule's log scale is not computed; an engine that tracks log scales calls execute_rule_with_logscale instead.

A rule never sees a missing input. When any input is missing, an engine does not call the rule at all, and does not run the annotation processors that follow a rule either; the result is missing, carrying only the annotations written before the call.

source
MessagePassingRulesBase.execute_rule_with_logscale — Function
execute_rule_with_logscale(spec::RuleSpec, output, scratch, algorithm, ctx, args, ann, target) -> Tuple

Run a resolved message rule and return (result, logscale), for an engine that tracks log scales. The arguments are execute_rule's. The log scale is the one the rule declares, a number or what its function or body computes, or an UndefinedLogScale naming the rule for a rule that declares none. It does not check that args carries log scales for a rule that reads them; check_reads_logscale does.

source
MessagePassingRulesBase.rule_scratch — Function
rule_scratch(spec::RuleSpec, algorithm, ctx, args, target)

Build the working memory a rule declares with scratch from these inputs, or return nothing for a rule that declares none. An engine builds it once per outbound stream and passes it to every execute_rule of that rule. The rule writes it before reading it, so the engine may rebuild it whenever it likes.

source
MessagePassingRulesBase.rule_scratch_type — Function
rule_scratch_type(spec::RuleSpec, algorithm, ctx, args, target) -> Type

The type of the working memory rule_scratch builds for these inputs, inferred from their types: concrete where inference pins it down, Any where it does not, and Nothing for a rule that declares no scratch. It depends only on the inputs' types, so it folds to a constant where the call is inferred, and an engine keeping the scratch between calls asserts it. It decides only whether a kept scratch is typed, never what a rule computes.

source
MessagePassingRulesBase.check_reads_logscale — Function
check_reads_logscale(spec::RuleSpec, args::RuleArgs) -> nothing

Check that args carries log scales when spec reads them (reads_logscale = true). Whoever resolves a rule calls it before running the rule; the calls in this package do.

Throws

ArgumentError naming the rule when it reads log scales and args.logscale is nothing: its caller does not track them.

source
MessagePassingRulesBase.check_selected_members — Function
check_selected_members(spec::RuleSpec, target, args::RuleArgs) -> nothing

Check that a group which spec selects members of by the target's index holds nothing where the selection leaves a member out, as an engine delivers it: for a target (:in, k), member k of m[:in][!k], and every member but k of m[:in][k]. A caller that writes a group's tuple by hand calls it before running the rule; this package's interactive calls and the tables of MessagePassingRulesTestUtils do. An engine, or code that builds its RuleArgs for message_passing_rule, builds the tuple itself and need not.

Throws

ArgumentError naming the selection and the member, and saying to pass nothing there: the rule would read a value no graph gives it.

source

Internal helpers

These names are not part of the public API. The macros' docstrings share text through const string fragments, DOC_RULE_*, DOC_CALL_*, DOC_MPR_* and DOC_DEPENDENCY_ENTRIES. The fragments have no docstrings of their own.

MessagePassingRulesBase.default_inputs_match — Function
default_inputs_match(args::RuleArgs, ::Val{required}, ::Type{types}) -> Bool

Whether args holds each typed input a rule declared with default names beside it, (container, key, selection), with its type in types, for a single interface, a cluster or a whole group. The guard the definition macro generates for such a rule, which returns a RuleNotFound where it is false. Computed from the types alone, so it folds to a constant.

source
MessagePassingRulesBase.WithLogScale — Type
WithLogScale(result, logscale)

A rule's result paired with its log scale, as with_logscale builds it. Only the body of a rule declared with logscale = from_body returns one, and whoever runs the rule unwraps it; no rule receives one, and a caller never sees one.

source