Skip to content

Commit

Permalink
Fix merge conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
fpapa250 committed Oct 23, 2024
2 parents 36e42b3 + e3aeee0 commit 3138cae
Show file tree
Hide file tree
Showing 43 changed files with 3,015 additions and 227 deletions.
4 changes: 4 additions & 0 deletions dev_tools/qualtran_dev_tools/notebook_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,10 @@
qualtran.bloqs.arithmetic.comparison._SQ_CMP_DOC,
qualtran.bloqs.arithmetic.comparison._LEQ_DOC,
qualtran.bloqs.arithmetic.comparison._CLinearDepthGreaterThan_DOC,
qualtran.bloqs.arithmetic.comparison._LINEAR_DEPTH_HALF_GREATERTHAN_DOC,
qualtran.bloqs.arithmetic.comparison._LINEAR_DEPTH_HALF_GREATERTHANEQUAL_DOC,
qualtran.bloqs.arithmetic.comparison._LINEAR_DEPTH_HALF_LESSTHAN_DOC,
qualtran.bloqs.arithmetic.comparison._LINEAR_DEPTH_HALF_LESSTHANEQUAL_DOC,
],
),
NotebookSpecV2(
Expand Down
21 changes: 11 additions & 10 deletions dev_tools/ui-export.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
Expand Down Expand Up @@ -45,12 +45,13 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import attrs\n",
"import hashlib\n",
"import json\n",
"\n",
"from qualtran import CompositeBloq\n",
"from qualtran.bloqs.rotations.programmable_rotation_gate_array import ProgrammableRotationGateArray\n",
Expand Down Expand Up @@ -108,7 +109,7 @@
},
{
"cell_type": "code",
"execution_count": 23,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
Expand All @@ -132,13 +133,6 @@
" for child_bloq, _ in call_graph.succ[bloq].items():\n",
" write_example(child_bloq)\n",
"\n",
" Path(f'ui_export/{bloq.__class__.__name__}').mkdir(parents=True, exist_ok=True)\n",
"\n",
" doc_name = f'ui_export/{bloq.__class__.__name__}/docs.txt'\n",
" if not os.path.isfile(doc_name):\n",
" with open(doc_name, 'w') as doc_file:\n",
" doc_file.write('\\n'.join(get_markdown_docstring(bloq.__class__)))\n",
"\n",
" file_name = f'ui_export/{bloq.__class__.__name__}/{bloq_filename(bloq)}'\n",
" if not os.path.isfile(file_name):\n",
" bloq_dict = {\n",
Expand All @@ -160,6 +154,13 @@
"for section in NB_BY_SECTION:\n",
" for notebook_spec in section[1]:\n",
" for bloq_spec in notebook_spec.bloq_specs:\n",
" Path(f'ui_export/{bloq_spec.bloq_cls.__name__}').mkdir(parents=True, exist_ok=True)\n",
"\n",
" doc_name = f'ui_export/{bloq_spec.bloq_cls.__name__}/docs.txt'\n",
" if not os.path.isfile(doc_name):\n",
" with open(doc_name, 'w') as doc_file:\n",
" doc_file.write('\\n'.join(get_markdown_docstring(bloq_spec.bloq_cls)))\n",
"\n",
" for example in bloq_spec.examples:\n",
" write_example(example.make())"
]
Expand Down
2 changes: 1 addition & 1 deletion qualtran/_infra/Bloqs-Tutorial.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,7 @@
"metadata": {},
"outputs": [],
"source": [
"from qualtran.bloqs.factoring import ModExp\n",
"from qualtran.bloqs.factoring.rsa import ModExp\n",
"\n",
"mod_exp = ModExp(base=8, mod=13*17, exp_bitsize=3, x_bitsize=1024)\n",
"show_bloq(mod_exp)"
Expand Down
7 changes: 6 additions & 1 deletion qualtran/_infra/bloq.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,12 @@ def _my_add_controlled(
add_controlled: A function with the signature documented above that the system
can use to automatically wire up the new control registers.
"""
from qualtran import Controlled
from qualtran import Controlled, CtrlSpec
from qualtran.bloqs.mcmt.controlled_via_and import ControlledViaAnd

if ctrl_spec != CtrlSpec():
# reduce controls to a single qubit
return ControlledViaAnd.make_ctrl_system(self, ctrl_spec=ctrl_spec)

return Controlled.make_ctrl_system(self, ctrl_spec=ctrl_spec)

Expand Down
44 changes: 43 additions & 1 deletion qualtran/_infra/data_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -772,9 +772,14 @@ class QMontgomeryUInt(QDType):
bitsize: The number of qubits used to represent the integer.
References:
[Montgomery modular multiplication](https://en.wikipedia.org/wiki/Montgomery_modular_multiplication)
[Montgomery modular multiplication](https://en.wikipedia.org/wiki/Montgomery_modular_multiplication).
[Performance Analysis of a Repetition Cat Code Architecture: Computing 256-bit Elliptic Curve Logarithm in 9 Hours with 126133 Cat Qubits](https://arxiv.org/abs/2302.06639).
Gouzien et al. 2023.
We follow Montgomery form as described in the above paper; namely, r = 2^bitsize.
"""

# TODO(https://github.com/quantumlib/Qualtran/issues/1471): Add modulus p as a class member.
bitsize: SymbolicInt

@property
Expand Down Expand Up @@ -810,6 +815,43 @@ def assert_valid_classical_val_array(
if np.any(val_array >= 2**self.bitsize):
raise ValueError(f"Too-large classical values encountered in {debug_str}")

def montgomery_inverse(self, xm: int, p: int) -> int:
"""Returns the modular inverse of an integer in montgomery form.
Args:
xm: An integer in montgomery form.
p: The modulus of the finite field.
"""
return ((pow(xm, -1, p)) * pow(2, 2 * self.bitsize, p)) % p

def montgomery_product(self, xm: int, ym: int, p: int) -> int:
"""Returns the modular product of two integers in montgomery form.
Args:
xm: The first montgomery form integer for the product.
ym: The second montgomery form integer for the product.
p: The modulus of the finite field.
"""
return (xm * ym * pow(2, -self.bitsize, p)) % p

def montgomery_to_uint(self, xm: int, p: int) -> int:
"""Converts an integer in montgomery form to a normal form integer.
Args:
xm: An integer in montgomery form.
p: The modulus of the finite field.
"""
return (xm * pow(2, -self.bitsize, p)) % p

def uint_to_montgomery(self, x: int, p: int) -> int:
"""Converts an integer into montgomery form.
Args:
x: An integer.
p: The modulus of the finite field.
"""
return (x * pow(2, int(self.bitsize), p)) % p


@attrs.frozen
class QGF(QDType):
Expand Down
26 changes: 26 additions & 0 deletions qualtran/_infra/data_types_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,32 @@ def test_qmontgomeryuint():
assert is_symbolic(QMontgomeryUInt(sympy.Symbol('x')))


@pytest.mark.parametrize('p', [13, 17, 29])
@pytest.mark.parametrize('val', [1, 5, 7, 9])
def test_qmontgomeryuint_operations(val, p):
qmontgomeryuint_8 = QMontgomeryUInt(8)
# Convert value to montgomery form and get the modular inverse.
val_m = qmontgomeryuint_8.uint_to_montgomery(val, p)
mod_inv = qmontgomeryuint_8.montgomery_inverse(val_m, p)

# Calculate the product in montgomery form and convert back to normal form for assertion.
assert (
qmontgomeryuint_8.montgomery_to_uint(
qmontgomeryuint_8.montgomery_product(val_m, mod_inv, p), p
)
== 1
)


@pytest.mark.parametrize('p', [13, 17, 29])
@pytest.mark.parametrize('val', [1, 5, 7, 9])
def test_qmontgomeryuint_conversions(val, p):
qmontgomeryuint_8 = QMontgomeryUInt(8)
assert val == qmontgomeryuint_8.montgomery_to_uint(
qmontgomeryuint_8.uint_to_montgomery(val, p), p
)


def test_qgf():
qgf_256 = QGF(characteristic=2, degree=8)
assert str(qgf_256) == 'QGF(2**8)'
Expand Down
4 changes: 3 additions & 1 deletion qualtran/_infra/gate_with_registers_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,10 @@ def test_gate_with_registers_decompose_from_context_auto_generated():


def test_non_unitary_controlled():
from qualtran.bloqs.mcmt.controlled_via_and import ControlledViaAnd

bloq = BloqWithDecompose()
assert bloq.controlled(control_values=[0]) == Controlled(bloq, CtrlSpec(cvs=0))
assert bloq.controlled(control_values=[0]) == ControlledViaAnd(bloq, CtrlSpec(cvs=0))


@pytest.mark.notebook
Expand Down
4 changes: 4 additions & 0 deletions qualtran/bloqs/arithmetic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
GreaterThanConstant,
LessThanConstant,
LessThanEqual,
LinearDepthHalfGreaterThan,
LinearDepthHalfGreaterThanEqual,
LinearDepthHalfLessThan,
LinearDepthHalfLessThanEqual,
SingleQubitCompare,
)
from qualtran.bloqs.arithmetic.controlled_addition import CAdd
Expand Down
22 changes: 21 additions & 1 deletion qualtran/bloqs/arithmetic/_shims.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@

from attrs import frozen

from qualtran import Bloq, QBit, QUInt, Register, Signature
from qualtran import Bloq, QBit, QMontgomeryUInt, QUInt, Register, Signature
from qualtran.bloqs.arithmetic.bitwise import BitwiseNot
from qualtran.bloqs.arithmetic.controlled_addition import CAdd
from qualtran.bloqs.basic_gates import Toffoli
from qualtran.bloqs.basic_gates.swap import TwoBitCSwap
from qualtran.resource_counting import BloqCountDictT, SympySymbolAllocator


Expand All @@ -39,6 +42,20 @@ def build_call_graph(self, ssa: SympySymbolAllocator) -> BloqCountDictT:
return {Toffoli(): self.n - 2}


@frozen
class CSub(Bloq):
n: int

@cached_property
def signature(self) -> 'Signature':
return Signature(
[Register('ctrl', QBit()), Register('x', QUInt(self.n)), Register('y', QUInt(self.n))]
)

def build_call_graph(self, ssa: SympySymbolAllocator) -> BloqCountDictT:
return {CAdd(QMontgomeryUInt(self.n)): 1, BitwiseNot(QMontgomeryUInt(self.n)): 3}


@frozen
class Lt(Bloq):
n: int
Expand All @@ -62,3 +79,6 @@ class CHalf(Bloq):
@cached_property
def signature(self) -> 'Signature':
return Signature([Register('ctrl', QBit()), Register('x', QUInt(self.n))])

def build_call_graph(self, ssa: SympySymbolAllocator) -> BloqCountDictT:
return {TwoBitCSwap(): self.n}
6 changes: 4 additions & 2 deletions qualtran/bloqs/arithmetic/addition.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,14 @@
"using $4n - 4 T$ gates. Uncomputation requires 0 T-gates.\n",
"\n",
"#### Parameters\n",
" - `bitsize`: Number of bits used to represent each input integer. The allocated output register is of size `bitsize+1` so it has enough space to hold the sum of `a+b`. \n",
" - `bitsize`: Number of bits used to represent each input integer. The allocated output register is of size `bitsize+1` so it has enough space to hold the sum of `a+b`.\n",
" - `is_adjoint`: Whether this is compute or uncompute version.\n",
" - `include_most_significant_bit`: Whether to add an extra most significant (i.e. carry) bit. \n",
"\n",
"#### Registers\n",
" - `a`: A bitsize-sized input register (register a above).\n",
" - `b`: A bitsize-sized input register (register b above).\n",
" - `c`: A bitize+1-sized LEFT/RIGHT register depending on whether the gate adjoint or not. \n",
" - `c`: The LEFT/RIGHT register depending on whether the gate adjoint or not. This register size is either bitsize or bitsize+1 depending on the value of `include_most_significant_bit`. \n",
"\n",
"#### References\n",
" - [Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648). \n"
Expand Down
28 changes: 22 additions & 6 deletions qualtran/bloqs/arithmetic/addition.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,18 +260,27 @@ class OutOfPlaceAdder(GateWithRegisters, cirq.ArithmeticGate): # type: ignore[m
Args:
bitsize: Number of bits used to represent each input integer. The allocated output register
is of size `bitsize+1` so it has enough space to hold the sum of `a+b`.
is_adjoint: Whether this is compute or uncompute version.
include_most_significant_bit: Whether to add an extra most significant (i.e. carry) bit.
Registers:
a: A bitsize-sized input register (register a above).
b: A bitsize-sized input register (register b above).
c: A bitize+1-sized LEFT/RIGHT register depending on whether the gate adjoint or not.
c: The LEFT/RIGHT register depending on whether the gate adjoint or not.
This register size is either bitsize or bitsize+1 depending on
the value of `include_most_significant_bit`.
References:
[Halving the cost of quantum addition](https://arxiv.org/abs/1709.06648)
"""

bitsize: 'SymbolicInt'
is_adjoint: bool = False
include_most_significant_bit: bool = True

@property
def out_bitsize(self):
return self.bitsize + (1 if self.include_most_significant_bit else 0)

@property
def signature(self):
Expand All @@ -280,14 +289,14 @@ def signature(self):
[
Register('a', QUInt(self.bitsize)),
Register('b', QUInt(self.bitsize)),
Register('c', QUInt(self.bitsize + 1), side=side),
Register('c', QUInt(self.out_bitsize), side=side),
]
)

def registers(self) -> Sequence[Union[int, Sequence[int]]]:
if not isinstance(self.bitsize, int):
raise ValueError(f'Symbolic bitsize {self.bitsize} not supported')
return [2] * self.bitsize, [2] * self.bitsize, [2] * (self.bitsize + 1)
return [2] * self.bitsize, [2] * self.bitsize, [2] * self.out_bitsize

def apply(self, a: int, b: int, c: int) -> Tuple[int, int, int]:
return a, b, c + a + b
Expand All @@ -307,7 +316,7 @@ def on_classical_vals(
return {
'a': a,
'b': b,
'c': add_ints(int(a), int(b), num_bits=self.bitsize + 1, is_signed=False),
'c': add_ints(int(a), int(b), num_bits=self.out_bitsize, is_signed=False),
}

def with_registers(self, *new_registers: Union[int, Sequence[int]]):
Expand All @@ -328,12 +337,19 @@ def decompose_from_registers(
cirq.CX(a[i], c[i + 1]),
cirq.CX(b[i], c[i]),
]
for i in range(self.bitsize)
for i in range(self.out_bitsize - 1)
]
if not self.include_most_significant_bit:
# Update c[-1] as c[-1] ^= a[-1]^b[-1]
i = self.bitsize - 1
optree.append([cirq.CX(a[i], c[i]), cirq.CX(b[i], c[i])])
return cirq.inverse(optree) if self.is_adjoint else optree

def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT':
return {And(uncompute=self.is_adjoint): self.bitsize, CNOT(): 5 * self.bitsize}
return {
And(uncompute=self.is_adjoint): self.out_bitsize - 1,
CNOT(): 5 * (self.bitsize - 1) + 2 + (3 if self.include_most_significant_bit else 0),
}

def __pow__(self, power: int):
if power == 1:
Expand Down
Loading

0 comments on commit 3138cae

Please sign in to comment.