Skip to content

Commit 4683e58

Browse files
authored
Split assignments (#2463)
Split the `Assignment` struct into two structs and eliminate the `VariableOrValue` enum. This simplifies some interfaces or at least makes them more orthogonal. The QueueItem is turned into a public data structure in turn.
1 parent fd973e9 commit 4683e58

7 files changed

+225
-256
lines changed

executor/src/witgen/jit/affine_symbolic_expression.rs

+10
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,16 @@ pub struct AffineSymbolicExpression<T: FieldElement, V> {
5656
range_constraints: BTreeMap<V, RangeConstraint<T>>,
5757
}
5858

59+
impl<T: FieldElement, V> Default for AffineSymbolicExpression<T, V> {
60+
fn default() -> Self {
61+
Self {
62+
coefficients: Default::default(),
63+
offset: T::zero().into(),
64+
range_constraints: Default::default(),
65+
}
66+
}
67+
}
68+
5969
/// Display for affine symbolic expressions, for informational purposes only.
6070
impl<T: FieldElement, V: Display> Display for AffineSymbolicExpression<T, V> {
6171
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {

executor/src/witgen/jit/block_machine_processor.rs

+20-20
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use powdr_number::FieldElement;
77

88
use crate::witgen::{
99
jit::{
10-
processor::Processor, prover_function_heuristics::decode_prover_functions,
11-
witgen_inference::Assignment,
10+
identity_queue::QueueItem, processor::Processor,
11+
prover_function_heuristics::decode_prover_functions,
1212
},
1313
machines::MachineParts,
1414
FixedData,
@@ -69,42 +69,43 @@ impl<'a, T: FieldElement> BlockMachineProcessor<'a, T> {
6969

7070
let prover_functions = decode_prover_functions(&self.machine_parts, self.fixed_data)?;
7171

72+
let mut queue_items = vec![];
73+
7274
// In the latch row, set the RHS selector to 1.
73-
let mut assignments = vec![];
7475
let selector = &connection.right.selector;
75-
assignments.push(Assignment::assign_constant(
76+
queue_items.push(QueueItem::constant_assignment(
7677
selector,
77-
self.latch_row as i32,
7878
T::one(),
79+
self.latch_row as i32,
7980
));
8081

8182
if let Some((index, value)) = known_concrete {
8283
// Set the known argument to the concrete value.
83-
assignments.push(Assignment::assign_constant(
84+
queue_items.push(QueueItem::constant_assignment(
8485
&connection.right.expressions[index],
85-
self.latch_row as i32,
8686
value,
87+
self.latch_row as i32,
8788
));
8889
}
8990

9091
// Set all other selectors to 0 in the latch row.
9192
for other_connection in self.machine_parts.connections.values() {
9293
let other_selector = &other_connection.right.selector;
9394
if other_selector != selector {
94-
assignments.push(Assignment::assign_constant(
95+
queue_items.push(QueueItem::constant_assignment(
9596
other_selector,
96-
self.latch_row as i32,
9797
T::zero(),
98+
self.latch_row as i32,
9899
));
99100
}
100101
}
101102

102103
// For each argument, connect the expression on the RHS with the formal parameter.
103104
for (index, expr) in connection.right.expressions.iter().enumerate() {
104-
assignments.push(Assignment::assign_variable(
105+
queue_items.push(QueueItem::variable_assignment(
105106
expr,
106-
self.latch_row as i32,
107107
Variable::Param(index),
108+
self.latch_row as i32,
108109
));
109110
}
110111

@@ -142,6 +143,11 @@ impl<'a, T: FieldElement> BlockMachineProcessor<'a, T> {
142143
.collect_vec()
143144
});
144145

146+
// Add the prover functions
147+
queue_items.extend(prover_functions.iter().flat_map(|f| {
148+
(0..self.block_size).map(move |row| QueueItem::ProverFunction(f.clone(), row as i32))
149+
}));
150+
145151
let requested_known = known_args
146152
.iter()
147153
.enumerate()
@@ -150,19 +156,13 @@ impl<'a, T: FieldElement> BlockMachineProcessor<'a, T> {
150156
self.fixed_data,
151157
self,
152158
identities,
153-
assignments,
159+
queue_items,
154160
requested_known,
155161
BLOCK_MACHINE_MAX_BRANCH_DEPTH,
156162
)
157163
.with_block_shape_check()
158164
.with_block_size(self.block_size)
159165
.with_requested_range_constraints((0..known_args.len()).map(Variable::Param))
160-
.with_prover_functions(
161-
prover_functions
162-
.iter()
163-
.flat_map(|f| (0..self.block_size).map(move |row| (f.clone(), row as i32)))
164-
.collect_vec()
165-
)
166166
.generate_code(can_process, witgen)
167167
.map_err(|e| {
168168
let err_str = e.to_string_with_variable_formatter(|var| match var {
@@ -340,10 +340,10 @@ params[2] = Add::c[0];"
340340
assert_eq!(c_rc, &RangeConstraint::from_mask(0xffffffffu64));
341341
assert_eq!(
342342
format_code(&result.code),
343-
"main_binary::operation_id[3] = params[0];
343+
"main_binary::sel[0][3] = 1;
344+
main_binary::operation_id[3] = params[0];
344345
main_binary::A[3] = params[1];
345346
main_binary::B[3] = params[2];
346-
main_binary::sel[0][3] = 1;
347347
main_binary::operation_id[2] = main_binary::operation_id[3];
348348
main_binary::operation_id[1] = main_binary::operation_id[2];
349349
main_binary::operation_id[0] = main_binary::operation_id[1];

executor/src/witgen/jit/debug_formatter.rs

+32-7
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,39 @@ impl<T: FieldElement, FixedEval: FixedEvaluator<T>> DebugFormatter<'_, T, FixedE
3232
fn format_identities(&self) -> String {
3333
self.identities
3434
.iter()
35-
.filter(|(id, row)| !self.witgen.is_complete(id, *row))
3635
.sorted_by_key(|(id, row)| (row, id.id()))
37-
.map(|(id, row)| {
38-
format!(
39-
"--------------[ identity {} on row {row}: ]--------------\n{}",
40-
id.id(),
41-
self.format_identity(id, *row)
42-
)
36+
.flat_map(|(id, row)| {
37+
let (skip, conflicting) = match &id {
38+
Identity::BusSend(..) => (self.witgen.is_complete_call(id, *row), false),
39+
Identity::Polynomial(PolynomialIdentity { expression, .. }) => {
40+
let value = self
41+
.witgen
42+
.evaluate(expression, *row)
43+
.and_then(|v| v.try_to_known().cloned());
44+
let conflict = value
45+
.as_ref()
46+
.and_then(|v| v.try_to_number().map(|n| n != 0.into()))
47+
.unwrap_or(false);
48+
// We can skip the identity if it does not have unknown variables
49+
// but only if there is no conflict.
50+
(value.is_some() && !conflict, conflict)
51+
}
52+
Identity::Connect(..) => (false, false),
53+
};
54+
if skip {
55+
None
56+
} else {
57+
Some(format!(
58+
"{}--------------[ identity {} on row {row}: ]--------------\n{}",
59+
if conflicting {
60+
"--------------[ !!! CONFLICT !!! ]--------------\n"
61+
} else {
62+
""
63+
},
64+
id.id(),
65+
self.format_identity(id, *row)
66+
))
67+
}
4368
})
4469
.join("\n")
4570
}

executor/src/witgen/jit/identity_queue.rs

+61-36
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,7 @@ use crate::witgen::{
1717
data_structures::identity::Identity, jit::variable::MachineCallVariable, FixedData,
1818
};
1919

20-
use super::{
21-
prover_function_heuristics::ProverFunction,
22-
variable::Variable,
23-
witgen_inference::{Assignment, VariableOrValue},
24-
};
20+
use super::{prover_function_heuristics::ProverFunction, variable::Variable};
2521

2622
/// Keeps track of identities that still need to be processed and
2723
/// updates this list based on the occurrence of updated variables
@@ -35,20 +31,9 @@ pub struct IdentityQueue<'a, T: FieldElement> {
3531
impl<'a, T: FieldElement> IdentityQueue<'a, T> {
3632
pub fn new(
3733
fixed_data: &'a FixedData<'a, T>,
38-
identities: &[(&'a Identity<T>, i32)],
39-
assignments: &[Assignment<'a, T>],
40-
prover_functions: &[(ProverFunction<'a, T>, i32)],
34+
items: impl IntoIterator<Item = QueueItem<'a, T>>,
4135
) -> Self {
42-
let queue: BTreeSet<_> = identities
43-
.iter()
44-
.map(|(id, row)| QueueItem::Identity(id, *row))
45-
.chain(assignments.iter().map(|a| QueueItem::Assignment(a.clone())))
46-
.chain(
47-
prover_functions
48-
.iter()
49-
.map(|(p, row)| QueueItem::ProverFunction(p.clone(), *row)),
50-
)
51-
.collect();
36+
let queue: BTreeSet<_> = items.into_iter().collect();
5237
let mut references = ReferencesComputer::new(fixed_data);
5338
let occurrences = Rc::new(
5439
queue
@@ -93,25 +78,50 @@ impl<'a, T: FieldElement> IdentityQueue<'a, T> {
9378
#[derive(Clone)]
9479
pub enum QueueItem<'a, T: FieldElement> {
9580
Identity(&'a Identity<T>, i32),
96-
Assignment(Assignment<'a, T>),
81+
VariableAssignment(VariableAssignment<'a, T>),
82+
ConstantAssignment(ConstantAssignment<'a, T>),
9783
ProverFunction(ProverFunction<'a, T>, i32),
9884
}
9985

100-
/// Sorts identities by row and then by ID, preceded by assignments.
10186
impl<T: FieldElement> Ord for QueueItem<'_, T> {
10287
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
10388
match (self, other) {
10489
(QueueItem::Identity(id1, row1), QueueItem::Identity(id2, row2)) => {
10590
(row1, id1.id()).cmp(&(row2, id2.id()))
10691
}
107-
(QueueItem::Assignment(a1), QueueItem::Assignment(a2)) => a1.cmp(a2),
92+
(QueueItem::VariableAssignment(a1), QueueItem::VariableAssignment(a2)) => a1.cmp(a2),
93+
(QueueItem::ConstantAssignment(a1), QueueItem::ConstantAssignment(a2)) => a1.cmp(a2),
10894
(QueueItem::ProverFunction(p1, row1), QueueItem::ProverFunction(p2, row2)) => {
10995
(row1, p1.index).cmp(&(row2, p2.index))
11096
}
111-
(QueueItem::Assignment(..), _) => std::cmp::Ordering::Less,
112-
(QueueItem::Identity(..), QueueItem::Assignment(..)) => std::cmp::Ordering::Greater,
113-
(QueueItem::Identity(..), QueueItem::ProverFunction(..)) => std::cmp::Ordering::Less,
114-
(QueueItem::ProverFunction(..), _) => std::cmp::Ordering::Greater,
97+
(a, b) => a.order().cmp(&b.order()),
98+
}
99+
}
100+
}
101+
102+
impl<'a, T: FieldElement> QueueItem<'a, T> {
103+
pub fn constant_assignment(lhs: &'a Expression<T>, rhs: T, row_offset: i32) -> Self {
104+
QueueItem::ConstantAssignment(ConstantAssignment {
105+
lhs,
106+
row_offset,
107+
rhs,
108+
})
109+
}
110+
111+
pub fn variable_assignment(lhs: &'a Expression<T>, rhs: Variable, row_offset: i32) -> Self {
112+
QueueItem::VariableAssignment(VariableAssignment {
113+
lhs,
114+
row_offset,
115+
rhs,
116+
})
117+
}
118+
119+
fn order(&self) -> u32 {
120+
match self {
121+
QueueItem::ConstantAssignment(..) => 0,
122+
QueueItem::VariableAssignment(..) => 1,
123+
QueueItem::Identity(..) => 2,
124+
QueueItem::ProverFunction(..) => 3,
115125
}
116126
}
117127
}
@@ -130,6 +140,24 @@ impl<T: FieldElement> PartialEq for QueueItem<'_, T> {
130140

131141
impl<T: FieldElement> Eq for QueueItem<'_, T> {}
132142

143+
/// An equality constraint between an algebraic expression evaluated
144+
/// on a certain row offset and a variable.
145+
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
146+
pub struct VariableAssignment<'a, T: FieldElement> {
147+
pub lhs: &'a Expression<T>,
148+
pub row_offset: i32,
149+
pub rhs: Variable,
150+
}
151+
152+
/// An equality constraint between an algebraic expression evaluated
153+
/// on a certain row offset and a constant.
154+
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
155+
pub struct ConstantAssignment<'a, T: FieldElement> {
156+
pub lhs: &'a Expression<T>,
157+
pub row_offset: i32,
158+
pub rhs: T,
159+
}
160+
133161
/// Utility to compute the variables that occur in a queue item.
134162
/// Follows intermediate column references and employs caches.
135163
struct ReferencesComputer<'a, T: FieldElement> {
@@ -171,17 +199,14 @@ impl<'a, T: FieldElement> ReferencesComputer<'a, T> {
171199
),
172200
Identity::Connect(..) => Box::new(std::iter::empty()),
173201
},
174-
QueueItem::Assignment(a) => {
175-
let vars_in_rhs = match &a.rhs {
176-
VariableOrValue::Variable(v) => Some(v.clone()),
177-
VariableOrValue::Value(_) => None,
178-
};
179-
Box::new(
180-
self.variables_in_expression(a.lhs, a.row_offset)
181-
.into_iter()
182-
.chain(vars_in_rhs),
183-
)
184-
}
202+
QueueItem::ConstantAssignment(a) => Box::new(
203+
self.variables_in_expression(a.lhs, a.row_offset)
204+
.into_iter(),
205+
),
206+
QueueItem::VariableAssignment(a) => Box::new(
207+
std::iter::once(a.rhs.clone())
208+
.chain(self.variables_in_expression(a.lhs, a.row_offset)),
209+
),
185210
QueueItem::ProverFunction(p, row) => Box::new(
186211
p.condition
187212
.iter()

0 commit comments

Comments
 (0)