Primitive Logic Directed Acyclic Graph
"Primitive Logic Directed Acyclic Graph" data structure, or "PL-DAG" for short, is fundamentally a Directed Acyclic Graph (DAG) where each node represents a logical relationship, and the leaf nodes correspond to literals.
Each node in the graph encapsulates information about how its incoming nodes or leafs are logically related. For instance, a node might represent an AND operation, meaning that if it evaluates to true, all its incoming nodes or leafs must also evaluate to true.
How it works
Each composite (node) is a linear inequality equation on the form
A = a + b + c >= 0
. A primitive (leaf) is just a name or alias connected to a literal. A literal here is a complex number of two values -1+3j
indicating what's the lowest value some variable could take (-1
) and the highest value (+3
). So a boolean primitive would have the literal value 1j
, since it can take on the value 0 or 1. Another primitive having 52j
(weeks for instance) could potentially take on every discrete value in between but is expressed only with the lowest and highest value.
Example
from pldag import PLDAG
model = PLDAG()
model.set_primitives("xyz")
id_ref = model.set_and(["x","y","z"], alias="A")
id_ref_again = model.id_from_alias("A")
assert id_ref == id_ref_again
assert model.propagate({"x": 1+1j, "y": 1+1j, "z": 1+1j}).get(id_ref) == 1+1j
assert model.propagate({"x": 1+1j, "y": 1+1j, "z": 1j}).get(id_ref) == 1j
assert model.propagate({"x": 1+1j, "y": 1+1j, "z": 0j}).get(id_ref) == 0j
There's also a quick way to use a solver. There's no built-in solver but is dependent on existing ones. Before using, reinstall the package with the solver variable set to the solver you'd want to use
pip install pldag
And then you can use it like following
from pldag import Solver
solution = next(iter(model.solve(objectives=[{"x": 1}], assume={id_ref: 1+1j}, solver=Solver.DEFAULT)))
assert solution.get(id_ref) == 1+1j