Skip to content

Commit b11dc3c

Browse files
author
GBathie
committed
Make code public
1 parent e44f695 commit b11dc3c

38 files changed

+3702
-8
lines changed

.gitignore

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,4 @@ Cargo.lock
1111
**/*.rs.bk
1212

1313
# MSVC Windows builds of rustc generate these, which store debugging information
14-
*.pdb
15-
16-
# RustRover
17-
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18-
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19-
# and can be added to the global gitignore or merged into this file. For a more nuclear
20-
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
21-
#.idea/
14+
*.pdb

Cargo.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "ltl-rs"
3+
version = "0.1.0"
4+
edition = "2021"
5+
default-run= "ltl-rs"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
clap = { version = "4.5.1", features = ["derive"] }
11+
env_logger = "0.11.2"
12+
fxhash = "0.2.1"
13+
itertools = "0.13.0"
14+
log = "0.4.20"
15+
ordered-float = "4.2.1"
16+
thiserror = "1.0.64"
17+
18+
[dev-dependencies]
19+
rand = "0.8.5"

src/algos/beam_search/cache.rs

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
use std::{
2+
cmp::Ordering,
3+
collections::{hash_map::Entry, BinaryHeap},
4+
};
5+
6+
use fxhash::FxHashMap;
7+
use itertools::Itertools;
8+
9+
use crate::{
10+
cache::{EnumFormulaCache, EnumFormulaCacheLine, FormulaCache},
11+
traits::Hashed,
12+
};
13+
14+
use crate::bool::{charac::BoolCharac, BoolFormula};
15+
16+
/// Keeps a hashmap for observational equivalence,
17+
/// and only the `max_line_size` formulas with highest density of each size.
18+
/// Computes domination over the line size.
19+
#[derive(Debug)]
20+
pub struct BeamSearchCache {
21+
entries: FxHashMap<<BoolCharac as Hashed>::HashType, BoolFormula>,
22+
lines: Vec<BinaryHeap<PcoBoolFormula>>,
23+
max_line_size: usize,
24+
}
25+
26+
impl BeamSearchCache {
27+
pub(crate) fn new(max_line_size: usize) -> Self {
28+
Self {
29+
entries: Default::default(),
30+
lines: vec![],
31+
max_line_size,
32+
}
33+
}
34+
}
35+
36+
impl FormulaCache<BoolCharac> for BeamSearchCache {
37+
fn len(&self) -> usize {
38+
self.lines.iter().map(|l| l.len()).sum()
39+
}
40+
41+
fn get(&self, hash: &<BoolCharac as Hashed>::HashType) -> Option<&BoolFormula> {
42+
self.entries.get(hash)
43+
}
44+
}
45+
46+
impl EnumFormulaCache<BoolCharac> for BeamSearchCache {
47+
type CacheLine<'a> = BeamSearchBoolCacheLine<'a>;
48+
49+
fn iter_size<'a>(&'a self, size: usize) -> impl Iterator<Item = &'a BoolFormula>
50+
where
51+
BoolCharac: 'a,
52+
{
53+
self.lines[size].iter().map(|pf| &pf.f)
54+
}
55+
56+
fn new_line_and_iter_size<'a>(
57+
&'a mut self,
58+
size: usize,
59+
) -> (
60+
impl Iterator<Item = &'a BoolFormula>,
61+
impl Iterator<Item = (&'a BoolFormula, &'a BoolFormula)>,
62+
Self::CacheLine<'a>,
63+
)
64+
where
65+
BoolCharac: 'a,
66+
{
67+
self.lines.push(BinaryHeap::new());
68+
let (old_lines, new) = self.lines.split_at_mut(size);
69+
70+
let new_line = BeamSearchBoolCacheLine {
71+
line: &mut new[0],
72+
hashes: &mut self.entries,
73+
max_line_size: self.max_line_size,
74+
};
75+
76+
let iter_size = size - 1;
77+
let iter_formulas = old_lines[iter_size].iter().map(|pf| &pf.f);
78+
79+
let iter_pairs_size = (size + 1) / 2;
80+
let iter_pairs = old_lines
81+
.iter()
82+
.zip(old_lines.iter().rev())
83+
.take(iter_pairs_size)
84+
.flat_map(|(i1, i2)| i1.iter().cartesian_product(i2))
85+
.map(|(pf1, pf2)| (&pf1.f, &pf2.f));
86+
(iter_formulas, iter_pairs, new_line)
87+
}
88+
89+
fn new_line<'a>(&'a mut self, size: usize) -> Self::CacheLine<'a>
90+
where
91+
BoolCharac: 'a,
92+
{
93+
self.lines.push(BinaryHeap::new());
94+
95+
BeamSearchBoolCacheLine {
96+
line: &mut self.lines[size],
97+
hashes: &mut self.entries,
98+
max_line_size: self.max_line_size,
99+
}
100+
}
101+
102+
fn nb_lines(&self) -> usize {
103+
self.lines.len()
104+
}
105+
}
106+
107+
impl IntoIterator for BeamSearchCache {
108+
type Item = BoolFormula;
109+
110+
type IntoIter = Box<dyn Iterator<Item = BoolFormula>>;
111+
112+
fn into_iter(self) -> Self::IntoIter {
113+
Box::new(self.lines.into_iter().flatten().map(|pf| pf.f))
114+
}
115+
}
116+
117+
pub(crate) struct BeamSearchBoolCacheLine<'a> {
118+
line: &'a mut BinaryHeap<PcoBoolFormula>,
119+
hashes: &'a mut FxHashMap<<BoolCharac as Hashed>::HashType, BoolFormula>,
120+
max_line_size: usize,
121+
}
122+
123+
impl<'a> BeamSearchBoolCacheLine<'a> {
124+
fn dominates(&self, f: &BoolFormula) -> Option<<BoolCharac as Hashed>::HashType> {
125+
self.line.iter().find_map(|sv| {
126+
if sv.dominates(f) {
127+
Some(sv.f.hashed())
128+
} else {
129+
None
130+
}
131+
})
132+
}
133+
}
134+
135+
impl<'a> EnumFormulaCacheLine<BoolCharac> for BeamSearchBoolCacheLine<'a> {
136+
fn push(&mut self, f: BoolFormula) -> bool {
137+
if self.dominates(&f).is_some() {
138+
return false;
139+
}
140+
141+
let hash = f.hashed();
142+
let removed = match self.hashes.entry(hash) {
143+
Entry::Occupied(_) => return false,
144+
Entry::Vacant(e) => {
145+
e.insert(f.clone());
146+
self.line.push(PcoBoolFormula { f });
147+
if self.line.len() > self.max_line_size {
148+
self.line.pop()
149+
} else {
150+
return true;
151+
}
152+
}
153+
};
154+
155+
self.hashes.remove(&removed.unwrap().f.hashed());
156+
157+
true
158+
}
159+
}
160+
161+
// Stores a SatVec together with the hash of the corresponding Boolean formula.
162+
// Used when removing dominated formulas in a single to canonicalize the entries at the end of the push round.
163+
#[derive(Debug, PartialEq, Eq)]
164+
struct PcoBoolFormula {
165+
pub(crate) f: BoolFormula,
166+
}
167+
168+
impl PartialOrd for PcoBoolFormula {
169+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
170+
Some(self.cmp(other))
171+
}
172+
}
173+
174+
/// Order by max popcount.
175+
impl Ord for PcoBoolFormula {
176+
fn cmp(&self, other: &Self) -> Ordering {
177+
other
178+
.f
179+
.charac
180+
.sv
181+
.popcount()
182+
.cmp(&self.f.charac.sv.popcount())
183+
}
184+
}
185+
186+
impl PcoBoolFormula {
187+
pub(crate) fn dominates(&self, f: &BoolFormula) -> bool {
188+
self.f.charac.sv.dominates(f.charac.sv)
189+
}
190+
}

src/algos/beam_search/mod.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//! Beam Search algorithm for Boolean Synthesis.
2+
//!
3+
//! Bottom-up enumeration with fixed width.
4+
//! Implemented using a fixed-width cache ([`BeamSearchCache`])
5+
//! and the enumeration algorithm.
6+
use cache::BeamSearchCache;
7+
use clap::Args;
8+
9+
use crate::{
10+
algos::enumeration::aux::enum_aux,
11+
bool::{charac::BoolCharac, cv::CharVec, BoolFormula},
12+
cache::{EnumFormulaCache, EnumFormulaCacheLine},
13+
formula::{rebuild_formula, tree::FormulaTree},
14+
ltl::trace::Operators,
15+
};
16+
17+
pub mod cache;
18+
19+
use super::{meta::cache::InitialBoolCache, BoolAlgoParams};
20+
21+
#[derive(Args, Clone, Copy)]
22+
pub struct BeamSearchParams {
23+
/// Number of formulas to keep at each level.
24+
beam_width: usize,
25+
/// Maximum enumeration size
26+
max_size_bool: usize,
27+
}
28+
29+
impl BoolAlgoParams for BeamSearchParams {
30+
type Data = ();
31+
32+
fn run(
33+
&self,
34+
cache: InitialBoolCache,
35+
operators: Operators,
36+
target: &[bool],
37+
) -> (Option<FormulaTree>, Self::Data) {
38+
let bool_target = target.iter().copied().collect();
39+
let bool_operators = operators.filter_bool();
40+
let mut cache = convert_cache_beam_search(cache, self.beam_width, bool_target);
41+
let f = enum_aux(
42+
&mut cache,
43+
&bool_operators,
44+
&bool_target,
45+
self.max_size_bool,
46+
);
47+
48+
let f_str = f.map(|f| rebuild_formula(&f, &cache));
49+
(f_str, ())
50+
}
51+
52+
fn name() -> &'static str {
53+
"beam_search"
54+
}
55+
}
56+
57+
fn convert_cache_beam_search(
58+
cache: InitialBoolCache,
59+
max_line_size: usize,
60+
target: CharVec,
61+
) -> BeamSearchCache {
62+
let mut bs_cache = BeamSearchCache::new(max_line_size);
63+
64+
for (size, cache) in cache.iter_lines().into_iter().enumerate() {
65+
let mut new_line = bs_cache.new_line(size);
66+
67+
for (cv, t, size) in cache {
68+
let cv = cv.into_iter().collect();
69+
let f = BoolFormula::new_base(BoolCharac::from_cv(cv, target), size, t);
70+
new_line.push(f);
71+
}
72+
}
73+
74+
bs_cache
75+
}

0 commit comments

Comments
 (0)