def test_sampled_expectation_symbol_input(self):
     """Test that SampledExpectation only accepts valid permutations of
     symbols."""
     sampled_expectation.SampledExpectation()
     sampled_expectation.SampledExpectation(backend=cirq.Simulator())
     sampled_expectation.SampledExpectation(
         differentiator=linear_combination.ForwardDifference())
    def test_sampled_expectation_type_inputs_error(self):
        """Test that SampledExpectation errors within Keras call."""

        bit = cirq.GridQubit(0, 0)
        symbol = sympy.Symbol('alpha')
        test_pstring = cirq.Z(bit)
        test_psum = cirq.PauliSum.from_pauli_strings([test_pstring])
        symb_circuit = cirq.Circuit(cirq.H(bit)**symbol)
        reg_circuit = cirq.Circuit(cirq.H(bit))

        with self.assertRaisesRegex(RuntimeError,
                                    expected_regex="repetitions not provided"):
            sampled_expectation.SampledExpectation()(symb_circuit,
                                                     symbol_names=[symbol],
                                                     symbol_values=[[0.5]],
                                                     operators=test_psum)

        with self.assertRaisesRegex(Exception,
                                    expected_regex="Unknown initializer"):
            sampled_expectation.SampledExpectation()(reg_circuit,
                                                     operators=test_psum,
                                                     initializer='junk',
                                                     repetitions=1)

        with self.assertRaisesRegex(Exception,
                                    expected_regex="cannot be parsed"):
            sampled_expectation.SampledExpectation()(reg_circuit,
                                                     operators=test_psum,
                                                     repetitions='junk')
    def test_dnn_qnn_dnn(self):
        """Train a fully hybrid network using an SampledExpectation layer.

        Train the network to output +-5 given an input of 1 or 0. This tests
        that everything works when SampledExpectation layer is a middle layers.
        """
        bit = cirq.GridQubit(0, 0)
        symbols = sympy.symbols('x, y, z')
        circuits = util.convert_to_tensor(
            [_gen_single_bit_rotation_problem(bit, symbols)] * 2)
        data_in = np.array([[1], [0]], dtype=np.float32)
        data_out = np.array([[5], [-5]], dtype=np.float32)

        classical_input = tf.keras.Input(shape=(1, ))
        circuit_input = tf.keras.Input(shape=(), dtype=tf.dtypes.string)
        d1 = tf.keras.layers.Dense(10)(classical_input)
        d2 = tf.keras.layers.Dense(3)(d1)
        quantum = sampled_expectation.SampledExpectation()(
            circuit_input,
            symbol_names=symbols,
            symbol_values=d2,
            operators=cirq.Z(bit),
            repetitions=5000)
        d3 = tf.keras.layers.Dense(1)(quantum)

        model = tf.keras.Model(inputs=[circuit_input, classical_input],
                               outputs=d3)

        model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.05),
                      loss=tf.keras.losses.mean_squared_error)
        history = model.fit(x=[circuits, data_in],
                            y=data_out,
                            batch_size=2,
                            epochs=75)
        self.assertAllClose(history.history['loss'][-1], 0, atol=4)
    def test_simple_param_value_input(self):
        """Train a densely connected hybrid model.

        This model will put a qubit in the zero or one state from a random state
        given the input zero or one.
        """
        bit = cirq.GridQubit(0, 0)
        symbols = sympy.symbols('x y z')
        circuit = _gen_single_bit_rotation_problem(bit, symbols)

        inputs = tf.keras.Input(shape=(1, ), dtype=tf.dtypes.float64)
        datum = tf.keras.Input(shape=(), dtype=tf.dtypes.string)
        l1 = tf.keras.layers.Dense(10)(inputs)
        l2 = tf.keras.layers.Dense(3)(l1)
        outputs = sampled_expectation.SampledExpectation()(
            datum,
            symbol_names=symbols,
            operators=cirq.Z(bit),
            symbol_values=l2,
            repetitions=5000)
        model = tf.keras.Model(inputs=[datum, inputs], outputs=outputs)

        data_in = np.array([[1], [0]], dtype=np.float32)
        data_out = np.array([[1], [-1]], dtype=np.float32)

        model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.05),
                      loss=tf.keras.losses.mean_squared_error)

        circuits = util.convert_to_tensor([circuit, circuit])

        history = model.fit(x=[circuits, data_in], y=data_out, epochs=30)
        self.assertAllClose(history.history['loss'][-1], 0, atol=0.3)
    def test_simple_op_input(self):
        """Test a simple operator input

        Learn qubit in the z+ state using two different measurement operators.
        """
        bit = cirq.GridQubit(0, 0)
        symbols = sympy.symbols('x y z')
        ops = util.convert_to_tensor([[cirq.Z(bit)], [cirq.Z(bit)]])
        n = tf.convert_to_tensor([[5000], [5000]], dtype=tf.int32)

        circuit = util.convert_to_tensor(
            [_gen_single_bit_rotation_problem(bit, symbols)] * 2)

        data_out = tf.convert_to_tensor(np.array([[1], [1]]))
        op_inp = tf.keras.Input(shape=(1, ), dtype=tf.dtypes.string)
        n_inp = tf.keras.Input(shape=(1, ), dtype=tf.dtypes.int32)
        circuit_inp = tf.keras.Input(shape=(), dtype=tf.dtypes.string)
        circuit_output = sampled_expectation.SampledExpectation()(
            circuit_inp,
            symbol_names=symbols,
            operators=op_inp,
            repetitions=n_inp)
        model = tf.keras.Model(inputs=[circuit_inp, op_inp, n_inp],
                               outputs=[circuit_output])

        model.compile(
            optimizer=tf.keras.optimizers.Adam(learning_rate=0.05),
            loss=tf.keras.losses.mean_squared_error,
        )
        history = model.fit(x=[circuit, ops, n],
                            y=data_out,
                            batch_size=1,
                            epochs=3)

        self.assertAllClose(history.history['loss'][-1], 0, atol=1e-2)
Beispiel #6
0
    def test_sampled_expectation_op_error(self, backend):
        """Test that expectation errors within underlying ops correctly."""
        # Note the expected_regex is left blank here since there is a
        # discrepancy between the error strings provided between backends.
        bit = cirq.GridQubit(0, 0)
        symbol = sympy.Symbol('alpha')
        test_pstring = cirq.Z(bit)
        test_psum = cirq.PauliSum.from_pauli_strings([test_pstring])
        symb_circuit = cirq.Circuit(cirq.H(bit)**symbol)
        reg_circuit = cirq.Circuit(cirq.H(bit))

        with self.assertRaisesRegex(Exception, expected_regex="pauli"):
            # Operators has wrong rank. Parse error.
            sampled_expectation.SampledExpectation(backend=backend)(
                [reg_circuit],
                operators=util.convert_to_tensor([test_psum]),
                repetitions=1)

        with self.assertRaisesRegex(Exception, expected_regex="symbol_values"):
            # symbol_values has wrong rank.
            sampled_expectation.SampledExpectation(backend=backend)(
                [symb_circuit],
                symbol_names=[symbol],
                symbol_values=[0.5],
                operators=test_psum,
                repetitions=1)

        with self.assertRaisesRegex(Exception, expected_regex="pauli"):
            # Wrong batch size for pauli operators.
            sampled_expectation.SampledExpectation(backend=backend)(
                symb_circuit,
                symbol_names=[symbol],
                operators=[[test_psum], [test_psum]],
                repetitions=1)

        with self.assertRaisesRegex(Exception, expected_regex="pauli"):
            # Wrong batch size for pauli operators.
            sampled_expectation.SampledExpectation(backend=backend)(
                reg_circuit,
                operators=[[test_psum], [test_psum]],
                repetitions=1)

        with self.assertRaisesRegex(Exception, expected_regex="0"):
            # Wrong repetitions.
            sampled_expectation.SampledExpectation(backend=backend)(
                reg_circuit, operators=test_psum, repetitions=-1)

        with self.assertRaisesRegex(Exception, expected_regex=""):
            # Wrong second dimension size for repetitions & pauli operators.
            sampled_expectation.SampledExpectation(backend=backend)(
                reg_circuit, operators=test_psum, repetitions=[5, 4, 3])

        with self.assertRaisesRegex(Exception, expected_regex=""):
            # Wrong batch_size for symbol values.
            sampled_expectation.SampledExpectation(backend=backend)(
                [reg_circuit],
                symbol_names=[symbol],
                symbol_values=np.zeros((3, 1)),
                operators=test_psum,
                repetitions=5)
    def test_sampled_expectation_instantiate_error(self):
        """Test that SampledExpectation errors with bad inputs."""
        class MySim(cirq.SimulatesFinalState):
            """Class to test sampler detection in Expectation."""
            def simulate_sweep(self):
                """Do nothing."""
                return

        with self.assertRaisesRegex(TypeError, expected_regex="Expectation"):
            sampled_expectation.SampledExpectation(backend=MySim())

        with self.assertRaisesRegex(TypeError,
                                    expected_regex="Sampler or None"):
            sampled_expectation.SampledExpectation(backend='junk')

        with self.assertRaisesRegex(
                TypeError,
                expected_regex="tfq.differentiators.Differentiator"):
            sampled_expectation.SampledExpectation(differentiator='junk')
 def test_sampled_expectation_simple_tf_train(self):
     """Train a layer using standard tf (not keras)."""
     bit = cirq.GridQubit(0, 0)
     circuit = cirq.Circuit(cirq.rx(sympy.Symbol('theta'))(bit))
     layer = sampled_expectation.SampledExpectation()
     optimizer = tf.optimizers.Adam(learning_rate=0.05)
     for _ in range(10):
         with tf.GradientTape() as tape:
             circuit_out = layer(circuit,
                                 symbol_names=['theta'],
                                 operators=cirq.Z(bit),
                                 repetitions=100)
             mse = tf.square(tf.reduce_sum(tf.subtract(circuit_out, -1)))
         grads = tape.gradient(mse, layer.trainable_weights)
         optimizer.apply_gradients(zip(grads, layer.trainable_weights))
     self.assertAllClose(mse.numpy(), 0, atol=1e-3)
Beispiel #9
0
    def test_simple_op_and_param_input(self, backend):
        """Test a simple operator and parameter input.

        Train a NN to put a qubit in the z+ or x+ states based on a classical
        binary input.
        """
        bit = cirq.GridQubit(0, 0)
        symbols = sympy.symbols('x y z')
        ops = util.convert_to_tensor([[cirq.Z(bit)], [cirq.Z(bit)]])
        n = tf.convert_to_tensor([[5000], [5000]], dtype=tf.int32)
        circuits = util.convert_to_tensor([
            _gen_single_bit_rotation_problem(
                bit, symbols, True if backend == 'noisy' else False)
        ] * 2)
        data_in = np.array([[1], [0]])
        data_out = np.array([[1], [1]])

        data_inp = tf.keras.layers.Input(shape=(1), dtype=tf.dtypes.float32)
        op_inp = tf.keras.layers.Input(shape=(1, ), dtype=tf.dtypes.string)
        n_inp = tf.keras.layers.Input(shape=(1, ), dtype=tf.dtypes.int32)
        circuit_inp = tf.keras.Input(shape=(), dtype=tf.dtypes.string)
        dense_1 = tf.keras.layers.Dense(10)(data_inp)
        dense_2 = tf.keras.layers.Dense(3)(dense_1)
        circuit_output = sampled_expectation.SampledExpectation(
            backend=backend)(circuit_inp,
                             symbol_names=symbols,
                             symbol_values=dense_2,
                             operators=op_inp,
                             repetitions=n_inp)

        functional_model = tf.keras.Model(
            inputs=[circuit_inp, data_inp, op_inp, n_inp],
            outputs=[circuit_output])

        functional_model.compile(
            optimizer=tf.keras.optimizers.Adam(learning_rate=0.05),
            loss=tf.keras.losses.mean_squared_error)
        history = functional_model.fit(x=[circuits, data_in, ops, n],
                                       y=data_out,
                                       batch_size=2,
                                       epochs=20)
        self.assertAllClose(history.history['loss'][-1], 0, atol=3)
Beispiel #10
0
    def __init__(
        self,
        model_circuit,
        operators,
        *,
        repetitions=None,
        backend='noiseless',
        differentiator=None,
        initializer=tf.keras.initializers.RandomUniform(0, 2 * np.pi),
        regularizer=None,
        constraint=None,
        **kwargs,
    ):
        """Instantiate this layer.

        Create a layer that will output expectation values of the given
        operators when fed quantum data to it's input layer. This layer will
        accept one input tensor representing a quantum data source (these
        circuits must not contain any symbols) and append the model_circuit to
        them, execute them and then finally output the expectation values.


        model_circuit: `cirq.Circuit` containing `sympy.Symbols` that will be
            used as the model which will be fed quantum data inputs.
        operators: `cirq.PauliSum` or Python `list` of `cirq.PauliSum` objects
            used as observables at the end of the model circuit.
        repetitions: Optional Python `int` indicating how many samples to use
            when estimating expectation values.  If `None` analytic expectation
            calculation is used.
        backend: Optional Backend to use to simulate states. Defaults to
            the noiseless TensorFlow simulator, however users may also
            specify a preconfigured cirq simulation object to use instead.
            If a cirq object is given it must inherit either
            `cirq.sim.simulator.SimulatesExpectationValues` if analytic
            expectations are desired or `cirq.Sampler` if sampled expectations
            are desired.
        differentiator: Optional `tfq.differentiator` object to specify how
            gradients of `model_circuit` should be calculated.
        initializer: Optional `tf.keras.initializer` object to specify how the
            symbols in `model_circuit` should be initialized when creating
            the managed variables.
        regularizer: Optional `tf.keras.regularizer` object applied to the
            managed variables parameterizing `model_circuit`.
        constraint: Optional `tf.keras.constraint` object applied to the
            managed variables parameterizing `model_circuit`.
        """
        super().__init__(**kwargs)

        # Ingest model_circuit.
        if not isinstance(model_circuit, cirq.Circuit):
            raise TypeError("model_circuit must be a cirq.Circuit object."
                            " Given: {}".format(model_circuit))

        self._symbols_list = list(
            sorted(util.get_circuit_symbols(model_circuit)))
        self._symbols = tf.constant([str(x) for x in self._symbols_list])

        self._model_circuit = util.convert_to_tensor([model_circuit])
        if len(self._symbols_list) == 0:
            raise ValueError("model_circuit has no sympy.Symbols. Please "
                             "provide a circuit that contains symbols so "
                             "that their values can be trained.")

        # Ingest operators.
        if isinstance(operators, (cirq.PauliString, cirq.PauliSum)):
            operators = [operators]
        if not isinstance(operators, (list, np.ndarray, tuple)):
            raise TypeError("operators must be a cirq.PauliSum or "
                            "cirq.PauliString, or a list, tuple, "
                            "or np.array containing them. "
                            "Got {}.".format(type(operators)))
        if not all([
                isinstance(op, (cirq.PauliString, cirq.PauliSum))
                for op in operators
        ]):
            raise TypeError("Each element in operators to measure "
                            "must be a cirq.PauliString"
                            " or cirq.PauliSum")
        self._operators = util.convert_to_tensor([operators])

        # Ingest and promote repetitions.
        self._analytic = False
        if repetitions is None:
            self._analytic = True
        if not self._analytic and not isinstance(repetitions,
                                                 numbers.Integral):
            raise TypeError("repetitions must be a positive integer value."
                            " Given: ".format(repetitions))
        if not self._analytic and repetitions <= 0:
            raise ValueError("Repetitions must be greater than zero.")
        if not self._analytic:
            self._repetitions = tf.constant(
                [[repetitions for _ in range(len(operators))]],
                dtype=tf.dtypes.int32)

        # Set backend and differentiator.
        if backend == 'noisy':
            raise ValueError("noisy backend value is not supported in "
                             "tfq.layers.PQC. Please use tfq.layers.NoisyPQC "
                             "instead.")

        not_default = backend is not 'noiseless'
        not_default &= backend is not None  # legacy backend=None support.
        if not isinstance(
                backend,
                cirq.Sampler) and repetitions is not None and not_default:
            raise TypeError("provided backend does not inherit cirq.Sampler "
                            "and repetitions!=None. Please provide a backend "
                            "that inherits cirq.Sampler or set "
                            "repetitions=None.")
        if not isinstance(backend,
                          cirq.sim.simulator.SimulatesExpectationValues
                          ) and repetitions is None and not_default:
            raise TypeError(
                "provided backend does not inherit "
                "cirq.sim.simulator.SimulatesExpectationValues and "
                "repetitions=None. Please provide a backend that "
                "inherits "
                "cirq.sim.simulator.SimulatesExpectationValues.")
        if self._analytic:
            self._executor = expectation.Expectation(
                backend=backend, differentiator=differentiator)
        else:
            self._executor = sampled_expectation.SampledExpectation(
                backend=backend, differentiator=differentiator)

        self._append_layer = elementary.AddCircuit()

        # Set additional parameter controls.
        self.initializer = tf.keras.initializers.get(initializer)
        self.regularizer = tf.keras.regularizers.get(regularizer)
        self.constraint = tf.keras.constraints.get(constraint)

        # Weight creation is not placed in a Build function because the number
        # of weights is independent of the input shape.
        self.parameters = self.add_weight('parameters',
                                          shape=self._symbols.shape,
                                          initializer=self.initializer,
                                          regularizer=self.regularizer,
                                          constraint=self.constraint,
                                          dtype=tf.float32,
                                          trainable=True)
    def test_static_cases(self):
        """Run inputs through in complex cases."""

        bit = cirq.GridQubit(0, 0)
        symbol = sympy.Symbol('alpha')
        test_pstring = cirq.Z(bit)
        test_psum = cirq.PauliSum.from_pauli_strings([test_pstring])
        symb_circuit = cirq.Circuit(cirq.H(bit)**symbol)
        reg_circuit = cirq.Circuit(cirq.H(bit))

        # Passing a 2d operators input requires a 1d circuit input.
        sampled_expectation.SampledExpectation()(
            [reg_circuit, reg_circuit],
            operators=[[test_psum, test_psum], [test_psum, test_psum]],
            repetitions=1)

        # Passing 2d operators along with other inputs.
        sampled_expectation.SampledExpectation()(
            [symb_circuit, symb_circuit],
            symbol_names=[symbol],
            operators=[[test_psum, test_psum], [test_psum, test_psum]],
            repetitions=1)
        sampled_expectation.SampledExpectation()(
            [symb_circuit, symb_circuit],
            symbol_names=[symbol],
            symbol_values=[[0.5], [0.8]],
            operators=[[test_psum, test_psum], [test_psum, test_psum]],
            repetitions=1)

        # Ensure tiling up of circuits works as expected.
        sampled_expectation.SampledExpectation()(reg_circuit,
                                                 operators=test_psum,
                                                 repetitions=1)
        sampled_expectation.SampledExpectation()(
            reg_circuit, operators=[test_psum, test_psum], repetitions=1)

        # Ensure tiling up of symbol_values works as expected.
        sampled_expectation.SampledExpectation()(symb_circuit,
                                                 symbol_names=[symbol],
                                                 symbol_values=[[0.5], [0.8]],
                                                 operators=test_psum,
                                                 repetitions=1)
        sampled_expectation.SampledExpectation()(symb_circuit,
                                                 symbol_names=[symbol],
                                                 symbol_values=[[0.5]],
                                                 operators=test_psum,
                                                 repetitions=1)

        # Test multiple operators with integer valued repetition.
        sampled_expectation.SampledExpectation()(
            symb_circuit,
            symbol_names=[symbol],
            symbol_values=[[0.5]],
            operators=[-1.0 * cirq.Z(bit),
                       cirq.X(bit) + 2.0 * cirq.Z(bit)],
            repetitions=1)
        sampled_expectation.SampledExpectation()(
            symb_circuit,
            symbol_names=[symbol],
            symbol_values=[[0.5]],
            operators=[-1.0 * cirq.Z(bit),
                       cirq.X(bit) + 2.0 * cirq.Z(bit)],
            repetitions=[5, 1])
    def __init__(self,
                 model_circuit,
                 operators,
                 *,
                 repetitions=None,
                 backend=None,
                 differentiator=None,
                 **kwargs):
        """Instantiate this layer.

        Create a layer that will output expectation values of the given
        operators when fed quantum data to it's input layer. This layer will
        take two input tensors, one representing a quantum data source (these
        circuits must not contain any symbols) and the other representing
        control parameters for the model circuit that gets appended to the
        datapoints.

        model_circuit: `cirq.Circuit` containing `sympy.Symbols` that will be
            used as the model which will be fed quantum data inputs.
        operators: `cirq.PauliSum` or Python `list` of `cirq.PauliSum` objects
            used as observables at the end of the model circuit.
        repetitions: Optional Python `int` indicating how many samples to use
            when estimating expectation values. If `None` analytic expectation
            calculation is used.
        backend: Optional Backend to use to simulate states. Defaults to
            the native TensorFlow simulator (None), however users may also
            specify a preconfigured cirq simulation object to use instead.
            If a cirq object is given it must inherit `cirq.SimulatesFinalState`
            if `sampled_based` is True or it must inherit `cirq.Sampler` if
            `sample_based` is False.
        differentiator: Optional `tfq.differentiator` object to specify how
            gradients of `model_circuit` should be calculated.
        """
        super().__init__(**kwargs)
        # Ingest model_circuit.
        if not isinstance(model_circuit, cirq.Circuit):
            raise TypeError("model_circuit must be a cirq.Circuit object."
                            " Given: ".format(model_circuit))

        self._symbols_list = list(
            sorted(util.get_circuit_symbols(model_circuit)))
        self._symbols = tf.constant([str(x) for x in self._symbols_list])

        self._circuit = util.convert_to_tensor([model_circuit])

        if len(self._symbols_list) == 0:
            raise ValueError("model_circuit has no sympy.Symbols. Please "
                             "provide a circuit that contains symbols so "
                             "that their values can be trained.")

        # Ingest operators.
        if isinstance(operators, (cirq.PauliString, cirq.PauliSum)):
            operators = [operators]

        if not isinstance(operators, (list, np.ndarray, tuple)):
            raise TypeError("operators must be a cirq.PauliSum or "
                            "cirq.PauliString, or a list, tuple, "
                            "or np.array containing them. "
                            "Got {}.".format(type(operators)))
        if not all([
                isinstance(op, (cirq.PauliString, cirq.PauliSum))
                for op in operators
        ]):
            raise TypeError("Each element in operators to measure "
                            "must be a cirq.PauliString"
                            " or cirq.PauliSum")

        self._operators = util.convert_to_tensor([operators])

        # Ingest and promote reptitions.
        self._analytic = False
        if repetitions is None:
            self._analytic = True

        if not self._analytic and not isinstance(repetitions,
                                                 numbers.Integral):
            raise TypeError("repetitions must be a positive integer value."
                            " Given: ".format(repetitions))

        if not self._analytic and repetitions <= 0:
            raise ValueError("Repetitions must be greater than zero.")

        if not self._analytic:
            self._repetitions = tf.constant(
                [[repetitions for _ in range(len(operators))]],
                dtype=tf.dtypes.int32)

        if not isinstance(
                backend, cirq.Sampler
        ) and repetitions is not None and backend is not None:
            raise TypeError("provided backend does not inherit cirq.Sampler "
                            "and repetitions!=None. Please provide a backend "
                            "that inherits cirq.Sampler or set "
                            "repetitions=None.")

        if not isinstance(backend, cirq.SimulatesFinalState
                          ) and repetitions is None and backend is not None:
            raise TypeError("provided backend does not inherit "
                            "cirq.SimulatesFinalState and repetitions=None. "
                            "Please provide a backend that inherits "
                            "cirq.SimulatesFinalState.")

        # Ingest backend and differentiator.
        if self._analytic:
            self._layer = expectation.Expectation(
                backend=backend, differentiator=differentiator)
        else:
            self._layer = sampled_expectation.SampledExpectation(
                backend=backend, differentiator=differentiator)

        self._append_layer = elementary.AddCircuit()