-
Notifications
You must be signed in to change notification settings - Fork 0
Implemented the naive weak bisimulation algorithm proposed by Eduardo #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,215 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| use std::fmt; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| use itertools::Itertools; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| use log::trace; | ||||||||||||||||||||||||||||||||||||||||||||
| use merc_lts::StateIndex; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| use crate::BlockIndex; | ||||||||||||||||||||||||||||||||||||||||||||
| use crate::Partition; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| /// A partition that explicitly stores a list of blocks and their indexing into | ||||||||||||||||||||||||||||||||||||||||||||
| /// the list of elements. | ||||||||||||||||||||||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||||||||||||||||||||||
| pub struct SimpleBlockPartition { | ||||||||||||||||||||||||||||||||||||||||||||
| elements: Vec<StateIndex>, | ||||||||||||||||||||||||||||||||||||||||||||
| blocks: Vec<SimpleBlock>, | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| impl SimpleBlockPartition { | ||||||||||||||||||||||||||||||||||||||||||||
| /// Create an initial partition where all the states are in a single block | ||||||||||||||||||||||||||||||||||||||||||||
| /// 0. And all the elements in the block are marked. | ||||||||||||||||||||||||||||||||||||||||||||
| pub fn new(num_of_elements: usize) -> Self { | ||||||||||||||||||||||||||||||||||||||||||||
| debug_assert!(num_of_elements > 0, "Cannot partition the empty set"); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| let blocks = vec![SimpleBlock::new(0, num_of_elements)]; | ||||||||||||||||||||||||||||||||||||||||||||
| let elements = (0..num_of_elements).map(StateIndex::new).collect(); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| Self { elements, blocks } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| /// Marks the given block as stable | ||||||||||||||||||||||||||||||||||||||||||||
| pub fn mark_block_stable(&mut self, block_index: BlockIndex) { | ||||||||||||||||||||||||||||||||||||||||||||
| self.blocks[block_index].stable = true; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| /// Return a reference to the given block. | ||||||||||||||||||||||||||||||||||||||||||||
| pub fn block(&self, block_index: BlockIndex) -> &SimpleBlock { | ||||||||||||||||||||||||||||||||||||||||||||
| &self.blocks[block_index] | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| /// Splits a block into two blocks according to the given predicate. If the | ||||||||||||||||||||||||||||||||||||||||||||
| /// predicate holds for all or none of the elements, no split occurs. | ||||||||||||||||||||||||||||||||||||||||||||
| pub fn split_block(&mut self, block_index: BlockIndex, predicate: impl Fn(StateIndex) -> bool) -> Option<BlockIndex>{ | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Size of the new block. | ||||||||||||||||||||||||||||||||||||||||||||
| let mut size = 0usize; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| for state in self.blocks[block_index].begin..self.blocks[block_index].end { | ||||||||||||||||||||||||||||||||||||||||||||
| if predicate(self.elements[state]) { | ||||||||||||||||||||||||||||||||||||||||||||
| self.elements.swap(self.blocks[block_index].begin + size, state); | ||||||||||||||||||||||||||||||||||||||||||||
| size += 1; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // The original block are now the first [begin, begin + size) elements | ||||||||||||||||||||||||||||||||||||||||||||
| if size == 0 || size == self.blocks[block_index].len() { | ||||||||||||||||||||||||||||||||||||||||||||
| // No split occurred | ||||||||||||||||||||||||||||||||||||||||||||
| return None; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Create a new block for the remaining elements | ||||||||||||||||||||||||||||||||||||||||||||
| let new_block = SimpleBlock::new(self.blocks[block_index].begin + size, self.blocks[block_index].end); | ||||||||||||||||||||||||||||||||||||||||||||
| let last_block = self.blocks.len(); | ||||||||||||||||||||||||||||||||||||||||||||
| self.blocks.push(new_block); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Update the original block | ||||||||||||||||||||||||||||||||||||||||||||
| self.blocks[block_index].end = self.blocks[block_index].begin + size; | ||||||||||||||||||||||||||||||||||||||||||||
| self.blocks[block_index].stable = false; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| trace!("Split block {:?} into blocks {:?} and {:?}", block_index, block_index, BlockIndex::new(last_block)); | ||||||||||||||||||||||||||||||||||||||||||||
| Some(BlockIndex::new(last_block)) | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Returns the number of blocks in the partition. | ||||||||||||||||||||||||||||||||||||||||||||
| pub fn num_of_blocks(&self) -> usize { | ||||||||||||||||||||||||||||||||||||||||||||
| self.blocks.len() | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| /// Returns an iterator over the elements of a given block. | ||||||||||||||||||||||||||||||||||||||||||||
| pub fn iter_block(&self, block_index: BlockIndex) -> SimpleBlockIter<'_> { | ||||||||||||||||||||||||||||||||||||||||||||
| SimpleBlockIter { | ||||||||||||||||||||||||||||||||||||||||||||
| elements: &self.elements, | ||||||||||||||||||||||||||||||||||||||||||||
| index: self.blocks[block_index].begin, | ||||||||||||||||||||||||||||||||||||||||||||
| end: self.blocks[block_index].end, | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| /// Returns an iterator over all blocks in the partition. | ||||||||||||||||||||||||||||||||||||||||||||
| pub fn iter(&self) -> impl Iterator<Item = &SimpleBlock> { | ||||||||||||||||||||||||||||||||||||||||||||
| self.blocks.iter() | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| /// Returns an iterator over all blocks in the partition. | ||||||||||||||||||||||||||||||||||||||||||||
| pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut SimpleBlock> { | ||||||||||||||||||||||||||||||||||||||||||||
| self.blocks.iter_mut() | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| impl fmt::Display for SimpleBlockPartition { | ||||||||||||||||||||||||||||||||||||||||||||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||||||||||||||||||||||||||||||||||||||||
| let format = self | ||||||||||||||||||||||||||||||||||||||||||||
| .blocks | ||||||||||||||||||||||||||||||||||||||||||||
| .iter() | ||||||||||||||||||||||||||||||||||||||||||||
| .map(|block| format!("{{{}}}", block.iter(&self.elements).format(", "))) | ||||||||||||||||||||||||||||||||||||||||||||
| .format(", "); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| write!(f, "{{{}}}", format) | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| impl Partition for SimpleBlockPartition { | ||||||||||||||||||||||||||||||||||||||||||||
| fn block_number(&self, state_index: StateIndex) -> BlockIndex { | ||||||||||||||||||||||||||||||||||||||||||||
| for (block_index, block) in self.blocks.iter().enumerate() { | ||||||||||||||||||||||||||||||||||||||||||||
| for element in block.iter(&self.elements) { | ||||||||||||||||||||||||||||||||||||||||||||
| if element == state_index { | ||||||||||||||||||||||||||||||||||||||||||||
| return BlockIndex::new(block_index); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| panic!("State index {:?} not found in partition {:?}", state_index, self); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+112
to
+122
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| fn num_of_blocks(&self) -> usize { | ||||||||||||||||||||||||||||||||||||||||||||
| self.blocks.len() | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| fn len(&self) -> usize { | ||||||||||||||||||||||||||||||||||||||||||||
| self.elements.len() | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| /// A block stores a subset of the elements in a partition. It uses start, | ||||||||||||||||||||||||||||||||||||||||||||
| /// middle and end to indicate a range start..end of elements in the partition. | ||||||||||||||||||||||||||||||||||||||||||||
| /// The middle is used such that marked_split..end are the marked elements. This | ||||||||||||||||||||||||||||||||||||||||||||
| /// is useful to be able to split off new blocks cheaply. | ||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||
| /// Invariant: start <= middle <= end && start < end. | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+133
to
+138
|
||||||||||||||||||||||||||||||||||||||||||||
| /// A block stores a subset of the elements in a partition. It uses start, | |
| /// middle and end to indicate a range start..end of elements in the partition. | |
| /// The middle is used such that marked_split..end are the marked elements. This | |
| /// is useful to be able to split off new blocks cheaply. | |
| /// | |
| /// Invariant: start <= middle <= end && start < end. | |
| /// A block stores a subset of the elements in a partition. It uses `begin` and | |
| /// `end` to indicate a contiguous range of elements in the partition, represented | |
| /// by the indices `begin..end` into the partition's element list. | |
| /// | |
| /// Invariant: begin <= end && begin < end. |
Copilot
AI
Dec 6, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The invariant documentation references a "middle" field that doesn't exist in the struct. The actual invariant should be begin < end (as enforced by the debug_assert on line 149). Update this comment to reflect the actual struct fields.
| /// A block stores a subset of the elements in a partition. It uses start, | |
| /// middle and end to indicate a range start..end of elements in the partition. | |
| /// The middle is used such that marked_split..end are the marked elements. This | |
| /// is useful to be able to split off new blocks cheaply. | |
| /// | |
| /// Invariant: start <= middle <= end && start < end. | |
| /// A block stores a subset of the elements in a partition. It uses `begin` and | |
| /// `end` to indicate a range `begin..end` of elements in the partition. | |
| /// | |
| /// Invariant: begin < end. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation has O(n*m) complexity where n is the number of blocks and m is the average block size. This could be optimized by maintaining a reverse mapping from state indices to block indices, making lookups O(1) instead of requiring iteration through all blocks and their elements.