QuantumCore

Hands-on lab · 1 step

Gradients with the parameter-shift rule

Implement the parameter-shift gradient of a variational circuit using only a black-box evaluate(params) that returns the exact expectation value f(θ)=⟨ψ(θ)∣H∣ψ(θ)⟩f(\theta)=\langle\psi(\theta)\vert H\vert\psi(\theta)\rangle. Every parameter sits in a single Pauli rotation RP(θj)=e−iθjP/2R_P(\theta_j)=e^{-i\theta_jP/2}, so ∂jf(θ)=12[f(θ+π2ej)−f(θ−π2ej)]\partial_jf(\theta)=\tfrac12\big[f(\theta+\tfrac\pi2e_j)-f(\theta-\tfrac\pi2e_j)\big] is exact. The grader checks the values on one visible and four hidden random three-qubit circuits, separately checks that your function calls evaluate exactly 2P2P times, and finally checks that the values are exact to 10−1210^{-12}, which no finite-difference quotient achieves.

0/1 step passedBest score 0/100

Step 1: Implement parameterShiftGradient

Complete the TypeScript function

parameterShiftGradient(evaluate: (params: number[]) => number, params: number[]): number[]

What evaluate does. evaluate(p) runs a fixed unitary circuit on ∣0⋯0⟩\lvert0\cdots0\rangle with the parameter values p and returns the exact expectation value f(p)=⟨ψ(p)∣H∣ψ(p)⟩f(p)=\langle\psi(p)\vert H\vert\psi(p)\rangle of a fixed observable HH (a real combination of Pauli strings). p must be an array with one finite number per parameter; entry p[j] is bound to the circuit's jj-th declared parameter. A wrong length or a non-finite entry throws. Every call is counted, as each call would be a separate circuit execution on hardware.

What to return. An array grad of length P=P= params.length with grad[j] =∂f/∂θj=\partial f/\partial\theta_j at θ=\theta= params. Every parameter-dependent gate is a Pauli rotation RP(θj)=e−iθjP/2R_P(\theta_j)=e^{-i\theta_jP/2} with P∈{X,Y,Z}P\in\{X,Y,Z\}, and each parameter appears in exactly one gate, so

∂f∂θj(θ)=12[f(θ+π2ej)−f(θ−π2ej)],\frac{\partial f}{\partial\theta_j}(\theta)=\frac12\Big[f\big(\theta+\tfrac\pi2e_j\big)-f\big(\theta-\tfrac\pi2e_j\big)\Big],

where eje_j is the jj-th unit vector: only entry jj is shifted.

Call budget. Exactly two calls of evaluate per parameter, i.e. exactly 2P2P calls in total. The unshifted value f(θ)f(\theta) is not needed. Do not modify params; work on copies.

Tests. Every test calls your function with the arguments (evaluate, params). The values check compares your gradient with the exact one; the calls check runs the same problems again and also requires exactly 2P2P calls; the exact check (tests exact-visible and exact-random-1 to exact-random-4) runs them once more with the absolute tolerance 10−1210^{-12}. The shift rule is exact up to floating-point rounding, so it passes all three; central finite differences with a small step also use 2P2P calls but fail exact.

  • visible and calls-visible: the circuit RY(0)(t1)R_Y^{(0)}(t_1), RY(1)(t2)R_Y^{(1)}(t_2), CNOT(0,1)\mathrm{CNOT}(0,1) with H=Z1H=Z_1, for which f(t1,t2)=cos⁡t1cos⁡t2f(t_1,t_2)=\cos t_1\cos t_2. At params = [0.4, 1.1] the gradient is (−sin⁡0.4cos⁡1.1, −cos⁡0.4sin⁡1.1)=(−0.1766386496831817, −0.8208563369208728)\big(-\sin0.4\cos1.1,\,-\cos0.4\sin1.1\big)=(-0.1766386496831817,\,-0.8208563369208728) (absolute tolerance 10−910^{-9}), using 2P=42P=4 calls.
  • random-1 to random-4 and calls-random-1 to calls-random-4 (hidden): random three-qubit circuits of RXR_X, RYR_Y, RZR_Z rotations interleaved with CNOTs, with respectively 7, 7, 7, 8 parameters and an observable that is a Pauli string or a sum of two weighted Pauli strings (absolute tolerance 10−710^{-7}). Only their arguments are sent to your browser; the expected gradients stay on the server.
TypeScript: export parameterShiftGradient, parameterShiftGradient, parameterShiftGradient · 15 tests · imports: quantumcore, quantumcore/qml
Run the tests first; the results of your latest run are submitted.