1use std::{
2 collections::{HashMap, HashSet},
3 str::FromStr,
4};
5
6use indexmap::IndexMap;
7use numpy::{PyArray2, ToPyArray};
8use quil_rs::{
9 instruction::{Instruction, QubitPlaceholder, TargetPlaceholder, Waveform},
10 program::{
11 analysis::{ControlFlowGraph, ControlFlowGraphOwned},
12 Calibrations, FrameSet, MemoryRegion,
13 },
14 Program,
15};
16use rigetti_pyo3::{
17 create_init_submodule, impl_as_mut_for_wrapper, impl_from_str, impl_parse, impl_repr,
18 num_complex::Complex64,
19 py_wrap_error, py_wrap_type,
20 pyo3::{
21 exceptions::PyValueError,
22 prelude::*,
23 types::{PyBytes, PyFunction, PyList},
24 IntoPy,
25 },
26 wrap_error, PyTryFrom, PyWrapper, PyWrapperMut, ToPython, ToPythonError,
27};
28
29use crate::{
30 impl_eq, impl_to_quil,
31 instruction::{
32 PyDeclaration, PyGateDefinition, PyInstruction, PyMemoryReference, PyPragma, PyQubit,
33 PyTarget, PyWaveform,
34 },
35};
36
37use self::{
38 analysis::{PyBasicBlock, PyControlFlowGraph},
39 scheduling::{PyScheduleSeconds, PyScheduleSecondsItem, PyTimeSpanSeconds},
40 source_map::{
41 PyCalibrationExpansion, PyCalibrationExpansionSourceMap,
42 PyCalibrationExpansionSourceMapEntry, PyCalibrationSource, PyMaybeCalibrationExpansion,
43 PyProgramCalibrationExpansion, PyProgramCalibrationExpansionSourceMap,
44 PyProgramCalibrationExpansionSourceMapEntry,
45 },
46};
47pub use self::{calibration::PyCalibrationSet, frame::PyFrameSet, memory::PyMemoryRegion};
48
49mod analysis;
50mod calibration;
51mod frame;
52mod memory;
53mod scheduling;
54mod source_map;
55
56wrap_error!(ProgramError(quil_rs::program::ProgramError));
57py_wrap_error!(quil, ProgramError, PyProgramError, PyValueError);
58
59py_wrap_type! {
60 #[derive(Debug, PartialEq)]
61 #[pyo3(module = "quil.program")]
63 PyProgram(Program) as "Program"
64}
65impl_as_mut_for_wrapper!(PyProgram);
66impl_repr!(PyProgram);
67impl_from_str!(PyProgram, ProgramError);
68impl_parse!(PyProgram);
69impl_to_quil!(PyProgram);
70impl_eq!(PyProgram);
71
72impl Default for PyProgram {
73 fn default() -> Self {
74 Self::new()
75 }
76}
77
78#[pymethods]
79impl PyProgram {
80 #[new]
81 pub fn new() -> Self {
82 Self(Program::default())
83 }
84
85 pub fn clone_without_body_instructions(&self) -> Self {
86 Self(self.as_inner().clone_without_body_instructions())
87 }
88
89 pub fn copy(&self) -> Self {
90 Self(self.as_inner().clone())
91 }
92
93 pub fn control_flow_graph(&self) -> PyControlFlowGraph {
94 ControlFlowGraphOwned::from(ControlFlowGraph::from(self.as_inner())).into()
95 }
96
97 pub fn expand_calibrations_with_source_map(&self) -> PyResult<PyProgramCalibrationExpansion> {
98 let expansion = self
99 .as_inner()
100 .expand_calibrations_with_source_map()
101 .map_err(ProgramError::from)
102 .map_err(ProgramError::to_py_err)?;
103 Ok(expansion.into())
104 }
105
106 #[getter]
107 pub fn body_instructions<'a>(&self, py: Python<'a>) -> PyResult<&'a PyList> {
108 Ok(PyList::new(
109 py,
110 self.as_inner()
111 .body_instructions()
112 .map(|i| i.to_python(py))
113 .collect::<PyResult<Vec<PyInstruction>>>()?,
114 ))
115 }
116
117 #[getter]
118 pub fn calibrations(&self, py: Python<'_>) -> PyResult<PyCalibrationSet> {
119 self.as_inner().calibrations.to_python(py)
120 }
121
122 #[setter]
123 pub fn set_calibrations(
124 &mut self,
125 py: Python<'_>,
126 calibrations: PyCalibrationSet,
127 ) -> PyResult<()> {
128 let program = self.as_inner_mut();
129 program.calibrations = Calibrations::py_try_from(py, &calibrations)?;
130 Ok(())
131 }
132
133 #[getter]
134 pub fn waveforms(&self, py: Python<'_>) -> PyResult<IndexMap<String, PyWaveform>> {
135 self.as_inner().waveforms.to_python(py)
136 }
137
138 #[setter]
139 pub fn set_waveforms(
140 &mut self,
141 py: Python<'_>,
142 waveforms: IndexMap<String, PyWaveform>,
143 ) -> PyResult<()> {
144 self.as_inner_mut().waveforms = IndexMap::<String, Waveform>::py_try_from(py, &waveforms)?;
145 Ok(())
146 }
147
148 #[getter]
149 pub fn frames(&self, py: Python<'_>) -> PyResult<PyFrameSet> {
150 self.as_inner().frames.to_python(py)
151 }
152
153 #[setter]
154 pub fn set_frames(&mut self, py: Python<'_>, frames: PyFrameSet) -> PyResult<()> {
155 self.as_inner_mut().frames = FrameSet::py_try_from(py, &frames)?;
156 Ok(())
157 }
158
159 #[getter]
160 pub fn memory_regions(&self, py: Python<'_>) -> PyResult<IndexMap<String, PyMemoryRegion>> {
161 self.as_inner()
162 .memory_regions
163 .iter()
164 .map(|(name, memory_region)| Ok((name.to_python(py)?, memory_region.to_python(py)?)))
165 .collect()
166 }
167
168 #[setter]
169 pub fn set_memory_regions(
170 &mut self,
171 py: Python<'_>,
172 memory_regions: IndexMap<String, PyMemoryRegion>,
173 ) -> PyResult<()> {
174 self.as_inner_mut().memory_regions =
175 IndexMap::<String, MemoryRegion>::py_try_from(py, &memory_regions)?;
176 Ok(())
177 }
178
179 #[getter]
180 pub fn declarations(&self, py: Python<'_>) -> PyResult<HashMap<String, PyDeclaration>> {
183 self.as_inner()
184 .to_instructions()
185 .iter()
186 .filter_map(|inst| match inst {
187 Instruction::Declaration(declaration) => Some(declaration),
188 _ => None,
189 })
190 .map(|declaration| Ok((declaration.name.clone(), declaration.to_python(py)?)))
191 .collect()
192 }
193
194 #[getter]
195 pub fn gate_definitions(&self, py: Python<'_>) -> PyResult<IndexMap<String, PyGateDefinition>> {
196 self.as_inner()
197 .gate_definitions
198 .iter()
199 .map(|(name, gate_def)| Ok((name.to_python(py)?, gate_def.to_python(py)?)))
200 .collect()
201 }
202
203 #[setter]
204 pub fn set_gate_definitions(&mut self, definitions: IndexMap<String, PyGateDefinition>) {
205 self.as_inner_mut().gate_definitions = definitions
206 .into_iter()
207 .map(|(name, gate_def)| (name, gate_def.into_inner()))
208 .collect();
209 }
210
211 #[getter]
212 pub fn pragma_extern_map(&self) -> IndexMap<Option<String>, PyPragma> {
213 self.as_inner()
214 .extern_pragma_map
215 .clone()
216 .into_iter()
217 .map(|(k, v)| (k, v.into()))
218 .collect()
219 }
220
221 pub fn dagger(&self) -> PyResult<Self> {
222 self.as_inner()
223 .dagger()
224 .map(PyProgram::from)
225 .map_err(ProgramError::from)
226 .map_err(ProgramError::to_py_err)
227 }
228
229 pub fn expand_calibrations(&self) -> PyResult<Self> {
230 self.as_inner()
231 .expand_calibrations()
232 .map(PyProgram::from)
233 .map_err(ProgramError::from)
234 .map_err(ProgramError::to_py_err)
235 }
236
237 pub fn into_simplified(&self) -> PyResult<Self> {
238 self.as_inner()
239 .into_simplified()
240 .map(PyProgram::from)
241 .map_err(ProgramError::from)
242 .map_err(ProgramError::to_py_err)
243 }
244
245 pub fn get_used_qubits(&self, py: Python<'_>) -> PyResult<HashSet<PyQubit>> {
246 self.as_inner()
247 .get_used_qubits()
248 .iter()
249 .map(|q| q.to_python(py))
250 .collect()
251 }
252
253 pub fn add_instruction(&mut self, instruction: PyInstruction) {
254 self.as_inner_mut().add_instruction(instruction.into())
255 }
256
257 pub fn add_instructions(&mut self, instructions: Vec<PyInstruction>) {
258 self.as_inner_mut()
259 .add_instructions(instructions.into_iter().map(Into::into))
260 }
261
262 pub fn to_instructions(&self, py: Python<'_>) -> PyResult<Vec<PyInstruction>> {
263 self.as_inner()
264 .to_instructions()
265 .iter()
266 .map(|i| i.to_python(py))
267 .collect()
268 }
269
270 pub fn to_unitary(&self, py: Python<'_>, n_qubits: u64) -> PyResult<Py<PyArray2<Complex64>>> {
271 Ok(self
272 .as_inner()
273 .to_unitary(n_qubits)
274 .map_err(ProgramError::from)
275 .map_err(ProgramError::to_py_err)?
276 .to_pyarray(py)
277 .to_owned())
278 }
279
280 pub fn filter_instructions(&self, py: Python, predicate: Py<PyFunction>) -> PyResult<Self> {
281 let filtered = self.as_inner().filter_instructions(|inst| {
282 Python::with_gil(|py| {
283 predicate
284 .call1(py, (inst.to_python(py).unwrap(),))
285 .unwrap_or_else(|err| panic!("predicate function returned an error: {err}"))
286 .extract(py)
287 .unwrap_or_else(|err| panic!("predicate function must return a bool: {err}"))
288 })
289 });
290 filtered.to_python(py)
291 }
292
293 pub fn resolve_placeholders(&mut self) {
294 self.as_inner_mut().resolve_placeholders();
295 }
296
297 pub fn wrap_in_loop(
298 &self,
299 loop_count_reference: PyMemoryReference,
300 start_target: PyTarget,
301 end_target: PyTarget,
302 iterations: u32,
303 ) -> Self {
304 PyProgram(self.as_inner().wrap_in_loop(
305 loop_count_reference.into_inner(),
306 start_target.into_inner(),
307 end_target.into_inner(),
308 iterations,
309 ))
310 }
311
312 #[pyo3(signature = (*, target_resolver = None, qubit_resolver = None))]
317 pub fn resolve_placeholders_with_custom_resolvers(
318 &mut self,
319 target_resolver: Option<Py<PyFunction>>,
320 qubit_resolver: Option<Py<PyFunction>>,
321 ) {
322 #[allow(clippy::type_complexity)]
323 let rs_qubit_resolver: Box<dyn Fn(&QubitPlaceholder) -> Option<u64>> =
324 if let Some(resolver) = qubit_resolver {
325 Box::new(move |placeholder: &QubitPlaceholder| -> Option<u64> {
326 Python::with_gil(|py| {
327 let resolved_qubit = resolver
328 .call1(
329 py,
330 (placeholder
331 .to_python(py)
332 .expect("QubitPlaceholder.to_python() should be infallible"),),
333 )
334 .unwrap_or_else(|err| {
335 panic!("qubit_resolver returned an error: {err}")
336 });
337
338 resolved_qubit.extract(py).unwrap_or_else(|err| {
339 panic!("qubit_resolver must return None or int: {err}")
340 })
341 })
342 })
343 } else {
344 self.as_inner().default_qubit_resolver()
345 };
346
347 #[allow(clippy::type_complexity)]
348 let rs_target_resolver: Box<dyn Fn(&TargetPlaceholder) -> Option<String>> =
349 if let Some(resolver) = target_resolver {
350 Box::new(move |placeholder: &TargetPlaceholder| -> Option<String> {
351 Python::with_gil(|py| {
352 let resolved_label = resolver
353 .call1(
354 py,
355 (placeholder
356 .to_python(py)
357 .expect("TargetPlaceholder.to_python() should be infallibe"),),
358 )
359 .unwrap_or_else(|err| {
360 panic!("label_resolver returned an error: {err}")
361 });
362
363 resolved_label.extract(py).unwrap_or_else(|err| {
364 panic!("label_resolver must return None or str: {err}")
365 })
366 })
367 })
368 } else {
369 self.as_inner().default_target_resolver()
370 };
371
372 self.as_inner_mut()
373 .resolve_placeholders_with_custom_resolvers(rs_target_resolver, rs_qubit_resolver);
374 }
375
376 pub fn __add__(&self, py: Python<'_>, rhs: Self) -> PyResult<Self> {
377 let new = self.as_inner().clone() + rhs.as_inner().clone();
378 new.to_python(py)
379 }
380
381 pub fn __iadd__(&mut self, rhs: Self) {
382 *self.as_inner_mut() += rhs.as_inner().clone()
383 }
384
385 pub fn __getstate__(&self, py: Python<'_>) -> PyResult<Py<PyBytes>> {
390 Ok(PyBytes::new(py, self.to_quil()?.as_bytes()).into_py(py))
391 }
392
393 pub fn __setstate__(&mut self, py: Python<'_>, state: &PyBytes) -> PyResult<()> {
394 *self = Program::from_str(std::str::from_utf8(state.as_bytes())?)
395 .map_err(ProgramError::from)
396 .map_err(ProgramError::to_py_err)?
397 .to_python(py)?;
398 Ok(())
399 }
400}
401
402create_init_submodule! {
403 classes: [
404 PyFrameSet,
405 PyProgram,
406 PyCalibrationExpansion,
407 PyCalibrationExpansionSourceMap,
408 PyCalibrationExpansionSourceMapEntry,
409 PyCalibrationSource,
410 PyMaybeCalibrationExpansion,
411 PyProgramCalibrationExpansion,
412 PyProgramCalibrationExpansionSourceMap,
413 PyProgramCalibrationExpansionSourceMapEntry,
414 PyCalibrationSet,
415 PyMemoryRegion,
416 PyBasicBlock,
417 PyControlFlowGraph,
418 PyScheduleSeconds,
419 PyScheduleSecondsItem,
420 PyTimeSpanSeconds
421 ],
422}