Transformers¶
These examples highlight how to use clingexplaid's transformers.
A transformer rewrites an ASP program on the level of its abstract syntax tree (AST).
Every transformer offers a parse_string method that takes a program as a string and returns the transformed program as a string.
To transform files instead, use parse_files or parse_file, depending on the transformer.
Note
The transformed programs start with #program base., since clingo's parser adds this statement to every program.
ConstraintTransformer¶
The ConstraintTransformer adds an atom to the head of every integrity constraint.
Instead of making the program unsatisfiable, a firing constraint now derives this atom, which tells you which constraint was violated.
from clingexplaid.transformers import ConstraintTransformer
PROGRAM = """
a(1..3).
:- a(X), X > 2.
"""
ct = ConstraintTransformer("unsat")
print(ct.parse_string(PROGRAM))
a(1..3).
:- a(X), X > 2.
#program base.
a((1..3)).
unsat :- a(X); X > 2.
With include_id=True, each constraint gets its own numbered atom, so you can tell the constraints apart.
The location of every constraint in the original program is stored in ct.constraint_location_lookup, indexed by its number.
from clingexplaid.transformers import ConstraintTransformer
PROGRAM = """
a(1..3).
:- a(X), X > 2.
:- not a(4).
"""
ct = ConstraintTransformer("unsat", include_id=True)
print(ct.parse_string(PROGRAM))
a(1..3).
:- a(X), X > 2.
:- not a(4).
#program base.
a((1..3)).
unsat(1) :- a(X); X > 2.
unsat(2) :- not a(4).
FactTransformer¶
The FactTransformer removes facts from a program.
Without arguments, it removes all facts.
from clingexplaid.transformers import FactTransformer
PROGRAM = """
a(1).
b(2).
c(X) :- a(X).
"""
ft = FactTransformer()
print(ft.parse_string(PROGRAM))
a(1).
b(2).
c(X) :- a(X).
#program base.
c(X) :- a(X).
To remove only some facts, pass a set of signatures as (name, arity) tuples.
Facts with any other signature are kept.
from clingexplaid.transformers import FactTransformer
PROGRAM = """
a(1).
b(2).
c(X) :- a(X).
"""
ft = FactTransformer(signatures={("a", 1)})
print(ft.parse_string(PROGRAM))
a(1).
b(2).
c(X) :- a(X).
#program base.
b(2).
c(X) :- a(X).
OptimizationRemover¶
The OptimizationRemover removes all optimization statements from a program.
This covers #minimize and #maximize statements as well as weak constraints.
from clingexplaid.transformers import OptimizationRemover
PROGRAM = """
{ a(1..3) }.
#minimize { X : a(X) }.
:~ a(2). [1@1]
"""
opt = OptimizationRemover()
print(opt.parse_string(PROGRAM))
{ a(1..3) }.
#minimize { X : a(X) }.
:~ a(2). [1@1]
#program base.
{ a((1..3)) }.
RuleIDTransformer¶
The RuleIDTransformer adds a numbered _rule atom to the body of every rule.
This keeps each ground rule traceable to the rule it came from.
A choice rule over all _rule atoms is added at the end, so they don't change the program's answer sets.
get_assumptions returns the assumptions that set all _rule atoms to true.
from clingexplaid.transformers import RuleIDTransformer
PROGRAM = """
a(1).
b(X) :- a(X).
:- b(1).
"""
rt = RuleIDTransformer()
print(rt.parse_string(PROGRAM))
# Assumptions setting _rule(1), _rule(2) and _rule(3) to true
assumptions = rt.get_assumptions()
a(1).
b(X) :- a(X).
:- b(1).
#program base.
a(1) :- _rule(1).
b(X) :- a(X); _rule(2).
#false :- b(1); _rule(3).
{_rule(1..3)}. % Choice rule to allow all _rule atoms to become assumptions
RuleSplitter¶
The RuleSplitter splits every rule with a body into two rules.
The first rule derives a _body atom from the original body.
Its arguments are the body encoded in base64 and a tuple of the body's variables.
The second rule derives the original head from this _body atom.
In the example, YShYKQ== is the base64 encoding of a(X).
from clingexplaid.transformers import RuleSplitter
PROGRAM = """
a(1).
b(X) :- a(X).
"""
rs = RuleSplitter()
print(rs.parse_string(PROGRAM))
a(1).
b(X) :- a(X).
#program base.
a(1).
_body("YShYKQ==",(X,)) :- a(X).
b(X) :- _body("YShYKQ==",(X,)).