def test_nums_frequency_and_spectra_missing(self): """Tests that an error is raised if neither information about the number of frequencies nor about the spectrum is given to ``reconstruct``.""" with pytest.raises( ValueError, match="Either nums_frequency or spectra must be given."): reconstruct(dummy_qnode)
def test_with_qnode(self, qnode, params, ids, nums_frequency, spectra, shifts, exp_calls, mocker): """Run a full reconstruction on a QNode.""" qnode = qml.QNode(qnode, dev_1) with qml.Tracker(qnode.device) as tracker: recons = reconstruct(qnode, ids, nums_frequency, spectra, shifts)(*params) assert tracker.totals["executions"] == exp_calls arg_names = list(signature(qnode.func).parameters.keys()) for outer_key in recons: outer_key_num = arg_names.index(outer_key) for inner_key, rec in recons[outer_key].items(): x0 = params[outer_key_num] if not pnp.isscalar(x0): x0 = x0[inner_key] shift_vec = qml.math.zeros_like(params[outer_key_num]) shift_vec[inner_key] = 1.0 shift_vec = 1.0 if pnp.isscalar( params[outer_key_num]) else shift_vec mask = (0.0 if pnp.isscalar(params[outer_key_num]) else pnp.ones(qml.math.shape(params[outer_key_num])) - shift_vec) univariate = lambda x: qnode( *params[:outer_key_num], params[outer_key_num] * mask + x * shift_vec, *params[outer_key_num + 1:], ) assert np.isclose(rec(x0), qnode(*params)) assert np.isclose(rec(x0 + 0.1), univariate(x0 + 0.1)) assert fun_close(rec, univariate, 10)
def test_differentiability_torch( self, qnode, params, ids, nums_frequency, spectra, shifts, exp_calls, mocker ): """Tests the reconstruction and differentiability with Torch.""" torch = pytest.importorskip("torch") qnode = qml.QNode(qnode, dev_1, interface="torch") params = tuple(torch.tensor(par, requires_grad=True, dtype=torch.float64) for par in params) if spectra is not None: spectra = { outer_key: { inner_key: torch.tensor(val, dtype=torch.float64) for inner_key, val in outer_val.items() } for outer_key, outer_val in spectra.items() } if shifts is not None: shifts = { outer_key: { inner_key: torch.tensor(val, dtype=torch.float64) for inner_key, val in outer_val.items() } for outer_key, outer_val in shifts.items() } with qml.Tracker(qnode.device) as tracker: recons = reconstruct(qnode, ids, nums_frequency, spectra, shifts)(*params) assert tracker.totals["executions"] == exp_calls arg_names = list(signature(qnode.func).parameters.keys()) for outer_key in recons: outer_key_num = arg_names.index(outer_key) for inner_key, rec in recons[outer_key].items(): x0 = params[outer_key_num] if not len(qml.math.shape(x0)) == 0: x0 = x0[inner_key] shift_vec = qml.math.zeros_like(params[outer_key_num]) shift_vec = qml.math.scatter_element_add(shift_vec, inner_key, 1.0) mask = torch.ones(qml.math.shape(params[outer_key_num])) - shift_vec else: shift_vec = 1.0 mask = 0.0 univariate = lambda x: qnode( *params[:outer_key_num], params[outer_key_num] * mask + x * shift_vec, *params[outer_key_num + 1 :], ) exp_qnode_grad = torch.autograd.functional.jacobian(qnode, params)[outer_key_num] exp_grad = lambda x: torch.autograd.functional.jacobian(univariate, x) grad = lambda x: torch.autograd.functional.jacobian(rec, x) assert np.isclose(grad(x0), exp_qnode_grad[inner_key]) assert np.isclose(grad(x0 + 0.1), exp_grad(x0 + 0.1)) assert fun_close( grad, exp_grad, zero=torch.tensor(0.0, requires_grad=True), samples=10 )
def test_differentiability_jax(self, qnode, params, ids, nums_frequency, spectra, shifts, exp_calls, mocker): """Tests the reconstruction and differentiability with JAX.""" jax = pytest.importorskip("jax") from jax.config import config config.update("jax_enable_x64", True) params = tuple(jax.numpy.array(par) for par in params) qnode = qml.QNode(qnode, dev_1, interface="jax") with qml.Tracker(qnode.device) as tracker: recons = reconstruct(qnode, ids, nums_frequency, spectra, shifts)(*params) assert tracker.totals["executions"] == exp_calls arg_names = list(signature(qnode.func).parameters.keys()) for outer_key in recons: outer_key_num = arg_names.index(outer_key) for inner_key, rec in recons[outer_key].items(): x0 = params[outer_key_num] if not pnp.isscalar(x0): x0 = x0[inner_key] shift_vec = qml.math.zeros_like(params[outer_key_num]) shift_vec = qml.math.scatter_element_add( shift_vec, inner_key, 1.0) shift_vec = 1.0 if pnp.isscalar( params[outer_key_num]) else shift_vec mask = (0.0 if pnp.isscalar(params[outer_key_num]) else pnp.ones(qml.math.shape(params[outer_key_num])) - shift_vec) univariate = lambda x: qnode( *params[:outer_key_num], params[outer_key_num] * mask + x * shift_vec, *params[outer_key_num + 1:], ) exp_qnode_grad = jax.grad(qnode, argnums=outer_key_num) exp_grad = jax.grad(univariate) grad = jax.grad(rec) assert np.isclose(grad(x0), exp_qnode_grad(*params)[inner_key]) assert np.isclose(grad(x0 + 0.1), exp_grad(x0 + 0.1)) assert fun_close(grad, exp_grad, 10)
def test_differentiability_autograd(self, qnode, params, ids, nums_frequency, spectra, shifts, exp_calls, mocker): """Tests the reconstruction and differentiability with autograd.""" qnode = qml.QNode(qnode, dev_1, interface="autograd") with qml.Tracker(qnode.device) as tracker: recons = reconstruct(qnode, ids, nums_frequency, spectra, shifts)(*params) assert tracker.totals["executions"] == exp_calls arg_names = list(signature(qnode.func).parameters.keys()) for outer_key in recons: outer_key_num = arg_names.index(outer_key) for inner_key, rec in recons[outer_key].items(): x0 = params[outer_key_num] if not pnp.isscalar(x0): x0 = x0[inner_key] shift_vec = qml.math.zeros_like(params[outer_key_num]) shift_vec[inner_key] = 1.0 shift_vec = 1.0 if pnp.isscalar( params[outer_key_num]) else shift_vec mask = (0.0 if pnp.isscalar(params[outer_key_num]) else pnp.ones(qml.math.shape(params[outer_key_num])) - shift_vec) univariate = lambda x: qnode( *params[:outer_key_num], params[outer_key_num] * mask + x * shift_vec, *params[outer_key_num + 1:], ) exp_qnode_grad = qml.grad(qnode, argnum=outer_key_num) exp_grad = qml.grad(univariate) grad = qml.grad(rec) if nums_frequency is None: # Gradient evaluation at reconstruction point not supported for # Dirichlet reconstruction assert np.isclose(grad(x0), exp_qnode_grad(*params)[inner_key]) assert np.isclose(grad(x0 + 0.1), exp_grad(x0 + 0.1)) assert fun_close(grad, exp_grad, 10)
def test_wrong_number_of_shifts(self, spectra, shifts): """Tests that an error is raised if the number of provided shifts does not match.""" with pytest.raises(ValueError, match="The number of provided shifts"): reconstruct(dummy_qnode, spectra=spectra, shifts=shifts)
def test_differentiability_tensorflow(self, qnode, params, ids, nums_frequency, spectra, shifts, exp_calls, mocker): """Tests the reconstruction and differentiability with TensorFlow.""" if qnode == qnode_4: pytest.skip( "Gradients are empty in TensorFlow for independent functions.") tf = pytest.importorskip("tensorflow") qnode = qml.QNode(qnode, dev_1, interface="tf") params = tuple(tf.Variable(par, dtype=tf.float64) for par in params) if spectra is not None: spectra = { outer_key: { inner_key: tf.constant(val, dtype=tf.float64) for inner_key, val in outer_val.items() } for outer_key, outer_val in spectra.items() } if shifts is not None: shifts = { outer_key: { inner_key: tf.constant(val, dtype=tf.float64) for inner_key, val in outer_val.items() } for outer_key, outer_val in shifts.items() } with qml.Tracker(qnode.device) as tracker: recons = reconstruct(qnode, ids, nums_frequency, spectra, shifts)(*params) assert tracker.totals["executions"] == exp_calls arg_names = list(signature(qnode.func).parameters.keys()) for outer_key in recons: outer_key_num = arg_names.index(outer_key) for inner_key, rec in recons[outer_key].items(): if outer_key == "Z" and inner_key == (1, 3): # This is a constant function dependence, which can # not be properly resolved by this test. continue x0 = params[outer_key_num] if not len(qml.math.shape(x0)) == 0: x0 = x0[inner_key] shift_vec = qml.math.zeros_like(params[outer_key_num]) shift_vec = qml.math.scatter_element_add( shift_vec, inner_key, 1.0) mask = pnp.ones(qml.math.shape( params[outer_key_num])) - shift_vec else: shift_vec = 1.0 mask = 0.0 univariate = lambda x: qnode( *params[:outer_key_num], params[outer_key_num] * mask + x * shift_vec, *params[outer_key_num + 1:], ) with tf.GradientTape() as tape: out = qnode(*params) exp_qnode_grad = tape.gradient(out, params[outer_key_num]) def exp_grad(x): x = tf.Variable(x, dtype=tf.float64) with tf.GradientTape() as tape: out = univariate(x) return tape.gradient(out, x) def grad(x): x = tf.Variable(x, dtype=tf.float64) with tf.GradientTape() as tape: out = rec(x) return tape.gradient(out, x) if nums_frequency is None: # Gradient evaluation at reconstruction point not supported for # Dirichlet reconstruction assert np.isclose(grad(x0), exp_qnode_grad[inner_key]) assert np.isclose(grad(x0 + 0.1), exp_grad(x0 + 0.1)) assert fun_close(grad, exp_grad, 10)