Parameterized Quantum Circuits and Rotation Gates

Parameterized quantum circuits (PQCs) contain gates with tunable parameters that can be optimized. Instead of using fixed angles, you use symbolic parameters that can be adjusted to find optimal values.

Why use parameters? Fixed angles give you one specific operation. Parameters let you:

  • Optimize: Find the best angles for your task
  • Train: Adjust parameters based on results (like machine learning)
  • Explore: Try different values without rewriting the circuit

Rotation gates (Rx, Ry, Rz) are the primary parameterized gates. Use string names (e.g., "theta") instead of numbers to create parameters, then bind values before execution.

Rotation Gates

Rotation gates rotate a qubit around the X, Y, or Z axis of the Bloch sphere. Each axis controls different aspects:

  • X-axis (Rx): Bit-flip rotations
  • Y-axis (Ry): Creates superpositions (like Hadamard)
  • Z-axis (Rz): Phase rotations (doesn't change probabilities)

Rx Gate

Rotates a qubit around the X-axis by angle θ.

[ R_x(\theta) = \begin{pmatrix} \cos(\theta/2) & -i\sin(\theta/2) \ -i\sin(\theta/2) & \cos(\theta/2) \end{pmatrix} ]

[ R_x(\pi/2)|0\rangle = \frac{1}{\sqrt{2}}(|0\rangle - i|1\rangle) ]

from qumat import QuMat
import numpy as np

backend_config = {
    'backend_name': 'qiskit',
    'backend_options': {'simulator_type': 'qasm_simulator', 'shots': 1000}
}
qumat = QuMat(backend_config)
qumat.create_empty_circuit(num_qubits=1)
qumat.apply_rx_gate(0, angle=np.pi / 2)
results = qumat.execute_circuit()

Ry Gate

Rotates a qubit around the Y-axis by angle θ.

[ R_y(\theta) = \begin{pmatrix} \cos(\theta/2) & -\sin(\theta/2) \ \sin(\theta/2) & \cos(\theta/2) \end{pmatrix} ]

[ R_y(\pi/2)|0\rangle = \frac{1}{\sqrt{2}}(|0\rangle + |1\rangle) ]

qumat.create_empty_circuit(num_qubits=1)
qumat.apply_ry_gate(0, angle=np.pi / 2)  # Creates (|0⟩ + |1⟩)/√2
results = qumat.execute_circuit()

Rz Gate

Rotates a qubit around the Z-axis by angle θ. Changes phase without affecting probability amplitudes.

[ R_z(\theta) = \begin{pmatrix} e^{-i\theta/2} & 0 \ 0 & e^{i\theta/2} \end{pmatrix} ]

[ R_z(\pi/2)\frac{1}{\sqrt{2}}(|0\rangle + |1\rangle) = \frac{1}{\sqrt{2}}(|0\rangle + i|1\rangle) ]

Probabilities remain: P(|0⟩) = P(|1⟩) = 1/2

qumat.create_empty_circuit(num_qubits=1)
qumat.apply_hadamard_gate(0)
qumat.apply_rz_gate(0, angle=np.pi / 2)
results = qumat.execute_circuit()

Notes: Angles are in radians (π = 180°, π/2 = 90°, π/4 = 45°). Supported across all backends (Qiskit, Cirq, Braket).

Creating Parameterized Circuits

Fixed value (angle is set once):

qumat.apply_ry_gate(0, angle=0.5)  # Fixed angle

Parameterized (angle can be changed):

qumat.apply_ry_gate(0, angle="theta")  # Parameter - can be optimized

Use string names instead of numbers to create parameters:

from qumat import QuMat
import numpy as np

backend_config = {
    'backend_name': 'qiskit',
    'backend_options': {'simulator_type': 'qasm_simulator', 'shots': 1000}
}
qumat = QuMat(backend_config)

# Step 1: Create circuit with parameterized gates
qumat.create_empty_circuit(num_qubits=2)
qumat.apply_ry_gate(0, angle="theta")  # String = parameter
qumat.apply_rz_gate(0, angle="phi")
qumat.apply_rx_gate(1, angle="alpha")

# Step 2: Parameters are automatically registered
print(list(qumat.parameters.keys()))  # ['theta', 'phi', 'alpha']

# Step 3: Bind parameters before execution
qumat.bind_parameters({
    "theta": np.pi / 4,
    "phi": np.pi / 2,
    "alpha": np.pi / 3
})

# Step 4: Execute with bound parameters
results = qumat.execute_circuit()

# Alternative: Bind during execution (skips step 3)
results = qumat.execute_circuit(parameter_values={
    "theta": np.pi / 4,
    "phi": np.pi / 2,
    "alpha": np.pi / 3
})

bind_parameters()

Assigns numerical values to symbolic parameters. Must be called before execute_circuit() if parameters are used.

Raises: ValueError if parameter name not found.

# Create circuit with parameters
qumat.create_empty_circuit(num_qubits=1)
qumat.apply_ry_gate(0, angle="theta")
qumat.apply_rz_gate(0, angle="phi")

# Bind values to parameters
qumat.bind_parameters({"theta": 0.5, "phi": 1.0})

# Now you can execute
results = qumat.execute_circuit()

Optimization Loops

The power of parameterized circuits: optimize parameters to minimize a cost function. The workflow is:

  1. Create circuit with parameters
  2. Try different parameter values
  3. Execute and compute cost
  4. Find values that minimize cost

Example:

from qumat import QuMat
import numpy as np

backend_config = {
    'backend_name': 'qiskit',
    'backend_options': {'simulator_type': 'qasm_simulator', 'shots': 1000}
}
qumat = QuMat(backend_config)

# Create parameterized circuit once
qumat.create_empty_circuit(num_qubits=2)
qumat.apply_ry_gate(0, angle="theta")
qumat.apply_cnot_gate(0, 1)
qumat.apply_rz_gate(1, angle="phi")

# Simple optimization loop
best_cost = float('inf')
best_params = None

for iteration in range(10):
    # Try different parameter values
    theta = np.random.uniform(0, 2 * np.pi)
    phi = np.random.uniform(0, 2 * np.pi)

    # Bind and execute
    qumat.bind_parameters({"theta": theta, "phi": phi})
    results = qumat.execute_circuit()

    # Compute cost (example: minimize probability of |00⟩)
    total_shots = sum(results.values())
    prob_00 = results.get("00", 0) / total_shots
    cost = prob_00

    # Track best result
    if cost < best_cost:
        best_cost = cost
        best_params = {"theta": theta, "phi": phi}

print(f"Best parameters: {best_params}, cost: {best_cost}")

# With SciPy optimizer
from scipy.optimize import minimize

def cost_function(params):
    theta, phi = params
    qumat.bind_parameters({"theta": theta, "phi": phi})
    results = qumat.execute_circuit()
    total_shots = sum(results.values())
    prob_00 = results.get("00", 0) / total_shots
    return prob_00

result = minimize(cost_function, [0.5, 0.5], method='COBYLA')
print(f"Optimized: {result.x}, cost: {result.fun}")

Key Points

  • Parameter creation: Use string names (e.g., "theta") in rotation gates to create parameters
  • Auto-registration: Parameters are automatically registered when first used
  • Binding required: All parameters must be bound (assigned values) before execution
  • Value types: Parameter values must be numerical (float or int)
  • Backend support: Works with all backends (Qiskit, Cirq, Braket)
  • Reuse: Create circuit once, bind different parameter values for optimization loops