Ejemplo n.º 1
0
 def test_get_cirq_state_op(self):
     """Input check the wrapper for the cirq state op."""
     with self.assertRaisesRegex(
             TypeError, "simulator must inherit cirq.SimulatesFinalState."):
         cirq_ops._get_cirq_simulate_state("junk")
     cirq_ops._get_cirq_simulate_state()
     cirq_ops._get_cirq_simulate_state(cirq.Simulator())
     cirq_ops._get_cirq_simulate_state(cirq.DensityMatrixSimulator())
Ejemplo n.º 2
0
 def test_cirq_state_op_inputs(self):
     """test input checking in the state sim op."""
     test_op = cirq_ops._get_cirq_simulate_state(cirq.Simulator())
     bits = cirq.GridQubit.rect(1, 5)
     test_circuit = serializer.serialize_circuit(
         cirq.testing.random_circuit(bits, MOMENT_DEPTH,
                                     0.9)).SerializeToString()
     # exceptions raised in the tf graph don't get passed
     # through in an identifiable way
     with self.assertRaisesRegex(
             tf.errors.InvalidArgumentError,
             'symbol_names tensor must be of type string'):
         _ = test_op([test_circuit], [0], [[0]])
     with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                 'programs tensor must be of type string'):
         _ = test_op([0], ['rx'], [[0]])
     with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                 'real-valued numeric tensor.'):
         _ = test_op([test_circuit], ['rx'], 'junk')
     with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                 'size of symbol_names tensor must match'):
         _ = test_op([test_circuit], ['rx'], [[1, 1]])
     with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                 'size of symbol_names tensor must match'):
         _ = test_op([test_circuit], ['rx', 'ry'], [[1]])
     with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                 'first dimension of symbol_values tensor'):
         _ = test_op([test_circuit, test_circuit], ['rx'], [[1]])
     _ = test_op([test_circuit], ['rx'], [[1]])
     _ = test_op([test_circuit], [], [[]])
Ejemplo n.º 3
0
 def test_state_empty_circuit(self):
     """Test empty circuits"""
     test_op = cirq_ops._get_cirq_simulate_state(
         cirq.sim.sparse_simulator.Simulator())
     test_empty_circuit = serializer.serialize_circuit(
         cirq.Circuit()).SerializeToString()
     _ = test_op([test_empty_circuit], [], [[]])
Ejemplo n.º 4
0
def get_state_op(backend=None):
    """Get a TensorFlow op that produces states from given quantum circuits.

    This function produces a non-differentiable op that will calculate
    batches of state tensors given tensor batches of `cirq.Circuit`s and
    parameter values.


    >>> # Simulate circuits with cirq.
    >>> my_op = tfq.get_state_op(backend=cirq.DensityMatrixSimulator())
    >>> # Simulate circuits with C++.
    >>> my_second_op = tfq.get_state_op()
    >>> # Prepare some inputs.
    >>> qubit = cirq.GridQubit(0, 0)
    >>> my_symbol = sympy.Symbol('alpha')
    >>> my_circuit_tensor = tfq.convert_to_tensor([
    ...     cirq.Circuit(cirq.Y(qubit) ** my_symbol)
    ... ])
    >>> my_values = np.array([[0.5]])
    >>> # This op can now be run to calculate the state.
    >>> output = my_second_op(my_circuit_tensor, ['alpha'], my_values)
    >>> output
    <tf.RaggedTensor [[(0.5+0.5j), (0.5+0.5j)]]>


    Args:
        backend: Optional Python `object` that specifies what backend this op
            should use when evaluating circuits. Can be any
            `cirq.SimulatesFinalState`. If not provided, the default C++
            wavefunction simulator will be used.

    Returns:
        A `callable` with the following signature:

        ```op(programs, symbol_names, symbol_values)```

        programs: `tf.Tensor` of strings with shape [batch_size] containing
            the string representations of the circuits to be executed.
        symbol_names: `tf.Tensor` of strings with shape [n_params], which
            is used to specify the order in which the values in
            `symbol_values` should be placed inside of the circuits in
            `programs`.
        symbol_values: `tf.Tensor` of real numbers with shape
            [batch_size, n_params] specifying parameter values to resolve
            into the circuits specified by programs, following the ordering
            dictated by `symbol_names`.

        Returns:
            `tf.Tensor` with shape [batch_size, <ragged> size of state] that
            contains the state information of the circuit.
    """

    # TODO (mbbrough): investigate how the above docstring renders.
    if backend is None:
        return lambda programs, symbol_names, symbol_values: \
        tfq_utility_ops.padded_to_ragged(TFQWavefunctionSimulator.state(
            programs, symbol_names, symbol_values))

    if isinstance(backend, (cirq.SimulatesFinalState)):
        return lambda programs, symbol_names, symbol_values: \
        tfq_utility_ops.padded_to_ragged(
            cirq_ops._get_cirq_simulate_state(backend)(
                programs, symbol_names, symbol_values))

    raise TypeError("Backend {} is invalid. Expected a Cirq.SimulatesFinalState"
                    " or None.".format(backend))
Ejemplo n.º 5
0
def get_state_op(
    backend=None,
    *,
    quantum_concurrent=quantum_context.get_quantum_concurrent_op_mode()):
    """Get a TensorFlow op that produces states from given quantum circuits.

    This function produces a non-differentiable op that will calculate
    batches of state tensors given tensor batches of `cirq.Circuit`s and
    parameter values.


    >>> # Simulate circuits with cirq.
    >>> my_op = tfq.get_state_op(backend=cirq.DensityMatrixSimulator())
    >>> # Simulate circuits with C++.
    >>> my_second_op = tfq.get_state_op()
    >>> # Prepare some inputs.
    >>> qubit = cirq.GridQubit(0, 0)
    >>> my_symbol = sympy.Symbol('alpha')
    >>> my_circuit_tensor = tfq.convert_to_tensor([
    ...     cirq.Circuit(cirq.Y(qubit) ** my_symbol)
    ... ])
    >>> my_values = np.array([[0.5]])
    >>> # This op can now be run to calculate the state.
    >>> output = my_second_op(my_circuit_tensor, ['alpha'], my_values)
    >>> output
    <tf.RaggedTensor [[(0.5+0.5j), (0.5+0.5j)]]>


    Args:
        backend: Optional Python `object` that specifies what backend this op
            should use when evaluating circuits. Can be any
            `cirq.SimulatesFinalState`. If not provided, the default C++
            wavefunction simulator will be used.
        quantum_concurrent: Optional Python `bool`. True indicates that the
            returned op should not block graph level parallelism on itself when
            executing. False indicates that graph level parallelism on itself
            should be blocked. Defaults to value specified in
            `tfq.get_quantum_concurrent_op_mode` which defaults to True
            (no blocking). This flag is only needed for advanced users when
            using TFQ for very large simulations, or when running on a real
            chip.

    Returns:
        A `callable` with the following signature:

        ```op(programs, symbol_names, symbol_values)```

        programs: `tf.Tensor` of strings with shape [batch_size] containing
            the string representations of the circuits to be executed.
        symbol_names: `tf.Tensor` of strings with shape [n_params], which
            is used to specify the order in which the values in
            `symbol_values` should be placed inside of the circuits in
            `programs`.
        symbol_values: `tf.Tensor` of real numbers with shape
            [batch_size, n_params] specifying parameter values to resolve
            into the circuits specified by programs, following the ordering
            dictated by `symbol_names`.

        Returns:
            `tf.Tensor` with shape [batch_size, <ragged> size of state] that
            contains the state information of the circuit.
    """

    # TODO (mbbrough): investigate how the above docstring renders.
    _check_quantum_concurrent(quantum_concurrent)

    op = None
    if backend is None:
        op = TFQWavefunctionSimulator.state

    if isinstance(backend, (cirq.SimulatesFinalState)):
        op = cirq_ops._get_cirq_simulate_state(backend)

    if op is not None:
        if quantum_concurrent is True:
            # Return an op that does not block graph level parallelism.
            return lambda programs, symbol_names, symbol_values: \
                tfq_utility_ops.padded_to_ragged(
                    op(programs, symbol_names, symbol_values))

        # Return an op that does block graph level parallelism.
        return lambda programs, symbol_names, symbol_values: \
            _GLOBAL_OP_LOCK.execute(lambda: tfq_utility_ops.padded_to_ragged(
                op(programs, symbol_names, symbol_values)))

    raise TypeError(
        "Backend {} is invalid. Expected a Cirq.SimulatesFinalState"
        " or None.".format(backend))
Ejemplo n.º 6
0
 def test_state_no_circuit(self):
     """Test empty tensors with no circuits at all."""
     test_op = cirq_ops._get_cirq_simulate_state(cirq.Simulator())
     empty_programs = tf.raw_ops.Empty(shape=(0,), dtype=tf.string)
     empty_values = tf.raw_ops.Empty(shape=(0, 0), dtype=tf.float32)
     _ = test_op(empty_programs, [], empty_values)
Ejemplo n.º 7
0
class CirqSimulateStateTest(tf.test.TestCase, parameterized.TestCase):
    """Tests get_cirq_simulate_state."""

    def test_get_cirq_state_op(self):
        """Input check the wrapper for the cirq state op."""
        with self.assertRaisesRegex(
                TypeError, "simulator must inherit cirq.SimulatesFinalState."):
            cirq_ops._get_cirq_simulate_state("junk")
        cirq_ops._get_cirq_simulate_state()
        cirq_ops._get_cirq_simulate_state(cirq.Simulator())
        cirq_ops._get_cirq_simulate_state(cirq.DensityMatrixSimulator())

    # TODO(trevormccrt): input checking might be parameterizeable over all ops
    # if we decide to properly input check our c++ ops
    def test_cirq_state_op_inputs(self):
        """test input checking in the state sim op."""
        test_op = cirq_ops._get_cirq_simulate_state(cirq.Simulator())
        bits = cirq.GridQubit.rect(1, 5)
        test_circuit = serializer.serialize_circuit(
            cirq.testing.random_circuit(bits, MOMENT_DEPTH,
                                        0.9)).SerializeToString()
        # exceptions raised in the tf graph don't get passed
        # through in an identifiable way
        with self.assertRaisesRegex(
                tf.errors.InvalidArgumentError,
                'symbol_names tensor must be of type string'):
            _ = test_op([test_circuit], [0], [[0]])
        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'programs tensor must be of type string'):
            _ = test_op([0], ['rx'], [[0]])
        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'real-valued numeric tensor.'):
            _ = test_op([test_circuit], ['rx'], 'junk')
        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'size of symbol_names tensor must match'):
            _ = test_op([test_circuit], ['rx'], [[1, 1]])
        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'size of symbol_names tensor must match'):
            _ = test_op([test_circuit], ['rx', 'ry'], [[1]])
        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'first dimension of symbol_values tensor'):
            _ = test_op([test_circuit, test_circuit], ['rx'], [[1]])
        _ = test_op([test_circuit], ['rx'], [[1]])
        _ = test_op([test_circuit], [], [[]])

    @parameterized.parameters([
        {
            'op_and_sim': (cirq_ops._get_cirq_simulate_state(WF_SIM), WF_SIM),
            'all_n_qubits': [2, 3]
        },
        {
            'op_and_sim': (cirq_ops._get_cirq_simulate_state(DM_SIM), DM_SIM),
            'all_n_qubits': [2, 3]
        },
        {
            'op_and_sim': (cirq_ops._get_cirq_simulate_state(WF_SIM), WF_SIM),
            'all_n_qubits': [2, 5, 8, 10]
        },
        {
            'op_and_sim': (cirq_ops._get_cirq_simulate_state(DM_SIM), DM_SIM),
            'all_n_qubits': [2, 5, 8, 10]
        },
    ])
    def test_simulate_state_output_padding(self, op_and_sim, all_n_qubits):
        """If a circuit executing op is asked to simulate states given circuits
        acting on different numbers of qubits, the op should return a tensor
        padded with zeros up to the size of the largest circuit. The padding
        should be physically correct, such that samples taken from the padded
        states still match samples taken from the original circuit."""
        op = op_and_sim[0]
        sim = op_and_sim[1]

        circuit_batch = []
        for n_qubits in all_n_qubits:
            qubits = cirq.GridQubit.rect(1, n_qubits)
            circuit_batch += util.random_circuit_resolver_batch(qubits, 1)[0]

        tfq_results = op(util.convert_to_tensor(circuit_batch), [],
                         [[]] * len(circuit_batch))

        # don't use batch_util here to enforce consistent padding everywhere
        # without extra tests
        manual_padded_results = []
        for circuit in circuit_batch:
            result = sim.simulate(circuit)

            # density matricies should be zero everywhere except for the
            # top left corner
            if isinstance(result, cirq.DensityMatrixTrialResult):
                dm = result.final_density_matrix
                blank_state = np.ones(
                    (2**max(all_n_qubits), 2**(max(all_n_qubits))),
                    dtype=np.complex64) * -2
                blank_state[:dm.shape[0], :dm.shape[1]] = dm
                manual_padded_results.append(blank_state)

            # state vectors should be zero everywhere to the right of the states
            # present in this system
            elif isinstance(result, cirq.StateVectorTrialResult):
                wf = result.final_state_vector
                blank_state = np.ones(
                    (2**max(all_n_qubits)), dtype=np.complex64) * -2
                blank_state[:wf.shape[0]] = wf
                manual_padded_results.append(blank_state)

            else:
                # TODO
                raise RuntimeError(
                    'Simulator returned unknown type of result.' +
                    str(type(result)))

        self.assertAllClose(tfq_results, manual_padded_results, atol=1e-5)

    def test_state_empty_circuit(self):
        """Test empty circuits"""
        test_op = cirq_ops._get_cirq_simulate_state(cirq.Simulator())
        test_empty_circuit = serializer.serialize_circuit(
            cirq.Circuit()).SerializeToString()
        _ = test_op([test_empty_circuit], [], [[]])

    def test_state_no_circuit(self):
        """Test empty tensors with no circuits at all."""
        test_op = cirq_ops._get_cirq_simulate_state(cirq.Simulator())
        empty_programs = tf.raw_ops.Empty(shape=(0,), dtype=tf.string)
        empty_values = tf.raw_ops.Empty(shape=(0, 0), dtype=tf.float32)
        _ = test_op(empty_programs, [], empty_values)