microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/fix-2145

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

compiler/qsc_circuit/src/operations.rs

181lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#[cfg(test)]
5mod tests;
6
7use miette::Diagnostic;
8use qsc_hir::{
9 hir::{Item, ItemKind},
10 ty::{Prim, Ty},
11};
12use thiserror::Error;
13
14#[derive(Clone, Debug, Diagnostic, Error)]
15pub enum Error {
16 #[error("expression does not evaluate to an operation that takes qubit parameters")]
17 #[diagnostic(code("Qsc.Circuit.NoCircuitForOperation"))]
18 #[diagnostic(help(
19 "provide the name of a callable or a lambda expression that only takes qubits as parameters"
20 ))]
21 NoQubitParameters,
22 #[error("cannot generate circuit for controlled invocation")]
23 #[diagnostic(code("Qsc.Circuit.ControlledUnsupported"))]
24 #[diagnostic(help(
25 "controlled invocations are not currently supported. consider wrapping the invocation in a lambda expression"
26 ))]
27 ControlledUnsupported,
28}
29
30/// If the item is a callable, returns the information that would
31/// be needed to generate a circuit for it.
32///
33/// If the item is not a callable, returns `None`.
34/// If the callable takes any non-qubit parameters, returns `None`.
35///
36/// If the callable only takes qubit parameters (including qubit arrays) or no parameters:
37///
38/// The first element of the return tuple is a vector,
39/// where each element corresponds to a parameter, and the
40/// value is the number of dimensions of the parameter.
41///
42/// For example, for input parameters
43/// `(Qubit, Qubit[][], Qubit[])` the parameter info is `vec![0, 2, 1]`.
44///
45/// The second element of the return tuple is the total number of qubits that would
46/// need be allocated to run this operation for the purposes of circuit generation.
47#[must_use]
48pub fn qubit_param_info(item: &Item) -> Option<(Vec<u32>, u32)> {
49 if let ItemKind::Callable(decl) = &item.kind {
50 if decl.input.ty == Ty::UNIT {
51 // Support no parameters by allocating 0 qubits.
52 return Some((vec![], 0));
53 }
54
55 let (qubit_param_dimensions, total_num_qubits) = get_qubit_param_info(&decl.input.ty);
56
57 if !qubit_param_dimensions.is_empty() {
58 return Some((qubit_param_dimensions, total_num_qubits));
59 }
60 }
61 None
62}
63
64/// Returns an entry expression to directly invoke the operation
65/// for the purposes of generating a circuit for it.
66///
67/// `operation_expr` is the source for the expression that refers to the operation,
68/// e.g. "Test.Foo" or "qs => H(qs[0])".
69///
70/// If the item is not a callable, returns `None`.
71/// If the callable takes any non-qubit parameters, returns `None`.
72pub fn entry_expr_for_qubit_operation(
73 item: &Item,
74 functor_app: qsc_data_structures::functors::FunctorApp,
75 operation_expr: &str,
76) -> Result<String, Error> {
77 if functor_app.controlled > 0 {
78 return Err(Error::ControlledUnsupported);
79 }
80
81 if let Some((qubit_param_dimensions, total_num_qubits)) = qubit_param_info(item) {
82 return Ok(operation_circuit_entry_expr(
83 operation_expr,
84 &qubit_param_dimensions,
85 total_num_qubits,
86 ));
87 }
88
89 Err(Error::NoQubitParameters)
90}
91
92/// Generates the entry expression to call the operation described by `params`.
93/// The expression allocates qubits and invokes the operation.
94#[must_use]
95fn operation_circuit_entry_expr(
96 operation_expr: &str,
97 qubit_param_dimensions: &[u32],
98 total_num_qubits: u32,
99) -> String {
100 let alloc_qubits = format!("use qs = Qubit[{total_num_qubits}];");
101
102 let mut qs_start = 0;
103 let mut call_args = vec![];
104 for dim in qubit_param_dimensions {
105 let dim = *dim;
106 let qs_len = NUM_QUBITS.pow(dim);
107 // Q# ranges are end-inclusive
108 let qs_end = qs_start + qs_len - 1;
109 if dim == 0 {
110 call_args.push(format!("qs[{qs_start}]"));
111 } else {
112 // Array argument - use a range to index
113 let mut call_arg = format!("qs[{qs_start}..{qs_end}]");
114 for _ in 1..dim {
115 // Chunk the array for multi-dimensional array arguments
116 call_arg = format!("Microsoft.Quantum.Arrays.Chunks({NUM_QUBITS}, {call_arg})");
117 }
118 call_args.push(call_arg);
119 }
120 qs_start = qs_end + 1;
121 }
122
123 let call_args = call_args.join(", ");
124
125 // We don't reset the qubits since we don't want reset gates
126 // included in circuit output.
127 // We also don't measure the qubits but we have to return a result
128 // array to satisfy Base Profile.
129 format!(
130 r#"{{
131 {alloc_qubits}
132 ({operation_expr})({call_args});
133 let r: Result[] = [];
134 r
135 }}"#
136 )
137}
138
139/// The number of qubits to allocate for each qubit array
140/// in the operation arguments.
141const NUM_QUBITS: u32 = 2;
142
143fn get_qubit_param_info(input: &Ty) -> (Vec<u32>, u32) {
144 match input {
145 Ty::Prim(Prim::Qubit) => return (vec![0], 1),
146 Ty::Array(ty) => {
147 if let Some(element_dim) = get_array_dimension(ty) {
148 let dim = element_dim + 1;
149 return (vec![dim], NUM_QUBITS.pow(dim));
150 }
151 }
152 Ty::Tuple(tys) => {
153 let params = tys.iter().map(get_array_dimension).collect::<Vec<_>>();
154
155 if params.iter().all(Option::is_some) {
156 return params.into_iter().map(Option::unwrap).fold(
157 (vec![], 0),
158 |(mut dims, mut total_qubits), dim| {
159 dims.push(dim);
160 total_qubits += NUM_QUBITS.pow(dim);
161 (dims, total_qubits)
162 },
163 );
164 }
165 }
166 _ => {}
167 }
168 (vec![], 0)
169}
170
171/// If `Ty` is a qubit or a qubit array, returns the number of dimensions of the array.
172/// A qubit is considered to be a 0-dimensional array.
173/// For example, for a `Qubit` it returns `Some(0)`, for a `Qubit[][]` it returns `Some(2)`.
174/// For a non-qubit type, returns `None`.
175fn get_array_dimension(input: &Ty) -> Option<u32> {
176 match input {
177 Ty::Prim(Prim::Qubit) => Some(0),
178 Ty::Array(ty) => get_array_dimension(ty).map(|d| d + 1),
179 _ => None,
180 }
181}
182