cqlib_core/circuit/classical_expr/
simplify.rs1use crate::circuit::classical::ClassicalType;
44use crate::circuit::classical_expr::expr::{
45 ClassicalBinaryOp, ClassicalCast, ClassicalCompareOp, ClassicalExpr, ClassicalExprKind,
46 ClassicalUnaryOp,
47};
48
49pub fn simplify(expr: &ClassicalExpr) -> ClassicalExpr {
60 simplify_node(expr)
61}
62
63fn simplify_node(expr: &ClassicalExpr) -> ClassicalExpr {
64 match expr.kind() {
65 ClassicalExprKind::Var(_)
66 | ClassicalExprKind::Value(_)
67 | ClassicalExprKind::BoolLiteral(_)
68 | ClassicalExprKind::BitLiteral(_)
69 | ClassicalExprKind::UIntLiteral { .. }
70 | ClassicalExprKind::BitVecLiteral { .. } => expr.clone(),
71
72 ClassicalExprKind::Unary { op, expr: inner } => {
73 let inner = simplify_node(inner);
74 simplify_unary(*op, &inner).unwrap_or_else(|| {
75 ClassicalExpr::from_kind(
76 inner.ty(),
77 ClassicalExprKind::Unary {
78 op: *op,
79 expr: inner,
80 },
81 )
82 })
83 }
84
85 ClassicalExprKind::Binary { op, lhs, rhs } => {
86 let lhs = simplify_node(lhs);
87 let rhs = simplify_node(rhs);
88 simplify_binary(*op, &lhs, &rhs).unwrap_or_else(|| {
89 let ty = lhs.ty();
90 ClassicalExpr::from_kind(ty, ClassicalExprKind::Binary { op: *op, lhs, rhs })
91 })
92 }
93
94 ClassicalExprKind::Compare { op, lhs, rhs } => {
95 let lhs = simplify_node(lhs);
96 let rhs = simplify_node(rhs);
97 simplify_compare(*op, &lhs, &rhs).unwrap_or_else(|| {
98 ClassicalExpr::from_kind(
99 ClassicalType::Bool,
100 ClassicalExprKind::Compare { op: *op, lhs, rhs },
101 )
102 })
103 }
104
105 ClassicalExprKind::Cast { cast, expr: inner } => {
106 let inner = simplify_node(inner);
107 simplify_cast(*cast, &inner).unwrap_or_else(|| {
108 let ty = match cast {
109 ClassicalCast::BitToBool => ClassicalType::Bool,
110 ClassicalCast::BitVecToUInt => {
111 if let ClassicalType::BitVec(width) = inner.ty() {
112 ClassicalType::UInt(width)
113 } else {
114 ClassicalType::Bool
116 }
117 }
118 };
119 ClassicalExpr::from_kind(
120 ty,
121 ClassicalExprKind::Cast {
122 cast: *cast,
123 expr: inner,
124 },
125 )
126 })
127 }
128
129 ClassicalExprKind::Select {
130 condition,
131 then_expr,
132 else_expr,
133 } => {
134 let condition = simplify_node(condition);
135 if let Some(result) = simplify_select(&condition, then_expr, else_expr) {
137 return result;
138 }
139 let then_expr = simplify_node(then_expr);
140 let else_expr = simplify_node(else_expr);
141 ClassicalExpr::from_kind(
142 then_expr.ty(),
143 ClassicalExprKind::Select {
144 condition,
145 then_expr,
146 else_expr,
147 },
148 )
149 }
150
151 ClassicalExprKind::ExtractBit { value, index } => {
152 let value = simplify_node(value);
153 ClassicalExpr::from_kind(
154 ClassicalType::Bit,
155 ClassicalExprKind::ExtractBit {
156 value,
157 index: *index,
158 },
159 )
160 }
161
162 ClassicalExprKind::ExtractBits {
163 value,
164 offset,
165 width,
166 } => {
167 let value = simplify_node(value);
168 ClassicalExpr::from_kind(
169 ClassicalType::BitVec(*width),
170 ClassicalExprKind::ExtractBits {
171 value,
172 offset: *offset,
173 width: *width,
174 },
175 )
176 }
177
178 ClassicalExprKind::Concat { parts } => {
179 let mut changed = false;
180 let simplified: Vec<ClassicalExpr> = parts
181 .iter()
182 .map(|part| {
183 let s = simplify_node(part);
184 if !changed && &s != part {
185 changed = true;
186 }
187 s
188 })
189 .collect();
190 if !changed {
191 return expr.clone();
192 }
193 let total_width = simplified
194 .iter()
195 .fold(0u32, |acc, part| acc.saturating_add(part.ty().width()));
196 let Some(width) = std::num::NonZeroU32::new(total_width) else {
197 return expr.clone();
199 };
200 ClassicalExpr::from_kind(
201 ClassicalType::BitVec(width),
202 ClassicalExprKind::Concat {
203 parts: simplified.into_boxed_slice(),
204 },
205 )
206 }
207
208 ClassicalExprKind::PackBits { bits } => {
209 let mut changed = false;
210 let simplified: Vec<ClassicalExpr> = bits
211 .iter()
212 .map(|bit| {
213 let s = simplify_node(bit);
214 if !changed && &s != bit {
215 changed = true;
216 }
217 s
218 })
219 .collect();
220 if !changed {
221 return expr.clone();
222 }
223 let Some(width) = std::num::NonZeroU32::new(simplified.len() as u32) else {
224 return expr.clone();
225 };
226 ClassicalExpr::from_kind(
227 ClassicalType::BitVec(width),
228 ClassicalExprKind::PackBits {
229 bits: simplified.into_boxed_slice(),
230 },
231 )
232 }
233 }
234}
235
236fn simplify_unary(op: ClassicalUnaryOp, child: &ClassicalExpr) -> Option<ClassicalExpr> {
238 if op != ClassicalUnaryOp::Not {
239 return None;
240 }
241 match child.kind() {
242 ClassicalExprKind::Unary {
243 op: ClassicalUnaryOp::Not,
244 expr: grandchild,
245 } => Some(grandchild.clone()),
246 _ => None,
247 }
248}
249
250fn simplify_binary(
252 op: ClassicalBinaryOp,
253 lhs: &ClassicalExpr,
254 rhs: &ClassicalExpr,
255) -> Option<ClassicalExpr> {
256 let ty = lhs.ty();
257
258 match op {
259 ClassicalBinaryOp::And => {
260 if rhs.is_bool_true() || rhs.is_bit_true() {
261 return Some(lhs.clone());
262 }
263 if lhs.is_bool_true() || lhs.is_bit_true() {
264 return Some(rhs.clone());
265 }
266 }
267 ClassicalBinaryOp::Or => {
268 if rhs.is_bool_false() || rhs.is_bit_false() {
269 return Some(lhs.clone());
270 }
271 if lhs.is_bool_false() || lhs.is_bit_false() {
272 return Some(rhs.clone());
273 }
274 }
275 ClassicalBinaryOp::Xor => {
276 if rhs.is_bool_false() || rhs.is_bit_false() {
277 return Some(lhs.clone());
278 }
279 if lhs.is_bool_false() || lhs.is_bit_false() {
280 return Some(rhs.clone());
281 }
282 }
283 }
284
285 if lhs == rhs {
286 match op {
287 ClassicalBinaryOp::And | ClassicalBinaryOp::Or => {
288 return Some(lhs.clone());
289 }
290 ClassicalBinaryOp::Xor => {
291 return ty.zero_literal().ok();
292 }
293 }
294 }
295
296 for (a, b) in [(lhs, rhs), (rhs, lhs)] {
299 if let ClassicalExprKind::Unary {
300 op: ClassicalUnaryOp::Not,
301 expr: x,
302 } = b.kind()
303 && a == x
304 {
305 return match op {
306 ClassicalBinaryOp::And => ty.zero_literal(),
307 ClassicalBinaryOp::Or | ClassicalBinaryOp::Xor => ty.one_literal(),
308 }
309 .ok();
310 }
311 }
312
313 None
314}
315
316fn simplify_compare(
318 op: ClassicalCompareOp,
319 lhs: &ClassicalExpr,
320 rhs: &ClassicalExpr,
321) -> Option<ClassicalExpr> {
322 if lhs != rhs {
323 return None;
324 }
325 match op {
326 ClassicalCompareOp::Eq | ClassicalCompareOp::Le | ClassicalCompareOp::Ge => {
327 Some(ClassicalExpr::bool_literal(true))
328 }
329 ClassicalCompareOp::Ne | ClassicalCompareOp::Lt | ClassicalCompareOp::Gt => {
330 Some(ClassicalExpr::bool_literal(false))
331 }
332 }
333}
334
335fn simplify_select(
337 condition: &ClassicalExpr,
338 then_expr: &ClassicalExpr,
339 else_expr: &ClassicalExpr,
340) -> Option<ClassicalExpr> {
341 match condition.kind() {
342 ClassicalExprKind::BoolLiteral(true) => Some(simplify_node(then_expr)),
343 ClassicalExprKind::BoolLiteral(false) => Some(simplify_node(else_expr)),
344 _ => None,
345 }
346}
347
348fn simplify_cast(cast: ClassicalCast, child: &ClassicalExpr) -> Option<ClassicalExpr> {
350 match (cast, child.kind()) {
351 (ClassicalCast::BitToBool, ClassicalExprKind::BitLiteral(v)) => {
352 Some(ClassicalExpr::bool_literal(*v))
353 }
354 (ClassicalCast::BitVecToUInt, ClassicalExprKind::BitVecLiteral { width, value }) => Some(
355 ClassicalExpr::uint_literal(width.get(), *value)
356 .expect("bit-vec literal value must be valid for uint of same width"),
357 ),
358 _ => None,
359 }
360}
361
362#[cfg(test)]
363#[path = "simplify_test.rs"]
364mod simplify_test;