I am striving to write more readable, declarative programs. So I decided to implement a simple algorithm we currently use. The procedural implementation is the following:
- There are commands and resources
- Each command can provide and require multiple resources
- The algorithm will loop over all commands and schedule the commands that all their requirements are provided for.
- All resources the command provides are now provided for
- We are finished if all commands are scheduled
- We can't satisfy the dependencies if there are commands left and we can't schedule new commands for an iteration of the algorithm
So the datalog variant I came up with looks nice but has two problems:
- It is WRONG
- I need a loop to read the result
You can find the complete source here.
It depends on hypothesis and you can easily run it with pytest.
Below the test that fails: If we require resources provided by a previous "rank" or order. It can't find it. I tried to make follows recursive, but then it fails even in the simple examples.
def test_graph_multirequire():
"""Test if the resolver can handle a graph with multiple requires"""
tree = [
[('A'), ('B'), ('C'), ('D'), ('E'), ('F'), ('G'), ()],
[(), ('A'), ('B'), ('C', 'A'), ('D'), ('E'), ('F'), ('G')]
]
run_graph(tree)
def run_graph(tree):
"""Run an example"""
try:
tree_len = len(tree[0])
index = list(range(tree_len))
random.shuffle(index)
for i in index:
+ is_command(i)
for provide in tree[0][i]:
+ provides(i, provide)
for require in tree[1][i]:
+ requires(i, require)
##############################
is_root(X) <= is_command(X) & ~requires(X, Y)
follows(X, Z) <= (
provides(X, Y) & requires(Z, Y) & (X != Z)
)
order(0, X) <= is_root(X)
order(N, X) <= (N > 0) & order(N - 1, Y) & follows(Y, X)
##############################
ordered = []
try:
for x in range(tree_len):
nodes = order(x, N)
if not nodes:
break
ordered.extend([x[0] for x in nodes])
except AttributeError:
ordered = index
assert len(ordered) >= tree_len
print(ordered)
provided = set()
for command in ordered:
assert set(tree[1][command]).issubset(provided)
provided.update(tree[0][command])
finally:
pd.clear()
My questions:
- Am I using the wrong tool?
- Is anybody aware of comprehensive datalog how-tos?
- How do you actually solve the problem above?
Edit:
- I am missing quantifiers like all() and exists(), how can I express this in pyDatalog?