I am trying to model sets of constraints where one set of constraints may or may not imply another set of constraints.
Some examples of individual constraints implying other constraints:
- IsInstance(Mapping) → IsInstance(Iterable)
- GreaterThan(0) → GreaterThan(-1)
- KeysAre([“A”, “B”, 1]) → HasKey(“A”)
- KeysAre([“A”, “B”, 1]) → MissingKey(“D”)
- KeysAre([“A”, “B”, 1]) → IsInstance(Mapping)
I don’t need to worry about evaluating the constraints on specific values, but I do want to consider many different combinations of constraints.
The way I was expecting to use Prolog was to:
- Define lots of rules for when constraints imply other constraints
- Query whether implications hold (Does KeysAre([“A”, “B”, 1]) imply IsInstance(Iterable)?)
Based on my limited experience with Prolog, it seems like one supported approach to model does constraint set A imply constraint set B:
- Define facts that encode the premise of my implication (all constraints in A)
- Define rules that can generate implied constraints from more specific constraints
- Query whether the conclusion of the implication can be inferred (all constraints in B)
One thing about the above that is weird is that it seems like the rules need to be defined after the facts, because many of the rules are recursive, and the facts are the base cases (and need to be defined first afaik)
This has some awkward implications (as far as I can tell)
- Since the rules are being defined after the facts, I am worrying about namespaces. I don’t think I can create a module for each group of rules, and then use those names in my dynamically generated fact files.
- Also, if I tried to just merge everything into one module/file, I would need to worry about collisions and wouldn’t be able to use the module organization for managing the namespaces.
- And if I didn’t merge everything into one module/file, I would need to worry about cases where some kind of constraint doesn’t appear in constraint set A, and so the modules trying to use that proposition would not be able to find it in the fact module(s).
Some paths forwards which I can see:
- Generate all of my facts/rules in the order into one giant file, and then manually worry about namespace collisions.
- Make all of my rules dynamic, and then use asserta & retract to add my facts afterwards - I haven’t really tested that yet, so I’m not sure how well that would work.
For additional context, I am currently using Janus to work with both Python an Prolog. I am currently building all of the files from strings in Python and then calling Prolog from Python.
Any advice on how to approach this would be appreciated!
EDIT:
Another thing which is awkward with my current approach is that my facts (which need to be the first thing defined) often need to include references to python objects (parameters of constraints). Janus makes it easy to pass python objects as variables when asking queries, but currently my approach for referencing them in “facts” is painful. I create a simple rule like
isInstance(Mapping) :- py_call(is_instance:load_class('collections.abc.Mapping'), Mapping).
, but I’m not sure how my full system will fit together yet, and this seems like another thing which may be tricky to work around. Maybe the asserta approach would address this?