python v1.x

Cirq

Google's Python framework for quantum circuits and algorithms

Quick install

pip install cirq

Background and History

Cirq was developed by Google Quantum AI and first released as an open-source Python framework in July 2018. The project was led by Dave Bacon, Craig Gidney, and other members of Google’s quantum computing team, with the goal of providing a circuit-level programming tool that could precisely target the constraints of near-term quantum hardware. Unlike higher-level frameworks that abstract away device topology, Cirq was designed from the start to give users fine-grained control over qubit placement, gate scheduling, and moment structure, which reflects Google’s emphasis on hardware-aware algorithm design.

Cirq’s development was closely tied to Google’s superconducting qubit processors. When Google announced its quantum supremacy experiment on the 53-qubit Sycamore processor in October 2019, the random circuit sampling benchmarks were implemented in Cirq. The framework’s GridQubit type directly maps to the 2D grid layout of Google’s chips, making it the natural choice for anyone targeting Google hardware. Cirq reached version 1.0 in late 2022, signaling API stability.

The framework lives under the quantumlib GitHub organization alongside related Google quantum projects such as OpenFermion, TensorFlow Quantum, and Stim (a fast Clifford circuit simulator for quantum error correction research). Cirq serves as the circuit construction backend for TensorFlow Quantum and integrates with the Google Quantum Computing Service for cloud access to Google’s processors. As of 2025, Cirq has over 4,000 GitHub stars and a steady contributor community, though it is smaller than Qiskit’s. Its niche is strongest among researchers working directly with Google hardware, studying quantum error correction, or building low-level circuit optimizations. Cirq’s sympy-based parameterization system and support for parameter sweeps make it particularly well suited for variational algorithm research where many circuit evaluations are needed.

Installation

pip install cirq

For Google Cloud Quantum Computing Service access:

pip install cirq-google

Key Imports

import cirq
from cirq import Circuit, LineQubit, GridQubit, Simulator

Qubit Types

Cirq has three qubit types. The type you use often depends on hardware layout.

TypeUse case
cirq.LineQubit(n)1D chain - good for algorithms
cirq.GridQubit(row, col)2D grid - matches Google hardware
cirq.NamedQubit('name')Arbitrary label - readable circuits
q0, q1 = cirq.LineQubit.range(2)
q = cirq.GridQubit(0, 1)
q = cirq.NamedQubit('ancilla')

Gates

Gates are applied to qubits using the on() method or by calling the gate directly.

cirq.H(q0)           # Hadamard
cirq.X(q0)           # Pauli-X
cirq.Y(q0)           # Pauli-Y
cirq.Z(q0)           # Pauli-Z
cirq.S(q0)           # S gate
cirq.T(q0)           # T gate
cirq.CNOT(q0, q1)    # Controlled-NOT
cirq.CZ(q0, q1)      # Controlled-Z
cirq.SWAP(q0, q1)    # SWAP
cirq.TOFFOLI(q0, q1, q2)  # Toffoli

Rotation gates:

cirq.rx(rads=1.57)(q0)   # Rx rotation
cirq.ry(rads=1.57)(q0)   # Ry rotation
cirq.rz(rads=1.57)(q0)   # Rz rotation

Parameterised gates:

import sympy
theta = sympy.Symbol('theta')
cirq.rx(theta)(q0)

Circuit Construction

circuit = cirq.Circuit()

# Add individual moments
circuit.append(cirq.H(q0))
circuit.append([cirq.H(q0), cirq.X(q1)])   # parallel ops

# Moment-by-moment control
circuit.append(cirq.Moment([cirq.H(q0), cirq.H(q1)]))

# One-liner
circuit = cirq.Circuit(cirq.H(q0), cirq.CNOT(q0, q1))

Measurement

# Measure specific qubits with a key
cirq.measure(q0, q1, key='result')

# Measure all at end
circuit.append(cirq.measure(q0, q1, key='m'))

Simulators

Simulator (statevector)

sim = cirq.Simulator()
result = sim.simulate(circuit)
print(result.final_state_vector)

DensityMatrixSimulator

sim = cirq.DensityMatrixSimulator()
result = sim.simulate(circuit)

Sample-based (like hardware)

sim = cirq.Simulator()
result = sim.run(circuit, repetitions=1024)
print(result.histogram(key='result'))
print(result.measurements)

Common Patterns

Bell state

q0, q1 = cirq.LineQubit.range(2)
circuit = cirq.Circuit([
    cirq.H(q0),
    cirq.CNOT(q0, q1),
    cirq.measure(q0, q1, key='m'),
])

Parameterised sweep

import sympy, numpy as np

theta = sympy.Symbol('theta')
circuit = cirq.Circuit(cirq.ry(theta)(q0), cirq.measure(q0, key='m'))

sweep = cirq.Linspace('theta', start=0, stop=2*np.pi, length=10)
results = cirq.Simulator().run_sweep(circuit, params=sweep, repetitions=100)

Noise model

noise = cirq.ConstantQubitNoiseModel(cirq.depolarize(0.01))
sim = cirq.DensityMatrixSimulator(noise=noise)

Circuit Visualisation

print(circuit)          # ASCII diagram
circuit.to_text_diagram()

Grover’s search (2-qubit)

A minimal Grover’s algorithm that searches for the marked state |11⟩. The oracle uses a CZ gate to flip the phase of the target state, and the diffuser applies the standard inversion-about-the-mean operator. For two qubits, a single Grover iteration is sufficient to reach the answer with certainty.

import cirq

q0, q1 = cirq.LineQubit.range(2)

# Initialize: put both qubits into equal superposition
init = [cirq.H(q0), cirq.H(q1)]

# Oracle: mark |11⟩ by flipping its phase with CZ
oracle = [cirq.CZ(q0, q1)]

# Diffuser (inversion about the mean)
# Apply H, X to both qubits, then CZ, then X, H again
diffuser = [
    cirq.H(q0), cirq.H(q1),
    cirq.X(q0), cirq.X(q1),
    cirq.CZ(q0, q1),
    cirq.X(q0), cirq.X(q1),
    cirq.H(q0), cirq.H(q1),
]

# Measurement
meas = [cirq.measure(q0, q1, key='result')]

# Build full circuit: init -> oracle -> diffuser -> measure
circuit = cirq.Circuit(init + oracle + diffuser + meas)
print(circuit)

# Run and verify |11⟩ is found with high probability
sim = cirq.Simulator()
result = sim.run(circuit, repetitions=1024)
print(result.histogram(key='result'))
# Expected output: Counter({3: 1024})  (3 in decimal is |11⟩)

VQE on H2 with Cirq and OpenFermion

A simplified variational quantum eigensolver (VQE) that estimates the ground-state energy of molecular hydrogen. This example uses OpenFermion for the Hamiltonian and a single-parameter hardware-efficient ansatz. In practice you would use a classical optimizer such as scipy.optimize.minimize; here we sweep over parameter values to illustrate the energy landscape.

pip install cirq openfermion openfermionpyscf
import cirq
import sympy
import numpy as np
import openfermion
from openfermion.chem import MolecularData
from openfermionpyscf import run_pyscf

# 1. Build the H2 Hamiltonian at bond length 0.74 Å
geometry = [('H', (0.0, 0.0, 0.0)), ('H', (0.0, 0.0, 0.74))]
basis = 'sto-3g'
multiplicity = 1
charge = 0

molecule = MolecularData(geometry, basis, multiplicity, charge)
molecule = run_pyscf(molecule)

# Get the qubit Hamiltonian via Jordan-Wigner transformation
hamiltonian = openfermion.jordan_wigner(
    molecule.get_molecular_hamiltonian()
)
hamiltonian_sparse = openfermion.get_sparse_operator(hamiltonian)

# 2. Build a simple parameterised ansatz on 2 qubits
q0, q1 = cirq.LineQubit.range(2)
theta = sympy.Symbol('theta')

ansatz = cirq.Circuit([
    cirq.X(q0),               # Start in |10⟩ (single excitation reference)
    cirq.ry(theta)(q1),       # Parameterised rotation
    cirq.CNOT(q0, q1),        # Entangle
])

# 3. Sweep theta and compute expectation value ⟨ψ(θ)|H|ψ(θ)⟩
sim = cirq.Simulator()
angles = np.linspace(0, 2 * np.pi, 50)
energies = []

for angle in angles:
    resolver = cirq.ParamResolver({'theta': angle})
    result = sim.simulate(ansatz, param_resolver=resolver)
    state = result.final_state_vector

    # Pad state to match Hamiltonian dimension (4 qubits for STO-3G H2)
    full_state = np.zeros(hamiltonian_sparse.shape[0], dtype=complex)
    for i, amp in enumerate(state):
        full_state[i] = amp

    energy = np.real(
        full_state.conj() @ hamiltonian_sparse.toarray() @ full_state
    )
    energies.append(energy)

min_energy = min(energies)
best_angle = angles[np.argmin(energies)]
print(f"VQE minimum energy: {min_energy:.6f} Ha at theta = {best_angle:.4f}")
print(f"Exact ground state energy: {molecule.fci_energy:.6f} Ha")

Note: the STO-3G H2 Hamiltonian acts on 4 qubits, but our 2-qubit ansatz explores only a subspace. A production VQE would use all 4 qubits with a more expressive ansatz (such as UCCSD) and a classical optimizer loop.

Running on Google hardware via cirq-google

To execute circuits on Google’s quantum processors, you use the cirq_google module and the Quantum Engine API. Access requires a Google Cloud project that has been approved for the quantum computing service.

import cirq
import cirq_google

# Authenticate and create an Engine client
# Requires: GOOGLE_CLOUD_PROJECT env var or explicit project ID
engine = cirq_google.get_engine(project_id='your-gcp-project')

# List available processors
processors = engine.list_processors()
for p in processors:
    print(p.processor_id)

# Select a processor and get its device specification
processor = engine.get_processor('weber')
device = processor.get_device()

# Build a circuit using GridQubits that match the device layout
q0 = cirq.GridQubit(5, 3)
q1 = cirq.GridQubit(5, 4)

circuit = cirq.Circuit([
    cirq.H(q0),
    cirq.CNOT(q0, q1),
    cirq.measure(q0, q1, key='m'),
])

# Validate the circuit against the device's connectivity and gate set
device.validate_circuit(circuit)

# Run on hardware
sampler = processor.get_sampler()
result = sampler.run(circuit, repetitions=1000)
print(result.histogram(key='m'))

Native gates on Sycamore-family processors:

Google’s processors support a specific native gate set. The Sycamore gate (cirq_google.SYC) is a two-qubit gate native to Google’s hardware and more natural than CNOT for these devices:

# The Sycamore gate: a native fSim gate with specific angles
syc = cirq_google.SYC
print(cirq.unitary(syc).round(3))

# Use the Sycamore gate directly in a circuit
circuit = cirq.Circuit([
    cirq_google.SYC(q0, q1),
    cirq.measure(q0, q1, key='m'),
])

# Compile an arbitrary circuit to the device's native gate set
compiled = cirq_google.optimized_for_sycamore(
    circuit,
    new_device=device,
    optimizer_type='sqrt_iswap',  # or 'sycamore'
)

Serialization

Cirq supports JSON serialization for circuits, making it straightforward to save, load, and share circuit definitions.

import cirq

q0, q1 = cirq.LineQubit.range(2)
circuit = cirq.Circuit([
    cirq.H(q0),
    cirq.CNOT(q0, q1),
    cirq.measure(q0, q1, key='m'),
])

# Serialize to JSON string
json_str = cirq.to_json(circuit)
print(json_str)

# Write directly to a file
cirq.to_json(circuit, 'my_circuit.json')

# Read back from a file
loaded_circuit = cirq.read_json('my_circuit.json')
assert circuit == loaded_circuit

# Read from a JSON string
loaded_from_str = cirq.read_json(json_string=json_str)
assert circuit == loaded_from_str

Serialization covers most built-in Cirq objects including gates, qubits, circuits, and noise models. Custom gate types need to implement the cirq.json_serialization protocol or be registered with a custom resolver.

Cirq vs Other Frameworks

  • Google hardware access. Cirq is the only framework with first-party support for Google Quantum AI processors. Qiskit targets IBM hardware, and PennyLane connects to multiple backends through plugins but has no native Google integration. If you are running experiments on Sycamore-family chips, Cirq is the required tool.
  • Moment-based circuit construction. Cirq organizes operations into discrete Moment objects, giving explicit control over which gates execute in parallel. Qiskit builds circuits gate-by-gate and relies on transpiler passes to schedule parallelism. PennyLane uses a tape-based model focused on differentiability. Cirq’s approach is particularly useful when optimizing circuit depth for noisy hardware.
  • Hardware topology matching with GridQubit. Cirq’s GridQubit(row, col) type maps directly to the 2D grid layout of superconducting processors, letting you reason about qubit connectivity at the circuit design stage. Qiskit handles topology mapping during transpilation, and PennyLane abstracts physical layout entirely. For hardware-aware algorithm design, Cirq’s model reduces the gap between logical and physical circuits.
  • Native Sycamore gate. Cirq provides cirq_google.SYC, the fSim-family gate native to Google’s processors, as a first-class object. Other frameworks would need to decompose this gate or define it manually. Working with the native gate set avoids unnecessary decomposition overhead and gives more accurate noise modeling.
  • Parameter sweeps for variational algorithms. Cirq’s run_sweep and Linspace/Points/Zip sweep objects let you evaluate a parameterized circuit across many parameter values in a single call, with efficient batching on both simulators and hardware. Qiskit supports parameter binding but without the same sweep abstraction. PennyLane takes a different approach, integrating directly with autodiff frameworks (PyTorch, JAX) for gradient computation, which is more natural for machine learning workloads.
  • Ecosystem scope. Qiskit has the largest ecosystem: transpiler, runtime primitives, Aer noise simulation, and a broad provider network. PennyLane excels at differentiable quantum programming and hybrid quantum-classical ML. Cirq is more focused and lower-level, which makes it powerful for researchers who want precise circuit control but means it has fewer high-level abstractions for application development.

Learning Resources