"""\********************************************************************************* Copyright (c) 2025 the Qrisp authors** This program and the accompanying materials are made available under the* terms of the Eclipse Public License 2.0 which is available at* http://www.eclipse.org/legal/epl-2.0.** This Source Code may also be made available under the following Secondary* Licenses when the conditions for such availability set forth in the Eclipse* Public License, v. 2.0 are satisfied: GNU General Public License, version 2* with the GNU Classpath Exception which is* available at https://www.gnu.org/software/classpath/license.html.** SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0********************************************************************************/"""importnumpyasnpfromqrisp.qtypes.quantum_floatimportQuantumFloatfromqrisp.miscimportgate_wrapfromqrisp.coreimportQuantumVariabledefcomparison_wrapper(func):defres_func(self,other):ifself.m!=0:raiseException("Tried to evaluate QuantumModulus comparison with non-zero Montgomery shift")conversion_flag=Falseifisinstance(other,QuantumModulus):ifother.m!=0:raiseException("Tried to evaluate QuantumModulus comparison with non-zero Montgomery shift")ifself.modulus!=other.modulus:raiseException("Tried to compare QuantumModulus instances of differing modulus")other.__class__=QuantumFloatself.__class__=QuantumFloatres=func(self,other)self.__class__=QuantumModulusifconversion_flag:other.__class__=QuantumModulusreturnresreturnres_func
[docs]classQuantumModulus(QuantumFloat):r""" This class is a subtype of :ref:`QuantumFloat`, which can be used to model and process `modular arithmetic <https://en.wikipedia.org/wiki/Modular_arithmetic>`_. Modular arithmetic plays an important role in many cryptographical applications, especially in Shor's algorithm. The QuantumModulus allows users convenient access to modular arithmetic, which for many gate based frameworks is rather complicated due to the intricacies of the reduction process. For a first example we simply add two instances: >>> from qrisp import QuantumModulus >>> N = 13 >>> a = QuantumModulus(N) >>> b = QuantumModulus(N) >>> a[:] = 5 >>> b[:] = 10 >>> c = a + b >>> print(c) {2: 1.0} We get the output 2 because >>> (5 + 10)%13 2 Similar to :ref:`QuantumFloat`, subtraction and addition are also supported: >>> d = a*b >>> print(d) {11: 1.0} Check the result: >>> (5*10)%13 11 Especially relevant for Shor's algorithm are the in-place operations: >>> a = QuantumModulus(N) >>> a[:] = 5 >>> a *= 10 >>> print(a) {11: 1.0} **Specifying a custom adder** It is possible to specify a custom adder that is used when processing the Modular-Arithmetic. For this, you can use the ``inpl_adder`` keyword. By default, the `Fourier-adder <https://arxiv.org/abs/quant-ph/0008033>`_ is used, but we can for instance also try the `Cuccaro-adder <https://arxiv.org/abs/quant-ph/0410184>`_. >>> from qrisp import cuccaro_adder >>> a = QuantumModulus(N, inpl_adder = cuccaro_adder) >>> a[:] = 5 >>> a *= 10 >>> print(a) {11: 1.0} Or the `Gidney-adder <https://arxiv.org/abs/1709.06648>`_. >>> from qrisp import gidney_adder >>> a = QuantumModulus(N, inpl_adder = gidney_adder) >>> a[:] = 5 >>> a *= 10 >>> print(a) {11: 1.0} To learn how to create your own adder for this feature, please visit :meth:`this page <qrisp.inpl_adder_test>`. .. warning:: Currently the adder is only used in in-place multiplication, since this is the relevant operation for Shor's algorithm. The other operations (such as addition etc.) will follow in a future release of Qrisp. **Advanced usage** The modular multiplication uses a technique called `Montgomery reduction <https://en.wikipedia.org/wiki/Montgomery_modular_multiplication>`_. The quantum version of this algorithm can be found in `this paper <https://arxiv.org/abs/1801.01081>`__. The idea behind Montgomery reduction is to choose a differing representation of numbers to enhance the reduction step of modular arithmetic. This representation works as follows: For an integer $m$ called Montgomery shift, the modular number $a \in \mathbb{Z}/N\mathbb{Z}$ is represented as .. math:: \hat{k} = (2^{-m} k) \text{mod} N If you're interested in why this representation is advantageous, we recommend checking out the linked resources above. For Qrisp, the Montgomery shift can be modified via the attribute ``m``. >>> a = QuantumModulus(N) >>> a.m = 3 >>> a[:] = 1 >>> print(a) {1: 1.0} We shift back to 0: >>> a.m -= 3 >>> print(a) {8: 1.0} Note that this shift is only a compiler shift - ie. no quantum gates are applied. Instead the :meth:`decoder <qrisp.QuantumVariable.decoder>` function is modified. """def__init__(self,modulus,inpl_adder=None,qs=None):self.m=int(np.ceil(np.log2(modulus)))self.modulus=modulusQuantumFloat.__init__(self,msize=self.m,qs=qs)ifinpl_adderisNone:fromqrisp.alg_primitives.arithmeticimportfourier_adderinpl_adder=fourier_adderself.inpl_adder=inpl_adderself.m=0defdecoder(self,i):fromqrisp.alg_primitives.arithmetic.modular_arithmeticimportmontgomery_decoderifi>=self.modulus:# or (np.gcd(i, self.modulus) != 1 and i != 0):returnnp.nanreturnmontgomery_decoder(i,2**self.m,self.modulus)defencoder(self,i):ifi>=self.modulus:raiseException("Tried to encode a number into QuantumModulus, which is greator or equal to the modulus")ifi<0:raiseException("Tried to encode a negative number into QuantumModulus")fromqrisp.alg_primitives.arithmetic.modular_arithmeticimportmontgomery_encoderifi>=self.modulus:# or (np.gcd(i, self.modulus) != 1 and i != 0):returnnp.nanreturnmontgomery_encoder(i,2**self.m,self.modulus)defencode(self,i):QuantumVariable.encode(self,self.encoder(i))@gate_wrap(permeability="args",is_qfree=True)def__mul__(self,other):fromqrisp.alg_primitives.arithmetic.modular_arithmeticimportmontgomery_mod_mul,montgomery_mod_semi_mulifisinstance(other,QuantumModulus):returnmontgomery_mod_mul(self,other)elifisinstance(other,int):returnmontgomery_mod_semi_mul(self,other)else:raiseException("Quantum modular multiplication with type {type(other)} not implemented")__rmul__=__mul__@gate_wrap(permeability=[1],is_qfree=True)def__imul__(self,other):ifisinstance(other,int):fromqrisp.alg_primitives.arithmetic.modular_arithmeticimportqft_semi_cl_inpl_mult,semi_cl_inpl_multfromqrisp.alg_primitives.arithmetic.addersimportfourier_adderifself.inpl_adderisfourier_adder:returnqft_semi_cl_inpl_mult(self,other%self.modulus)else:returnsemi_cl_inpl_mult(self,other%self.modulus)else:raiseException("Quantum modular multiplication with type {type(other)} not implemented")@gate_wrap(permeability="args",is_qfree=True)def__add__(self,other):ifisinstance(other,int):other=self.encoder(other%self.modulus)elifisinstance(other,QuantumModulus):ifself.m!=other.m:raiseException("Tried to add two QuantumModulus with differing Montgomery shift")elifisinstance(other,QuantumFloat):ifself.m!=0:raiseException("Tried to add a QuantumFloat and QuantumModulus with non-zero Montgomery shift")fromqrisp.alg_primitives.arithmetic.modular_arithmeticimportmod_adderres=self.duplicate(init=True)mod_adder(other,res,self.inpl_adder,self.modulus)returnres__radd__=__mul__@gate_wrap(permeability=[1],is_qfree=True)def__iadd__(self,other):ifisinstance(other,int):other=self.encoder(other%self.modulus)elifisinstance(other,QuantumModulus):ifself.m!=other.m:fromqrisp.alg_primitives.arithmetic.modular_arithmeticimportmontgomery_additionmontgomery_addition(other,self)returnselfelifisinstance(other,QuantumFloat):ifself.m!=0:raiseException("Tried to add a QuantumFloat and QuantumModulus with non-zero Montgomery shift")fromqrisp.alg_primitives.arithmetic.modular_arithmeticimportmod_addermod_adder(other,self,self.inpl_adder,self.modulus)returnself@gate_wrap(permeability="args",is_qfree=True)def__sub__(self,other):ifisinstance(other,int):other=self.encoder(other%self.modulus)elifisinstance(other,QuantumModulus):ifself.m!=other.m:raiseException("Tried to add subtract QuantumModulus with differing Montgomery shift")elifisinstance(other,QuantumFloat):ifself.m!=0:raiseException("Tried to subtract a QuantumFloat and QuantumModulus with non-zero Montgomery shift")fromqrisp.alg_primitives.arithmetic.modular_arithmeticimportmod_adderfromqrisp.environmentsimportinvertres=self.duplicate(init=True)withinvert():mod_adder(other,res,self.inpl_adder,self.modulus)returnres@gate_wrap(permeability="args",is_qfree=True)def__rsub__(self,other):ifisinstance(other,int):other=self.encoder(other%self.modulus)elifisinstance(other,QuantumModulus):ifself.m!=other.m:raiseException("Tried to add subtract QuantumModulus with differing Montgomery shift")elifisinstance(other,QuantumFloat):ifself.m!=0:raiseException("Tried to subtract a QuantumFloat and QuantumModulus with non-zero Montgomery shift")fromqrisp.alg_primitives.arithmetic.modular_arithmeticimportmod_adderres=self.duplicate()res-=selfmod_adder(other,res,self.inpl_adder,self.modulus)returnres@gate_wrap(permeability=[1],is_qfree=True)def__isub__(self,other):ifisinstance(other,int):other=self.encoder(other%self.modulus)elifisinstance(other,QuantumModulus):ifself.m!=other.m:raiseException("Tried to add subtract QuantumModulus with differing Montgomery shift")elifisinstance(other,QuantumFloat):ifself.m!=0:raiseException("Tried to subtract a QuantumFloat and QuantumModulus with non-zero Montgomery shift")fromqrisp.alg_primitives.arithmetic.modular_arithmeticimportmod_adderfromqrisp.environmentsimportinvertwithinvert():mod_adder(other,self,self.inpl_adder,self.modulus)returnself@comparison_wrapperdef__lt__(self,other):fromqrisp.alg_primitivesimportuint_ltreturnuint_lt(self,other,self.inpl_adder)@comparison_wrapperdef__gt__(self,other):fromqrisp.alg_primitivesimportuint_gtreturnuint_gt(self,other,self.inpl_adder)@comparison_wrapperdef__le__(self,other):fromqrisp.alg_primitivesimportuint_lereturnuint_le(self,other,self.inpl_adder)@comparison_wrapperdef__ge__(self,other):fromqrisp.alg_primitivesimportuint_gereturnuint_ge(self,other,self.inpl_adder)@comparison_wrapperdef__eq__(self,other):returnQuantumVariable.__eq__(self,other)@comparison_wrapperdef__ne__(self,other):returnQuantumVariable.__ne__(self,other)def__hash__(self):returnQuantumFloat.__hash__(self)
Get in touch!
If you are interested in Qrisp or high-level quantum algorithm research in general connect with us on our
Slack workspace.