microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.25.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

library/std/src/Std/InternalHelpers.qs

228lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4
5import Std.Arrays.*;
6import Std.Core.*;
7import Std.Math.*;
8import Std.Intrinsic.*;
9import QIR.Intrinsic.*;
10
11internal operation CH(control : Qubit, target : Qubit) : Unit is Adj {
12 within {
13 S(target);
14 H(target);
15 T(target);
16 } apply {
17 CNOT(control, target);
18 }
19}
20
21internal operation CCH(control1 : Qubit, control2 : Qubit, target : Qubit) : Unit is Adj {
22 within {
23 S(target);
24 H(target);
25 T(target);
26 } apply {
27 CCNOT(control1, control2, target);
28 }
29}
30
31internal operation ApplyGlobalPhase(theta : Double) : Unit is Ctl + Adj {
32 body ... {
33 ControllableGlobalPhase(theta);
34 }
35 adjoint ... {
36 ControllableGlobalPhase(-theta);
37 }
38}
39
40// Global phase is not relevant for physical systems, but controlled global phase is physical. We use
41// the Rz gate to implement controlled global phase physically, and then correct for the extra global phase it
42// introduces in simulation using additional calls to the simulation-only global phase intrinsic.
43// We use a separate operation for this controlled case to avoid recursive calls to the same operation
44// that can interfere with runtime capabilities analysis.
45internal operation ControllableGlobalPhase(theta : Double) : Unit is Ctl {
46 body ... {
47 GlobalPhase([], theta);
48 }
49 controlled (ctls, ...) {
50 if Length(ctls) == 0 {
51 GlobalPhase([], theta);
52 } else {
53 Controlled Rz(ctls[1...], (theta, ctls[0]));
54 GlobalPhase(ctls[1...], theta / 2.0);
55 }
56 }
57}
58
59// Global phase intrinsic, which only has affect in simulation and is a no-op otherwise.
60internal operation GlobalPhase(ctls : Qubit[], theta : Double) : Unit {
61 body intrinsic;
62}
63
64internal operation CRz(control : Qubit, theta : Double, target : Qubit) : Unit is Adj {
65 Rz(theta / 2.0, target);
66 CNOT(control, target);
67 Rz(-theta / 2.0, target);
68 CNOT(control, target);
69}
70
71internal operation CS(control : Qubit, target : Qubit) : Unit is Adj + Ctl {
72 T(control);
73 T(target);
74 CNOT(control, target);
75 Adjoint T(target);
76 CNOT(control, target);
77}
78
79internal operation CT(control : Qubit, target : Qubit) : Unit is Adj {
80 let angle = PI() / 8.0;
81 Rz(angle, control);
82 Rz(angle, target);
83 CNOT(control, target);
84 Adjoint Rz(angle, target);
85 CNOT(control, target);
86 // This decomposition for controlled-T introduces a global phase (due to the unmatched call to Rz from above).
87 // We correct for this global phase in simulation, which is a no-op on hardware.
88 ApplyGlobalPhase(angle / 2.0);
89}
90
91internal operation EntangleForJointMeasure(basis : Pauli, aux : Qubit, qubit : Qubit) : Unit {
92 if basis == PauliX {
93 __quantum__qis__cx__body(aux, qubit);
94 } elif basis == PauliZ {
95 __quantum__qis__cz__body(aux, qubit);
96 } elif basis == PauliY {
97 __quantum__qis__cy__body(aux, qubit);
98 }
99}
100
101/// Collects the given list of control qubits into one or two of the given auxiliary qubits, using
102/// all but the last qubits in the auxiliary list as scratch qubits. The auxiliary list must be
103/// big enough to accommodate the data, so it is usually smaller than controls list by number of
104/// qubits needed for the eventual controlled unitary application. The passed adjustment value is
105/// used to ensure the right number of auxiliary qubits are processed.
106///
107/// For example, if the controls list is 6 qubits, the auxiliary list must be 5 qubits, and the
108/// state from the 6 control qubits will be collected into the last qubit of the auxiliary array.
109internal operation CollectControls(ctls : Qubit[], aux : Qubit[], adjustment : Int) : Unit is Adj {
110 // First collect the controls into the first part of the auxiliary list.
111 for i in 0..2..(Length(ctls) - 2) {
112 CCNOT(ctls[i], ctls[i + 1], aux[i / 2]);
113 }
114 // Then collect the auxiliary qubits in the first part of the list forward into the last
115 // qubit of the auxiliary list. The adjustment is used to allow the caller to reduce or increase
116 // the number of times this is run based on the eventual number of control qubits needed.
117 for i in 0..((Length(ctls) / 2) - 2 - adjustment) {
118 CCNOT(aux[i * 2], aux[(i * 2) + 1], aux[i + Length(ctls) / 2]);
119 }
120}
121
122/// When collecting controls, if there is an uneven number of original control qubits then the
123/// last control and the second to last auxiliary will be collected into the last auxiliary.
124internal operation AdjustForSingleControl(ctls : Qubit[], aux : Qubit[]) : Unit is Adj {
125 if Length(ctls) % 2 != 0 {
126 CCNOT(ctls[Length(ctls) - 1], aux[Length(ctls) - 3], aux[Length(ctls) - 2]);
127 }
128}
129
130internal operation PhaseCCX(control1 : Qubit, control2 : Qubit, target : Qubit) : Unit is Adj {
131 // https://arxiv.org/pdf/1210.0974.pdf#page=2
132 H(target);
133 CNOT(target, control1);
134 CNOT(control1, control2);
135 T(control2);
136 Adjoint T(control1);
137 T(target);
138 CNOT(target, control1);
139 CNOT(control1, control2);
140 Adjoint T(control2);
141 CNOT(target, control2);
142 H(target);
143}
144
145internal operation CCZ(control1 : Qubit, control2 : Qubit, target : Qubit) : Unit is Adj {
146 within {
147 MapPauliAxis(PauliX, PauliZ, target);
148 } apply {
149 CCNOT(control1, control2, target);
150 }
151}
152
153internal operation CCY(control1 : Qubit, control2 : Qubit, target : Qubit) : Unit is Adj {
154 within {
155 MapPauliAxis(PauliX, PauliY, target);
156 } apply {
157 CCNOT(control1, control2, target);
158 }
159}
160
161internal operation CRxx(control : Qubit, theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit {
162 within {
163 MapPauliAxis(PauliZ, PauliX, qubit0);
164 MapPauliAxis(PauliZ, PauliX, qubit1);
165 } apply {
166 CRzz(control, theta, qubit0, qubit1);
167 }
168}
169
170internal operation CRyy(control : Qubit, theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit {
171 within {
172 MapPauliAxis(PauliZ, PauliY, qubit0);
173 MapPauliAxis(PauliZ, PauliY, qubit1);
174 } apply {
175 CRzz(control, theta, qubit0, qubit1);
176 }
177}
178
179internal operation CRzz(control : Qubit, theta : Double, qubit0 : Qubit, qubit1 : Qubit) : Unit {
180 within {
181 CNOT(qubit1, qubit0);
182 } apply {
183 Controlled Rz([control], (theta, qubit0));
184 }
185}
186
187internal function IndicesOfNonIdentity(paulies : Pauli[]) : Int[] {
188 mutable indices = [];
189 for i in 0..Length(paulies) - 1 {
190 if (paulies[i] != PauliI) {
191 set indices += [i];
192 }
193 }
194 indices
195}
196
197internal function RemovePauliI(paulis : Pauli[], qubits : Qubit[]) : (Pauli[], Qubit[]) {
198 let indices = IndicesOfNonIdentity(paulis);
199 let newPaulis = Subarray(indices, paulis);
200 let newQubits = Subarray(indices, qubits);
201 return (newPaulis, newQubits);
202}
203
204internal operation SpreadZ(from : Qubit, to : Qubit[]) : Unit is Adj {
205 let targets = GetSpread(from, to);
206 for (ctl, tgt) in targets {
207 CNOT(ctl, tgt);
208 }
209}
210
211internal function GetSpread(from : Qubit, to : Qubit[]) : (Qubit, Qubit)[] {
212 mutable queue = [(from, to)];
213 mutable targets = [];
214 while Length(queue) > 0 {
215 mutable (next, rest) = (queue[0], queue[1...]);
216 set queue = rest;
217 let (next_from, next_to) = next;
218 if Length(next_to) > 0 {
219 set targets = [(next_to[0], next_from)] + targets;
220 if Length(next_to) > 1 {
221 let half = Length(next_to) / 2;
222 set queue = [(next_from, next_to[1..half]), (next_to[0], next_to[(half + 1)...])] + rest;
223 }
224 }
225 }
226
227 targets
228}
229