def faithfulness(primes: dict, update: str, trap_space: Union[dict, str]) -> bool: """ The model checking approach for deciding whether *trap_space* is faithful, i.e., whether all free variables oscillate in all of the attractors contained in it, in the state transition graph defined by *primes* and *update*. The approach is described and discussed in :ref:`Klarner2015(a) <klarner2015trap>`. It is decided by a single CTL query of the pattern :ref:`EF_all_unsteady <EF_all_unsteady>` and the random-walk-approach of the function :ref:`random_walk <random_walk>`. .. note:: In the (very unlikely) case that the random walk does not end in an attractor state an exception will be raised. .. note:: Faithfulness depends on the update strategy, i.e., a trapspace may be faithful in the synchronous STG but not faithful in the asynchronous STG or vice versa. .. note:: A typical use case is to decide whether a minimal trap space is faithful. .. note:: *trap_space* is in fact not required to be a trap set, i.e., it may be an arbitrary subspace. If it is an arbitrary subspace then the involved variables are artificially fixed to be constant. **arguments**: * *primes*: prime implicants * *update*: the update strategy, one of *"asynchronous"*, *"synchronous"*, *"mixed"* * *trap_space*: a subspace **returns**: * *answer*: whether *trap_space* is faithful in the STG defined by *primes* and *update* **example**:: >>> mintspaces = compute_trap_spaces(primes, "min") >>> x = mintspaces[0] >>> faithfulness(primes, x) True """ if len(trap_space) == len(primes): return True primes = copy_primes(primes=primes) create_constants(primes=primes, constants=trap_space) percolate(primes=primes, remove_constants=True) constants = find_constants(primes=primes) if len(constants) > len(trap_space): return False formula = exists_finally_unsteady_components(names=list(primes)) spec = f"CTLSPEC AG({formula})" init = "INIT TRUE" answer = model_checking(primes=primes, update=update, initial_states=init, specification=spec) return answer
def faithfulness_with_counterexample(primes: dict, update: str, trap_space: dict) -> (bool, List[dict]): """ Performs the same steps as :ref:`faithfulness` but also returns a counterexample which is *None* if it does not exist. A counterexample of a faithful test is a state that belongs to an attractor which has more fixed variables than there are in *trap_space*. **arguments**: * *primes*: prime implicants * *update*: the update strategy, one of *"asynchronous"*, *"synchronous"*, *"mixed"* * *trap_space*: a subspace **returns**: * *answer*: whether *trap_space* is faithful in the STG defined by *primes* and *update* * *counter_example*: a state that belongs to an attractor that does not oscillate in all free variables or *None* if no counterexample exists **example**:: >>> mintspaces = compute_trap_spaces(primes, "min") >>> x = mintspaces[0] >>> faithfulness(primes, x) True """ if len(trap_space) == len(primes): return True, None primes = percolate(primes=primes, add_constants=trap_space, copy=True) constants = find_constants(primes=primes) remove_all_constants(primes=primes) if len(constants) > len(trap_space): counterexample = find_attractor_state_by_randomwalk_and_ctl( primes=primes, update=update) attractor_state = merge_dicts(dicts=[counterexample, constants]) return False, attractor_state spec = f"CTLSPEC AG({exists_finally_unsteady_components(names=list(primes))})" answer, counterexample = model_checking(primes=primes, update=update, initial_states="INIT TRUE", specification=spec, enable_counterexample=True) if answer: return True, None else: attractor_state = find_attractor_state_by_randomwalk_and_ctl( primes=primes, update=update, initial_state=counterexample[-1]) attractor_state = merge_dicts(dicts=[attractor_state, constants]) return False, attractor_state
def univocality_with_counterexample( primes: dict, update: str, trap_space: Union[dict, str]) -> (bool, List[dict]): """ Performs the same steps as :ref:`univocality` but also returns a counterexample which is *None* if it does not exist. A counterexample of a univocality test are two states that belong to different attractors. **arguments**: * *primes*: prime implicants * *update*: the update strategy, one of *"asynchronous"*, *"synchronous"*, *"mixed"* * *trap_space*: a subspace **returns**: * *answer*: whether *trap_space* is univocal in the STG defined by *primes* and *update* * *counter_example*: two states that belong to different attractors or *None* if no counterexample exists **example**:: >>> mintspaces = compute_trap_spaces(primes, "min") >>> trapspace = mintrapspaces[0] >>> answer, counterex = univocality_with_counterexample(primes, trapspace, "asynchronous") """ primes = percolate(primes=primes, add_constants=trap_space, copy=True) constants = find_constants(primes=primes) remove_all_constants(primes=primes) if primes == {}: return True, None attractor_state = find_attractor_state_by_randomwalk_and_ctl(primes=primes, update=update) spec = f"CTLSPEC {exists_finally_one_of_subspaces(primes=primes, subspaces=[attractor_state])}" init = "INIT TRUE" answer, counterexample = model_checking(primes=primes, update=update, initial_states=init, specification=spec, enable_counterexample=True) if answer: return True, None else: attractor_state2 = find_attractor_state_by_randomwalk_and_ctl( primes=primes, update=update, initial_state=counterexample[-1]) attractor_state2 = merge_dicts(dicts=[attractor_state2, constants]) attractor_state = merge_dicts(dicts=[attractor_state, constants]) counterexample = attractor_state, attractor_state2 return False, counterexample
def iterative_completeness_algorithm( primes: dict, update: str, compute_counterexample: bool, max_output: int = 1000) -> Union[Tuple[bool, Optional[dict]], bool]: """ The iterative algorithm for deciding whether the minimal trap spaces are complete. The function is implemented by line-by-line following of the pseudo code algorithm given in "Approximating attractors of Boolean networks by iterative CTL model checking", Klarner and Siebert 2015. **arguments**: * *primes*: prime implicants * *update*: the update strategy, one of *"asynchronous"*, *"synchronous"*, *"mixed"* * *compute_counterexample*: whether to compute a counterexample **returns**: * *answer*: whether *subspaces* is complete in the STG defined by *primes* and *update*, * *counterexample*: a state that can not reach one of the minimal trap spaces of *primes* or *None* if no counterexample exists **example**:: >>> answer, counterexample = completeness_with_counterexample(primes, "asynchronous") >>> answer False >>> state2str(counterexample) 10010111101010100001100001011011111111 """ primes = percolate(primes=primes, copy=True) constants_global = find_constants(primes=primes) remove_all_constants(primes=primes) min_trap_spaces = compute_trap_spaces(primes=primes, type_="min", max_output=max_output) if min_trap_spaces == [{}]: if compute_counterexample: return True, None else: return True current_set = [({}, set([]))] while current_set: p, w = current_set.pop() primes_reduced = copy_primes(primes=primes) create_constants(primes=primes_reduced, constants=p) igraph = primes2igraph(primes=primes_reduced) cgraph = digraph2condensationgraph(digraph=igraph) cgraph_dash = cgraph.copy() for U in cgraph.nodes(): if set(U).issubset(set(w)): cgraph_dash.remove_node(U) w_dash = w.copy() refinement = [] top_layer = [ U for U in cgraph_dash.nodes() if cgraph_dash.in_degree(U) == 0 ] for U in top_layer: u_dash = find_ancestors(igraph, U) primes_restricted = copy_primes(primes_reduced) remove_all_variables_except(primes=primes_restricted, names=u_dash) q = compute_trap_spaces(primes=primes_restricted, type_="min", max_output=max_output) phi = exists_finally_one_of_subspaces(primes=primes_restricted, subspaces=q) init = "INIT TRUE" spec = f"CTLSPEC {phi}" if compute_counterexample: answer, counterexample = model_checking( primes=primes_restricted, update=update, initial_states=init, specification=spec, enable_counterexample=True) if not answer: downstream = [x for x in igraph if x not in U] arbitrary_state = random_state(downstream) top_layer_state = counterexample[-1] counterexample = merge_dicts([ constants_global, p, top_layer_state, arbitrary_state ]) return False, counterexample else: answer = model_checking(primes=primes_restricted, update=update, initial_states=init, specification=spec) if not answer: return False refinement += intersection([p], q) w_dash.update(u_dash) for q in intersection(refinement): q_tilde = find_constants( primes=percolate(primes=primes, add_constants=q, copy=True)) if q_tilde not in min_trap_spaces: current_set.append((q_tilde, w_dash)) if compute_counterexample: return True, None else: return True
def univocality(primes: dict, update: str, trap_space: Union[dict, str]) -> bool: """ The model checking and random-walk-based method for deciding whether *trap_space* is univocal, i.e., whether there is a unique attractor contained in it, in the state transition graph defined by *primes* and *update*. The approach is described and discussed in :ref:`Klarner2015(a) <klarner2015trap>`. The function performs two steps: first it searches for a state that belongs to an attractor inside of *trap_space* using the random-walk-approach and the function :ref:`random_walk <random_walk>`, then it uses CTL model checking, specifically the pattern :ref:`AGEF_oneof_subspaces <AGEF_oneof_subspaces>`, to decide if the attractor is unique inside *trap_space*. .. note:: In the (very unlikely) case that the random walk does not end in an attractor state an exception will be raised. .. note:: Univocality depends on the update strategy, i.e., a trapspace may be univocal in the synchronous STG but not univocal in the asynchronous STG or vice versa. .. note:: A typical use case is to decide whether a minimal trap space is univocal. .. note:: *trap_space* is in fact not required to be a trap set, i.e., it may be an arbitrary subspace. If it is an arbitrary subspace then the involved variables are artificially fixed to be constant. **arguments**: * *primes*: prime implicants * *update*: the update strategy, one of *"asynchronous"*, *"synchronous"*, *"mixed"* * *trap_space*: a subspace **returns**: * *answer*: whether *trap_space* is univocal in the STG defined by *primes* and *update* **example**:: >>> mintspaces = compute_trap_spaces(primes, "min") >>> x = mintrapspaces[0] >>> univocality(primes, "asynchronous", x) True """ primes = copy_primes(primes=primes) create_constants(primes=primes, constants=trap_space) percolate(primes=primes, remove_constants=True) if primes == {}: return True attractor_state = find_attractor_state_by_randomwalk_and_ctl(primes=primes, update=update) formula = exists_finally_one_of_subspaces(primes=primes, subspaces=[attractor_state]) spec = f"CTLSPEC {formula}" init = "INIT TRUE" answer = model_checking(primes=primes, update=update, initial_states=init, specification=spec) return answer