def __init__(self): self.size = 1 self.blocked = set() self.barriers = set() self.targets = set() self.pos = dict() self.robots = [{}] self.moves = [] self.current_target = None self.solution = None ctl = Control() ctl.load("board.lp") ctl.ground([("base", [])]) ctl.solve(on_model=self.__on_model)
def solve_instance(self): prg = Control() prg.load("puzzle15.lp") prg.load(self.new_filename) ret, parts, step = SolveResult.unsatisfiable, [], 1 parts.append(("base", [])) while ret == SolveResult.unsatisfiable: parts.append(("step", [step])) parts.append(("check", [step])) prg.ground(parts) prg.release_external(Function("query", [step - 1])) prg.assign_external(Function("query", [step]), True) #f = lambda m: stdout.write(str(m)+'\n') print("step:" + str(step) + " Solving...") ret, parts, step = prg.solve(on_model=self.on_model), [], step + 1 if ret.__repr__() == 'UNSAT': ret = SolveResult.unsatisfiable else: ret = SolveResult.satisfiable
def main(): # Create a Control object that will unify models against the appropriate # predicates. Then load the asp file that encodes the problem domain. ctrl = Control(unifier=[Driver, Item, Assignment]) ctrl.load(ASP_PROGRAM) # Dynamically generate the instance data drivers = [Driver(name=n) for n in ["dave", "morri", "michael"]] items = [Item(name="item{}".format(i)) for i in range(1, 6)] instance = FactBase(drivers + items) # Add the instance data and ground the ASP program ctrl.add_facts(instance) ctrl.ground([("base", [])]) # Generate a solution - use a call back that saves the solution solution = None def on_model(model): nonlocal solution solution = model.facts(atoms=True) ctrl.solve(on_model=on_model) if not solution: raise ValueError("No solution found") # Do something with the solution - create a query so we can print out the # assignments for each driver. # query=solution.select(Assignment).where(lambda x,o: x.driver == o) query=solution.query(Driver,Assignment)\ .join(Driver.name == Assignment.driver)\ .group_by(Driver.name).order_by(Assignment.time).select(Assignment) for d, aiter in query.all(): assignments = list(aiter) if not assignments: print("Driver {} is not working today".format(d)) else: print("Driver {} must deliver: ".format(d)) for a in assignments: print("\t Item {} at time {}".format(a.item, a.time))
class Solver: def __init__(self, horizon=0): self.__horizon = horizon self.__prg = Control(['-t4']) self.__future = None self.__solution = None self.__assign = [] self.__prg.load("board.lp") self.__prg.load("robots.lp") parts = [ ("base", []) , ("check", [Number(0)]) , ("state", [Number(0)]) ] for t in range(1, self.__horizon+1): parts.extend([ ("trans", [Number(t)]) , ("check", [Number(t)]) , ("state", [Number(t)]) ]) self.__prg.ground(parts) self.__prg.assign_external(Function("horizon", [Number(self.__horizon)]), True) def __next(self): assert(self.__horizon < 30) self.__prg.assign_external(Function("horizon", [Number(self.__horizon)]), False) self.__horizon += 1 self.__prg.ground([ ("trans", [Number(self.__horizon)]) , ("check", [Number(self.__horizon)]) , ("state", [Number(self.__horizon)]) ]) self.__prg.assign_external(Function("horizon", [Number(self.__horizon)]), True) def start(self, board): self.__assign = [] for robot, (x, y) in board.pos.items(): self.__assign.append(Function("pos", [Function(robot), Number(x+1), Number(y+1), Number(0)])) self.__assign.append(Function("target", [ Function(board.current_target[0]) , Number(board.current_target[2] + 1) , Number(board.current_target[3] + 1) ])) for x in self.__assign: self.__prg.assign_external(x, True) self.__solution = None self.__future = self.__prg.solve(on_model=self.__on_model, async_=True) def busy(self): if self.__future is None: return False if self.__future.wait(0): if self.__solution is None: self.__next() self.__future = self.__prg.solve(on_model=self.__on_model, async_=True) return True else: self.__future = None return False return True def stop(self): if self.__future is not None: self.__future.cancel() self.__future.wait() self.__future = None self.get() def get(self): solution = self.__solution self.__solution = None for x in self.__assign: self.__prg.assign_external(x, False) self.__assign = [] return solution def __on_model(self, m): self.__solution = [] for atom in m.symbols(atoms=True): if atom.name == "move" and len(atom.arguments) == 4: c, x, y, t = [(n.number if n.type == SymbolType.Number else str(n)) for n in atom.arguments] self.__solution.append((c, x, y, t)) self.__solution.sort(key=lambda x: x[3]) p = None i = 0 for x in self.__solution: if p is not None and \ p[0] == x[0] and \ p[1] == x[1] and \ p[2] == x[2]: break p = x i += 1 del self.__solution[i:]
f.cancel() ret = f.get() else: ret = None if msg == "interrupt": state = States.IDLE elif msg == "exit": return elif msg == "less_pigeon_please": prg.assign_external(Function("p"), False) state = States.IDLE elif msg == "more_pigeon_please": prg.assign_external(Function("p"), True) state = States.IDLE elif msg == "solve": state = States.SOLVE else: raise (RuntimeError("unexpected message: " + msg)) if ret is not None and not ret.unknown: k = k + 1 prg.ground([("sleep", [Number(k)])]) prg.release_external(Function("sleep", [Number(k - 1)])) prg.assign_external(Function("sleep", [Number(k)]), True) finally: conn.close() prg = Control() prg.load("client.lp") main(prg)
class App: def __init__(self, args): self.control = Control() self.args = args self.horizon = 0 self.objects = 0 self.end = None def show(self, model): if not self.args.quiet: print("Model: {}".format(model)) def ground(self, kind): count = self.objects + self.horizon + 1 parts = [("expand", [Number(count)])] if self.args.scratch and count > 1: self.control = Control() for source in self.args.file: self.control.load(source) for i in range(0, self.objects): parts.append(("object", [Number(i + 1, count)])) for i in range(0, self.horizon): parts.append(("horizon", [Number(i + 1, count)])) if self.args.scratch or count == 1: for option in self.args.option: setattr(self.control.configuration, option[0], option[1]) parts.append(("base", [])) if kind: self.objects += 1 parts.append(("object", [Number(self.objects), Number(count)])) else: self.horizon += 1 parts.append(("horizon", [Number(self.horizon), Number(count)])) if self.args.verbose: print("") print("Objects: {}".format(Number(self.objects))) print("Horizon: {}".format(Number(self.horizon))) self.control.ground(parts) if self.args.verbose: print("Solving: {}".format(count)) def run(self): for source in self.args.file: self.control.load(source) if self.args.maxobj is None: self.end = self.control.get_const("n").number else: self.end = self.args.maxobj while self.objects < self.end: self.ground(True) while True: ret = self.control.solve(on_model=self.show) if self.args.stats: args = { "sort_keys": True, "indent": 0, "separators": (',', ': ') } stats = {} for x in [ "step", "enumerated", "time_cpu", "time_solve", "time_sat", "time_unsat", "time_total" ]: stats[x] = self.control.statistics[x] for x in ["lp", "ctx", "solvers"]: for y in self.control.statistics[x]: stats[y] = self.control.statistics[x][y] print(json.dumps(stats, *args)) if ret.satisfiable: break self.ground(False)
class SolveThread(Thread): STATE_SOLVE = 1 STATE_IDLE = 2 STATE_EXIT = 3 def __init__(self, connection): Thread.__init__(self) self.k = 0 self.prg = Control() self.prg.load("client.lp") self.prg.ground([("pigeon", []), ("sleep", [Number(self.k)])]) self.prg.assign_external(Function("sleep", [Number(self.k)]), True) self.state = SolveThread.STATE_IDLE self.input = Connection() self.output = connection def on_model(self, model): self.output.send("answer: " + str(model)), def on_finish(self, ret): self.output.send("finish: " + str(ret) + (" (INTERRUPTED)" if ret.interrupted else "")) def handle_message(self, msg): if msg == "interrupt": self.state = SolveThread.STATE_IDLE elif msg == "exit": self.state = SolveThread.STATE_EXIT elif msg == "less_pigeon_please": self.prg.assign_external(Function("p"), False) self.state = SolveThread.STATE_IDLE elif msg == "more_pigeon_please": self.prg.assign_external(Function("p"), True) self.state = SolveThread.STATE_IDLE elif msg == "solve": self.state = SolveThread.STATE_SOLVE else: raise (RuntimeError("unexpected message: " + msg)) def run(self): while True: if self.state == SolveThread.STATE_SOLVE: f = self.prg.solve(on_model=self.on_model, on_finish=self.on_finish, async_=True) msg = self.input.receive() if self.state == SolveThread.STATE_SOLVE: f.cancel() ret = f.get() else: ret = None self.handle_message(msg) if self.state == SolveThread.STATE_EXIT: return elif ret is not None and not ret.unknown: self.k = self.k + 1 self.prg.ground([("sleep", [Number(self.k)])]) self.prg.release_external( Function("sleep", [Number(self.k - 1)])) self.prg.assign_external(Function("sleep", [Number(self.k)]), True)
class VizloControl(Control): def add_to_painter(self, model: Union[Model, PythonModel, Collection[clingo.Symbol]]): """ will register model with the internal painter. On all consecutive calls to paint(), this model will be painted. :param model: the model to add to the painter. :return: """ self.painter.append(PythonModel(model)) def __init__(self, arguments: List[str] = [], logger=None, message_limit: int = 20, print_entire_models=False, atom_draw_maximum=15): self.control = Control(arguments, logger, message_limit) self.painter: List[PythonModel] = list() self.program: ASTProgram = list() self.raw_program: str = "" self.transformer = JustTheRulesTransformer() self._print_changes_only = not print_entire_models self._atom_draw_maximum = atom_draw_maximum def _set_print_only_changes(self, value: bool) -> None: self._print_changes_only = value def ground(self, parts: List[Tuple[str, List[Symbol]]], context: Any = None) -> None: self.control.ground(parts, context) def solve(self, assumptions: List[Union[Tuple[Symbol, bool], int]] = [], on_model=None, on_statistics=None, on_finish=None, yield_: bool = False, async_: bool = False) -> Union[SolveHandle, SolveResult]: return self.control.solve(assumptions, on_model, on_statistics, on_finish, yield_, async_) def load(self, path): prg = "" with open(path) as f: for line in f: prg += line self.program += prg self.control.load(path) def add(self, name: str, parameters: List[str], program: str) -> None: self.raw_program += program self.control.add(name, parameters, program) def find_nodes_corresponding_to_stable_models(self, g, stable_models): correspoding_nodes = set() for model in stable_models: for node in g.nodes(): log(f"{node} {type(node.model)} == {model} {type(model)} -> {set(node.model) == model}" ) if set(node.model) == model and len( g.edges(node)) == 0: # This is a leaf log(f"{node} <-> {model}") correspoding_nodes.add(node) break return correspoding_nodes def prune_graph_leading_to_models(self, graph: nx.DiGraph, models_as_nodes): before = len(graph) relevant_nodes = set() for model in models_as_nodes: for relevant_node in nx.all_simple_paths(graph, INITIAL_EMPTY_SET, model): relevant_nodes.update(relevant_node) all_nodes = set(graph.nodes()) irrelevant_nodes = all_nodes - relevant_nodes graph.remove_nodes_from(irrelevant_nodes) after = len(graph) log(f"Removed {before - after} of {before} nodes ({(before - after) / before})" ) def _make_graph(self, _sort=True): """ Ties together transformation and solving. Transforms the already added program parts and creates a solving tree. :param _sort: Whether the program should be sorted automatically. Setting this to false will likely result into wrong results! :return: :raises ValueError: """ if not len(self.raw_program): raise ValueError("Can't paint an empty program.") else: t = JustTheRulesTransformer() program = t.transform(self.raw_program, _sort) if len(self.painter): universe = get_ground_universe(program) global_assumptions = make_global_assumptions( universe, self.painter) solve_runner = SolveRunner(program, t.rule2signatures) g = solve_runner.make_graph(global_assumptions) else: solve_runner = SolveRunner(program, symbols_in_heads_map=t.rule2signatures) g = solve_runner.make_graph() return g def paint(self, atom_draw_maximum: int = 20, show_entire_model: bool = False, sort_program: bool = True, **kwargs): """ Will create a graph visualization of the solving process. If models have been added using add_to_painter, only the solving paths that lead to these models will be drawn. :param atom_draw_maximum: int The maximum amount of atoms that will be printed for each partial model. (default=20) :param show_entire_model: bool If false, only the atoms that have been added at a solving step will be printed (up to atom_draw_maximum). If true, all atoms will always be printed (up to atom_draw_maximum). (default=False) :param sort_program: If true, the rules of a program will be sorted and grouped by their dependencies. Each set of rules will contain all rules in which each atom in its heads is contained in a head. :param kwargs: kwargs will be forwarded to the visualisation module. See graph.draw() :return: """ if type(atom_draw_maximum) != int: raise ValueError( f"Argument atom_draw_maximum should be an integer (received {atom_draw_maximum})." ) g = self._make_graph(sort_program) display = NetworkxDisplay(g, atom_draw_maximum, not show_entire_model) img = display.draw(**kwargs) return img def _add_and_ground(self, prg): """Short cut for complex add and ground calls, should only be used for debugging purposes.""" self.add("base", [], prg) self.ground([("base", [])]) ################## # Just pass-through stuff ################## @property def configuration(self) -> Configuration: return self.control.configuration @property def is_conflicting(self) -> bool: return self.control.is_conflicting @property def statistics(self) -> dict: return self.control.statistics @property def symbolic_atoms(self) -> SymbolicAtoms: return self.control.symbolic_atoms @property def theory_atoms(self) -> TheoryAtomIter: return self.control.theory_atoms @property def use_enumeration_assumption(self) -> bool: return self.control.use_enumeration_assumption def assign_external(self, external: Union[Symbol, int], truth: Optional[bool], **kwargs) -> None: self.control.assign_external(external, truth, **kwargs) def backend(self) -> Backend: return self.control.backend() def builder(self) -> ProgramBuilder: return self.control.builder() def cleanup(self) -> None: self.control.cleanup() def get_const(self, name: str) -> Optional[Symbol]: return self.control.get_const(name) def interrupt(self): self.control.interrupt() def register_observer(self, observer, replace=False): self.register_observer(observer, replace) def release_external(self, symbol: Union[Symbol, int]) -> None: self.control.release_external(symbol)