def extract_mutex_rules(typeset: Typeset, output=None) -> Union[Atom, Tuple[List[str], Typeset]]: """Extract Mutex rules from the Formula""" rules_str = [] rules_typeset = Typeset() for mutex_group in typeset.mutex_types: or_elements = [] if len(mutex_group) > 1: for mutex_type in mutex_group: neg_group = mutex_group.symmetric_difference({mutex_type}) and_elements = [mutex_type.name] for elem in neg_group: and_elements.append(Logic.not_(elem.name)) or_elements.append(Logic.and_(and_elements, brackets=True)) rules_str.append(Logic.g_(Logic.or_(or_elements, brackets=False))) rules_typeset |= Typeset(mutex_group) if len(rules_str) == 0: return None if output is not None and output == FormulaOutput.ListCNF: return rules_str, rules_typeset return Atom(formula=(Logic.and_(rules_str, brackets=True), rules_typeset), kind=AtomKind.MUTEX_RULE)
def context_active_rules(typeset: Typeset, output=None) -> Union[Atom, Tuple[List[str], Typeset]]: """Extract Liveness rules from the Formula""" rules_str = [] rules_typeset = Typeset() inputs, outs = typeset.extract_inputs_outputs() active_context_types = [] for t in inputs: if isinstance(t, Boolean): if t.kind == TypeKinds.ACTIVE or t.kind == TypeKinds.CONTEXT: active_context_types.append(t.name) rules_typeset |= Typeset({t}) if len(active_context_types) > 0: rules_str.append(Logic.g_(Logic.and_(active_context_types))) if len(rules_str) == 0: return None if output is not None and output == FormulaOutput.ListCNF: return rules_str, rules_typeset return Atom(formula=(Logic.and_(rules_str, brackets=True), rules_typeset), kind=AtomKind.LIVENESS_RULE)
def and_(formulae: List[Tuple[str, Typeset]], brackets: bool = False) -> Tuple[str, Typeset]: """Returns an Tuple[str, Typeset] formula representing the logical AND of list_propoositions""" if len(formulae) > 1: propositions = [formula[0] for formula in formulae] typesets = [formula[1] for formula in formulae] if "FALSE" in propositions: return "FALSE", Typeset() """Remove all TRUE elements""" propositions = list(filter("TRUE".__ne__, propositions)) if len(propositions) == 0: return "TRUE", Typeset() conj = ' & '.join(propositions) typeset = Typeset() for t in typesets: typeset |= t if brackets: return f"({conj})", typeset else: return conj, typeset elif len(formulae) == 1: return formulae[0] else: raise Exception("List of propositions is empty")
def __init__(self, actions: Set[Types] = None, locations: Set[Types] = None, sensors: Set[Types] = None, contexts: Set[Types] = None): super().__init__() self.__typeset = Typeset(actions | locations | sensors | contexts | {Active()}) for name, elem in self.__typeset.items(): super(World, self).__setitem__(name, elem.to_atom()) super(World, self).__setitem__(f"!{name}", ~elem.to_atom()) self.__rules: Set[Rule] = set()
def process_bin_input( pre: Union[Specification, Boolean], post: Union[Specification, Boolean]) -> Tuple[Typeset, str, str]: new_typeset = Typeset() if isinstance(pre, Boolean): new_typeset |= pre pre = pre.name elif isinstance(pre, Specification): new_typeset |= pre.typeset pre = pre.string else: raise AttributeError if isinstance(post, Boolean): new_typeset |= post post = post.name elif isinstance(post, Specification): new_typeset |= post.typeset post = post.string else: raise AttributeError return new_typeset, pre, post
def __init__(self, ltl: LTL): variables = Typeset() variables |= ltl.variables pattern_formula = "G( F(" + ltl.formula() + "))" super().__init__(pattern_formula, variables)
def or_(formulae: List[Tuple[str, Typeset]], brakets: bool = True) -> Tuple[str, Typeset]: """Returns an formula formula representing the logical OR of list_propoositions""" if len(formulae) > 1: propositions = [formula[0] for formula in formulae] typesets = [formula[1] for formula in formulae] typeset = Typeset() for t in typesets: typeset |= t if "TRUE" in propositions: return "TRUE", typeset """Remove all FALSE elements""" propositions = list(filter("FALSE".__ne__, propositions)) res = " | ".join(propositions) if brakets: return f"({res})", typeset else: return res, typeset elif len(formulae) == 1: return formulae[0] else: raise Exception("List of propositions is empty")
def __convert_to_nuxmv(typeset: Typeset) -> List[str]: tuple_vars = [] for k, v in typeset.items(): if isinstance(v, Boolean): tuple_vars.append(f"{k}: boolean") elif isinstance(v, BoundedInteger): tuple_vars.append(f"{k}: {k.min}..{k.max}") return tuple_vars
def all_entry_locations(self, typeset: Typeset) -> Set[ReachLocation]: """List of all locations from where it is possible to reach any of the locations of the controller""" reachable_locations = set() for location in self.locations: """Extracting the locations from where 'first_location_to_visit' is reachable""" for class_name in location.adjacency_set: for t in typeset.values(): if type(t).__name__ == class_name: reachable_locations.add(t) return reachable_locations
def extract_refinement_rules(typeset: Typeset, output=None) -> Union[Atom, Tuple[List[str], Typeset]]: """Extract Refinement rules from the Formula""" rules_str = [] rules_typeset = Typeset() for key_type, set_super_types in typeset.super_types.items(): if isinstance(key_type, Boolean): for super_type in set_super_types: rules_str.append(Logic.g_(Logic.implies_(key_type.name, super_type.name))) rules_typeset |= Typeset({key_type}) rules_typeset |= Typeset(set_super_types) if len(rules_str) == 0: return None if output is not None and output == FormulaOutput.ListCNF: return rules_str, rules_typeset return Atom(formula=(Logic.and_(rules_str, brackets=True), rules_typeset), kind=AtomKind.MUTEX_RULE)
def extract_adjacency_rules(typeset: Typeset, output=None) -> Union[Atom, Tuple[List[str], Typeset]]: """Extract Adjacency rules from the Formula""" rules_str = [] rules_typeset = Typeset() for key_type, set_adjacent_types in typeset.adjacent_types.items(): if isinstance(key_type, Boolean): """G(a -> X(b | c | d))""" rules_str.append( Logic.g_(Logic.implies_(key_type.name, Logic.x_(Logic.or_([e.name for e in set_adjacent_types]))))) rules_typeset |= Typeset({key_type}) rules_typeset |= Typeset(set_adjacent_types) if len(rules_str) == 0: return None if output is not None and output == FormulaOutput.ListCNF: return rules_str, rules_typeset return Atom(formula=(Logic.and_(rules_str, brackets=True), rules_typeset), kind=AtomKind.ADJACENCY_RULE)
def __init__(self, supertypes: Dict[AllTypes, Set[AllTypes]]): super().__init__(atom=Atom("TRUE"), kind=FormulaKind.REFINEMENT_RULES) for key_type, set_super_types in supertypes.items(): if isinstance(key_type, Boolean): for super_type in set_super_types: f = Logic.g_(Logic.implies_(key_type.name, super_type.name)) t = Typeset({key_type, super_type}) new_atom = Atom(formula=(f, t), kind=AtomKind.REFINEMENT_RULE) self.__iand__(new_atom)
def __init__(self, formula: Union[str, Tuple[str, Typeset]] = None, kind: AtomKind = None, check: bool = True, dontcare: bool = False): """Atomic Specification (can be an AP, but also an LTL_forced formula that cannot be broken down, e.g. a Pattern)""" if kind is None: self.__kind = AtomKind.UNDEFINED self.__kind = kind if self.__kind == AtomKind.REFINEMENT_RULE or \ self.__kind == AtomKind.ADJACENCY_RULE or \ self.__kind == AtomKind.MUTEX_RULE: self.__spec_kind = SpecKind.RULE else: self.__spec_kind = SpecKind.UNDEFINED """Indicates if the formula is negated""" self.__negation: bool = False """Indicates if the formula is a dontcare (weather is true or false)""" self.__dontcare: bool = dontcare """Used for linking guarantees to assumptions""" self.__saturation = None if formula is None: raise AttributeError if isinstance(formula, str): if formula == "TRUE": self.__base_formula: Tuple[str, Typeset] = "TRUE", Typeset() elif formula == "FALSE": self.__base_formula: Tuple[str, Typeset] = "FALSE", Typeset() else: self.__base_formula: Tuple[str, Typeset] = formula if check and kind != AtomKind.ADJACENCY_RULE and kind != AtomKind.MUTEX_RULE and kind != AtomKind.REFINEMENT_RULE: if not self.is_satisfiable(): raise AtomNotSatisfiableException(formula=self.__base_formula)
def process_unary_input(element: Union[Specification, Boolean]) -> Tuple[str, Typeset]: typeset = Typeset() if isinstance(element, Boolean): input_str = element.name typeset |= element elif isinstance(element, Specification): input_str = element.string typeset |= element.typeset else: raise AttributeError return input_str, typeset
def process_uni_input(l: Union[Atom, Boolean]) -> Tuple[Typeset, str]: new_typeset = Typeset() if isinstance(l, Boolean): new_typeset |= l l = l.name elif isinstance(l, Atom): new_typeset |= l.typeset l = l.string else: raise AttributeError return new_typeset, l
def process_bin_contextual_input( pre: Union[Atom, Boolean], post: Union[Atom, Boolean], context: Union[Atom, Boolean], active: Union[Atom, Boolean]) -> Tuple[Typeset, str, str, str, str]: new_typeset = Typeset() if isinstance(pre, Boolean): new_typeset |= pre pre = pre.name elif isinstance(pre, Atom): new_typeset |= pre.typeset pre = pre.string else: raise AttributeError if isinstance(post, Boolean): new_typeset |= post post = post.name elif isinstance(post, Atom): new_typeset |= post.typeset post = post.string else: raise AttributeError if context is not None: if isinstance(context, Boolean): new_typeset |= context context = context.name elif isinstance(context, Atom): new_typeset |= context.typeset context = context.string else: raise AttributeError else: context = "TRUE" if isinstance(active, Boolean): new_typeset |= active active = active.name elif isinstance(active, Atom): new_typeset |= active.typeset active = active.string else: raise AttributeError return new_typeset, pre, post, context, active
def process_input(ls: Union[Atom, Boolean, List[Atom], List[Boolean]]) -> Tuple[Typeset, List[str]]: new_typeset = Typeset() formulae_str = [] if not isinstance(ls, list): ls = [ls] for i, elem in enumerate(ls): if isinstance(elem, Boolean): new_typeset |= elem formulae_str.append(elem.name) elif isinstance(elem, Atom): new_typeset |= elem.typeset formulae_str.append(elem.string) else: raise AttributeError return new_typeset, formulae_str
def create_transition_controller(self, start: Types, finish: Types, t_trans: int) -> Controller: t_controller_name = f"TRANS_{start.name}->{finish.name}" if self.session_name is None: folder_name = f"{self.t_controllers_folder_name}/{t_controller_name}" else: folder_name = f"{self.session_name}/{self.t_controllers_folder_name}/{t_controller_name}" typeset = Typeset({start, finish}) realizable = False for n_steps in range(1, t_trans): trans_spec_str = Logic.and_([start.name, Logic.xn_(finish.name, n_steps)]) trans_spec = Atom(formula=(trans_spec_str, typeset)) trans_contract = Contract(guarantees=trans_spec) try: controller_info = trans_contract.get_controller_info(world_ts=self.world.typeset) a, g, i, o = controller_info.get_strix_inputs() controller_synthesis_input = StringMng.get_controller_synthesis_str(controller_info) Store.save_to_file(controller_synthesis_input, f"t_controller_{start.name}_{finish.name}_specs.txt", folder_name) realized, kiss_mealy, time = Strix.generate_controller(a, g, i, o) if realized: realizable = True break except ControllerException as e: raise TransSynthesisFail(self, e) if not realizable: raise Exception( f"Controller [{start.name}, {finish.name}] cannot be synthetized in {t_trans} steps") else: Store.save_to_file(kiss_mealy, f"{start.name}_{finish.name}_mealy", folder_name) # Store.generate_eps_from_dot(dot_mealy, f"{start.name}_{finish.name}_dot", # folder_name) t_controller = Controller(mealy_machine=kiss_mealy, world=self.world, name=t_controller_name, synth_time=time) Store.save_to_file(str(t_controller), f"{start.name}_{finish.name}_table", folder_name) return t_controller
class World(dict): """"Instanciate atomic propositions (and their negation) for each Type""" def __init__(self, actions: Set[Types] = None, locations: Set[Types] = None, sensors: Set[Types] = None, contexts: Set[Types] = None): super().__init__() self.__typeset = Typeset(actions | locations | sensors | contexts | {Active()}) for name, elem in self.__typeset.items(): super(World, self).__setitem__(name, elem.to_atom()) super(World, self).__setitem__(f"!{name}", ~elem.to_atom()) self.__rules: Set[Rule] = set() @property def rules(self) -> Set[Rule]: return self.__rules @property def typeset(self) -> Typeset: return self.__typeset def add_rules(self, rules: Set[Rule]): self.__rules |= rules def adjacent_types(self, location: ReachLocation) -> Set[ReachLocation]: adjacent_types = set() for class_name in location.adjacency_set: for t in self.typeset.values(): if type(t).__name__ == class_name: adjacent_types.add(t) return adjacent_types
def process_binary_input(pre: Union[Specification, Boolean], post: Union[Atom, Boolean]) -> Tuple[str, str, Typeset]: typeset = Typeset() if isinstance(pre, Boolean): pre_str = pre.name typeset |= pre elif isinstance(pre, Specification): pre_str = pre.string typeset |= pre.typeset else: raise AttributeError if isinstance(post, Boolean): post_str = post.name typeset |= post elif isinstance(post, Specification): post_str = post.string typeset |= post.typeset else: raise AttributeError return pre_str, post_str, typeset
def __init__(self, name: str = "c"): super().__init__(name) class GoD(ReachLocation): def __init__(self, name: str = "d"): super().__init__(name) a = GoA() b = GoB() c = GoC() d = GoD() a = Atom((a.name, Typeset({a}))) b = Atom((b.name, Typeset({b}))) c = Atom((c.name, Typeset({c}))) d = Atom((d.name, Typeset({d}))) test = a >> ~ a print(test.formula(FormulaOutput.CNF)[0]) print(test.formula(FormulaOutput.DNF)[0]) one = a & b print("\none") print(one.formula(FormulaOutput.CNF)[0]) print(one.formula(FormulaOutput.DNF)[0])
def get_controller_info(self, world_ts: Typeset = None) -> SynthesisInfo: """Extract All Info Needed to Build a Controller from the Contract""" """Assumptions""" assumptions = [] a_mutex = [] a_liveness = [] """Guarantees""" guarantees = [] g_mutex = [] g_adjacency = [] a_typeset = Typeset() g_typeset = Typeset() list, typeset = self.assumptions.formula(FormulaOutput.ListCNF) assumptions.extend(list) a_typeset |= typeset list, typeset = self.guarantees.formula(FormulaOutput.ListCNF) guarantees.extend(list) g_typeset |= typeset if world_ts is not None: instance_ts = Typeset.get_instance_ts((a_typeset | g_typeset), world_ts) else: instance_ts = (a_typeset | g_typeset) """Extracting Inputs and Outputs Including the variables""" i_set, o_set = instance_ts.extract_inputs_outputs() i_typeset = Typeset(i_set) o_typeset = Typeset(o_set) """Mutex Rules""" ret = Atom.extract_mutex_rules(i_typeset, output=FormulaOutput.ListCNF) if ret is not None: rules, typeset = ret a_mutex.extend(rules) ret = Atom.extract_mutex_rules(o_typeset, output=FormulaOutput.ListCNF) if ret is not None: rules, typeset = ret g_mutex.extend(rules) """Adjacecy Rules""" ret = Atom.extract_adjacency_rules(o_typeset, output=FormulaOutput.ListCNF) if ret is not None: rules, typeset = ret g_adjacency.extend(rules) """Adding Liveness To Sensors (input)""" ret = Atom.extract_liveness_rules(i_typeset, output=FormulaOutput.ListCNF) if ret is not None: rules, typeset = ret a_liveness.extend(rules) """Adding context and active signal rules""" ret = Atom.context_active_rules(i_typeset, output=FormulaOutput.ListCNF) if ret is not None: rules, typeset = ret assumptions.extend(rules) """Extract Inputs and Outputs""" inputs = [t.name for t in i_set] outputs = [t.name for t in o_set] return SynthesisInfo(assumptions=assumptions, a_liveness=a_liveness, a_mutex=a_mutex, guarantees=guarantees, g_mutex=g_mutex, g_adjacency=g_adjacency, inputs=inputs, outputs=outputs)
def get_controller_info(self, world_ts: Typeset = None) -> SynthesisInfo: """Extract All Info Needed to Build a Controller from the Contract""" if world_ts is None: world_ts = Typeset() """Assumptions""" a_initial: List[str] = [] a_fairness: List[str] = [] a_safety: List[str] = [] a_mutex: List[str] = [] """Guarantees""" g_initial: List[str] = [] g_safety: List[str] = [] g_mutex: List[str] = [] g_goal: List[str] = [] a_typeset = Typeset() g_typeset = Typeset() list, typeset = self.assumptions.formula(FormulaOutput.ListCNF) a_initial.extend(list) a_typeset |= typeset list, typeset = self.guarantees.formula(FormulaOutput.ListCNF) g_goal.extend(list) g_typeset |= typeset all_typeset = a_typeset | g_typeset | world_ts """Inputs typeset""" i_typeset = Typeset(all_typeset.extract_inputs()) """Output typeset""" o_typeset = Typeset(all_typeset.extract_outputs()) actions_typeset = Typeset(all_typeset.ext()) """Mutex""" ret = Atom.extract_mutex_rules(i_typeset, output=FormulaOutput.ListCNF) if ret is not None: rules, typeset = ret a_mutex.extend(rules) ret = Atom.extract_mutex_rules(o_typeset, output=FormulaOutput.ListCNF) if ret is not None: rules, typeset = ret g_mutex.extend(rules) """For the rules we extracts rules from the variables based on assumptions and inputs""" a_typeset = a_typeset | i_typeset """We remove the inputs typeset from the guarantees and we incorporate the variables ts""" g_typeset = g_typeset - i_typeset """Adding Mutex Rules""" ret = Atom.extract_mutex_rules(a_typeset, output=FormulaOutput.ListCNF) if ret is not None: rules, typeset = ret a_mutex.extend(rules) a_typeset |= typeset ret = Atom.extract_mutex_rules(g_typeset, output=FormulaOutput.ListCNF) if ret is not None: rules, typeset = ret g_mutex.extend(rules) g_typeset |= typeset """Adjacecy rules can include things that are in the world_ts""" if world_ts is not None: adjacency_ts = g_typeset | world_ts else: adjacency_ts = g_typeset ret = Atom.extract_adjacency_rules(adjacency_ts, output=FormulaOutput.ListCNF) if ret is not None: rules, typeset = ret g_adjacency.extend(rules) g_typeset |= typeset """Adding Liveness To Sensors (input)""" ret = Atom.extract_liveness_rules(i_typeset, output=FormulaOutput.ListCNF) if ret is not None: rules, typeset = ret a_liveness.extend(rules) """Extract Inputs and Outputs""" inputs = [t.name for t in (a_typeset | g_typeset).extract_inputs()] outputs = [t.name for t in (a_typeset | g_typeset).extract_outputs()] return SynthesisInfo(a_initial=assumptions, a_fairness=a_liveness, a_mutex=a_mutex, guarantees=guarantees, g_mutex=g_mutex, g_safety=g_adjacency, inputs=inputs, outputs=outputs)
def to_atom(self): from specification.atom import Atom from typeset import Typeset return Atom(formula=(self.name, Typeset({self})), check=False)
def to_atom(self): from specification.atom import Atom, AtomKind from typeset import Typeset return Atom(formula=(self.name, Typeset({self})), check=False, kind=AtomKind.LOCATION)
night = Night().to_atom() a1 = A1().to_atom() a2 = A2().to_atom() b1 = B1().to_atom() b2 = B2().to_atom() z = Z().to_atom() """Ordered Patrolling Location a1, a2 starting from a1""" patrol_a = a1 & OrderedPatrolling([a1, a2]) """Ordered Patrolling Location b1, b2""" patrol_b = b1 & OrderedPatrolling([b1, b2]) """Inputs and Outputs""" i_set, o_set = (patrol_a.typeset | patrol_b.typeset | z.typeset).extract_inputs_outputs() i_typeset = Typeset(i_set) o_typeset = Typeset(o_set) """Mutex""" mutex_context = Atom.extract_mutex_rules(i_typeset) mutex_locs = Atom.extract_mutex_rules(o_typeset) """Liveness""" live_day = Atom.extract_liveness_rules(day.typeset) live_night = Atom.extract_liveness_rules(night.typeset) """Topology""" topology = Atom.extract_adjacency_rules(o_typeset) c1 = Contract(assumptions=live_day, guarantees=patrol_a & mutex_locs & topology) c2 = Contract(assumptions=live_night, guarantees=patrol_b & mutex_locs & topology)