diff --git a/docs/blog/posts/2025/a-new-journey.md b/docs/blog/posts/2025/a-new-journey.md
index aba9b9c4d..dda795b1a 100644
--- a/docs/blog/posts/2025/a-new-journey.md
+++ b/docs/blog/posts/2025/a-new-journey.md
@@ -30,7 +30,7 @@ At its core, Bloqade strives to be the neutral atom SDK for getting the most out
Many quantum algorithms are hybrid, requiring both classical and quantum resources to work together in tandem. This could be anything from syndrome extraction and measurement-based computing to variational parameter updates in VQE methods and orbital fragmentation methods in molecular simulation. Through the use of the Kirin compiler infrastructure, Bloqade embraces this philosophy of heterogeneous compute. Kirin programs are written as (compositions of) [kernels](https://en.wikipedia.org/wiki/Compute_kernel) -- subroutines that are intended to run on particular hardware (such as QPUs), or orchestrated to run on heterogeneous compute (such as a real-time classical runtime plus a QPU). These subroutines -- plus the built-in hybrid representations-- enable many key primitives, such as error correction.
-Additionally, the ability to compose functions together and to use typical classical programming structures like `if` and recursions enables many simplifications in writing complex circuits. In fact, recursions and the ability to dynamically allocate new memory (which is not known until runtime) enables many powerful subroutines and is natively enabled with Bloqade's kernel-based representation; for example, see [this implementation](../../../digital/examples/qasm2/repeat_until_success.py) of a repeat-until-success program.
+Additionally, the ability to compose functions together and to use typical classical programming structures like `if` and recursions enables many simplifications in writing complex circuits. In fact, recursions and the ability to dynamically allocate new memory (which is not known until runtime) enables many powerful subroutines and is natively enabled with Bloqade's kernel-based representation.
## Analog, digital, logical: towards real quantum utility
diff --git a/docs/digital/examples/index.md b/docs/digital/examples/index.md
index fffb67214..85a45ac9d 100644
--- a/docs/digital/examples/index.md
+++ b/docs/digital/examples/index.md
@@ -59,45 +59,3 @@ While bloqade-circuit provides a number of different dialects (eDSLs), it may al
Learn how to apply our heuristic noise models built to work with the cirq SDK.
-
-
-## QASM2
-
-One of the most central languages used to define quantum programs is QASM2.
-You can also write your quantum programs using the QASM2 dialect directly in bloqade-circuit.
-
-!!! warning
-
- Some of the examples below use the `qasm2.extended` dialect, which adds more advanced language features, such as control flow.
- However, this dialect is deprecated and we recommend using `squin` instead.
-
-
-
-
-
-- [Quantum Fourier Transform](../examples/qasm2/qft/)
-
- ---
-
- An example showing how to implement the well-known Quantum Fourier Transform (QFT).
-
-- [GHZ Preparation and Parallelism](../examples/qasm2/ghz/)
-
- ---
-
- Learn how to use parallelism to reduce the circuit (execution) depth.
-
-- [Pauli Exponentiation for Quantum Simulation](../examples/qasm2/pauli_exponentiation/)
-
- ---
-
- Simulating Hamiltonian dynamics by exponentiating Pauli operators.
-
-
-- [Repeat until success with STAR gadget](../examples/qasm2/repeat_until_success/)
-
- ---
-
- Here's how to implement a Z phase gate with the repeat-until-success protocol.
-
-
diff --git a/docs/digital/examples/qasm2/ghz.py b/docs/digital/examples/qasm2/ghz.py
deleted file mode 100644
index de71bf049..000000000
--- a/docs/digital/examples/qasm2/ghz.py
+++ /dev/null
@@ -1,165 +0,0 @@
-# %% [markdown]
-# # GHZ State Preparation with Parallelism
-# In this example, we will implement a *Greenberger-Horne-Zeilinger* (GHZ) state preparation circuit with $N = 2^n$ qubits.
-#
-# First, we will present the standard linear-depth construction in Bloqade but later we will present a log-depth
-# construction that achieves the same result. We then take this one step further and use the fact that Bloqade
-# (and QuEra's neutral atom hardware!) support *parallel* gates, allowing for the application of the same gate
-# across multiple qubits simultaneously. Combined with the fact that atom *shuttling* allows for arbitrary
-# connectivity, we can also decrease the circuit *execution* depth from $N$ to just $n$.
-
-# %%
-import math
-
-from kirin.dialects import ilist
-
-from bloqade import qasm2
-
-# %% [markdown]
-# ## Simple Linear Depth Implementation of a GHZ State Preparation Circuit
-#
-# A simple GHZ state preparation circuit can be built with $N - 1$ CX gates and $1$ H gate.
-# This gives the circuit an execution depth of $N$.
-
-#
-#
-#
-#
-#
-
-
-# %%
-def ghz_linear(n: int):
- n_qubits = int(2**n)
-
- @qasm2.extended
- def ghz_linear_program():
-
- qreg = qasm2.qreg(n_qubits)
- # Apply a Hadamard on the first qubit
- qasm2.h(qreg[0])
- # Create a cascading sequence of CX gates
- # necessary for quantum computers that
- # only have nearest-neighbor connectivity between qubits
- for i in range(1, n_qubits):
- qasm2.cx(qreg[i - 1], qreg[i])
-
- return ghz_linear_program
-
-
-# %% [markdown]
-# ## Log-depth Implementation of a GHZ State Preparation Circuit
-#
-# Let's take a look how we can rewrite the circuit to take advantage of QuEra's hardware capabilities.
-# We can achieve log(N) circuit depth by [rearranging the CX gates (see *Mooney, White, Hill, Hollenberg* - 2021)](https://arxiv.org/abs/2101.08946).
-#
-
-# %% [markdown]
-#
-#
Circuit vs. Execution Depth
-#
-# Before going any further, it's worth distinguishing between the concept of circuit depth and circuit execution depth.
-# For example, in the following implementation, each CX gate instruction inside the for-loop is executed in sequence.
-# So even though the circuit depth is $log(N) = n$, the circuit execution depth is still $N$.
-#
-#
-
-#
-#
-#
-#
-#
-
-
-# %%
-def ghz_log_depth(n: int):
- n_qubits = int(2**n)
-
- @qasm2.extended
- def layer_of_cx(i_layer: int, qreg: qasm2.QReg):
- step = n_qubits // (2**i_layer)
- for j in range(0, n_qubits, step):
- qasm2.cx(ctrl=qreg[j], qarg=qreg[j + step // 2])
-
- @qasm2.extended
- def ghz_log_depth_program():
-
- qreg = qasm2.qreg(n_qubits)
-
- qasm2.h(qreg[0])
- for i in range(n):
- layer_of_cx(i_layer=i, qreg=qreg)
-
- return ghz_log_depth_program
-
-
-# %% [markdown]
-# ## Our Native Gate Set and Parallelism
-# By nature, our digital quantum computer can execute native gates in parallel in an single instruction/ execution cycle.
-# The concept is very similar to the SIMD (Single Instruction, Multiple Data) in classical computing.
-#
-# On our hardware, there are two important factors to be considered:
-# 1. the native gate set allows for arbitrary (parallel) rotations and (parallel) CZ gates.
-# 2. Our atom shuttling architecture allows arbitrary qubit connectivity. This means that our parallel instruction is not limited to fixed connectivity (for example nearest neighbor connectivity).
-#
-# With this in mind, we can rewrite the `layer` subroutine to now use the `qasm2.parallel` dialect in Bloqade.
-# We know that the CX gate can be decomposed into a CZ gate with two single-qubit gates $R_y(-\pi/2)$ and $R_y(\pi/2)$ acting on the target qubits.
-# With this decomposition in mind, we can now using our parallel gate instructions `parallel.u` and `parallel.cz`.
-# With the following modification, we can further reduce the circuit execution depth to just $n$ (log of the total number of qubits $N$)
-
-
-# %%
-def ghz_log_simd(n: int):
- n_qubits = int(2**n)
-
- @qasm2.extended
- def layer(i_layer: int, qreg: qasm2.QReg):
- step = n_qubits // (2**i_layer)
-
- def get_qubit(x: int):
- return qreg[x]
-
- ctrl_qubits = ilist.Map(fn=get_qubit, collection=range(0, n_qubits, step))
- targ_qubits = ilist.Map(
- fn=get_qubit, collection=range(step // 2, n_qubits, step)
- )
-
- # Ry(-pi/2)
- qasm2.parallel.u(qargs=targ_qubits, theta=-math.pi / 2, phi=0.0, lam=0.0)
-
- # CZ gates
- qasm2.parallel.cz(ctrls=ctrl_qubits, qargs=targ_qubits)
-
- # Ry(pi/2)
- qasm2.parallel.u(qargs=targ_qubits, theta=math.pi / 2, phi=0.0, lam=0.0)
-
- @qasm2.extended
- def ghz_log_depth_program():
-
- qreg = qasm2.qreg(n_qubits)
-
- qasm2.h(qreg[0])
- for i in range(n):
- layer(i_layer=i, qreg=qreg)
-
- return ghz_log_depth_program
-
-
-# %% [markdown]
-#
-#
Using Closures to Capture Global Variables
-#
-# While bloqade.qasm2 permits a main program with arguments, standard QASM2 does not.
-# To get around this, we need to put the program in a closure.
-# Our Kirin compiler toolchain can capture the global variable inside the closure.
-# In this case, the n_qubits will be captured upon calling the ghz_log_simd(n_qubits) python function.
-# As a result, the returned QASM2 program will not have any arguments.
-#
-#
-
-# %%
-target = qasm2.emit.QASM2(
- allow_parallel=True,
-)
-ast = target.emit(ghz_log_simd(4))
-qasm2.parse.pprint(ast)
diff --git a/docs/digital/examples/qasm2/pauli_exponentiation.py b/docs/digital/examples/qasm2/pauli_exponentiation.py
deleted file mode 100644
index 823b75b85..000000000
--- a/docs/digital/examples/qasm2/pauli_exponentiation.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# %% [markdown]
-# # Pauli Exponentiation for Quantum Simulation
-# In this example, we will consider a simple Pauli Exponentiation circuit.
-
-# %%
-import math
-
-from bloqade import qasm2
-
-# %% [markdown]
-# First, we define the `zzzz_gadget` function which is a simple implementation of Pauli Z exponentiation
-# with a parameterized angle `gamma`.
-
-
-# %%
-@qasm2.extended
-def zzzz_gadget(targets: tuple[qasm2.Qubit, ...], gamma: float):
- for i in range(len(targets) - 1):
- qasm2.cx(targets[i], targets[i + 1])
-
- qasm2.rz(targets[-1], gamma)
-
- for j in range(len(targets) - 1):
- qasm2.cx(targets[-j - 1], targets[-j - 2])
-
-
-# %% [markdown]
-# Next, we define the `pauli_basis_change` function which is a simple implementation of Pauli basis change
-# with a parameterized start and end Pauli basis.
-
-
-# %%
-@qasm2.extended
-def pauli_basis_change(targets: tuple[qasm2.Qubit, ...], start: str, end: str):
- # assert len(targets) == len(start)
- # assert len(targets) == len(end)
-
- # for qubit, start_pauli, end_pauli in zip(targets, start, end):
- for i in range(len(targets)):
- qubit = targets[i]
- start_pauli = start[i]
- end_pauli = end[i]
-
- target = start_pauli + end_pauli
- if target == "ZX":
- qasm2.ry(qubit, math.pi / 2)
- elif target == "ZY":
- qasm2.rx(qubit, -math.pi / 2)
- # elif target == "ZZ":
- # pass
- # elif target == "XX":
- # pass
- elif target == "XY":
- qasm2.rz(qubit, math.pi / 2)
- elif target == "XZ":
- qasm2.ry(qubit, -math.pi / 2)
- elif target == "YX":
- qasm2.rz(qubit, -math.pi / 2)
- # elif target == "YY":
- # pass
- elif target == "YZ":
- qasm2.rx(qubit, math.pi / 2)
-
-
-# %% [markdown]
-# Putting it all together, we define the `pauli_exponential` function which is a simple implementation of Pauli Exponentiation
-# with a parameterized Pauli basis and angle `gamma`.
-# %%
-@qasm2.extended
-def pauli_exponential(targets: tuple[qasm2.Qubit, ...], pauli: str, gamma: float):
- # assert len(targets) == len(pauli)
-
- pauli_basis_change(targets=targets, start="Z" * len(targets), end=pauli)
- zzzz_gadget(targets=targets, gamma=gamma)
- pauli_basis_change(targets=targets, start=pauli, end="Z" * len(targets))
-
-
-# %% [markdown]
-# Finally, we define the `main` function as the entry point of the program.
-
-#
-#
-#
-#
-#
-
-
-# %%
-@qasm2.extended
-def main():
- register = qasm2.qreg(4)
- pauli_exponential((register[0], register[1], register[2]), "ZXY", 0.5)
-
-
-# %% [markdown]
-# we can now ask the compiler to emit the QASM2 code for the `main` function.
-# %%
-target = qasm2.emit.QASM2()
-ast = target.emit(main)
-qasm2.parse.pprint(ast)
diff --git a/docs/digital/examples/qasm2/pauli_exponentiation.svg b/docs/digital/examples/qasm2/pauli_exponentiation.svg
deleted file mode 100644
index 833d71ba9..000000000
--- a/docs/digital/examples/qasm2/pauli_exponentiation.svg
+++ /dev/null
@@ -1,147 +0,0 @@
-
diff --git a/docs/digital/examples/qasm2/qaoa.py b/docs/digital/examples/qasm2/qaoa.py
deleted file mode 100644
index 1b918c47b..000000000
--- a/docs/digital/examples/qasm2/qaoa.py
+++ /dev/null
@@ -1,189 +0,0 @@
-# %% [markdown]
-# Lets do a simple example of a prototype circuit that benefits from parallelism: QAOA
-# solving the MaxCut problem. For more details, see [arXiv:1411.4028](https://arxiv.org/abs/1411.4028)
-# and the considerable literature that has developed around this algorithm.
-
-# %%
-import math
-from typing import Any
-
-import kirin
-import networkx as nx
-from kirin.dialects import ilist
-
-from bloqade import qasm2
-
-pi = math.pi
-
-# %% [markdown]
-# MaxCut is a combinatorial graph problem that seeks to bi-partition the nodes of some
-# graph G such that the number of edges between the two partitions is maximized.
-# Here, we choose a random 3 regular graph with 32 nodes [ref](https://journals.aps.org/pra/abstract/10.1103/PhysRevA.103.042612)
-
-# %%
-N = 32
-G = nx.random_regular_graph(3, N, seed=42)
-
-
-# %% [markdown]
-# To build the quantum program, we use a builder function and use closure to pass variables
-# inside of the kernel function (kirin methods).
-# In this case, the two variables that are passed inside are the edges and nodes of the graph.
-#
-# The QAOA first prepares the |+> state as a superposition of all possible bitstrings,
-# then repeats between the (diagonal) cost function and the mixer X with angles gamma and beta.
-# It is parameterized by gamma and betas, which are each the p length lists of angles.
-#
-# Lets first implement the sequential version of the QAOA algorithm, which
-# does not inform any parallelism to the compiler.
-
-
-# %%
-def qaoa_sequential(G: nx.Graph) -> kirin.ir.Method:
-
- edges = list(G.edges)
- nodes = list(G.nodes)
- N = len(nodes)
-
- @qasm2.extended
- def kernel(gamma: ilist.IList[float, Any], beta: ilist.IList[float, Any]):
- # Initialize the register in the |+> state
- q = qasm2.qreg(N)
- for i in range(N): # structural control flow is native to the Kirin compiler
- qasm2.h(q[i])
-
- # Repeat the cost and mixer layers
- for i in range(len(gamma)):
- # The cost layer, which corresponds to a ZZ(phase) gate applied
- # to each edge of the graph
- for j in range(len(edges)):
- edge = edges[j]
- qasm2.cx(q[edge[0]], q[edge[1]])
- qasm2.rz(q[edge[1]], gamma[i])
- qasm2.cx(q[edge[0]], q[edge[1]])
- # The mixer layer, which corresponds to a X(phase) gate applied
- # to each node of the graph
- for j in range(N):
- qasm2.rx(q[j], beta[i])
-
- return q
-
- return kernel
-
-
-# %% [markdown]
-# Next, lets implement a SIMD (Single Instruction, Multiple Data) version of the QAOA algorithm,
-# which effectively represents the parallelism in the QAOA algorithm.
-# This can be done by coloring the (commuting) ZZ(phase) gates into groups with non-overlapping
-# sets of qubits, and then applying each of those groups in parallel.
-# By [Vizing's theorem](https://en.wikipedia.org/wiki/Vizing%27s_theorem) the edges of a graph
-# can efficiently be colored into $\Delta+1$ colors, where $\Delta$ is the maximum degree of the graph.
-# Unfortunately, networkx does not have a native implementation of the algorithm so instead we use
-# the lesser [Brooks' theorem]https://en.wikipedia.org/wiki/Brooks%27_theorem) to color the edges
-# using an equitable coloring of the line graph.
-
-
-# %%
-def qaoa_simd(G: nx.Graph) -> kirin.ir.Method:
-
- nodes = list(G.nodes)
-
- # Note that graph computation is happening /outside/ the kernel function:
- # this is a computation that occurs on your laptop in Python when you generate
- # a program, as opposed to on a piece of quantum hardware, which is what
- # occurs inside of the kernel.
- Gline = nx.line_graph(G)
- colors = nx.algorithms.coloring.equitable_color(Gline, num_colors=5)
- left_ids = ilist.IList(
- [
- ilist.IList([edge[0] for edge in G.edges if colors[edge] == i])
- for i in range(5)
- ]
- )
- right_ids = ilist.IList(
- [
- ilist.IList([edge[1] for edge in G.edges if colors[edge] == i])
- for i in range(5)
- ]
- )
- # We can use composition of kernel functions to simplify repeated code.
- # Small snippets (say, the CX gate) can be written once and then called
- # many times.
-
- @qasm2.extended
- def parallel_h(qargs: ilist.IList[qasm2.Qubit, Any]):
- qasm2.parallel.u(qargs=qargs, theta=pi / 2, phi=0.0, lam=pi)
-
- # A parallel CX gate is equivalently a parallel H gate, followed by a parallel CZ gate,
- # followed by another parallel H. the CZ can be done in any order as they permute.
- @qasm2.extended
- def parallel_cx(
- ctrls: ilist.IList[qasm2.Qubit, Any], qargs: ilist.IList[qasm2.Qubit, Any]
- ):
- parallel_h(qargs)
- qasm2.parallel.cz(ctrls, qargs)
- parallel_h(qargs)
-
- @qasm2.extended
- def parallel_cz_phase(
- ctrls: ilist.IList[qasm2.Qubit, Any],
- qargs: ilist.IList[qasm2.Qubit, Any],
- gamma: float,
- ):
- parallel_cx(ctrls, qargs)
- qasm2.parallel.rz(qargs, gamma)
- parallel_cx(ctrls, qargs)
-
- @qasm2.extended
- def kernel(gamma: ilist.IList[float, Any], beta: ilist.IList[float, Any]):
- # Declare the register and set it to the |+> state
- q = qasm2.qreg(len(nodes))
- # qasm2.glob.u(theta=pi / 2, phi=0.0, lam=pi,registers=[q])
-
- def get_qubit(x: int):
- return q[x]
-
- all_qubits = ilist.map(fn=get_qubit, collection=range(N))
-
- parallel_h(all_qubits)
-
- for i in range(len(gamma)): # For each QAOA layer...
- # Do the ZZ phase gates...
- for cind in range(
- 5
- ): # by applying a parallel CZ phase gate in parallel for each color,
- ctrls = ilist.map(fn=get_qubit, collection=left_ids[cind])
- qargs = ilist.map(fn=get_qubit, collection=right_ids[cind])
- parallel_cz_phase(ctrls, qargs, gamma[i])
- # ...then, do an X phase gate. Observe that because this happens on every
- # qubit, we can do a global rotation, which is higher fidelity than
- # parallel local rotations.
- # qasm2.glob.u(theta=beta[i],phi=0.0,lam=0.0,registers=[q])
- qasm2.parallel.u(qargs=all_qubits, theta=beta[i], phi=0.0, lam=0.0)
-
- return q
-
- return kernel
-
-
-# %%
-print("--- Sequential ---")
-qaoa_sequential(G).code.print()
-
-# %%
-kernel = qaoa_simd(G)
-
-print("\n\n--- Simd ---")
-kernel.print()
-
-
-# %%
-@qasm2.extended
-def main():
- kernel([0.1, 0.2], [0.3, 0.4])
-
-
-# %%
-target = qasm2.emit.QASM2()
-ast = target.emit(main)
-qasm2.parse.pprint(ast)
diff --git a/docs/digital/examples/qasm2/qft.py b/docs/digital/examples/qasm2/qft.py
deleted file mode 100644
index 700b79de3..000000000
--- a/docs/digital/examples/qasm2/qft.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# %% [markdown]
-# # Quantum Fourier Transform
-# In this example, we will explore the Quantum Fourier Transform (QFT) circuit using
-# recursion and iteration -- a convenient way to implement the QFT circuit using
-# our high-level programming features.
-#
-# To begin, we will import the `qasm2` module from the `bloqade` package and the `PyQrack`
-# backend from the `bloqade.pyqrack` module.
-# %%
-import math
-
-from bloqade.pyqrack import StackMemorySimulator
-
-from bloqade import qasm2
-
-# %% [markdown]
-# In the following, we will define the Quantum Fourier Transform (QFT) circuit using recursion
-# inside a kernel function `qft`. The `qft` function takes two arguments: a quantum register `qreg`
-# and an integer `n` representing the number of qubits we want to apply the QFT circuit to.
-# %%
-pi = math.pi
-
-
-@qasm2.extended
-def qft(qreg: qasm2.QReg, n: int, k: int):
- if k != n:
- qasm2.h(qreg[k])
- for i in range(k + 1, n):
- qasm2.cu1(qreg[i], qreg[k], 2 * math.pi / 2**i)
- qft(qreg, n, k + 1) # recursion
- return qreg
-
-
-# %% [markdown]
-# Next, we will call this kernel function `qft` inside a `main` function to check if
-# the QFT circuit is correctly implemented. We will use a quantum register of size 3.
-
-
-# %%
-@qasm2.extended
-def main():
- return qft(qasm2.qreg(3), 3, 0)
-
-
-# %% [markdown]
-# Finally, we will run the `main` function on the `PyQrack` backend and print the quantum register
-# to see the final state of the qubits after applying the QFT circuit.
-#
-#
-#
-#
-#
-
-
-# %%
-device = StackMemorySimulator(min_qubits=3)
-qreg = device.run(main)
-print(qreg)
-
-# %% [markdown]
-# we can also emit the QASM2 code for the `main` function and print it to see the QASM2 code
-# that corresponds to the QFT circuit.
-
-# %%
-from bloqade.qasm2.emit import QASM2 # noqa: E402
-from bloqade.qasm2.parse import pprint # noqa: E402
-
-target = QASM2()
-ast = target.emit(main)
-pprint(ast)
diff --git a/docs/digital/examples/qasm2/repeat_until_success.py b/docs/digital/examples/qasm2/repeat_until_success.py
deleted file mode 100644
index 5519cbfad..000000000
--- a/docs/digital/examples/qasm2/repeat_until_success.py
+++ /dev/null
@@ -1,154 +0,0 @@
-# %% [markdown]
-# # Repeat Until Success with STAR Gadget
-# In this example, we will demonstrate a near-term fault tolerant gadget
-# which is a repeat-until-success protocol to implement a Z phase gate
-# using a resource state (similar to a T state), Pauli gates, and feed-forward measurement.
-#
-# For more information, see https://journals.aps.org/prxquantum/pdf/10.1103/PRXQuantum.5.010337,
-# especially Fig. 7.
-
-# %%
-from bloqade import qasm2
-
-# %% [markdown]
-# This example highlights a few interesting capabilities of having a full kernel structure with
-# runtime control flow. One example is the ability to dynamically allocate qubits, possibly
-# based on previous run-time measurement outcomes.
-#
-# In this case, we prepare a resource state, which is a generalization of the T state with
-# an arbitrary Z rotation $|0\rangle + e^{i\theta}|1\rangle$.
-
-
-# %%
-@qasm2.extended
-def prep_resource_state(theta: float):
- qreg = qasm2.qreg(1)
- qubit = qreg[0]
- qasm2.h(qubit)
- qasm2.rz(qubit, theta)
- return qubit
-
-
-# %% [markdown]
-# Using this resource state, we can teleport the Z phase gate to a target qubit using
-# only Clifford gates, which are much easier to implement fault-tolerantly.
-# This is implemented by first applying a CNOT gate controlled by the resource state
-# on the target qubit, then measuring the target qubit in the computational basis.
-# If the measurement outcome is 1 (which occurs with 50% probability), the gadget
-# executed a Z(theta) gate on the target qubit and teleported it
-# to the new resource state.
-#
-# However, if the measurement outcome is 0 (which occurs with 50% probability),
-# we apply an X gate, and the gadget executed a Z(-theta) gate on the target qubit.
-# In order to correct this gate, we must apply a Z(+2*theta) gate on the new target state.
-# Of course, we can apply this Z(+2*theta) gate by applying the same gadget with twice
-# the angle, and repeat until we get the correct outcome.
-
-# %% [markdown]
-# The simplest way to implement the gadget is to simply post-select the correct measurement outcome
-# using an assert statement. This is straightforward, but comes with an exponential overhead in the
-# number of resource states: there is a 50% chance of success at each step, so there is only a
-# $2^{-n}$ chance of success after $n$ Z phase gates.
-
-
-# %%
-@qasm2.extended
-def z_phase_gate_postselect(target: qasm2.Qubit, theta: float) -> qasm2.Qubit:
- ancilla = prep_resource_state(theta)
- qasm2.cx(ancilla, target)
- creg = qasm2.creg(1)
- qasm2.measure(target, creg[0])
- if creg[0]:
- qasm2.x(ancilla)
- return ancilla
-
-
-# %% [markdown]
-# To (deterministically) implement the gate, we can recursively apply the gadget by correcting
-# the angle of the Z gate by applying Z(+2*theta).
-# Observe that, while it is efficient to represent this as a composition of kernels,
-# there is no equivalent representation as a circuit, as the number of resource qubits and
-# total number of gates is not known until runtime.
-
-
-# %%
-@qasm2.extended
-def z_phase_gate_recursive(target: qasm2.Qubit, theta: float) -> qasm2.Qubit:
- """
- https://journals.aps.org/prxquantum/pdf/10.1103/PRXQuantum.5.010337 Fig. 7
- """
- ancilla = prep_resource_state(theta)
- qasm2.cx(ancilla, target)
- creg = qasm2.creg(1)
- qasm2.measure(target, creg[0])
- if creg[0]:
- qasm2.x(ancilla)
- else:
- ancilla = z_phase_gate_recursive(ancilla, 2 * theta)
- return ancilla
-
-
-# %% [markdown]
-# An alternative representation uses control flow to
-# implement the same gate. If the number of repeats is fixed, this can be represented
-# as a static circuit, though it would require a large number of resource qubits and
-# may still fail with a small probability $2^{-attempts}$.
-
-
-# %%
-@qasm2.extended
-def z_phase_gate_loop(target: qasm2.Qubit, theta: float, attempts: int):
- """
- https://journals.aps.org/prxquantum/pdf/10.1103/PRXQuantum.5.010337 Fig. 7
- """
- creg = qasm2.creg(1) # Implicitly initialized to 0, thanks qasm...
- for ctr in range(attempts):
- ancilla = prep_resource_state(theta * (2**ctr))
- if not creg[0]:
- qasm2.cx(ancilla, target)
- qasm2.measure(target, creg[0])
- target = ancilla
- qasm2.x(target)
-
-
-# %% [markdown]
-# Before we analyze these circuits, we must declare a main function
-# which takes no inputs, as qasm2 does not support parameterized circuits or
-# subcircuits.
-
-# %%
-theta = 0.1 # Specify some Z rotation angle. Note that this is being defined
-
-# %% [markdown]
-# outside the main function and being used inside the function via closure.
-
-
-# %%
-@qasm2.extended
-def postselect_main():
- target = qasm2.qreg(1)
- z_phase_gate_postselect(target[0], theta)
-
-
-@qasm2.extended
-def recursion_main():
- target = qasm2.qreg(1)
- z_phase_gate_recursive(target[0], theta)
-
-
-@qasm2.extended
-def loop_main():
- target = qasm2.qreg(1)
- z_phase_gate_loop(target[0], theta, 5)
-
-
-# %% [markdown]
-# Now lets explore running some interpreters on these circuits.
-# We support the quantum emulation backend PyQrack, which simulates quantum
-# circuits using state vectors.
-
-# %%
-from bloqade.pyqrack import PyQrack # noqa: E402
-
-device = PyQrack()
-device.run(postselect_main)
diff --git a/docs/manifesto.md b/docs/manifesto.md
index 197005745..aee595b40 100644
--- a/docs/manifesto.md
+++ b/docs/manifesto.md
@@ -19,7 +19,7 @@ At its core, Bloqade strives to be the neutral atom SDK for getting the most out
## Hybrid computing beyond circuits
Many quantum algorithms are hybrid, requiring both classical and quantum resources to work together in a hybrid computation architecture. This could be anything from syndrome extraction and measurement-based computing to variational parameter updates in VQE methods and orbital fragmentation methods in molecular simulation. Through the use of the Kirin compiler infrastructure, Bloqade embraces this philosophy of heterogeneous compute. Kirin programs are written as (compositions of) [kernels](https://en.wikipedia.org/wiki/Compute_kernel)-- subroutines that are intended to run on particular hardware (such as QPUs), or orchestrated to run on heterogeneous compute (such as a real-time classical runtime plus a QPU). These subroutines-- plus the built-in hybrid representations-- enable many key primitives, such as error correction.
-Additionally, the ability to compose functions together and to use typical classical programming structures like `if` and recursion enables many simplifications in writing raw circuit executions. In fact, recursion and the ability to dynamically allocate new memory (which is not known until runtime) enables many powerful subroutines and is natively enabled with Bloqade's kernel-based representation; for example, see [this implementation](digital/examples/qasm2/repeat_until_success.py) of a repeat-until-success program.
+Additionally, the ability to compose functions together and to use typical classical programming structures like `if` and recursion enables many simplifications in writing raw circuit executions. In fact, recursion and the ability to dynamically allocate new memory (which is not known until runtime) enables many powerful subroutines and is natively enabled with Bloqade's kernel-based representation.
## Analog, digital, logical: towards real quantum utility
diff --git a/mkdocs.yml b/mkdocs.yml
index 78c31f91e..556ec2df8 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -30,12 +30,6 @@ nav:
- Tutorials:
- Circuits with Bloqade: "digital/tutorials/circuits_with_bloqade.py"
- Automatic parallelism: "digital/tutorials/auto_parallelism.py"
- - QASM2 examples:
- - Quantum Fourier Transform: "digital/examples/qasm2/qft.py"
- - GHZ state preparation: "digital/examples/qasm2/ghz.py"
- - Pauli Exponential: "digital/examples/qasm2/pauli_exponentiation.py"
- - Repeat Until Success: "digital/examples/qasm2/repeat_until_success.py"
- - QAOA: "digital/examples/qasm2/qaoa.py"
- Squin dialect examples:
- Deutsch-Jozsa Algorithm: digital/examples/squin/deutsch_squin.py
- GHZ state preparation with noise: digital/examples/squin/ghz.py