pyfcstm.verify.algorithms.transition
Transition-focused SMT-local verification algorithms.
This module checks relationships between multiple transitions rather than a
single guard or effect. The algorithms still use local, quantifier-free SMT
queries; they do not unroll runtime execution over multiple cycles. Public
callers should normally import these functions from pyfcstm.verify.
Algorithm map:
Function |
Formula shape |
Diagnostic |
|---|---|---|
Feasibility of a later outgoing trigger while every earlier same-source trigger is disabled. |
|
|
Existence of an input valuation not covered by any initial-transition trigger. |
|
Example:
>>> from pyfcstm.verify import transition_shadowed_by_predecessor
>>> callable(transition_shadowed_by_predecessor)
True
__all__
- pyfcstm.verify.algorithms.transition.__all__ = ['composite_init_guards_incomplete', 'transition_shadowed_by_predecessor']
Built-in mutable sequence.
If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.
transition_shadowed_by_predecessor
- pyfcstm.verify.algorithms.transition.transition_shadowed_by_predecessor(machine: StateMachine, variables: Sequence[VarDefine], *, smt_timeout_ms: int | None = None) AlgorithmResult[source]
Detect outgoing transitions shadowed by earlier transitions.
FCSTM transition order matters for transitions with the same source state: an earlier enabled transition prevents later outgoing transitions from being selected. This algorithm groups ordered outgoing transitions by source, translates each event/guard trigger into a predicate, and asks whether a later trigger can ever be enabled after all prior triggers are disabled.
For a source state with ordered outgoing transitions
t_1, ..., t_n, letH_i(V, E)be the trigger predicate for transitiont_iover model variablesVand synthetic event booleansE. For candidate transitiont_j, the shadowing witness query is:\[\exists V,E.\ T(V) \land D(V,E) \land H_j(V,E) \land \bigwedge_{i<j} \lnot H_i(V,E)\]If the query is unsatisfiable and
H_jitself is satisfiable, then every defined candidate valuation is covered by prior triggers andt_jis shadowed. The implementation also avoids deterministic diagnostics when predecessor continuation through stoppable composite validation is not locally proven stable.Result semantics:
kind == "unsat"means one or more later transitions are shadowed and diagnostics carryW_TRANSITION_SHADOWED.kind == "sat"means every checked later transition has a remaining candidate valuation or there are no ordered outgoing competitors.kind == "unknown"or"timeout"means at least one relevant SMT query was inconclusive and no deterministic diagnostic was emitted.kind == "undecidable_skip"means translation, definedness, or stable continuation evidence was insufficient for a sound shadowing result.
- Parameters:
machine (StateMachine) – State machine to inspect.
variables (Sequence[VarDefine]) – Variable definitions available to the model.
smt_timeout_ms (Optional[int], optional) – Optional solver timeout in milliseconds.
- Returns:
Algorithm result.
- Return type:
Example:
>>> from textwrap import dedent >>> from pyfcstm.dsl import parse_with_grammar_entry >>> from pyfcstm.model import parse_dsl_node_to_state_machine >>> from pyfcstm.verify import transition_shadowed_by_predecessor >>> def parse_machine(source): ... ast = parse_with_grammar_entry(dedent(source), "state_machine_dsl") ... return parse_dsl_node_to_state_machine(ast) >>> def variables(machine): ... return tuple(machine.defines.values()) >>> # The second A-source transition is never considered after A -> B. >>> machine = parse_machine(''' ... state System { ... state A; ... state B; ... state C; ... [*] -> A; ... A -> B; ... A -> C; ... } ... ''') >>> result = transition_shadowed_by_predecessor(machine, variables(machine)) >>> result.kind 'unsat' >>> result.diagnostics[0]["code"] 'W_TRANSITION_SHADOWED' >>> result.diagnostics[0]["data"]["reason"] 'unconditional_catchall' >>> # Disjoint guards leave a candidate valuation for each transition. >>> machine = parse_machine(''' ... def int x = 0; ... state System { ... state A; ... state B; ... state C; ... [*] -> A; ... A -> B : if [x < 0]; ... A -> C : if [x > 0]; ... } ... ''') >>> transition_shadowed_by_predecessor(machine, variables(machine)).kind 'sat'
composite_init_guards_incomplete
- pyfcstm.verify.algorithms.transition.composite_init_guards_incomplete(machine: StateMachine, variables: Sequence[VarDefine], *, smt_timeout_ms: int | None = None) AlgorithmResult[source]
Detect composite states whose initial transitions do not cover inputs.
The result
kindfollows the witness query:'sat'means a coverage gap was found and diagnostics were emitted, while'unsat'means the initial-transition triggers cover all modeled inputs.A composite state with only guarded or event-triggered initial transitions may fail to choose an initial child for some valuation. This algorithm checks each composite state’s initial transitions and searches for a model variable and event input assignment that disables all initial-transition triggers.
For a composite state with initial transitions
i_1, ..., i_n, letH_k(V, E)be the trigger predicate for initial transitioni_k. The gap query is:\[\exists V,E.\ T(V) \land \lnot \bigvee_{k=1}^{n} H_k(V,E)\]Result semantics:
kind == "sat"means the query found an uncovered valuation and the diagnostics carryW_COMPOSITE_INIT_INCOMPLETEwith a witness model when Z3 provided one.kind == "unsat"means every checked composite initial-transition set covers all modeled variable and event inputs.kind == "unknown"or"timeout"means a query was inconclusive and no coverage diagnostic was emitted for that composite.kind == "undecidable_skip"means trigger translation or definedness evidence was insufficient for a sound coverage decision.
- Parameters:
machine (StateMachine) – State machine to inspect.
variables (Sequence[VarDefine]) – Variable definitions available to the model.
smt_timeout_ms (Optional[int], optional) – Optional solver timeout in milliseconds.
- Returns:
Algorithm result.
- Return type:
Example:
>>> from textwrap import dedent >>> from pyfcstm.dsl import parse_with_grammar_entry >>> from pyfcstm.model import parse_dsl_node_to_state_machine >>> from pyfcstm.verify import composite_init_guards_incomplete >>> def parse_machine(source): ... ast = parse_with_grammar_entry(dedent(source), "state_machine_dsl") ... return parse_dsl_node_to_state_machine(ast) >>> def variables(machine): ... return tuple(machine.defines.values()) >>> # x = 0 disables both guarded initial transitions, so a gap exists. >>> machine = parse_machine(''' ... def int x = 0; ... state System { ... state A; ... state B; ... [*] -> A : if [x > 0]; ... [*] -> B : if [x < 0]; ... } ... ''') >>> result = composite_init_guards_incomplete(machine, variables(machine)) >>> result.kind 'sat' >>> result.diagnostics[0]["code"] 'W_COMPOSITE_INIT_INCOMPLETE' >>> # The pair x >= 0 and x < 0 covers every integer input. >>> machine = parse_machine(''' ... def int x = 0; ... state System { ... state A; ... state B; ... [*] -> A : if [x >= 0]; ... [*] -> B : if [x < 0]; ... } ... ''') >>> composite_init_guards_incomplete(machine, variables(machine)).kind 'unsat'