Where aggregates go
An aggregate may appear only in a rule head, never in a body. This is a deliberate restriction: the head is where the summary lands, and keeping aggregates out of bodies keeps evaluation well-defined. The two rules below are the canonical shapes:X, how many distinct edge(X, Y) rows exist. The second
sums the second column of every weight fact into a single total.
The five aggregates
XLOG provides exactly five aggregate operators — no others:| Aggregate | Meaning |
|---|---|
count(X) | Number of rows in the group |
sum(X) | Sum of X over the group |
min(X) | Smallest X in the group |
max(X) | Largest X in the group |
logsumexp(X) | Log of the sum of exponentials — the numerically stable “soft maximum” used in probabilistic and neural work |
There is no built-in
avg. Compute an average as sum divided by count — derive
each with its own rule, then combine them:Group-by is implicit
You never write aGROUP BY clause. The non-aggregate variables in the head are the
grouping key. In:
X is not aggregated, so the engine forms one group per distinct X and computes
count(Y) within each. When the head has no non-aggregate variable, the whole relation
is a single group — that is exactly what total(sum(W)) does, producing one global
total.
Value types
An aggregate’s result type follows from the operator and the type of the column it consumes:| Aggregate | Input type | Result type |
|---|---|---|
count | any type | u64 |
sum | u32 | u64 |
min | u32 | u32 |
max | u32 | u32 |
logsumexp | f64 | f64 |
count always returns a u64 regardless of what it counts. sum widens a u32 column
to u64 so a large total cannot overflow the count of narrow inputs, while min and
max return the same type they consume — they only select an existing value, so no
widening is needed.
Aggregation is stratified
An aggregate introduces a stratification boundary: a relation defined by aggregation cannot depend, recursively, on itself through that aggregate. Concretely, you cannot have a rule whose head aggregates a predicate that (directly or transitively) depends on the head. The engine computes each aggregate only once its inputs are fully determined, so results are well-defined rather than chasing a moving target. This mirrors how negation is stratified in Facts and rules: both need their inputs settled before they can produce a sound answer.Probabilistic engines
Exact versus Monte Carlo inference, and the finite caps on exact probabilistic
aggregation.
Lists and meta
Build and pattern-match lists, and collect results with the meta-predicates.