Skip to content

Commit

Permalink
Make Toffoli an atomic, leaf bloq (#1388)
Browse files Browse the repository at this point in the history
* Make Toffoli an atomic, leaf bloq

* Link issue
  • Loading branch information
mpharrigan authored Sep 6, 2024
1 parent 21922ee commit 098f7ea
Show file tree
Hide file tree
Showing 20 changed files with 110 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@
_signed_to_twos,
SignedIntegerToTwosComplement,
)
from qualtran.bloqs.basic_gates import TGate
from qualtran.bloqs.basic_gates import Toffoli


def test_signed_to_twos(bloq_autotester):
bloq_autotester(_signed_to_twos)


def test_signed_to_twos_complement_t_complexity():
def test_signed_to_twos_complement_toffoli_count():
bb = BloqBuilder()
bitsize = 5
q0 = bb.add_register('x', bitsize)
q0 = bb.add(SignedIntegerToTwosComplement(bitsize), x=q0)
cbloq = bb.finalize(x=q0)
_, sigma = cbloq.call_graph()
assert sigma[TGate()] == 4 * (5 - 2)
assert sigma[Toffoli()] == (5 - 2)
21 changes: 14 additions & 7 deletions qualtran/bloqs/basic_gates/toffoli.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,31 @@
# limitations under the License.
import itertools
from functools import cached_property
from typing import Dict, List, Optional, Set, Tuple, TYPE_CHECKING, Union
from typing import Dict, List, Optional, Tuple, TYPE_CHECKING, Union

import numpy as np
from attrs import frozen
from numpy.typing import NDArray

from qualtran import Bloq, bloq_example, BloqDocSpec, Connection, QBit, Register, Signature
from qualtran.bloqs.basic_gates import TGate
from qualtran import (
Bloq,
bloq_example,
BloqDocSpec,
CompositeBloq,
Connection,
DecomposeTypeError,
QBit,
Register,
Signature,
)
from qualtran.cirq_interop.t_complexity_protocol import TComplexity
from qualtran.resource_counting import SympySymbolAllocator

if TYPE_CHECKING:
import cirq
import quimb.tensor as qtn

from qualtran.cirq_interop import CirqQuregT
from qualtran.drawing import WireSymbol
from qualtran.resource_counting import BloqCountT, SympySymbolAllocator
from qualtran.simulation.classical_sim import ClassicalValT


Expand All @@ -56,8 +63,8 @@ def signature(self) -> Signature:
def adjoint(self) -> 'Bloq':
return self

def build_call_graph(self, ssa: 'SympySymbolAllocator') -> Set['BloqCountT']:
return {(TGate(), 4)}
def decompose_bloq(self) -> 'CompositeBloq':
raise DecomposeTypeError(f"{self} is atomic")

def _t_complexity_(self):
return TComplexity(t=4)
Expand Down
9 changes: 3 additions & 6 deletions qualtran/bloqs/basic_gates/toffoli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import numpy as np

from qualtran import BloqBuilder
from qualtran.bloqs.basic_gates import TGate, Toffoli, ZeroState
from qualtran.bloqs.basic_gates import Toffoli, ZeroState
from qualtran.bloqs.basic_gates.toffoli import _toffoli
from qualtran.drawing.musical_score import Circle, ModPlus
from qualtran.testing import assert_wire_symbols_match_expected
Expand All @@ -27,12 +27,9 @@ def test_toffoli(bloq_autotester):
bloq_autotester(_toffoli)


def test_toffoli_t_count():
counts = Toffoli().bloq_counts()
assert counts == {TGate(): 4}

def test_toffoli_sigma():
_, sigma = Toffoli().call_graph()
assert sigma == {TGate(): 4}
assert sigma == {Toffoli(): 1}


def test_toffoli_cirq():
Expand Down
8 changes: 4 additions & 4 deletions qualtran/bloqs/chemistry/df/prepare_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

from openfermion.resource_estimates.utils import power_two, QI, QR

from qualtran.bloqs.basic_gates import TGate
from qualtran.bloqs.basic_gates import Toffoli
from qualtran.bloqs.chemistry.df.prepare import (
_indexed_data,
_prep_inner,
Expand Down Expand Up @@ -70,7 +70,7 @@ def test_outerprep_t_counts():
assert toff == cost1a_mod + cost1b + cost1cd


def test_indexed_data_t_counts():
def test_indexed_data_toffoli_counts():
num_spin_orb = 108
num_aux = 360
num_bits_rot_aa = 7
Expand All @@ -79,12 +79,12 @@ def test_indexed_data_t_counts():
num_aux=num_aux, num_spin_orb=num_spin_orb, num_eig=num_eig, num_bits_rot_aa=num_bits_rot_aa
)
_, counts = in_l_data_l.call_graph()
toff = counts[TGate()] // 4
toff = counts[Toffoli()]
in_l_data_l = OutputIndexedData(
num_aux=num_aux, num_spin_orb=num_spin_orb, num_eig=num_eig, num_bits_rot_aa=num_bits_rot_aa
).adjoint()
_, counts = in_l_data_l.call_graph()
toff += counts[TGate()] // 4
toff += counts[Toffoli()]
# captured from cost2 in openfermion df.compute_cost
nxi = (num_spin_orb // 2 - 1).bit_length()
nlxi = (num_eig + num_spin_orb // 2 - 1).bit_length()
Expand Down
6 changes: 3 additions & 3 deletions qualtran/bloqs/chemistry/df/select_bloq_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from openfermion.resource_estimates.utils import QI, QR

from qualtran.bloqs.basic_gates import TGate
from qualtran.bloqs.basic_gates import Toffoli
from qualtran.bloqs.chemistry.df.select_bloq import ProgRotGateArray


Expand All @@ -27,12 +27,12 @@ def test_rotations():
num_aux=num_aux, num_eig=num_eig, num_spin_orb=num_spin_orb, num_bits_rot=num_bits_rot
)
_, counts = rot.call_graph()
toff = counts[TGate()] // 4
toff = counts[Toffoli()]
rot = ProgRotGateArray(
num_aux=num_aux, num_eig=num_eig, num_spin_orb=num_spin_orb, num_bits_rot=num_bits_rot
).adjoint()
_, counts = rot.call_graph()
toff += counts[TGate()] // 4
toff += counts[Toffoli()]
toff *= 2 # cost is for the two applications of the (rot, rot^) pair
# the rot gate array includes the offset addition, qrom and cost for applying the rotations.
# it does not include the swaps and the controlled Z which is included in the openfermion costs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from qualtran.bloqs.basic_gates import TGate
from qualtran.bloqs.basic_gates import Toffoli
from qualtran.bloqs.chemistry.pbc.first_quantization.prepare import (
UniformSuperpostionIJFirstQuantization,
)
Expand All @@ -34,15 +34,14 @@ def test_prepare_kinetic_t_counts():
expected_cost = (14 * n_eta + 8 * b_r - 36) + 2 * (2 * num_bits_p + 9)
uni = UniformSuperpostionIJFirstQuantization(eta, num_bits_rot_aa=b_r)
_, counts = uni.call_graph()
qual_cost = counts[TGate()]
qual_cost = counts[Toffoli()]
uni = UniformSuperpostionIJFirstQuantization(eta, num_bits_rot_aa=b_r).adjoint()
_, counts = uni.call_graph()
qual_cost += counts[TGate()]
qual_cost += counts[Toffoli()]
prep = PrepareTFirstQuantization(num_bits_p, eta, num_bits_rot_aa=b_r)
_, counts = prep.call_graph()
qual_cost += counts[TGate()]
qual_cost += counts[Toffoli()]
prep = PrepareTFirstQuantization(num_bits_p, eta, num_bits_rot_aa=b_r).adjoint()
_, counts = prep.call_graph()
qual_cost += counts[TGate()]
qual_cost //= 4
qual_cost += counts[Toffoli()]
assert qual_cost == expected_cost
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import pytest

from qualtran.bloqs.basic_gates import TGate
from qualtran.bloqs.basic_gates import Toffoli
from qualtran.bloqs.chemistry.pbc.first_quantization.projectile.prepare_t import (
_prep_power_two_proj,
_prep_t_proj,
Expand All @@ -40,13 +40,12 @@ def test_prepare_kinetic_t_proj_counts():
qual_cost = 0
prep = PrepareTFirstQuantizationWithProj(num_bits_p, num_bits_n, eta, num_bits_rot_aa=b_r)
_, counts = prep.call_graph()
qual_cost += counts[TGate()]
qual_cost += counts[Toffoli()]
prep = PrepareTFirstQuantizationWithProj(
num_bits_p, num_bits_n, eta, num_bits_rot_aa=b_r
).adjoint()
_, counts = prep.call_graph()
qual_cost += counts[TGate()]
qual_cost //= 4
qual_cost += counts[Toffoli()]
assert qual_cost == expected_cost


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import numpy as np
import pytest

from qualtran.bloqs.basic_gates import TGate
from qualtran.bloqs.chemistry.pbc.first_quantization.projectile.select_and_prepare import (
_prep_first_quant,
_sel_first_quant,
Expand Down Expand Up @@ -68,7 +67,7 @@ def test_select_t_costs():
assert cost == expected_cost


def test_prepare_t_costs():
def test_prepare_toffoli_costs():
num_bits_p = 6
num_bits_n = 8
eta = 10
Expand All @@ -90,7 +89,7 @@ def test_prepare_t_costs():
num_bits_rot_aa=b_r,
num_bits_t=num_bits_t,
)
cost += prep_first_quant.call_graph()[1][TGate()] // 4
cost += get_cost_value(prep_first_quant, QECGatesCost()).total_toffoli_only()
prep_first_quant = PrepareFirstQuantizationWithProj(
num_bits_p,
num_bits_n,
Expand All @@ -102,7 +101,7 @@ def test_prepare_t_costs():
num_bits_rot_aa=b_r,
num_bits_t=num_bits_t,
).adjoint()
cost += prep_first_quant.call_graph()[1][TGate()] // 4
cost += get_cost_value(prep_first_quant, QECGatesCost()).total_toffoli_only()
n_eta = (eta - 1).bit_length()
expected_cost = 6 * num_bits_t + 2 # C1
expected_cost += 14 * n_eta + 8 * b_r - 36 # C2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# 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.
from qualtran.bloqs.basic_gates import TGate
from qualtran.bloqs.basic_gates import Toffoli
from qualtran.bloqs.chemistry.pbc.first_quantization.projectile.select_t import (
_sel_t_proj,
SelectTFirstQuantizationWithProj,
Expand All @@ -26,4 +26,4 @@ def test_select_kinetic_t_counts():
num_bits_n = 6
sel = SelectTFirstQuantizationWithProj(num_bits_n, 10)
_, counts = sel.call_graph()
assert counts[TGate()] // 4 == 5 * (num_bits_n - 1) + 2 + 1
assert counts[Toffoli()] == 5 * (num_bits_n - 1) + 2 + 1
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@
# 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.
from qualtran.bloqs.basic_gates import TGate
from qualtran.bloqs.chemistry.pbc.first_quantization.projectile.select_uv import (
_sel_uv_proj,
SelectUVFirstQuantizationWithProj,
)
from qualtran.resource_counting import get_cost_value, QECGatesCost


def test_sel_uv_proj(bloq_autotester):
bloq_autotester(_sel_uv_proj)


def test_select_uv_t_counts():
def test_select_uv_toffoli_counts():
num_bits_p = 6
num_bits_n = 9
eta = 10
Expand All @@ -32,8 +32,7 @@ def test_select_uv_t_counts():
expected_cost += 3 * num_bits_p + 3 * num_bits_n
expected_cost += 3 * (2 * num_bits_n * num_bits_nuc_pos - num_bits_n * (num_bits_n + 1) - 1)
sel = SelectUVFirstQuantizationWithProj(num_bits_p, num_bits_n, eta, eta, num_bits_nuc_pos)
_, counts = sel.call_graph()
qual_cost = counts[TGate()] // 4
qual_cost = get_cost_value(sel, QECGatesCost()).total_toffoli_only()
# -6 due to different cost of addition.
qual_cost += 6
assert qual_cost == expected_cost
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
import numpy as np
import pytest

from qualtran.bloqs.basic_gates import TGate
from qualtran.bloqs.chemistry.pbc.first_quantization.select_and_prepare import (
_prep_first_quant,
_sel_first_quant,
PrepareFirstQuantization,
SelectFirstQuantization,
)
from qualtran.resource_counting import get_cost_value, QECGatesCost
from qualtran.testing import execute_notebook


Expand Down Expand Up @@ -49,7 +49,7 @@ def test_select_t_costs():
sel_first_quant = SelectFirstQuantization(
num_bits_p, eta, num_atoms, lambda_zeta, num_bits_nuc_pos=num_bits_nuc_pos
)
cost += sel_first_quant.call_graph()[1][TGate()]
cost += get_cost_value(sel_first_quant, QECGatesCost()).total_t_count()

expected_cost = 7 * (12 * eta * num_bits_p) + 4 * (4 * eta - 8)
expected_cost += 4 * (5 * (num_bits_p - 1) + 2)
Expand All @@ -61,7 +61,7 @@ def test_select_t_costs():
assert cost == expected_cost


def test_prepare_t_costs():
def test_prepare_toffoli_costs():
num_bits_p = 6
eta = 10
num_atoms = 10
Expand All @@ -81,7 +81,7 @@ def test_prepare_t_costs():
num_bits_rot_aa=b_r,
num_bits_t=num_bits_t,
)
cost += prep_first_quant.call_graph()[1][TGate()] // 4
cost += get_cost_value(prep_first_quant, QECGatesCost()).total_toffoli_only()
prep_first_quant = PrepareFirstQuantization(
num_bits_p,
eta,
Expand All @@ -92,7 +92,7 @@ def test_prepare_t_costs():
num_bits_rot_aa=b_r,
num_bits_t=num_bits_t,
).adjoint()
cost += prep_first_quant.call_graph()[1][TGate()] // 4
cost += get_cost_value(prep_first_quant, QECGatesCost()).total_toffoli_only()
n_eta = (eta - 1).bit_length()
expected_cost = (14 * n_eta + 8 * b_r - 36) + 2 * (2 * num_bits_p + 9)
expected_cost += 3 * num_bits_p**2 + num_bits_p + 4 * num_bits_m * (num_bits_p + 1) + 4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# 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.
from qualtran.bloqs.basic_gates import TGate
from qualtran.bloqs.basic_gates import Toffoli
from qualtran.bloqs.chemistry.pbc.first_quantization.select_t import (
_select_t,
SelectTFirstQuantization,
Expand All @@ -26,4 +26,4 @@ def test_select_kinetic_t_counts():
num_bits_p = 6
sel = SelectTFirstQuantization(num_bits_p, 10)
_, counts = sel.call_graph()
assert counts[TGate()] == 4 * 5 * (num_bits_p - 1) + 4 * 2
assert counts[Toffoli()] == 5 * (num_bits_p - 1) + 2
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,25 @@
# 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.
from qualtran.bloqs.basic_gates import TGate
from qualtran.bloqs.chemistry.pbc.first_quantization.select_uv import (
_select_uv,
SelectUVFirstQuantization,
)
from qualtran.resource_counting import get_cost_value, QECGatesCost


def test_select_uv(bloq_autotester):
bloq_autotester(_select_uv)


def test_select_uv_t_counts():
def test_select_uv_toffoli_counts():
num_bits_p = 6
eta = 10
num_bits_nuc_pos = 8
expected_cost = 24 * num_bits_p + 3 * (
2 * num_bits_p * num_bits_nuc_pos - num_bits_p * (num_bits_p + 1) - 1
)
sel = SelectUVFirstQuantization(num_bits_p, eta, eta, num_bits_nuc_pos)
_, counts = sel.call_graph()
qual_cost = counts[TGate()] // 4
qual_cost = get_cost_value(sel, QECGatesCost()).total_toffoli_only()
# + 6 as they cost additon as nbits not nbits - 1, there are 6 additions / subtractions.
assert qual_cost + 6 == expected_cost
Loading

0 comments on commit 098f7ea

Please sign in to comment.