Exemple #1
0
    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)
Exemple #2
0
    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
Exemple #3
0
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))
Exemple #4
0
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:]
Exemple #5
0
                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)
Exemple #6
0
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)
Exemple #7
0
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)
Exemple #8
0
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)