def __init__(self, backend='noiseless', differentiator=None, **kwargs): """Instantiate this Layer. Create a layer that will output expectation values gained from simulating a quantum circuit. Args: backend: Optional Backend to use to simulate states. Defaults to the 'noiseless' simulator, options include {'noiseless', 'noisy'}. In the noisy case a `repetitions` call argument must be provided. Users may also specify a preconfigured cirq object to use instead, which must inherit `cirq.sim.simulator.SimulatesExpectationValues`. differentiator: Optional Differentiator to use to calculate analytic derivative values of given operators_to_measure and circuit, which must inherit `tfq.differentiators.Differentiator` and implements `differentiate_analytic` method. Defaults to None, which uses `tfq.differentiators.ParameterShift()`. If `backend` is also 'noiseless' then default is `tfq.differentiators.Adjoint`. """ super().__init__(**kwargs) # Ingest backend. if not isinstance( backend, cirq.sim.simulator.SimulatesExpectationValues) and \ isinstance(backend, cirq.Sampler): raise TypeError("Backend implements cirq.Sampler but not " "cirq.sim.simulator.SimulatesExpectationValues. " "Please use SampledExpectation instead.") used_op = None self.noisy = False if backend == 'noiseless': backend = None # Ingest differentiator. if differentiator is None: differentiator = parameter_shift.ParameterShift() if backend is None: differentiator = adjoint.Adjoint() if not isinstance(differentiator, diff.Differentiator): raise TypeError("Differentiator must inherit from " "tfq.differentiators.Differentiator") if backend == 'noisy': used_op = noisy_expectation_op.expectation self._expectation_op = differentiator.generate_differentiable_op( sampled_op=used_op) self.noisy = True else: used_op = circuit_execution_ops.get_expectation_op(backend=backend) self._expectation_op = differentiator.generate_differentiable_op( analytic_op=used_op) self._w = None
def test_gradient_circuits_grad_comparison(self, differentiator, n_qubits, n_programs, n_ops, symbol_names): """Test that analytic gradient agrees with the one from grad circuits""" # Get random circuits to check. qubits = cirq.GridQubit.rect(1, n_qubits) circuit_batch, resolver_batch = \ util.random_symbol_circuit_resolver_batch( cirq.GridQubit.rect(1, n_qubits), symbol_names, n_programs) psums = [ util.random_pauli_sums(qubits, 1, n_ops) for _ in circuit_batch ] # Convert to tensors. symbol_names_array = np.array(symbol_names) symbol_values_array = np.array( [[resolver[symbol] for symbol in symbol_names] for resolver in resolver_batch], dtype=np.float32) symbol_names_tensor = tf.convert_to_tensor(symbol_names_array) symbol_values_tensor = tf.convert_to_tensor(symbol_values_array) programs = util.convert_to_tensor(circuit_batch) ops_tensor = util.convert_to_tensor(psums) # Get gradients using expectations of gradient circuits. (batch_programs, new_symbol_names, batch_symbol_values, batch_weights, batch_mapper) = differentiator.get_gradient_circuits( programs, symbol_names_tensor, symbol_values_tensor) analytic_op = circuit_execution_ops.get_expectation_op() batch_pauli_sums = tf.tile(tf.expand_dims(ops_tensor, 1), [1, tf.shape(batch_programs)[1], 1]) n_batch_programs = tf.reduce_prod(tf.shape(batch_programs)) n_symbols = len(symbol_names) batch_expectations = analytic_op( tf.reshape(batch_programs, [n_batch_programs]), new_symbol_names, tf.reshape(batch_symbol_values, [n_batch_programs, n_symbols]), tf.reshape(batch_pauli_sums, [n_batch_programs, n_ops])) batch_expectations = tf.reshape(batch_expectations, tf.shape(batch_pauli_sums)) batch_jacobian = tf.map_fn( lambda x: tf.einsum('km,kmp->kp', x[0], tf.gather(x[1], x[2])), (batch_weights, batch_expectations, batch_mapper), fn_output_signature=tf.float32) grad_manual = tf.reduce_sum(batch_jacobian, -1) # Get gradients using autodiff. differentiator.refresh() differentiable_op = differentiator.generate_differentiable_op( analytic_op=analytic_op) with tf.GradientTape() as g: g.watch(symbol_values_tensor) exact_outputs = differentiable_op(programs, symbol_names_tensor, symbol_values_tensor, ops_tensor) grad_auto = g.gradient(exact_outputs, symbol_values_tensor) self.assertAllClose(grad_manual, grad_auto)
def test_analytic_functional(self, diff): """Test that the differentiate_analytic function WORKS.""" differentiable_op = diff.generate_differentiable_op( analytic_op=circuit_execution_ops.get_expectation_op()) circuit, names, values, ops, _, true_f, true_g = _simple_op_inputs() with tf.GradientTape() as g: g.watch(values) res = differentiable_op(circuit, names, values, ops) # Just check that it computes without failing. self.assertAllClose(true_f, res, atol=1e-2, rtol=1e-2) self.assertAllClose(true_g, g.gradient(res, values), atol=1e-2, rtol=1e-2)
def test_parameter_shift_analytic(self): """Test if ParameterShift.differentiate_analytical doesn't crash before running.""" programs, names, values, ops, _, true_f, true_g = \ _simple_op_inputs() ps = parameter_shift.ParameterShift() op = ps.generate_differentiable_op( analytic_op=circuit_execution_ops.get_expectation_op()) with tf.GradientTape() as g: g.watch(values) expectations = op(programs, names, values, ops) grads = g.gradient(expectations, values) self.assertAllClose(expectations, true_f, atol=1e-2, rtol=1e-2) self.assertAllClose(grads, true_g, atol=1e-2, rtol=1e-2)
def test_stochastic_differentiator_call_analytic(self, coordinate, generator, cost, uniform): """Test if SGDifferentiator.differentiate_analytical doesn't crash before running.""" programs, names, values, ops, _, true_f, true_g = \ _simple_op_inputs() diff = stochastic_differentiator.SGDifferentiator( coordinate, generator, cost, uniform) op = diff.generate_differentiable_op( analytic_op=circuit_execution_ops.get_expectation_op()) with tf.GradientTape() as g: g.watch(values) expectations = op(programs, names, values, ops) grads = g.gradient(expectations, values) self.assertAllClose(expectations, true_f, atol=1e-2, rtol=1e-2) self.assertAllClose(grads, true_g, atol=1e-2, rtol=1e-2)
def __init__(self, backend=None, differentiator=None, **kwargs): """Instantiate this Layer. Create a layer that will output expectation values gained from simulating a quantum circuit. Args: backend: Optional Backend to use to simulate states. Defaults to the native TensorFlow simulator (None), however users may also specify a preconfigured cirq object to use instead, which must inherit `cirq.sim.simulator.SimulatesExpectationValues`. differentiator: Optional Differentiator to use to calculate analytic derivative values of given operators_to_measure and circuit, which must inherit `tfq.differentiators.Differentiator` and implements `differentiate_analytic` method. Defaults to None, which uses `linear_combination.ForwardDifference()`. If `backend` is also None then default is `tfq.differentiators.Adjoint`. """ super().__init__(**kwargs) # Ingest backend. if not isinstance( backend, cirq.sim.simulator.SimulatesExpectationValues) and \ isinstance(backend, cirq.Sampler): raise TypeError("Backend implements cirq.Sampler but not " "cirq.sim.simulator.SimulatesExpectationValues. " "Please use SampledExpectation instead.") # Ingest differentiator. if differentiator is None: differentiator = linear_combination.ForwardDifference() if backend is None: differentiator = adjoint.Adjoint() if not isinstance(differentiator, diff.Differentiator): raise TypeError("Differentiator must inherit from " "tfq.differentiators.Differentiator") self._expectation_op = differentiator.generate_differentiable_op( analytic_op=circuit_execution_ops.get_expectation_op( backend=backend)) self._w = None
def test_get_expectation_inputs(self): """Test that get expectation only accepts inputs it should.""" circuit_execution_ops.get_expectation_op() circuit_execution_ops.get_expectation_op(backend=cirq.Simulator()) circuit_execution_ops.get_expectation_op( backend=cirq.DensityMatrixSimulator()) circuit_execution_ops.get_expectation_op() with self.assertRaisesRegex(NotImplementedError, expected_regex='Sample-based'): mock_engine = mock.Mock() circuit_execution_ops.get_expectation_op( cirq.google.QuantumEngineSampler(engine=mock_engine, processor_id='test', gate_set=cirq.google.XMON)) with self.assertRaisesRegex( TypeError, expected_regex="a Cirq.SimulatesFinalState"): circuit_execution_ops.get_expectation_op(backend="junk")
from absl.testing import parameterized from scipy import stats import cirq from tensorflow_quantum.core.ops import batch_util, circuit_execution_ops from tensorflow_quantum.python import util # Number of random circuits to use in a test batch. BATCH_SIZE = 15 # These get used everywhere WF_SIM = cirq.sim.sparse_simulator.Simulator() DM_SIM = cirq.sim.density_matrix_simulator.DensityMatrixSimulator() EXPECTATION_OPS = [ circuit_execution_ops.get_expectation_op(backend=None), circuit_execution_ops.get_expectation_op(backend=WF_SIM), circuit_execution_ops.get_expectation_op(backend=DM_SIM) ] SAMPLING_OPS = [ circuit_execution_ops.get_sampling_op(backend=None), circuit_execution_ops.get_sampling_op(backend=WF_SIM), circuit_execution_ops.get_sampling_op(backend=DM_SIM) ] STATE_OPS = [ circuit_execution_ops.get_state_op(backend=None), circuit_execution_ops.get_state_op(backend=WF_SIM), circuit_execution_ops.get_state_op(backend=DM_SIM) ]
from absl.testing import parameterized from scipy import stats import cirq from tensorflow_quantum.core.ops import batch_util, circuit_execution_ops from tensorflow_quantum.python import util # Number of random circuits to use in a test batch. BATCH_SIZE = 15 # These get used everywhere WF_SIM = cirq.sim.sparse_simulator.Simulator() DM_SIM = cirq.sim.density_matrix_simulator.DensityMatrixSimulator() EXPECTATION_OPS = [ circuit_execution_ops.get_expectation_op(backend=None, quantum_concurrent=True), circuit_execution_ops.get_expectation_op(backend=WF_SIM, quantum_concurrent=True), circuit_execution_ops.get_expectation_op(backend=DM_SIM, quantum_concurrent=True), # For timing interests C++ backend is tested in quantum_concurrent mode. circuit_execution_ops.get_expectation_op(backend=None, quantum_concurrent=False) ] SAMPLING_OPS = [ circuit_execution_ops.get_sampling_op(backend=None, quantum_concurrent=True), circuit_execution_ops.get_sampling_op(backend=WF_SIM, quantum_concurrent=True), circuit_execution_ops.get_sampling_op(backend=DM_SIM,
class AnalyticGradientCorrectnessTest(tf.test.TestCase, parameterized.TestCase): """Test correctness of the differentiators to reference cirq algorithm.""" @parameterized.parameters( list( util.kwargs_cartesian_product(**{ 'differentiator': ANALYTIC_DIFFS, 'op': ANALYTIC_OPS })) + [{ 'differentiator': adjoint.Adjoint(), 'op': circuit_execution_ops.get_expectation_op() }]) def test_backprop(self, differentiator, op): """Test that gradients are correctly backpropagated through a quantum circuit via comparison to analytical results. """ differentiator.refresh() op = differentiator.generate_differentiable_op(analytic_op=op) def exact_grad(theta): new_theta = 2 * np.pi * theta return -2 * np.pi * np.sin(new_theta) * np.exp(np.cos(new_theta)) bit = cirq.GridQubit(0, 0) circuits = util.convert_to_tensor( [cirq.Circuit(cirq.X(bit)**sympy.Symbol('rx')) for _ in range(2)]) pstring = util.convert_to_tensor([[ cirq.PauliSum.from_pauli_strings([cirq.PauliString({bit: cirq.Z})]) ] for _ in circuits]) base_rot_angles = tf.constant([[0.25], [0.125]]) with tf.GradientTape() as g: g.watch(base_rot_angles) input_angles = 2 * base_rot_angles exp_res = tf.exp( op(circuits, tf.convert_to_tensor(['rx']), input_angles, pstring)) grad = g.gradient(exp_res, base_rot_angles) exact = [[exact_grad(0.25)], [exact_grad(0.125)]] # will this be too tight? time will tell. self.assertAllClose(exact, grad.numpy(), rtol=0.01, atol=0.01) @parameterized.parameters( list( util.kwargs_cartesian_product( **{ 'differentiator': ANALYTIC_DIFFS, 'op': ANALYTIC_OPS, 'n_qubits': [5], 'n_programs': [3], 'n_ops': [3], 'symbol_names': [['a', 'b']] })) + [{ 'differentiator': adjoint.Adjoint(), 'op': circuit_execution_ops.get_expectation_op(), 'n_qubits': 10, 'n_programs': 5, 'n_ops': 3, 'symbol_names': ['a', 'b'] }]) def test_gradients_vs_cirq_finite_difference(self, differentiator, op, n_qubits, n_programs, n_ops, symbol_names): """Compare TFQ differentiators to fine-grained noiseless cirq finite differencing. """ differentiator.refresh() op = differentiator.generate_differentiable_op(analytic_op=op) qubits = cirq.GridQubit.rect(1, n_qubits) circuit_batch, resolver_batch = \ util.random_symbol_circuit_resolver_batch( cirq.GridQubit.rect(1, n_qubits), symbol_names, n_programs) psums = [ util.random_pauli_sums(qubits, 1, n_ops) for _ in circuit_batch ] symbol_values_array = np.array( [[resolver[symbol] for symbol in symbol_names] for resolver in resolver_batch], dtype=np.float32) # calculate tfq gradient symbol_values_tensor = tf.convert_to_tensor(symbol_values_array) programs = util.convert_to_tensor(circuit_batch) ops = util.convert_to_tensor(psums) with tf.GradientTape() as g: g.watch(symbol_values_tensor) expectations = op(programs, tf.convert_to_tensor(symbol_names), symbol_values_tensor, ops) tfq_grads = g.gradient(expectations, symbol_values_tensor) # calculate gradients in cirq using a very simple forward differencing # scheme cirq_grads = _cirq_simple_finite_difference(circuit_batch, resolver_batch, symbol_names, psums) # will this be too tight? time will tell. self.assertAllClose(cirq_grads, tfq_grads, rtol=2e-2, atol=2e-2) @parameterized.parameters( list( util.kwargs_cartesian_product(**{ 'differentiator': ANALYTIC_DIFFS, 'op': ANALYTIC_OPS, })) + [{ 'differentiator': adjoint.Adjoint(), 'op': circuit_execution_ops.get_expectation_op(), }]) def test_analytic_value_with_simple_circuit(self, differentiator, op): """Test the value of differentiator with simple circuit.""" # Get an expectation op, with this differentiator attached. differentiator.refresh() op = differentiator.generate_differentiable_op(analytic_op=op) qubit = cirq.GridQubit(0, 0) circuit = util.convert_to_tensor( [cirq.Circuit(cirq.X(qubit)**sympy.Symbol('alpha'))]) psums = util.convert_to_tensor([[cirq.Z(qubit)]]) symbol_values_array = np.array([[0.123]], dtype=np.float32) # Calculate tfq gradient. symbol_values_tensor = tf.convert_to_tensor(symbol_values_array) with tf.GradientTape() as g: g.watch(symbol_values_tensor) expectations = op(circuit, tf.convert_to_tensor(['alpha']), symbol_values_tensor, psums) grads = g.gradient(expectations, symbol_values_tensor) ground_truth_grads = np.array([[-1.1839752]]) self.assertAllClose(ground_truth_grads, grads, rtol=1e-2, atol=1e-2) @parameterized.parameters( list( util.kwargs_cartesian_product(**{ 'differentiator': ANALYTIC_DIFFS, 'op': ANALYTIC_OPS, })) + [{ 'differentiator': adjoint.Adjoint(), 'op': circuit_execution_ops.get_expectation_op(), }]) def test_empty_circuit_grad(self, differentiator, op): """Test that providing no circuits will fail gracefully.""" differentiator.refresh() op = differentiator.generate_differentiable_op(analytic_op=op) circuit = tf.convert_to_tensor([], dtype=tf.string) psums = tf.raw_ops.Empty(shape=(0, 0), dtype=tf.string) # Calculate tfq gradient. symbol_values_tensor = tf.raw_ops.Empty(shape=(0, 0), dtype=tf.float32) symbol_names_tensor = tf.convert_to_tensor([], dtype=tf.string) with tf.GradientTape() as g: g.watch(symbol_values_tensor) expectations = op(circuit, symbol_names_tensor, symbol_values_tensor, psums) grads = g.gradient(expectations, symbol_values_tensor) self.assertShapeEqual(grads.numpy(), tf.raw_ops.Empty(shape=(0, 0), dtype=tf.float32))
linear_combination.ForwardDifference(error_order=2, grid_spacing=0.0001), linear_combination.CentralDifference(grid_spacing=0.0001), linear_combination.CentralDifference(error_order=4, grid_spacing=0.0001), parameter_shift.ParameterShift(), ] SAMPLED_DIFFS = [ linear_combination.ForwardDifference(grid_spacing=0.05), linear_combination.CentralDifference(grid_spacing=0.05), parameter_shift.ParameterShift(), ] SAMPLED_DIFFS_TOLS = [0.5, 0.5, 0.2] ANALYTIC_OPS = [ circuit_execution_ops.get_expectation_op(cirq.sim.Simulator()), # WF circuit_execution_ops.get_expectation_op() # C++ ] SAMPLED_OPS = [ circuit_execution_ops.get_sampled_expectation_op( cirq.sim.Simulator()), # WF circuit_execution_ops.get_sampled_expectation_op() # C++ ] def _cirq_simple_finite_difference(circuit_batch, resolvers, symbol_names, op_batch, grid_spacing=0.0001):
class GradientCorrectnessTest(tf.test.TestCase, parameterized.TestCase): """Test correctness of the differentiators to reference cirq algorithm.""" @parameterized.parameters( list( util.kwargs_cartesian_product( **{ 'differentiator': DIFFS + STOCHASTIC_DIFFS, 'op': OPS, 'stochastic_cost': [False, True] })) + [{ 'differentiator': adjoint.Adjoint(), 'op': circuit_execution_ops.get_expectation_op(), 'stochastic_cost': False }]) def test_backprop(self, differentiator, op, stochastic_cost): """Test that gradients are correctly backpropagated through a quantum circuit via comparison to analytical results. """ # hack to add stoachastic cost. TODO (jaeyoo): remove this hack. differentiator.stochastic_cost = stochastic_cost differentiator.refresh() op = differentiator.generate_differentiable_op(analytic_op=op) def exact_grad(theta): new_theta = 2 * np.pi * theta return -2 * np.pi * np.sin(new_theta) * np.exp(np.cos(new_theta)) bit = cirq.GridQubit(0, 0) circuits = util.convert_to_tensor( [cirq.Circuit(cirq.X(bit)**sympy.Symbol('rx')) for _ in range(2)]) pstring = util.convert_to_tensor([[ cirq.PauliSum.from_pauli_strings([cirq.PauliString({bit: cirq.Z})]) ] for _ in circuits]) base_rot_angles = tf.constant([[0.25], [0.125]]) with tf.GradientTape() as g: g.watch(base_rot_angles) input_angles = 2 * base_rot_angles exp_res = tf.exp( op(circuits, tf.convert_to_tensor(['rx']), input_angles, pstring)) grad = g.gradient(exp_res, base_rot_angles) exact = [[exact_grad(0.25)], [exact_grad(0.125)]] # will this be too tight? time will tell. self.assertAllClose(exact, grad.numpy(), rtol=0.01, atol=0.01) @parameterized.parameters( list( util.kwargs_cartesian_product( **{ 'differentiator': DIFFS, 'op': OPS, 'n_qubits': [5], 'n_programs': [3], 'n_ops': [3], 'symbol_names': [['a', 'b']] })) + [{ 'differentiator': adjoint.Adjoint(), 'op': circuit_execution_ops.get_expectation_op(), 'n_qubits': 5, 'n_programs': 5, 'n_ops': 3, 'symbol_names': ['a', 'b'] }]) def test_gradients_vs_cirq_finite_difference(self, differentiator, op, n_qubits, n_programs, n_ops, symbol_names): """Compare TFQ differentiators to fine-grained noiseless cirq finite differencing. DISCLAIMER : the consistency of STOCHASTIC_DIFFS is hard to be checked. Its expectation value should be checked, but it takes long time because SGDifferentiator is not optimized. Until optimized, the consistency will be performed in benchmarks/scripts/differentiators:convergence_test TODO(jaeyoo) : move convergence_test here once SGDifferentiator is optimized. """ differentiator.refresh() op = differentiator.generate_differentiable_op(analytic_op=op) qubits = cirq.GridQubit.rect(1, n_qubits) circuit_batch, resolver_batch = \ util.random_symbol_circuit_resolver_batch( cirq.GridQubit.rect(1, n_qubits), symbol_names, n_programs) psums = [ util.random_pauli_sums(qubits, 1, n_ops) for _ in circuit_batch ] symbol_values_array = np.array( [[resolver[symbol] for symbol in symbol_names] for resolver in resolver_batch], dtype=np.float32) # calculate tfq gradient symbol_values_tensor = tf.convert_to_tensor(symbol_values_array) programs = util.convert_to_tensor(circuit_batch) ops = util.convert_to_tensor(psums) with tf.GradientTape() as g: g.watch(symbol_values_tensor) expectations = op(programs, tf.convert_to_tensor(symbol_names), symbol_values_tensor, ops) tfq_grads = g.gradient(expectations, symbol_values_tensor) # calculate gradients in cirq using a very simple forward differencing # scheme cirq_grads = _cirq_simple_finite_difference(circuit_batch, resolver_batch, symbol_names, psums) # will this be too tight? time will tell. self.assertAllClose(cirq_grads, tfq_grads, rtol=1e-2, atol=1e-2) @parameterized.parameters( list( util.kwargs_cartesian_product( **{ 'differentiator': DIFFS + STOCHASTIC_DIFFS, 'op': OPS, 'stochastic_cost': [False, True] })) + [{ 'differentiator': adjoint.Adjoint(), 'op': circuit_execution_ops.get_expectation_op(), 'stochastic_cost': False }]) def test_analytic_value_with_simple_circuit(self, differentiator, op, stochastic_cost): """Test the value of differentiator with simple circuit. Since there are only one symbol, one gate and one op, there is only one samling result, STOCHATIC_DIFFS shows the same result with that of deterministic differentiators.""" # Get an expectation op, with this differentiator attached. differentiator.refresh() differentiator.stochastic_cost = stochastic_cost op = differentiator.generate_differentiable_op(analytic_op=op) qubit = cirq.GridQubit(0, 0) circuit = util.convert_to_tensor( [cirq.Circuit(cirq.X(qubit)**sympy.Symbol('alpha'))]) psums = util.convert_to_tensor([[cirq.Z(qubit)]]) symbol_values_array = np.array([[0.123]], dtype=np.float32) # Calculate tfq gradient. symbol_values_tensor = tf.convert_to_tensor(symbol_values_array) with tf.GradientTape() as g: g.watch(symbol_values_tensor) expectations = op(circuit, tf.convert_to_tensor(['alpha']), symbol_values_tensor, psums) grads = g.gradient(expectations, symbol_values_tensor) ground_truth_grads = np.array([[-1.1839752]]) self.assertAllClose(ground_truth_grads, grads, rtol=1e-2, atol=1e-2)
stochastic_differentiator.SGDifferentiator(stochastic_coordinate=False, stochastic_generator=False, stochastic_cost=False), stochastic_differentiator.SGDifferentiator(stochastic_coordinate=True, stochastic_generator=False, stochastic_cost=False), stochastic_differentiator.SGDifferentiator(stochastic_coordinate=False, stochastic_generator=True, stochastic_cost=False), stochastic_differentiator.SGDifferentiator(stochastic_coordinate=True, stochastic_generator=True, stochastic_cost=False), ] OPS = [ circuit_execution_ops.get_expectation_op(cirq.sim.Simulator()), # WF circuit_execution_ops.get_expectation_op( cirq.DensityMatrixSimulator()), # DM circuit_execution_ops.get_expectation_op() # C++ ] def _cirq_simple_finite_difference(circuit_batch, resolvers, symbol_names, op_batch, grid_spacing=0.0001): """A simple finite difference code that calculates the gradient of a batch of circuits using cirq.""" simulator = cirq.sim.Simulator()