Example #1
0
 def test_get_supported_channels(self):
     """Confirm one of every channel is returned."""
     mapping_1 = util.get_supported_channels()
     self.assertEqual(
         len(mapping_1.keys()),
         len(serializer.SERIALIZER.supported_gate_types()) -
         len(util.get_supported_gates()))
Example #2
0
class NoisyExpectationCalculationTest(tf.test.TestCase,
                                      parameterized.TestCase):
    """Tests tfq.noise.expectation."""
    def test_noisy_expectation_inputs(self):
        """Make sure noisy expectation op fails gracefully on bad inputs."""
        n_qubits = 5
        batch_size = 5
        symbol_names = ['alpha']
        qubits = cirq.GridQubit.rect(1, n_qubits)
        circuit_batch, resolver_batch = \
            util.random_symbol_circuit_resolver_batch(
                qubits, symbol_names, batch_size, include_channels=True)

        symbol_values_array = np.array(
            [[resolver[symbol] for symbol in symbol_names]
             for resolver in resolver_batch])

        pauli_sums = util.random_pauli_sums(qubits, 3, batch_size)
        num_samples = [[10]] * batch_size

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'programs must be rank 1'):
            # Circuit tensor has too many dimensions.
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor([circuit_batch]), symbol_names,
                symbol_values_array,
                util.convert_to_tensor([[x] for x in pauli_sums]), num_samples)

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'symbol_names must be rank 1.'):
            # symbol_names tensor has too many dimensions.
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor(circuit_batch),
                np.array([symbol_names]), symbol_values_array,
                util.convert_to_tensor([[x] for x in pauli_sums]), num_samples)

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'symbol_values must be rank 2.'):
            # symbol_values_array tensor has too many dimensions.
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor(circuit_batch), symbol_names,
                np.array([symbol_values_array]),
                util.convert_to_tensor([[x] for x in pauli_sums]), num_samples)

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'symbol_values must be rank 2.'):
            # symbol_values_array tensor has too few dimensions.
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor(circuit_batch), symbol_names,
                symbol_values_array[0],
                util.convert_to_tensor([[x] for x in pauli_sums]), num_samples)

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'pauli_sums must be rank 2.'):
            # pauli_sums tensor has too few dimensions.
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor(circuit_batch),
                symbol_names, symbol_values_array,
                util.convert_to_tensor(list(pauli_sums)), num_samples)

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'pauli_sums must be rank 2.'):
            # pauli_sums tensor has too many dimensions.
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor(circuit_batch), symbol_names,
                symbol_values_array,
                [util.convert_to_tensor([[x]
                                         for x in pauli_sums])], num_samples)

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'num_samples must be rank 2'):
            # num_samples tensor has the wrong shape.
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor(circuit_batch), symbol_names,
                symbol_values_array,
                util.convert_to_tensor([[x] for x in pauli_sums]),
                [num_samples])

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'num_samples must be rank 2'):
            # num_samples tensor has the wrong shape.
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor(circuit_batch), symbol_names,
                symbol_values_array,
                util.convert_to_tensor([[x] for x in pauli_sums]),
                num_samples[0])

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'Unparseable proto'):
            # circuit tensor has the right type but invalid values.
            noisy_sampled_expectation_op.sampled_expectation(
                ['junk'] * batch_size, symbol_names, symbol_values_array,
                util.convert_to_tensor([[x] for x in pauli_sums]), num_samples)

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'Could not find symbol in parameter map'):
            # symbol_names tensor has the right type but invalid values.
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor(circuit_batch), ['junk'],
                symbol_values_array,
                util.convert_to_tensor([[x] for x in pauli_sums]), num_samples)

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'qubits not found in circuit'):
            # pauli_sums tensor has the right type but invalid values.
            new_qubits = [cirq.GridQubit(5, 5), cirq.GridQubit(9, 9)]
            new_pauli_sums = util.random_pauli_sums(new_qubits, 2, batch_size)
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor(circuit_batch), symbol_names,
                symbol_values_array,
                util.convert_to_tensor([[x] for x in new_pauli_sums]),
                num_samples)

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'Unparseable proto'):
            # pauli_sums tensor has the right type but invalid values 2.
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor(circuit_batch), symbol_names,
                symbol_values_array, [['junk']] * batch_size, num_samples)

        with self.assertRaisesRegex(TypeError, 'Cannot convert'):
            # circuits tensor has the wrong type.
            noisy_sampled_expectation_op.sampled_expectation(
                [1.0] * batch_size, symbol_names, symbol_values_array,
                util.convert_to_tensor([[x] for x in pauli_sums]), num_samples)

        with self.assertRaisesRegex(TypeError, 'Cannot convert'):
            # symbol_names tensor has the wrong type.
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor(circuit_batch), [0.1234],
                symbol_values_array,
                util.convert_to_tensor([[x] for x in pauli_sums]), num_samples)

        with self.assertRaisesRegex(tf.errors.UnimplementedError, ''):
            # symbol_values tensor has the wrong type.
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor(circuit_batch), symbol_names,
                [['junk']] * batch_size,
                util.convert_to_tensor([[x] for x in pauli_sums]), num_samples)

        with self.assertRaisesRegex(TypeError, 'Cannot convert'):
            # pauli_sums tensor has the wrong type.
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor(circuit_batch), symbol_names,
                symbol_values_array, [[1.0]] * batch_size, num_samples)

        with self.assertRaisesRegex(TypeError, 'missing'):
            # we are missing an argument.
            # pylint: disable=no-value-for-parameter
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor(circuit_batch), symbol_names,
                symbol_values_array, num_samples)
            # pylint: enable=no-value-for-parameter

        with self.assertRaisesRegex(TypeError, 'positional arguments'):
            # pylint: disable=too-many-function-args
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor(circuit_batch), symbol_names,
                symbol_values_array,
                util.convert_to_tensor([[x] for x in pauli_sums]), [],
                num_samples)
            # pylint: enable=too-many-function-args

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    expected_regex='do not match'):
            # wrong op size.
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor([cirq.Circuit()]), symbol_names,
                symbol_values_array.astype(np.float64),
                util.convert_to_tensor([[x] for x in pauli_sums]), num_samples)

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'greater than 0'):
            # pylint: disable=too-many-function-args
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor(circuit_batch), symbol_names,
                symbol_values_array,
                util.convert_to_tensor([[x] for x in pauli_sums]),
                [[-1]] * batch_size)
            # pylint: enable=too-many-function-args

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    expected_regex='do not match'):
            # wrong symbol_values size.
            noisy_sampled_expectation_op.sampled_expectation(
                util.convert_to_tensor(circuit_batch), symbol_names,
                symbol_values_array[:int(batch_size * 0.5)],
                util.convert_to_tensor([[x] for x in pauli_sums]), num_samples)

    @parameterized.parameters([
        {
            'n_qubits': 13,
            'batch_size': 1,
            'noisy': False
        },  # ComputeLarge.
        {
            'n_qubits': 6,
            'batch_size': 25,
            'noisy': False
        },  # ComputeSmall.
        {
            'n_qubits': 6,
            'batch_size': 10,
            'noisy': True
        },  # ComputeSmall.
        {
            'n_qubits': 8,
            'batch_size': 1,
            'noisy': True
        }  # ComputeLarge.
    ])
    def test_simulate_consistency(self, batch_size, n_qubits, noisy):
        """Test consistency with batch_util.py simulation."""
        symbol_names = ['alpha', 'beta']
        qubits = cirq.GridQubit.rect(1, n_qubits)

        circuit_batch, resolver_batch = \
            util.random_symbol_circuit_resolver_batch(
                qubits, symbol_names, batch_size, include_channels=noisy)

        symbol_values_array = np.array(
            [[resolver[symbol] for symbol in symbol_names]
             for resolver in resolver_batch])

        pauli_sums1 = util.random_pauli_sums(qubits, 3, batch_size)
        pauli_sums2 = util.random_pauli_sums(qubits, 3, batch_size)
        batch_pauli_sums = [[x, y] for x, y in zip(pauli_sums1, pauli_sums2)]
        num_samples = [[10000] * 2] * batch_size

        op_exps = noisy_sampled_expectation_op.sampled_expectation(
            util.convert_to_tensor(circuit_batch),
            symbol_names, symbol_values_array,
            util.convert_to_tensor(batch_pauli_sums), num_samples)

        cirq_exps = batch_util.batch_calculate_expectation(
            circuit_batch, resolver_batch, batch_pauli_sums,
            cirq.DensityMatrixSimulator() if noisy else cirq.Simulator())
        tol = 0.5
        self.assertAllClose(cirq_exps, op_exps, atol=tol, rtol=tol)

    @parameterized.parameters([{
        'channel': x
    } for x in util.get_supported_channels()])
    def test_single_channel(self, channel):
        """Individually test adding just a single channel type to circuits."""
        symbol_names = []
        batch_size = 5
        n_qubits = 6
        qubits = cirq.GridQubit.rect(1, n_qubits)

        circuit_batch, resolver_batch = \
            util.random_circuit_resolver_batch(
                qubits, batch_size, include_channels=False)

        for i in range(batch_size):
            circuit_batch[i] = circuit_batch[i] + channel.on_each(*qubits)

        symbol_values_array = np.array(
            [[resolver[symbol] for symbol in symbol_names]
             for resolver in resolver_batch])

        pauli_sums1 = util.random_pauli_sums(qubits, 3, batch_size)
        pauli_sums2 = util.random_pauli_sums(qubits, 3, batch_size)
        batch_pauli_sums = [[x, y] for x, y in zip(pauli_sums1, pauli_sums2)]
        num_samples = [[20000] * 2] * batch_size

        op_exps = noisy_sampled_expectation_op.sampled_expectation(
            util.convert_to_tensor(circuit_batch),
            symbol_names, symbol_values_array,
            util.convert_to_tensor(batch_pauli_sums), num_samples)

        cirq_exps = batch_util.batch_calculate_expectation(
            circuit_batch, resolver_batch, batch_pauli_sums,
            cirq.DensityMatrixSimulator())

        self.assertAllClose(cirq_exps, op_exps, atol=0.35, rtol=0.35)

    def test_correctness_empty(self):
        """Test the expectation for empty circuits."""
        empty_circuit = util.convert_to_tensor([cirq.Circuit()])
        empty_symbols = tf.convert_to_tensor([], dtype=tf.dtypes.string)
        empty_values = tf.convert_to_tensor([[]])
        empty_paulis = tf.convert_to_tensor([[]], dtype=tf.dtypes.string)
        empty_n_samples = tf.convert_to_tensor([[]], dtype=tf.int32)

        out = noisy_sampled_expectation_op.sampled_expectation(
            empty_circuit, empty_symbols, empty_values, empty_paulis,
            empty_n_samples)

        expected = np.array([[]], dtype=np.complex64)
        self.assertAllClose(out, expected)

    def test_correctness_no_circuit(self):
        """Test the correctness with the empty tensor."""
        empty_circuit = tf.raw_ops.Empty(shape=(0, ), dtype=tf.string)
        empty_symbols = tf.raw_ops.Empty(shape=(0, ), dtype=tf.string)
        empty_values = tf.raw_ops.Empty(shape=(0, 0), dtype=tf.float32)
        empty_paulis = tf.raw_ops.Empty(shape=(0, 0), dtype=tf.string)
        empty_n_samples = tf.raw_ops.Empty(shape=(0, 0), dtype=tf.int32)

        out = noisy_sampled_expectation_op.sampled_expectation(
            empty_circuit, empty_symbols, empty_values, empty_paulis,
            empty_n_samples)

        self.assertShapeEqual(np.zeros((0, 0)), out)
class NoisySamplingTest(tf.test.TestCase, parameterized.TestCase):
    """Tests tfq.noise.expectation."""

    def _compute_hists(self, x, n_qubits):
        """Compute the batchwise histograms of a sample tensor."""
        x = np.asarray(x)
        return [
            np.histogram(
                sample.dot(1 << np.arange(sample.shape[-1] - 1, -1, -1)),
                range=(0, 2**n_qubits),
                bins=2**n_qubits)[0] for sample in x
        ]

    def test_simulate_samples_inputs(self):
        """Make sure the sample op fails gracefully on bad inputs."""
        n_qubits = 5
        batch_size = 5
        num_samples = 10
        symbol_names = ['alpha']
        qubits = cirq.GridQubit.rect(1, n_qubits)
        circuit_batch, resolver_batch = \
            util.random_symbol_circuit_resolver_batch(
                qubits, symbol_names, batch_size)

        symbol_values_array = np.array(
            [[resolver[symbol]
              for symbol in symbol_names]
             for resolver in resolver_batch])

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'rank 1. Got rank 2'):
            # programs tensor has the wrong shape.
            noisy_samples_op.samples(util.convert_to_tensor([circuit_batch]),
                                     symbol_names, symbol_values_array,
                                     [num_samples])

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'rank 1. Got rank 2'):
            # symbol_names tensor has the wrong shape.
            noisy_samples_op.samples(util.convert_to_tensor(circuit_batch),
                                     np.array([symbol_names]),
                                     symbol_values_array, [num_samples])

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'rank 2. Got rank 3'):
            # symbol_values tensor has the wrong shape.
            noisy_samples_op.samples(util.convert_to_tensor(circuit_batch),
                                     symbol_names,
                                     np.array([symbol_values_array]),
                                     [num_samples])

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'rank 2. Got rank 1'):
            # symbol_values tensor has the wrong shape 2.
            noisy_samples_op.samples(util.convert_to_tensor(circuit_batch),
                                     symbol_names, symbol_values_array[0],
                                     [num_samples])

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'rank 1. Got rank 2'):
            # num_samples tensor has the wrong shape.
            noisy_samples_op.samples(util.convert_to_tensor(circuit_batch),
                                     symbol_names, symbol_values_array,
                                     [[num_samples]])

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'Unparseable proto'):
            # programs tensor has the right type, but invalid value.
            noisy_samples_op.samples(['junk'] * batch_size, symbol_names,
                                     symbol_values_array, [num_samples])

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    'Could not find symbol in parameter map'):
            # symbol_names tensor has the right type, but invalid value.
            noisy_samples_op.samples(util.convert_to_tensor(circuit_batch),
                                     ['junk'], symbol_values_array,
                                     [num_samples])

        with self.assertRaisesRegex(TypeError, 'Cannot convert'):
            # programs tensor has the wrong type.
            noisy_samples_op.samples([1] * batch_size, symbol_names,
                                     symbol_values_array, [num_samples])

        with self.assertRaisesRegex(TypeError, 'Cannot convert'):
            # programs tensor has the wrong type.
            noisy_samples_op.samples(util.convert_to_tensor(circuit_batch), [1],
                                     symbol_values_array, [num_samples])

        with self.assertRaisesRegex(tf.errors.UnimplementedError,
                                    'Cast string to float is not supported'):
            # programs tensor has the wrong type.
            noisy_samples_op.samples(util.convert_to_tensor(circuit_batch),
                                     symbol_names, [['junk']] * batch_size,
                                     [num_samples])

        with self.assertRaisesRegex(Exception, 'junk'):
            # num_samples tensor has the wrong type.
            noisy_samples_op.samples(util.convert_to_tensor(circuit_batch),
                                     symbol_names, symbol_values_array,
                                     ['junk'])

        with self.assertRaisesRegex(TypeError, 'missing'):
            # too few tensors.
            # pylint: disable=no-value-for-parameter
            noisy_samples_op.samples(util.convert_to_tensor(circuit_batch),
                                     symbol_names, symbol_values_array)
            # pylint: enable=no-value-for-parameter

        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,
                                    expected_regex='do not match'):
            # wrong symbol_values size.
            noisy_samples_op.samples(
                util.convert_to_tensor(circuit_batch), symbol_names,
                symbol_values_array[:int(batch_size * 0.5)], num_samples)

    @parameterized.parameters([
        {
            'n_qubits': 13,
            'batch_size': 1,
            'noisy': False
        },  # ComputeLarge.
        {
            'n_qubits': 6,
            'batch_size': 25,
            'noisy': False
        },  # ComputeSmall.
        {
            'n_qubits': 6,
            'batch_size': 10,
            'noisy': True
        },  # ComputeSmall.
        {
            'n_qubits': 8,
            'batch_size': 1,
            'noisy': True
        }  # ComputeLarge.
    ])
    def test_simulate_consistency(self, batch_size, n_qubits, noisy):
        """Test consistency with batch_util.py simulation."""
        symbol_names = ['alpha', 'beta']
        qubits = cirq.GridQubit.rect(1, n_qubits)

        circuit_batch, resolver_batch = \
            util.random_symbol_circuit_resolver_batch(
                qubits, symbol_names, batch_size, include_channels=noisy)

        symbol_values_array = np.array(
            [[resolver[symbol]
              for symbol in symbol_names]
             for resolver in resolver_batch])

        n_samples = 10000
        op_samples = noisy_samples_op.samples(
            util.convert_to_tensor(circuit_batch), symbol_names,
            symbol_values_array, [n_samples]).to_list()

        op_hists = self._compute_hists(op_samples, n_qubits)

        cirq_samples = batch_util.batch_sample(
            circuit_batch, resolver_batch, n_samples,
            cirq.DensityMatrixSimulator() if noisy else cirq.Simulator())

        cirq_hists = self._compute_hists(cirq_samples, n_qubits)
        tol = 1.5 if noisy else 1.0
        for a, b in zip(op_hists, cirq_hists):
            self.assertLess(stats.entropy(a + 1e-8, b + 1e-8), tol)

    @parameterized.parameters([{
        'channel': x
    } for x in util.get_supported_channels()])
    def test_single_channel(self, channel):
        """Individually test adding just a single channel type to circuits."""
        symbol_names = []
        batch_size = 3
        n_qubits = 5
        qubits = cirq.GridQubit.rect(1, n_qubits)

        circuit_batch, resolver_batch = \
            util.random_circuit_resolver_batch(
                qubits, batch_size, include_channels=False)

        for i in range(batch_size):
            circuit_batch[i] = circuit_batch[i] + channel.on_each(*qubits)

        symbol_values_array = np.array(
            [[resolver[symbol]
              for symbol in symbol_names]
             for resolver in resolver_batch])

        n_samples = (2**n_qubits) * 1000

        op_samples = noisy_samples_op.samples(
            util.convert_to_tensor(circuit_batch), symbol_names,
            symbol_values_array, [n_samples]).to_list()
        op_hists = self._compute_hists(op_samples, n_qubits)

        cirq_samples = batch_util.batch_sample(circuit_batch, resolver_batch,
                                               n_samples,
                                               cirq.DensityMatrixSimulator())
        cirq_hists = self._compute_hists(cirq_samples, n_qubits)

        for a, b in zip(op_hists, cirq_hists):
            self.assertLess(stats.entropy(a + 1e-8, b + 1e-8), 0.15)

    def test_correct_padding(self):
        """Test the variable sized circuits are properly padded."""
        symbol_names = []
        batch_size = 2
        n_qubits = 5
        qubits1 = cirq.GridQubit.rect(1, n_qubits)
        qubits2 = cirq.GridQubit.rect(1, n_qubits + 1)

        circuit_batch1, resolver_batch1 = \
            util.random_circuit_resolver_batch(
                qubits1, batch_size, include_channels=True)

        circuit_batch2, resolver_batch2 = \
            util.random_circuit_resolver_batch(
                qubits2, batch_size, include_channels=True)

        p1 = [[resolver[symbol]
               for symbol in symbol_names]
              for resolver in resolver_batch1]
        p2 = [[resolver[symbol]
               for symbol in symbol_names]
              for resolver in resolver_batch2]
        symbol_values_array = np.array(p1 + p2)

        n_samples = 10

        op_samples = noisy_samples_op.samples(
            util.convert_to_tensor(circuit_batch1 + circuit_batch2),
            symbol_names, symbol_values_array, [n_samples]).to_list()
        a_reps = np.asarray(op_samples[:2])
        b_reps = np.asarray(op_samples[2:])
        self.assertEqual(a_reps.shape, (2, 10, 5))
        self.assertEqual(b_reps.shape, (2, 10, 6))

    def test_correctness_empty(self):
        """Test the expectation for empty circuits."""
        empty_circuit = util.convert_to_tensor([cirq.Circuit()])
        empty_symbols = tf.convert_to_tensor([], dtype=tf.dtypes.string)
        empty_values = tf.convert_to_tensor([[]])
        empty_n_samples = tf.convert_to_tensor([1], dtype=tf.int32)

        out = noisy_samples_op.samples(empty_circuit, empty_symbols,
                                       empty_values, empty_n_samples)

        expected = np.array([[[]]], dtype=np.int8)
        self.assertAllClose(out.to_tensor(), expected)

    def test_correctness_no_circuit(self):
        """Test the correctness with the empty tensor."""
        empty_circuit = tf.raw_ops.Empty(shape=(0,), dtype=tf.string)
        empty_symbols = tf.raw_ops.Empty(shape=(0,), dtype=tf.string)
        empty_values = tf.raw_ops.Empty(shape=(0, 0), dtype=tf.float32)
        empty_n_samples = tf.convert_to_tensor([1], dtype=tf.int32)

        out = noisy_samples_op.samples(empty_circuit, empty_symbols,
                                       empty_values, empty_n_samples)

        self.assertShapeEqual(np.zeros((0, 0, 0)), out.to_tensor())