Source code for pennylane.labs.resource_estimation.templates.qubitize
# Copyright 2025 Xanadu Quantum Technologies Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
r"""Resource operators for PennyLane subroutine templates."""
import math
import numpy as np
from pennylane.labs import resource_estimation as plre
from pennylane.labs.resource_estimation.qubit_manager import AllocWires, FreeWires
from pennylane.labs.resource_estimation.resource_operator import (
CompressedResourceOp,
GateCount,
ResourceOperator,
resource_rep,
)
from pennylane.wires import Wires
# pylint: disable=too-many-arguments, arguments-differ
[docs]
class ResourceQubitizeTHC(ResourceOperator):
r"""Resource class for Qubitization of THC Hamiltonian.
Args:
compact_ham (~pennylane.labs.resource_estimation.CompactHamiltonian): a tensor hypercontracted
Hamiltonian for which the walk operator is being created
coeff_precision (float, optional): precision for loading the coefficients of Hamiltonian
rotation_precision (float, optional): precision for loading the rotation angles for basis rotation
compare_precision (float, optional): precision for comparing two numbers
wires (list[int] or optional): the wires on which the operator acts
Resources:
The resources are calculated based on `arXiv:2011.03494 <https://arxiv.org/abs/2011.03494>`_
**Example**
The resources for this operation are computed using:
>>> compact_ham = plre.CompactHamiltonian.thc(num_orbitals=20, tensor_rank=40)
>>> res = plre.estimate_resources(plre.ResourceQubitizeTHC(compact_ham))
>>> print(res)
--- Resources: ---
Total qubits: 147.0
Total gates : 1.631E+5
Qubit breakdown:
clean qubits: 10, dirty qubits: 82.0, algorithmic qubits: 55
Gate breakdown:
{'X': 2.046E+4, 'CNOT': 8.721E+4, 'Toffoli': 1.584E+4, 'Hadamard': 3.923E+4, 'Z': 160, 'Y': 200}
"""
def __init__(
self,
compact_ham,
coeff_precision=1e-5,
rotation_precision=1e-5,
compare_precision=1e-3,
wires=None,
):
if compact_ham.method_name != "thc":
raise TypeError(
f"Unsupported Hamiltonian representation for ResourceQubitizeTHC."
f"This method works with thc Hamiltonian, {compact_ham.method_name} provided"
)
self.compact_ham = compact_ham
self.coeff_precision = coeff_precision
self.rotation_precision = rotation_precision
self.compare_precision = compare_precision
if wires is not None:
self.wires = Wires(wires)
self.num_wires = len(self.wires)
else:
num_orb = compact_ham.params["num_orbitals"]
tensor_rank = compact_ham.params["tensor_rank"]
self.num_wires = num_orb * 2 + 2 * int(np.ceil(math.log2(2 * tensor_rank + 1))) + 1
self.wires = None
super().__init__(wires=wires)
@property
def resource_params(self) -> dict:
r"""Returns a dictionary containing the minimal information needed to compute the resources.
Returns:
dict: A dictionary containing the resource parameters:
* compact_ham (~pennylane.labs.resource_estimation.CompactHamiltonian): a tensor hypercontracted
Hamiltonian for which the walk operator is being created
* coeff_precision (float, optional): precision for loading the coefficients of Hamiltonian
* rotation_precision (float, optional): precision for loading the rotation angles for basis rotation
* compare_precision (float, optional): precision for comparing two numbers
"""
return {
"compact_ham": self.compact_ham,
"coeff_precision": self.coeff_precision,
"rotation_precision": self.rotation_precision,
"compare_precision": self.compare_precision,
}
[docs]
@classmethod
def resource_rep(
cls, compact_ham, coeff_precision, rotation_precision, compare_precision
) -> CompressedResourceOp:
"""Returns a compressed representation containing only the parameters of
the Operator that are needed to compute a resource estimation.
Args:
compact_ham (~pennylane.labs.resource_estimation.CompactHamiltonian): a tensor hypercontracted
Hamiltonian for which the walk operator is being created
coeff_precision (float, optional): precision for loading the coefficients of Hamiltonian
rotation_precision (float, optional): precision for loading the rotation angles for basis rotation
compare_precision (float, optional): precision for comparing two numbers
Returns:
CompressedResourceOp: the operator in a compressed representation
"""
params = {
"compact_ham": compact_ham,
"coeff_precision": coeff_precision,
"rotation_precision": rotation_precision,
"compare_precision": compare_precision,
}
return CompressedResourceOp(cls, params)
[docs]
@classmethod
def default_resource_decomp(
cls,
compact_ham,
coeff_precision=1e-5,
rotation_precision=1e-5,
compare_precision=1e-3,
**kwargs,
) -> list[GateCount]:
r"""Returns a list representing the resources of the operator. Each object represents a quantum gate
and the number of times it occurs in the decomposition.
Args:
compact_ham (~pennylane.labs.resource_estimation.CompactHamiltonian): a tensor hypercontracted
Hamiltonian for which the walk operator is being created
coeff_precision (float, optional): precision for loading the coefficients of Hamiltonian
rotation_precision (float, optional): precision for loading the rotation angles for basis rotation
compare_precision (float, optional): precision for comparing two numbers
Returns:
list[GateCount]: A list of GateCount objects, where each object
represents a specific quantum gate and the number of times it appears
in the decomposition.
"""
num_orb = compact_ham.params["num_orbitals"]
tensor_rank = compact_ham.params["tensor_rank"]
rot_prec_wires = abs(np.floor(math.log2(rotation_precision)))
coeff_wires = abs(int(np.floor(math.log2(coeff_precision))))
compare_precision = abs(int(np.floor(math.log2(compare_precision))))
# Number of qubits needed for the integrals tensors
m_register = int(np.ceil(math.log2(2 * tensor_rank + 1)))
gate_list = []
# Select Circuit Fig. 5 in arXiv:2011.03494
# 1 register of rotation precision for loading the angles,
# 2 registers for resource state, 2 wires for spin registers
gate_list.append(AllocWires(3 * rot_prec_wires + 4))
# Prepare resource state
phasegrad = resource_rep(plre.ResourcePhaseGradient, {"num_wires": rot_prec_wires})
gate_list.append(plre.GateCount(phasegrad, 1))
# Loading the angles controlled on tensor indices
qrom_angle = resource_rep(
plre.ResourceQROM,
{
"num_bitstrings": 2**m_register,
"size_bitstring": rot_prec_wires,
"clean": False,
"select_swap_depth": 1,
},
)
gate_list.append(plre.GateCount(qrom_angle, 2 * (num_orb)))
# Cliffords for conversion to RZ rotations
# cost per rotation times 4 for the number of rotations per angle
h = resource_rep(plre.ResourceHadamard)
gate_list.append(plre.GateCount(h, 16 * (num_orb)))
z = resource_rep(plre.ResourceZ)
gate_list.append(plre.GateCount(z, 8 * (num_orb)))
cnot = resource_rep(plre.ResourceCNOT)
gate_list.append(plre.GateCount(cnot, 16 * (num_orb)))
x = resource_rep(plre.ResourceX)
gate_list.append(plre.GateCount(x, 8 * (num_orb)))
y = resource_rep(plre.ResourceY)
gate_list.append(plre.GateCount(y, 8 * (num_orb)))
# Swap gates for swapping the state register based on the state
cswap = resource_rep(plre.ResourceCSWAP)
gate_list.append(plre.GateCount(cswap, 4 * num_orb * (num_orb)))
# Phase gradient technique for rotation
semiadder = resource_rep(
plre.ResourceControlled,
{
"base_cmpr_op": resource_rep(
plre.ResourceSemiAdder,
{"max_register_size": rot_prec_wires},
),
"num_ctrl_wires": 1,
"num_ctrl_values": 0,
},
)
# 2 semiadders required per R gate, and there are 4 R gates per angle.
gate_list.append(plre.GateCount(semiadder, 8 * (num_orb)))
# iZ gate in the center of rotations
gate_list.append(plre.GateCount(x, 2 * num_orb))
gate_list.append(plre.GateCount(y, 2 * num_orb))
# unloading the angles
gate_list.append(plre.GateCount(qrom_angle, 2 * (num_orb)))
# Free spin and rotation precision wires
gate_list.append(FreeWires(rot_prec_wires + 2))
# Prepare and unprepare Circuit
# This is calculating only the Toffolis from Eq. 33 of arXiv:2011.03494
d = num_orb + tensor_rank * (2 * tensor_rank + 1)
nd = int(np.ceil(math.log2(num_orb + tensor_rank * (2 * tensor_rank + 1))))
gate_list.append(AllocWires(2 * m_register + nd + 2 * coeff_wires))
m = 2 * m_register + 2 + coeff_wires
toffoli = resource_rep(plre.ResourceToffoli)
num_toffoli = (
28 * m_register
+ 4 * compare_precision
- 18
+ 2 * m_register**2
+ 2 * coeff_wires
+ np.ceil(d / 16)
+ m * (16 - 1)
+ np.ceil(d / 16)
+ 16
)
gate_list.append(plre.GateCount(toffoli, num_toffoli))
# Reflection
mcx = resource_rep(
plre.ResourceMultiControlledX, {"num_ctrl_wires": nd, "num_ctrl_values": nd}
)
gate_list.append(plre.GateCount(h, 2))
gate_list.append(plre.GateCount(mcx))
return gate_list
_modules/pennylane/labs/resource_estimation/templates/qubitize
Download Python script
Download Notebook
View on GitHub