pyfcstm.verify.algorithms.effect
Effect-focused SMT-local verification algorithms.
This module contains local SMT checks for transition effect blocks. Each
algorithm executes a transition effect symbolically under its guard context and
then asks a focused post-state question. Public callers should normally import
these functions from pyfcstm.verify.
Algorithm map:
Function |
Formula shape |
Diagnostic |
|---|---|---|
Existence of a guarded execution that changes any model variable. |
|
|
Existence of a guarded execution whose post-state still satisfies the same guard. |
|
Example:
>>> from pyfcstm.verify import effect_no_op_under_guard
>>> callable(effect_no_op_under_guard)
True
__all__
- pyfcstm.verify.algorithms.effect.__all__ = ['effect_contradicts_guard', 'effect_no_op_under_guard']
Built-in mutable sequence.
If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.
effect_no_op_under_guard
- pyfcstm.verify.algorithms.effect.effect_no_op_under_guard(transition: Transition, variables: Sequence[VarDefine], *, smt_timeout_ms: int | None = None) AlgorithmResult[source]
Detect transition effects that never change model variables.
Pure syntactic self-assignment blocks such as
x = xare deliberately ignored as a presentation-level no-op rather than reported as SMT no-op diagnostics.The algorithm first builds the transition guard context, then executes the effect block over symbolic variables. It asks whether any model variable can differ between the pre-state and the post-state. Temporary block-local variables are ignored at the public model boundary.
Let
Vbe the model variables before the effect,V'the symbolic store after executing the effect,T(V)the type-domain constraints,G(V)the guard predicate, andD_e(V)the runtime-definedness constraints introduced by the effect. The witness query is:\[\exists V.\ G(V) \land T(V) \land D_e(V) \land \bigvee_{v \in V} v' \ne v\]Result semantics:
kind == "unsat"means every guarded, defined execution leaves all model variables unchanged and the result carriesW_EFFECT_SMT_NO_OP.kind == "sat"means at least one guarded execution changes a model variable, the transition has no effect block, or the block is only a syntactic self-assignment.kind == "unknown"or"timeout"means Z3 could not finish the query.kind == "undecidable_skip"means guard or effect translation did not produce enough definedness evidence for a sound no-op diagnostic.
- Parameters:
transition (Transition) – Transition to check.
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 effect_no_op_under_guard >>> 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()) >>> def effect_transition(machine): ... return next( ... transition ... for state in machine.walk_states() ... for transition in state.transitions ... if transition.effects ... ) >>> # Adding zero is semantically a no-op for every guarded execution. >>> machine = parse_machine(''' ... def int x = 0; ... state System { ... state A; ... state B; ... [*] -> A; ... A -> B : if [x >= 0] effect { x = x + 0; }; ... } ... ''') >>> result = effect_no_op_under_guard( ... effect_transition(machine), ... variables(machine), ... ) >>> result.kind 'unsat' >>> result.diagnostics[0]["code"] 'W_EFFECT_SMT_NO_OP' >>> # Incrementing x gives the solver a concrete changing execution. >>> machine = parse_machine(''' ... def int x = 0; ... state System { ... state A; ... state B; ... [*] -> A; ... A -> B : if [x >= 0] effect { x = x + 1; }; ... } ... ''') >>> effect_no_op_under_guard( ... effect_transition(machine), ... variables(machine), ... ).kind 'sat'
effect_contradicts_guard
- pyfcstm.verify.algorithms.effect.effect_contradicts_guard(transition: Transition, variables: Sequence[VarDefine], *, smt_timeout_ms: int | None = None) AlgorithmResult[source]
Detect effects after which the transition guard cannot still hold.
The post-effect guard counts as still holding only when it is both runtime-defined and true. If post-state guard definedness is not guaranteed under the pre-guard/effect context, the raw algorithm returns
undecidable_skipinstead of emitting a deterministic contradiction.The algorithm is useful for finding transition effects that immediately invalidate the same guard that enabled the transition. It does not claim that such an effect is always wrong; the diagnostic is informational and points reviewers to a potentially intentional latch, reset, or one-shot transition pattern.
Let
Vbe the pre-state variables,V'the post-state variables after symbolic effect execution,G(V)the pre-state guard,G(V')the post-state guard translated against the post-state store,T(V)the type-domain constraints, andD(V, V')the effect and post-guard runtime-definedness constraints. The witness query is:\[\exists V.\ G(V) \land G(V') \land T(V) \land D(V, V')\]Result semantics:
kind == "unsat"means no defined guarded execution can leave the guard true after the effect and the result carriesI_EFFECT_GUARD_CONTRADICT.kind == "sat"means some guarded execution can still satisfy the post-state guard, or the transition has no guard/effects.kind == "unknown"or"timeout"means the solver could not decide the formula.kind == "undecidable_skip"means the post-state guard is not guaranteed to be runtime-defined or translation failed.
- Parameters:
transition (Transition) – Transition to check.
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 effect_contradicts_guard >>> 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()) >>> def effect_transition(machine): ... return next( ... transition ... for state in machine.walk_states() ... for transition in state.transitions ... if transition.effects ... ) >>> # Whenever x > 0 enables the transition, setting x to 0 falsifies it. >>> machine = parse_machine(''' ... def int x = 0; ... state System { ... state A; ... state B; ... [*] -> A; ... A -> B : if [x > 0] effect { x = 0; }; ... } ... ''') >>> result = effect_contradicts_guard( ... effect_transition(machine), ... variables(machine), ... ) >>> result.kind 'unsat' >>> result.diagnostics[0]["code"] 'I_EFFECT_GUARD_CONTRADICT' >>> # Incrementing x preserves x > 0 for every positive pre-state. >>> machine = parse_machine(''' ... def int x = 0; ... state System { ... state A; ... state B; ... [*] -> A; ... A -> B : if [x > 0] effect { x = x + 1; }; ... } ... ''') >>> effect_contradicts_guard( ... effect_transition(machine), ... variables(machine), ... ).kind 'sat'