1use crate::circuit::operation::ValueOperation;
16use crate::circuit::{
17 Circuit, CircuitParam, ClassicalControlOp, Instruction, Operation, Parameter, ParameterValue,
18 Qubit, StandardGate, ValueClassicalControlOp, ValueControlBody, ValueInstruction,
19 ValueSwitchCase,
20};
21use crate::compile::error::CompilerError;
22use crate::compile::knowledge::library::RuleLibrary;
23use crate::compile::knowledge::matcher::KnowledgeInstructionKey as RewriteInstructionKey;
24use crate::compile::transform::rewrite::basis::{TargetContext, validate_final_target};
25use crate::compile::transform::rewrite::config::RewriteConfig;
26use crate::compile::transform::rewrite::diagnostics::KnowledgeRewriteDiagnostics;
27use crate::compile::transform::rewrite::matcher::{
28 BlockMatchCache, CompiledRuleSet, PatchPlanStep, ReplacementItem, RewritePatch,
29 RunActiveRuleSet, is_gphase_instruction, patch_application_plan, resolve_operation_param,
30 select_rewrites_for_anchor_ranges,
31};
32
33use crate::compile::transform::lowering_support::LoweringTarget;
34use crate::compile::transform::rebuild::{CircuitRebuildContext, ClassicalRemap};
35use crate::compile::transform::{TransformOutcome, Transformer};
36use smallvec::SmallVec;
37use std::borrow::Cow;
38use std::collections::{HashMap, HashSet};
39use std::hash::Hash;
40use std::ops::Range;
41use std::sync::{Arc, OnceLock};
42
43static BUILTIN_COMPILED_RULES: OnceLock<Result<Arc<CompiledRuleSet>, String>> = OnceLock::new();
44pub(super) const SMALL_CIRCUIT_FULL_SCAN_THRESHOLD: usize = 4_096;
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64enum RewriteExecutionPolicy {
65 Auto,
66 #[cfg(test)]
67 FullScan,
68 #[cfg(test)]
69 Incremental,
70}
71
72#[derive(Debug, Clone, Default, PartialEq, Eq)]
74pub struct KnowledgeRewriteStats {
75 pub rounds_executed: u8,
77 pub rules_applied: usize,
79 pub changed_sequences: usize,
81 pub reached_fixpoint: bool,
83}
84
85impl KnowledgeRewriteStats {
86 fn merge_round(&mut self, other: &RoundStats) {
87 self.rules_applied += other.rules_applied;
88 self.changed_sequences += other.changed_sequences;
89 }
90}
91
92#[derive(Debug, Clone)]
94pub struct KnowledgeRewriteResult {
95 pub circuit: Circuit,
96 pub changed: bool,
97 pub stats: KnowledgeRewriteStats,
98 pub diagnostics: KnowledgeRewriteDiagnostics,
100}
101
102pub(crate) struct KnowledgeRewriteSessionResult {
105 pub(crate) circuit: Option<Circuit>,
106 pub(crate) changed: bool,
107 pub(crate) stats: KnowledgeRewriteStats,
108 pub(crate) diagnostics: KnowledgeRewriteDiagnostics,
109}
110
111struct LinearRewriteResult {
112 circuit: Option<Circuit>,
113 changed: bool,
114 stats: KnowledgeRewriteStats,
115 diagnostics: KnowledgeRewriteDiagnostics,
116}
117
118impl LinearRewriteResult {
119 fn into_public(self, source: &Circuit) -> KnowledgeRewriteResult {
120 KnowledgeRewriteResult {
121 circuit: self.circuit.unwrap_or_else(|| source.clone()),
122 changed: self.changed,
123 stats: self.stats,
124 diagnostics: self.diagnostics,
125 }
126 }
127
128 fn into_session(self) -> KnowledgeRewriteSessionResult {
129 KnowledgeRewriteSessionResult {
130 circuit: self.circuit,
131 changed: self.changed,
132 stats: self.stats,
133 diagnostics: self.diagnostics,
134 }
135 }
136}
137
138#[derive(Debug, Clone, Default)]
139struct RoundStats {
140 rules_applied: usize,
141 changed_sequences: usize,
142 representation_changes: usize,
143 diagnostics: KnowledgeRewriteDiagnostics,
144}
145
146impl RoundStats {
147 fn changed(&self) -> bool {
148 self.rules_applied > 0 || self.representation_changes > 0
149 }
150}
151
152#[derive(Debug, Clone, PartialEq, Eq, Hash)]
153enum ScopeSegment {
154 IfThen(usize),
155 IfElse(usize),
156 WhileBody(usize),
157 ForBody(usize),
158 SwitchCase { control: usize, case: usize },
159 SwitchDefault(usize),
160}
161
162#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
163struct ScopeId(Vec<ScopeSegment>);
164
165impl ScopeId {
166 fn child(&self, segment: ScopeSegment) -> Self {
167 let mut path = self.0.clone();
168 path.push(segment);
169 Self(path)
170 }
171}
172
173#[derive(Debug, Clone, PartialEq, Eq, Hash)]
174struct BlockId {
175 scope: ScopeId,
176 slot: usize,
177}
178
179#[derive(Debug, Clone)]
180enum BlockWorkset {
181 Full,
182 Ranges(Vec<Range<usize>>),
183}
184
185#[derive(Debug, Clone, Default)]
186struct RewriteWorkset {
187 blocks: HashMap<BlockId, BlockWorkset>,
188 full_scopes: HashSet<ScopeId>,
189 caches: HashMap<BlockId, Arc<BlockMatchCache>>,
190}
191
192#[derive(Debug, Clone)]
198pub(crate) struct LinearRewriteWorkspace {
199 cache: BlockMatchCache,
200}
201
202impl LinearRewriteWorkspace {
203 pub(super) fn after_linear_replacements(
204 self,
205 after: &Circuit,
206 replacements: &[super::OperationReplacement],
207 qubits_renamed: bool,
208 ) -> Option<Self> {
209 Some(Self {
210 cache: self
211 .cache
212 .after_linear_replacements(after, replacements, qubits_renamed)?,
213 })
214 }
215}
216
217impl RewriteWorkset {
218 fn block_ranges(&self, block: &BlockId) -> Option<&[Range<usize>]> {
219 if self.full_scopes.contains(&block.scope) {
220 return None;
221 }
222 match self.blocks.get(block) {
223 Some(BlockWorkset::Full) => None,
224 Some(BlockWorkset::Ranges(ranges)) => Some(ranges),
225 None => Some(&[]),
226 }
227 }
228
229 fn mark_full_scope(&mut self, scope: ScopeId) {
230 self.blocks.retain(|block, _| block.scope != scope);
231 self.caches.retain(|block, _| block.scope != scope);
232 self.full_scopes.insert(scope);
233 }
234}
235
236#[derive(Debug, Clone)]
238pub struct KnowledgeRewriter {
239 config: RewriteConfig,
240 execution_policy: RewriteExecutionPolicy,
241}
242
243impl KnowledgeRewriter {
244 pub fn new(config: RewriteConfig) -> Self {
246 Self {
247 config,
248 execution_policy: RewriteExecutionPolicy::Auto,
249 }
250 }
251
252 pub fn production() -> Self {
254 Self::new(RewriteConfig::production())
255 }
256
257 pub fn lowering() -> Self {
259 Self::new(RewriteConfig::lowering())
260 }
261
262 pub const fn config(&self) -> &RewriteConfig {
263 &self.config
264 }
265
266 pub(crate) fn proof_reach(&self) -> Result<usize, CompilerError> {
273 Ok(builtin_compiled_rules()?.max_match_reach(&self.config))
274 }
275
276 pub(crate) fn proof_is_qubit_bijection_invariant(&self) -> Result<bool, CompilerError> {
285 Ok(builtin_compiled_rules()?.is_qubit_bijection_invariant())
286 }
287
288 #[cfg(test)]
289 pub(crate) fn force_full_scan(mut self) -> Self {
290 self.execution_policy = RewriteExecutionPolicy::FullScan;
291 self
292 }
293
294 #[cfg(test)]
295 pub(crate) fn force_incremental(mut self) -> Self {
296 self.execution_policy = RewriteExecutionPolicy::Incremental;
297 self
298 }
299
300 pub fn run(&self, circuit: &Circuit) -> Result<KnowledgeRewriteResult, CompilerError> {
318 self.run_internal(circuit, true)
319 }
320
321 fn run_internal(
322 &self,
323 circuit: &Circuit,
324 collect_diagnostics: bool,
325 ) -> Result<KnowledgeRewriteResult, CompilerError> {
326 if self.config.max_rounds() == 0 {
327 return Err(CompilerError::InvalidInput(
328 "rewrite max_rounds must be greater than zero".to_string(),
329 ));
330 }
331
332 let rules = builtin_compiled_rules()?;
333 let target_context = TargetContext::from_config(&self.config, rules.as_ref())?;
334 let incremental = match self.execution_policy {
335 RewriteExecutionPolicy::Auto => {
336 recursive_operation_count(circuit.operations()) >= SMALL_CIRCUIT_FULL_SCAN_THRESHOLD
337 }
338 #[cfg(test)]
339 RewriteExecutionPolicy::FullScan => false,
340 #[cfg(test)]
341 RewriteExecutionPolicy::Incremental => true,
342 };
343 if incremental && is_linear_workspace_eligible(circuit) {
344 return self
345 .run_linear_workspace(
346 circuit,
347 rules.as_ref(),
348 target_context.as_ref(),
349 None,
350 None,
351 collect_diagnostics,
352 )
353 .map(|(result, _)| result.into_public(circuit));
354 }
355 let active_rules = rules.active_rules(&self.config, target_context.as_ref());
356
357 let mut current = circuit.clone();
358 let mut aggregate = KnowledgeRewriteStats::default();
359 let mut diagnostics = KnowledgeRewriteDiagnostics::default();
360 let mut changed = false;
361 let mut workset: Option<RewriteWorkset> = None;
362
363 for round in 1..=self.config.max_rounds() {
364 aggregate.rounds_executed = round;
365 let (next, round_stats, next_workset) = RoundRewriter::run(
366 ¤t,
367 rules.as_ref(),
368 &active_rules,
369 &self.config,
370 target_context.as_ref(),
371 workset.as_ref(),
372 collect_diagnostics,
373 )?;
374 diagnostics.merge(round_stats.diagnostics);
375 if !round_stats.changed() {
376 aggregate.reached_fixpoint = true;
377 break;
378 }
379
380 changed = true;
381 aggregate.merge_round(&round_stats);
382 current = next;
383 workset = incremental.then_some(next_workset);
384 }
385
386 validate_final_target(¤t, &self.config)?;
387
388 Ok(KnowledgeRewriteResult {
389 circuit: current,
390 changed,
391 stats: aggregate,
392 diagnostics,
393 })
394 }
395
396 pub(crate) fn run_for_session(
400 &self,
401 circuit: &Circuit,
402 reconciled: Option<(Vec<Range<usize>>, Option<LinearRewriteWorkspace>)>,
403 collect_diagnostics: bool,
404 ) -> Result<
405 (
406 KnowledgeRewriteSessionResult,
407 Option<LinearRewriteWorkspace>,
408 ),
409 CompilerError,
410 > {
411 if !is_linear_workspace_eligible(circuit) {
412 let fell_back_from_incremental = reconciled.is_some();
413 return self
414 .run_internal(circuit, collect_diagnostics)
415 .map(|mut result| {
416 if collect_diagnostics && fell_back_from_incremental {
417 result.diagnostics.full_scan_fallbacks =
418 result.diagnostics.full_scan_fallbacks.saturating_add(1);
419 }
420 let circuit = result.changed.then_some(result.circuit);
421 (
422 KnowledgeRewriteSessionResult {
423 circuit,
424 changed: result.changed,
425 stats: result.stats,
426 diagnostics: result.diagnostics,
427 },
428 None,
429 )
430 });
431 }
432 if self.config.max_rounds() == 0 {
433 return Err(CompilerError::InvalidInput(
434 "rewrite max_rounds must be greater than zero".to_string(),
435 ));
436 }
437 let rules = builtin_compiled_rules()?;
438 let target_context = TargetContext::from_config(&self.config, rules.as_ref())?;
439 let (ranges, workspace) = reconciled.map_or((None, None), |(ranges, workspace)| {
440 (Some(ranges), workspace)
441 });
442 self.run_linear_workspace(
443 circuit,
444 rules.as_ref(),
445 target_context.as_ref(),
446 ranges,
447 workspace,
448 collect_diagnostics,
449 )
450 .map(|(result, workspace)| (result.into_session(), Some(workspace)))
451 }
452
453 fn run_linear_workspace(
471 &self,
472 circuit: &Circuit,
473 rules: &CompiledRuleSet,
474 target_context: Option<&TargetContext>,
475 initial_anchor_ranges: Option<Vec<Range<usize>>>,
476 workspace: Option<LinearRewriteWorkspace>,
477 collect_diagnostics: bool,
478 ) -> Result<(LinearRewriteResult, LinearRewriteWorkspace), CompilerError> {
479 let mut operations = Cow::Borrowed(circuit.operations());
480 let mut cache = match workspace {
481 Some(workspace) if workspace.cache.len() == operations.len() => workspace.cache,
482 _ => BlockMatchCache::new_with_diagnostics(circuit, &operations, collect_diagnostics)?,
483 };
484 let mut anchor_ranges = initial_anchor_ranges;
485 let mut aggregate = KnowledgeRewriteStats::default();
486 let diagnostics_before = cache.diagnostics();
487 let mut changed = false;
488 let mut phase_delta = Parameter::from(0.0);
489 let max_match_reach = rules.max_match_reach(&self.config);
490 let active_rules = rules.active_rules(&self.config, target_context);
491
492 for round in 1..=self.config.max_rounds() {
493 aggregate.rounds_executed = round;
494 let patches = select_rewrites_for_anchor_ranges(
495 operations.as_ref(),
496 &cache,
497 rules,
498 &active_rules,
499 &self.config,
500 target_context,
501 anchor_ranges.as_deref(),
502 )?;
503 if patches.is_empty() {
504 aggregate.reached_fixpoint = true;
505 break;
506 }
507
508 changed = true;
509 aggregate.rules_applied = aggregate.rules_applied.saturating_add(patches.len());
510 aggregate.changed_sequences = aggregate.changed_sequences.saturating_add(1);
511 let next_cache = cache
512 .into_rewritten(operations.as_ref(), &patches)
513 .ok_or_else(|| {
514 CompilerError::InvariantViolation(
515 "linear rewrite workspace could not materialize its selected patches"
516 .to_string(),
517 )
518 })?;
519 let (next_ranges, expected_len) =
520 dirty_ranges_after_patches(operations.len(), &patches, max_match_reach);
521 if expected_len != next_cache.len() {
525 return Err(CompilerError::InvariantViolation(format!(
526 "linear rewrite dirty-range length {expected_len} does not match rewritten block length {}",
527 next_cache.len()
528 )));
529 }
530 let (next_operations, round_phase) =
531 apply_linear_patches(operations.as_ref(), &patches)?;
532 phase_delta = &phase_delta + &round_phase;
533 operations = Cow::Owned(next_operations);
534 cache = next_cache;
535 anchor_ranges = Some(next_ranges);
536 }
537
538 let output = if changed {
539 let value_operations = operations
540 .into_owned()
541 .into_iter()
542 .enumerate()
543 .map(|(position, operation)| ValueOperation {
544 instruction: ValueInstruction::from_instruction(operation.instruction),
545 qubits: operation.qubits,
546 params: cache
547 .params(position)
548 .iter()
549 .cloned()
550 .map(ParameterValue::from)
551 .collect(),
552 label: operation.label,
553 })
554 .collect();
555 Some(CircuitRebuildContext::new(circuit).finish(
556 circuit.qubits(),
557 value_operations,
558 &circuit.global_phase() + &phase_delta,
559 )?)
560 } else {
561 None
562 };
563 validate_final_target(output.as_ref().unwrap_or(circuit), &self.config)?;
564 let mut diagnostics = KnowledgeRewriteDiagnostics::default();
565 diagnostics.merge_matcher(cache.diagnostics().saturating_delta(diagnostics_before));
566 Ok((
567 LinearRewriteResult {
568 circuit: output,
569 changed,
570 stats: aggregate,
571 diagnostics,
572 },
573 LinearRewriteWorkspace { cache },
574 ))
575 }
576}
577
578fn builtin_compiled_rules() -> Result<Arc<CompiledRuleSet>, CompilerError> {
579 match BUILTIN_COMPILED_RULES.get_or_init(|| {
580 let library = RuleLibrary::builtin_rules().map_err(|err| err.to_string())?;
581 CompiledRuleSet::from_library(library)
582 .map(Arc::new)
583 .map_err(|err| err.to_string())
584 }) {
585 Ok(rules) => Ok(Arc::clone(rules)),
586 Err(message) => Err(CompilerError::InvariantViolation(message.clone())),
587 }
588}
589
590impl Transformer for KnowledgeRewriter {
593 fn name(&self) -> &'static str {
594 "knowledge_rewrite"
595 }
596
597 fn transform(
598 &self,
599 circuit: &Circuit,
600 _analysis: Option<&crate::compile::transform::CircuitAnalysis>,
601 ) -> Result<TransformOutcome, CompilerError> {
602 let result = self.run(circuit)?;
603 Ok(if result.changed {
604 TransformOutcome::Changed(result.circuit)
605 } else {
606 TransformOutcome::Unchanged
607 })
608 }
609}
610
611pub fn rewrite_circuit(
613 circuit: &Circuit,
614 config: RewriteConfig,
615) -> Result<KnowledgeRewriteResult, CompilerError> {
616 KnowledgeRewriter::new(config).run(circuit)
617}
618
619struct RoundRewriter<'a> {
620 source: &'a Circuit,
621 rules: &'a CompiledRuleSet,
622 active_rules: &'a RunActiveRuleSet,
623 config: &'a RewriteConfig,
624 target_context: Option<&'a TargetContext>,
625 collect_diagnostics: bool,
626 workset: Option<&'a RewriteWorkset>,
627 next_workset: RewriteWorkset,
628 max_match_reach: usize,
629 rebuild: CircuitRebuildContext,
630 stats: RoundStats,
631}
632
633impl<'a> RoundRewriter<'a> {
634 fn run(
635 source: &'a Circuit,
636 rules: &'a CompiledRuleSet,
637 active_rules: &'a RunActiveRuleSet,
638 config: &'a RewriteConfig,
639 target_context: Option<&'a TargetContext>,
640 workset: Option<&'a RewriteWorkset>,
641 collect_diagnostics: bool,
642 ) -> Result<(Circuit, RoundStats, RewriteWorkset), CompilerError> {
643 let mut rewriter = Self {
644 source,
645 rules,
646 active_rules,
647 config,
648 target_context,
649 collect_diagnostics,
650 workset,
651 next_workset: RewriteWorkset::default(),
652 max_match_reach: rules.max_match_reach(config),
653 rebuild: CircuitRebuildContext::new(source),
654 stats: RoundStats::default(),
655 };
656 let mut phase_delta = Parameter::from(0.0);
657 let root_classical = rewriter.rebuild.root_classical().clone();
658 let mut operations = Vec::with_capacity(source.operations().len());
659
660 rewriter.apply_sequence(
661 source.operations(),
662 &root_classical,
663 &ScopeId::default(),
664 LoweringTarget::top_level(&mut operations, &mut phase_delta),
665 )?;
666 let global_phase = &source.global_phase() + &phase_delta;
667 let circuit = rewriter
668 .rebuild
669 .finish(source.qubits(), operations, global_phase)?;
670
671 Ok((circuit, rewriter.stats, rewriter.next_workset))
672 }
673
674 fn apply_sequence(
675 &mut self,
676 operations: &[Operation],
677 classical_remap: &ClassicalRemap,
678 scope: &ScopeId,
679 mut target: LoweringTarget<'_>,
680 ) -> Result<(), CompilerError> {
681 let mut cursor = 0;
682 let mut block_slot = 0;
683 let mut control_slot = 0;
684 while cursor < operations.len() {
685 if RewriteInstructionKey::from_instruction(&operations[cursor].instruction).is_none() {
686 let current_control_slot = control_slot;
687 if matches!(
688 operations[cursor].instruction,
689 Instruction::ClassicalControl(_)
690 ) {
691 control_slot += 1;
692 }
693 self.emit_original_operation(
694 &operations[cursor],
695 classical_remap,
696 scope,
697 current_control_slot,
698 &mut target,
699 )?;
700 cursor += 1;
701 block_slot += 1;
702 continue;
703 }
704
705 let block_start = cursor;
706 while cursor < operations.len()
707 && RewriteInstructionKey::from_instruction(&operations[cursor].instruction)
708 .is_some()
709 {
710 cursor += 1;
711 }
712
713 let block = &operations[block_start..cursor];
714 let block_id = BlockId {
715 scope: scope.clone(),
716 slot: block_slot,
717 };
718 let ranges = self
719 .workset
720 .map(|workset| workset.block_ranges(&block_id))
721 .unwrap_or(None);
722 let cache = match self
723 .workset
724 .filter(|workset| !workset.full_scopes.contains(scope))
725 .and_then(|workset| workset.caches.get(&block_id))
726 {
727 Some(cache) if cache.len() == block.len() => Arc::clone(cache),
728 _ => Arc::new(BlockMatchCache::new_with_diagnostics(
729 self.source,
730 block,
731 self.collect_diagnostics,
732 )?),
733 };
734 let diagnostics_before = cache.diagnostics();
735 let patches = select_rewrites_for_anchor_ranges(
736 block,
737 cache.as_ref(),
738 self.rules,
739 self.active_rules,
740 self.config,
741 self.target_context,
742 ranges,
743 )?;
744 self.stats
745 .diagnostics
746 .merge_matcher(cache.diagnostics().saturating_delta(diagnostics_before));
747 if patches.is_empty() {
748 self.next_workset
749 .caches
750 .insert(block_id.clone(), Arc::clone(&cache));
751 if target.is_top_level()
752 && block
753 .iter()
754 .any(|operation| is_gphase_instruction(&operation.instruction))
755 {
756 self.next_workset.mark_full_scope(scope.clone());
757 }
758 for operation in block {
759 self.emit_original_operation(
760 operation,
761 classical_remap,
762 scope,
763 control_slot,
764 &mut target,
765 )?;
766 }
767 } else {
768 self.stats.changed_sequences += 1;
769 self.record_next_block_workset(&block_id, block, &patches);
770 if let Some(next_cache) = cache.rewritten(block, &patches) {
771 self.next_workset
772 .caches
773 .insert(block_id.clone(), Arc::new(next_cache));
774 }
775 self.emit_rewritten_block(block, patches, classical_remap, &mut target)?;
776 }
777 }
778
779 Ok(())
780 }
781
782 fn record_next_block_workset(
791 &mut self,
792 block_id: &BlockId,
793 block: &[Operation],
794 patches: &[RewritePatch],
795 ) {
796 let contains_gphase = block
797 .iter()
798 .any(|operation| is_gphase_instruction(&operation.instruction))
799 || patches.iter().any(|patch| {
800 patch
801 .replacements
802 .iter()
803 .any(|replacement| is_gphase_instruction(&replacement.instruction))
804 });
805 if contains_gphase {
806 self.next_workset.mark_full_scope(block_id.scope.clone());
807 return;
808 }
809
810 if self.block_summary_changes(block, patches) {
811 self.next_workset
812 .blocks
813 .insert(block_id.clone(), BlockWorkset::Full);
814 return;
815 }
816
817 let (ranges, _) = dirty_ranges_after_patches(block.len(), patches, self.max_match_reach);
818 self.next_workset
819 .blocks
820 .insert(block_id.clone(), BlockWorkset::Ranges(ranges));
821 }
822
823 fn block_summary_changes(&self, block: &[Operation], patches: &[RewritePatch]) -> bool {
824 let mut old_keys = HashMap::<RewriteInstructionKey, usize>::new();
825 let mut old_qubits = HashMap::<Qubit, usize>::new();
826 for operation in block {
827 if let Some(key) = RewriteInstructionKey::from_instruction(&operation.instruction) {
828 *old_keys.entry(key).or_default() += 1;
829 }
830 for &qubit in &operation.qubits {
831 *old_qubits.entry(qubit).or_default() += 1;
832 }
833 }
834 let mut new_keys = old_keys.clone();
835 let mut new_qubits = old_qubits.clone();
836 for patch in patches {
837 for &position in &patch.matched_positions {
838 let operation = &block[position];
839 if let Some(key) = RewriteInstructionKey::from_instruction(&operation.instruction) {
840 decrement_count(&mut new_keys, &key);
841 }
842 for qubit in &operation.qubits {
843 decrement_count(&mut new_qubits, qubit);
844 }
845 }
846 for replacement in &patch.replacements {
847 *new_keys.entry(replacement.key.clone()).or_default() += 1;
848 for &qubit in &replacement.qubits {
849 *new_qubits.entry(qubit).or_default() += 1;
850 }
851 }
852 }
853
854 old_keys.keys().collect::<HashSet<_>>() != new_keys.keys().collect::<HashSet<_>>()
855 || old_qubits.keys().collect::<HashSet<_>>()
856 != new_qubits.keys().collect::<HashSet<_>>()
857 }
858
859 fn emit_rewritten_block(
860 &mut self,
861 block: &[Operation],
862 patches: Vec<RewritePatch>,
863 classical_remap: &ClassicalRemap,
864 target: &mut LoweringTarget<'_>,
865 ) -> Result<(), CompilerError> {
866 for step in patch_application_plan(block.len(), &patches)? {
869 match step {
870 PatchPlanStep::Replacements(patch) => {
871 self.stats.rules_applied += 1;
872 for replacement in &patch.replacements {
873 self.emit_replacement(replacement, target)?;
874 }
875 }
876 PatchPlanStep::DropMatched => {}
877 PatchPlanStep::Keep(position) => {
878 let operation = &block[position];
879 self.emit_operation(
880 operation.instruction.clone(),
881 operation.qubits.clone(),
882 operation.params.as_slice(),
883 operation.label.clone(),
884 classical_remap,
885 target,
886 )?;
887 }
888 }
889 }
890
891 Ok(())
892 }
893
894 fn emit_original_operation(
895 &mut self,
896 operation: &Operation,
897 classical_remap: &ClassicalRemap,
898 scope: &ScopeId,
899 control_slot: usize,
900 target: &mut LoweringTarget<'_>,
901 ) -> Result<(), CompilerError> {
902 if !self.config.recurses_control_flow() {
903 return self.emit_preserved_operation(operation, classical_remap, target);
904 }
905
906 if let Instruction::ClassicalControl(control) = &operation.instruction {
907 let instruction =
908 self.rewrite_control_flow(control, classical_remap, scope, control_slot)?;
909 let qubits = instruction.used_qubits().into_iter().collect();
910 return self.emit_value_operation(
911 ValueInstruction::ClassicalControl(instruction),
912 qubits,
913 CircuitRebuildContext::resolve_source_params(
914 self.source,
915 operation.params.as_slice(),
916 )?,
917 operation.label.clone(),
918 target,
919 );
920 }
921
922 self.emit_preserved_operation(operation, classical_remap, target)
923 }
924
925 fn rewrite_control_flow(
926 &mut self,
927 control: &ClassicalControlOp,
928 classical_remap: &ClassicalRemap,
929 parent_scope: &ScopeId,
930 control_slot: usize,
931 ) -> Result<ValueClassicalControlOp, CompilerError> {
932 let cc = match control {
933 ClassicalControlOp::If(op) => {
934 let then_scope = parent_scope.child(ScopeSegment::IfThen(control_slot));
935 let mut then_body = Vec::with_capacity(op.then_body().operations().len());
936 let mut then_phase = Parameter::from(0.0);
937 self.apply_sequence(
938 op.then_body().operations(),
939 classical_remap,
940 &then_scope,
941 LoweringTarget::control_flow_body(&mut then_body, &mut then_phase),
942 )?;
943 if !then_phase.is_zero() {
944 self.next_workset.mark_full_scope(then_scope);
945 }
946 self.prepend_body_phase(&mut then_body, then_phase);
947
948 let else_body = op
949 .else_body()
950 .map(|body| {
951 let else_scope = parent_scope.child(ScopeSegment::IfElse(control_slot));
952 let mut rewritten = Vec::with_capacity(body.operations().len());
953 let mut body_phase = Parameter::from(0.0);
954 self.apply_sequence(
955 body.operations(),
956 classical_remap,
957 &else_scope,
958 LoweringTarget::control_flow_body(&mut rewritten, &mut body_phase),
959 )?;
960 if !body_phase.is_zero() {
961 self.next_workset.mark_full_scope(else_scope);
962 }
963 self.prepend_body_phase(&mut rewritten, body_phase);
964 Ok::<_, CompilerError>(rewritten)
965 })
966 .transpose()?;
967
968 ValueClassicalControlOp::If {
969 condition: classical_remap.remap_expr(op.condition())?,
970 then_body: ValueControlBody::new(then_body),
971 else_body: else_body.map(ValueControlBody::new),
972 }
973 }
974 ClassicalControlOp::While(op) => {
975 let body_scope = parent_scope.child(ScopeSegment::WhileBody(control_slot));
976 let mut body = Vec::with_capacity(op.body().operations().len());
977 let mut body_phase = Parameter::from(0.0);
978 self.apply_sequence(
979 op.body().operations(),
980 classical_remap,
981 &body_scope,
982 LoweringTarget::control_flow_body(&mut body, &mut body_phase),
983 )?;
984 if !body_phase.is_zero() {
985 self.next_workset.mark_full_scope(body_scope);
986 }
987 self.prepend_body_phase(&mut body, body_phase);
988
989 ValueClassicalControlOp::While {
990 condition: classical_remap.remap_expr(op.condition())?,
991 body: ValueControlBody::new(body),
992 }
993 }
994 ClassicalControlOp::For(op) => {
995 let body_scope = parent_scope.child(ScopeSegment::ForBody(control_slot));
996 let mut body = Vec::with_capacity(op.body().operations().len());
997 let mut body_phase = Parameter::from(0.0);
998 self.apply_sequence(
999 op.body().operations(),
1000 classical_remap,
1001 &body_scope,
1002 LoweringTarget::control_flow_body(&mut body, &mut body_phase),
1003 )?;
1004 if !body_phase.is_zero() {
1005 self.next_workset.mark_full_scope(body_scope);
1006 }
1007 self.prepend_body_phase(&mut body, body_phase);
1008
1009 ValueClassicalControlOp::For {
1010 var: classical_remap.remap_var(op.var())?,
1011 start: classical_remap.remap_expr(op.start())?,
1012 stop: classical_remap.remap_expr(op.stop())?,
1013 step: classical_remap.remap_expr(op.step())?,
1014 body: ValueControlBody::new(body),
1015 }
1016 }
1017 ClassicalControlOp::Switch(op) => {
1018 let cases = op
1019 .cases()
1020 .iter()
1021 .enumerate()
1022 .map(|(case_index, case)| {
1023 let case_scope = parent_scope.child(ScopeSegment::SwitchCase {
1024 control: control_slot,
1025 case: case_index,
1026 });
1027 let mut rewritten = Vec::with_capacity(case.body().operations().len());
1028 let mut body_phase = Parameter::from(0.0);
1029 self.apply_sequence(
1030 case.body().operations(),
1031 classical_remap,
1032 &case_scope,
1033 LoweringTarget::control_flow_body(&mut rewritten, &mut body_phase),
1034 )?;
1035 if !body_phase.is_zero() {
1036 self.next_workset.mark_full_scope(case_scope);
1037 }
1038 self.prepend_body_phase(&mut rewritten, body_phase);
1039 Ok::<_, CompilerError>(ValueSwitchCase::new(
1040 case.value(),
1041 ValueControlBody::new(rewritten),
1042 ))
1043 })
1044 .collect::<Result<Vec<_>, _>>()?;
1045 let default = op
1046 .default()
1047 .map(|body| {
1048 let default_scope =
1049 parent_scope.child(ScopeSegment::SwitchDefault(control_slot));
1050 let mut rewritten = Vec::with_capacity(body.operations().len());
1051 let mut body_phase = Parameter::from(0.0);
1052 self.apply_sequence(
1053 body.operations(),
1054 classical_remap,
1055 &default_scope,
1056 LoweringTarget::control_flow_body(&mut rewritten, &mut body_phase),
1057 )?;
1058 if !body_phase.is_zero() {
1059 self.next_workset.mark_full_scope(default_scope);
1060 }
1061 self.prepend_body_phase(&mut rewritten, body_phase);
1062 Ok::<_, CompilerError>(ValueControlBody::new(rewritten))
1063 })
1064 .transpose()?;
1065
1066 ValueClassicalControlOp::Switch {
1067 target: classical_remap.remap_expr(op.target())?,
1068 cases,
1069 default,
1070 }
1071 }
1072 ClassicalControlOp::Break => ValueClassicalControlOp::Break,
1073 ClassicalControlOp::Continue => ValueClassicalControlOp::Continue,
1074 };
1075
1076 Ok(cc)
1077 }
1078
1079 fn emit_operation(
1080 &mut self,
1081 instruction: Instruction,
1082 qubits: SmallVec<[Qubit; 3]>,
1083 params: &[CircuitParam],
1084 label: Option<Box<str>>,
1085 classical_remap: &ClassicalRemap,
1086 target: &mut LoweringTarget<'_>,
1087 ) -> Result<(), CompilerError> {
1088 if is_gphase_instruction(&instruction) {
1089 if target.is_top_level() {
1090 self.stats.representation_changes += 1;
1091 }
1092 target.accumulate_phase(self.source_gphase_param(params)?);
1093 return Ok(());
1094 }
1095
1096 let instruction = self
1097 .rebuild
1098 .remap_non_control_instruction(&instruction, classical_remap)?;
1099 let params = CircuitRebuildContext::resolve_source_params(self.source, params)?;
1100 self.emit_value_operation(instruction, qubits, params, label, target)
1101 }
1102
1103 fn emit_preserved_operation(
1104 &mut self,
1105 operation: &Operation,
1106 classical_remap: &ClassicalRemap,
1107 target: &mut LoweringTarget<'_>,
1108 ) -> Result<(), CompilerError> {
1109 if is_gphase_instruction(&operation.instruction) {
1110 if target.is_top_level() {
1111 self.stats.representation_changes += 1;
1112 }
1113 target.accumulate_phase(self.source_gphase_param(&operation.params)?);
1114 return Ok(());
1115 }
1116
1117 let operation =
1118 self.rebuild
1119 .remap_preserved_operation(self.source, operation, classical_remap)?;
1120 target.push(operation);
1121 Ok(())
1122 }
1123
1124 fn emit_value_operation(
1125 &mut self,
1126 instruction: ValueInstruction,
1127 qubits: SmallVec<[Qubit; 3]>,
1128 params: SmallVec<[ParameterValue; 1]>,
1129 label: Option<Box<str>>,
1130 target: &mut LoweringTarget<'_>,
1131 ) -> Result<(), CompilerError> {
1132 target.push(ValueOperation {
1133 instruction,
1134 qubits,
1135 params,
1136 label,
1137 });
1138 Ok(())
1139 }
1140
1141 fn emit_replacement(
1142 &mut self,
1143 replacement: &ReplacementItem,
1144 target: &mut LoweringTarget<'_>,
1145 ) -> Result<(), CompilerError> {
1146 if is_gphase_instruction(&replacement.instruction) {
1147 target.accumulate_phase(Self::replacement_gphase_param(replacement)?);
1148 return Ok(());
1149 }
1150
1151 let params = replacement.params.iter().cloned().collect();
1152 self.emit_value_operation(
1153 ValueInstruction::from_instruction(replacement.instruction.clone()),
1154 replacement.qubits.clone(),
1155 params,
1156 None,
1157 target,
1158 )
1159 }
1160
1161 fn prepend_body_phase(&mut self, body: &mut Vec<ValueOperation>, phase: Parameter) {
1162 if phase.is_zero() {
1163 return;
1164 }
1165
1166 body.insert(
1167 0,
1168 ValueOperation {
1169 instruction: ValueInstruction::from_instruction(Instruction::Standard(
1170 StandardGate::GPhase,
1171 )),
1172 qubits: SmallVec::new(),
1173 params: smallvec::smallvec![ParameterValue::from(phase)],
1174 label: None,
1175 },
1176 );
1177 }
1178
1179 fn source_gphase_param(&self, params: &[CircuitParam]) -> Result<Parameter, CompilerError> {
1180 if params.len() != 1 {
1181 return Err(CompilerError::InvariantViolation(
1182 "GPhase operation must contain one parameter".to_string(),
1183 ));
1184 }
1185 resolve_operation_param(self.source, ¶ms[0], 0)
1186 }
1187
1188 fn replacement_gphase_param(replacement: &ReplacementItem) -> Result<Parameter, CompilerError> {
1189 let phase = replacement.params.first().ok_or_else(|| {
1190 CompilerError::InvariantViolation(
1191 "GPhase replacement must contain one parameter".to_string(),
1192 )
1193 })?;
1194 Ok(Parameter::from(phase))
1195 }
1196}
1197
1198pub(super) fn is_linear_workspace_eligible(circuit: &Circuit) -> bool {
1206 circuit.classical_vars().is_empty()
1207 && circuit.classical_values().is_empty()
1208 && circuit.operations().iter().all(|operation| {
1209 RewriteInstructionKey::from_instruction(&operation.instruction).is_some()
1210 && !is_gphase_instruction(&operation.instruction)
1211 })
1212}
1213
1214fn dirty_ranges_after_patches(
1234 old_len: usize,
1235 patches: &[RewritePatch],
1236 max_match_reach: usize,
1237) -> (Vec<Range<usize>>, usize) {
1238 let mut ranges = Vec::with_capacity(patches.len());
1239 let mut delta = 0isize;
1240 let mut new_len = old_len as isize;
1241 for patch in patches {
1242 let inserted = patch
1243 .replacements
1244 .iter()
1245 .filter(|replacement| !is_gphase_instruction(&replacement.instruction))
1246 .count() as isize;
1247 let removed = patch.matched_positions.len() as isize;
1248 let new_start = (patch.first_position as isize + delta).max(0) as usize;
1249 delta += inserted - removed;
1250 new_len += inserted - removed;
1251 let after_span = (patch.last_position as isize + 1 + delta).max(0) as usize;
1252 let dirty_start = new_start.saturating_sub(max_match_reach);
1253 let dirty_end = after_span.max(new_start.saturating_add(1));
1254 ranges.push(dirty_start..dirty_end);
1255 }
1256
1257 let new_len = new_len.max(0) as usize;
1258 for range in &mut ranges {
1259 range.end = range.end.min(new_len);
1260 }
1261 ranges.retain(|range| range.start < range.end);
1262 merge_ranges(&mut ranges);
1263 if ranges.is_empty() && new_len > 0 {
1264 ranges.push(0..new_len.min(max_match_reach.saturating_add(1)));
1265 }
1266 (ranges, new_len)
1267}
1268
1269fn apply_linear_patches(
1280 operations: &[Operation],
1281 patches: &[RewritePatch],
1282) -> Result<(Vec<Operation>, Parameter), CompilerError> {
1283 let steps = patch_application_plan(operations.len(), patches)?;
1284 let mut output = Vec::with_capacity(operations.len());
1285 let mut phase_delta = Parameter::from(0.0);
1286 let mut operations = operations.iter();
1287 for step in steps {
1288 match step {
1289 PatchPlanStep::Replacements(patch) => {
1290 for replacement in &patch.replacements {
1291 if is_gphase_instruction(&replacement.instruction) {
1292 phase_delta =
1293 &phase_delta + &RoundRewriter::replacement_gphase_param(replacement)?;
1294 continue;
1295 }
1296 output.push(Operation {
1297 instruction: replacement.instruction.clone(),
1298 qubits: replacement.qubits.clone(),
1299 params: replacement
1300 .params
1301 .iter()
1302 .map(|_| CircuitParam::Fixed(0.0))
1303 .collect(),
1304 label: None,
1305 });
1306 }
1307 }
1308 PatchPlanStep::DropMatched => {
1309 operations.next();
1310 }
1311 PatchPlanStep::Keep(_) => {
1312 output.push(
1313 operations
1314 .next()
1315 .expect(
1316 "patch application plan produces exactly one step per block position",
1317 )
1318 .clone(),
1319 );
1320 }
1321 }
1322 }
1323 Ok((output, phase_delta))
1324}
1325
1326fn decrement_count<K>(counts: &mut HashMap<K, usize>, key: &K)
1327where
1328 K: Eq + Hash,
1329{
1330 let remove = counts.get_mut(key).is_some_and(|count| {
1331 *count = count.saturating_sub(1);
1332 *count == 0
1333 });
1334 if remove {
1335 counts.remove(key);
1336 }
1337}
1338
1339fn merge_ranges(ranges: &mut Vec<Range<usize>>) {
1340 ranges.sort_by_key(|range| range.start);
1341 let mut merged = Vec::<Range<usize>>::with_capacity(ranges.len());
1342 for range in ranges.drain(..) {
1343 if let Some(previous) = merged.last_mut()
1344 && range.start <= previous.end
1345 {
1346 previous.end = previous.end.max(range.end);
1347 } else {
1348 merged.push(range);
1349 }
1350 }
1351 *ranges = merged;
1352}
1353
1354fn recursive_operation_count(operations: &[Operation]) -> usize {
1355 let mut count = operations.len();
1356 for operation in operations {
1357 if let Instruction::ClassicalControl(control) = &operation.instruction {
1358 for body in control.bodies() {
1359 count = count.saturating_add(recursive_operation_count(body.operations()));
1360 }
1361 }
1362 }
1363 count
1364}