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:
 | 
			
		||||
    atoms = atoms.intersection(DL_ATOMS)
 | 
			
		||||
    return atoms in models
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,29 @@
 | 
			
		|||
from copy import deepcopy
 | 
			
		||||
from itertools import chain
 | 
			
		||||
from operator import itemgetter
 | 
			
		||||
from operator import getitem
 | 
			
		||||
from typing import Iterable, List, Tuple
 | 
			
		||||
from functools import partial
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -25,34 +45,65 @@ class Ontology:
 | 
			
		|||
        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()}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        # Might only need to watch just theory atoms / just symbol atoms but for now
 | 
			
		||||
        # watching everything is easier
 | 
			
		||||
        for lit in chain(self.symbolic_atoms, self.theory_atoms):
 | 
			
		||||
            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):
 | 
			
		||||
        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]:
 | 
			
		||||
        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):
 | 
			
		||||
        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:
 | 
			
		||||
        print("propagate: ", end="")
 | 
			
		||||
        self.print_assignment()
 | 
			
		||||
        for change in changes:
 | 
			
		||||
            atom = abs(change)
 | 
			
		||||
            assert atom not in self.assignment
 | 
			
		||||
            self.assignment[atom] = change >= 0
 | 
			
		||||
 | 
			
		||||
        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_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]):
 | 
			
		||||
        for change in changes:
 | 
			
		||||
| 
						 | 
				
			
			@ -60,9 +111,9 @@ class Ontology:
 | 
			
		|||
            del self.assignment[atom]
 | 
			
		||||
 | 
			
		||||
    def check(self, pcontrol: PropagateControl) -> None:
 | 
			
		||||
        print("check: ", end="")
 | 
			
		||||
        self.print_assignment()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def print_assignment(self):
 | 
			
		||||
        print("assignment: ", end="")
 | 
			
		||||
        for lit in self.assignment:
 | 
			
		||||
| 
						 | 
				
			
			@ -82,7 +133,6 @@ class Ontology:
 | 
			
		|||
        neg = "not " if is_neg else ""
 | 
			
		||||
        return f"({theory}{neg}{symbol})"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def lookup_solver_lit(self, lit: int) -> Tuple[Symbol, bool, bool]:
 | 
			
		||||
        atom = abs(lit)
 | 
			
		||||
        if (atom_symb := self.symbolic_atoms.get(atom, None)) is not None:
 | 
			
		||||
| 
						 | 
				
			
			@ -95,7 +145,6 @@ class Ontology:
 | 
			
		|||
program = """
 | 
			
		||||
a :- not b.
 | 
			
		||||
b :- not a.
 | 
			
		||||
c :- not d.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
def add_external_atoms(program: str) -> str:
 | 
			
		||||
    control = clingo.Control(["0"])
 | 
			
		||||
    control.add("base", [], program)
 | 
			
		||||
    control.add("base", [], program.replace("\n", ""))
 | 
			
		||||
    control.ground([("base", [])])
 | 
			
		||||
 | 
			
		||||
    theory_grammar = """
 | 
			
		||||
| 
						 | 
				
			
			@ -113,13 +162,14 @@ def add_external_atoms(program: str) -> str:
 | 
			
		|||
}.
 | 
			
		||||
"""
 | 
			
		||||
    external_atoms = "\n".join(
 | 
			
		||||
        f"{atom} :- &o{{{atom}}}." for atom in 
 | 
			
		||||
        (str(atom.symbol) for atom in control.symbolic_atoms)
 | 
			
		||||
        f"{atom} :- &o{{{atom}}}."
 | 
			
		||||
        for atom in (str(atom.symbol) for atom in control.symbolic_atoms)
 | 
			
		||||
    )
 | 
			
		||||
    return theory_grammar + program + external_atoms
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
program = add_external_atoms(program)
 | 
			
		||||
print(program)
 | 
			
		||||
control = clingo.Control(["0"])
 | 
			
		||||
propagator = Ontology()
 | 
			
		||||
control.register_propagator(propagator)
 | 
			
		||||
| 
						 | 
				
			
			@ -133,4 +183,3 @@ control.ground([("base", [])])
 | 
			
		|||
with control.solve(yield_=True) as solve_handle:
 | 
			
		||||
    for model in solve_handle:
 | 
			
		||||
        print("answer set:", model)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue