Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion qmath/add/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Quantum addition algorithms."""

from .cdkm2004 import CDKMAdder
from .ttk2009 import TTKAdder
from .ttk2009 import TTKAdder
3 changes: 2 additions & 1 deletion qmath/add/adders_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from qmath.add import CDKMAdder, TTKAdder



# Tests in-place adder:
# * Initializes two registers: x of size n1 and y of size n2.
# * Fills them with random numbers.
Expand Down Expand Up @@ -108,4 +109,4 @@ def test_adder_ttk(num_bits: tuple[int, int]):

def test_adder_ttk_controlled():
_check_controlled_adder(TTKAdder(), 5, 5)
_check_controlled_adder(TTKAdder(), 5, 4, num_trials=10)
_check_controlled_adder(TTKAdder(), 5, 4, num_trials=10)
1 change: 1 addition & 0 deletions qmath/mult/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
from .jhaa2016 import JHHAMultipler
from .mct2017 import MCTMultipler
from .multiplier import Multiplier
from .cg2019 import schoolbook_multiplication
111 changes: 111 additions & 0 deletions qmath/mult/cg2019.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Implementation of multiplier presented in paper:
# Asymptotically Efficient Quantum Karatsuba Multiplication
# Craig Gidney, 2019.
# https://arxiv.org/abs/1904.07356
# All numbers are integer, Little Endian.
# Based off q# implementation from (https://github.com/fedimser/quant-arith-re/blob/main/lib/src/QuantumArithmetic/CG2019.qs)

from psiqworkbench import Qubits, QUInt, qubricks
from psiqworkbench.interoperability import implements
from psiqworkbench.qubricks import Qubrick, GidneyAdd

from ..utils.gates import ccnot, cnot

# from ..add.cdkm2004 import CDKMAdder

class schoolbook_multiplication(Qubrick):
"""
Implementation of the schoolbook multiplication:
"Asymptotically Efficient Quantum Karatsuba Multiplication",
Craig Gidney, 2019.
https://arxiv.org/abs/1904.07356
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.name = "schoolbook multiplier"
self.description = "Asymptotically efficient multiplier by Craig Gidney, 2019."
self.gadder = qubricks.GidneyAdd()

def _plusEqual(self, lvalue: Qubits, offset: Qubits):
#trimmedOffset = offset[ : len(offset) - min([len(lvalue), len(offset)]) ] #Python doesnt include the last element -> could be a edian issue
trimmedOffset = offset[ len(offset) - min([len(lvalue), len(offset)]) :] #to fix edian issue?
pad_num_qubits = len(lvalue)-len(trimmedOffset)
if len(trimmedOffset) > 0:
pad = 0
if pad_num_qubits > 0:
pad: Qubits = self.alloc_temp_qreg(len(lvalue) - len(trimmedOffset), "pad")
paddedOffset = trimmedOffset | pad #+ should be replaced with |
self.gadder.compute(lvalue, paddedOffset) #use gidney adder (try making it work with this first)

def _compute(self, a: Qubits, b: Qubits, c: Qubits): #parent class of all subtypes for Qubits vs QuInt?
n1 = len(a)
n2 = len(b)
w: Qubits = self.alloc_temp_qreg(n2, "w")
for k in range(n1):
for i in range(n2):
ccnot(b[i], a[k], w[i])
#print(f"QUInt = {c.read()}")
self._plusEqual(c[k: len(c) ], w)
#print(f"QUInt = {c.read()}")
for i in range(n2):
ccnot(b[i], a[k], w[i])

class karatsub_multiplication(Qubrick):
"""
Implementation of the multiplier presented in paper:
"Asymptotically Efficient Quantum Karatsuba Multiplication",
Craig Gidney, 2019.
https://arxiv.org/abs/1904.07356
"""
def __init__(self):
super().__init__()
self.name = "CG2019 Multiplier"
self.description = "Asymptotically efficient multiplier by Craig Gidney, 2019."
self.schoolbook_multiplier = schoolbook_multiplication()

def _splitPadBuffer(self, buf: Qubits, pad: Qubits, basePieceSize: int, desiredPieceSize: int, pieceCount: int) -> list[Qubits]:
"""
Splits buf into pieces of base_piece_size, pads each to desired_piece_size using pad.

Args:
buf: Buffer of qubits to split
pad: Padding qubits (should have enough for all missing pieces)
base_piece_size: Initial size of each piece from buf
desired_piece_size: Target size after padding
piece_count: Number of pieces to create

Returns:
List of Qubits objects, each of size desired_piece_size
"""
result = [] #how to convert "mutable result : Qubit[][] = [];" from Q# to python?
k_pad = 0

for i in range(pieceCount):
k_buf = i * basePieceSize
res_i = []

#extract from buffer if needed
if(k_buf < len(buf)):
res_i = buf[k_buf:min(k_buf + basePieceSize, len(buf))]

#calculate how much padding is needed
missing = desiredPieceSize - len(res_i)
result += [res_i + pad[k_pad : k_pad + missing]]
k_pad += missing

return result

def _mergeBufferRanges(work_registers: list[Qubits], start: int, length: int):
result = [] #how to convert "mutable result : Qubit[] = [];" from Q# to python?
for i in range(len(work_registers)):
for j in range(length):
result += [work_registers[i][start+j]]
return result

def _ceillg2(n:int) -> int:
if (n <= 1):
return 0
# else:


# def _compute(self, a: Qubits, b: Qubits, c: Qubits) -> None:
31 changes: 30 additions & 1 deletion qmath/mult/multipliers_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
from psiqworkbench import QPU, QUInt

from qmath.mult import JHHAMultipler, MCTMultipler, Multiplier
from qmath.mult.cg2019 import schoolbook_multiplication


def _check_multiplier(multiplier: Multiplier, num_bits, num_trials=5):
qc = QPU(filters=[">>64bit>>", ">>bit-sim>>"])
qc.reset(4 * num_bits + 1)
qc.reset(1000)#4 * num_bits + 1)

a = QUInt(num_bits, "a", qc)
b = QUInt(num_bits, "b", qc)
Expand All @@ -24,6 +25,23 @@ def _check_multiplier(multiplier: Multiplier, num_bits, num_trials=5):
assert c.read() == x * y


def _check_multiplier_set(multiplier: Multiplier, num_bits, x_in : int, y_in: int):
qc = QPU(filters=[">>64bit>>", ">>bit-sim>>"])
qc.reset(1000)#4 * num_bits + 1)

a = QUInt(num_bits, "a", qc)
b = QUInt(num_bits, "b", qc)
c = QUInt(2 * num_bits, "c", qc)

x = x_in
y = y_in
a.write(x)
b.write(y)
c.write(0)
multiplier.compute(a, b, c)
assert c.read() == x * y


@pytest.mark.parametrize("num_bits", [1, 2, 5, 10])
def test_mct_multiplier(num_bits: int):
_check_multiplier(MCTMultipler(), num_bits)
Expand All @@ -32,3 +50,14 @@ def test_mct_multiplier(num_bits: int):
@pytest.mark.parametrize("num_bits", [1, 2, 5, 10])
def test_jhha_multiplier(num_bits: int):
_check_multiplier(JHHAMultipler(num_bits), 10)

#workbench is little endian -> LE for some q# functions
#q# is big endian

@pytest.mark.parametrize("num_bits", [2, 5, 8, 10])
def test_sb_multiplier(num_bits: int):
_check_multiplier(schoolbook_multiplication(), num_bits, num_trials=5)

#@pytest.mark.parametrize("num_bits", [2])
#def test_sb_multiplier(num_bits: int):
# _check_multiplier_set(schoolbook_multiplication(), num_bits, 3, 3)
7 changes: 7 additions & 0 deletions qmath/utils/math.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#Random math functions that might be useful in multiple places

def floorlog2(n: int) -> int:
"""Return floor(log2(n)) for positive integers using Python's bit_length."""
if n < 1:
raise ValueError("n must be >= 1")
return n.bit_length() - 1