stash
This commit is contained in:
parent
8e6e9edf64
commit
99a9a6e6a1
|
@ -1,3 +1,4 @@
|
||||||
{
|
{
|
||||||
"discord.enabled": true
|
"discord.enabled": true,
|
||||||
|
"python.formatting.provider": "black"
|
||||||
}
|
}
|
Binary file not shown.
|
@ -17,3 +17,4 @@ def propagate(atoms: Set[str]) -> Set[str]:
|
||||||
def check(atoms: Set[str]) -> bool:
|
def check(atoms: Set[str]) -> bool:
|
||||||
atoms = atoms.intersection(DL_ATOMS)
|
atoms = atoms.intersection(DL_ATOMS)
|
||||||
return atoms in models
|
return atoms in models
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,29 @@
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from operator import itemgetter
|
from operator import getitem
|
||||||
from typing import Iterable, List, Tuple
|
from typing import Iterable, List, Tuple
|
||||||
|
from functools import partial
|
||||||
import clingo
|
import clingo
|
||||||
from clingo import PropagateControl, PropagateInit, PropagateControl, PropagatorCheckMode, Assignment, Symbol, Control, TheoryAtom
|
from clingo import (
|
||||||
|
PropagateControl,
|
||||||
|
PropagateInit,
|
||||||
|
PropagateControl,
|
||||||
|
PropagatorCheckMode,
|
||||||
|
Assignment,
|
||||||
|
Symbol,
|
||||||
|
Control,
|
||||||
|
TheoryAtom,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
API notes:
|
||||||
|
add_clause is disjunctive
|
||||||
|
add_nogood is conjunctive
|
||||||
|
|
||||||
|
defined appear in a rule head
|
||||||
|
No way to strict atoms except through adding nogoods/clauses?
|
||||||
|
"""
|
||||||
|
|
||||||
import ontology as O
|
import ontology as O
|
||||||
|
|
||||||
|
@ -11,7 +31,7 @@ import ontology as O
|
||||||
class Ontology:
|
class Ontology:
|
||||||
def init(self, init: PropagateInit) -> None:
|
def init(self, init: PropagateInit) -> None:
|
||||||
init.check_mode = PropagatorCheckMode.Total
|
init.check_mode = PropagatorCheckMode.Total
|
||||||
|
|
||||||
self.assignment = dict()
|
self.assignment = dict()
|
||||||
|
|
||||||
self.symbolic_atoms = {
|
self.symbolic_atoms = {
|
||||||
|
@ -22,37 +42,68 @@ class Ontology:
|
||||||
init.solver_literal(atom.literal): atom.elements[0].terms[0].name
|
init.solver_literal(atom.literal): atom.elements[0].terms[0].name
|
||||||
for atom in init.theory_atoms
|
for atom in init.theory_atoms
|
||||||
}
|
}
|
||||||
self.symbolic_atoms_inv = { v: k for k, v in self.symbolic_atoms.items() }
|
self.symbolic_atoms_inv = {v: k for k, v in self.symbolic_atoms.items()}
|
||||||
self.theory_atoms_inv = { v: k for k, v in self.theory_atoms.items() }
|
self.theory_atoms_inv = {v: k for k, v in self.theory_atoms.items()}
|
||||||
|
|
||||||
|
|
||||||
# Might only need to watch just theory atoms / just symbol atoms but for now
|
# Might only need to watch just theory atoms / just symbol atoms but for now
|
||||||
# watching everything is easier
|
# watching everything is easier
|
||||||
for lit in chain(self.symbolic_atoms, self.theory_atoms):
|
for lit in chain(self.symbolic_atoms, self.theory_atoms):
|
||||||
init.add_watch(lit)
|
init.add_watch(lit)
|
||||||
|
|
||||||
|
# Could add these with additional rules, but I think that will change the semantics of the O atoms.
|
||||||
|
# An ontology atom must be true if it's regular counterpart is also true
|
||||||
|
# The opposite direction is already enforced by rules.
|
||||||
|
for theory_atom in self.theory_atoms_inv:
|
||||||
|
theory_lit = self.theory_atoms_inv[theory_atom]
|
||||||
|
symbolic_lit = self.symbolic_atoms_inv[theory_atom]
|
||||||
|
|
||||||
|
init.add_clause((-symbolic_lit, theory_lit))
|
||||||
|
|
||||||
def truthy_atoms_text(self):
|
def truthy_atoms_text(self):
|
||||||
return (str(self.lookup_solver_lit(atom)[0]) for atom in self.assignment if self.assignment[atom])
|
return (
|
||||||
|
str(self.lookup_solver_lit(atom)[0])
|
||||||
|
for atom in self.assignment
|
||||||
|
if self.assignment[atom]
|
||||||
|
)
|
||||||
|
|
||||||
def atom_text_to_dl_atom(self, atoms: Iterable[str]) -> Iterable[int]:
|
def atom_text_to_dl_atom(self, atoms: Iterable[str]) -> Iterable[int]:
|
||||||
return map(self.theory_atoms.get, atoms)
|
return (
|
||||||
|
lit
|
||||||
|
for atom in atoms
|
||||||
|
if (lit := self.theory_atoms_inv.get(atom)) is not None
|
||||||
|
)
|
||||||
|
|
||||||
def falsey_atoms_text(self):
|
def falsey_atoms_text(self):
|
||||||
return (str(self.lookup_solver_lit(atom)[0]) for atom in self.assignment if not self.assignment[atom])
|
return (
|
||||||
|
str(self.lookup_solver_lit(atom)[0])
|
||||||
|
for atom in self.assignment
|
||||||
|
if not self.assignment[atom]
|
||||||
|
)
|
||||||
|
|
||||||
|
def assign_nogood_true(self, pcontrol: PropagateControl, lits: Iterable[int]):
|
||||||
|
not_lits = (-lit for lit in lits)
|
||||||
|
assignment = (
|
||||||
|
lit if is_pos else -lit
|
||||||
|
for lit, is_pos in chain(
|
||||||
|
self.symbolic_atoms.items(), self.theory_atoms.items()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
pcontrol.add_nogood(chain(assignment, not_lits))
|
||||||
|
|
||||||
def propagate(self, pcontrol: PropagateControl, changes) -> None:
|
def propagate(self, pcontrol: PropagateControl, changes) -> None:
|
||||||
|
print("propagate: ", end="")
|
||||||
|
self.print_assignment()
|
||||||
for change in changes:
|
for change in changes:
|
||||||
atom = abs(change)
|
atom = abs(change)
|
||||||
assert atom not in self.assignment
|
assert atom not in self.assignment
|
||||||
self.assignment[atom] = change >= 0
|
self.assignment[atom] = change >= 0
|
||||||
|
|
||||||
in_atoms = set(self.truthy_atoms_text())
|
in_atoms = set(self.truthy_atoms_text())
|
||||||
out_atoms = self.atom_text_to_dl_atom(O.propagate())
|
out_atoms = set(self.atom_text_to_dl_atom(O.propagate(in_atoms)))
|
||||||
new_atoms = out_atoms - in_atoms
|
new_atoms = out_atoms - in_atoms
|
||||||
new_lits = self.atom_text_to_dl_atom(new_atoms)
|
|
||||||
|
|
||||||
|
|
||||||
|
self.assign_nogood_true(pcontrol, new_atoms)
|
||||||
|
pcontrol.propagate()
|
||||||
|
|
||||||
def undo(self, thread_id: int, assignment: Assignment, changes: List[int]):
|
def undo(self, thread_id: int, assignment: Assignment, changes: List[int]):
|
||||||
for change in changes:
|
for change in changes:
|
||||||
|
@ -60,9 +111,9 @@ class Ontology:
|
||||||
del self.assignment[atom]
|
del self.assignment[atom]
|
||||||
|
|
||||||
def check(self, pcontrol: PropagateControl) -> None:
|
def check(self, pcontrol: PropagateControl) -> None:
|
||||||
|
print("check: ", end="")
|
||||||
self.print_assignment()
|
self.print_assignment()
|
||||||
|
|
||||||
|
|
||||||
def print_assignment(self):
|
def print_assignment(self):
|
||||||
print("assignment: ", end="")
|
print("assignment: ", end="")
|
||||||
for lit in self.assignment:
|
for lit in self.assignment:
|
||||||
|
@ -82,7 +133,6 @@ class Ontology:
|
||||||
neg = "not " if is_neg else ""
|
neg = "not " if is_neg else ""
|
||||||
return f"({theory}{neg}{symbol})"
|
return f"({theory}{neg}{symbol})"
|
||||||
|
|
||||||
|
|
||||||
def lookup_solver_lit(self, lit: int) -> Tuple[Symbol, bool, bool]:
|
def lookup_solver_lit(self, lit: int) -> Tuple[Symbol, bool, bool]:
|
||||||
atom = abs(lit)
|
atom = abs(lit)
|
||||||
if (atom_symb := self.symbolic_atoms.get(atom, None)) is not None:
|
if (atom_symb := self.symbolic_atoms.get(atom, None)) is not None:
|
||||||
|
@ -92,10 +142,9 @@ class Ontology:
|
||||||
return None, False, False
|
return None, False, False
|
||||||
|
|
||||||
|
|
||||||
program="""
|
program = """
|
||||||
a :- not b.
|
a :- not b.
|
||||||
b :- not a.
|
b :- not a.
|
||||||
c :- not d.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Need to ground program before we can look up symbolic atoms and
|
# Need to ground program before we can look up symbolic atoms and
|
||||||
|
@ -103,7 +152,7 @@ c :- not d.
|
||||||
# So we parse and ground and then revert back to text then reparse
|
# So we parse and ground and then revert back to text then reparse
|
||||||
def add_external_atoms(program: str) -> str:
|
def add_external_atoms(program: str) -> str:
|
||||||
control = clingo.Control(["0"])
|
control = clingo.Control(["0"])
|
||||||
control.add("base", [], program)
|
control.add("base", [], program.replace("\n", ""))
|
||||||
control.ground([("base", [])])
|
control.ground([("base", [])])
|
||||||
|
|
||||||
theory_grammar = """
|
theory_grammar = """
|
||||||
|
@ -111,15 +160,16 @@ def add_external_atoms(program: str) -> str:
|
||||||
kterm {- : 0, unary };
|
kterm {- : 0, unary };
|
||||||
&o/0 : kterm, any
|
&o/0 : kterm, any
|
||||||
}.
|
}.
|
||||||
"""
|
"""
|
||||||
external_atoms = "\n".join(
|
external_atoms = "\n".join(
|
||||||
f"{atom} :- &o{{{atom}}}." for atom in
|
f"{atom} :- &o{{{atom}}}."
|
||||||
(str(atom.symbol) for atom in control.symbolic_atoms)
|
for atom in (str(atom.symbol) for atom in control.symbolic_atoms)
|
||||||
)
|
)
|
||||||
return theory_grammar + program + external_atoms
|
return theory_grammar + program + external_atoms
|
||||||
|
|
||||||
|
|
||||||
program = add_external_atoms(program)
|
program = add_external_atoms(program)
|
||||||
|
print(program)
|
||||||
control = clingo.Control(["0"])
|
control = clingo.Control(["0"])
|
||||||
propagator = Ontology()
|
propagator = Ontology()
|
||||||
control.register_propagator(propagator)
|
control.register_propagator(propagator)
|
||||||
|
@ -133,4 +183,3 @@ control.ground([("base", [])])
|
||||||
with control.solve(yield_=True) as solve_handle:
|
with control.solve(yield_=True) as solve_handle:
|
||||||
for model in solve_handle:
|
for model in solve_handle:
|
||||||
print("answer set:", model)
|
print("answer set:", model)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue