Skip to content

Commit e71aaa0

Browse files
authored
Low-ancilla incrementer
2 parents 1dcb4d2 + ddda326 commit e71aaa0

8 files changed

Lines changed: 728 additions & 7 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ jobs:
1919
- name: Install requirements
2020
run: |
2121
pip install pytest
22-
pip install qsharp==1.19.0
22+
pip install qdk==1.29.1
2323
- name: Run tests
2424
run: pytest

lib/qsharp.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"src/QuantumArithmetic/DM2004.qs",
1414
"src/QuantumArithmetic/GKDKH2021.qs",
1515
"src/QuantumArithmetic/JHHA2016.qs",
16+
"src/QuantumArithmetic/LAInc.qs",
1617
"src/QuantumArithmetic/LYTZW2013.qs",
1718
"src/QuantumArithmetic/LYY2021.qs",
1819
"src/QuantumArithmetic/MCT2017.qs",

lib/src/EstimateUtils.qs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
/// Runs operation on the given number of qubits.
2+
operation RunUnaryOp(n : Int, op : (Qubit[]) => Unit) : Unit {
3+
use a = Qubit[n];
4+
op(a);
5+
}
6+
7+
/// Runs controlled operation on the given number of qubits.
8+
operation RunUnaryOpCtl(n : Int, op : (Qubit[]) => Unit is Ctl) : Unit {
9+
use ctrl = Qubit[1];
10+
use a = Qubit[n];
11+
Controlled op(ctrl, (a));
12+
}
13+
114
operation BinaryOpExtraOut(n : Int, x_val : Int, y_val : Int, op : (Qubit[], Qubit[], Qubit[], Qubit) => Unit) : Int {
215
use x = Qubit[n];
316
use y = Qubit[n];
@@ -53,14 +66,14 @@ operation RunModExp(n : Int, op : (Qubit[], Qubit[], BigInt, BigInt) => Unit) :
5366
op(x_qubits, ans, a, N);
5467
}
5568

56-
operation RunRadix(n: Int, radix: Int, op : (Qubit[], Qubit[], Qubit[], Int, (Qubit[], Qubit[], Qubit[]) => Unit is Adj) => Unit is Adj, adder_op: (Qubit[], Qubit[], Qubit[]) => Unit is Adj) : Unit {
69+
operation RunRadix(n : Int, radix : Int, op : (Qubit[], Qubit[], Qubit[], Int, (Qubit[], Qubit[], Qubit[]) => Unit is Adj) => Unit is Adj, adder_op : (Qubit[], Qubit[], Qubit[]) => Unit is Adj) : Unit {
5770
use a = Qubit[n];
5871
use b = Qubit[n];
5972
use c = Qubit[n];
6073
op(a, b, c, radix, adder_op);
6174
}
6275

63-
operation RunRadixCarry(n: Int, radix: Int, op : (Qubit[], Qubit[], Qubit[], Int, (Qubit[], Qubit[], Qubit[], Qubit) => Unit is Adj) => Unit is Adj, adder_op: (Qubit[], Qubit[], Qubit[], Qubit) => Unit is Adj) : Unit {
76+
operation RunRadixCarry(n : Int, radix : Int, op : (Qubit[], Qubit[], Qubit[], Int, (Qubit[], Qubit[], Qubit[], Qubit) => Unit is Adj) => Unit is Adj, adder_op : (Qubit[], Qubit[], Qubit[], Qubit) => Unit is Adj) : Unit {
6477
use a = Qubit[n];
6578
use b = Qubit[n];
6679
use c = Qubit[n];

lib/src/QuantumArithmetic/LAInc.qs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/// Low-ancilla incrementer circuit.
2+
3+
4+
function Log2(x : Int) : Double {
5+
return Std.Math.Log(Std.Convert.IntAsDouble(x)) / Std.Math.LogOf2();
6+
}
7+
8+
/// Computes ans := CTO(x), where CTO(x) is number of trailing (least
9+
/// significant) "1" bits in register x before first zero bit. CTO(0)=0.
10+
/// ans must be prepared in zero state.
11+
operation CountTrailingOnes(x : Qubit[], ans : Qubit[]) : Unit is Ctl + Adj {
12+
body (...) {
13+
Controlled CountTrailingOnes([], (x, ans));
14+
}
15+
controlled (ctrl, ...) {
16+
let x_len = Length(x);
17+
let ctrl_len = Length(ctrl);
18+
if ctrl_len >= 2 {
19+
use anc = Qubit();
20+
within {
21+
AND(ctrl[0], ctrl[1], anc);
22+
} apply {
23+
Controlled CountTrailingOnes([anc] + ctrl[2..ctrl_len-1], (x, ans));
24+
}
25+
} elif (x_len == 1 and ctrl_len == 0) {
26+
CNOT(x[0], ans[0]);
27+
} elif (x_len == 1 and ctrl_len == 1) {
28+
CCNOT(ctrl[0], x[0], ans[0]);
29+
} elif (x_len == 2 and ctrl_len == 0) {
30+
X(x[1]);
31+
CCNOT(x[0], x[1], ans[0]);
32+
X(x[1]);
33+
CCNOT(x[0], x[1], ans[1]);
34+
} elif (x_len == 2 and ctrl_len == 1) {
35+
use x0 = Qubit();
36+
within {
37+
AND(ctrl[0], x[0], x0);
38+
} apply {
39+
X(x[1]);
40+
CCNOT(x0, x[1], ans[0]);
41+
X(x[1]);
42+
CCNOT(x0, x[1], ans[1]);
43+
}
44+
} else {
45+
let n : Int = Std.Math.Ceiling(Log2(x_len));
46+
let x_low = x[0..(1 <<< (n - 1))-1];
47+
let x_high = x[(1 <<< (n - 1))..x_len-1];
48+
if (x_len == 1 <<< n) {
49+
Std.Diagnostics.Fact(Length(ans) >= n + 1, "ans too small");
50+
Controlled CountTrailingOnes(ctrl, (x_low, ans[0..n-1]));
51+
Controlled CountTrailingOnes(ctrl + [ans[n-1]], (x_high, ans[0..n-2] + [ans[n]]));
52+
Controlled CNOT(ctrl, (ans[n], ans[n-1]));
53+
} else {
54+
Std.Diagnostics.Fact(Length(ans) >= n, "ans too small");
55+
Controlled CountTrailingOnes(ctrl, (x_low, ans[0..n-1]));
56+
Controlled CountTrailingOnes(ctrl + [ans[n-1]], (x_high, ans[0..n-2]));
57+
}
58+
}
59+
}
60+
}
61+
62+
/// Flips first `ctr` bits in `target`.
63+
/// If `ctr==0`, does nothing.
64+
/// If `ctr==Length(target)`, flips all bits.
65+
/// If `ctr>Length(target)`, behavior is undefined.
66+
operation FlipFirst(target : Qubit[], ctr : Qubit[]) : Unit is Ctl + Adj {
67+
body (...) {
68+
Controlled FlipFirst([], (target, ctr));
69+
}
70+
controlled (ctrl, ...) {
71+
let target_len = Length(target);
72+
let ctr_len = Length(ctr);
73+
let n = Std.Math.Floor(Log2(target_len)) + 1;
74+
let ctrl_len = Length(ctrl);
75+
if ctrl_len >= 2 {
76+
use anc = Qubit();
77+
within {
78+
AND(ctrl[0], ctrl[1], anc);
79+
} apply {
80+
Controlled FlipFirst([anc] + ctrl[2..ctrl_len-1], (target, ctr));
81+
}
82+
} elif (ctr_len > n) {
83+
// Counter too large, ignore highest qubits.
84+
Controlled FlipFirst(ctrl, (target, ctr[0..n-1]));
85+
} elif (ctr_len < n) {
86+
// Counter too small, can only affect prefix of target.
87+
Controlled FlipFirst(ctrl, (target[0..(1 <<< ctr_len)-2], ctr));
88+
} elif (target_len == 1 and ctrl_len == 0) {
89+
CNOT(ctr[0], target[0]);
90+
} elif (target_len == 1 and ctrl_len == 1) {
91+
CCNOT(ctrl[0], ctr[0], target[0]);
92+
} elif (target_len == 2 and ctrl_len == 0) {
93+
CNOT(ctr[0], target[0]);
94+
CNOT(ctr[1], target[0]);
95+
CNOT(ctr[1], target[1]);
96+
} elif (target_len == 2 and ctrl_len == 1) {
97+
CCNOT(ctrl[0], ctr[0], target[0]);
98+
CCNOT(ctrl[0], ctr[1], target[0]);
99+
CCNOT(ctrl[0], ctr[1], target[1]);
100+
} else {
101+
Std.Diagnostics.Fact(ctr_len == n, "");
102+
Std.Diagnostics.Fact(target_len >= (1 <<< (n - 1)), "");
103+
let target_low : Qubit[] = target[0..(1 <<< (n - 1))-1];
104+
let target_high : Qubit[] = target[(1 <<< (n - 1))..target_len-1];
105+
106+
Controlled ApplyToEachCA(ctrl + [ctr[n-1]], (X, target_low));
107+
if (Length(target_high) > 0) {
108+
Controlled FlipFirst(ctrl + [ctr[n-1]], (target_high, ctr[0..n-2]));
109+
}
110+
if (Length(target_low) > 1) {
111+
Controlled X(ctrl, (ctr[n-1]));
112+
Controlled FlipFirst(ctrl + [ctr[n-1]], (target_low[0..Length(target_low)-2], ctr[0..n-2]));
113+
Controlled X(ctrl, (ctr[n-1]));
114+
}
115+
}
116+
}
117+
}
118+
119+
/// Flips target iff x==y.
120+
operation FlipIfEqual(x : Qubit[], y : BigInt, target : Qubit) : Unit is Ctl + Adj {
121+
let y_bits = Std.Convert.BigIntAsBoolArray(y, Length(x));
122+
within {
123+
ApplyPauliFromBitString(PauliX, false, y_bits, x);
124+
} apply {
125+
Controlled X(x, (target));
126+
}
127+
}
128+
129+
/// Computes x = (x+1)%(2^n), where n=Length(x).
130+
operation IncrementByFlip(x : Qubit[]) : Unit is Adj {
131+
body (...) {
132+
Controlled IncrementByFlip([], (x));
133+
}
134+
controlled (ctrl, ...) {
135+
use ctr = Qubit[Std.Math.Floor(Log2(Length(x) + 1)) + 1];
136+
use carry = Qubit();
137+
CountTrailingOnes(x, ctr);
138+
QuantumArithmetic.ConstAdder.AddConstant(1L, ctr);
139+
Controlled FlipFirst(ctrl, (x + [carry], ctr));
140+
QuantumArithmetic.ConstAdder.AddConstant(-1L, ctr);
141+
142+
// Uncompute carry.
143+
// We know that carry=1 iff ctr=Length(x).
144+
let x_len = Std.Convert.IntAsBigInt(Length(x));
145+
Controlled FlipIfEqual(ctrl, (ctr, x_len, carry));
146+
147+
// Uncompute ctr.
148+
within {
149+
Controlled ApplyToEachCA(ctrl, (X, x));
150+
} apply {
151+
Adjoint CountTrailingOnes(x, ctr);
152+
}
153+
}
154+
}

lib/src/TestUtils.qs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,39 @@ operation MeasureBigInt(reg : Qubit[]) : BigInt {
2626
return ans;
2727
}
2828

29+
/// Tests artihemtic operation that acts on array of qubit registers.
30+
/// Numbers are unsigned little-endian integers.
31+
operation TestArithmeticOp(
32+
op : (Qubit[][]) => Unit,
33+
sizes : Int[],
34+
vals : BigInt[]
35+
) : BigInt[] {
36+
Fact(Length(sizes) == Length(vals), "sizes and vals must have the same length.");
37+
let n = Length(sizes);
38+
mutable total = 0;
39+
for sz in sizes {
40+
set total += sz;
41+
}
42+
use allQubits = Qubit[total];
43+
mutable regs : Qubit[][] = [];
44+
mutable offset = 0;
45+
for sz in sizes {
46+
set regs += [allQubits[offset..offset + sz - 1]];
47+
set offset += sz;
48+
}
49+
for i in 0..n - 1 {
50+
ApplyBigInt(vals[i], regs[i]);
51+
}
52+
53+
op(regs);
54+
55+
mutable results : BigInt[] = [];
56+
for i in 0..n - 1 {
57+
set results += [MeasureBigInt(regs[i])];
58+
}
59+
return results;
60+
}
61+
2962
// Applies binary operation on quantum integers.
3063
// 1. Creates qubit register x of size n, populates it with integer x_val.
3164
// 2. Creates qubit register y of size n, populates it with integer y_val.

0 commit comments

Comments
 (0)