Source code for uniqc.pytorch.gradient
"""
Parameter-shift rule gradient computation for quantum circuits.
The parameter-shift rule allows exact gradient computation for parametric
quantum gates without finite differences. For gates with generator G:
∂⟨G(θ)⟩/∂θ = (⟨G(θ + π/2)⟩ - ⟨G(θ - π/2)⟩) / 2
This module provides functions to compute gradients for all parameters
in a parametric circuit.
"""
from __future__ import annotations
from collections.abc import Callable
from typing import TYPE_CHECKING
import numpy as np
if TYPE_CHECKING:
from uniqc.circuit_builder import Circuit
__all__ = ["parameter_shift_gradient", "compute_all_gradients"]
[docs]
def parameter_shift_gradient(
circuit: Circuit,
param_name: str,
expectation_fn: Callable[[Circuit], float],
shift: float = np.pi / 2,
) -> float:
"""Compute gradient using the parameter-shift rule.
For a parametric gate G(θ), the gradient of an expectation value is:
∂⟨G(θ)⟩/∂θ = 0.5 * (⟨G(θ + π/2)⟩ - ⟨G(θ - π/2)⟩)
Args:
circuit: Parametric circuit with the parameter
param_name: Name of the parameter to differentiate
expectation_fn: Function that computes expectation value from a circuit
shift: Shift value (default π/2 for standard Pauli rotation gates)
Returns:
Gradient value
Example:
>>> def expectation(c):
... # Simulate and compute <Z0>
... sim.simulate(c.originir)
... return sim.expectation([("Z", [0])])
>>> grad = parameter_shift_gradient(circuit, "theta", expectation)
"""
# Get base parameter value
if not hasattr(circuit, "_parameters") or param_name not in circuit._parameters:
raise ValueError(f"Parameter '{param_name}' not found in circuit")
param = circuit._parameters[param_name]
base_value = param._bound_value if param.is_bound() else 0.0
# Plus shift circuit
plus_circuit = circuit.copy()
plus_circuit._parameters[param_name].bind(base_value + shift)
# Minus shift circuit
minus_circuit = circuit.copy()
minus_circuit._parameters[param_name].bind(base_value - shift)
# Compute expectations
exp_plus = expectation_fn(plus_circuit)
exp_minus = expectation_fn(minus_circuit)
return 0.5 * (exp_plus - exp_minus)
[docs]
def compute_all_gradients(
circuit: Circuit,
expectation_fn: Callable[[Circuit], float],
shift: float = np.pi / 2,
) -> dict[str, float]:
"""Compute gradients for all parameters in a circuit.
Uses the parameter-shift rule for each parameter independently.
Args:
circuit: Parametric circuit
expectation_fn: Function that computes expectation value from a circuit
shift: Shift value for parameter-shift rule
Returns:
Dictionary mapping parameter names to gradient values
Example:
>>> grads = compute_all_gradients(circuit, expectation)
>>> print(grads) # {'theta': 0.5, 'phi': -0.3}
"""
gradients = {}
if not hasattr(circuit, "_parameters"):
return gradients
for param_name in circuit._parameters:
gradients[param_name] = parameter_shift_gradient(circuit, param_name, expectation_fn, shift)
return gradients