diff --git a/dev_tools/qualtran_dev_tools/notebook_specs.py b/dev_tools/qualtran_dev_tools/notebook_specs.py
index f0b156b58..292f56869 100644
--- a/dev_tools/qualtran_dev_tools/notebook_specs.py
+++ b/dev_tools/qualtran_dev_tools/notebook_specs.py
@@ -66,6 +66,7 @@
 import qualtran.bloqs.chemistry.hubbard_model.qubitization
 import qualtran.bloqs.chemistry.pbc.first_quantization.prepare_t
 import qualtran.bloqs.chemistry.pbc.first_quantization.prepare_uv
+import qualtran.bloqs.chemistry.pbc.first_quantization.prepare_zeta
 import qualtran.bloqs.chemistry.pbc.first_quantization.projectile.select_and_prepare
 import qualtran.bloqs.chemistry.pbc.first_quantization.select_t
 import qualtran.bloqs.chemistry.pbc.first_quantization.select_uv
@@ -311,6 +312,7 @@
             qualtran.bloqs.chemistry.pbc.first_quantization.prepare_uv._PREPARE_UV,
             qualtran.bloqs.chemistry.pbc.first_quantization.select_t._SELECT_T,
             qualtran.bloqs.chemistry.pbc.first_quantization.select_uv._SELECT_UV,
+            qualtran.bloqs.chemistry.pbc.first_quantization.prepare_zeta._PREPARE_ZETA,
         ],
         directory=f'{SOURCE_DIR}/bloqs/chemistry/pbc/first_quantization',
     ),
diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/first_quantization.ipynb b/qualtran/bloqs/chemistry/pbc/first_quantization/first_quantization.ipynb
index 69a0ea74a..b1de1dbf2 100644
--- a/qualtran/bloqs/chemistry/pbc/first_quantization/first_quantization.ipynb
+++ b/qualtran/bloqs/chemistry/pbc/first_quantization/first_quantization.ipynb
@@ -822,6 +822,119 @@
     "show_call_graph(select_uv_g)\n",
     "show_counts_sigma(select_uv_sigma)"
    ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "69c98b9f",
+   "metadata": {
+    "cq.autogen": "PrepareZetaState.bloq_doc.md"
+   },
+   "source": [
+    "## `PrepareZetaState`\n",
+    "PREPARE the superpostion over $l$ weighted by $\\zeta_l$.\n",
+    "\n",
+    "See https://github.com/quantumlib/Qualtran/issues/473.\n",
+    "#### Parameters\n",
+    " - `num_bits_p`: The number of bits to represent each dimension of the momentum register.\n",
+    " - `eta`: The number of electrons.\n",
+    " - `m_param`: $\\mathcal{M}$ in the reference.\n",
+    " - `lambda_zeta`: sum of nuclear charges. \n",
+    "\n",
+    "#### Registers\n",
+    " - `l`: the register indexing the atomic number. \n",
+    "\n",
+    "#### References\n",
+    " - [Fault-Tolerant Quantum Simulations of Chemistry in First Quantization](https://arxiv.org/abs/2105.12767). page 23-24, last 3 paragraphs.\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "074bc1b8",
+   "metadata": {
+    "cq.autogen": "PrepareZetaState.bloq_doc.py"
+   },
+   "outputs": [],
+   "source": [
+    "from qualtran.bloqs.chemistry.pbc.first_quantization.prepare_zeta import PrepareZetaState"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "fd3071f7",
+   "metadata": {
+    "cq.autogen": "PrepareZetaState.example_instances.md"
+   },
+   "source": [
+    "### Example Instances"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "31ee49a4",
+   "metadata": {
+    "cq.autogen": "PrepareZetaState.prepare_zeta"
+   },
+   "outputs": [],
+   "source": [
+    "num_atoms = 10\n",
+    "lambda_zeta = 10\n",
+    "num_bits_nuc_pos = 8\n",
+    "\n",
+    "prepare_zeta = PrepareZetaState(\n",
+    "    num_atoms=num_atoms, lambda_zeta=lambda_zeta, num_bits_nuc_pos=num_bits_nuc_pos\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4fb19c71",
+   "metadata": {
+    "cq.autogen": "PrepareZetaState.graphical_signature.md"
+   },
+   "source": [
+    "#### Graphical Signature"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "6498eee0",
+   "metadata": {
+    "cq.autogen": "PrepareZetaState.graphical_signature.py"
+   },
+   "outputs": [],
+   "source": [
+    "from qualtran.drawing import show_bloqs\n",
+    "show_bloqs([prepare_zeta],\n",
+    "           ['`prepare_zeta`'])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "1e644753",
+   "metadata": {
+    "cq.autogen": "PrepareZetaState.call_graph.md"
+   },
+   "source": [
+    "### Call Graph"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "fbef8e6e",
+   "metadata": {
+    "cq.autogen": "PrepareZetaState.call_graph.py"
+   },
+   "outputs": [],
+   "source": [
+    "from qualtran.resource_counting.generalizers import ignore_split_join\n",
+    "prepare_zeta_g, prepare_zeta_sigma = prepare_zeta.call_graph(max_depth=1, generalizer=ignore_split_join)\n",
+    "show_call_graph(prepare_zeta_g)\n",
+    "show_counts_sigma(prepare_zeta_sigma)"
+   ]
   }
  ],
  "metadata": {
diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_t_test.py b/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_t_test.py
index f495ff791..afa581df1 100644
--- a/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_t_test.py
+++ b/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_t_test.py
@@ -12,7 +12,6 @@
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 
-from qualtran.bloqs.basic_gates import Toffoli
 from qualtran.bloqs.chemistry.pbc.first_quantization.prepare import (
     UniformSuperpostionIJFirstQuantization,
 )
@@ -20,6 +19,7 @@
     _prepare_t,
     PrepareTFirstQuantization,
 )
+from qualtran.resource_counting import get_cost_value, QECGatesCost
 
 
 def test_prepare_t(bloq_autotester):
@@ -33,15 +33,12 @@ def test_prepare_kinetic_t_counts():
     n_eta = (eta - 1).bit_length()
     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[Toffoli()]
+
+    qual_cost = get_cost_value(uni, QECGatesCost()).total_toffoli_only()
     uni = UniformSuperpostionIJFirstQuantization(eta, num_bits_rot_aa=b_r).adjoint()
-    _, counts = uni.call_graph()
-    qual_cost += counts[Toffoli()]
+    qual_cost += get_cost_value(uni, QECGatesCost()).total_toffoli_only()
     prep = PrepareTFirstQuantization(num_bits_p, eta, num_bits_rot_aa=b_r)
-    _, counts = prep.call_graph()
-    qual_cost += counts[Toffoli()]
+    qual_cost += get_cost_value(prep, QECGatesCost()).total_toffoli_only()
     prep = PrepareTFirstQuantization(num_bits_p, eta, num_bits_rot_aa=b_r).adjoint()
-    _, counts = prep.call_graph()
-    qual_cost += counts[Toffoli()]
+    qual_cost += get_cost_value(prep, QECGatesCost()).total_toffoli_only()
     assert qual_cost == expected_cost
diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_zeta.py b/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_zeta.py
index de328009e..dc76e1ccf 100644
--- a/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_zeta.py
+++ b/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_zeta.py
@@ -19,7 +19,7 @@
 import numpy as np
 from attrs import evolve, frozen
 
-from qualtran import Bloq, QAny, Register, Signature
+from qualtran import Bloq, bloq_example, BloqDocSpec, QAny, Register, Signature
 from qualtran.bloqs.basic_gates import Toffoli
 
 if TYPE_CHECKING:
@@ -65,3 +65,22 @@ def build_call_graph(self, ssa: 'SympySymbolAllocator') -> 'BloqCountDictT':
         else:
 
             return {Toffoli(): self.lambda_zeta}
+
+
+@bloq_example
+def _prepare_zeta() -> PrepareZetaState:
+    num_atoms = 10
+    lambda_zeta = 10
+    num_bits_nuc_pos = 8
+
+    prepare_zeta = PrepareZetaState(
+        num_atoms=num_atoms, lambda_zeta=lambda_zeta, num_bits_nuc_pos=num_bits_nuc_pos
+    )
+    return prepare_zeta
+
+
+_PREPARE_ZETA = BloqDocSpec(
+    bloq_cls=PrepareZetaState,
+    import_line='from qualtran.bloqs.chemistry.pbc.first_quantization.prepare_zeta import PrepareZetaState',
+    examples=(_prepare_zeta,),
+)
diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_zeta_test.py b/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_zeta_test.py
index 212f1adba..55cb3afc0 100644
--- a/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_zeta_test.py
+++ b/qualtran/bloqs/chemistry/pbc/first_quantization/prepare_zeta_test.py
@@ -11,8 +11,8 @@
 #  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.chemistry.pbc.first_quantization.prepare_zeta import PrepareZetaState
+from qualtran.bloqs.chemistry.pbc.first_quantization.prepare_zeta import _prepare_zeta
 
 
-def test_uniform_superposition_ij():
-    prep = PrepareZetaState(num_atoms=10, lambda_zeta=20, num_bits_nuc_pos=8)
+def test_prepare_zeta(bloq_autotester):
+    bloq_autotester(_prepare_zeta)
diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/prepare_t_test.py b/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/prepare_t_test.py
index ba3c715a7..a250da50e 100644
--- a/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/prepare_t_test.py
+++ b/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/prepare_t_test.py
@@ -14,13 +14,13 @@
 
 import pytest
 
-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,
     PreparePowerTwoStateWithProj,
     PrepareTFirstQuantizationWithProj,
 )
+from qualtran.resource_counting import get_cost_value, QECGatesCost
 
 
 def test_prep_t_proj(bloq_autotester):
@@ -39,13 +39,11 @@ def test_prepare_kinetic_t_proj_counts():
     expected_cost = 2 * (2 * num_bits_n + 9) + 2 * (num_bits_n - num_bits_p) + 20
     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[Toffoli()]
+    qual_cost += get_cost_value(prep, QECGatesCost()).total_toffoli_only()
     prep = PrepareTFirstQuantizationWithProj(
         num_bits_p, num_bits_n, eta, num_bits_rot_aa=b_r
     ).adjoint()
-    _, counts = prep.call_graph()
-    qual_cost += counts[Toffoli()]
+    qual_cost += get_cost_value(prep, QECGatesCost()).total_toffoli_only()
     assert qual_cost == expected_cost
 
 
diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/select_t_test.py b/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/select_t_test.py
index b654c30ac..f0752fda4 100644
--- a/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/select_t_test.py
+++ b/qualtran/bloqs/chemistry/pbc/first_quantization/projectile/select_t_test.py
@@ -11,11 +11,11 @@
 #  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 Toffoli
 from qualtran.bloqs.chemistry.pbc.first_quantization.projectile.select_t import (
     _sel_t_proj,
     SelectTFirstQuantizationWithProj,
 )
+from qualtran.resource_counting import get_cost_value, QECGatesCost
 
 
 def test_sel_t_proj(bloq_autotester):
@@ -25,5 +25,5 @@ def test_sel_t_proj(bloq_autotester):
 def test_select_kinetic_t_counts():
     num_bits_n = 6
     sel = SelectTFirstQuantizationWithProj(num_bits_n, 10)
-    _, counts = sel.call_graph()
-    assert counts[Toffoli()] == 5 * (num_bits_n - 1) + 2 + 1
+    toffolis = get_cost_value(sel, QECGatesCost()).total_toffoli_only()
+    assert toffolis == 5 * (num_bits_n - 1) + 2 + 1
diff --git a/qualtran/bloqs/chemistry/pbc/first_quantization/select_t_test.py b/qualtran/bloqs/chemistry/pbc/first_quantization/select_t_test.py
index 46f1cd2a4..58dec6f7e 100644
--- a/qualtran/bloqs/chemistry/pbc/first_quantization/select_t_test.py
+++ b/qualtran/bloqs/chemistry/pbc/first_quantization/select_t_test.py
@@ -11,11 +11,11 @@
 #  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 Toffoli
 from qualtran.bloqs.chemistry.pbc.first_quantization.select_t import (
     _select_t,
     SelectTFirstQuantization,
 )
+from qualtran.resource_counting import get_cost_value, QECGatesCost
 
 
 def test_select_t(bloq_autotester):
@@ -25,5 +25,5 @@ def test_select_t(bloq_autotester):
 def test_select_kinetic_t_counts():
     num_bits_p = 6
     sel = SelectTFirstQuantization(num_bits_p, 10)
-    _, counts = sel.call_graph()
-    assert counts[Toffoli()] == 5 * (num_bits_p - 1) + 2
+    toffolis = get_cost_value(sel, QECGatesCost()).total_toffoli_only()
+    assert toffolis == 5 * (num_bits_p - 1) + 2
diff --git a/qualtran/bloqs/chemistry/resource_estimation.ipynb b/qualtran/bloqs/chemistry/resource_estimation.ipynb
index 9218086ee..39585e49e 100644
--- a/qualtran/bloqs/chemistry/resource_estimation.ipynb
+++ b/qualtran/bloqs/chemistry/resource_estimation.ipynb
@@ -84,7 +84,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 1,
+   "execution_count": null,
    "id": "2fb17f7f",
    "metadata": {},
    "outputs": [],
@@ -113,7 +113,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": null,
    "id": "b9991178",
    "metadata": {},
    "outputs": [],
@@ -202,7 +202,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": null,
    "id": "5efd66ae",
    "metadata": {},
    "outputs": [],
@@ -305,7 +305,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 10,
+   "execution_count": null,
    "id": "614bfb21",
    "metadata": {},
    "outputs": [],
@@ -346,9 +346,8 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "_, sigma = df_bloq.call_graph()\n",
     "refl_cost_and_qpe = (df_bloq.num_aux -1).bit_length() + (num_spin_orb // 2 - 1).bit_length() + num_bits_state_prep + 1 + 2\n",
-    "print(f'qualtran cost = {sigma[TGate()] // 4} vs paper = {21753 - refl_cost_and_qpe}')"
+    "print(f'qualtran cost = {get_toffoli_counts(df_bloq)} vs paper = {21753 - refl_cost_and_qpe}')"
    ]
   },
   {
@@ -363,7 +362,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": null,
    "id": "349b70ab",
    "metadata": {},
    "outputs": [],
@@ -384,7 +383,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": null,
    "id": "2ef506ab",
    "metadata": {},
    "outputs": [],
@@ -411,7 +410,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": null,
    "id": "e34dcb1c",
    "metadata": {},
    "outputs": [],
diff --git a/qualtran/bloqs/chemistry/trotter/grid_ham/inverse_sqrt_test.py b/qualtran/bloqs/chemistry/trotter/grid_ham/inverse_sqrt_test.py
index a1af126a1..43ecf9c31 100644
--- a/qualtran/bloqs/chemistry/trotter/grid_ham/inverse_sqrt_test.py
+++ b/qualtran/bloqs/chemistry/trotter/grid_ham/inverse_sqrt_test.py
@@ -41,7 +41,6 @@ def test_newton_raphson_inverse_sqrt_bloq_counts():
     poly_bitsize = 15
     target_bitsize = 22
     bloq = NewtonRaphsonApproxInverseSquareRoot(int_bitsize, poly_bitsize, target_bitsize)
-    _, counts = bloq.call_graph()
     cost_square = poly_bitsize**2 // 2 - 4
     cost_scale = poly_bitsize * (2 * int_bitsize - 1) - int_bitsize**2
     cost_mult = 2 * (target_bitsize**2 - target_bitsize - 1)
diff --git a/qualtran/bloqs/chemistry/trotter/hubbard/hopping_test.py b/qualtran/bloqs/chemistry/trotter/hubbard/hopping_test.py
index 652664817..006c1c6c3 100644
--- a/qualtran/bloqs/chemistry/trotter/hubbard/hopping_test.py
+++ b/qualtran/bloqs/chemistry/trotter/hubbard/hopping_test.py
@@ -11,15 +11,12 @@
 #  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 import Bloq
-from qualtran.bloqs.basic_gates import Rz, TGate, ZPowGate
-from qualtran.bloqs.bookkeeping import ArbitraryClifford
 from qualtran.bloqs.chemistry.trotter.hubbard.hopping import (
     _hopping_tile,
     _hopping_tile_hwp,
     _plaquette,
 )
-from qualtran.resource_counting.generalizers import PHI
+from qualtran.resource_counting import get_cost_value, QECGatesCost
 
 
 def test_hopping_tile(bloq_autotester):
@@ -30,27 +27,18 @@ def test_hopping_plaquette(bloq_autotester):
     bloq_autotester(_plaquette)
 
 
-def catch_rotations(bloq) -> Bloq:
-    if isinstance(bloq, (Rz, ZPowGate)):
-        if isinstance(bloq, ZPowGate):
-            return Rz(angle=PHI)
-        elif abs(float(bloq.angle)) < 1e-12:
-            return ArbitraryClifford(1)
-        else:
-            return Rz(angle=PHI)
-    return bloq
-
-
 def test_hopping_tile_t_counts():
     bloq = _hopping_tile()
-    _, counts = bloq.call_graph(generalizer=catch_rotations)
-    assert counts[TGate()] == 8 * bloq.length**2 // 2
-    assert counts[Rz(PHI)] == 2 * bloq.length**2 // 2
+    costs = get_cost_value(bloq, QECGatesCost())
+    assert costs.t == 8 * bloq.length**2 // 2
+    assert costs.rotation == 2 * bloq.length**2 // 2
 
 
 def test_hopping_tile_hwp_t_counts():
     bloq = _hopping_tile_hwp()
-    _, counts = bloq.call_graph(generalizer=catch_rotations)
+    costs = get_cost_value(bloq, QECGatesCost())
     n_rot_par = bloq.length**2 // 2
-    assert counts[Rz(PHI)] == 2 * n_rot_par.bit_length()
-    assert counts[TGate()] == 8 * bloq.length**2 // 2 + 2 * 4 * (n_rot_par - n_rot_par.bit_count())
+    assert costs.rotation == 2 * n_rot_par.bit_length()
+    assert costs.total_t_count(ts_per_rotation=0) == 8 * bloq.length**2 // 2 + 2 * 4 * (
+        n_rot_par - n_rot_par.bit_count()
+    )
diff --git a/qualtran/bloqs/chemistry/trotter/hubbard/hubbard.ipynb b/qualtran/bloqs/chemistry/trotter/hubbard/hubbard.ipynb
index 54fab69e6..0d46f45fd 100644
--- a/qualtran/bloqs/chemistry/trotter/hubbard/hubbard.ipynb
+++ b/qualtran/bloqs/chemistry/trotter/hubbard/hubbard.ipynb
@@ -616,7 +616,7 @@
    "outputs": [],
    "source": [
     "length = 8\n",
-    "angle = 0.5\n",
+    "angle = 0.52728\n",
     "hubb_u = 4.0\n",
     "interaction_hwp = InteractionHWP(length, angle, hubb_u)"
    ]
diff --git a/qualtran/bloqs/chemistry/trotter/hubbard/interaction.py b/qualtran/bloqs/chemistry/trotter/hubbard/interaction.py
index 9783ee833..5a1d23cd0 100644
--- a/qualtran/bloqs/chemistry/trotter/hubbard/interaction.py
+++ b/qualtran/bloqs/chemistry/trotter/hubbard/interaction.py
@@ -130,7 +130,7 @@ def _interaction() -> Interaction:
 @bloq_example
 def _interaction_hwp() -> InteractionHWP:
     length = 8
-    angle = 0.5
+    angle = 0.52728
     hubb_u = 4.0
     interaction_hwp = InteractionHWP(length, angle, hubb_u)
     return interaction_hwp
diff --git a/qualtran/bloqs/chemistry/trotter/hubbard/interaction_test.py b/qualtran/bloqs/chemistry/trotter/hubbard/interaction_test.py
index 43b0479b5..deeccb2a5 100644
--- a/qualtran/bloqs/chemistry/trotter/hubbard/interaction_test.py
+++ b/qualtran/bloqs/chemistry/trotter/hubbard/interaction_test.py
@@ -11,10 +11,9 @@
 #  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 Rz, TGate
-from qualtran.bloqs.chemistry.trotter.hubbard.hopping_test import catch_rotations
+
 from qualtran.bloqs.chemistry.trotter.hubbard.interaction import _interaction, _interaction_hwp
-from qualtran.resource_counting.generalizers import PHI
+from qualtran.resource_counting import get_cost_value, QECGatesCost
 
 
 def test_hopping_tile(bloq_autotester):
@@ -27,14 +26,14 @@ def test_interaction_hwp(bloq_autotester):
 
 def test_interaction_hwp_bloq_counts():
     bloq = _interaction_hwp()
-    _, counts = bloq.call_graph(generalizer=catch_rotations)
+    costs = get_cost_value(bloq, QECGatesCost())
     n_rot_par = bloq.length**2 // 2
-    assert counts[Rz(PHI)] == 2 * n_rot_par.bit_length()
-    assert counts[TGate()] == 2 * 4 * (n_rot_par - n_rot_par.bit_count())
+    assert costs.rotation == 2 * n_rot_par.bit_length()
+    assert costs.total_t_count(ts_per_rotation=0) == 2 * 4 * (n_rot_par - n_rot_par.bit_count())
 
 
 def test_interaction_bloq_counts():
     bloq = _interaction()
-    _, counts = bloq.call_graph(generalizer=catch_rotations)
+    costs = get_cost_value(bloq, QECGatesCost())
     n_rot = bloq.length**2
-    assert counts[Rz(PHI)] == n_rot
+    assert costs.rotation == n_rot
diff --git a/qualtran/bloqs/chemistry/trotter/hubbard/qpe_cost_optimization.ipynb b/qualtran/bloqs/chemistry/trotter/hubbard/qpe_cost_optimization.ipynb
index 6f4e10cdd..ef5a26f31 100644
--- a/qualtran/bloqs/chemistry/trotter/hubbard/qpe_cost_optimization.ipynb
+++ b/qualtran/bloqs/chemistry/trotter/hubbard/qpe_cost_optimization.ipynb
@@ -106,36 +106,21 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "from typing import Dict, Union, Tuple\n",
+    "from typing import Tuple\n",
     "\n",
     "import numpy as np\n",
     "import sympy\n",
+    "import attrs\n",
     "\n",
-    "from qualtran.resource_counting.classify_bloqs import bloq_is_rotation\n",
-    "from qualtran.resource_counting.generalizers import PHI\n",
     "from qualtran.cirq_interop.t_complexity_protocol import TComplexity\n",
-    "from qualtran import Bloq\n",
-    "from qualtran.bloqs.basic_gates import TGate, Rz\n",
-    "from qualtran.bloqs.bookkeeping import ArbitraryClifford\n",
-    "\n",
-    "\n",
-    "def catch_rotations(bloq) -> Bloq:\n",
-    "    \"\"\"Generalizer to catch rotations.\"\"\"\n",
-    "    if isinstance(bloq, Rz):\n",
-    "        if isinstance(bloq.angle, float) and abs(bloq.angle) < 1e-12:\n",
-    "            return ArbitraryClifford(1)\n",
-    "        else:\n",
-    "            return Rz(angle=PHI, eps=bloq.eps)\n",
-    "    return bloq\n",
+    "from qualtran.resource_counting import get_cost_value, QECGatesCost\n",
     "\n",
     "\n",
-    "def t_and_rot_counts_from_sigma(sigma: Dict['Bloq', Union[int, 'sympy.Expr']]) -> Tuple[int, int]:\n",
-    "    ret = sigma.get(TGate(), 0)\n",
-    "    n_rot = 0\n",
-    "    for bloq, counts in sigma.items():\n",
-    "        if bloq_is_rotation(bloq):\n",
-    "            n_rot += counts\n",
-    "    return ret, n_rot\n",
+    "def t_and_rot_counts_from_bloq(bloq) -> Tuple[int, int]:\n",
+    "    costs = get_cost_value(bloq, QECGatesCost())\n",
+    "    n_rot = costs.rotation\n",
+    "    n_t = costs.total_t_count(ts_per_rotation=0)\n",
+    "    return n_t, n_rot\n",
     "\n",
     "\n",
     "def timestep_from_params(delta_ts: float, xi: float, prod_ord: int) -> float:\n",
@@ -265,7 +250,7 @@
     "from qualtran.bloqs.chemistry.trotter.hubbard.trotter_step import build_plaq_unitary_second_order_suzuki\n",
     "\n",
     "trotter_step = build_plaq_unitary_second_order_suzuki(length, hubb_u, timestep, eps=1e-10)\n",
-    "n_t, n_rot = t_and_rot_counts_from_sigma(trotter_step.call_graph(generalizer=catch_rotations)[1])\n",
+    "n_t, n_rot = t_and_rot_counts_from_bloq(trotter_step)\n",
     "print(f\"N_T = {n_t} vs {(3*length**2 // 2)*8}\")\n",
     "print(f\"N_rot = {n_rot} vs {(3 * length**2 + 2*length**2)}\")"
    ]
@@ -283,8 +268,8 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "import attrs\n",
     "from qualtran.drawing import show_call_graph\n",
+    "from qualtran.resource_counting.generalizers import generalize_rotation_angle \n",
     "# get appropriate epsilon given our input parameters now we know the number of rotations\n",
     "eps_single_rot = get_single_rot_eps(n_rot, delta_ht, timestep)\n",
     "print(f\"Adjusted eps_single_rot: {eps_single_rot}\")\n",
@@ -294,7 +279,7 @@
     "# But let's show the call graph anyway to check the parameters all all what we expect.\n",
     "updated_eps_bloqs = tuple(attrs.evolve(b, eps=eps_single_rot) for b in trotter_step.bloqs)\n",
     "trotter_step = attrs.evolve(trotter_step, bloqs=updated_eps_bloqs)\n",
-    "trotter_step_g, _ = trotter_step.call_graph(generalizer=catch_rotations)\n",
+    "trotter_step_g, _ = trotter_step.call_graph(generalizer=generalize_rotation_angle)\n",
     "show_call_graph(trotter_step_g)"
    ]
   },
@@ -418,7 +403,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "from scipy.optimize import minimize, bisect, newton\n",
+    "from scipy.optimize import minimize\n",
     "def objective(delta_ts, delta_ht, n_rot, n_t, xi_bound, prod_ord):\n",
     "    t_counts = qpe_t_count(epsilon - delta_ts - delta_ht, delta_ts, delta_ht, n_rot, n_t, xi_bound, prod_ord)\n",
     "    return t_counts\n",
@@ -467,7 +452,7 @@
    "source": [
     "from qualtran.bloqs.chemistry.trotter.hubbard.trotter_step import build_plaq_hwp_unitary_second_order_suzuki\n",
     "trotter_step_hwp = build_plaq_hwp_unitary_second_order_suzuki(length, hubb_u, timestep, eps=1e-10)\n",
-    "n_t_hwp, n_rot_hwp = t_and_rot_counts_from_sigma(trotter_step_hwp.call_graph(generalizer=catch_rotations)[1])\n",
+    "n_t_hwp, n_rot_hwp = t_and_rot_counts_from_bloq(trotter_step_hwp)\n",
     "print(f\"N_T(HWP) = {n_t_hwp} vs {(3*length**2 // 2)*8}\")\n",
     "print(f\"N_rot(HWP) = {n_rot_hwp} vs {(3 * length**2 + 2*length**2)}\")\n",
     "delta_ht_opt, delta_ts_opt, delta_pe_opt, t_opt = minimize_linesearch(n_rot_hwp, n_t_hwp, xi_bound, prod_ord)\n",
@@ -490,7 +475,7 @@
    "outputs": [],
    "source": [
     "trotter_step_hwp = build_plaq_hwp_unitary_second_order_suzuki(length, hubb_u, timestep, eps=1e-10, strip_layer=True)\n",
-    "n_t_hwp, n_rot_hwp = t_and_rot_counts_from_sigma(trotter_step_hwp.call_graph(generalizer=catch_rotations)[1])\n",
+    "n_t_hwp, n_rot_hwp = t_and_rot_counts_from_bloq(trotter_step_hwp)\n",
     "print(f\"N_T(HWP) = {n_t_hwp}\")\n",
     "print(f\"N_rot(HWP) = {n_rot_hwp}\")\n",
     "delta_ht_opt, delta_ts_opt, delta_pe_opt, t_opt = minimize_linesearch(n_rot_hwp, n_t_hwp, xi_bound, prod_ord)\n",
@@ -513,14 +498,18 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "s_eps_r, s_length, s_hubb_u, s_timestep, s_tau = sympy.symbols(r'\\epsilon_{R}, L, u, t, \\tau')"
+    "s_eps_r, s_length, s_hubb_u, s_timestep, s_tau = sympy.symbols(r'\\epsilon_{R}, L, u, t, \\tau')\n",
+    "s_delta_ht, s_delta_ts, s_delta_pe, s_p, s_xi = sympy.symbols(\n",
+    "    '\\Delta_{HT}, \\Delta_{TS}, \\Delta_{PE}, p, xi'\n",
+    ")\n",
+    "s_n_rot, s_n_t, s_n_pe = sympy.symbols('N_R, N_T, N_PE')"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "First let's check the Bloq counts look correct for the Trotter step, there are two sources rotations from the interaction and hopping bloq and some direct T gates from the `TwoBitFFFT` gate."
+    "First let's check the Bloq counts look correct for the Trotter step, there are two sources: rotations from the interaction and hopping bloq, and some direct T gates from the `TwoBitFFFT` gate."
    ]
   },
   {
@@ -534,8 +523,8 @@
     "s_trotter_step = build_plaq_unitary_second_order_suzuki(\n",
     "    s_length, s_hubb_u, s_timestep, eps=s_eps_r, hubb_t=s_tau\n",
     ")\n",
-    "t_counts = t_counts_from_sigma(s_trotter_step.call_graph(generalizer=catch_rotations)[1])\n",
-    "t_counts"
+    "t_counts, n_rot = t_and_rot_counts_from_bloq(s_trotter_step)\n",
+    "t_counts + rotation_cost(n_rot, s_delta_ht, s_timestep)"
    ]
   },
   {
@@ -545,7 +534,7 @@
    "outputs": [],
    "source": [
     "# check the symbolic counts match the expected counts\n",
-    "t_counts_orig = t_counts_from_sigma(trotter_step.call_graph(generalizer=catch_rotations)[1])\n",
+    "t_counts_orig, n_rot = t_and_rot_counts_from_bloq(trotter_step)\n",
     "# for some reason substituting both at once leads to a precision error\n",
     "t_counts = t_counts.evalf(subs={s_eps_r: eps_single_rot})\n",
     "t_counts_symb = t_counts.evalf(subs={s_length: length})\n",
@@ -565,10 +554,6 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "s_delta_ht, s_delta_ts, s_delta_pe, s_p, s_xi = sympy.symbols(\n",
-    "    '\\Delta_{HT}, \\Delta_{TS}, \\Delta_{PE}, p, xi'\n",
-    ")\n",
-    "s_n_rot, s_n_t, s_n_pe = sympy.symbols('N_R, N_T, N_PE')\n",
     "s_timestep = (s_delta_ts / s_xi) ** (1 / s_p)\n",
     "s_eps_r = (s_delta_ht * s_timestep) / s_n_rot\n",
     "s_n_pe = 0.76 * sympy.pi / (s_delta_pe * s_timestep)\n",
@@ -577,8 +562,9 @@
     ")\n",
     "# just use this cost in lieu of a QPE bloq\n",
     "# See: https://github.com/quantumlib/Qualtran/issues/932 this should be replaced by a real bloq.\n",
-    "t_counts = s_n_pe * t_counts_from_sigma(s_trotter_step.call_graph(generalizer=catch_rotations)[1])\n",
-    "t_counts"
+    "s_t_counts, s_n_rot =  t_and_rot_counts_from_bloq(s_trotter_step)\n",
+    "qpe_cost_symb = s_n_pe * (s_t_counts + rotation_cost(s_n_rot, s_delta_ht, s_timestep))\n",
+    "qpe_cost_symb"
    ]
   },
   {
@@ -587,7 +573,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "symb_t_count = t_counts.evalf(\n",
+    "symb_t_count = qpe_cost_symb.evalf(\n",
     "    subs={\n",
     "        s_length: length,\n",
     "        s_delta_ht: delta_ht,\n",
@@ -597,7 +583,7 @@
     "        s_n_rot: n_rot,\n",
     "    }\n",
     ")\n",
-    "symb_t_count = symb_t_count.evalf(subs={s_p: prod_ord})\n",
+    "symb_t_count = symb_t_count.evalf(subs={s_p: prod_ord}, maxn=500)\n",
     "tot_t_count = qpe_t_count(delta_pe, delta_ts, delta_ht, n_rot, n_t, xi_bound, prod_ord)\n",
     "assert int(symb_t_count) == int(tot_t_count)"
    ]
@@ -619,7 +605,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.11.7"
+   "version": "3.10.4"
   }
  },
  "nbformat": 4,
diff --git a/qualtran/bloqs/chemistry/trotter/hubbard/trotter_step_test.py b/qualtran/bloqs/chemistry/trotter/hubbard/trotter_step_test.py
index 773235f2b..0bcdb6e32 100644
--- a/qualtran/bloqs/chemistry/trotter/hubbard/trotter_step_test.py
+++ b/qualtran/bloqs/chemistry/trotter/hubbard/trotter_step_test.py
@@ -13,36 +13,23 @@
 #  limitations under the License.
 import pytest
 
-from qualtran import Bloq
-from qualtran.bloqs.basic_gates import Rz
-from qualtran.bloqs.basic_gates.t_gate import TGate
-from qualtran.bloqs.bookkeeping import ArbitraryClifford
 from qualtran.bloqs.chemistry.trotter.hubbard.trotter_step import (
     build_plaq_unitary_second_order_suzuki,
 )
-from qualtran.resource_counting.generalizers import PHI
+from qualtran.resource_counting import get_cost_value, QECGatesCost
 from qualtran.testing import execute_notebook
 
 
-def catch_rotations(bloq) -> Bloq:
-    if isinstance(bloq, Rz):
-        if isinstance(bloq.angle, float) and abs(bloq.angle) < 1e-12:
-            return ArbitraryClifford(1)
-        else:
-            return Rz(angle=PHI)
-    return bloq
-
-
 def test_second_order_suzuki_costs():
     length = 8
     u = 4
-    dt = 0.1
+    dt = 0.1234
     unitary = build_plaq_unitary_second_order_suzuki(length, u, dt)
-    _, sigma = unitary.call_graph(generalizer=catch_rotations)
+    costs = get_cost_value(unitary, QECGatesCost())
     # there are 3 hopping unitaries contributing 8 Ts from from the F gate
-    assert sigma[TGate()] == (3 * length**2 // 2) * 8
+    assert costs.total_t_count(ts_per_rotation=0) == (3 * length**2 // 2) * 8
     # 3 hopping unitaries and 2 interaction unitaries
-    assert sigma[Rz(PHI)] == (3 * length**2 + 2 * length**2)
+    assert costs.rotation == (3 * length**2 + 2 * length**2)
 
 
 @pytest.mark.notebook
diff --git a/qualtran/bloqs/chemistry/writing_algorithms.ipynb b/qualtran/bloqs/chemistry/writing_algorithms.ipynb
index f020eef15..262f4e1c4 100644
--- a/qualtran/bloqs/chemistry/writing_algorithms.ipynb
+++ b/qualtran/bloqs/chemistry/writing_algorithms.ipynb
@@ -180,16 +180,26 @@
    "outputs": [],
    "source": [
     "import numpy as np\n",
-    "from qualtran.bloqs.basic_gates import TGate\n",
-    "from qualtran.bloqs.chemistry.chem_tutorials import plot_linear_log_log\n",
     "import matplotlib.pyplot as plt\n",
     "\n",
+    "from qualtran import Bloq\n",
+    "from qualtran.bloqs.chemistry.chem_tutorials import plot_linear_log_log\n",
+    "from qualtran.resource_counting import get_cost_value, QECGatesCost\n",
+    "from qualtran.resource_counting.generalizers import generalize_cswap_approx\n",
+    "\n",
+    "def toffoli_count(bloq: Bloq) -> int:\n",
+    "    # TODO: The rotations here should be in terms of Toffoli via phase gradient rotation.\n",
+    "    cost = get_cost_value(bloq, QECGatesCost(), generalizer=generalize_cswap_approx).total_t_and_ccz_count(ts_per_rotation=0)\n",
+    "    n_t = cost['n_t']\n",
+    "    assert n_t == 0, \"Found raw T gates, should be only Toffolis\"\n",
+    "    return cost['n_ccz']\n",
+    "\n",
     "fig, ax = plt.subplots()\n",
     "basis_vals = np.linspace(10, 100, 10, dtype=int)\n",
-    "tcounts = [PrepareSecondQuantization(int(n), qroam_block_size=1).call_graph()[1][TGate()] for n in basis_vals]\n",
-    "plot_linear_log_log(ax, basis_vals, np.array(tcounts))\n",
+    "toff_counts = [toffoli_count(PrepareSecondQuantization(int(n), qroam_block_size=1)) for n in basis_vals]\n",
+    "plot_linear_log_log(ax, basis_vals, np.array(toff_counts))\n",
     "ax.set_xlabel(\"$N$\")\n",
-    "ax.set_ylabel(\"$T$ count\")"
+    "ax.set_ylabel(\"Toffoli count\")"
    ]
   },
   {
@@ -208,12 +218,12 @@
     "fig, ax = plt.subplots()\n",
     "basis_vals = np.linspace(50, 200, 10, dtype=int)\n",
     "for ib, block_size in enumerate([1, 2, 8, 32, None]):\n",
-    "    tcounts = [PrepareSecondQuantization(int(n), qroam_block_size=block_size).call_graph()[1][TGate()] for n in basis_vals]\n",
+    "    toff_counts = [toffoli_count(PrepareSecondQuantization(int(n), qroam_block_size=block_size)) for n in basis_vals]\n",
     "    if block_size is None:\n",
     "        block_size = 'opt'\n",
-    "    plot_linear_log_log(ax, basis_vals, np.array(tcounts), label=f'block size = {block_size}: ', color=f'C{ib}')\n",
+    "    plot_linear_log_log(ax, basis_vals, np.array(toff_counts), label=f'block size = {block_size}: ', color=f'C{ib}')\n",
     "ax.set_xlabel(\"$N$\")\n",
-    "ax.set_ylabel(\"$T$ count\")"
+    "ax.set_ylabel(\"Tofflis count\")"
    ]
   },
   {
@@ -302,9 +312,8 @@
     "from typing import Dict\n",
     "from qualtran import SoquetT, BloqBuilder, Register\n",
     "\n",
-    "from qualtran.bloqs.state_preparation import PrepareUniformSuperposition\n",
     "from qualtran.bloqs.data_loading.select_swap_qrom import SelectSwapQROM, find_optimal_log_block_size\n",
-    "from qualtran.bloqs.basic_gates import CSwap, Hadamard, OnEach\n",
+    "from qualtran.bloqs.basic_gates import Hadamard, OnEach\n",
     "\n",
     "\n",
     "@frozen\n",
@@ -519,8 +528,8 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "prep_basic = PrepareSecondQuantization(num_spin_orb).call_graph()[1].get(TGate())\n",
-    "prep_decom = prep.decompose_bloq().call_graph()[1].get(TGate())\n",
+    "prep_basic = toffoli_count(PrepareSecondQuantization(num_spin_orb))\n",
+    "prep_decom = toffoli_count(prep.decompose_bloq())\n",
     "print(f\"call graph = {prep_basic}, decomposition = {prep_decom}\")"
    ]
   },
@@ -567,7 +576,7 @@
     "alt_pqrs, keep = build_alt_keep_vals(tpq, eris, num_spin_orb, num_bits_state_prep)\n",
     "prep = PrepareSecondQuantizationDetailed(num_spin_orb, tuple(alt_pqrs), tuple(keep), num_bits_state_prep=num_bits_state_prep)\n",
     "prep_sparse = PrepareSparse.from_hamiltonian_coeffs(num_spin_orb, tpq, eris, num_bits_state_prep).decompose_bloq()\n",
-    "print(f\"naive = {prep.call_graph()[1].get(TGate())}, sparse = {prep_sparse.call_graph()[1].get(TGate())}\")"
+    "print(f\"naive = {toffoli_count(prep)}, sparse = {toffoli_count(prep_sparse)}\")"
    ]
   }
  ],
@@ -587,7 +596,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.11.6"
+   "version": "3.11.9"
   }
  },
  "nbformat": 4,