Skip to content

Commit 74ffdef

Browse files
authored
Use more types of constraints in fuzzer (#212)
* test more constraints in Fuzzer * fix normalize_extension_supports
1 parent 1a094ea commit 74ffdef

2 files changed

Lines changed: 157 additions & 13 deletions

File tree

cspuz_core/src/integration/tests/fuzz.rs

Lines changed: 134 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::IntegrationTester;
22
use crate::arithmetic::CmpOp;
3+
use crate::csp::Stmt;
34
use crate::integration::*;
45

56
struct Fuzzer {
@@ -69,13 +70,13 @@ impl Fuzzer {
6970
}
7071
}
7172

72-
let mut exprs = vec![];
73+
let mut stmt_descs = vec![];
7374
for _ in 0..num_exprs {
74-
let complexity = self.next_u32(max_complexity);
75-
76-
let expr = self.random_bool_expr(&bool_vars, &int_vars, complexity);
77-
exprs.push(expr.clone());
78-
tester.add_expr(expr);
75+
let stmt = self.random_stmt(&bool_vars, &int_vars, max_complexity);
76+
let mut buf = vec![];
77+
let _ = stmt.pretty_print(&mut buf);
78+
stmt_descs.push(String::from_utf8(buf).unwrap_or_default());
79+
tester.add_constraint(stmt);
7980
}
8081

8182
if !tester.check_internal(true) {
@@ -85,18 +86,124 @@ impl Fuzzer {
8586
for desc in &int_var_descs {
8687
eprintln!("- {}", desc);
8788
}
88-
eprintln!("Expressions:");
89-
for expr in exprs {
90-
eprint!("- ");
91-
let mut out_buf = vec![];
92-
let _ = expr.pretty_print(&mut out_buf);
93-
eprint!("{}", String::from_utf8(out_buf).unwrap());
94-
eprintln!();
89+
eprintln!("Statements:");
90+
for desc in &stmt_descs {
91+
eprintln!("- {}", desc);
9592
}
9693
panic!();
9794
}
9895
}
9996

97+
fn random_stmt(
98+
&mut self,
99+
bool_vars: &[BoolVar],
100+
int_vars: &[IntVar],
101+
max_complexity: u32,
102+
) -> Stmt {
103+
// Weights: 0,1 = AllDifferent, 2 = ActiveVerticesConnected,
104+
// 3 (feature-gated) = ExtensionSupports, rest = BoolExpr
105+
#[cfg(feature = "csp-extra-constraints")]
106+
let mode = self.next_u32(11);
107+
#[cfg(not(feature = "csp-extra-constraints"))]
108+
let mode = self.next_u32(10);
109+
110+
match mode {
111+
0 | 1 => self.random_alldifferent_stmt(bool_vars, int_vars, max_complexity),
112+
2 => self.random_active_vertices_connected_stmt(bool_vars, int_vars, max_complexity),
113+
#[cfg(feature = "csp-extra-constraints")]
114+
3 => self.random_extension_supports_stmt(bool_vars, int_vars),
115+
_ => {
116+
let complexity = self.next_u32(max_complexity);
117+
Stmt::Expr(self.random_bool_expr(bool_vars, int_vars, complexity))
118+
}
119+
}
120+
}
121+
122+
fn random_alldifferent_stmt(
123+
&mut self,
124+
bool_vars: &[BoolVar],
125+
int_vars: &[IntVar],
126+
max_complexity: u32,
127+
) -> Stmt {
128+
if int_vars.is_empty() {
129+
let complexity = self.next_u32(max_complexity);
130+
return Stmt::Expr(self.random_bool_expr(bool_vars, int_vars, complexity));
131+
}
132+
// 2 to 5 expressions
133+
let n = (self.next_u32(4) as usize + 2).min(5);
134+
let exprs: Vec<IntExpr> = (0..n)
135+
.map(|_| {
136+
let c = self.next_u32(max_complexity / 2 + 1);
137+
self.random_int_expr(bool_vars, int_vars, c)
138+
})
139+
.collect();
140+
Stmt::AllDifferent(exprs)
141+
}
142+
143+
fn random_active_vertices_connected_stmt(
144+
&mut self,
145+
bool_vars: &[BoolVar],
146+
int_vars: &[IntVar],
147+
max_complexity: u32,
148+
) -> Stmt {
149+
if bool_vars.is_empty() {
150+
let complexity = self.next_u32(max_complexity);
151+
return Stmt::Expr(self.random_bool_expr(bool_vars, int_vars, complexity));
152+
}
153+
// 2 to 6 vertices
154+
let n = (self.next_u32(5) as usize + 2).min(bool_vars.len()).max(2);
155+
let vertex_exprs: Vec<BoolExpr> = (0..n)
156+
.map(|_| {
157+
let c = self.next_u32(max_complexity / 2 + 1);
158+
self.random_bool_expr(bool_vars, int_vars, c)
159+
})
160+
.collect();
161+
let mut edges = vec![];
162+
for i in 0..n {
163+
for j in (i + 1)..n {
164+
if self.next_u32(3) != 0 {
165+
edges.push((i, j));
166+
}
167+
}
168+
}
169+
Stmt::ActiveVerticesConnected(vertex_exprs, edges)
170+
}
171+
172+
#[cfg(feature = "csp-extra-constraints")]
173+
fn random_extension_supports_stmt(
174+
&mut self,
175+
_bool_vars: &[BoolVar],
176+
int_vars: &[IntVar],
177+
) -> Stmt {
178+
if int_vars.is_empty() {
179+
return Stmt::Expr(BoolExpr::Const(true));
180+
}
181+
// 1 to 3 variables in the scope
182+
let n = (self.next_u32(3) as usize + 1).min(int_vars.len());
183+
let exprs: Vec<IntExpr> = (0..n)
184+
.map(|_| {
185+
let idx = self.next_u32(int_vars.len() as u32) as usize;
186+
int_vars[idx].expr()
187+
})
188+
.collect();
189+
// 1 to 6 support tuples; values in -3..=3 to match int var domains
190+
let n_tuples = self.next_u32(6) as usize + 1;
191+
let mut supports = vec![];
192+
for _ in 0..n_tuples {
193+
let tuple: Vec<Option<i32>> = (0..n)
194+
.map(|_| {
195+
if self.next_u32(5) == 0 {
196+
None // wildcard
197+
} else {
198+
Some(self.next_i32(-3, 4))
199+
}
200+
})
201+
.collect();
202+
supports.push(tuple);
203+
}
204+
Stmt::ExtensionSupports(exprs, supports)
205+
}
206+
100207
fn random_bool_expr(
101208
&mut self,
102209
bool_vars: &[BoolVar],
@@ -173,7 +280,11 @@ impl Fuzzer {
173280
}
174281
}
175282

283+
#[cfg(feature = "csp-extra-constraints")]
284+
let mode = self.next_u32(5);
285+
#[cfg(not(feature = "csp-extra-constraints"))]
176286
let mode = self.next_u32(4);
287+
177288
match mode {
178289
0 => {
179290
let cond_complexity = self.next_u32(complexity);
@@ -210,6 +321,16 @@ impl Fuzzer {
210321

211322
IntExpr::Linear(vec![(t1, scale1), (t2, scale2)])
212323
}
324+
#[cfg(feature = "csp-extra-constraints")]
325+
4 => {
326+
let left_complexity = self.next_u32(complexity);
327+
let right_complexity = complexity - left_complexity - 1;
328+
329+
let lhs = Box::new(self.random_int_expr(bool_vars, int_vars, left_complexity));
330+
let rhs = Box::new(self.random_int_expr(bool_vars, int_vars, right_complexity));
331+
332+
IntExpr::Mul(lhs, rhs)
333+
}
213334
_ => unreachable!(),
214335
}
215336
}

cspuz_core/src/normalizer.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,12 @@ fn normalize_extension_supports(
10541054
}
10551055
}
10561056

1057+
if supports_idx.is_empty() {
1058+
// No support tuple is compatible with the variable domains: constraint is UNSAT
1059+
env.norm.add_constraint(Constraint::new());
1060+
return;
1061+
}
1062+
10571063
for n_prefix in 0..vars.len() {
10581064
let mut left = 0;
10591065
while left < supports_idx.len() {
@@ -1532,6 +1538,23 @@ mod tests {
15321538
}
15331539
}
15341540

1541+
#[cfg(feature = "csp-extra-constraints")]
1542+
#[test]
1543+
fn test_normalization_extension_supports_3() {
1544+
for use_native in [false, true] {
1545+
let mut tester = NormalizerTester::new();
1546+
tester.config.use_native_extension_supports = use_native;
1547+
1548+
let a = tester.new_int_var(Domain::range(0, 3));
1549+
let b = tester.new_int_var(Domain::range(0, 3));
1550+
tester.add_constraint(Stmt::ExtensionSupports(
1551+
vec![a.expr(), b.expr()],
1552+
vec![vec![Some(4), Some(-1)]],
1553+
));
1554+
tester.check();
1555+
}
1556+
}
1557+
15351558
#[cfg(feature = "csp-extra-constraints")]
15361559
#[test]
15371560
fn test_normalization_circuit_1() {

0 commit comments

Comments
 (0)