def calculate_system(self, entity=None, include_subentities=True):
        logger.debug("FAST: Calculating for all entities")
        if not hasattr(self, "cache"):
            self.init_z3_constraints_and_vars()
        all_dts = []

        # """ do the other things """
        new_cache = translate_to_context(self.cache, z3.Context())

        for influence in model.get_all_influences(entity):
            if self.contains_if_condition(influence):
                self.get_condition_change_enablers(influence, all_dts,
                                                   new_cache)

        # updates = [up for up in get_updates(self.entity) if up.state == up._parent.current]
        for update in model.get_all_updates(entity):
            if update.state is update._parent.current:  # only the currently active updates
                if self.contains_if_condition(update):
                    self.get_condition_change_enablers(update, all_dts,
                                                       new_cache)

        # TODO: check for transitions whether they can be done by time only
        for trans in model.get_all_transitions(entity):
            if trans._parent.current is trans.source:
                self.get_transition_time(trans, all_dts, new_cache)
        """ Return all behaviour change times """
        return all_dts
Пример #2
0
    def check_port_connections(self):
        """
        Verify that a port has maximum one influence OR one update per state writing to it.
        when an influence is defined, no action can write to that port.
        """
        all_ports = crest.get_all_ports(self.model)
        influences_to_target = {p: [] for p in all_ports}
        updates_to_target = {p: [] for p in all_ports}
        actions_to_target = {p: [] for p in all_ports}

        # fill data stores
        for inf in crest.get_all_influences(self.model):
            influences_to_target[inf.target].append(inf)

        for up in crest.get_all_updates(self.model):
            updates_to_target[up.target].append(up)

        for action in crest.get_all_actions(self.model):
            actions_to_target[action.target].append(action)

        for port in all_ports:
            assert not (len(influences_to_target[port]) > 0 and (
                len(updates_to_target[port]) > 0 or len(actions_to_target[port]) > 0)
                ), f"There are [influences and (updates or actions)] writing to port {port._name} (entity: {port._parent._name})"

            assert len(influences_to_target[port]) < 2, f"There are two influences writing to {port._name}"

            states = [update.state for update in updates_to_target[port]]
            assert len(states) == len(set(states)), f"Port {port._name} (entity: {port._parent._name}) is written by multiple updates linked to the same state"

            transitions = [action.transition for action in actions_to_target[port]]
            assert len(transitions) == len(set(transitions)), f"Port {port._name} (entity: {port._parent._name}) is written by multiple actions linked to the same transition"
Пример #3
0
    def check_objects_have_parents_and_are_not_referenced_twice(self):
        """
        - check that ports, states, updates, influences and transitions have a parent specificaiton each.
        - Test that they also are only used once (i.e. they only appear once in the list)
        """
        # logger.debug("ports:")
        all_objs = crest.get_all_ports(self.model)
        # for o in all_objs:
        #     print(o._name, o._parent)
        for obj in all_objs:
            assert all_objs.count(obj) == 1, f"Port {obj._name} has been used multiple times"
            assert obj._parent is not None, f"Port {obj._name} has no parent definition"

        # logger.debug("states:")
        all_objs = crest.get_all_states(self.model)
        # for o in all_objs:
        #     print(o._name, o._parent)
        for obj in all_objs:
            assert all_objs.count(obj) == 1, f"State {obj._name} has been used multiple times"
            assert obj._parent is not None, f"State {obj._name} has no parent definition"

        # logger.debug("updates:")
        all_objs = crest.get_all_updates(self.model)
        # for o in all_objs:
        #     print(o._name, o._parent)
        for obj in all_objs:
            assert all_objs.count(obj) == 1, f"Update {obj._name} has been used multiple times"
            assert obj._parent is not None, f"Update {obj._name} has no parent definition"

        # logger.debug("influences")
        all_objs = crest.get_all_influences(self.model)
        # for o in all_objs:
        #     print(o._name, o._parent)
        for obj in all_objs:
            assert all_objs.count(obj) == 1, f"Influence {obj._name} has been used multiple times"
            assert obj._parent is not None, f"Influence {obj._name} has no parent definition"

        # logger.debug("transitions:")
        all_objs = crest.get_all_transitions(self.model)
        # for o in all_objs:
        #     print(o._name, o._parent)
        for obj in all_objs:
            assert all_objs.count(obj) == 1, f"Transition '{obj._name}' has been used multiple times"
            assert obj._parent is not None, f"Transition '{obj._name}' has no parent definition"
Пример #4
0
    def check_update_sanity(self):
        """Check that each update is properly named, 
        has a state and from the same entity and a target port that is in the "targets" of the entity. 
        Also verifies the signature of the update function.
        """
        for update in crest.get_all_updates(self.model):
            assert update._name is not None, f"There is an Update in {update._parent._name} ({update._parent.__class__.__name__}) whose name is 'None'"
            assert update._name != "", f"There is an Update in {update._parent._name} ({update._parent.__class__.__name__}) whose name is empty string"

            assert isinstance(update.state, crest.State), f"Update {update._name}'s state is not a crest.State. It is: {update.state} ({update.state.__class__})"
            assert update.state in crest.get_states(update._parent), f"Update's state {update.state._name} ({update.state}) is not in the states of entity {update._parent._name} ({update._parent})"

            assert isinstance(update.target, crest.Port), f"Update {update._name}'s target is not a crest.Port"
            assert update.target in api.get_targets(update._parent), f"Update's target {update.target._name} ({update.target}) is not in the targets of entity {update._parent._name} ({update._parent})"

            assert isinstance(update.function, (crestml.LearnedFunction, types.FunctionType)), f"Update {update._name}'s function needs to be of type types.FunctionType or crestdsl.ml.LearnedFunction"
            assert 'dt' in inspect.signature(update.function).parameters, f"Update {update._name}'s function has no dt parameter. entity: {update._parent._name} ({update._parent.__class__.__name__})"
            assert 'self' in inspect.signature(update.function).parameters, f"Update {update._name}'s function has no self parameter. entity: {update._parent._name} ({update._parent.__class__.__name__})"
            assert len(inspect.signature(update.function).parameters) == 2, f"An update should have one one argument 'dt' besides 'self'"

            for port in SH.get_read_ports_from_update(update.function, update):
                assert port in api.get_sources(update._parent), f"Update {update._name} seems to be reading a port {port._name} ({port}) which is not in the sources of its entity {update._parent._name} ({update._parent})"
def init_z3_constraints_and_vars(entity, timeunit, use_integer_and_real):

    cache = SimpleNamespace()

    # create port variables for all ports
    cache.z3_vars = {}
    cache.z3_port_constraints = {}

    dt_var = get_z3_var(timeunit, 'dt')
    cache.z3_vars['dt'] = dt_var
    cache.z3_vars['dt'].type = timeunit

    for port in model.get_all_ports(entity):
        portname = port._name
        portname_with_parent = port._parent._name + "." + port._name

        variable = get_z3_variable(port, port._name)
        pre_var = get_z3_variable(port, port._name + "_0")

        cache.z3_vars[port] = {
            portname: variable,
            portname_with_parent: variable,
            portname + "_0": pre_var,
            portname_with_parent + "_0": pre_var,
            portname + ".pre": pre_var,
            portname_with_parent + ".pre": pre_var,
        }

        # pre_value = get_z3_value(port, port._name + "_0")
        # cache.z3_port_constraints[port] = pre_var == pre_value  # init condition needs to be set

    # create entity constraints for all modifiers
    cache.z3_modifier_constraints = {}
    cache.z3_conditionchanged_constraintsets = {}
    for influence in model.get_all_influences(entity):
        constraints = get_constraints_from_modifier(
            influence,
            cache.z3_vars,
            use_integer_and_real=use_integer_and_real,
            cache=False)
        cache.z3_modifier_constraints[influence] = constraints

        # TODO: this should be nicer somehow ...
        # add port and constraint for the influence param
        z3_src = cache.z3_vars[influence.source][influence.source._name]
        params = SH.get_param_names(influence.function)
        param_key = params[0] + "_" + str(id(influence))
        z3_param = get_z3_variable(influence.source, params[0],
                                   str(id(influence)))
        cache.z3_vars[param_key] = {params[0] + "_0": z3_param}

        conv = Z3ConditionChangeCalculator(
            cache.z3_vars,
            entity=influence._parent,
            container=influence,
            use_integer_and_real=use_integer_and_real)
        cache.z3_conditionchanged_constraintsets[influence] = (
            conv.calculate_constraints(influence.function), z3_src == z3_param)

    for update in model.get_all_updates(entity):
        constraints = get_constraints_from_modifier(update,
                                                    cache.z3_vars,
                                                    use_integer_and_real,
                                                    cache=False)
        cache.z3_modifier_constraints[update] = constraints

        conv = Z3ConditionChangeCalculator(
            cache.z3_vars,
            entity=update._parent,
            container=update,
            use_integer_and_real=use_integer_and_real)
        cache.z3_conditionchanged_constraintsets[update] = (
            conv.calculate_constraints(update.function), [])

    for transition in model.get_all_transitions(entity):
        conv = Z3Converter(cache.z3_vars,
                           entity=transition._parent,
                           container=transition,
                           use_integer_and_real=use_integer_and_real)
        guard_constraint = conv.to_z3(transition.guard)
        cache.z3_modifier_constraints[transition] = guard_constraint

    return cache
Пример #6
0
def get_modifier_map(root_entity, port_list, cache=True):
    """Creates a dict that has ports as keys and a list of influences/updates that influence those ports as values."""

    logger.debug(
        f"creating modifier map for ports {[p._name +' (in: '+ p._parent._name+')' for p in port_list]}"
    )
    modifier_map = {port: list() for port in port_list}
    map_change = True

    all_updates = get_all_updates(root_entity)
    all_influences = get_all_influences(root_entity)

    while map_change:
        map_change = False  # initially we think there are no changes
        for port, modifiers in modifier_map.copy().items(
        ):  # iterate over a copy, so we can modify the original list
            # we only look at ports that have no influences (it might be because there exist none, but that small overhead is okay for now)
            if len(modifiers) == 0:
                logger.debug(
                    f"trying to find modifiers for port '{port._name}' of entity '{port._parent._name} ({port._parent.__class__.__name__})'"
                )
                influences = [
                    inf for inf in all_influences if port == inf.target
                ]
                modifier_map[port].extend(influences)
                for inf in influences:
                    logger.debug(
                        f"'{port._name}' is modified by influence '{inf._name}'"
                    )
                    # this means influences is not empty, hence we change the map (probably)
                    map_change = True
                    if inf.source not in modifier_map:
                        modifier_map[inf.source] = list(
                        )  # add an empty list, the next iteration will try to fill it

                updates = [
                    up for up in all_updates
                    if port == up.target and up.state == up._parent.current
                ]

                modifier_map[port].extend(updates)
                for up in updates:
                    # logger.debug(f"'{port._name}' is modified by update '{up._name}'")
                    # read_ports = SH.get_read_ports_from_update(up.function, up)  # +[up.target]
                    accessed_ports = SH.get_accessed_ports(up.function,
                                                           up,
                                                           exclude_pre=False,
                                                           cache=cache)

                    # logger.debug(f"'{up._name}' in '{up._parent._name}' reads the following ports: {[(p._name, p._parent._name) for p in accessed_ports]}")
                    for read_port in accessed_ports:
                        # this means there are updates and we change the map
                        map_change = True
                        if read_port not in modifier_map:
                            # logger.debug(f"adding {read_port._name} to modifier_map")
                            modifier_map[read_port] = list(
                            )  # add an empty list, the next iteration will try to fill it
    logger.debug(
        f"the modifier map looks like this: \n{pformat(prettify_modifier_map(modifier_map))}"
    )
    return modifier_map
    def calculate_system(self, entity=None, include_subentities=True):
        logger.debug("FAST: Calculating for all entities")
        if not hasattr(self, "cache"):
            self.init_z3_constraints_and_vars()
        all_dts = []
        """ setup workers with own context for each """
        job_queue = queue.Queue()
        num_threads = 4

        thread_workers = []
        for i in range(num_threads):
            new_cache = translate_to_context(self.cache, z3.Context())
            thread_worker = threading.Thread(target=self.thread_crawler,
                                             args=(job_queue, new_cache,
                                                   all_dts))
            thread_worker.setDaemon(True)
            thread_worker.start()
        """ Fill queue with stuff to do """
        # TODO: check for transitions whether they can be done by time only
        for trans in model.get_all_transitions(entity):
            if trans._parent.current is trans.source:
                job_queue.put((self.get_transition_time, trans))

        for influence in model.get_all_influences(entity):
            if self.contains_if_condition(influence):
                job_queue.put((self.get_condition_change_enablers, influence))

        # updates = [up for up in get_updates(self.entity) if up.state == up._parent.current]
        for update in model.get_all_updates(entity):
            if update.state is update._parent.current:  # only the currently active updates
                if self.contains_if_condition(update):
                    job_queue.put((self.get_condition_change_enablers, update))
        """ wait for queue to finish """
        job_queue.join()

        for tw in thread_workers:
            assert not tw.isAlive()

        # """ do the other things """

        # workers = []
        # for influence in model.get_all_influences(entity):
        #     if self.contains_if_condition(influence):
        #         ctx_i = z3.Context()
        #         new_cache = translate_to_context(self.cache, ctx_i)
        #         worker = threading.Thread(target=self.get_condition_change_enablers, args=(influence, all_dts, new_cache))
        #         worker.start()
        #         workers.append(worker)
        #         worker.join()
        #
        #
        # # updates = [up for up in get_updates(self.entity) if up.state == up._parent.current]
        # for update in model.get_all_updates(entity):
        #     if update.state is update._parent.current:  # only the currently active updates
        #         if self.contains_if_condition(update):
        #             ctx_i = z3.Context()
        #             new_cache = translate_to_context(self.cache, ctx_i)
        #             worker = threading.Thread(target=self.get_condition_change_enablers, args=(update, all_dts, new_cache))
        #             worker.start()
        #             workers.append(worker)
        #             worker.join()
        #
        # # TODO: check for transitions whether they can be done by time only
        # for trans in model.get_all_transitions(entity):
        #     if trans._parent.current is trans.source:
        #         ctx_i = z3.Context()
        #         new_cache = translate_to_context(self.cache, ctx_i)
        #         worker = threading.Thread(target=self.get_transition_time, args=(trans, all_dts, new_cache))
        #         worker.start()
        #         workers.append(worker)
        #         worker.join()

        # print(f"Working on {len(workers)} threads")
        # for worker in workers:
        #     worker.join()
        """ Return all behaviour change times """
        return all_dts