1use std::collections::{HashMap, HashSet};
2
3use crate::{
4 expression::Expression,
5 instruction::{Gate, GateModifier, Qubit},
6 pickleable_new,
7 quil::Quil,
8};
9
10#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone)]
12pub enum DefGateSequenceExpansionError {
13 #[error("gate sequence expected {expected} arguments, found {found}")]
14 ParameterCount { expected: usize, found: usize },
15 #[error("cyclic sequence gate definition detected: {0:?}")]
16 CyclicSequenceGateDefinition(Vec<String>),
17 #[error(
18 "SEQUENCE gate expected to be applied to {expected} qubit{}, \
19 but was applied to {found}",
20 if *expected == 1 { "" } else { "s" },
21 )]
22 QubitCount { expected: usize, found: usize },
23 #[error("expected fixed qubit argument, found {}", .0.to_quil_or_debug())]
24 NonFixedQubitArgument(Qubit),
25 #[error(
26 "gate modifiers on invocations of sequence gate definitions are currently unsupported, \
27 but found {0:?}"
28 )]
29 GateModifiersUnsupported(Vec<GateModifier>),
30 #[error("gate sequence elements must reference parameters, but found {}", .0.to_quil_or_debug())]
34 InvalidGateSequenceElementQubit(Qubit),
35 #[error("qubit variable {0} in gate sequence is undefined")]
39 UndefinedGateSequenceElementQubit(String),
40}
41
42#[derive(Clone, Debug, PartialEq, Eq, Hash)]
44#[cfg_attr(feature = "stubs", pyo3_stub_gen::derive::gen_stub_pyclass)]
45#[cfg_attr(
46 feature = "python",
47 pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
48)]
49pub struct DefGateSequence {
50 pub(crate) qubits: Vec<String>,
52 pub(crate) gates: Vec<Gate>,
54}
55
56pickleable_new! {
57 impl DefGateSequence {
58 pub fn try_new(qubits: Vec<String>, gates: Vec<Gate>) -> Result<DefGateSequence, DefGateSequenceError> {
65 let (gates, qubits) = validate_defgate_as_sequence_elements(gates, qubits)?;
66 Ok(Self { qubits, gates })
67 }
68 }
69}
70
71impl DefGateSequence {
72 pub(crate) fn expand(
73 &self,
74 gate_parameter_arguments: HashMap<String, Expression>,
75 qubit_arguments: Vec<Qubit>,
76 ) -> Result<Vec<Gate>, DefGateSequenceExpansionError> {
77 if qubit_arguments.len() != self.qubits.len() {
78 return Err(DefGateSequenceExpansionError::QubitCount {
79 expected: self.qubits.len(),
80 found: qubit_arguments.len(),
81 });
82 }
83
84 let fixed_qubit_arguments = qubit_arguments
85 .into_iter()
86 .map(|qubit| {
87 if let Qubit::Fixed(fixed_qubit) = qubit {
88 Ok(fixed_qubit)
89 } else {
90 Err(DefGateSequenceExpansionError::NonFixedQubitArgument(qubit))
91 }
92 })
93 .collect::<Result<Vec<u64>, _>>()?;
94
95 let qubit_argument_map: HashMap<_, _> = self
96 .qubits
97 .iter()
98 .zip(fixed_qubit_arguments.iter())
99 .map(|(qubit_variable, fixed_qubit)| {
100 (qubit_variable.clone(), Qubit::Fixed(*fixed_qubit))
101 })
102 .collect();
103
104 self.gates
105 .iter()
106 .map(|gate| {
107 let gate_parameters = gate
108 .parameters
109 .iter()
110 .map(|parameter| parameter.substitute_variables(&gate_parameter_arguments))
111 .collect::<Vec<_>>();
112
113 let gate_qubits = gate
114 .qubits
115 .iter()
116 .map(|qubit| {
117 if let Qubit::Variable(qubit_variable) = qubit {
118 qubit_argument_map.get(qubit_variable)
119 .map_or_else(||
120 Err(DefGateSequenceExpansionError::UndefinedGateSequenceElementQubit(qubit_variable.clone())),
121 |qubit| Ok(qubit.clone())
122 )
123 } else {
124 Err(DefGateSequenceExpansionError::InvalidGateSequenceElementQubit(qubit.clone()))
126 }
127 })
128 .collect::<Result<Vec<Qubit>, _>>()?;
129
130 Ok(Gate {
133 name: gate.name.clone(),
134 parameters: gate_parameters,
135 qubits: gate_qubits,
136 modifiers: gate.modifiers.clone(),
137 })
138 })
139 .collect()
140 }
141}
142
143fn validate_defgate_as_sequence_elements(
144 gates: Vec<Gate>,
145 qubit_parameters: Vec<String>,
146) -> Result<(Vec<Gate>, Vec<String>), DefGateSequenceError> {
147 if qubit_parameters.is_empty() {
148 return Err(DefGateSequenceError::AtLeastOneQubitParameterRequired);
149 }
150 let qubit_parameter_set: HashSet<_> = qubit_parameters.iter().collect();
151
152 gates.iter().enumerate().try_for_each(|(i, gate)| {
153 gate.qubits.iter().enumerate().try_for_each(|(j, qubit)| {
154 if let Qubit::Variable(argument) = qubit {
155 if qubit_parameter_set.contains(argument) {
156 Ok(())
157 } else {
158 Err(DefGateSequenceError::UndefinedGateSequenceElementQubit {
159 gate_index: i,
160 qubit_argument_index: j,
161 argument_name: argument.clone(),
162 })
163 }
164 } else {
165 Err(DefGateSequenceError::InvalidGateSequenceElementQubit {
166 gate_index: i,
167 qubit_argument_index: j,
168 qubit: qubit.clone(),
169 })
170 }
171 })
172 })?;
173 Ok((gates, qubit_parameters))
174}
175
176#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone)]
178pub enum DefGateSequenceError {
179 #[error(
180 "\"{argument_name}\" is undefined at qubit argument {qubit_argument_index} of gate {gate_index}"
181 )]
182 UndefinedGateSequenceElementQubit {
183 gate_index: usize,
184 qubit_argument_index: usize,
185 argument_name: String,
186 },
187 #[error("DEFGATE AS SEQUENCE elements must be gates with qubit arguments, found {} at qubit argument {qubit_argument_index} of gate {gate_index}", qubit.to_quil_or_debug())]
188 InvalidGateSequenceElementQubit {
189 gate_index: usize,
190 qubit_argument_index: usize,
191 qubit: Qubit,
192 },
193 #[error("DEFGATE AS SEQUENCE must have at least one qubit parameter")]
194 AtLeastOneQubitParameterRequired,
195}