1use crate::circuit::{
68 Circuit, CircuitId, CircuitParam, ClassicalControlOp, ClassicalDataOp, ClassicalExpr,
69 ClassicalType, ClassicalValue, ClassicalVar, Instruction, Operation, Parameter, ParameterValue,
70 Qubit, ValueClassicalControlOp, ValueControlBody, ValueInstruction, ValueOperation,
71 ValueSwitchCase,
72};
73use crate::compile::CompilerError;
74use smallvec::SmallVec;
75use std::collections::HashMap;
76
77#[derive(Debug, Clone)]
91pub struct ClassicalRemap {
92 vars: HashMap<ClassicalVar, ClassicalVar>,
93 values: HashMap<ClassicalValue, ClassicalValue>,
94}
95
96impl ClassicalRemap {
97 pub fn map_var(&self, var: ClassicalVar) -> Option<ClassicalVar> {
99 self.vars.get(&var).copied()
100 }
101
102 pub fn map_value(&self, value: ClassicalValue) -> Option<ClassicalValue> {
104 self.values.get(&value).copied()
105 }
106
107 pub fn remap_var(&self, var: ClassicalVar) -> Result<ClassicalVar, CompilerError> {
109 self.map_var(var).ok_or_else(|| {
110 CompilerError::InvariantViolation(format!(
111 "missing classical variable remap for id {}",
112 var.id()
113 ))
114 })
115 }
116
117 pub fn remap_value(&self, value: ClassicalValue) -> Result<ClassicalValue, CompilerError> {
119 self.map_value(value).ok_or_else(|| {
120 CompilerError::InvariantViolation(format!(
121 "missing classical value remap for id {}",
122 value.index()
123 ))
124 })
125 }
126
127 pub fn remap_expr(&self, expr: &ClassicalExpr) -> Result<ClassicalExpr, CompilerError> {
132 expr.remap_classical_ids(&self.vars, &self.values)
133 .map_err(CompilerError::Circuit)
134 }
135
136 pub fn remap_data_op(&self, op: &ClassicalDataOp) -> Result<ClassicalDataOp, CompilerError> {
142 Ok(match op {
143 ClassicalDataOp::Store { target, value } => ClassicalDataOp::Store {
144 target: self.remap_var(*target)?,
145 value: self.remap_expr(value)?,
146 },
147 ClassicalDataOp::MeasureBit { result } => ClassicalDataOp::MeasureBit {
148 result: self.remap_value(*result)?,
149 },
150 ClassicalDataOp::MeasureBits { result } => ClassicalDataOp::MeasureBits {
151 result: self.remap_value(*result)?,
152 },
153 })
154 }
155}
156
157#[derive(Debug, Clone)]
184pub struct CircuitRebuildContext {
185 target_circuit_id: CircuitId,
186 classical_vars: Vec<ClassicalType>,
187 classical_values: Vec<ClassicalType>,
188 root_classical: ClassicalRemap,
189}
190
191impl CircuitRebuildContext {
192 pub fn new(source: &Circuit) -> Self {
199 let target_circuit_id = CircuitId::new();
200 let classical_vars = source.classical_vars().to_vec();
201 let classical_values = source.classical_values().to_vec();
202
203 let vars = classical_vars
204 .iter()
205 .copied()
206 .enumerate()
207 .map(|(index, ty)| {
208 (
209 ClassicalVar::new(source.id(), index as u32, ty),
210 ClassicalVar::new(target_circuit_id, index as u32, ty),
211 )
212 })
213 .collect();
214 let values = classical_values
215 .iter()
216 .copied()
217 .enumerate()
218 .map(|(index, ty)| {
219 (
220 ClassicalValue::new(source.id(), index as u32, ty),
221 ClassicalValue::new(target_circuit_id, index as u32, ty),
222 )
223 })
224 .collect();
225
226 Self {
227 target_circuit_id,
228 classical_vars,
229 classical_values,
230 root_classical: ClassicalRemap { vars, values },
231 }
232 }
233
234 pub fn root_classical(&self) -> &ClassicalRemap {
242 &self.root_classical
243 }
244
245 pub fn allocate_classical_instance(&mut self, circuit: &Circuit) -> ClassicalRemap {
254 let var_base = self.classical_vars.len();
255 let mut vars = HashMap::with_capacity(circuit.classical_vars().len());
256 for (index, ty) in circuit.classical_vars().iter().copied().enumerate() {
257 self.classical_vars.push(ty);
258 vars.insert(
259 ClassicalVar::new(circuit.id(), index as u32, ty),
260 ClassicalVar::new(self.target_circuit_id, (var_base + index) as u32, ty),
261 );
262 }
263
264 let value_base = self.classical_values.len();
265 let mut values = HashMap::with_capacity(circuit.classical_values().len());
266 for (index, ty) in circuit.classical_values().iter().copied().enumerate() {
267 self.classical_values.push(ty);
268 values.insert(
269 ClassicalValue::new(circuit.id(), index as u32, ty),
270 ClassicalValue::new(self.target_circuit_id, (value_base + index) as u32, ty),
271 );
272 }
273
274 ClassicalRemap { vars, values }
275 }
276
277 pub fn finish(
286 self,
287 qubits: Vec<Qubit>,
288 operations: Vec<ValueOperation>,
289 global_phase: Parameter,
290 ) -> Result<Circuit, CompilerError> {
291 let mut circuit = Circuit::from_operations(
292 qubits,
293 operations,
294 Some(self.classical_vars),
295 Some(self.classical_values),
296 )?;
297 circuit.set_global_phase(global_phase);
298 Ok(circuit)
299 }
300
301 pub fn resolve_source_param(
307 source: &Circuit,
308 param: &CircuitParam,
309 ) -> Result<ParameterValue, CompilerError> {
310 Ok(ParameterValue::from(source.resolve_parameter(param)?))
311 }
312
313 pub fn resolve_source_params(
316 source: &Circuit,
317 params: &[CircuitParam],
318 ) -> Result<SmallVec<[ParameterValue; 1]>, CompilerError> {
319 params
320 .iter()
321 .map(|param| Self::resolve_source_param(source, param))
322 .collect()
323 }
324
325 pub fn remap_preserved_operation(
334 &self,
335 source: &Circuit,
336 operation: &Operation,
337 classical_remap: &ClassicalRemap,
338 ) -> Result<ValueOperation, CompilerError> {
339 Ok(ValueOperation {
340 instruction: self.remap_instruction(source, &operation.instruction, classical_remap)?,
341 qubits: operation.qubits.clone(),
342 params: Self::resolve_source_params(source, &operation.params)?,
343 label: operation.label.clone(),
344 })
345 }
346
347 pub fn remap_instruction(
359 &self,
360 source: &Circuit,
361 instruction: &Instruction,
362 classical_remap: &ClassicalRemap,
363 ) -> Result<ValueInstruction, CompilerError> {
364 match instruction {
365 Instruction::ClassicalControl(control) => Ok(ValueInstruction::ClassicalControl(
366 self.remap_control_flow(source, control, classical_remap)?,
367 )),
368 Instruction::ClassicalData(op) => Ok(ValueInstruction::from_instruction(
369 Instruction::ClassicalData(classical_remap.remap_data_op(op)?),
370 )),
371 _ => Ok(ValueInstruction::from_instruction(instruction.clone())),
372 }
373 }
374
375 pub fn remap_non_control_instruction(
383 &self,
384 instruction: &Instruction,
385 classical_remap: &ClassicalRemap,
386 ) -> Result<ValueInstruction, CompilerError> {
387 match instruction {
388 Instruction::ClassicalControl(_) => Err(CompilerError::InvariantViolation(
389 "classical control must be rebuilt by the transform".to_string(),
390 )),
391 Instruction::ClassicalData(op) => Ok(ValueInstruction::from_instruction(
392 Instruction::ClassicalData(classical_remap.remap_data_op(op)?),
393 )),
394 _ => Ok(ValueInstruction::from_instruction(instruction.clone())),
395 }
396 }
397
398 pub fn remap_control_flow(
412 &self,
413 source: &Circuit,
414 control: &ClassicalControlOp,
415 classical_remap: &ClassicalRemap,
416 ) -> Result<ValueClassicalControlOp, CompilerError> {
417 Ok(match control {
418 ClassicalControlOp::If(op) => ValueClassicalControlOp::If {
419 condition: classical_remap.remap_expr(op.condition())?,
420 then_body: ValueControlBody::new(self.remap_preserved_sequence(
421 source,
422 op.then_body().operations(),
423 classical_remap,
424 )?),
425 else_body: op
426 .else_body()
427 .map(|body| {
428 self.remap_preserved_sequence(source, body.operations(), classical_remap)
429 .map(ValueControlBody::new)
430 })
431 .transpose()?,
432 },
433 ClassicalControlOp::While(op) => ValueClassicalControlOp::While {
434 condition: classical_remap.remap_expr(op.condition())?,
435 body: ValueControlBody::new(self.remap_preserved_sequence(
436 source,
437 op.body().operations(),
438 classical_remap,
439 )?),
440 },
441 ClassicalControlOp::For(op) => ValueClassicalControlOp::For {
442 var: classical_remap.remap_var(op.var())?,
443 start: classical_remap.remap_expr(op.start())?,
444 stop: classical_remap.remap_expr(op.stop())?,
445 step: classical_remap.remap_expr(op.step())?,
446 body: ValueControlBody::new(self.remap_preserved_sequence(
447 source,
448 op.body().operations(),
449 classical_remap,
450 )?),
451 },
452 ClassicalControlOp::Switch(op) => ValueClassicalControlOp::Switch {
453 target: classical_remap.remap_expr(op.target())?,
454 cases: op
455 .cases()
456 .iter()
457 .map(|case| {
458 Ok(ValueSwitchCase::new(
459 case.value(),
460 ValueControlBody::new(self.remap_preserved_sequence(
461 source,
462 case.body().operations(),
463 classical_remap,
464 )?),
465 ))
466 })
467 .collect::<Result<Vec<_>, CompilerError>>()?,
468 default: op
469 .default()
470 .map(|body| {
471 self.remap_preserved_sequence(source, body.operations(), classical_remap)
472 .map(ValueControlBody::new)
473 })
474 .transpose()?,
475 },
476 ClassicalControlOp::Break => ValueClassicalControlOp::Break,
477 ClassicalControlOp::Continue => ValueClassicalControlOp::Continue,
478 })
479 }
480
481 pub fn remap_preserved_sequence(
488 &self,
489 source: &Circuit,
490 operations: &[Operation],
491 classical_remap: &ClassicalRemap,
492 ) -> Result<Vec<ValueOperation>, CompilerError> {
493 operations
494 .iter()
495 .map(|operation| self.remap_preserved_operation(source, operation, classical_remap))
496 .collect()
497 }
498}
499
500#[cfg(test)]
501mod tests {
502 use super::*;
503 use crate::circuit::StandardGate;
504
505 #[test]
506 fn source_resolution_folds_constants_but_parameter_value_preserves_representation() {
507 use crate::circuit::{CircuitError, Parameter};
508
509 let mut source = Circuit::new(1);
510 let (index, _) = source.add_parameter(Parameter::pi());
511 let param = CircuitParam::Index(index as u32);
512 assert!(matches!(source.parameter_value(¶m).unwrap(),
513 ParameterValue::Param(value) if value == Parameter::pi()));
514 assert!(
515 matches!(CircuitRebuildContext::resolve_source_param(&source, ¶m).unwrap(),
516 ParameterValue::Fixed(value) if value == std::f64::consts::PI)
517 );
518 let (index, _) = source.add_parameter(Parameter::symbol("theta"));
519 assert!(
520 matches!(CircuitRebuildContext::resolve_source_param(&source, &CircuitParam::Index(index as u32)).unwrap(),
521 ParameterValue::Param(value) if value == Parameter::symbol("theta"))
522 );
523 for value in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
524 let param = CircuitParam::Fixed(value);
525 assert!(
526 matches!(source.parameter_value(¶m).unwrap(), ParameterValue::Fixed(stored) if stored.to_bits() == value.to_bits())
527 );
528 assert!(matches!(
529 CircuitRebuildContext::resolve_source_param(&source, ¶m),
530 Err(CompilerError::Circuit(CircuitError::InvalidParameterValue(
531 0,
532 _
533 )))
534 ));
535 }
536 }
537
538 #[test]
539 fn preserved_rebuild_remaps_runtime_classical_handles_recursively() {
540 let mut source = Circuit::new(3);
541 let flag = source.var(ClassicalType::Bool);
542 let counter = source.var(ClassicalType::uint(2).unwrap());
543 let selector = source.var(ClassicalType::uint(2).unwrap());
544 let bits_out = source.var(ClassicalType::bit_vec(2).unwrap());
545 let measured = source.measure(Qubit::new(0)).unwrap();
546 let measured_bits = source.measure_bits([Qubit::new(1), Qubit::new(2)]).unwrap();
547 source
548 .store(flag, ClassicalExpr::bit_to_bool(measured.expr()).unwrap())
549 .unwrap();
550 source
551 .if_(flag.expr(), |body| body.x(Qubit::new(0)))
552 .unwrap();
553 source
554 .for_uint(
555 counter,
556 selector.expr(),
557 ClassicalExpr::uint_literal(2, 3).unwrap(),
558 ClassicalExpr::uint_literal(2, 1).unwrap(),
559 |body, loop_value| {
560 body.store(bits_out, measured_bits.expr())?;
561 body.switch(loop_value, |cases| {
562 cases.value(1, |case_body| case_body.h(Qubit::new(1)))?;
563 cases.default(|case_body| case_body.z(Qubit::new(2)))?;
564 Ok(())
565 })
566 },
567 )
568 .unwrap();
569
570 let rebuild = CircuitRebuildContext::new(&source);
571 assert!(rebuild.root_classical().map_var(flag).is_some());
572 assert!(
573 rebuild
574 .root_classical()
575 .map_value(measured.value())
576 .is_some()
577 );
578 let operations = source
579 .operations()
580 .iter()
581 .map(|operation| {
582 rebuild.remap_preserved_operation(&source, operation, rebuild.root_classical())
583 })
584 .collect::<Result<Vec<_>, _>>()
585 .unwrap();
586 let rebuilt = rebuild
587 .finish(source.qubits(), operations, source.global_phase())
588 .unwrap();
589
590 assert_ne!(source.id(), rebuilt.id());
591 assert_eq!(rebuilt.classical_vars(), source.classical_vars());
592 assert_eq!(rebuilt.classical_values(), source.classical_values());
593 rebuilt.validate().unwrap();
594 for operation in rebuilt.operations() {
595 assert_operation_handles_belong_to(operation, rebuilt.id());
596 }
597 }
598
599 #[test]
600 fn allocate_classical_instance_produces_distinct_handles_for_same_source() {
601 let mut definition = Circuit::new(1);
602 let flag = definition.var(ClassicalType::Bool);
603 definition.measure(Qubit::new(0)).unwrap();
604 definition
605 .if_(flag.expr(), |body| body.x(Qubit::new(0)))
606 .unwrap();
607
608 let source = definition.clone(); let mut rebuild = CircuitRebuildContext::new(&source);
610
611 let first = rebuild.allocate_classical_instance(&definition);
612 let second = rebuild.allocate_classical_instance(&definition);
613
614 let source_var = ClassicalVar::new(definition.id(), 0, ClassicalType::Bool);
616 let first_mapped = first.map_var(source_var).unwrap();
617 let second_mapped = second.map_var(source_var).unwrap();
618 assert_ne!(
619 first_mapped, second_mapped,
620 "two allocations of the same definition must produce independent handles"
621 );
622
623 let source_value = ClassicalValue::new(definition.id(), 0, ClassicalType::Bit);
625 let first_val = first.map_value(source_value).unwrap();
626 let second_val = second.map_value(source_value).unwrap();
627 assert_ne!(first_val, second_val);
628 }
629
630 #[test]
631 fn remap_data_op_is_callable_on_standalone_remap() {
632 let mut source = Circuit::new(1);
633 let old_var = source.var(ClassicalType::Bool);
634 let measured = source.measure(Qubit::new(0)).unwrap();
635 let old_value = measured.value();
636 let rebuild = CircuitRebuildContext::new(&source);
637 let remap = rebuild.root_classical();
638 let new_var = remap.map_var(old_var).unwrap();
639 let new_value = remap.map_value(old_value).unwrap();
640
641 let op = ClassicalDataOp::MeasureBit { result: old_value };
642 let remapped = remap.remap_data_op(&op).unwrap();
643 let ClassicalDataOp::MeasureBit { result } = remapped else {
644 panic!("expected MeasureBit");
645 };
646 assert_eq!(result, new_value);
647
648 let op = ClassicalDataOp::Store {
649 target: old_var,
650 value: old_value.expr(),
651 };
652 let remapped = remap.remap_data_op(&op).unwrap();
653 match remapped {
654 ClassicalDataOp::Store { target, value } => {
655 assert_eq!(target, new_var);
656 assert_eq!(value, new_value.expr());
657 }
658 _ => panic!("expected Store"),
659 }
660 }
661
662 fn assert_operation_handles_belong_to(operation: &Operation, circuit_id: CircuitId) {
663 match &operation.instruction {
664 Instruction::ClassicalData(op) => {
665 assert_classical_data_handles_belong_to(op, circuit_id)
666 }
667 Instruction::ClassicalControl(op) => assert_control_handles_belong_to(op, circuit_id),
668 Instruction::Standard(StandardGate::X | StandardGate::H | StandardGate::Z) => {}
669 _ => {}
670 }
671 }
672
673 fn assert_classical_data_handles_belong_to(op: &ClassicalDataOp, circuit_id: CircuitId) {
674 match op {
675 ClassicalDataOp::Store { target, value } => {
676 assert_eq!(target.circuit_id(), circuit_id);
677 assert_expr_handles_belong_to(value, circuit_id);
678 }
679 ClassicalDataOp::MeasureBit { result } | ClassicalDataOp::MeasureBits { result } => {
680 assert_eq!(result.circuit_id(), circuit_id);
681 }
682 }
683 }
684
685 fn assert_control_handles_belong_to(op: &ClassicalControlOp, circuit_id: CircuitId) {
686 match op {
687 ClassicalControlOp::If(op) => {
688 assert_expr_handles_belong_to(op.condition(), circuit_id);
689 assert_body_handles_belong_to(op.then_body().operations(), circuit_id);
690 if let Some(body) = op.else_body() {
691 assert_body_handles_belong_to(body.operations(), circuit_id);
692 }
693 }
694 ClassicalControlOp::While(op) => {
695 assert_expr_handles_belong_to(op.condition(), circuit_id);
696 assert_body_handles_belong_to(op.body().operations(), circuit_id);
697 }
698 ClassicalControlOp::For(op) => {
699 assert_eq!(op.var().circuit_id(), circuit_id);
700 assert_expr_handles_belong_to(op.start(), circuit_id);
701 assert_expr_handles_belong_to(op.stop(), circuit_id);
702 assert_expr_handles_belong_to(op.step(), circuit_id);
703 assert_body_handles_belong_to(op.body().operations(), circuit_id);
704 }
705 ClassicalControlOp::Switch(op) => {
706 assert_expr_handles_belong_to(op.target(), circuit_id);
707 for case in op.cases() {
708 assert_body_handles_belong_to(case.body().operations(), circuit_id);
709 }
710 if let Some(body) = op.default() {
711 assert_body_handles_belong_to(body.operations(), circuit_id);
712 }
713 }
714 ClassicalControlOp::Break | ClassicalControlOp::Continue => {}
715 }
716 }
717
718 fn assert_body_handles_belong_to(operations: &[Operation], circuit_id: CircuitId) {
719 for operation in operations {
720 assert_operation_handles_belong_to(operation, circuit_id);
721 }
722 }
723
724 fn assert_expr_handles_belong_to(expr: &ClassicalExpr, circuit_id: CircuitId) {
725 for var in expr.vars() {
726 assert_eq!(var.circuit_id(), circuit_id);
727 }
728 for value in expr.values() {
729 assert_eq!(value.circuit_id(), circuit_id);
730 }
731 }
732}