Hone logo
Hone
Problems

Simple Model Checking for Finite State Machines

Model checking is a powerful technique for verifying that a system (often represented as a finite state machine) satisfies a given property. This challenge asks you to implement a basic model checker in Rust that determines if a given finite state machine (FSM) satisfies a simple LTL (Linear Temporal Logic) property: "Always (G) - Globally, a certain condition holds true in every state reachable from the initial state." This is a foundational concept in formal verification and is useful for ensuring the correctness of systems like protocols, hardware designs, and software.

Problem Description

You are tasked with creating a model checker that verifies whether a given FSM satisfies the "Always" (G) LTL property. The FSM will be represented as an adjacency list where each key is a state and the value is a vector of tuples. Each tuple represents a transition: (next_state, input_symbol). The property to be checked will be a function that takes a state as input and returns a boolean indicating whether the condition holds true in that state.

What needs to be achieved:

  1. FSM Representation: The FSM is provided as a HashMap<State, Vec<(State, InputSymbol)>>. State and InputSymbol are enums defined below.
  2. Property Function: A function fn property(state: State) -> bool defines the property to be checked.
  3. Model Checking Algorithm: Implement a breadth-first search (BFS) algorithm to explore all reachable states from the initial state.
  4. Verification: For each reachable state, apply the property function. If the property ever fails (returns false), the model checker should immediately return false. If the BFS completes without finding a state where the property fails, the model checker should return true.

Key Requirements:

  • The model checker must handle cycles in the FSM. BFS is suitable for this.
  • The model checker must correctly identify whether the property holds true for all reachable states.
  • The model checker should be efficient enough to handle reasonably sized FSMs.

Expected Behavior:

The function check_fsm_property should take the FSM (as a HashMap), the initial state, and the property function as input. It should return true if the property holds for all reachable states, and false otherwise.

Edge Cases to Consider:

  • Empty FSM: What should happen if the FSM is empty (no states)?
  • Unreachable States: States that are not reachable from the initial state should not affect the result.
  • Cycles: The BFS must terminate even if the FSM contains cycles.
  • Initial State Not Satisfying Property: The property might not hold true in the initial state.

Examples

Example 1:

use std::collections::{HashMap, VecDeque};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum State {
    S1,
    S2,
    S3,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum InputSymbol {
    A,
    B,
}

fn check_fsm_property(
    fsm: &HashMap<State, Vec<(State, InputSymbol)>>,
    initial_state: State,
    property: fn(State) -> bool,
) -> bool {
    // Implementation goes here
}

fn main() {
    let mut fsm: HashMap<State, Vec<(State, InputSymbol)>> = HashMap::new();
    fsm.insert(State::S1, vec![(State::S2, InputSymbol::A), (State::S3, InputSymbol::B)]);
    fsm.insert(State::S2, vec![(State::S1, InputSymbol::B)]);
    fsm.insert(State::S3, vec![(State::S2, InputSymbol::A)]);

    let property = |state: State| {
        match state {
            State::S1 => true,
            State::S2 => false, // Property fails in S2
            State::S3 => true,
        }
    };

    let result = check_fsm_property(&fsm, State::S1, property);
    println!("Result: {}", result); // Expected Output: Result: false
}

Explanation: The FSM has a cycle. The property fails in state S2, which is reachable from the initial state S1. Therefore, the model checker should return false.

Example 2:

use std::collections::{HashMap, VecDeque};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum State {
    S1,
    S2,
    S3,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum InputSymbol {
    A,
    B,
}

fn check_fsm_property(
    fsm: &HashMap<State, Vec<(State, InputSymbol)>>,
    initial_state: State,
    property: fn(State) -> bool,
) -> bool {
    // Implementation goes here
}

fn main() {
    let mut fsm: HashMap<State, Vec<(State, InputSymbol)>> = HashMap::new();
    fsm.insert(State::S1, vec![(State::S2, InputSymbol::A)]);
    fsm.insert(State::S2, vec![(State::S3, InputSymbol::B)]);
    fsm.insert(State::S3, vec![(State::S1, InputSymbol::A)]);

    let property = |state: State| {
        match state {
            State::S1 => true,
            State::S2 => true,
            State::S3 => true,
        }
    };

    let result = check_fsm_property(&fsm, State::S1, property);
    println!("Result: {}", result); // Expected Output: Result: true
}

Explanation: The property holds true in all reachable states (S1, S2, and S3). Therefore, the model checker should return true.

Constraints

  • FSM Size: The number of states in the FSM will be at most 100.
  • Input Symbols: The number of distinct input symbols will be at most 5.
  • Property Function: The property function must be computationally inexpensive (e.g., no complex calculations).
  • Memory Usage: The memory usage should be reasonable for the given FSM size.
  • Time Complexity: The algorithm should have a time complexity of O(V + E), where V is the number of states and E is the number of transitions.

Notes

  • Use a VecDeque for the BFS queue.
  • Use a HashSet to keep track of visited states to prevent infinite loops in cyclic FSMs.
  • The State and InputSymbol enums are provided for clarity. You can modify them if needed, but ensure the function signatures remain consistent.
  • Focus on correctness and clarity of the code. While performance is a consideration, prioritize a working solution first.
  • Consider how to handle the case where the initial state is not present in the FSM. Return true in this case, as there are no reachable states to violate the property.
  • The provided examples are meant to guide you, but you should test your implementation with a variety of FSMs and properties.
Loading editor...
rust