def _incremental(ctl: Control): ctl.add('step0', [], 'a :- b. b :- a. {a;b}.') ctl.ground([('step0', [])]) ctl.solve() ctl.add('step1', [], 'c :- d. d :- c. {c;d}.') ctl.ground([('step1', [])]) ctl.solve()
def test_basic_example(): "Example taken from clingo module documentation: https://potassco.org/clingo/python-api/5.5/clingo/index.html" from clingo.symbol import Number from clingo.control import Control class Context: def inc(self, x): return Number(x.number + 1) def seq(self, x, y): return [x, y] def on_model(m): print(repr(m), m, dir(m)) assert False, "Clingo module seems to have python support. Some test on the output model are to be done (just be sure there is a `a` atom). Model: " + repr( m) assert clyngor.have_python_support() ctl = Control() try: ctl.add("base", [], "a. #script(python) #end.") except RuntimeError as err: # case where python support is not implemented assert err.args == ( '<block>:1:4-25: error: python support not available\n', ) assert not clyngor.have_python_support() else: # python support available ctl.ground([("base", [])], context=Context()) ctl.solve(on_model=on_model)
def main(self, ctl: Control, files: Sequence[str]): ''' Register the difference constraint propagator, and then ground and solve. ''' ctl.register_propagator(self._propagator) ctl.add("base", [], THEORY) if not files: files = ["-"] self._rewrite(ctl, files) ctl.ground([("base", [])]) if self._minimize is None: ctl.solve(on_model=self._propagator.on_model) else: ctl.add("bound", ["b", "v"], "&diff(head) { v-0 } <= b.") while cast(SolveResult, ctl.solve(on_model=self._on_model)).satisfiable: print("Found new bound: {}".format(self._bound)) if self._bound is None: break ctl.ground([ ("bound", [Number(cast(int, self._bound) - 1), self._minimize]) ]) if self._bound is not None: print("Optimum found")
def run(files: Sequence[str], parts: Parts, assign: Externals = ()): ''' Loads the given files into a control object, grounds the given program parts, and assign the given externals to the given truth values. ''' ctl = Control() print(' loading files:') for file_ in files: print(f' - {file_}') ctl.load(file_) print(' grounding:') for part in parts: print(f' - {part_str(part)}') ctl.ground([part]) if assign: print(' assigning externals:') for sym, truth in assign: print(f' - {sym}={truth}') ctl.assign_external(sym, truth) print(' solutions:') ctl.solve(on_model=lambda m: print(f' - {m}'))
def run(self): ''' Runs the example. ''' ctl = Control() ctl.load("example.lp") ctl.ground([("base", [])], context=self) ctl.solve(on_model=print)
def _assume(ctl: Control): ctl.add("base", [], '{a;b}.') ctl.ground([('base', [])]) lit_a = cast(SymbolicAtom, ctl.symbolic_atoms[Function("a")]).literal lit_b = cast(SymbolicAtom, ctl.symbolic_atoms[Function("b")]).literal ctl.solve(assumptions=[lit_a, lit_b]) ctl.solve(assumptions=[-lit_a, -lit_b])
def main(self, ctl: Control, files: Sequence[str]): ''' Main function of the application. ''' for path in files: ctl.load(path) if not files: ctl.load("-") ctl.ground([("base", [])], context=self) ctl.solve()
def main(self, ctl: Control, files: Sequence[str]): ''' The main function implementing incremental solving. ''' if not files: files = ["-"] for file_ in files: ctl.load(file_) ctl.add("check", ["t"], "#external query(t).") conf = self._conf step = 0 ret: Optional[SolveResult] = None while ((conf.imax is None or step < conf.imax) and (ret is None or step < conf.imin or ((conf.istop == "SAT" and not ret.satisfiable) or (conf.istop == "UNSAT" and not ret.unsatisfiable) or (conf.istop == "UNKNOWN" and not ret.unknown)))): parts = [] parts.append(("check", [Number(step)])) if step > 0: ctl.release_external(Function("query", [Number(step - 1)])) parts.append(("step", [Number(step)])) else: parts.append(("base", [])) ctl.ground(parts) ctl.assign_external(Function("query", [Number(step)]), True) ret, step = cast(SolveResult, ctl.solve()), step + 1
def main(self, ctl: Control, files: Sequence[str]): """ The main function called with a Control object and a list of files passed on the command line. """ if not files: files = ["-"] check: List[ast.AST] = [] with ProgramBuilder(ctl) as bld: trans = Transformer(bld, check) parse_files(files, trans.add) ctl.register_propagator(CheckPropagator(check)) ctl.ground([("base", [])]) ctl.solve()
def main(self, ctl: Control, files): prg = Program() ctl.register_observer(ProgramObserver(prg)) for f in files: ctl.load(f) if not files: ctl.load('-') ctl.ground([("base", [])]) fct, ukn = well_founded(prg) print('Facts:') print(f'{" ".join(map(str, fct))}') print('Unknown:') print(f'{" ".join(map(str, ukn))}') ctl.solve()
def approximate(ctl: Control) -> Optional[Tuple[Sequence[Symbol], Sequence[Symbol]]]: ''' Approximate the stable models of a program. Parameters ---------- ctl A control object with a program. Grounding should be performed on this control object before calling this function. Returns ------- Returns `None` if the problem is determined unsatisfiable. Otherwise, returns an approximation of the stable models of the program in form of a pair of sequences of symbols. Atoms contained in the first sequence are true and atoms not contained in the second sequence are false in all stable models. Notes ----- Runs in polynomial time. An approximation might be returned even if the problem is unsatisfiable. ''' # solve with a limit of 0 conflicts to propagate direct consequences assert isinstance(ctl.configuration.solve, Configuration) solve_limit = ctl.configuration.solve.solve_limit ctl.configuration.solve.solve_limit = 0 ctl.solve() ctl.configuration.solve.solve_limit = solve_limit ctl.cleanup() # check if the problem is conflicting if ctl.is_conflicting: return None # return approximation lower = [] upper = [] for sa in ctl.symbolic_atoms: upper.append(sa.symbol) if sa.is_fact: lower.append(sa.symbol) return lower, upper
class Checker: """ Class wrapping a solver to perform the second level check. """ _ctl: Control _map: List[Tuple[int, int]] def __init__(self): self._ctl = Control() self._map = [] def backend(self) -> Backend: """ Return the backend of the underlying solver. """ return self._ctl.backend() def add(self, guess_lit: int, check_lit: int): """ Map the given solver literal to the corresponding program literal in the checker. """ self._map.append((guess_lit, check_lit)) def ground(self, check: Sequence[ast.AST]): """ Ground the check program. """ with ProgramBuilder(self._ctl) as bld: for stm in check: bld.add(stm) self._ctl.ground([("base", [])]) def check(self, control: PropagateControl) -> bool: """ Return true if the check program is unsatisfiable w.r.t. to the atoms of the guess program. The truth values of the atoms of the guess program are stored in the assignment of the given control object. """ assignment = control.assignment assumptions = [] for guess_lit, check_lit in self._map: guess_truth = assignment.value(guess_lit) assumptions.append(check_lit if guess_truth else -check_lit) ret = cast(SolveResult, self._ctl.solve(assumptions)) if ret.unsatisfiable is not None: return ret.unsatisfiable raise RuntimeError("search interrupted")
def main(self, ctl: Control, files: Sequence[str]): ''' Main function implementing branch and bound optimization. ''' if not files: files = ["-"] for file_ in files: ctl.load(file_) ctl.add("bound", ["b"], ":- #sum { V,I: _minimize(V,I) } >= b.") ctl.ground([("base", [])]) while cast(SolveResult, ctl.solve(on_model=self._on_model)).satisfiable: print("Found new bound: {}".format(self._bound)) ctl.ground([("bound", [Number(cast(int, self._bound))])]) if self._bound is not None: print("Optimum found")
def __call__(self, ctl: Control): ctl.add('base', [], self._prg) # nocoverage ctl.ground([('base', [])]) # nocoverage ctl.solve() # nocoverage