Documentation Index
Fetch the complete documentation index at: https://mintlify.com/HarvardPL/AbcDatalog/llms.txt
Use this file to discover all available pages before exploring further.
DatalogExecutor provides an event-driven model for Datalog evaluation that differs from the batch DatalogEngine interface. Instead of computing all facts up front, an executor allows you to register listeners that fire whenever a new fact matching a given predicate is derived, and to inject new EDB facts into the running evaluation at any time. This is useful for streaming or reactive scenarios where input data arrives incrementally.
DatalogParallelExecutor is the concrete implementation. It runs the Datalog evaluation concurrently in separate threads using ExtensibleBottomUpEvalManager under the hood.
Lifecycle
An executor must be used in the following order:
initialize — load the program and declare which EDB relations are extensible
registerListener — attach callbacks (must happen before start)
start — begin evaluation
addFactAsynchronously — feed new facts during evaluation
shutdown — terminate the executor
Methods
initialize
void initialize(Set<Clause> program, Set<PredicateSym> extendibleEdbPreds)
throws DatalogValidationException
Loads the Datalog program and specifies which EDB relations may receive new facts during evaluation via addFactAsynchronously. Should be called exactly once.
The program: EDB facts (body-less clauses) and IDB rules.
extendibleEdbPreds
Set<PredicateSym>
required
Predicate symbols whose extensions may grow after evaluation starts. Only facts whose predicate is in this set may be added via addFactAsynchronously.
start
Begins the Datalog evaluation. Throws IllegalStateException if the executor has not been initialized or if evaluation has already been started.
addFactAsynchronously
void addFactAsynchronously(PositiveAtom edbFact)
Injects a new ground EDB fact into the running evaluation. The fact is processed asynchronously; derived consequences may arrive via registered listeners at any point after this call returns.
A ground atom (no variables) belonging to one of the extendible EDB predicates declared in initialize.
Throws IllegalArgumentException if the atom is not ground or if its predicate was not declared as extendible in initialize. Throws IllegalStateException if the executor has not been initialized.
registerListener
void registerListener(PredicateSym p, DatalogListener listener)
Registers a callback to be invoked each time a new fact with predicate symbol p is derived during evaluation. The listener may be called from an arbitrary thread.
The predicate symbol to watch.
The callback to invoke. Must be registered before start() is called.
Listeners are invoked from arbitrary evaluation threads. They must not block and must be thread-safe. Performing long-running work or acquiring locks inside a listener risks deadlocking or stalling the evaluation.
shutdown
Terminates the executor and releases its internal thread pool. The executor cannot be reused after shutdown is called.
DatalogListener
DatalogListener is a single-method interface with a single method:
void newFactDerived(PositiveAtom fact)
The newly derived ground fact. fact.isGround() is guaranteed to return true.
Because DatalogListener has exactly one abstract method, it can be provided as a lambda expression.
DatalogParallelExecutor
DatalogParallelExecutor is the concrete DatalogExecutor implementation:
DatalogParallelExecutor ex = new DatalogParallelExecutor();
It implements all five methods above and executes evaluation work in a background thread pool managed by ExtensibleBottomUpEvalManager.
Code example
The following is adapted from ExecutorExample.java:
import edu.harvard.seas.pl.abcdatalog.ast.Clause;
import edu.harvard.seas.pl.abcdatalog.ast.PositiveAtom;
import edu.harvard.seas.pl.abcdatalog.ast.PredicateSym;
import edu.harvard.seas.pl.abcdatalog.executor.DatalogParallelExecutor;
import edu.harvard.seas.pl.abcdatalog.parser.DatalogParser;
import edu.harvard.seas.pl.abcdatalog.parser.DatalogTokenizer;
import java.io.StringReader;
import java.util.Collections;
import java.util.Set;
String program =
"edge(0,1). edge(1,2). tc(X,Y) :- edge(X,Y)."
+ "tc(X,Y) :- edge(X,Z), tc(Z,Y). cycle :- tc(X,X).";
DatalogTokenizer t = new DatalogTokenizer(new StringReader(program));
Set<Clause> ast = DatalogParser.parseProgram(t);
PredicateSym edge = PredicateSym.create("edge", 2);
PredicateSym tc = PredicateSym.create("tc", 2);
PredicateSym cycle = PredicateSym.create("cycle", 0);
// 1. Initialize
DatalogParallelExecutor ex = new DatalogParallelExecutor();
ex.initialize(ast, Collections.singleton(edge));
// 2. Register listeners before start()
ex.registerListener(tc, fact -> System.out.println("Derived: " + fact));
ex.registerListener(cycle, fact -> System.out.println("*** Cycle detected. ***"));
// 3. Start evaluation
ex.start();
// 4. Feed new facts asynchronously
String newFacts = "edge(2,3). edge(3,4). edge(4,0).";
DatalogTokenizer ft = new DatalogTokenizer(new StringReader(newFacts));
while (ft.hasNext()) {
ex.addFactAsynchronously(DatalogParser.parseClauseAsPositiveAtom(ft));
}
ex.shutdown();