1
use std::mem::swap;
2

            
3
use bumpalo::Bump;
4
use log::debug;
5
use log::info;
6
use log::log_enabled;
7
use log::trace;
8
use merc_io::TimeProgress;
9
use merc_lts::IncomingTransitions;
10
use merc_lts::LTS;
11
use merc_lts::LabelIndex;
12
use merc_lts::LabelledTransitionSystem;
13
use merc_lts::StateIndex;
14
use rustc_hash::FxHashMap;
15
use rustc_hash::FxHashSet;
16

            
17
use merc_collections::BlockIndex;
18
use merc_collections::IndexedPartition;
19
use merc_utilities::Timing;
20

            
21
use crate::BlockPartition;
22
use crate::BlockPartitionBuilder;
23
use crate::Equivalence;
24
use crate::Partition;
25
use crate::Signature;
26
use crate::SignatureBuilder;
27
use crate::branching_bisim_signature;
28
use crate::branching_bisim_signature_inductive;
29
use crate::branching_bisim_signature_sorted;
30
use crate::is_tau_hat;
31
use crate::longest_tau_path;
32
use crate::reduce_lts;
33
use crate::strong_bisim_signature;
34
use crate::tau_loop_elimination_and_reorder;
35
use crate::weak_bisim_presignature_sorted;
36
use crate::weak_bisim_signature_sorted;
37
use crate::weak_bisim_signature_sorted_full;
38
use crate::weak_bisim_signature_sorted_taus;
39

            
40
/// Computes a strong bisimulation partitioning using signature refinement
41
700
pub fn strong_bisim_sigref<L: LTS>(lts: L, timing: &Timing) -> (L, BlockPartition) {
42
700
    let incoming = timing.measure("preprocess", || IncomingTransitions::new(&lts));
43

            
44
700
    let partition = timing.measure("reduction", || {
45
700
        signature_refinement::<_, _, _, false>(
46
700
            &lts,
47
700
            &incoming,
48
387338
            |state_index, partition, _, builder| {
49
387338
                strong_bisim_signature(state_index, &lts, partition, builder);
50
387338
            },
51
            |_, _| None,
52
        )
53
700
    });
54

            
55
700
    (lts, partition)
56
700
}
57

            
58
/// Computes a strong bisimulation partitioning using signature refinement
59
400
pub fn strong_bisim_sigref_naive<L: LTS>(lts: L, timing: &Timing) -> (L, IndexedPartition) {
60
400
    let partition = timing.measure("reduction", || {
61
22496
        signature_refinement_naive::<_, _, false>(&lts, |state_index, partition, _, builder| {
62
22496
            strong_bisim_signature(state_index, &lts, partition, builder);
63
22496
        })
64
400
    });
65

            
66
400
    (lts, partition)
67
400
}
68

            
69
/// Computes a branching bisimulation partitioning using signature refinement.
70
/// 
71
/// The `state` is any state for which we return the equivalent state in the preprocessed LTS.
72
500
pub fn branching_bisim_sigref<L: LTS>(lts: L, state: StateIndex, timing: &Timing) -> (LabelledTransitionSystem<L::Label>, StateIndex, BlockPartition) {
73
500
    let (preprocessed_lts, mapped_state) = timing.measure("preprocess", || tau_loop_elimination_and_reorder(lts, state));
74
    
75
500
    let incoming = timing.measure("preprocess", || IncomingTransitions::new(&preprocessed_lts));
76

            
77
500
    if log_enabled!(log::Level::Debug) {
78
        let path = longest_tau_path(&preprocessed_lts);
79
        debug!("longest_tau_path" = path.len(); "The longest tau path is {:?}", path);
80
500
    }
81

            
82
500
    let mut expected_builder = SignatureBuilder::default();
83
500
    let mut visited = FxHashSet::default();
84
500
    let mut stack = Vec::new();
85

            
86
500
    let partition = timing.measure("reduction", || {
87
500
        signature_refinement::<_, _, _, true>(
88
500
            &preprocessed_lts,
89
500
            &incoming,
90
699191
            |state_index, partition, state_to_key, builder| {
91
699191
                branching_bisim_signature_inductive(state_index, &preprocessed_lts, partition, state_to_key, builder);
92

            
93
                // Compute the expected signature, only used in debugging.
94
699191
                if cfg!(debug_assertions) {
95
699191
                    branching_bisim_signature(
96
699191
                        state_index,
97
699191
                        &preprocessed_lts,
98
699191
                        partition,
99
699191
                        &mut expected_builder,
100
699191
                        &mut visited,
101
699191
                        &mut stack,
102
                    );
103
699191
                    let expected_result = builder.clone();
104

            
105
699191
                    let signature = Signature::new(builder);
106
699191
                    debug_assert_eq!(
107
699191
                        signature.as_slice(),
108
                        expected_result,
109
                        "The sorted and expected signature should be the same"
110
                    );
111
                }
112
699191
            },
113
699191
            |signature, key_to_signature| {
114
                // Inductive signatures.
115
699191
                for (label, key) in signature.iter().rev() {
116
611134
                    if is_tau_hat(*label, &preprocessed_lts)
117
110589
                        && key_to_signature[*key].is_subset_of(signature, (*label, *key))
118
                    {
119
61469
                        return Some(*key);
120
549665
                    }
121

            
122
549665
                    if !is_tau_hat(*label, &preprocessed_lts) {
123
500545
                        return None;
124
49120
                    }
125
                }
126

            
127
137177
                None
128
699191
            },
129
        )
130
500
    });
131

            
132
    // Combine the SCC partition with the branching bisimulation partition.
133
500
    (preprocessed_lts, mapped_state, partition)
134
500
}
135

            
136
/// Computes a branching bisimulation partitioning using signature refinement without dirty blocks.
137
/// 
138
/// The `state` is any state for which we return the equivalent state in the preprocessed LTS.
139
500
pub fn branching_bisim_sigref_naive<L: LTS>(
140
500
    lts: L,
141
500
    state: StateIndex,
142
500
    timing: &Timing,
143
500
) -> (LabelledTransitionSystem<L::Label>, StateIndex, IndexedPartition) {
144
500
    let (preprocessed_lts, mapped_state) = timing.measure("preprocess", || tau_loop_elimination_and_reorder(lts, state));
145

            
146
500
    timing.measure("reduction", || {
147
500
        let mut expected_builder = SignatureBuilder::default();
148
500
        let mut visited = FxHashSet::default();
149
500
        let mut stack = Vec::new();
150

            
151
500
        let partition = signature_refinement_naive::<_, _, false>(
152
500
            &preprocessed_lts,
153
22511
            |state_index, partition, state_to_signature, builder| {
154
22511
                branching_bisim_signature_sorted(
155
22511
                    state_index,
156
22511
                    &preprocessed_lts,
157
22511
                    partition,
158
22511
                    state_to_signature,
159
22511
                    builder,
160
                );
161

            
162
                // Compute the expected signature, only used in debugging.
163
22511
                if cfg!(debug_assertions) {
164
22511
                    branching_bisim_signature(
165
22511
                        state_index,
166
22511
                        &preprocessed_lts,
167
22511
                        partition,
168
22511
                        &mut expected_builder,
169
22511
                        &mut visited,
170
22511
                        &mut stack,
171
                    );
172
22511
                    let expected_result = builder.clone();
173

            
174
22511
                    let signature = Signature::new(builder);
175
22511
                    debug_assert_eq!(
176
22511
                        signature.as_slice(),
177
                        expected_result,
178
                        "The sorted and expected signature should be the same"
179
                    );
180
                }
181
22511
            },
182
        );
183

            
184
500
        (preprocessed_lts, mapped_state, partition)
185
500
    })
186
500
}
187

            
188
/// Computes a branching bisimulation partitioning using signature refinement without dirty blocks.
189
/// 
190
/// The `state` is any state for which we return the equivalent state in the preprocessed LTS.
191
400
pub fn weak_bisim_sigref_inductive_naive<L: LTS>(
192
400
    lts: L,
193
400
    state: StateIndex,
194
400
    preprocess: bool,
195
400
    timing: &Timing,
196
400
) -> (LabelledTransitionSystem<L::Label>, StateIndex, IndexedPartition) {
197
    // Preprocess the LTS if desired.
198
400
    if preprocess {
199
        let lts = timing.measure("preprocess", || {
200
            reduce_lts(lts, Equivalence::BranchingBisim, true, timing)
201
        });
202
        weak_bisim_sigref_inductive_naive_impl(lts, state, timing)
203
    } else {
204
400
        weak_bisim_sigref_inductive_naive_impl(lts, state, timing)
205
    }
206
400
}
207

            
208
/// Implementation of [weak_bisim_sigref_inductive_naive] that deals with both preprocessed and regular LTSs.
209
/// 
210
/// The `state` is any state for which we return the equivalent state in the preprocessed LTS.
211
400
pub fn weak_bisim_sigref_inductive_naive_impl<L: LTS>(
212
400
    lts: L,
213
400
    state: StateIndex,
214
400
    timing: &Timing,
215
400
) -> (LabelledTransitionSystem<L::Label>, StateIndex, IndexedPartition) {
216
400
    let (preprocessed_lts, mapped_state) = timing.measure("preprocess", || tau_loop_elimination_and_reorder(lts, state));
217
400
    let partition = timing.measure("reduction", || signature_refinement_weak(&preprocessed_lts));
218
400
    (preprocessed_lts, mapped_state, partition)
219
400
}
220

            
221
/// Computes a branching bisimulation partitioning using signature refinement without dirty blocks.
222
/// 
223
/// The `state` is any state for which we return the equivalent state in the preprocessed LTS.
224
400
pub fn weak_bisim_sigref_naive<L: LTS>(
225
400
    lts: L,
226
400
    state: StateIndex,
227
400
    preprocess: bool,
228
400
    timing: &Timing,
229
400
) -> (LabelledTransitionSystem<L::Label>, StateIndex, IndexedPartition) {
230
    // Preprocess the LTS if desired.
231
400
    if preprocess {
232
        let lts = timing.measure("preprocess", || {
233
            reduce_lts(lts, Equivalence::BranchingBisim, true, timing)
234
        });
235
        weak_bisim_sigref_naive_impl(lts, state, timing)
236
    } else {
237
400
        weak_bisim_sigref_naive_impl(lts, state, timing)
238
    }
239
400
}
240

            
241
/// Implementation of [weak_bisim_sigref_naive] that deals with both
242
/// preprocessed and regular LTSs.
243
/// 
244
/// The `state` is any state for which we return the equivalent state in the
245
/// preprocessed LTS.
246
400
fn weak_bisim_sigref_naive_impl<L: LTS>(
247
400
    lts: L,
248
400
    state: StateIndex,
249
400
    timing: &Timing,
250
400
) -> (LabelledTransitionSystem<L::Label>, StateIndex, IndexedPartition) {
251
400
    let (preprocessed_lts, mapped_state) = timing.measure("preprocess", || tau_loop_elimination_and_reorder(lts, state));
252
400
    let partition = timing.measure("reduction", || {
253
400
        signature_refinement_naive::<_, _, true>(
254
400
            &preprocessed_lts,
255
12554
            |state_index, partition, state_to_signature, builder| {
256
12554
                weak_bisim_signature_sorted(state_index, &preprocessed_lts, partition, state_to_signature, builder)
257
12554
            },
258
        )
259
400
    });
260

            
261
400
    (preprocessed_lts, mapped_state, partition)
262
400
}
263

            
264
/// General signature refinement algorithm that accepts an arbitrary signature
265
///
266
/// The signature function is called for each state and should fill the
267
/// signature builder with the signature of the state. It consists of the
268
/// current partition, the signatures per state for the next partition.
269
1200
fn signature_refinement<F, G, L, const BRANCHING: bool>(
270
1200
    lts: &L,
271
1200
    incoming: &IncomingTransitions,
272
1200
    mut signature: F,
273
1200
    mut renumber: G,
274
1200
) -> BlockPartition
275
1200
where
276
1200
    F: FnMut(StateIndex, &BlockPartition, &[BlockIndex], &mut SignatureBuilder),
277
1200
    G: FnMut(&[(LabelIndex, BlockIndex)], &Vec<Signature>) -> Option<BlockIndex>,
278
1200
    L: LTS,
279
{
280
    // Avoids reallocations when computing the signature.
281
1200
    let mut arena = Bump::new();
282
1200
    let mut builder = SignatureBuilder::default();
283
1200
    let mut split_builder = BlockPartitionBuilder::default();
284

            
285
    // Put all the states in the initial partition { S }.
286
1200
    let mut id: FxHashMap<Signature<'_>, BlockIndex> = FxHashMap::default();
287

            
288
    // Assigns the signature to each state.
289
1200
    let mut partition = BlockPartition::new(lts.num_of_states());
290
1200
    let mut state_to_key: Vec<BlockIndex> = Vec::new();
291
613631
    state_to_key.resize_with(lts.num_of_states(), || BlockIndex::new(0));
292
1200
    let mut key_to_signature: Vec<Signature> = Vec::new();
293

            
294
    // Refine partitions until stable.
295
1200
    let mut iteration = 0usize;
296
1200
    let mut states = Vec::new();
297

            
298
    // Used to keep track of dirty blocks.
299
1200
    let mut worklist = vec![BlockIndex::new(0)];
300

            
301
1200
    let progress = TimeProgress::new(
302
359
        |(iteration, blocks)| {
303
359
            info!("Iteration {iteration}, found {blocks} blocks...");
304
359
        },
305
        5,
306
    );
307

            
308
151626
    while let Some(block_index) = worklist.pop() {
309
        // Clear the current partition to start the next blocks.
310
150426
        id.clear();
311

            
312
        // Removes the existing signatures.
313
150426
        key_to_signature.clear();
314

            
315
        // Safety: The current signatures have been removed, so it safe to reuse the memory.
316
150426
        let id: &mut FxHashMap<Signature<'_>, BlockIndex> = unsafe { std::mem::transmute(&mut id) };
317
150426
        let key_to_signature: &'_ mut Vec<Signature<'_>> = unsafe { std::mem::transmute(&mut key_to_signature) };
318

            
319
150426
        arena.reset();
320

            
321
150426
        let block = partition.block(block_index);
322
150426
        debug_assert!(
323
150426
            block.has_marked(),
324
            "Every block in the worklist should have at least one marked state"
325
        );
326

            
327
150426
        if BRANCHING {
328
95377
            partition.mark_backward_closure(block_index, incoming);
329
100794
        }
330

            
331
        // Blocks above this number are new in this iteration.
332
150426
        let num_blocks = partition.num_of_blocks();
333

            
334
        // This is a workaround for a data race in bumpalo for zero-sized slices.
335
150426
        let empty_slice: &[(LabelIndex, BlockIndex)] = &[];
336

            
337
390259
        for new_block_index in
338
1086529
            partition.partition_marked_with(block_index, &mut split_builder, |state_index, partition| {
339
1086529
                signature(state_index, partition, &state_to_key, &mut builder);
340

            
341
                // Compute the signature of a single state
342
1086529
                let index = if let Some(key) = renumber(&builder, key_to_signature) {
343
61469
                    key
344
1025060
                } else if let Some((_, index)) = id.get_key_value(&Signature::new(&builder)) {
345
733477
                    *index
346
                } else {
347
291583
                    let slice = if builder.is_empty() {
348
982
                        empty_slice
349
                    } else {
350
290601
                        arena.alloc_slice_copy(&builder)
351
                    };
352
291583
                    let number = BlockIndex::new(key_to_signature.len());
353
291583
                    id.insert(Signature::new(slice), number);
354
291583
                    key_to_signature.push(Signature::new(slice));
355

            
356
291583
                    number
357
                };
358

            
359
                // (branching) Keep track of the signature for every block in the next partition.
360
1086529
                state_to_key[state_index] = index;
361

            
362
1086529
                trace!("State {state_index} signature {builder:?} index {index}");
363
1086529
                index
364
1086529
            })
365
        {
366
390259
            if block_index != new_block_index {
367
                // If this is a new block, mark the incoming states as dirty
368
239833
                states.clear();
369
239833
                states.extend(partition.iter_block(new_block_index));
370

            
371
776194
                for &state_index in &states {
372
802859
                    for transition in incoming.incoming_transitions(state_index) {
373
802859
                        if BRANCHING {
374
                            // Mark incoming states into old blocks, or visible actions.
375
500302
                            if !lts.is_hidden_label(transition.label)
376
98690
                                || partition.block_number(transition.from) < num_blocks
377
                            {
378
427362
                                let other_block = partition.block_number(transition.from);
379

            
380
427362
                                if !partition.block(other_block).has_marked() {
381
94877
                                    // If block was not already marked then add it to the worklist.
382
94877
                                    worklist.push(other_block);
383
332485
                                }
384

            
385
427362
                                partition.mark_element(transition.from);
386
72940
                            }
387
                        } else {
388
                            // In this case mark all incoming states.
389
302557
                            let other_block = partition.block_number(transition.from);
390

            
391
302557
                            if !partition.block(other_block).has_marked() {
392
54349
                                // If block was not already marked then add it to the worklist.
393
54349
                                worklist.push(other_block);
394
248208
                            }
395

            
396
302557
                            partition.mark_element(transition.from);
397
                        }
398
                    }
399
                }
400
150426
            }
401
        }
402

            
403
150426
        trace!("Iteration {iteration} partition {partition}");
404

            
405
150426
        iteration += 1;
406

            
407
150426
        progress.print((iteration, partition.num_of_blocks()));
408
    }
409

            
410
1200
    trace!("Refinement partition {partition}");
411
1200
    partition
412
1200
}
413

            
414
/// Weak signature refinement algorithm, doing inductive signatures naively.
415
///
416
/// The signature function is called for each state and should fill the
417
/// signature builder with the pre_signature of the state.
418
400
fn signature_refinement_weak<L: LTS>(lts: &L) -> IndexedPartition
419
400
where {
420
    // Avoids reallocations when computing the signature.
421
400
    let mut arena = Bump::new();
422
400
    let mut builder = SignatureBuilder::default();
423

            
424
    // Put all the states in the initial partition { S }.
425
400
    let mut id: FxHashMap<Signature<'_>, BlockIndex> = FxHashMap::default();
426

            
427
    // Assigns the signature to each state.
428
400
    let mut partition = IndexedPartition::new(lts.num_of_states());
429
400
    let mut next_partition = IndexedPartition::new(lts.num_of_states());
430
400
    let mut state_to_signature: Vec<Option<usize>> = Vec::new();
431
400
    let mut key_to_signature: Vec<Signature> = Vec::new();
432
400
    let mut state_to_taus: Vec<Signature> = Vec::new();
433

            
434
400
    state_to_signature.resize_with(lts.num_of_states(), || None);
435
400
    state_to_taus.resize_with(lts.num_of_states(), Signature::default);
436

            
437
400
    let mut old_count = 1;
438
400
    let mut iteration = 0;
439

            
440
400
    let progress = TimeProgress::new(
441
        |(iteration, blocks)| {
442
            debug!("Iteration {iteration}, found {blocks} blocks...",);
443
        },
444
        5,
445
    );
446

            
447
    // This is a workaround for a data race in bumpalo for zero-sized slices.
448
400
    let empty_slice: &[(LabelIndex, BlockIndex)] = &[];
449
    // Refine partitions until stable.
450

            
451
1367
    while old_count != id.len() {
452
967
        old_count = id.len();
453
967
        progress.print((iteration, old_count));
454
967
        swap(&mut partition, &mut next_partition);
455

            
456
        // Clear the current partition to start the next blocks.
457
967
        id.clear();
458

            
459
        // Remove the current signatures.
460
967
        arena.reset();
461

            
462
967
        state_to_signature.clear();
463
967
        key_to_signature.clear();
464

            
465
967
        state_to_signature.resize_with(lts.num_of_states(), || None);
466
        // Safety: The current signatures have been removed, so it safe to reuse the memory.
467
967
        let state_to_signature: &'_ mut Vec<Option<usize>> = unsafe { std::mem::transmute(&mut state_to_signature) };
468
967
        let id: &'_ mut FxHashMap<Signature<'_>, BlockIndex> = unsafe { std::mem::transmute(&mut id) };
469
967
        let key_to_signature: &'_ mut Vec<Signature<'_>> = unsafe { std::mem::transmute(&mut key_to_signature) };
470
967
        let state_to_taus: &'_ mut Vec<Signature<'_>> = unsafe { std::mem::transmute(&mut state_to_taus) };
471

            
472
        // Compute for each state its tau signature. This seems inefficient, but for now it works.
473
967
        state_to_taus.resize_with(lts.num_of_states(), Signature::default);
474
33811
        for state in lts.iter_states() {
475
33811
            weak_bisim_signature_sorted_taus(state, lts, &partition, state_to_taus, &mut builder);
476

            
477
33811
            let slice = if builder.is_empty() {
478
                empty_slice
479
            } else {
480
33811
                arena.alloc_slice_copy(&builder)
481
            };
482
33811
            state_to_taus[state] = Signature::new(slice);
483
        }
484

            
485
33811
        for state_index in lts.iter_states() {
486
            // Compute the Presignature of a single state
487
33811
            weak_bisim_presignature_sorted(
488
33811
                state_index,
489
33811
                lts,
490
33811
                &partition,
491
33811
                state_to_taus,
492
33811
                state_to_signature,
493
33811
                &mut builder,
494
            );
495

            
496
            // Inductive step see if presig is a subset of a tau reachable state.
497
33811
            let mut inductive_key = None;
498
118544
            for keyvalue in builder.as_slice() {
499
118544
                if is_tau_hat(keyvalue.0, lts) {
500
19293
                    let tau_sig = &key_to_signature[keyvalue.1.value()];
501
19293
                    let presig = Signature::new(&builder);
502

            
503
19293
                    if tau_sig.is_subset_of(presig.as_slice(), *keyvalue) {
504
9114
                        inductive_key = Some(*keyvalue.1);
505
9114
                        break;
506
10179
                    }
507
99251
                }
508
            }
509
33811
            if let Some(inductive_key) = inductive_key {
510
9114
                trace!(
511
                    "State {state_index} with pre {:?} uses inductive key {inductive_key}:{:?}",
512
                    builder.as_slice(),
513
                    key_to_signature[inductive_key].as_slice()
514
                );
515
9114
                state_to_signature[state_index] = Some(inductive_key);
516
9114
                next_partition.set_block(*state_index, BlockIndex::new(inductive_key));
517
            } else {
518
                // If not: expand the signature completely.
519
24697
                weak_bisim_signature_sorted_full(
520
24697
                    state_index,
521
24697
                    lts,
522
24697
                    &partition,
523
24697
                    state_to_taus,
524
24697
                    state_to_signature,
525
24697
                    key_to_signature,
526
24697
                    &mut builder,
527
                );
528
24697
                trace!("State {state_index} final signature {:?}", builder.as_slice());
529

            
530
                // Keep track of the index for every state
531
24697
                let mut new_id = BlockIndex::new(key_to_signature.len());
532
24697
                if let Some((_signature, index)) = id.get_key_value(&Signature::new(&builder)) {
533
13865
                    // SAFETY: We know that the signature lives as long as the arena
534
13865
                    state_to_signature[state_index] = Some(index.value());
535
13865
                    new_id = *index;
536
13865
                } else {
537
10832
                    let slice = if builder.is_empty() {
538
                        empty_slice
539
                    } else {
540
10832
                        arena.alloc_slice_copy(&builder)
541
                    };
542
10832
                    id.insert(Signature::new(slice), new_id);
543
10832
                    key_to_signature.push(Signature::new(slice));
544

            
545
10832
                    state_to_signature[state_index] = Some(new_id.value());
546
                }
547

            
548
24697
                next_partition.set_block(*state_index, new_id);
549
            };
550
        }
551

            
552
967
        iteration += 1;
553

            
554
967
        debug_assert!(
555
967
            iteration <= lts.num_of_states().max(2),
556
            "There can never be more splits than number of states, but at least two iterations for stability"
557
        );
558
    }
559

            
560
400
    trace!("Refinement partition {partition}");
561
    // debug_assert!(
562
    //     is_valid_refinement(lts, &partition, |state_index, partition, builder| signature(
563
    //         state_index,
564
    //         partition,
565
    //         &state_to_signature,
566
    //         builder
567
    //     )),
568
    //     "The resulting partition is not a valid partition."
569
    // );
570
400
    partition
571
400
}
572

            
573
/// General signature refinement algorithm that accepts an arbitrary signature
574
///
575
/// The signature function is called for each state and should fill the
576
/// signature builder with the signature of the state. It consists of the
577
/// current partition, the signatures per state for the next partition.
578
1300
fn signature_refinement_naive<F, L: LTS, const WEAK: bool>(lts: &L, mut signature: F) -> IndexedPartition
579
1300
where
580
1300
    F: FnMut(StateIndex, &IndexedPartition, &Vec<Signature<'_>>, &mut SignatureBuilder),
581
{
582
    // Avoids reallocations when computing the signature.
583
1300
    let mut arena = Bump::new();
584
1300
    let mut builder = SignatureBuilder::default();
585

            
586
    // Put all the states in the initial partition { S }.
587
1300
    let mut id: FxHashMap<Signature<'_>, BlockIndex> = FxHashMap::default();
588

            
589
    // Assigns the signature to each state.
590
1300
    let mut partition = IndexedPartition::new(lts.num_of_states());
591
1300
    let mut next_partition = IndexedPartition::new(lts.num_of_states());
592
1300
    let mut state_to_signature: Vec<Signature<'_>> = Vec::new();
593
1300
    state_to_signature.resize_with(lts.num_of_states(), Signature::default);
594

            
595
    // Refine partitions until stable.
596
1300
    let mut old_count = 1;
597
1300
    let mut iteration = 0;
598

            
599
1300
    let progress = TimeProgress::new(
600
        |(iteration, blocks)| {
601
            debug!("Iteration {iteration}, found {blocks} blocks...",);
602
        },
603
        5,
604
    );
605

            
606
    // This is a workaround for a data race in bumpalo for zero-sized slices.
607
1300
    let empty_slice: &[(LabelIndex, BlockIndex)] = &[];
608

            
609
4592
    while old_count != id.len() {
610
3292
        trace!("Iteration {} ({} blocks)", iteration, id.len());
611

            
612
3292
        old_count = id.len();
613
3292
        progress.print((iteration, old_count));
614
3292
        swap(&mut partition, &mut next_partition);
615

            
616
        // Clear the current partition to start the next blocks.
617
3292
        id.clear();
618

            
619
3292
        state_to_signature.clear();
620
3292
        state_to_signature.resize_with(lts.num_of_states(), Signature::default);
621

            
622
        // Safety: The current signatures have been removed, so it safe to reuse the memory.
623
3292
        let id: &'_ mut FxHashMap<Signature<'_>, BlockIndex> = unsafe { std::mem::transmute(&mut id) };
624
3292
        let state_to_signature: &mut Vec<Signature<'_>> = unsafe { std::mem::transmute(&mut state_to_signature) };
625

            
626
        // Remove the current signatures.
627
3292
        arena.reset();
628

            
629
3292
        if WEAK {
630
9490
            for state_index in lts.iter_states() {
631
9490
                weak_bisim_signature_sorted_taus(state_index, lts, &partition, state_to_signature, &mut builder);
632

            
633
9490
                trace!("State {state_index} weak signature {:?}", builder);
634

            
635
                // Keep track of the index for every state, either use the arena to allocate space or simply borrow the value.
636
9490
                let slice = if builder.is_empty() {
637
                    empty_slice
638
                } else {
639
9490
                    arena.alloc_slice_copy(&builder)
640
                };
641
9490
                state_to_signature[state_index] = Signature::new(slice);
642
            }
643
2346
        }
644

            
645
45540
        for state_index in lts.iter_states() {
646
            // Compute the signature of a single state
647
45540
            signature(state_index, &partition, state_to_signature, &mut builder);
648

            
649
45540
            trace!("State {state_index} signature {builder:?}");
650

            
651
            // Keep track of the index for every state, either use the arena to allocate space or simply borrow the value.
652
45540
            let mut new_id = BlockIndex::new(id.len());
653
45540
            if let Some((signature, index)) = id.get_key_value(&Signature::new(&builder)) {
654
28233
                // SAFETY: We know that the signature lives as long as the arena
655
28233
                state_to_signature[state_index] = unsafe {
656
28233
                    std::mem::transmute::<Signature<'_>, Signature<'_>>(Signature::new(signature.as_slice()))
657
28233
                };
658
28233
                new_id = *index;
659
28233
            } else {
660
17307
                let slice = if builder.is_empty() {
661
1898
                    empty_slice
662
                } else {
663
15409
                    arena.alloc_slice_copy(&builder)
664
                };
665
17307
                id.insert(Signature::new(slice), new_id);
666

            
667
                // (branching) Keep track of the signature for every block in the next partition.
668
17307
                state_to_signature[state_index] = Signature::new(slice);
669
            }
670

            
671
45540
            next_partition.set_block(*state_index, new_id);
672
        }
673

            
674
3292
        iteration += 1;
675

            
676
3292
        debug_assert!(
677
3292
            iteration <= lts.num_of_states().max(2),
678
            "There can never be more splits than number of states, but at least two iterations for stability"
679
        );
680
    }
681

            
682
1300
    trace!("Refinement partition {partition}");
683
1300
    debug_assert!(
684
12021
        is_valid_refinement(lts, &partition, |state_index, partition, builder| signature(
685
12021
            state_index,
686
12021
            partition,
687
12021
            &state_to_signature,
688
12021
            builder
689
        )),
690
        "The resulting partition is not a valid partition."
691
    );
692
1300
    partition
693
1300
}
694

            
695
/// Returns true iff the given partition is a strong bisimulation partition
696
1300
pub fn is_valid_refinement<F, P, L>(lts: &L, partition: &P, mut compute_signature: F) -> bool
697
1300
where
698
1300
    F: FnMut(StateIndex, &P, &mut SignatureBuilder),
699
1300
    P: Partition,
700
1300
    L: LTS,
701
{
702
    // Check that the partition is indeed stable and as such is a quotient of strong bisimulation
703
1300
    let mut block_to_signature: Vec<Option<SignatureBuilder>> = vec![None; partition.num_of_blocks()];
704

            
705
    // Avoids reallocations when computing the signature.
706
1300
    let mut builder = SignatureBuilder::default();
707

            
708
12021
    for state_index in lts.iter_states() {
709
12021
        let block = partition.block_number(state_index);
710

            
711
        // Compute the flat signature, which has Hash and is more compact.
712
12021
        compute_signature(state_index, partition, &mut builder);
713
12021
        let signature: Vec<(LabelIndex, BlockIndex)> = builder.clone();
714

            
715
12021
        if let Some(block_signature) = &block_to_signature[block] {
716
6322
            if signature != *block_signature {
717
                trace!(
718
                    "State {state_index} has a different signature {signature:?} then the block {block} which has signature {block_signature:?}"
719
                );
720
                return false;
721
6322
            }
722
5699
        } else {
723
5699
            block_to_signature[block] = Some(signature);
724
5699
        };
725
    }
726

            
727
    // Check if there are two blocks with the same signature
728
1300
    let mut signature_to_block: FxHashMap<Signature, usize> = FxHashMap::default();
729

            
730
5699
    for (block_index, signature) in block_to_signature
731
1300
        .iter()
732
5699
        .map(|signature: &Option<SignatureBuilder>| signature.as_ref().expect("Signature should be defined"))
733
1300
        .enumerate()
734
    {
735
5699
        if let Some(other_block_index) = signature_to_block.get(&Signature::new(signature)) {
736
            if block_index != *other_block_index {
737
                trace!("Block {block_index} and {other_block_index} have the same signature {signature:?}");
738
                return false;
739
            }
740
5699
        } else {
741
5699
            signature_to_block.insert(Signature::new(signature), block_index);
742
5699
        }
743
    }
744

            
745
1300
    true
746
1300
}
747

            
748
#[cfg(test)]
749
mod tests {
750
    use test_log::test;
751

            
752
    use merc_io::DumpFiles;
753
    use merc_lts::random_lts;
754
    use merc_lts::write_aut;
755
    use merc_utilities::Timing;
756
    use merc_utilities::random_test;
757

            
758
    use super::BlockIndex;
759
    use super::LTS;
760
    use super::Partition;
761
    use super::StateIndex;
762
    use super::branching_bisim_sigref;
763
    use super::branching_bisim_sigref_naive;
764
    use super::strong_bisim_sigref;
765
    use super::strong_bisim_sigref_naive;
766
    use super::weak_bisim_sigref_inductive_naive;
767
    use super::weak_bisim_sigref_naive;
768

            
769
    /// Returns true iff the partitions are equal, runs in O(n^2).
770
300
    fn equal_partitions<P: Partition, Q: Partition>(left: &P, right: &Q) -> bool {
771
        // Check that states in the same block, have a single (unique) number in
772
        // the other partition.
773
1670
        for block_index in (0..left.num_of_blocks()).map(BlockIndex::new) {
774
1670
            let mut other_block_index = None;
775

            
776
3775
            for state_index in (0..left.len())
777
1670
                .map(StateIndex::new)
778
109253
                .filter(|&state_index| left.block_number(state_index) == block_index)
779
            {
780
3775
                match other_block_index {
781
1670
                    None => other_block_index = Some(right.block_number(state_index)),
782
2105
                    Some(other_block_index) => {
783
2105
                        if right.block_number(state_index) != other_block_index {
784
                            return false;
785
2105
                        }
786
                    }
787
                }
788
            }
789
        }
790

            
791
1670
        for block_index in (0..right.num_of_blocks()).map(BlockIndex::new) {
792
1670
            let mut other_block_index = None;
793

            
794
3775
            for state_index in (0..left.len())
795
1670
                .map(StateIndex::new)
796
109253
                .filter(|&state_index| right.block_number(state_index) == block_index)
797
            {
798
3775
                match other_block_index {
799
1670
                    None => other_block_index = Some(left.block_number(state_index)),
800
2105
                    Some(other_block_index) => {
801
2105
                        if left.block_number(state_index) != other_block_index {
802
                            return false;
803
2105
                        }
804
                    }
805
                }
806
            }
807
        }
808

            
809
300
        true
810
300
    }
811

            
812
    /// Checks that the strong bisimulation partition is a refinement of the branching bisimulation partition.
813
200
    fn is_refinement<L: LTS, P: Partition, Q: Partition>(lts: &L, strong_partition: &P, branching_partition: &Q) {
814
2493
        for state_index in lts.iter_states() {
815
149539
            for other_state_index in lts.iter_states() {
816
149539
                if strong_partition.block_number(state_index) == strong_partition.block_number(other_state_index) {
817
                    // If the states are together according to strong bisimilarity, then they should also be together according to branching bisimilarity.
818
11763
                    assert_eq!(
819
11763
                        branching_partition.block_number(state_index),
820
11763
                        branching_partition.block_number(other_state_index),
821
                        "The strong partition should be a refinement of the branching partition, but states {state_index} and {other_state_index} are in different strong blocks"
822
                    );
823
137776
                }
824
            }
825
        }
826
200
    }
827

            
828
    #[test]
829
    #[cfg_attr(miri, ignore)] // Miri is too slow
830
    fn test_random_strong_bisim_sigref() {
831
100
        random_test(100, |rng| {
832
100
            let mut files = DumpFiles::new("test_random_strong_bisim_sigref");
833

            
834
100
            let lts = random_lts(rng, 10, 3, 3);
835
100
            files.dump("input.aut", |writer| write_aut(writer, &lts)).unwrap();
836

            
837
100
            let mut timing = Timing::new();
838

            
839
100
            let (result_lts, result_partition) = strong_bisim_sigref(lts.clone(), &mut timing);
840
100
            let (expected_lts, expected_partition) = strong_bisim_sigref_naive(lts, &mut timing);
841

            
842
100
            files
843
100
                .dump("result.aut", |writer| write_aut(writer, &result_lts))
844
100
                .unwrap();
845
100
            files
846
100
                .dump("expected.aut", |writer| write_aut(writer, &expected_lts))
847
100
                .unwrap();
848

            
849
            // There is no preprocessing so this works.
850
100
            assert!(equal_partitions(&result_partition, &expected_partition));
851
100
        });
852
    }
853

            
854
    #[test]
855
    #[cfg_attr(miri, ignore)] // Miri is too slow
856
    fn test_random_branching_bisim_sigref() {
857
100
        random_test(100, |rng| {
858
100
            let mut files = DumpFiles::new("test_random_branching_bisim_sigref");
859

            
860
100
            let lts = random_lts(rng, 10, 3, 3);
861
100
            files.dump("input.aut", |writer| write_aut(writer, &lts)).unwrap();
862

            
863
100
            let mut timing = Timing::new();
864

            
865
100
            let (result_lts, _, result_partition) = branching_bisim_sigref(lts.clone(), StateIndex::new(0), &mut timing);
866
100
            let (expected_lts, _, expected_partition) = branching_bisim_sigref_naive(lts, StateIndex::new(0), &mut timing);
867

            
868
100
            files
869
100
                .dump("result.aut", |writer| write_aut(writer, &result_lts))
870
100
                .unwrap();
871
100
            files
872
100
                .dump("expected.aut", |writer| write_aut(writer, &expected_lts))
873
100
                .unwrap();
874

            
875
            // There is no preprocessing so this works.
876
100
            assert!(equal_partitions(&result_partition, &expected_partition));
877
100
        });
878
    }
879

            
880
    #[test]
881
    #[cfg_attr(miri, ignore)] // Miri is too slow
882
    fn test_random_weak_bisim_sigref() {
883
100
        random_test(100, |rng| {
884
100
            let mut files = DumpFiles::new("test_random_weak_bisim_sigref");
885

            
886
100
            let lts = random_lts(rng, 10, 3, 3);
887
100
            files.dump("input.aut", |writer| write_aut(writer, &lts)).unwrap();
888

            
889
100
            let mut timing = Timing::new();
890

            
891
100
            let (result_lts, _, result_partition) = weak_bisim_sigref_naive(lts.clone(), StateIndex::new(0), false, &mut timing);
892
100
            let (expected_lts, _, expected_partition) = weak_bisim_sigref_inductive_naive(lts, StateIndex::new(0), false, &mut timing);
893

            
894
100
            files
895
100
                .dump("result.aut", |writer| write_aut(writer, &result_lts))
896
100
                .unwrap();
897
100
            files
898
100
                .dump("expected.aut", |writer| write_aut(writer, &expected_lts))
899
100
                .unwrap();
900

            
901
            // There is no preprocessing so this works.
902
100
            assert!(equal_partitions(&result_partition, &expected_partition));
903
100
        });
904
    }
905

            
906
    #[test]
907
    #[cfg_attr(miri, ignore)] // Miri is too slow
908
    fn test_random_branching_bisim_sigref_naive() {
909
100
        random_test(100, |rng| {
910
100
            let mut files = DumpFiles::new("test_random_branching_bisim_sigref_naive");
911

            
912
100
            let lts = random_lts(rng, 10, 3, 3);
913
100
            files.dump("input.aut", |writer| write_aut(writer, &lts)).unwrap();
914

            
915
100
            let mut timing = Timing::new();
916

            
917
100
            let (preprocessed_lts, _, branching_partition) = branching_bisim_sigref_naive(lts, StateIndex::new(0), &mut timing);
918
100
            files
919
100
                .dump("preprocessed.aut", |writer| write_aut(writer, &preprocessed_lts))
920
100
                .unwrap();
921

            
922
100
            let strong_partition = strong_bisim_sigref_naive(preprocessed_lts.clone(), &mut timing).1;
923
100
            is_refinement(&preprocessed_lts, &strong_partition, &branching_partition);
924
100
        });
925
    }
926

            
927
    #[test]
928
    #[cfg_attr(miri, ignore)] // Miri is too slow
929
    fn test_random_weak_bisim_sigref_naive() {
930
100
        random_test(100, |rng| {
931
100
            let mut files = DumpFiles::new("test_random_weak_bisim_sigref_naive");
932

            
933
100
            let lts = random_lts(rng, 10, 3, 3);
934
100
            files.dump("input.aut", |writer| write_aut(writer, &lts)).unwrap();
935

            
936
100
            let mut timing = Timing::new();
937

            
938
100
            let (preprocessed_lts, _, weak_partition) = weak_bisim_sigref_naive(lts, StateIndex::new(0), false, &mut timing);
939
100
            files
940
100
                .dump("preprocessed.aut", |writer| write_aut(writer, &preprocessed_lts))
941
100
                .unwrap();
942

            
943
100
            let (_, _, branching_partition) = branching_bisim_sigref_naive(preprocessed_lts.clone(), StateIndex::new(0), &mut timing);
944
100
            is_refinement(&preprocessed_lts, &branching_partition, &weak_partition);
945
100
        });
946
    }
947
}