- Demos/
- Algorithms/
How to estimate the resource cost of QSVT
How to estimate the resource cost of QSVT
Published: February 27, 2026. Last updated: February 27, 2026.
The Quantum Singular Value Transformation (QSVT) is a versatile algorithm that is applicable to a wide range of
problems, including unstructured search, Hamiltonian simulation, matrix inversion, and many more [1].
PennyLane makes it easy to build circuits and experiment with QSVT using the qsvt() function.
For more information on how to use PennyLane’s QSVT functionality checkout our other demos:
It’s important to understand the quantum resource cost of the QSVT algorithm for a variety of system sizes.
Fortunately, PennyLane’s resource estimator module makes that easy, even if the QSVT problem
you’re interested in is too big to simulate right now. If you are new to resource estimation in PennyLane or need
a quick refresher, checkout this demo on how to use PennyLane for Resource Estimation.
In this demo, you will learn how to use PennyLane’s estimator module to easily estimate the
cost of QSVT. There are two ways of doing so: the Executable workflow and the Estimator workflow. The
Estimator workflow involves expressing our QSVT circuit using estimator operators. This
workflow scales for any system size, has a simpler UI and produces tighter resource estimates. For users
who have already built a standard PennyLane circuit, the Executable workflow allows for resource estimation
with only one extra line of code.
Estimating the cost of QSVT
Let’s estimate the cost of performing a quintic (5th degree) polynomial transformation to the matrix \(A\):
This particular matrix can be expressed as a linear combination of unitaries (LCU) \(A = 0.1 \cdot Z_{0}Z_{1} + 0.2 \cdot X_{0}X_{1} + 0.3 \cdot X_{0}Z_{1}\). The LCU representation is crucial for building the block encoding operator using the standard method of LCUs. For a recap on this technique, see our demo on linear combination of unitaries and block encodings.
import pennylane as qml
A = 0.1 * (qml.Z(0) @ qml.Z(1)) + 0.2 * (qml.X(0) @ qml.X(1)) + 0.3 * (qml.X(0) @ qml.Z(1))
print(qml.matrix(A, wire_order=[0, 1]))
[[ 0.1+0.j 0. +0.j 0.3+0.j 0.2+0.j]
[ 0. +0.j -0.1+0.j 0.2+0.j -0.3+0.j]
[ 0.3+0.j 0.2+0.j -0.1+0.j 0. +0.j]
[ 0.2+0.j -0.3+0.j 0. +0.j 0.1+0.j]]
Resources from an Executable Workflow
Suppose we already had a PennyLane circuit which used QSVT to apply the quintic polynomial transformation to \(A\). We can obtain the resource estimate with only a couple of lines of code with estimate().
import pennylane.numpy as qnp
import pennylane.estimator as qre
## --- QSVT Workflow: ---
num_terms = len(A)
num_encoding_wires = int(qnp.ceil(qnp.log2(num_terms)))
encoding_wires = [f"e_{i}" for i in range(num_encoding_wires)]
poly = (0, 0, 0, 0, 0, 1) # f(x) = x^5
def circ():
qml.qsvt(A, poly, encoding_wires=encoding_wires)
return
## --- Resource Estimation: ---
gs = {"X", "Y", "Z", "S", "T", "Hadamard", "CNOT", "Toffoli"}
resources = qre.estimate(circ, gate_set=gs)()
print(resources)
--- Resources: ---
Total wires: 5
algorithmic wires: 4
allocated wires: 1
zero state: 1
any state: 0
Total gates : 2.968E+3
'Toffoli': 10,
'T': 2.772E+3,
'CNOT': 82,
'X': 44,
'Hadamard': 60
This works well for small systems. For larger system sizes, we can use some of the other functionalities
from the estimator module that are designed for scale to estimate the cost of QSVT.
Resources from an Estimator Workflow
The LCU representation of \(A\) is efficiently stored using the
PauliHamiltonian class. This produces a compact object
specifically for resource estimation. The block encoding operator is built with the
ChangeOpBasis
class which uses the compute-uncompute pattern to implement the
\(\text{Prep}^{\dagger} \circ \text{Select} \circ \text{Prep}\) operator.
The resources for QSVT can then be obtained using this block encoding. Note that these operators are specifically designed for resource estimation and are not supported for execution or simulation in a circuit.
## --- LCU representation of A: ---
lcu_A = qre.PauliHamiltonian(
num_qubits=2,
pauli_terms={"ZZ": 1, "XX": 1, "XZ": 1},
) # represents A = 0.1*ZZ + 0.2*XX + 0.3*XZ
num_terms = lcu_A.num_terms
num_qubits = lcu_A.num_qubits
## --- Block Encoding operator: ---
num_encoding_wires = int(qnp.ceil(qnp.log2(num_terms)))
encoding_wires = [f"e_{i}" for i in range(num_encoding_wires)]
lcu_wires = [f"t_{i}" for i in range(num_qubits)]
Prep = qre.QubitUnitary( # Prep the coeffs of the LCU
num_encoding_wires,
wires=encoding_wires,
)
Select = qre.SelectPauli( # Select over ops in the LCU
lcu_A,
wires=lcu_wires + encoding_wires,
)
BlockEncoding = qre.ChangeOpBasis(Prep, Select) # Prep ○ Sel Prep^t
## --- QSVT operator: ---
qsvt_op = qre.QSVT(
block_encoding=BlockEncoding,
encoding_dims=(4, 4), # The shape of matrix A
poly_deg=5, # quintic
)
## --- Resource Estimation: ---
gs = {"X", "Y", "Z", "S", "T", "Hadamard", "CNOT", "Toffoli"}
resources = qre.estimate(qsvt_op, gate_set=gs)
print(resources)
--- Resources: ---
Total wires: 5
algorithmic wires: 4
allocated wires: 1
zero state: 1
any state: 0
Total gates : 2.620E+3
'Toffoli': 10,
'T': 2.420E+3,
'CNOT': 90,
'X': 40,
'Z': 0,
'S': 0,
'Hadamard': 60
Representing the QSVT workflow like this allows us to easily perform resource estimation larger system sizes without any computational overheads. Let’s extend this example to a 50 qubit system with an LCU of 2000 terms and a 100th degree polynomial transformation. Notice how simple it is to update the code and obtain the cost of this larger system:
## --- LCU representation of A: ---
lcu_A = qre.PauliHamiltonian(
num_qubits=50,
pauli_terms={"ZZ": 250, "XX": 750, "XZ": 1000},
) # 2000 terms !
num_terms = lcu_A.num_terms
num_qubits = lcu_A.num_qubits
## --- Block Encoding operator: ---
num_encoding_wires = int(qnp.ceil(qnp.log2(num_terms)))
encoding_wires = [f"e_{i}" for i in range(num_encoding_wires)]
lcu_wires = [f"t_{i}" for i in range(num_qubits)]
Prep = qre.QROMStatePreparation( # Efficient Prep for large systems
num_encoding_wires,
wires=encoding_wires,
)
Select = qre.SelectPauli( # Select over ops in the LCU
lcu_A,
wires=lcu_wires + encoding_wires,
)
BlockEncoding = qre.ChangeOpBasis(Prep, Select) # Prep ○ Sel Prep^t
## --- QSVT operator: ---
qsvt_op = qre.QSVT(
block_encoding=BlockEncoding,
encoding_dims=(2**50, 2**50), # The shape of matrix A
poly_deg=100,
)
## --- Resource Estimation: ---
gs = {"X", "Y", "Z", "S", "T", "Hadamard", "CNOT", "Toffoli"}
resources = qre.estimate(qsvt_op, gate_set=gs)
print(resources)
--- Resources: ---
Total wires: 124
algorithmic wires: 61
allocated wires: 63
zero state: 63
any state: 0
Total gates : 3.025E+7
'Toffoli': 1.979E+6,
'T': 1.320E+4,
'CNOT': 1.823E+7,
'X': 3.674E+6,
'Z': 6.400E+3,
'S': 1.280E+4,
'Hadamard': 6.332E+6
With PennyLane’s resource estimation functionality we can analyze the cost of QSVT workflows for large system sizes consisting of hundreds of qubits and millions of gates!
Conclusion
In this demo, you learned how to use PennyLane’s estimator module to determine the
resource requirements for QSVT. Now that you are armed with these tools for resource estimation,
I challenge you to find another problem where polynomial transformations may be helpful, and figure out:
what are the logical resource requirements of solving this on a quantum computer?
References
About the author
Jay Soni
Jay completed his BSc. in Mathematical Physics from the University of Waterloo and currently works as a Quantum Software Developer at Xanadu. Fun fact, you will often find him sipping on a Tim Horton's IceCapp while he is working.
Total running time of the script: (0 minutes 0.042 seconds)