ket/
process.rs

1// SPDX-FileCopyrightText: 2024 Evandro Chagas Ribeiro da Rosa <evandro@quantuloop.com>
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use serde::{Deserialize, Serialize};
6use std::collections::{HashMap, HashSet};
7
8use crate::{
9    circuit::Circuit,
10    decompose::{AuxMode, Registry, Schema, State},
11    error::{KetError, Result},
12    execution::{Configuration, FeatureStatus, QuantumExecution, Qubit},
13    ir::{
14        gate::QuantumGate,
15        hamiltonian::Hamiltonian,
16        instructions::Instruction,
17        qubit::{LogicalQubit, PhysicalQubit},
18    },
19    mapping,
20    prelude::QPU,
21};
22
23type QubitList = Vec<LogicalQubit>;
24type CtrlStack = Vec<QubitList>;
25
26#[derive(Debug)]
27enum GateInstruction {
28    Gate {
29        gate: QuantumGate,
30        target: LogicalQubit,
31        control: Vec<LogicalQubit>,
32    },
33    AuxRegistry(std::rc::Rc<std::cell::RefCell<Registry>>),
34}
35
36impl GateInstruction {
37    fn inverse(self) -> Self {
38        match self {
39            Self::Gate {
40                gate,
41                target,
42                control,
43            } => Self::Gate {
44                gate: gate.inverse(),
45                target,
46                control,
47            },
48            Self::AuxRegistry(registry) => Self::AuxRegistry(registry),
49        }
50    }
51}
52
53type GateList = Vec<GateInstruction>;
54pub type Sample = (Vec<u64>, Vec<u64>);
55
56#[derive(Debug, Clone, Default, Deserialize, Serialize)]
57pub struct DumpData {
58    pub basis_states: Vec<Vec<u64>>,
59    pub amplitudes_real: Vec<f64>,
60    pub amplitudes_imag: Vec<f64>,
61}
62
63#[derive(Debug, Serialize)]
64pub struct Metadata {
65    pub logical_gate_count: HashMap<usize, usize>,
66    pub logical_circuit_depth: usize,
67    pub physical_gate_count: Option<HashMap<usize, usize>>,
68    pub physical_circuit_depth: Option<usize>,
69    pub allocated_qubits: usize,
70    pub terminated: bool,
71    pub decomposition: HashMap<String, usize>,
72}
73
74#[derive(Debug, Default)]
75pub struct Process {
76    ctrl_stack: Vec<CtrlStack>,
77    ctrl_list: QubitList,
78    ctrl_list_is_valid: bool,
79
80    logical_circuit: Circuit<LogicalQubit>,
81
82    physical_circuit: Option<Circuit<PhysicalQubit>>,
83
84    adj_stack: Vec<GateList>,
85
86    measurements: Vec<Option<u64>>,
87
88    samples: Vec<Option<Sample>>,
89
90    exp_values: Vec<Option<f64>>,
91
92    dumps: Vec<Option<DumpData>>,
93
94    configuration: Configuration,
95
96    allocated_qubits: usize,
97    qubit_count: usize,
98
99    aux_count: usize,
100
101    pub(crate) qubit_valid: HashMap<LogicalQubit, bool>,
102    pub(crate) qubit_measured: HashMap<LogicalQubit, bool>,
103
104    alloc_stack: Vec<LogicalQubit>,
105    clean_qubits: HashSet<LogicalQubit>,
106
107    terminated: bool,
108
109    decomposition_stats: HashMap<String, usize>,
110}
111
112impl Process {
113    pub fn new(configuration: Configuration) -> Self {
114        Self {
115            ctrl_stack: vec![Vec::new()],
116            configuration,
117            ..Default::default()
118        }
119    }
120
121    fn flatten_control_qubits(&mut self) {
122        if !self.ctrl_list_is_valid {
123            self.ctrl_list = self
124                .ctrl_stack
125                .last()
126                .unwrap()
127                .clone()
128                .into_iter()
129                .flatten()
130                .collect();
131            self.ctrl_list_is_valid = true;
132        }
133    }
134
135    fn non_gate_checks(
136        &mut self,
137        qubits: Option<&[LogicalQubit]>,
138        feature: Option<FeatureStatus>,
139    ) -> Result<()> {
140        if self.terminated {
141            Err(KetError::TerminatedProcess)
142        } else if matches!(feature, Some(FeatureStatus::Disable)) {
143            Err(KetError::MeasurementDisabled)
144        } else if !(self.ctrl_stack.len() == 1 && self.ctrl_stack[0].is_empty()) {
145            Err(KetError::ControlledScope)
146        } else if !self.adj_stack.is_empty() {
147            Err(KetError::InverseScope)
148        } else if qubits.map_or(false, |qubits| {
149            qubits
150                .iter()
151                .any(|qubit| !*self.qubit_valid.entry(*qubit).or_insert(true))
152        }) {
153            Err(KetError::QubitUnavailable)
154        } else {
155            Ok(())
156        }
157    }
158
159    fn gate_checks(&mut self, target: LogicalQubit) -> Result<()> {
160        if !*self.qubit_valid.entry(target).or_insert(true) {
161            Err(KetError::QubitUnavailable)
162        } else if self.ctrl_list.contains(&target) {
163            Err(KetError::ControlTargetOverlap)
164        } else if self.terminated {
165            Err(KetError::TerminatedProcess)
166        } else {
167            Ok(())
168        }
169    }
170
171    fn adj_ctrl_checks(&mut self, qubits: Option<&[LogicalQubit]>) -> Result<()> {
172        if qubits.map_or(false, |qubits| {
173            qubits
174                .iter()
175                .any(|qubit| !*self.qubit_valid.entry(*qubit).or_insert(true))
176        }) {
177            Err(KetError::QubitUnavailable)
178        } else if qubits.map_or(false, |qubits| {
179            qubits.iter().any(|qubit| self.ctrl_list.contains(qubit))
180        }) {
181            Err(KetError::ControlTwice)
182        } else if self.terminated {
183            Err(KetError::TerminatedProcess)
184        } else {
185            Ok(())
186        }
187    }
188
189    pub fn alloc(&mut self) -> Result<LogicalQubit> {
190        self.non_gate_checks(None, None)?;
191
192        self.reserve_qubits(1)?;
193        self.allocated_qubits += 1;
194
195        Ok(self.alloc_stack.pop().unwrap())
196    }
197
198    fn reserve_qubits(&mut self, num_qubits: usize) -> Result<()> {
199        while self.alloc_stack.len() < num_qubits {
200            if self.allocated_qubits > self.configuration.num_qubits {
201                return Err(KetError::MaxQubitsReached);
202            }
203
204            let qubit = LogicalQubit::main(self.qubit_count);
205
206            self.qubit_count += 1;
207
208            self.alloc_stack.push(qubit);
209            assert!(self.clean_qubits.insert(qubit));
210        }
211
212        Ok(())
213    }
214
215    fn try_alloc_aux(
216        &mut self,
217        num_qubits: usize,
218        interacting_qubits: Option<&[LogicalQubit]>,
219    ) -> Option<Vec<LogicalQubit>> {
220        if (interacting_qubits.is_none()
221            && (num_qubits + self.allocated_qubits) > self.configuration.num_qubits)
222            || (interacting_qubits.is_some()
223                && (num_qubits + interacting_qubits.unwrap().len()) > self.configuration.num_qubits)
224        {
225            return None;
226        }
227
228        let result: Vec<_> = (0..num_qubits)
229            .map(|index| LogicalQubit::aux(index + self.aux_count))
230            .collect();
231
232        self.aux_count += num_qubits;
233
234        let reserver_qubits = if let Some(interacting_qubits) = interacting_qubits {
235            let dirty_available = self.allocated_qubits - interacting_qubits.len();
236            if dirty_available >= num_qubits {
237                0
238            } else {
239                num_qubits - dirty_available
240            }
241        } else {
242            num_qubits
243        };
244
245        self.reserve_qubits(reserver_qubits).unwrap(); // this should not fail if the first check is correct
246
247        Some(result)
248    }
249
250    fn free_aux(&mut self, registry: &Registry) {
251        if let Some(aux_qubits) = &registry.aux_qubits {
252            let mut allocated = HashSet::new();
253            for aux_qubit in aux_qubits {
254                let mut main_qubit = None;
255                for interacting_qubit in self.logical_circuit.interacting_qubits(*aux_qubit) {
256                    for candidate_qubit in self
257                        .logical_circuit
258                        .interacting_qubits_rev(*interacting_qubit)
259                    {
260                        if candidate_qubit.is_aux() {
261                            continue;
262                        }
263                        let use_this = match &registry.interacting_qubits {
264                            Some(interacting_qubits) => {
265                                !interacting_qubits.contains(candidate_qubit)
266                                    && !allocated.contains(candidate_qubit)
267                            }
268                            None => {
269                                self.clean_qubits.contains(candidate_qubit)
270                                    && !allocated.contains(candidate_qubit)
271                            }
272                        };
273
274                        if use_this {
275                            main_qubit = Some(*candidate_qubit);
276                            break;
277                        }
278                    }
279                }
280                let main_qubit = if let Some(main_qubit) = main_qubit {
281                    main_qubit
282                } else {
283                    let mut main_qubit = None;
284                    for candidate_qubit in &self.clean_qubits {
285                        if !allocated.contains(candidate_qubit) {
286                            main_qubit = Some(*candidate_qubit);
287                            break;
288                        }
289                    }
290
291                    if main_qubit.is_none() {
292                        for candidate_qubit in 0..self.allocated_qubits {
293                            let candidate_qubit = LogicalQubit::main(candidate_qubit);
294                            if !allocated.contains(&candidate_qubit)
295                                && !registry
296                                    .interacting_qubits
297                                    .as_ref()
298                                    .unwrap()
299                                    .contains(&candidate_qubit)
300                            {
301                                main_qubit = Some(candidate_qubit);
302                                break;
303                            }
304                        }
305                    }
306
307                    main_qubit.unwrap()
308                };
309                allocated.insert(main_qubit);
310                self.logical_circuit.alloc_aux_qubit(*aux_qubit, main_qubit);
311            }
312        }
313    }
314
315    pub fn gate(&mut self, gate: QuantumGate, target: LogicalQubit) -> Result<()> {
316        if gate.is_identity() {
317            return Ok(());
318        }
319
320        self.flatten_control_qubits();
321        self.gate_checks(target)?;
322
323        for qubit in self.ctrl_list.iter().chain([&target]) {
324            self.clean_qubits.remove(qubit);
325        }
326
327        if !self.ctrl_list.is_empty() && self.configuration.qpu.is_some() {
328            let mut schema = Schema::default();
329            let interacting_qubits: Vec<_> =
330                self.ctrl_list.iter().cloned().chain([target]).collect();
331
332            for algorithm in gate.decomposition_list(self.ctrl_list.len()) {
333                if !algorithm.need_aux() {
334                    schema = Schema {
335                        algorithm,
336                        aux_qubits: None,
337                    };
338                    break;
339                }
340
341                if let Some(qubits) = self.try_alloc_aux(
342                    algorithm.aux_needed(self.ctrl_list.len()),
343                    if matches!(algorithm.aux_mode(), AuxMode::Dirty) {
344                        Some(&interacting_qubits)
345                    } else {
346                        None
347                    },
348                ) {
349                    schema = Schema {
350                        algorithm,
351                        aux_qubits: Some(qubits),
352                    };
353                    break;
354                }
355            }
356
357            let registry: std::rc::Rc<std::cell::RefCell<Registry>> =
358                std::rc::Rc::new(std::cell::RefCell::new(Registry {
359                    algorithm: schema.algorithm,
360                    aux_qubits: schema.aux_qubits.clone(),
361                    interacting_qubits: if schema.algorithm.aux_mode() == AuxMode::Dirty {
362                        Some(interacting_qubits)
363                    } else {
364                        None
365                    },
366                    ..Default::default()
367                }));
368
369            self.push_gate(GateInstruction::AuxRegistry(registry.clone()));
370
371            for (gate, target, control) in gate.decompose(
372                target,
373                &self.ctrl_list,
374                schema,
375                self.configuration.qpu.as_ref().unwrap().u4_gate,
376            ) {
377                let control = control.map_or(vec![], |control| vec![control]);
378                self.push_gate(GateInstruction::Gate {
379                    gate,
380                    target,
381                    control,
382                });
383            }
384
385            self.push_gate(GateInstruction::AuxRegistry(registry));
386        } else {
387            self.push_gate(GateInstruction::Gate {
388                gate,
389                target,
390                control: self.ctrl_list.to_owned(),
391            });
392        }
393
394        Ok(())
395    }
396
397    fn push_gate(&mut self, gate: GateInstruction) {
398        if let Some(ajd_stack) = self.adj_stack.last_mut() {
399            ajd_stack.push(gate);
400        } else {
401            match gate {
402                GateInstruction::Gate {
403                    gate,
404                    target,
405                    control,
406                } => {
407                    self.logical_circuit.gate(gate, target, &control);
408                    if let Some(QuantumExecution::Live(execution)) =
409                        self.configuration.execution.as_mut()
410                    {
411                        execution.gate(gate, target, &control);
412                    }
413                }
414                GateInstruction::AuxRegistry(registry) => {
415                    let mut registry = registry.borrow_mut();
416                    match registry.state {
417                        State::Begin => {
418                            registry.num_u4 =
419                                *self.logical_circuit.gate_count.entry(2).or_default();
420                            registry.state = State::End;
421                        }
422                        State::End => {
423                            *self
424                                .decomposition_stats
425                                .entry(registry.algorithm.to_string())
426                                .or_default() +=
427                                *self.logical_circuit.gate_count.entry(2).or_default()
428                                    - registry.num_u4;
429                            self.free_aux(&registry);
430                        }
431                    }
432                }
433            }
434        }
435    }
436
437    pub fn global_phase(&mut self, angle: f64) -> Result<()> {
438        self.adj_ctrl_checks(None)?;
439        self.flatten_control_qubits();
440
441        if self.ctrl_list.is_empty() {
442            return Ok(());
443        }
444
445        let qubits = self.ctrl_list.clone();
446
447        self.ctrl_begin()?;
448        self.ctrl_push(&qubits[1..])?;
449        self.gate(QuantumGate::Phase(angle), qubits[0])?;
450        self.ctrl_pop()?;
451        self.ctrl_end()?;
452        Ok(())
453    }
454
455    pub fn measure(&mut self, qubits: &[LogicalQubit]) -> Result<usize> {
456        self.non_gate_checks(Some(qubits), Some(self.configuration.measure))?;
457
458        let index = self.measurements.len();
459
460        self.logical_circuit.measure(qubits, index);
461
462        self.measurements.push(
463            if let Some(QuantumExecution::Live(execution)) = self.configuration.execution.as_mut() {
464                Some(execution.measure(qubits))
465            } else {
466                None
467            },
468        );
469
470        for qubit in qubits {
471            self.qubit_measured.insert(*qubit, true);
472        }
473
474        if !matches!(self.configuration.measure, FeatureStatus::ValidAfter) {
475            for qubit in qubits {
476                self.qubit_valid.insert(*qubit, false);
477            }
478        }
479        Ok(index)
480    }
481
482    pub fn sample(&mut self, qubits: &[LogicalQubit], shots: usize) -> Result<usize> {
483        self.non_gate_checks(Some(qubits), Some(self.configuration.sample))?;
484
485        let index = self.samples.len();
486
487        self.logical_circuit.sample(qubits, shots, index);
488
489        self.samples.push(
490            if let Some(QuantumExecution::Live(execution)) = self.configuration.execution.as_mut() {
491                Some(execution.sample(qubits, shots))
492            } else {
493                None
494            },
495        );
496
497        if !matches!(self.configuration.sample, FeatureStatus::ValidAfter) {
498            self.terminated = true;
499            self.transpile();
500        }
501
502        Ok(index)
503    }
504
505    pub fn exp_value(&mut self, hamiltonian: Hamiltonian<LogicalQubit>) -> Result<usize> {
506        let qubits = hamiltonian.qubits().cloned().collect::<Vec<_>>();
507        self.non_gate_checks(Some(&qubits), Some(self.configuration.exp_value))?;
508
509        let index = self.exp_values.len();
510
511        self.exp_values.push(
512            if let Some(QuantumExecution::Live(execution)) = self.configuration.execution.as_mut() {
513                Some(execution.exp_value(&hamiltonian))
514            } else {
515                None
516            },
517        );
518
519        self.logical_circuit.exp_value(hamiltonian, index);
520
521        if !matches!(self.configuration.exp_value, FeatureStatus::ValidAfter) {
522            self.terminated = true;
523            self.transpile();
524        }
525
526        Ok(index)
527    }
528
529    pub fn dump(&mut self, qubits: &[LogicalQubit]) -> Result<usize> {
530        self.non_gate_checks(Some(qubits), Some(self.configuration.dump))?;
531
532        let index = self.dumps.len();
533
534        self.logical_circuit.dump(qubits, index);
535
536        self.dumps.push(
537            if let Some(QuantumExecution::Live(execution)) = self.configuration.execution.as_mut() {
538                Some(execution.dump(qubits))
539            } else {
540                None
541            },
542        );
543
544        if !matches!(self.configuration.dump, FeatureStatus::ValidAfter) {
545            self.terminated = true;
546            self.transpile();
547        }
548
549        Ok(index)
550    }
551
552    pub fn transpile(&mut self) {
553        self.terminated = true;
554
555        if let (
556            Some(QPU {
557                coupling_graph: Some(coupling_graph),
558                ..
559            }),
560            None,
561        ) = (
562            self.configuration.qpu.as_mut(),
563            self.physical_circuit.as_ref(),
564        ) {
565            coupling_graph.calculate_distance();
566        }
567
568        if let (
569            Some(QPU {
570                coupling_graph: Some(coupling_graph),
571                u4_gate,
572                u2_gates,
573            }),
574            None,
575        ) = (
576            self.configuration.qpu.as_ref(),
577            self.physical_circuit.as_ref(),
578        ) {
579            let mapping = mapping::allocation::initial(
580                &self.logical_circuit.interaction_graph(),
581                coupling_graph,
582            );
583            let mut physical_circuit =
584                mapping::map_circuit(mapping, coupling_graph, &self.logical_circuit, *u4_gate, 4);
585            physical_circuit.gate_map(*u2_gates);
586            self.physical_circuit = Some(physical_circuit);
587        }
588    }
589
590    pub fn execute(&mut self) -> Result<()> {
591        self.transpile();
592
593        let mut results = None;
594
595        if let Some(QuantumExecution::Batch(execution)) = self.configuration.execution.as_mut() {
596            execution.submit_execution(
597                &self.logical_circuit.instructions,
598                self.physical_circuit
599                    .as_ref()
600                    .map(|circuit| circuit.instructions.as_ref()),
601            );
602            results = Some(execution.get_results());
603        }
604
605        if let Some(mut results) = results {
606            if self.measurements.len() != results.measurements.len()
607                || self.exp_values.len() != results.exp_values.len()
608                || self.samples.len() != results.samples.len()
609                || self.dumps.len() != results.dumps.len()
610            {
611                return Err(KetError::ResultDataMismatch);
612            }
613
614            results
615                .measurements
616                .drain(..)
617                .zip(self.measurements.iter_mut())
618                .for_each(|(result, measurement)| {
619                    *measurement = Some(result);
620                });
621
622            results
623                .exp_values
624                .drain(..)
625                .zip(self.exp_values.iter_mut())
626                .for_each(|(result, exp_value)| {
627                    *exp_value = Some(result);
628                });
629
630            results
631                .samples
632                .drain(..)
633                .zip(self.samples.iter_mut())
634                .for_each(|(result, sample)| {
635                    assert_eq!(result.0.len(), result.1.len());
636                    *sample = Some(result);
637                });
638
639            results
640                .dumps
641                .drain(..)
642                .zip(self.dumps.iter_mut())
643                .for_each(|(result, dump)| {
644                    *dump = Some(result);
645                });
646        }
647        Ok(())
648    }
649
650    pub fn get_measure(&self, index: usize) -> Option<u64> {
651        self.measurements.get(index).copied().flatten()
652    }
653
654    pub fn get_sample(&self, index: usize) -> Option<&Sample> {
655        self.samples.get(index).and_then(|s| s.as_ref())
656    }
657
658    pub fn get_exp_value(&self, index: usize) -> Option<f64> {
659        self.exp_values.get(index).copied().flatten()
660    }
661
662    pub fn get_dump(&self, index: usize) -> Option<&DumpData> {
663        self.dumps.get(index).and_then(|d| d.as_ref())
664    }
665
666    pub fn ctrl_push(&mut self, qubits: &[LogicalQubit]) -> Result<()> {
667        self.flatten_control_qubits();
668        self.adj_ctrl_checks(Some(qubits))?;
669        self.ctrl_stack.last_mut().unwrap().push(qubits.to_owned());
670        self.ctrl_list_is_valid = false;
671        Ok(())
672    }
673
674    pub fn ctrl_pop(&mut self) -> Result<()> {
675        self.ctrl_list_is_valid = false;
676
677        if self.ctrl_stack.last_mut().unwrap().pop().is_none() {
678            Err(KetError::ControlStackEmpty)
679        } else {
680            Ok(())
681        }
682    }
683
684    pub fn adj_begin(&mut self) -> Result<()> {
685        self.adj_ctrl_checks(None)?;
686        self.adj_stack.push(vec![]);
687        Ok(())
688    }
689
690    pub fn adj_end(&mut self) -> Result<()> {
691        if let Some(mut gates) = self.adj_stack.pop() {
692            while let Some(gate) = gates.pop() {
693                self.push_gate(gate.inverse());
694            }
695            Ok(())
696        } else {
697            Err(KetError::InverseScopeEmpty)
698        }
699    }
700
701    pub fn ctrl_begin(&mut self) -> Result<()> {
702        self.adj_ctrl_checks(None)?;
703        self.ctrl_stack.push(vec![]);
704        self.ctrl_list_is_valid = false;
705        Ok(())
706    }
707
708    pub fn ctrl_end(&mut self) -> Result<()> {
709        self.adj_ctrl_checks(None)?;
710        match self.ctrl_stack.pop() {
711            Some(stack) => {
712                if !stack.is_empty() {
713                    Err(KetError::ControlStackNotEmpty)
714                } else {
715                    self.ctrl_list_is_valid = false;
716                    if self.ctrl_stack.is_empty() {
717                        Err(KetError::ControlStackRemovePrimary)
718                    } else {
719                        Ok(())
720                    }
721                }
722            }
723            None => Err(KetError::ControlStackRemovePrimary),
724        }
725    }
726
727    pub fn instructions(&self) -> &[Instruction<LogicalQubit>] {
728        &self.logical_circuit.instructions
729    }
730
731    pub fn instructions_json(&self) -> String {
732        serde_json::to_string(&self.instructions()).unwrap()
733    }
734
735    pub fn isa_instructions(&self) -> Option<&[Instruction<PhysicalQubit>]> {
736        self.physical_circuit
737            .as_ref()
738            .map(|c| c.instructions.as_ref())
739    }
740
741    pub fn isa_instructions_json(&self) -> String {
742        serde_json::to_string(&self.isa_instructions()).unwrap()
743    }
744
745    pub fn metadata(&self) -> Metadata {
746        Metadata {
747            logical_gate_count: self.logical_circuit.gate_count.clone(),
748            logical_circuit_depth: self.logical_circuit.depth(),
749            physical_gate_count: self
750                .physical_circuit
751                .as_ref()
752                .map(|circuit| circuit.gate_count.clone()),
753            physical_circuit_depth: self
754                .physical_circuit
755                .as_ref()
756                .map(|circuit| circuit.depth()),
757            allocated_qubits: self.allocated_qubits,
758            terminated: self.terminated,
759            decomposition: self.decomposition_stats.clone(),
760        }
761    }
762}