Operator Usage Examples¶
This tutorial demonstrates how to use various operators.
[ ]:
import pysparq as ps
import numpy as np
Arithmetic Operators¶
[ ]:
ps.System.clear()
ps.System.add_register("a", ps.UnsignedInteger, 4)
ps.System.add_register("b", ps.UnsignedInteger, 4)
ps.System.add_register("result", ps.UnsignedInteger, 4)
state = ps.SparseState()
ps.Init_Unsafe("a", 7)(state)
ps.Init_Unsafe("b", 10)(state)
Addition¶
[ ]:
# Out-of-place addition: result ^= a + b
ps.Add_UInt_UInt("a", "b", "result")(state)
print("Add_UInt_UInt:")
ps.pprint(state)
[ ]:
# In-place addition: b += a
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", 7)(state)
ps.Init_Unsafe("b", 10)(state)
op = ps.Add_UInt_UInt_InPlace("a", "b")
op(state)
print("Add_UInt_UInt_InPlace:")
ps.pprint(state)
# b = (10 + 7) % 16 = 1
[ ]:
# Undo
op.dag(state)
print("After dagger:")
ps.pprint(state)
Multiplication¶
[ ]:
ps.System.clear()
ps.System.add_register("x", ps.UnsignedInteger, 4)
ps.System.add_register("triple", ps.UnsignedInteger, 4)
state = ps.SparseState()
ps.Init_Unsafe("x", 5)(state)
# Multiply by an odd constant (guarantees bijectivity)
ps.Mult_UInt_ConstUInt("x", 3, "triple")(state)
print("Mult_UInt_ConstUInt(x, 3):")
ps.pprint(state)
Comparison and Flags¶
[ ]:
ps.System.clear()
ps.System.add_register("a", ps.UnsignedInteger, 4)
ps.System.add_register("b", 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("a", 3)(state)
ps.Init_Unsafe("b", 5)(state)
ps.Compare_UInt_UInt("a", "b", "less", "equal")(state)
print("Compare_UInt_UInt:")
ps.pprint(state)
Quantum Gates¶
[ ]:
ps.System.clear()
ps.System.add_register("q", ps.Boolean, 1)
state = ps.SparseState()
print("Initial:")
ps.pprint(state)
[ ]:
# X gate (NOT)
ps.X_Bool("q", 0)(state)
print("After X gate:")
ps.pprint(state)
[ ]:
# Hadamard
ps.Hadamard_Bool("q")(state)
print("After Hadamard:")
ps.pprint(state)
[ ]:
# Phase gate
ps.Phase_Bool("q", 0, np.pi/4)(state)
print("After Phase(π/4):")
ps.pprint(state)
QFT¶
[ ]:
ps.System.clear()
ps.System.add_register("q", ps.UnsignedInteger, 3)
state = ps.SparseState()
ps.Init_Unsafe("q", 1)(state)
print("Initial:")
ps.pprint(state)
[ ]:
# QFT
ps.QFT("q")(state)
print("After QFT:")
ps.pprint(state)
[ ]:
# InverseQFT
ps.InverseQFT("q")(state)
print("After InverseQFT:")
ps.pprint(state)
Conditional Operations¶
[ ]:
ps.System.clear()
ps.System.add_register("x", ps.UnsignedInteger, 2)
ps.System.add_register("result", ps.UnsignedInteger, 2)
ps.System.add_register("ctrl", ps.Boolean, 1)
state = ps.SparseState()
ps.Init_Unsafe("x", 3)(state)
ps.Init_Unsafe("ctrl", 1)(state)
# Perform the addition when ctrl = 1
ps.Add_UInt_ConstUInt("x", 5, "result").conditioned_by_nonzeros("ctrl")(state)
print("Conditional addition (ctrl=1):")
ps.pprint(state)
[ ]:
# The condition does not trigger when ctrl = 0
ps.System.clear()
ps.System.add_register("x", ps.UnsignedInteger, 2)
ps.System.add_register("result", ps.UnsignedInteger, 2)
ps.System.add_register("ctrl", ps.Boolean, 1)
state = ps.SparseState()
ps.Init_Unsafe("x", 3)(state)
ps.Init_Unsafe("ctrl", 0)(state) # ctrl = 0
ps.Add_UInt_ConstUInt("x", 5, "result").conditioned_by_nonzeros("ctrl")(state)
print("Conditional addition (ctrl=0):")
ps.pprint(state)
# result stays 0
Block Encoding: Tridiagonal Matrix¶
Block Encoding embeds a classical matrix into a unitary and is central to quantum linear-algebra algorithms. The example below uses BlockEncodingTridiagonal from pysparq.algorithms.block_encoding to block-encode the tridiagonal matrix :math:A = \alpha I + \beta T.
[ ]:
from pysparq.algorithms.block_encoding import (
get_tridiagonal_matrix,
BlockEncodingTridiagonal,
)
alpha, beta, dim = 0.5, 0.3, 4
A = get_tridiagonal_matrix(alpha, beta, dim)
print(f"Tridiagonal matrix A:\n{A}")
# Build the Block Encoding circuit
ps.System.clear()
ps.System.add_register("main_reg", ps.UnsignedInteger, 2) # dim = 2^2
ps.System.add_register("anc_UA", ps.UnsignedInteger, 4)
state = ps.SparseState()
ps.Init_Unsafe("main_reg", 0)(state)
ps.Init_Unsafe("anc_UA", 0)(state)
block_enc = BlockEncodingTridiagonal("main_reg", "anc_UA", alpha, beta)
block_enc(state)
print(f"Block Encoding applied successfully, number of basis states: {state.size()}")
block_enc.dag(state) # release the ancilla registers
print(f"After inverse Block Encoding, number of basis states: {state.size()}")
Custom Operators on the Python Side¶
When you need to combine several existing operators, wrap them directly on the Python side:
[ ]:
class MyIncrement:
"""Add 1 to the register value (out-of-place result register)."""
def __init__(self, src: str, dst: str):
self.src = src
self.dst = dst
def __call__(self, state: ps.SparseState):
ps.Add_UInt_UInt(self.src, self.dst)(state)
def dag(self, state: ps.SparseState):
ps.Add_UInt_UInt(self.src, self.dst)(state) # XOR semantics: applying it twice restores the value
ps.System.clear()
ps.System.add_register("counter", ps.UnsignedInteger, 4)
ps.System.add_register("result", ps.UnsignedInteger, 4)
state = ps.SparseState()
ps.Init_Unsafe("counter", 5)(state)
ps.Init_Unsafe("result", 0)(state)
my_inc = MyIncrement("counter", "result")
my_inc(state) # result = 0 + 5 = 5
print(f"After addition, result = {state.basis_states[0].get(ps.System.get_id('result')).value}")
my_inc.dag(state) # result = 5 + 5 = 0, restored
print(f"After dagger, result = {state.basis_states[0].get(ps.System.get_id('result')).value}")
If you need new primitives (beyond what the existing operators can express), use compile_operator to compile C++ code into a dynamically linked library:
[ ]:
from pysparq.dynamic_operator import compile_operator
cpp_code = '''
class FlipOp : public SelfAdjointOperator {
size_t reg_id;
public:
FlipOp(size_t r) : reg_id(r) {}
void operator()(std::vector<System>& state) const override {
for (auto& s : state) {
s.get(reg_id).value ^= 1;
}
}
};
'''
FlipOp = compile_operator(
name="FlipOp",
cpp_code=cpp_code,
base_class="SelfAdjointOperator",
constructor_args=[("size_t", "reg_id")],
)
ps.System.clear()
ps.System.add_register("q", ps.UnsignedInteger, 4)
state = ps.SparseState()
ps.Init_Unsafe("q", 1)(state)
print("Initial:", state.basis_states[0].get(ps.System.get_id("q")).value)
flip = FlipOp(reg_id=0)
flip(state) # flip bit 0
print("After flip:", state.basis_states[0].get(ps.System.get_id("q")).value)
Summary¶
This tutorial demonstrated:
Arithmetic operators: Add, Mult, Compare
Quantum gates: X, Hadamard, Phase
QFT / InverseQFT
Conditional operations
Block Encoding: block-encode a tridiagonal matrix with
BlockEncodingTridiagonalCustom operators: compose existing operators on the Python side, or compile C++ code via
compile_operator