Arithmetic Operators¶
Arithmetic operators implement quantum integer arithmetic, including addition, multiplication, shifting, comparison, and other operations.
Overview¶
Operator |
Operation |
Type |
Unitarity class |
|---|---|---|---|
|
|
Out-of-place |
SelfAdjoint |
|
|
In-place |
BaseOperator |
|
|
Out-of-place |
SelfAdjoint |
|
|
In-place |
BaseOperator |
|
|
Out-of-place |
SelfAdjoint |
|
|
In-place |
BaseOperator |
|
|
In-place |
BaseOperator |
|
Circular left shift |
In-place |
BaseOperator |
|
Circular right shift |
In-place |
BaseOperator |
|
Comparison flags |
Out-of-place |
SelfAdjoint |
|
Less-than flag |
Out-of-place |
SelfAdjoint |
|
|
Out-of-place |
SelfAdjoint |
|
Bitwise NOT |
In-place |
SelfAdjoint |
|
Swap two registers |
In-place |
SelfAdjoint |
|
Compute the midpoint |
Out-of-place |
SelfAdjoint |
—
Addition Operators¶
Add_UInt_UInt (out-of-place addition)¶
Operation: result ^= lhs + rhs
Unitarity guarantee: XOR mechanism — applying twice restores the original value.
Type constraints: All registers must be UnsignedInteger.
Bit constraints: No special requirements; the result is truncated to the output register size.
import pysparq as ps
ps.System.clear()
ps.System.add_register("lhs", ps.UnsignedInteger, 4)
ps.System.add_register("rhs", ps.UnsignedInteger, 4)
ps.System.add_register("result", ps.UnsignedInteger, 4)
state = ps.SparseState()
ps.Init_Unsafe("lhs", 3)(state)
ps.Init_Unsafe("rhs", 5)(state)
# result = 0 ^ (3 + 5) = 8
ps.Add_UInt_UInt("lhs", "rhs", "result")(state)
ps.pprint(state)
# Output: |lhs=3,rhs=5,result=8⟩ : (1+0j)
# Apply again = undo
ps.Add_UInt_UInt("lhs", "rhs", "result")(state)
# result = 8 ^ 8 = 0
Add_UInt_UInt_InPlace (in-place addition)¶
Operation: rhs = (rhs + lhs) mod 2^n
Dagger implementation: rhs = (rhs + 2^n - lhs) mod 2^n
Type constraints: Both registers must be UnsignedInteger.
Bit constraints: Registers of the same size are recommended; otherwise the smaller value is truncated.
ps.System.clear()
ps.System.add_register("lhs", ps.UnsignedInteger, 4)
ps.System.add_register("rhs", ps.UnsignedInteger, 4)
state = ps.SparseState()
ps.Init_Unsafe("lhs", 7)(state)
ps.Init_Unsafe("rhs", 10)(state)
# rhs = (10 + 7) % 16 = 1 (overflow wraps around)
op = ps.Add_UInt_UInt_InPlace("lhs", "rhs")
op(state)
# Undo: rhs = (1 + 16 - 7) % 16 = 10
op.dag(state)
Add_UInt_ConstUInt (constant out-of-place addition)¶
Operation: result ^= lhs + const
Type constraints: All registers must be UnsignedInteger.
ps.System.clear()
ps.System.add_register("lhs", ps.UnsignedInteger, 4)
ps.System.add_register("result", ps.UnsignedInteger, 4)
state = ps.SparseState()
ps.Init_Unsafe("lhs", 3)(state)
# result = 0 ^ (3 + 5) = 8
ps.Add_UInt_ConstUInt("lhs", 5, "result")(state)
Add_ConstUInt_InPlace (constant in-place addition)¶
Operation: reg = (reg + const) mod 2^n
Dagger implementation: reg = (reg + 2^n - const) mod 2^n
ps.System.clear()
ps.System.add_register("counter", ps.UnsignedInteger, 4)
state = ps.SparseState()
ps.Init_Unsafe("counter", 10)(state)
# counter = (10 + 7) % 16 = 1
op = ps.Add_ConstUInt_InPlace("counter", 7)
op(state)
# Undo
op.dag(state) # counter = 10
—
Multiplication Operators¶
Mult_UInt_ConstUInt (constant out-of-place multiplication)¶
Operation: result ^= input * const
Unitarity guarantee: XOR mechanism.
Warning
The multiplier should be odd in order to guarantee bijectivity. An even multiplier loses the information of the lowest bit.
ps.System.clear()
ps.System.add_register("input", ps.UnsignedInteger, 4)
ps.System.add_register("result", ps.UnsignedInteger, 4)
state = ps.SparseState()
ps.Init_Unsafe("input", 3)(state)
# Good: odd multiplier
ps.Mult_UInt_ConstUInt("input", 3, "result")(state)
# result = 0 ^ (3 * 3) = 9
# Avoid: even multipliers are not bijective
# ps.Mult_UInt_ConstUInt("input", 2, "result")(state) # loses the LSB
Add_Mult_UInt_ConstUInt_InPlace (multiply-accumulate)¶
Operation: result += input * const
Dagger implementation: result += (2^n - input * const) mod 2^n
ps.System.clear()
ps.System.add_register("input", ps.UnsignedInteger, 4)
ps.System.add_register("result", ps.UnsignedInteger, 8) # larger to avoid overflow
state = ps.SparseState()
ps.Init_Unsafe("input", 3)(state)
ps.Init_Unsafe("result", 5)(state)
# result = 5 + (3 * 4) = 17
op = ps.Add_Mult_UInt_ConstUInt_InPlace("input", 4, "result")
op(state)
# Undo
op.dag(state)
—
Modular Multiplication Operator¶
Mod_Mult_UInt_ConstUInt_InPlace (modular multiplication operator)¶
Operation: y → y * a^(2^x) mod N (in-place modular multiplication)
Dagger: y → y * a^(-2^x) mod N (the modular inverse is computed with the extended Euclidean algorithm)
Type constraints: UnsignedInteger; the register size must be ≥ ⌈log₂(N)⌉.
Condition: a and N must be coprime (gcd(a, N) = 1); otherwise an exception is raised at construction.
import pysparq as ps
ps.System.clear()
reg = ps.System.add_register("y", ps.UnsignedInteger, 4)
state = ps.SparseState()
ps.Init_Unsafe("y", 3)(state)
# y = 3 * 7 mod 15 = 6
op = ps.Mod_Mult_UInt_ConstUInt_InPlace("y", 7, 0, 15)
op(state)
# Undo: y = 6 * 13 mod 15 = 3
op.dag(state)
—
Shift Operators¶
ShiftLeft_InPlace (circular left shift)¶
Operation: Circularly shifts left by digit bits
Dagger: .dag() is implemented and has the same effect as ShiftRight_InPlace(reg, digit); the two are daggers of each other.
Type constraints: UnsignedInteger or SignedInteger.
Bit constraints: digit <= register size.
ps.System.clear()
ps.System.add_register("reg", ps.UnsignedInteger, 4)
state = ps.SparseState()
ps.Init_Unsafe("reg", 0b1010)(state) # 10
ps.ShiftLeft_InPlace("reg", 1)(state)
# reg = 0b0101 = 5
# Undo
op.dag(state)
# reg = 0b1010 = 10
ShiftRight_InPlace (circular right shift)¶
Operation: Circularly shifts right by digit bits
Dagger: ShiftLeft_InPlace(reg, digit)
ps.Init_Unsafe("reg", 0b1010)(state) # 10
ps.ShiftRight_InPlace("reg", 1)(state)
# reg = 0b0101 = 5
# ShiftLeft_InPlace and ShiftRight_InPlace are inverses of each other
ps.ShiftLeft_InPlace("reg", 1)(state)
# reg = 0b1010 = 10
—
Comparison Operators¶
Compare_UInt_UInt (comparison)¶
Operation: Sets less_flag and equal_flag based on lhs < rhs and lhs == rhs.
Type constraints: Inputs UnsignedInteger, outputs Boolean.
ps.System.clear()
ps.System.add_register("lhs", ps.UnsignedInteger, 4)
ps.System.add_register("rhs", ps.UnsignedInteger, 4)
ps.System.add_register("less", ps.Boolean, 1)
ps.System.add_register("equal", ps.Boolean, 1)
state = ps.SparseState()
ps.Init_Unsafe("lhs", 3)(state)
ps.Init_Unsafe("rhs", 5)(state)
ps.Compare_UInt_UInt("lhs", "rhs", "less", "equal")(state)
# less = 1 (3 < 5), equal = 0
Less_UInt_UInt (less-than comparison)¶
Operation: Sets only less_flag.
—
Other Operators¶
Assign (assignment)¶
Operation: dst ^= src
Unitarity guarantee: XOR mechanism.
ps.System.clear()
ps.System.add_register("src", ps.UnsignedInteger, 4)
ps.System.add_register("dst", ps.UnsignedInteger, 4)
state = ps.SparseState()
ps.Init_Unsafe("src", 5)(state)
# dst = 0 ^ 5 = 5
ps.Assign("src", "dst")(state)
FlipBools (bitwise NOT)¶
Operation: reg = ~reg
Type constraints: Any integer type.
ps.System.clear()
ps.System.add_register("reg", ps.UnsignedInteger, 4)
state = ps.SparseState()
ps.Init_Unsafe("reg", 0b1010)(state) # 10
ps.FlipBools("reg")(state)
# reg = 0b0101 = 5
Swap_General_General (swap)¶
Operation: Swaps the values of two registers.
ps.System.clear()
ps.System.add_register("a", ps.UnsignedInteger, 4)
ps.System.add_register("b", ps.UnsignedInteger, 4)
state = ps.SparseState()
ps.Init_Unsafe("a", 3)(state)
ps.Init_Unsafe("b", 5)(state)
ps.Swap_General_General("a", "b")(state)
# a = 5, b = 3
GetMid_UInt_UInt (midpoint computation)¶
Operation: mid ^= (left + right) // 2
Purpose: Binary search algorithms.
ps.System.clear()
ps.System.add_register("left", ps.UnsignedInteger, 4)
ps.System.add_register("right", ps.UnsignedInteger, 4)
ps.System.add_register("mid", ps.UnsignedInteger, 4)
state = ps.SparseState()
ps.Init_Unsafe("left", 2)(state)
ps.Init_Unsafe("right", 8)(state)
ps.GetMid_UInt_UInt("left", "right", "mid")(state)
# mid = (2 + 8) // 2 = 5