Exemple #1
0
 def test_no_gradient_circuits(self):
     """Confirm the adjoint differentiator has no gradient circuits."""
     dif = adjoint.Adjoint()
     with self.assertRaisesRegex(NotImplementedError,
                                 expected_regex="no accessible "
                                 "gradient circuits"):
         _ = dif.get_gradient_circuits(None, None, None)
Exemple #2
0
    def test_sample_errors(self):
        """Ensure that the adjoint method won't attach to sample ops."""

        dif = adjoint.Adjoint()
        op = circuit_execution_ops.get_sampled_expectation_op()
        with self.assertRaisesRegex(ValueError,
                                    expected_regex='not supported'):
            dif.generate_differentiable_op(sampled_op=op)
Exemple #3
0
    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
Exemple #4
0
    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
Exemple #5
0
 def test_instantiation(self):
     """Test that adjoint can be created."""
     adjoint.Adjoint()
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))
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)