def test_correctness_without_symbols(self, n_qubits, batch_size, inner_dim_size): """Test that inner_product works with symbols.""" qubits = cirq.GridQubit.rect(1, n_qubits) circuit_batch, _ = \ util.random_circuit_resolver_batch( qubits, batch_size) other_batch = [ util.random_circuit_resolver_batch(qubits, inner_dim_size)[0] for i in range(batch_size) ] programs = util.convert_to_tensor(circuit_batch) other_programs = util.convert_to_tensor(other_batch) symbol_names = tf.convert_to_tensor([], dtype=tf.dtypes.string) symbol_values = tf.convert_to_tensor([[] for _ in range(batch_size)]) out = inner_product_op.inner_product(programs, symbol_names, symbol_values, other_programs) out_arr = np.empty((batch_size, inner_dim_size), dtype=np.complex64) for i in range(batch_size): final_wf = cirq.final_state_vector(circuit_batch[i]) for j in range(inner_dim_size): internal_wf = cirq.final_state_vector(other_batch[i][j]) out_arr[i][j] = np.vdot(final_wf, internal_wf) self.assertAllClose(out, out_arr)
def test_tf_gradient_correctness_with_symbols(self, n_qubits, batch_size, inner_dim_size): """Tests that tf.gradient of inner_product works with symbols.""" symbol_names = ['alpha', 'beta', 'gamma'] n_params = len(symbol_names) qubits = cirq.GridQubit.rect(1, n_qubits) circuit_batch, resolver_batch = \ util.random_symbol_circuit_resolver_batch( qubits, symbol_names, batch_size) other_batch = [ util.random_circuit_resolver_batch(qubits, inner_dim_size)[0] for i in range(batch_size) ] symbol_values_array = np.array( [[resolver[symbol] for symbol in symbol_names] for resolver in resolver_batch]) programs = util.convert_to_tensor(circuit_batch) other_programs = util.convert_to_tensor(other_batch) symbol_names_tensor = tf.convert_to_tensor(symbol_names, dtype=tf.dtypes.string) symbol_values = tf.convert_to_tensor(symbol_values_array) with tf.GradientTape() as tape: tape.watch(symbol_values) ip = inner_product_op.inner_product(programs, symbol_names_tensor, symbol_values, other_programs) out = tape.gradient(ip, symbol_values) out_arr = np.zeros((batch_size, n_params), dtype=np.complex64) # dx came from _GRAD_EPS of core/src/adj_util.cc dx = 5e-3 for i in range(batch_size): for k, name in enumerate(symbol_names): if name in resolver_batch[i].param_dict: new_resolver = copy.deepcopy(resolver_batch[i]) new_resolver.param_dict[name] += dx final_circuit_p = cirq.resolve_parameters( circuit_batch[i], new_resolver) new_resolver = copy.deepcopy(resolver_batch[i]) new_resolver.param_dict[name] -= dx final_circuit_m = cirq.resolve_parameters( circuit_batch[i], new_resolver) final_wf_p = cirq.final_state_vector(final_circuit_p) final_wf_m = cirq.final_state_vector(final_circuit_m) # Performs central finite difference. final_wf_grad = 0.5 * (final_wf_p - final_wf_m) / dx for j in range(inner_dim_size): internal_wf = cirq.final_state_vector(other_batch[i][j]) out_arr[i][k] += np.vdot(final_wf_grad, internal_wf) self.assertAllClose(out, np.conj(out_arr), atol=1e-3)
def test_correctness_no_circuit(self): """Test the inner product between no circuits.""" 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) other_program = tf.raw_ops.Empty(shape=(0, 0), dtype=tf.string) out = inner_product_op.inner_product(empty_circuit, empty_symbols, empty_values, other_program) self.assertShapeEqual(np.zeros((0, 0)), out)
def test_correctness_empty(self): """Test the inner product between two empty circuits.""" empty_cicuit = util.convert_to_tensor([cirq.Circuit()]) empty_symbols = tf.convert_to_tensor([], dtype=tf.dtypes.string) empty_values = tf.convert_to_tensor([[]]) other_program = util.convert_to_tensor([[cirq.Circuit()]]) out = inner_product_op.inner_product(empty_cicuit, empty_symbols, empty_values, other_program) expected = np.array([[1.0]], dtype=np.complex64) self.assertAllClose(out, expected)
def test_correctness_empty(self): """Tests the inner product with 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([[]]) other_program = util.convert_to_tensor([[cirq.Circuit()]]) out = inner_product_op.inner_product(empty_circuit, empty_symbols, empty_values, other_program) expected = np.array([[1.0]], dtype=np.complex64) self.assertAllClose(out, expected) qubit = cirq.GridQubit(0, 0) non_empty_circuit = util.convert_to_tensor( [cirq.Circuit(cirq.X(qubit))]) empty_symbols = tf.convert_to_tensor([], dtype=tf.dtypes.string) empty_values = tf.convert_to_tensor([[]]) other_program = util.convert_to_tensor([[cirq.Circuit()]]) with self.assertRaisesRegex(tf.errors.InvalidArgumentError, 'qubits not found'): inner_product_op.inner_product(non_empty_circuit, empty_symbols, empty_values, other_program)
def test_correctness_with_symbols(self, n_qubits, batch_size, inner_dim_size): """Tests that inner_product works with symbols.""" symbol_names = ['alpha', 'beta', 'gamma'] qubits = cirq.GridQubit.rect(1, n_qubits) circuit_batch, resolver_batch = \ util.random_symbol_circuit_resolver_batch( qubits, symbol_names, batch_size) other_batch = [ util.random_circuit_resolver_batch(qubits, inner_dim_size)[0] for i in range(batch_size) ] symbol_values_array = np.array( [[resolver[symbol] for symbol in symbol_names] for resolver in resolver_batch]) programs = util.convert_to_tensor(circuit_batch) other_programs = util.convert_to_tensor(other_batch) symbol_names = tf.convert_to_tensor(symbol_names, dtype=tf.dtypes.string) symbol_values = tf.convert_to_tensor(symbol_values_array) out = inner_product_op.inner_product(programs, symbol_names, symbol_values, other_programs) out_arr = np.empty((batch_size, inner_dim_size), dtype=np.complex64) for i in range(batch_size): final_circuit = cirq.resolve_parameters(circuit_batch[i], resolver_batch[i]) final_wf = cirq.final_state_vector(final_circuit) for j in range(inner_dim_size): internal_wf = cirq.final_state_vector(other_batch[i][j]) out_arr[i][j] = np.vdot(final_wf, internal_wf) self.assertAllClose(out, out_arr, atol=1e-5)
def test_tf_gradient_correctness_without_symbols(self, n_qubits, batch_size, inner_dim_size): """Tests that tf.gradient of inner_product works without symbols.""" qubits = cirq.GridQubit.rect(1, n_qubits) circuit_batch, _ = \ util.random_circuit_resolver_batch( qubits, batch_size) other_batch = [ util.random_circuit_resolver_batch(qubits, inner_dim_size)[0] for i in range(batch_size) ] programs = util.convert_to_tensor(circuit_batch) other_programs = util.convert_to_tensor(other_batch) symbol_names = tf.convert_to_tensor([], dtype=tf.dtypes.string) symbol_values = tf.convert_to_tensor([[] for _ in range(batch_size)]) with tf.GradientTape() as tape: tape.watch(symbol_values) ip = inner_product_op.inner_product(programs, symbol_names, symbol_values, other_programs) out = tape.gradient(ip, symbol_values) self.assertAllClose(out, tf.zeros_like(symbol_values), atol=1e-3)
def fidelity(programs, symbol_names, symbol_values, other_programs): """Calculate the fidelity between circuits. Compute (potentially many) fidelities between the given circuits and the symbol free comparison circuits. Calculates out[i][j] = $ | \langle \psi_{\text{programs[i]}} \\ (\text{symbol_values[i]}) | \psi_{\text{other_programs[j]}} \rangle \\ |^2 $ >>> symbols = sympy.symbols('alpha beta') >>> qubits = cirq.GridQubit.rect(1, 2) >>> reference_circuits = [ ... cirq.Circuit((cirq.H**symbols[0]).on_each(qubits)), ... cirq.Circuit( ... cirq.X(qubits[0]) ** symbols[0], ... cirq.Y(qubits[1]) ** symbols[1]) ... ] >>> other_circuits = [ ... cirq.Circuit(cirq.X.on_each(qubits)), ... cirq.Circuit((cirq.Y**0.125).on_each(qubits)), ... cirq.Circuit((cirq.X**0.5).on_each(qubits)) ... ] >>> reference_tensor = tfq.convert_to_tensor(reference_circuits) >>> symbol_tensor = tf.convert_to_tensor([s.name for s in symbols]) >>> values_tensor = tf.convert_to_tensor(np.arange(4).reshape(2, 2)) >>> other_tensor = tfq.convert_to_tensor([other_circuits, other_circuits]) >>> fid = tfq.math.fidelity(reference_tensor, symbol_tensor, ... values_tensor, other_tensor) >>> fid tf.Tensor( [[ 0., 0.925, 0.25], [ 0., 0.036, 0.25]],shape=(2, 3), dtype=float32) Note: `other_programs` must not contain any free symbols. These can be resolved beforehand with `tfq.resolve_parameters`. Args: programs: `tf.Tensor` of strings with shape [batch_size] containing the string representations of the circuits 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 specificed by programs, following the ordering dictated by `symbol_names`. other_programs: `tf.Tensor` of strings with shape [batch_size, n_others] containing the string representations of the circuits with which to compute the overlap on `programs` with. Must not contain any free symbols. Returns: `tf.Tensor` with shape [batch_size, n_others] where `out[i][j]` is equal to the fidelity of `programs[i]` with `symbol_values[i]` resolved in and `other_programs[i][j]`. """ ip = inner_product_op.inner_product(programs, symbol_names, tf.cast(symbol_values, tf.float32), other_programs) return tf.math.abs(ip)**2
def test_inner_product_inputs(self): """Make sure that inner_product 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) symbol_values_array = np.array( [[resolver[symbol] for symbol in symbol_names] for resolver in resolver_batch]) other_batch = [ util.random_circuit_resolver_batch(qubits, 3)[0] for i in range(batch_size) ] with self.assertRaisesRegex(tf.errors.InvalidArgumentError, 'programs must be rank 1'): # Circuit tensor has too many dimensions. inner_product_op.inner_product( util.convert_to_tensor([circuit_batch]), symbol_names, symbol_values_array, util.convert_to_tensor(other_batch)) with self.assertRaisesRegex(tf.errors.InvalidArgumentError, 'symbol_names must be rank 1.'): # symbol_names tensor has too many dimensions. inner_product_op.inner_product( util.convert_to_tensor(circuit_batch), np.array([symbol_names]), symbol_values_array, util.convert_to_tensor(other_batch)) with self.assertRaisesRegex(tf.errors.InvalidArgumentError, 'symbol_values must be rank 2.'): # symbol_values_array tensor has too many dimensions. inner_product_op.inner_product( util.convert_to_tensor(circuit_batch), symbol_names, np.array([symbol_values_array]), util.convert_to_tensor(other_batch)) with self.assertRaisesRegex(tf.errors.InvalidArgumentError, 'symbol_values must be rank 2.'): # symbol_values_array tensor has too few dimensions. inner_product_op.inner_product( util.convert_to_tensor(circuit_batch), symbol_names, symbol_values_array[0], util.convert_to_tensor(other_batch)) with self.assertRaisesRegex(tf.errors.InvalidArgumentError, 'other_programs must be rank 2.'): # other_programs tensor has too few dimensions. inner_product_op.inner_product( util.convert_to_tensor(circuit_batch), symbol_names, symbol_values_array, util.convert_to_tensor(circuit_batch)) with self.assertRaisesRegex(tf.errors.InvalidArgumentError, 'other_programs must be rank 2.'): # pauli_sums tensor has too many dimensions. inner_product_op.inner_product( util.convert_to_tensor(circuit_batch), symbol_names, symbol_values_array, util.convert_to_tensor([[x] for x in other_batch])) with self.assertRaisesRegex(tf.errors.InvalidArgumentError, 'Unparseable proto'): # circuit tensor has the right type but invalid values. inner_product_op.inner_product(['junk'] * batch_size, symbol_names, symbol_values_array, util.convert_to_tensor(other_batch)) with self.assertRaisesRegex(tf.errors.InvalidArgumentError, 'Could not find symbol in parameter map'): # symbol_names tensor has the right type but invalid values. inner_product_op.inner_product( util.convert_to_tensor(circuit_batch), ['junk'], symbol_values_array, util.convert_to_tensor(other_batch)) with self.assertRaisesRegex(tf.errors.InvalidArgumentError, 'not found in reference circuit'): # other_programs tensor has the right type but operates on # qubits that the reference ciruit doesn't have. new_qubits = [cirq.GridQubit(5, 5), cirq.GridQubit(9, 9)] new_circuits, _ = util.random_circuit_resolver_batch( new_qubits, batch_size) inner_product_op.inner_product( util.convert_to_tensor(circuit_batch), symbol_names, symbol_values_array, util.convert_to_tensor([[x] for x in new_circuits])) with self.assertRaisesRegex(tf.errors.InvalidArgumentError, 'not found in paired circuit'): # other_programs tensor has the right type but operates on # qubits that the reference ciruit doesn't have. new_qubits = cirq.GridQubit.rect(1, n_qubits - 1) new_circuits, _ = util.random_circuit_resolver_batch( new_qubits, batch_size) inner_product_op.inner_product( util.convert_to_tensor(circuit_batch), symbol_names, symbol_values_array, util.convert_to_tensor([[x] for x in new_circuits])) with self.assertRaisesRegex(TypeError, 'Cannot convert'): # circuits tensor has the wrong type. inner_product_op.inner_product([1.0] * batch_size, symbol_names, symbol_values_array, util.convert_to_tensor(other_batch)) with self.assertRaisesRegex(TypeError, 'Cannot convert'): # symbol_names tensor has the wrong type. inner_product_op.inner_product( util.convert_to_tensor(circuit_batch), [0.1234], symbol_values_array, util.convert_to_tensor(other_batch)) with self.assertRaisesRegex(tf.errors.UnimplementedError, ''): # symbol_values tensor has the wrong type. inner_product_op.inner_product( util.convert_to_tensor(circuit_batch), symbol_names, [['junk']] * batch_size, util.convert_to_tensor(other_batch)) with self.assertRaisesRegex(TypeError, 'Cannot convert'): # other_programs tensor has the wrong type. inner_product_op.inner_product( util.convert_to_tensor(circuit_batch), symbol_names, symbol_values_array, [[1.0]] * batch_size) with self.assertRaisesRegex(TypeError, 'missing'): # we are missing an argument. # pylint: disable=no-value-for-parameter inner_product_op.inner_product( util.convert_to_tensor(circuit_batch), symbol_names, symbol_values_array) # pylint: enable=no-value-for-parameter with self.assertRaisesRegex(TypeError, 'positional arguments'): # pylint: disable=too-many-function-args inner_product_op.inner_product( util.convert_to_tensor(circuit_batch), symbol_names, symbol_values_array, util.convert_to_tensor(other_batch), []) with self.assertRaisesRegex(tf.errors.InvalidArgumentError, expected_regex='do not match'): # batch programs has wrong batch size. inner_product_op.inner_product( util.convert_to_tensor(circuit_batch), symbol_names, symbol_values_array, util.convert_to_tensor(other_batch[:int(batch_size * 0.5)])) with self.assertRaisesRegex(tf.errors.InvalidArgumentError, expected_regex='do not match'): # batch programs has wrong batch size. inner_product_op.inner_product( util.convert_to_tensor(circuit_batch), symbol_names, symbol_values_array[::int(batch_size * 0.5)], util.convert_to_tensor(other_batch)) with self.assertRaisesRegex( tf.errors.InvalidArgumentError, expected_regex='Found symbols in other_programs'): # other_programs has symbols. inner_product_op.inner_product( util.convert_to_tensor(circuit_batch), symbol_names, symbol_values_array, util.convert_to_tensor([[x] for x in circuit_batch])) res = inner_product_op.inner_product( util.convert_to_tensor(circuit_batch), symbol_names, symbol_values_array.astype(np.float64), util.convert_to_tensor(other_batch)) self.assertDTypeEqual(res, np.complex64)