stash
This commit is contained in:
		
							parent
							
								
									8e6e9edf64
								
							
						
					
					
						commit
						99a9a6e6a1
					
				
					 4 changed files with 75 additions and 24 deletions
				
			
		
							
								
								
									
										3
									
								
								.vscode/settings.json
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.vscode/settings.json
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,3 +1,4 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    "discord.enabled": true
 | 
					    "discord.enabled": true,
 | 
				
			||||||
 | 
					    "python.formatting.provider": "black"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								__pycache__/ontology.cpython-39.pyc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								__pycache__/ontology.cpython-39.pyc
									
										
									
									
									
										Normal file
									
								
							
										
											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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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…
	
	Add table
		
		Reference in a new issue