class_docstr: Tests for caching behaviour
class_docstr_tok: ["Tests","for","caching","behaviour"]
docstr: Test that, when using parameter-shift transform,
  caching reduces the number of evaluations to their optimum
  when computing Hessians.
docstr_tok: ["Test","that",",","when","using","parameter-shift","transform",",","caching","reduces","the","number","of","evaluations","to","their","optimum","when","computing","Hessians","."]
code: def test_caching_param_shift_hessian(self, num_params):
      """Test that, when using parameter-shift transform,
      caching reduces the number of evaluations to their optimum
      when computing Hessians."""
      dev = DefaultQubit()
      params = torch.arange(
          1, num_params + 1, requires_grad=True, dtype=torch.float64
      )
  
      N = len(params)
  
      def get_cost_tape(x):
          with qml.queuing.AnnotatedQueue() as q:
              qml.RX(x[0], wires=[0])
              qml.RY(x[1], wires=[1])
  
              for i in range(2, num_params):
                  qml.RZ(x[i], wires=[i % 2])
  
              qml.CNOT(wires=[0, 1])
              qml.var(qml.prod(qml.PauliZ(0), qml.PauliX(1)))
  
          return qml.tape.QuantumScript.from_queue(q)
  
      def cost_no_cache(x):
          return qml.execute(
              [get_cost_tape(x)],
              dev,
              gradient_fn=qml.gradients.param_shift,
              cache=False,
              max_diff=2,
          )[0]
  
      def cost_cache(x):
          return qml.execute(
              [get_cost_tape(x)],
              dev,
              gradient_fn=qml.gradients.param_shift,
              cache=True,
              max_diff=2,
          )[0]
  
      # No caching: number of executions is not ideal
      with qml.Tracker(dev) as tracker:
          hess1 = torch.autograd.functional.hessian(
              cost_no_cache, params
          )
  
      if num_params == 2:
          # compare to theoretical result
          x, y, *_ = params.clone().detach()
          expected = torch.tensor(
              [
                  [
                      2 * torch.cos(2 * x) * torch.sin(y) ** 2,
                      torch.sin(2 * x) * torch.sin(2 * y),
                  ],
                  [
                      torch.sin(2 * x) * torch.sin(2 * y),
                      -2 * torch.cos(x) ** 2 * torch.cos(2 * y),
                  ],
              ]
          )
          assert torch.allclose(expected, hess1)
  
      expected_runs = 1  # forward pass
  
      # Jacobian of an involutory observable:
      # ------------------------------------
      #
      # 2 * N execs: evaluate the analytic derivative of <A>
      # 1 execs: Get <A>, the expectation value of the tape with unshifted parameters.
      num_shifted_evals = 2 * N
      runs_for_jacobian = num_shifted_evals + 1
      expected_runs += runs_for_jacobian
  
      # Each tape used to compute the Jacobian is then shifted again
      expected_runs += runs_for_jacobian * num_shifted_evals
      assert tracker.totals["executions"] == expected_runs
  
      # Use caching: number of executions is ideal
  
      with qml.Tracker(dev) as tracker2:
          hess2 = torch.autograd.functional.hessian(cost_cache, params)
      assert torch.allclose(hess1, hess2)
  
      expected_runs_ideal = 1  # forward pass
      expected_runs_ideal += 2 * N  # Jacobian
      expected_runs_ideal += N + 1  # Hessian diagonal
      expected_runs_ideal += (
          4 * N * (N - 1) // 2
      )  # Hessian off-diagonal
      assert tracker2.totals["executions"] == expected_runs_ideal
      assert expected_runs_ideal < expected_runs
  
imports: import numpy
  import pytest
  from param_shift_dev import ParamShiftDerivativesDevice
  import pennylane
  from pennylane.devices import DefaultQubit
  from pennylane.gradients import param_shift
  from pennylane import execute
  from pennylane.measurements import Shots
label_desc: Use image node_id 1 for calling the TestCaching obj's underlying member method code with example usage: obj.test_caching_param_shift_hessian(num_params) and returns: qml, qml, qml