def snakes_find_the_head(g):
    # We'll want a type that represents a cell in the grid. We use a datatype because we can make it a sum type -
    # that is, its 0-arity constructors represent a closed enumeration of distinct objects.
    Cell = Datatype("Cell")

    for x, y in g.coords():
        if g.snake(x, y) is not None:
            Cell.declare("cell_{}_{}".format(x, y))
    Cell = Cell.create()

    # We'll have two functions that we explicitly declare. One is the short-distance "connected" relationship.
    # The other is a convenience function for turning the coordinates of a cell into the datatype member that
    # represents that position.
    Connected = Function("Connected", Cell, Cell, BoolSort())
    XYToCell = Function("XYToCell", IntSort(), IntSort(), Cell)

    cell = {}
    for x, y in g.coords():
        if g.snake(x, y) is not None:
            cell[x, y] = getattr(Cell, "cell_{}_{}".format(x, y))
            g.add(XYToCell(x, y) == cell[x, y])

    # Two cells are connected *if and only* if they are adjacent and both hold snakes
    # We need to be really clear about the "and only if" part; a naive implementation here will let the
    # solver fill in other arbitrary values for `Connected` in order to make the desired outcome true.
    # We do this by ensuring there's a value declared for our Connected relationship between every pair
    # of potential arguments.

    for x1, y1 in g.coords():
        c1 = g.snake(x1, y1)
        if c1 is None:
            continue
        # If there's a snake here, the cell is connected to itself
        g.add(Connected(cell[x1, y1], cell[x1, y1]) == c1)
        for x2, y2 in g.coords():
            c2 = g.snake(x2, y2)
            if c2 is None or (x1, y1) == (x2, y2):
                continue
            if manh(x1, y1, x2, y2) == 1:
                g.add(Connected(cell[x1, y1], cell[x2, y2]) == And(c1, c2))
            else:
                # Without this, our function declaration is only partial. The solver can fill in missing values
                # in order to scupper our good intentions.
                g.add(Not(Connected(cell[x1, y1], cell[x2, y2])))

    # The transitive closure of Connectedness is Reaches
    Reaches = TransitiveClosure(Connected)

    # For every cell in the grid, if it's a snake then we can connect it to the head position
    hx, hy = g.head()
    for x, y in g.coords():
        c = g.snake(x, y)
        if c is None:
            continue

        g.add(Implies(c, Reaches(cell[x, y], XYToCell(hx, hy))))
Exemple #2
0
def skyscraper(givens: Dict[Direction, List[int]]) -> str:
    """Solver for Skyscraper minipuzzles."""
    sym = grilops.make_number_range_symbol_set(1, SIZE)
    sg = grilops.SymbolGrid(LATTICE, sym)
    shifter = Shifter(sg.solver)

    # Each row and each column contains each building height exactly once.
    for y in range(SIZE):
        sg.solver.add(Distinct(*[sg.grid[Point(y, x)] for x in range(SIZE)]))
    for x in range(SIZE):
        sg.solver.add(Distinct(*[sg.grid[Point(y, x)] for y in range(SIZE)]))

    # We'll use the sightlines accumulator to keep track of a tuple storing:
    #   the tallest building we've seen so far
    #   the number of visible buildings we've encountered
    Acc = Datatype("Acc")  # pylint: disable=C0103
    Acc.declare("acc", ("tallest", IntSort()), ("num_visible", IntSort()))
    Acc = Acc.create()  # pylint: disable=C0103

    def accumulate(a, height):
        return Acc.acc(
            If(height > Acc.tallest(a), height, Acc.tallest(a)),
            If(height > Acc.tallest(a),
               Acc.num_visible(a) + 1, Acc.num_visible(a)))

    for d, gs in givens.items():
        for i, g in enumerate(gs):
            if d.vector.dy != 0:
                g = g - shifter.col_shifts.get(i, 0)
                p = Point(0 if d.vector.dy < 0 else SIZE - 1, i)
            elif d.vector.dx != 0:
                g = g - shifter.row_shifts.get(i, 0)
                p = Point(i, 0 if d.vector.dx < 0 else SIZE - 1)
            sg.solver.add(g == Acc.num_visible(  # type: ignore[attr-defined]
                grilops.sightlines.reduce_cells(
                    sg,
                    p,
                    LATTICE.opposite_direction(d),
                    Acc.acc(0, 0),  # type: ignore[attr-defined]
                    accumulate)))

    assert sg.solve()
    sg.print()
    print()
    shifter.print_shifts()
    print()
    return shifter.eval_binary()
    def __init__(self, template, encoder, encoder_info, spec):
        self._template = template
        self._spec = spec
        self._encoder = encoder
        self._encoder_info = encoder_info

        # state
        self.state_type = Datatype('T%d' % template.template_index)
        for i in range(0, template.bound):
            self.state_type.declare('t%d_%d' % (template.template_index, i))
        self.state_sort = self.state_type.create()

        # output functions
        self.output_functions = [Function(str(output_signal),
                                          self.state_sort, BoolSort())
                                 for output_signal in template.outputs]

        logging.debug("Output functions for template %d: %s",
                      template.template_index, self.output_functions)

        self.guard_function = None
        # guard function: t, I, t' -> g [BitVec(Sum_i |Ti|])
        self._define_guard()

        # transition enabled
        self.is_enabled = None
        self._define_is_enabled()

        # any enabled
        self.is_any_enabled = None
        self._define_is_any_enabled()

        # lazy initialization
        self._guard_set = None

        self.state_guard = None
        self._define_state_guard()

        # declare function
        self.delta_enabled_functions = [self._get_delta_enabled_function(i)
                                        for i in range(0, template.cutoff)]
Exemple #4
0
def main():
    """Skyscraper solver example."""
    lattice = grilops.get_square_lattice(SIZE)
    sg = grilops.SymbolGrid(lattice, SYM)

    # Each row and each column contains each building height exactly once.
    for y in range(SIZE):
        sg.solver.add(Distinct(*[sg.grid[Point(y, x)] for x in range(SIZE)]))
    for x in range(SIZE):
        sg.solver.add(Distinct(*[sg.grid[Point(y, x)] for y in range(SIZE)]))

    # We'll use the sightlines accumulator to keep track of a tuple storing:
    #   the tallest building we've seen so far
    #   the number of visible buildings we've encountered
    Acc = Datatype("Acc")  # pylint: disable=C0103
    Acc.declare("acc", ("tallest", IntSort()), ("num_visible", IntSort()))
    Acc = Acc.create()  # pylint: disable=C0103

    def accumulate(a, height):
        return Acc.acc(
            If(height > Acc.tallest(a), height, Acc.tallest(a)),
            If(height > Acc.tallest(a),
               Acc.num_visible(a) + 1, Acc.num_visible(a)))

    for x, c in enumerate(GIVEN_TOP):
        sg.solver.add(c == Acc.num_visible(
            grilops.sightlines.reduce_cells(sg, Point(0, x), Vector(1, 0),
                                            Acc.acc(0, 0), accumulate)))
    for y, c in enumerate(GIVEN_LEFT):
        sg.solver.add(c == Acc.num_visible(
            grilops.sightlines.reduce_cells(sg, Point(y, 0), Vector(0, 1),
                                            Acc.acc(0, 0), accumulate)))
    for y, c in enumerate(GIVEN_RIGHT):
        sg.solver.add(c == Acc.num_visible(
            grilops.sightlines.reduce_cells(sg, Point(
                y, SIZE - 1), Vector(0, -1), Acc.acc(0, 0), accumulate)))
    for x, c in enumerate(GIVEN_BOTTOM):
        sg.solver.add(c == Acc.num_visible(
            grilops.sightlines.reduce_cells(sg, Point(
                SIZE - 1, x), Vector(-1, 0), Acc.acc(0, 0), accumulate)))

    if sg.solve():
        sg.print()
        print()
        if sg.is_unique():
            print("Unique solution")
        else:
            print("Alternate solution")
            sg.print()
    else:
        print("No solution")
Exemple #5
0
from .meyer import U
from .util.z3py_set import Set
from .util.z3py_rel import Rel
from .util.z3py_util import const, consts, show_record_element

## @file program.py
#  This module can be used for definition of specification/program instance
#
#

SET = ArraySort(U, BoolSort())
# OOPSet = ArraySort(IntSort(), ArraySort(U, BoolSort()))
PRE = ArraySort(U, BoolSort())
POST = ArraySort(U, ArraySort(U, BoolSort()))

PROG = Datatype('Prog')
PROG.declare('mk_prog', ('set', SET), ('pre', PRE), ('post', POST))
PROG = PROG.create()
set_ = PROG.set
pre_ = PROG.pre
post_ = PROG.post


class Program():
    """Base class for Program instance."""

    #  @param p A program instance created by Z3.py.
    def __init__(self, p):
        self.p = p

    #  @param x An element that is included in Set of this program.
    def encode_automaton(self, automaton, automaton_index,
                         is_architecture_specific, cutoff, global_cutoff):
        '''
        Encodes the given automaton

        :param automaton: Automaton instance
        :param automaton_index: Automaton index used for identifying the lambda
                                function corresponding to the automaton
        :param is_architecture_specific: Whether automaton is required
                                         by the used architecture
        :param cutoff: Cut-off associated with the current automaton
        :param global_cutoff: Maximum of all automata-specific cut-offs
        '''
        # declare UCT state
        uct_state = Datatype('Q_%d' % automaton_index)
        state_prefix = 'q%d_' % automaton_index

        nodes_list = list(automaton.nodes)
        for node in nodes_list:
            uct_state.declare(state_prefix + node.name)
        uct_state = uct_state.create()
        uct_states_dict = {nodes_list[i].name:
                           getattr(uct_state, uct_state.constructor(i).name())
                           for i in range(len(nodes_list))}

        # declare lambda functions
        lambda_b_function_argument_sorts = \
            [uct_state] + \
            self.get_fresh_global_state_sorts(cutoff=cutoff) + \
            [BoolSort()]

        lambda_b_function = Function('lambda_b_%d' % (automaton_index),
                                     lambda_b_function_argument_sorts)

        lambda_s_function_argument_sorts = \
            [uct_state] + \
            self.get_fresh_global_state_sorts(cutoff=cutoff) + \
            [IntSort()]

        lambda_s_function = Function('lambda_s_%d' % (automaton_index),
                                     lambda_s_function_argument_sorts)

        # avoid global deadlocks in case of the fairness property
        if is_architecture_specific:
            self._avoid_deadlocks(uct_state, lambda_b_function,
                                  cutoff, global_cutoff)

        assert(len(automaton.initial_sets_list) == 1)
        initial_uct_states = [uct_states_dict[node.name]
                              for node in automaton.initial_sets_list[0]]
        initial_system_states = self._get_initial_system_states(cutoff=cutoff)

        # list of tuples in format (q0, (t1, t2, ...))
        initial_state_tuples = product(*[initial_uct_states,
                                         initial_system_states])
        # merge tuples
        initial_state_tuples = [tuple([item[0]] + list(item[1]))
                                for item in initial_state_tuples]

        logging.debug("Automaton %d   Initial states: %s",
                      automaton_index, initial_state_tuples)

        for initial_uct_state in initial_state_tuples:
            self.encoder_info.solver.add(lambda_b_function(initial_uct_state))
            self.encoder_info.solver.add(lambda_s_function(initial_uct_state) == 0)

        # (template function, instance index)
        template_instance_index_tuples = \
            self._get_templates_instance_index_tuples(cutoff=cutoff)

        # assignment of the scheduling variables the particular processes
        # are scheduled (k,i) -> scheduling variable assignment list
        schedule_values_dict = dict(self.spec.get_schedule_values())
        scheduling_signals = self.spec.get_scheduling_signals()

        # used for SCC lambda_s optimization
        sccs = build_state_to_rejecting_scc(automaton)
        scc_lambda_functions = \
            {scc: Function('lambda_s_%d_%d' % (automaton_index, scc_index),
                           [uct_state] +
                           self.get_fresh_global_state_sorts(cutoff=cutoff) +
                           [BitVecSort(len(scc))])
             for scc_index, scc in enumerate(sccs.values())}

        spec_cutoff_process_indices = \
            self.get_process_indices(cutoff=self.spec.cutoff)

        global_state_tuples = \
            self.get_fresh_global_state_variables(cutoff=cutoff,
                                                  prefix="curr",
                                                  include_indices=True)

        global_state_dict = dict(global_state_tuples)

        current_global_state = [state_variable for _, state_variable
                                in global_state_tuples]

        input_signals_set = {(t[0].template_index, t[1]):
                             t[0].get_input_signals(t[1])
                             for t in template_instance_index_tuples}

        input_signals_list = [sig for t in template_instance_index_tuples
                              for sig in t[0].get_input_signals(t[1])]

        input_signal_expr_dict = {sig: Bool(str(sig))
                                  for sig in input_signals_list}

        # dictionary of output signals -> function call
        output_signal_expr_dict = \
            {signal_name:
             signal_function(global_state_dict[(template_function.template_index,
                                                instance_index)])
             for template_function, instance_index in template_instance_index_tuples
             for signal_name, signal_function in
             template_function.get_output_signals_function_dict(instance_index).items()}

        function_placeholder_signals_set = \
             set(self.architecture.get_placeholder_signals(cutoff))

        transitions = [(src_node, transition, target_node_info)
                       for src_node in automaton.nodes
                       for transition, target_node_infos
                       in src_node.transitions.items()
                       for target_node_info in target_node_infos[0]]
        node = None
        for src_node, transition, target_node_info in transitions:

            target_node, is_rejecting_target_node = target_node_info

            logging.debug("Automaton: %d: %s->%s, condition: %s",
                          automaton_index, src_node.name, target_node.name,
                          transition)

            for templ_func, i in template_instance_index_tuples:
                # we use k for the template index and i for the instance index
                # as defined in the paper
                k = templ_func.template_index

                others_global_state_tuples = \
                    [(k_i, state_variable)
                     for k_i, state_variable in
                     filter(lambda k_i_state_tuple: k_i_state_tuple[0] !=
                            (k, i),
                            global_state_tuples)]

                others_global_state = [state for _, state
                                       in others_global_state_tuples]

                current_local_state = global_state_dict[(k, i)]

                next_local_state = Const('t_next_%d_%d' % (k, i),
                                         templ_func.state_sort)
                next_global_state = [state
                                     if k_i != (k, i)
                                     else next_local_state
                                     for k_i, state in global_state_tuples]

                # get scheduling assignment if current instance is scheduled
                sched_assignment = schedule_values_dict[(k, i)]
                sched_assignment_dict = \
                    {scheduling_signals[i]: sched_assignment[i]
                     for i in range(0, len(scheduling_signals))}

                logging.debug("\tinstance: (%d, %d) sched=%s",
                              k, i, sched_assignment)

                # parameters for functions
                guard_set_call_expr = \
                    templ_func.guard_set(
                        self._blowup_state_set(others_global_state_tuples,
                                               spec_cutoff_process_indices,
                                               (k, i)))

                # only add constraint if scheduling assignment
                # matches the label
                if not self._compare_scheduling(sched_assignment_dict,
                                                transition):
                    logging.debug("\tSKIP %s->%s, condition: %s, scheduling=%s"
                                  % (src_node.name, target_node.name,
                                     transition, sched_assignment))
                    continue

                transition_keys = set(transition.keys())
                used_input_signals = transition_keys.intersection(set(input_signals_list))
                used_output_signals = transition_keys.intersection(output_signal_expr_dict.keys())
                used_placeholder_signals = transition_keys.intersection(function_placeholder_signals_set)
                used_scheduler_signals = transition_keys.intersection(set(scheduling_signals))

                assert(len(used_input_signals) + \
                       len(used_output_signals) + \
                       len(used_placeholder_signals) + \
                       len(used_scheduler_signals) == len(transition.items()))

                condition = []

                for input_signal in used_input_signals:
                    condition.append(input_signal_expr_dict[input_signal] ==
                                     transition[input_signal])

                for output_signal in used_output_signals:
                    condition.append(output_signal_expr_dict[output_signal] ==
                                     transition[output_signal])

                for placeholder_signal in used_placeholder_signals:
                    ph_instance = (placeholder_signal.template_index,
                                   placeholder_signal.instance_index)
                    ph_relative_global_state_tuples = \
                        list(filter(lambda x: x[0] !=
                                    ph_instance, global_state_tuples))
                    ph_template_func = self.encoder_info.template_functions[
                        placeholder_signal.template_index]

                    ph_gs = ph_template_func.guard_set(
                        self._blowup_state_set(ph_relative_global_state_tuples,
                                               spec_cutoff_process_indices,
                                               ph_instance))

                    ph_relative_current_local_state = global_state_dict[ph_instance]
                    ph_relative_current_inputs = \
                        [input_signal_expr_dict[sig]
                         for sig in input_signals_set[ph_instance]]

                    if placeholder_signal.name.startswith('enabled'):
                        condition.append(ph_template_func.is_any_enabled(
                            [ph_relative_current_local_state] + \
                            ph_relative_current_inputs + \
                            [ph_gs]) == transition[placeholder_signal])
                    elif placeholder_signal.name.startswith('active'):
                        condition.append(self.encoder_info.is_scheduled(
                            [placeholder_signal.template_index,
                             placeholder_signal.instance_index] + \
                            sched_assignment) == transition[placeholder_signal])
                    elif placeholder_signal.name.startswith('init'):
                        req_initial_states = ph_template_func.get_initial_states()
                        assert(len(req_initial_states) == 1)
                        condition.append(
                            (ph_relative_current_local_state == req_initial_states[0]) ==
                            transition[placeholder_signal])
                    else:
                        raise Exception(placeholder_signal.name)

                condition_expression = True
                if len(condition) > 0:
                    condition_expression = And(*condition)

                current_local_input_arguments = [input_signal_expr_dict[sig]
                                                 for sig in input_signals_set[(k, i)]]
                input_arguments = [input_signal_expr_dict[signal]
                                   for signal in input_signals_list]
                forall_arguments = \
                    [current_local_state, next_local_state] + \
                    others_global_state + \
                    input_arguments

                current_combined_state_parameters = \
                    [uct_states_dict[src_node.name]] + \
                    current_global_state
                next_combined_state_parameters = \
                    [uct_states_dict[target_node.name]] + \
                    next_global_state

                delta_enabled_function_parameters = [current_local_state] + \
                                                    current_local_input_arguments + \
                                                    [next_local_state] + \
                                                    [guard_set_call_expr]

                lambda_s_req_expr = None
                if self._encoding_optimization & EncodingOptimization.LAMBDA_SCC:
                    logging.debug("Use LAMBDA_SCC optimization")
                    lambda_s_req_expr = True
                    current_scc = sccs.get(src_node)
                    if current_scc is not None and current_scc == sccs.get(target_node):
                        scc_ls_func = scc_lambda_functions[current_scc]
                        lambda_s_req_expr = \
                            [UGE, UGT][is_rejecting_target_node](scc_ls_func(next_combined_state_parameters),
                                                                 scc_ls_func(current_combined_state_parameters))
                else:
                    # using no lambda_s optimizations
                    if is_rejecting_target_node:
                        lambda_s_req_expr = (
                            lambda_s_function(next_combined_state_parameters) >
                            lambda_s_function(current_combined_state_parameters))
                    else:
                        lambda_s_req_expr = (
                            lambda_s_function(next_combined_state_parameters) >=
                            lambda_s_function(current_combined_state_parameters))

                extended_condition_expr = \
                    templ_func.delta_enabled_functions[i](
                        delta_enabled_function_parameters)

                extended_condition_expr = \
                    Or(extended_condition_expr,
                       And(current_local_state == next_local_state,
                           Not(templ_func.is_any_enabled(
                               [current_local_state] + \
                               current_local_input_arguments + \
                               [guard_set_call_expr]))))

                expr = ForAll(forall_arguments, Implies(
                    And(lambda_b_function(current_combined_state_parameters),
                        condition_expression,
                        extended_condition_expr),
                    And(lambda_b_function(next_combined_state_parameters),
                        lambda_s_req_expr)))

                logging.debug("\tADD  %s->%s, condition: %s, scheduling=%s",
                              src_node.name, target_node.name,
                              transition, sched_assignment)

                self.encoder_info.solver.add(expr)
Exemple #7
0
    def test_datatype_payloads(self):
        lattice = get_square_lattice(3)
        sym = make_number_range_symbol_set(0, 2)
        row_grid = SymbolGrid(lattice, sym)
        col_grid = SymbolGrid(lattice, sym, solver=row_grid.solver)

        RowCol = Datatype("RowCol")
        RowCol.declare("row_col", ("row", IntSort()), ("col", IntSort()))
        RowCol = RowCol.create()

        sc = ShapeConstrainer(lattice, [
            Shape([
                (Vector(0, 0), RowCol.row_col(0, 0)),
                (Vector(0, 1), RowCol.row_col(0, 1)),
                (Vector(1, 0), RowCol.row_col(1, 0)),
            ]),
            Shape([
                (Vector(0, 1), RowCol.row_col(0, 2)),
                (Vector(1, 0), RowCol.row_col(1, 1)),
                (Vector(1, 1), RowCol.row_col(1, 2)),
                (Vector(2, 1), RowCol.row_col(2, 2)),
            ]),
            Shape([
                (Vector(0, 0), RowCol.row_col(2, 0)),
                (Vector(0, 1), RowCol.row_col(2, 1)),
            ]),
        ],
                              solver=row_grid.solver,
                              complete=True)

        for p in lattice.points:
            row_grid.solver.add(
                row_grid.cell_is(p, RowCol.row(sc.shape_payload_grid[p])))
            col_grid.solver.add(
                col_grid.cell_is(p, RowCol.col(sc.shape_payload_grid[p])))

        self.assertTrue(row_grid.solve())
        solved_row_grid = row_grid.solved_grid()
        solved_col_grid = col_grid.solved_grid()
        for p in lattice.points:
            self.assertEqual(solved_row_grid[p], p.y)
            self.assertEqual(solved_col_grid[p], p.x)
        self.assertTrue(row_grid.is_unique())
class TemplateFunction(metaclass=ABCMeta):
    '''
    Provides functionality for generating and adding template
    specific formulae to the SMT context
    '''

    def __init__(self, template, encoder, encoder_info, spec):
        self._template = template
        self._spec = spec
        self._encoder = encoder
        self._encoder_info = encoder_info

        # state
        self.state_type = Datatype('T%d' % template.template_index)
        for i in range(0, template.bound):
            self.state_type.declare('t%d_%d' % (template.template_index, i))
        self.state_sort = self.state_type.create()

        # output functions
        self.output_functions = [Function(str(output_signal),
                                          self.state_sort, BoolSort())
                                 for output_signal in template.outputs]

        logging.debug("Output functions for template %d: %s",
                      template.template_index, self.output_functions)

        self.guard_function = None
        # guard function: t, I, t' -> g [BitVec(Sum_i |Ti|])
        self._define_guard()

        # transition enabled
        self.is_enabled = None
        self._define_is_enabled()

        # any enabled
        self.is_any_enabled = None
        self._define_is_any_enabled()

        # lazy initialization
        self._guard_set = None

        self.state_guard = None
        self._define_state_guard()

        # declare function
        self.delta_enabled_functions = [self._get_delta_enabled_function(i)
                                        for i in range(0, template.cutoff)]

#         self._define_optimizations()

    def _define_guard(self):
        """Defines the guard function

        guard: T, I, T -> BitVec(Sum_i |Ti|)
        t  -- current state
        i  -- multiple parameters corresponding to the inputs I
        t' -- successor state
        """

        guard_function_params = [self.state_sort] + \
            self._get_input_sorts() + \
            [self.state_sort] + \
            [BitVecSort(self._encoder_info.guard_size)]
        self.guard_function = Function(str('guard_%d' %
                                           self._template.template_index),
                                       guard_function_params)

    def _get_delta_enabled_function(self, instance_index):
        """Defines the function delta_enabled

        delta_enabled_i: T x I x T x S' -> Bool
        t, i, t -- transition identifying values
        s_s     -- current global state set

        The scheduling must be ensured by an external function.

        """
        function_arguments = self._get_guard_params_sorts() + \
            [BitVecSort(self._encoder_info.guard_size)] + [BoolSort()]

        function_declaration = Function('delta_enabled_%d_%d' %
                                        (self._template.template_index,
                                         instance_index),
                                        function_arguments)

        t_current_variable = Const('tc', self.state_sort)
        t_next_variable = Const('tn', self.state_sort)
        input_variables = \
            self._get_fresh_input_variables(instance_index=instance_index)
        guard_params = [t_current_variable] + \
            input_variables + \
            [t_next_variable]

        current_guard_set_parameter = BitVec('s', self._encoder_info.guard_size)

        function_parameters = guard_params + [current_guard_set_parameter]

        function_body = \
            ForAll(function_parameters,
                   function_declaration(function_parameters) ==
                   self._encoder_info.eval_guard(current_guard_set_parameter,
                                                 self.guard_function(guard_params)))

        self._encoder_info.solver.add(function_body)
        return function_declaration

    def _define_is_any_enabled(self):
        """Defines the function is_any_enabled

        is_any_enabled checks if there exists at least one enabled
        transition for a given

        * current state
        * input values
        * global state set

        is_any_enabled: T x I x S' -> Bool
        t, i -- part of transition identifying values
        s_s  -- global state set
        """
        function_arguments = self._get_guard_params_sorts()[:-1] + \
            [BitVecSort(self._encoder_info.guard_size), BoolSort()]

        # function declaration
        self.is_any_enabled = Function('is_any_enabled_%d' %
                                       (self._template.template_index),
                                       function_arguments)

        # function body
        guard_parameters = self.get_fresh_guard_params_variables()[:-1]
        global_state_parameter = BitVec('gs', self._encoder_info.guard_size)
        function_parameters = guard_parameters + [global_state_parameter]

        t_next = Const('tn', self.state_sort)

        function_body = \
            ForAll(function_parameters,
                   self.is_any_enabled(function_parameters) ==
                   Exists(t_next,
                          self._encoder_info.eval_guard(
                              global_state_parameter,
                              self.guard_function(guard_parameters + [t_next]))))
        self._encoder_info.solver.add(function_body)

    def _define_is_enabled(self):
        '''
        Defines the function is_enabled

        is_enabled checks if the transition identified by
        (current state, next state) is enabled for the given

        * input values
        * global state set

        is_any_enabled: T x I x T x S' -> Bool
        t, i, t -- current state, input, next state
        s_s     -- global state set
        '''

        function_arguments = \
            self._get_guard_params_sorts() + \
            [BitVecSort(self._encoder_info.guard_size), BoolSort()]

        # function declaration
        self.is_enabled = Function('is_enabled_%d' %
                                   (self._template.template_index),
                                   function_arguments)

        # function body
        guard_parameters = self.get_fresh_guard_params_variables()
        global_state_parameter = BitVec('gs', self._encoder_info.guard_size)
        function_parameters = guard_parameters + [global_state_parameter]

        function_body = \
            ForAll(function_parameters,
                   self.is_enabled(function_parameters) ==
                   self._encoder_info.eval_guard(
                       global_state_parameter,
                       self.guard_function(guard_parameters)))

        self._encoder_info.solver.add(function_body)

    @property
    def guard_set(self):
        '''
        Guard set function
        '''
        if self._guard_set is None:
            self.define_guard_set()
        return self._guard_set

    def define_guard_set(self):
        """
        Defines the method that determines the relative global states guard set

        T1 T1 ... T1 T2 ... Tn -> BitVec
        """
        function_arguments = \
            self._encoder.get_fresh_global_state_sorts(
                absent_process=(self._template.template_index, 0)) + \
            [BitVecSort(self._encoder_info.guard_size)]
        function_declaration = Function('guard_set_%d' %
                                        (self._template.template_index),
                                        function_arguments)

        self._guard_set = function_declaration

        # function body
        global_state_tuples_parameters = \
            self._encoder.get_fresh_global_state_variables(
                absent_process=(self._template.template_index, 0))

        global_state_tuples_indices = \
            [k for _, k, _ in
             self._encoder.get_fresh_global_state_tuples(
                 absent_process=(self._template.template_index, 0))]
        function_parameters = global_state_tuples_parameters

        assert(len(global_state_tuples_parameters) ==
               len(global_state_tuples_indices))

        bit_sum_expr = \
            reduce(lambda x, y: x | y,
                   [self._encoder_info.template_functions
                    [global_state_tuples_indices[i]].state_guard(
                        global_state_tuples_parameters[i])
                    for i in range(0, len(global_state_tuples_indices))])

        function_body = ForAll(function_parameters,
                               function_declaration(function_parameters) ==
                               bit_sum_expr)
        self._encoder_info.solver.add(function_body)

    def _get_guard_params_sorts(self):
        '''
        Returns the guard parameter sorts T_k x I_k x T_k
        where I_k is Bool x Bool x ... x Bool (depending on number of inputs)
        '''
        return [self.state_sort] + self._get_input_sorts() + [self.state_sort]

    def get_fresh_guard_params_variables(self):
        '''
        Returns new guard parameter variables (tc, i_k, tn) of T_k x I_k x T_k

        The variable names are tc, tn, and inputs as defined by the signal names
        '''
        return [Const('tc', self.state_sort)] + \
                self._get_fresh_input_variables() + \
                [Const('tn', self.state_sort)]

    def _get_fresh_input_variables(self, instance_index=None, sfx_pfx=""):
        '''
        Gets fresh input variables i_k of I_k (i.e., Boolean variables of Boolean)

        The signal name can be extended by a suffix that consists of the
        parameter sfx_pfx concatenated with an optional instance index.
        By default, the variable name is equal to the signal name.

        :param instance_index:
        :param sfx_pfx:
        '''
        suffix = sfx_pfx or "" + ("" if instance_index is None \
                                  else "_" + str(instance_index))

        return [Bool(str(input_signal) + suffix)
                for input_signal in self._template.inputs]

    def get_fresh_input_variables(self, prefix=None):
        '''
        Returns fresh input variables

        :todo: TODO: clear, improve!
               instance_index can also be some suffix string
        :param prefix:
        '''
        return self._get_fresh_input_variables(instance_index=prefix)

    def get_fresh_input_assignments(self):
        '''
        Returns a tuple of possible assignments for each input variable
        '''
        return [(False, True)] * len(self._template.inputs)

    def _get_input_sorts(self):
        '''
        Returns a list of sorts used for the SMT representation of the
        template's input signals
        '''
        return [BoolSort(), ] * len(self._template.inputs)

    def get_initial_states(self):
        '''
        Returns Z3 consts that represent initial states of the given template
        '''
        return [getattr(self.state_sort, self.state_sort.constructor(i).name())
                for i in range(len(self._template.initial_states))]

    def get_input_signals(self, instance_index=None):
        '''
        Returns input signals for a particular instance or quantified signals

        Returns the input signals for a single instance of the particular
        template or the quantified template signals if no instance index is
        provided

        :param instance_index:
        '''
        if instance_index is None:
            return self._template.inputs
        return [template_input.get_instance_signal(instance_index)
                for template_input in self._template.inputs]

    def get_output_signals_function_dict(self, instance_index):
        """
        Returns output signal mapped to SMT functions

        Returns a dictionary of signal name -> SMT function for all
        instantiated signal names corresponding to template signals
        """
        return {self._template.outputs[i].get_instance_signal(instance_index):
                self.output_functions[i] for i
                in range(0, len(self.output_functions))}

    def _define_optimizations(self):
        # add state order
        idx_state_func = Function('state_index%d' % self.template_index,
                                        IntSort(), self.state_sort)
        state_indices = [(getattr(self.state_sort,
                          self.state_sort.constructor(i).name()), i)
                         for i in range(0, self._template.bound)]

        state_order_conjuncts = \
            [idx_state_func(index) == state
             for state, index in state_indices]
        self._encoder_info.solver.add(And(state_order_conjuncts))

        # if t_i is connected some t_{j} with j>=i+2, then there
        # must be a connection from t_i to t_{i+1} or there is some
        # other incoming transition for the particular node
        idx_curr_var = Int('idx_curr')
        idx_next_var = Int('idx_next')
        input_vars = self.get_fresh_input_variables()

        guard_params = [idx_state_func(idx_curr_var)] + input_vars + \
            [idx_state_func(idx_next_var)]
        exists_transition2 = \
            Exists(
               input_vars + [idx_next_var],
               And(idx_next_var > idx_curr_var + 1,
                   (self.guard_function(guard_params) !=
                    BitVecVal(0, self._encoder_info.guard_size))))

        guard_params = [idx_state_func(idx_curr_var)] + input_vars + \
            [idx_state_func(idx_curr_var + 1)]
        exists_transition1 = \
            Exists(
               input_vars,
               (self.guard_function(guard_params) !=
                BitVecVal(0, self._encoder_info.guard_size)))

        input_vars_other = self.get_fresh_input_variables(prefix="other")
        idx_other_var = Int('idx_other')
        guard_params = [idx_state_func(idx_other_var)] + input_vars_other + \
            [idx_state_func(idx_curr_var + 1)]
        exists_transition1_other = \
            Exists(
               [idx_other_var] + input_vars_other,
               And(idx_other_var != idx_curr_var + 1,
                   self.guard_function(guard_params) !=
                   BitVecVal(0, self._encoder_info.guard_size)))

#         self._encoder_info.solver.add(
#             ForAll([idx_curr_var],
#                Implies(
#                    And([idx_curr_var >= 0,
#                         idx_curr_var < self._template.bound - 2]),
#                    Implies(exists_transition2,
#                            Or(exists_transition1, exists_transition1_other)))))

    @property
    def template_index(self):
        return self._template.template_index

    @abstractmethod
    def _define_state_guard(self):
        pass
Exemple #9
0
"""Z3 implementation of L - the meta-Kappa devised by Adrien Husson and Jean
Krivine - using the Python bindings for Z3.
"""

from z3 import Datatype, ArraySort, IntSort, BoolSort, Function, Const
from z3_helpers import Iff, Equals

# Node is a datatype representing a vertex or node in a Kappa graph.
Node = Datatype('Node')
Node.declare('node', ('unique_identifier', IntSort()))
Node = Node.create()

# A datatype for storing a pair of edges
Edge = Datatype('Edge')
Edge.declare('edge', ('node1', Node), ('node2', Node))
Edge = Edge.create()

Nodeset = ArraySort(Node, BoolSort())
Edgeset = ArraySort(Edge, BoolSort())

Labelset = ArraySort(IntSort(), BoolSort())
Labelmap = ArraySort(Node, Labelset)

# Graph, before a rule or action has applied. Merged Pregraph and Postgraph
# into a single datatype.
Graph = Datatype('Graph')
Graph.declare('graph', ('has', Nodeset), ('links', Edgeset),
              ('parents', Edgeset), ('labelmap', Labelmap))
Graph = Graph.create()

# Atomic action. An Action is comprised of a set of these.
Exemple #10
0
HEIGHT, WIDTH = 6, 10
LATTICE = grilops.get_rectangle_lattice(HEIGHT, WIDTH)

SYM = grilops.make_letter_range_symbol_set("A", "Z")


class Color(IntEnum):
    """The color of a section of a piece."""
    YELLOW = 1
    BLUE = 2
    RED = 3
    WHITE = 4


LetterColor = Datatype("LetterColor")
LetterColor.declare("letter_color", ("letter", IntSort()),
                    ("color", IntSort()))
LetterColor = LetterColor.create()


def letter_color(letter: str, color: Color) -> ExprRef:
    """Creates a LetterColor z3 constant."""
    return LetterColor.letter_color(SYM[letter],
                                    color.value)  # type: ignore[attr-defined]


SHAPES: List[Shape] = [
    Shape([
        (Vector(0, 1), letter_color("R", Color.BLUE)),
        (Vector(1, 0), letter_color("R", Color.BLUE)),
from z3 import (BoolSort, Const, Datatype, Exists, ForAll, Function, Implies,
                Not, Solver, unsat)

problem = """Someone who lived in Dreadbury Mansion killed Aunt Agatha. Agatha,
the Butler and Charles were the only people who lived in Dreadbury
Mansion. A killer always hates his victim, and is never richer than
his victim. Charles hates no one that aunt Agatha hates. Agatha hates
everyone except the butler. The butler hates everyone not richer than
Aunt Agatha. The butler also hates everyone Agatha hates. No one hates
everyone. Agatha is not the butler.  Who killed Aunt Agatha?
"""

print(problem)

# declare finite data type mansion
MansionDT = Datatype("Mansion")
MansionDT.declare("Agatha")
MansionDT.declare("Butler")
MansionDT.declare("Charles")

# create finite sort Mansion
Mansion = MansionDT.create()

# constants for ease of reference
a, b, c = Mansion.Agatha, Mansion.Butler, Mansion.Charles

# declare predicates
killed = Function("killed", Mansion, Mansion, BoolSort())
hates = Function("hates", Mansion, Mansion, BoolSort())
richer = Function("richer", Mansion, Mansion, BoolSort())
Exemple #12
-1
"""Z3 implementation of L - the meta-Kappa devised by Adrien Husson and Jean
Krivine - using the Python bindings for Z3.
"""

from z3 import Datatype, ArraySort, IntSort, BoolSort, Function, Const
from z3_helpers import Iff, Equals


# Node is a datatype representing a vertex or node in a Kappa graph.
Node = Datatype("Node")
Node.declare("node", ("unique_identifier", IntSort()))
Node = Node.create()

# A datatype for storing a pair of edges
Edge = Datatype("Edge")
Edge.declare("edge", ("node1", Node), ("node2", Node))
Edge = Edge.create()

Nodeset = ArraySort(Node, BoolSort())
Edgeset = ArraySort(Edge, BoolSort())

Labelset = ArraySort(IntSort(), BoolSort())
Labelmap = ArraySort(Node, Labelset)

# Graph, before a rule or action has applied. Merged Pregraph and Postgraph
# into a single datatype.
Graph = Datatype("Graph")
Graph.declare("graph", ("has", Nodeset), ("links", Edgeset), ("parents", Edgeset), ("labelmap", Labelmap))
Graph = Graph.create()

# Atomic action. An Action is comprised of a set of these.