def test_from_tensor(self, item): """Check from_tensor assuming convert_to_tensor works.""" item_nested_tensorized = util.convert_to_tensor([[item, item], [item, item]]) item_flat_tensorized = util.convert_to_tensor([item, item]) item_nested_cycled = util.convert_to_tensor( util.from_tensor(item_nested_tensorized)) self.assertAllEqual(item_nested_tensorized, item_nested_cycled) item_flat_cycled = util.convert_to_tensor( util.from_tensor(item_flat_tensorized)) self.assertAllEqual(item_flat_tensorized, item_flat_cycled)
def test_from_tensor_errors(self): """test that from_tensor fails when it should.""" with self.assertRaisesRegex(TypeError, expected_regex='Error decoding item'): util.from_tensor( tf.convert_to_tensor([ 'bad', serializer.serialize_circuit( cirq.Circuit()).SerializeToString() ])) with self.assertRaisesRegex(TypeError, expected_regex='Error decoding item'): util.from_tensor( tf.convert_to_tensor([ serializer.serialize_circuit( cirq.Circuit()).SerializeToString() + b'bad' ])) with self.assertRaisesRegex(TypeError, expected_regex='single type'): util.from_tensor( tf.convert_to_tensor([ serializer.serialize_circuit( cirq.Circuit()).SerializeToString(), serializer.serialize_paulisum( cirq.X(BITS[0]) + cirq.Y(BITS[1])).SerializeToString() ])) with self.assertRaisesRegex(TypeError, expected_regex='received bad type'): util.from_tensor("junk")
def test_weight_coefficient(self): """Test that scalar multiples of trivial case work.""" bit = cirq.GridQubit(0, 0) circuit = cirq.Circuit( cirq.X(bit)**(sympy.Symbol('alpha') * 2.0), cirq.Y(bit)**(sympy.Symbol('alpha') * 3.0), cirq.Z(bit)**(sympy.Symbol('alpha') * 4.0), ) inputs = util.convert_to_tensor([circuit]) symbols = tf.convert_to_tensor(['alpha']) new = tf.convert_to_tensor(['new']) res = tfq_ps_util_ops.tfq_ps_symbol_replace(inputs, symbols, new) output = util.from_tensor(res) correct_00 = cirq.Circuit( cirq.X(bit)**(sympy.Symbol('new') * 2.0), cirq.Y(bit)**(sympy.Symbol('alpha') * 3.0), cirq.Z(bit)**(sympy.Symbol('alpha') * 4.0), ) correct_01 = cirq.Circuit( cirq.X(bit)**(sympy.Symbol('alpha') * 2.0), cirq.Y(bit)**(sympy.Symbol('new') * 3.0), cirq.Z(bit)**(sympy.Symbol('alpha') * 4.0), ) correct_02 = cirq.Circuit( cirq.X(bit)**(sympy.Symbol('alpha') * 2.0), cirq.Y(bit)**(sympy.Symbol('alpha') * 3.0), cirq.Z(bit)**(sympy.Symbol('new') * 4.0), ) self.assertEqual(correct_00, output[0][0][0]) self.assertEqual(correct_01, output[0][0][1]) self.assertEqual(correct_02, output[0][0][2])
def test_simple_case(self): """Test trivial case.""" bit = cirq.GridQubit(0, 0) circuit = cirq.Circuit( cirq.X(bit)**sympy.Symbol('alpha'), cirq.Y(bit)**sympy.Symbol('alpha'), cirq.Z(bit)**sympy.Symbol('alpha'), ) inputs = util.convert_to_tensor([circuit]) symbols = tf.convert_to_tensor(['alpha']) new = tf.convert_to_tensor(['new']) res = tfq_ps_util_ops.tfq_ps_symbol_replace(inputs, symbols, new) output = util.from_tensor(res) correct_00 = cirq.Circuit( cirq.X(bit)**sympy.Symbol('new'), cirq.Y(bit)**sympy.Symbol('alpha'), cirq.Z(bit)**sympy.Symbol('alpha'), ) correct_01 = cirq.Circuit( cirq.X(bit)**sympy.Symbol('alpha'), cirq.Y(bit)**sympy.Symbol('new'), cirq.Z(bit)**sympy.Symbol('alpha'), ) correct_02 = cirq.Circuit( cirq.X(bit)**sympy.Symbol('alpha'), cirq.Y(bit)**sympy.Symbol('alpha'), cirq.Z(bit)**sympy.Symbol('new'), ) self.assertEqual(correct_00, output[0][0][0]) self.assertEqual(correct_01, output[0][0][1]) self.assertEqual(correct_02, output[0][0][2])
def test_append_circuit(self, max_n_bits, symbols, n_circuits): """Generate a bunch of circuits of different lengths acting on different numbers of qubits and append them using our op, checking that results are consistant with the native cirq method. """ base_circuits = [] circuits_to_append = [] qubits = cirq.GridQubit.rect(1, max_n_bits) other_qubits = cirq.GridQubit.rect(2, max_n_bits) base_circuits, _ = util.random_symbol_circuit_resolver_batch( qubits, symbols, n_circuits, include_scalars=False) circuits_to_append, _ = util.random_symbol_circuit_resolver_batch( other_qubits, symbols, n_circuits, include_scalars=False) serialized_base_circuits = util.convert_to_tensor(base_circuits) serialized_circuits_to_append = util.convert_to_tensor( circuits_to_append) tfq_results = tfq_utility_ops.append_circuit( serialized_base_circuits, serialized_circuits_to_append) tfq_results = util.from_tensor(tfq_results) cirq_results = [ a + b for a, b in zip(base_circuits, circuits_to_append) ] self.assertAllEqual(util.convert_to_tensor(tfq_results), util.convert_to_tensor(cirq_results))
def test_resolve_parameters_consistency_basic(self): """Compare tfq op to cirq resolving.""" qubits = cirq.GridQubit.rect(1, 4) circuit = cirq.Circuit() symbols = [] for n, q in enumerate(qubits): new_bit = sympy.Symbol("bit_{}".format(n)) circuit += cirq.X(q)**new_bit symbols.append(new_bit) symbol_names = [str(s) for s in symbols] bitstring_list = [[0, 0, 0, 0], [0, 1, 0, 1], [1, 0, 1, 1]] circuit_list = [] resolver_list = [] for bitstring in bitstring_list: resolve_dict = {} for s, b in zip(symbols, bitstring): resolve_dict[s] = b resolver_list.append(cirq.ParamResolver(resolve_dict)) circuit_list.append(circuit) test_resolved_circuits = util.from_tensor( tfq_utility_ops.resolve_parameters( util.convert_to_tensor(circuit_list), symbol_names, np.asarray(bitstring_list))) expected_resolved_circuits = [] for circuit, resolver in zip(circuit_list, resolver_list): expected_resolved_circuits.append( cirq.resolve_parameters(circuit, resolver)) for exp_c, test_c in zip(expected_resolved_circuits, test_resolved_circuits): self.assertAllEqual(exp_c, test_c)
def test_moment_preservation(self): """Test Moment-structure preservation.""" t = sympy.Symbol('t') r = sympy.Symbol('r') qubits = cirq.GridQubit.rect(1, 6) circuit_batch = [ cirq.Circuit( cirq.Moment([cirq.H(q) for q in qubits]), cirq.Moment([ cirq.X(qubits[4]), cirq.PhasedXPowGate(phase_exponent=np.random.random() * t).on(qubits[5]), cirq.ISwapPowGate(exponent=np.random.random() * t).on( qubits[0], qubits[1]), cirq.FSimGate(theta=np.random.random() * t, phi=np.random.random() * r).on( qubits[2], qubits[3]) ]), cirq.Moment([cirq.H(q) for q in qubits])) ] inputs = util.convert_to_tensor(circuit_batch) outputs = tfq_ps_util_ops.tfq_ps_decompose(inputs) decomposed_programs = util.from_tensor(outputs) # Now all programs don't have ISWAP & PhasedXPowGate because ISWAP has # 3 eigenvalues and PhasedXPowGate doesn't have _eigen_components. # Check if two programs are identical. rand_resolver = {'t': np.random.random(), 'r': np.random.random()} self.assertAllClose(cirq.unitary( cirq.resolve_parameters(circuit_batch[0], rand_resolver)), cirq.unitary( cirq.resolve_parameters(decomposed_programs[0], rand_resolver)), atol=1e-5) # Check if the Moments are conserved. max_decomposed_length = 3 n_non_decomposed_moments = 2 self.assertEqual(len(decomposed_programs[0]), n_non_decomposed_moments + max_decomposed_length) # Total length of Moments = 5 # The non-decomposed moments should be the same. self.assertEqual(decomposed_programs[0][0], circuit_batch[0][0]) self.assertEqual(decomposed_programs[0][-1], circuit_batch[0][-1]) # Check paralellized decompose gates in Moment[1]~[3]. # The target ops are replaced by the first decomposition gates. It means # the first Moment has exactly the same number of gate ops. self.assertEqual(len(decomposed_programs[0][1]), len(circuit_batch[0][1])) # From the second Moments, the Moments only have decomposition gates. # In this example, two ISwapPowGate & one PhasedXPowGate are located. # Since PhasedXPowGate, ISwapPowGate, FSimGate has 3, 2, 3 result gates # Moment[2] have 3 gate ops and Moment[3] have 2 gate ops. self.assertEqual(len(decomposed_programs[0][2]), 3) self.assertEqual(len(decomposed_programs[0][3]), 2)
def test_addcircuit_modify(self): """Test that a addcircuit layer correctly modifies input circuits.""" bits = cirq.GridQubit.rect(1, 20) circuit_a = cirq.testing.random_circuit(bits, 10, 0.9, util.get_supported_gates()) circuit_b = cirq.testing.random_circuit(bits, 10, 0.9, util.get_supported_gates()) expected_append = util.convert_to_tensor([circuit_a + circuit_b]) expected_prepend = util.convert_to_tensor([circuit_b + circuit_a]) append_layer = elementary.AddCircuit() prepend_layer = elementary.AddCircuit() actual_append = util.convert_to_tensor( util.from_tensor(append_layer(circuit_a, append=circuit_b))) actual_prepend = util.convert_to_tensor( util.from_tensor(prepend_layer(circuit_a, prepend=circuit_b))) self.assertEqual(expected_append.numpy()[0], actual_append.numpy()[0]) self.assertEqual(expected_prepend.numpy()[0], actual_prepend.numpy()[0])
def test_iswap_gate_test(self): """Test 1 ISwapPowGate decomposition.""" t = sympy.Symbol('t') qubits = cirq.GridQubit.rect(1, 2) circuit = cirq.Circuit( cirq.ISwapPowGate(exponent=np.random.random() * t).on(*qubits)) inputs = util.convert_to_tensor([circuit]) outputs = tfq_ps_util_ops.tfq_ps_decompose(inputs) decomposed_programs = util.from_tensor(outputs) rand_resolver = {'t': np.random.random()} self.assertAllClose(cirq.unitary( cirq.resolve_parameters(circuit, rand_resolver)), cirq.unitary( cirq.resolve_parameters(decomposed_programs[0], rand_resolver)), atol=1e-5)
def test_more_complex_moment_preservation(self): """Test Moment-structure preservation.""" circuit_batch = _complex_test_circuit() inputs = util.convert_to_tensor(circuit_batch) outputs = tfq_ps_util_ops.tfq_ps_decompose(inputs) decomposed_programs = util.from_tensor(outputs) # Now all programs don't have ISWAP & PhasedXPowGate because ISWAP has # 3 eigenvalues and PhasedXPowGate doesn't have _eigen_components. # Check if two programs are identical. rand_resolver = {'t': np.random.random(), 'r': np.random.random()} for i in range(3): self.assertAllClose(cirq.unitary( cirq.resolve_parameters(circuit_batch[i], rand_resolver)), cirq.unitary( cirq.resolve_parameters( decomposed_programs[i], rand_resolver)), atol=1e-5) # Check if the Moments are conserved. # Circuit 1. max_decomposed_length = 3 n_non_decomposed_moments = 2 self.assertEqual(len(decomposed_programs[0]), n_non_decomposed_moments + max_decomposed_length) # Total length of Moments = 5 # The non-decomposed moments should be the same. self.assertEqual(decomposed_programs[0][0], circuit_batch[0][0]) self.assertEqual(decomposed_programs[0][-1], circuit_batch[0][-1]) # Check paralellized decompose gates in Moment[1]~[3]. # The target ops are replaced by the first decomposition gates. It means # the first Moment has exactly the same number of gate ops. self.assertEqual(len(decomposed_programs[0][1]), len(circuit_batch[0][1])) # From the second Moments, the Moments only have decomposition gates. # In this example, two ISwapPowGate & one PhasedXPowGate are located. # Since PhasedXPowGate, ISwapPowGate, FSimGate has 3, 2, 3 result gates # Moment[2] have 3 gate ops and Moment[3] have 2 gate ops. self.assertEqual(len(decomposed_programs[0][2]), 3) self.assertEqual(len(decomposed_programs[0][3]), 2) # Circuit 2. two FSimGates. self.assertEqual(len(decomposed_programs[1]), 2 * max_decomposed_length) # Circuit 3. one PXP between two ISwapPowGates. self.assertEqual(len(decomposed_programs[2]), max_decomposed_length)
def test_phased_x_pow_gate_test(self): """Test 1 PhasedXPowGate decomposition.""" t = sympy.Symbol('t') r = sympy.Symbol('r') q = cirq.GridQubit(0, 0) circuit = cirq.Circuit( cirq.PhasedXPowGate(phase_exponent=np.random.random() * r, exponent=np.random.random() * t).on(q)) inputs = util.convert_to_tensor([circuit]) outputs = tfq_ps_util_ops.tfq_ps_decompose(inputs) decomposed_programs = util.from_tensor(outputs) rand_resolver = {'t': np.random.random(), 'r': np.random.random()} self.assertAllClose(cirq.unitary( cirq.resolve_parameters(circuit, rand_resolver)), cirq.unitary( cirq.resolve_parameters(decomposed_programs[0], rand_resolver)), atol=1e-5)
def _parse_pauli_sums(pauli_sums, n_programs, n_ops): """Helper function to parse given pauli sums to collect observable coefficients. Currently `cirq.PauliSum` is not subscriptable, which means it is not possible to construct a uniform-shape tensor whose elements are consistently matched to `cirq.PauliString` inside of given `PauliSum` because the order of `PauliString`'s can be different whenever accessed. So, the current version of _parse_pauli_sums only consider a `PauliSum` to be sampled, not a `PauliString`. The observable coefficients are then sum of the absolute value of coefficients of `PauliString`'s in the `PauliSum`. Args: pauli_sums : `tf.Tensor` of strings with shape [n_programs, n_ops] representing output observables for each program. n_programs: `tf.Tensor` of the number of programs. n_ops: `tf.Tensor` of the number of pauli sums. Returns: observable_coeff_: `tf.Tensor` of real numbers. This involves the coefficients of Pauli sum terms of the first PauliString. It is directly used to calculate probabilities. [n_programs, n_ops] """ pauli_sums = util.from_tensor(pauli_sums) def get_pauli_sum_coeff(i): def get_i_pauli_sum_coeff(j): # Because PauliSum object is not subscriptable, use for-loop. # pauli_sums[i][j] : j-th `PauliSum` of i-th program. return tf.reduce_sum( tf.abs([ pstring.coefficient.real for pstring in pauli_sums[i][j] ])) return tf.map_fn(get_i_pauli_sum_coeff, tf.range(n_ops), dtype=tf.float32) observable_coeff = tf.map_fn(get_pauli_sum_coeff, tf.range(n_programs), dtype=tf.float32) return observable_coeff
def test_resolve_parameters_consistency(self, n_qubits, symbol_names): """Compare tfq op to cirq resolving for randomized circuits.""" # Get random circuit batches qubits = cirq.GridQubit.rect(1, n_qubits) batch_size = 15 n_moments = 15 circuit_batch, resolver_batch = \ util.random_symbol_circuit_resolver_batch( qubits, symbol_names, batch_size, n_moments=n_moments, include_channels=True, include_scalars=True) # Remove one of the symbols from the resolvers symbol_names_partial = symbol_names[1:] symbol_values_array_partial = np.array( [[resolver[symbol] for symbol in symbol_names_partial] for resolver in resolver_batch]) resolver_batch_partial = [ cirq.ParamResolver( {symbol: resolver[symbol] for symbol in symbol_names_partial}) for resolver in resolver_batch ] # Resolve in two ways and compare results test_resolved_circuits = util.from_tensor( tfq_utility_ops.resolve_parameters( util.convert_to_tensor(circuit_batch), symbol_names_partial, symbol_values_array_partial)) expected_resolved_circuits = [] for circuit, resolver in zip(circuit_batch, resolver_batch_partial): expected_resolved_circuits.append( cirq.resolve_parameters(circuit, resolver)) # TODO(zaqqwerty): Find a way to eliminate parsing. for test_c, exp_c in zip(test_resolved_circuits, expected_resolved_circuits): for test_m, exp_m in zip(test_c, exp_c): for test_o, exp_o in zip(test_m, exp_m): self.assertTrue( util.gate_approx_eq(test_o.gate, exp_o.gate))
def test_weight_coefficient(self): """Test that scalar multiples of trivial case work.""" bit = cirq.GridQubit(0, 0) circuit = cirq.Circuit( cirq.X(bit)**(sympy.Symbol('alpha') * 2.4), cirq.Y(bit)**(sympy.Symbol('alpha') * 3.4), cirq.Z(bit)**(sympy.Symbol('alpha') * 4.4), ) inputs = util.convert_to_tensor([circuit]) symbols = tf.convert_to_tensor(['alpha']) new = tf.convert_to_tensor(['new']) res = tfq_ps_util_ops.tfq_ps_symbol_replace(inputs, symbols, new) output = util.from_tensor(res) correct_00 = cirq.Circuit( cirq.X(bit)**(sympy.Symbol('new') * 2.4), cirq.Y(bit)**(sympy.Symbol('alpha') * 3.4), cirq.Z(bit)**(sympy.Symbol('alpha') * 4.4), ) correct_01 = cirq.Circuit( cirq.X(bit)**(sympy.Symbol('alpha') * 2.4), cirq.Y(bit)**(sympy.Symbol('new') * 3.4), cirq.Z(bit)**(sympy.Symbol('alpha') * 4.4), ) correct_02 = cirq.Circuit( cirq.X(bit)**(sympy.Symbol('alpha') * 2.4), cirq.Y(bit)**(sympy.Symbol('alpha') * 3.4), cirq.Z(bit)**(sympy.Symbol('new') * 4.4), ) for i, c in enumerate([correct_00, correct_01, correct_02]): u1 = cirq.unitary( cirq.resolve_parameters(c, param_resolver={ 'alpha': 1.23, 'new': 4.56 })) u2 = cirq.unitary( cirq.resolve_parameters(output[0][0][i], param_resolver={ 'alpha': 1.23, 'new': 4.56 })) self.assertTrue(cirq.approx_eq(u1, u2, atol=1e-5))
def test_get_gradient_circuits(self): """Test that the correct objects are returned.""" diff = parameter_shift.ParameterShift() # Circuits to differentiate. symbols = [sympy.Symbol("s0"), sympy.Symbol("s1")] q0 = cirq.GridQubit(0, 0) q1 = cirq.GridQubit(1, 2) input_programs = util.convert_to_tensor([ cirq.Circuit( cirq.X(q0)**symbols[0], cirq.Y(q0)**symbols[0], cirq.ry(symbols[1])(q1)), cirq.Circuit(cirq.Y(q1)**symbols[1]), ]) input_symbol_names = tf.constant([str(s) for s in symbols]) input_symbol_values = tf.constant([[1.5, -2.7], [-0.3, 0.9]]) # First, for each symbol `s`, check how many times `s` appears in each # program `p`, `n_ps`. Let `n_param_gates` be the maximum of `n_ps` over # all symbols and programs. Then, the shape of `batch_programs` will be # [n_programs, n_symbols * n_param_gates * n_shifts], where `n_shifts` # is 2 because we decompose into gates with 2 eigenvalues. For row index # `p` we have for column indices between `i * n_param_gates * n_shifts` # and `(i + 1) * n_param_gates * n_shifts`, the first `n_pi * 2` # programs are parameter shifted versions of `input_programs[p]` and the # remaining programs are empty. # Here, `n_param_gates` is 2. impurity_symbol_name = "_impurity_for_param_shift" impurity_symbol = sympy.Symbol(impurity_symbol_name) expected_batch_programs_0 = util.convert_to_tensor([ cirq.Circuit( cirq.X(q0)**impurity_symbol, cirq.Y(q0)**symbols[0], cirq.ry(symbols[1])(q1)), cirq.Circuit( cirq.X(q0)**impurity_symbol, cirq.Y(q0)**symbols[0], cirq.ry(symbols[1])(q1)), cirq.Circuit( cirq.X(q0)**symbols[0], cirq.Y(q0)**impurity_symbol, cirq.ry(symbols[1])(q1)), cirq.Circuit( cirq.X(q0)**symbols[0], cirq.Y(q0)**impurity_symbol, cirq.ry(symbols[1])(q1)), cirq.Circuit( cirq.X(q0)**symbols[0], cirq.Y(q0)**symbols[0], cirq.ry(impurity_symbol)(q1)), cirq.Circuit( cirq.X(q0)**symbols[0], cirq.Y(q0)**symbols[0], cirq.ry(impurity_symbol)(q1)), cirq.Circuit(), cirq.Circuit() ]) expected_batch_programs_1 = util.convert_to_tensor([ cirq.Circuit(), cirq.Circuit(), cirq.Circuit(), cirq.Circuit(), cirq.Circuit(cirq.Y(q1)**impurity_symbol), cirq.Circuit(cirq.Y(q1)**impurity_symbol), cirq.Circuit(), cirq.Circuit() ]) expected_batch_programs = tf.stack( [expected_batch_programs_0, expected_batch_programs_1]) # The new symbols are the old ones, with an extra used for shifting. expected_new_symbol_names = tf.concat( [input_symbol_names, tf.constant([impurity_symbol_name])], 0) # The batch symbol values are the input symbol values, tiled and with # shifted values appended. Locations that have empty programs should # also have zero for the shift. # The shifted values are the original value plus 1/2 divided by the # `exponent_scalar` of the gate. expected_batch_symbol_values = tf.constant( [[[1.5, -2.7, 1.5 + 0.5], [1.5, -2.7, 1.5 - 0.5], [1.5, -2.7, 1.5 + 0.5], [1.5, -2.7, 1.5 - 0.5], [1.5, -2.7, -2.7 + np.pi / 2], [1.5, -2.7, -2.7 - np.pi / 2], [1.5, -2.7, -2.7], [1.5, -2.7, -2.7]], [[-0.3, 0.9, -0.3], [-0.3, 0.9, -0.3], [-0.3, 0.9, -0.3], [-0.3, 0.9, -0.3], [-0.3, 0.9, 0.9 + 0.5], [-0.3, 0.9, 0.9 - 0.5], [-0.3, 0.9, 0.9], [-0.3, 0.9, 0.9]]]) # Empty program locations are given zero weight. expected_batch_weights = tf.constant( [[[np.pi / 2, -np.pi / 2, np.pi / 2, -np.pi / 2], [0.5, -0.5, 0.0, 0.0]], [[0.0, 0.0, 0.0, 0.0], [np.pi / 2, -np.pi / 2, 0.0, 0.0]]]) expected_batch_mapper = tf.constant([[[0, 1, 2, 3], [4, 5, 6, 7]], [[0, 1, 2, 3], [4, 5, 6, 7]]]) (test_batch_programs, test_new_symbol_names, test_batch_symbol_values, test_batch_weights, test_batch_mapper) = diff.get_gradient_circuits( input_programs, input_symbol_names, input_symbol_values) for i in range(tf.shape(input_programs)[0]): self.assertAllEqual(util.from_tensor(expected_batch_programs[i]), util.from_tensor(test_batch_programs[i])) self.assertAllEqual(expected_new_symbol_names, test_new_symbol_names) self.assertAllClose(expected_batch_symbol_values, test_batch_symbol_values, atol=1e-5) self.assertAllClose(expected_batch_weights, test_batch_weights, atol=1e-5) self.assertAllEqual(expected_batch_mapper, test_batch_mapper)
def test_complex_pad(self): """Test trickier padding.""" bit = cirq.GridQubit(0, 0) bit2 = cirq.GridQubit(0, 1) circuit = cirq.Circuit( cirq.X(bit)**sympy.Symbol('alpha'), cirq.Y(bit)**sympy.Symbol('alpha'), cirq.Z(bit)**sympy.Symbol('alpha'), cirq.XX(bit, bit2)**sympy.Symbol('alpha')) circuit2 = cirq.Circuit( cirq.X(bit)**sympy.Symbol('beta'), cirq.Y(bit)**sympy.Symbol('beta'), cirq.Z(bit)**sympy.Symbol('beta'), cirq.XX(bit, bit2)**sympy.Symbol('alpha')) circuit3 = cirq.Circuit( cirq.X(bit)**sympy.Symbol('alpha'), cirq.Y(bit)**sympy.Symbol('alpha'), cirq.Z(bit)**sympy.Symbol('alpha'), cirq.XX(bit, bit2)**sympy.Symbol('alpha')) inputs = util.convert_to_tensor([circuit, circuit2, circuit3]) symbols = tf.convert_to_tensor(['alpha', 'beta', 'gamma']) new = tf.convert_to_tensor(['new', 'old', 'nothing']) res = tfq_ps_util_ops.tfq_ps_symbol_replace(inputs, symbols, new) output = util.from_tensor(res) correct_000 = cirq.Circuit( cirq.X(bit)**sympy.Symbol('new'), cirq.Y(bit)**sympy.Symbol('alpha'), cirq.Z(bit)**sympy.Symbol('alpha'), cirq.XX(bit, bit2)**sympy.Symbol('alpha')) correct_001 = cirq.Circuit( cirq.X(bit)**sympy.Symbol('alpha'), cirq.Y(bit)**sympy.Symbol('new'), cirq.Z(bit)**sympy.Symbol('alpha'), cirq.XX(bit, bit2)**sympy.Symbol('alpha')) correct_002 = cirq.Circuit( cirq.X(bit)**sympy.Symbol('alpha'), cirq.Y(bit)**sympy.Symbol('alpha'), cirq.Z(bit)**sympy.Symbol('new'), cirq.XX(bit, bit2)**sympy.Symbol('alpha')) correct_003 = cirq.Circuit( cirq.X(bit)**sympy.Symbol('alpha'), cirq.Y(bit)**sympy.Symbol('alpha'), cirq.Z(bit)**sympy.Symbol('alpha'), cirq.XX(bit, bit2)**sympy.Symbol('new')) self.assertEqual(correct_000, output[0][0][0]) self.assertEqual(correct_001, output[0][0][1]) self.assertEqual(correct_002, output[0][0][2]) self.assertEqual(correct_003, output[0][0][3]) self.assertEqual(correct_000, output[2][0][0]) self.assertEqual(correct_001, output[2][0][1]) self.assertEqual(correct_002, output[2][0][2]) self.assertEqual(correct_003, output[2][0][3]) correct_110 = cirq.Circuit( cirq.X(bit)**sympy.Symbol('old'), cirq.Y(bit)**sympy.Symbol('beta'), cirq.Z(bit)**sympy.Symbol('beta'), cirq.XX(bit, bit2)**sympy.Symbol('alpha')) correct_111 = cirq.Circuit( cirq.X(bit)**sympy.Symbol('beta'), cirq.Y(bit)**sympy.Symbol('old'), cirq.Z(bit)**sympy.Symbol('beta'), cirq.XX(bit, bit2)**sympy.Symbol('alpha')) correct_112 = cirq.Circuit( cirq.X(bit)**sympy.Symbol('beta'), cirq.Y(bit)**sympy.Symbol('beta'), cirq.Z(bit)**sympy.Symbol('old'), cirq.XX(bit, bit2)**sympy.Symbol('alpha')) correct_113 = cirq.Circuit() self.assertEqual(correct_110, output[1][1][0]) self.assertEqual(correct_111, output[1][1][1]) self.assertEqual(correct_112, output[1][1][2]) self.assertEqual(correct_113, output[1][1][3]) correct_100 = cirq.Circuit( cirq.X(bit)**sympy.Symbol('beta'), cirq.Y(bit)**sympy.Symbol('beta'), cirq.Z(bit)**sympy.Symbol('beta'), cirq.XX(bit, bit2)**sympy.Symbol('new')) correct_101 = cirq.Circuit() correct_102 = cirq.Circuit() correct_103 = cirq.Circuit() self.assertEqual(correct_100, output[1][0][0]) self.assertEqual(correct_101, output[1][0][1]) self.assertEqual(correct_102, output[1][0][2]) self.assertEqual(correct_103, output[1][0][3]) correct_220 = cirq.Circuit() correct_221 = cirq.Circuit() correct_222 = cirq.Circuit() correct_223 = cirq.Circuit() self.assertEqual(correct_220, output[2][2][0]) self.assertEqual(correct_221, output[2][2][1]) self.assertEqual(correct_222, output[2][2][2]) self.assertEqual(correct_223, output[2][2][3]) correct = cirq.Circuit() for i in range(3): for j in range(3): for k in range(3): if i != j and (not (i == 2 and j == 0)) \ and (not (i == 1 and j == 0)): self.assertEqual(correct, output[i][j][k])
def test_decompose_with_complex_circuit(self): """Test decompose with complex circuit.""" names = ['CLAE', 'HRYV', 'IRKB', 'LKRV', 'PJOU', 'CJKX', 'NASW'] # Test circuit has a Moment with 1) FSimGate & PhasedXPowGate, # 2) PhasedXPowGate & ISwapPowGate and 3) FSimGate & ISwapPowGate. # Be careful, they are not decomposed if not parameterized. circuit_batch = [ cirq.Circuit([ cirq.Moment(operations=[ cirq.FSimGate(theta=0.10338130973488413 * sympy.Symbol('CLAE'), phi=0.10338130973488413 * sympy.Symbol('IRKB')). on(cirq.GridQubit(0, 2), cirq.GridQubit(0, 3)), cirq.PhasedXPowGate(phase_exponent=1.0, exponent=0.86426029696045281 * sympy.Symbol('HRYV')).on( cirq.GridQubit(0, 1)), ]), cirq.Moment(operations=[ cirq.Y.on(cirq.GridQubit(0, 3)), cirq.Z.on(cirq.GridQubit(0, 0)), cirq.FSimGate(theta=1, phi=1).on(cirq.GridQubit(0, 1), cirq.GridQubit(0, 2)), ]), cirq.Moment(operations=[ (cirq.CNOT**(0.92874230274398684 * sympy.Symbol('IRKB')) ).on(cirq.GridQubit(0, 1), cirq.GridQubit(0, 2)), ]), cirq.Moment(operations=[ cirq.PhasedXPowGate(phase_exponent=sympy.Symbol('PJOU'), exponent=0.2081415255258906 * sympy.Symbol('LKRV')).on( cirq.GridQubit(0, 2)), (cirq.ISWAP**(0.32860954996781722 * sympy.Symbol('PJOU')) ).on(cirq.GridQubit(0, 1), cirq.GridQubit(0, 3)), ]), cirq.Moment(operations=[ cirq.PhasedXPowGate(phase_exponent=sympy.Symbol('CJKX')).on( cirq.GridQubit(0, 1)), cirq.ZZ.on(cirq.GridQubit(0, 0), cirq.GridQubit(0, 3)), (cirq.X**(0.6826594585474709 * sympy.Symbol('HRYV'))).on(cirq.GridQubit(0, 2)), ]), cirq.Moment(operations=[ (cirq.ZZ**(0.18781276022427218 * sympy.Symbol('PJOU')) ).on(cirq.GridQubit(0, 0), cirq.GridQubit(0, 3)), ]), cirq.Moment(operations=[ cirq.Y.on(cirq.GridQubit(0, 0)), ]), cirq.Moment(operations=[ cirq.FSimGate(theta=0.13793763138552417 * sympy.Symbol('CJKX'), phi=0.13793763138552417 * sympy.Symbol('PJOU')). on(cirq.GridQubit(0, 2), cirq.GridQubit(0, 3)), (cirq.ISWAP**(0.028165738453673095 * sympy.Symbol('NASW')) ).on(cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)), ]), cirq.Moment(operations=[ cirq.FSimGate(theta=0.74356520426349459 * sympy.Symbol('CJKX'), phi=0.74356520426349459 * sympy.Symbol('NASW')). on(cirq.GridQubit(0, 3), cirq.GridQubit(0, 0)), ]), cirq.Moment(operations=[ cirq.CNOT.on(cirq.GridQubit(0, 0), cirq.GridQubit(0, 2)), cirq.SWAP.on(cirq.GridQubit(0, 3), cirq.GridQubit(0, 1)), ]), cirq.Moment(operations=[ cirq.H.on(cirq.GridQubit(0, 3)), cirq.H.on(cirq.GridQubit(0, 2)), cirq.CNOT.on(cirq.GridQubit(0, 1), cirq.GridQubit(0, 0)), ]), cirq.Moment(operations=[ cirq.CNOT.on(cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)), cirq.YY.on(cirq.GridQubit(0, 2), cirq.GridQubit(0, 3)), ]), cirq.Moment(operations=[ cirq.CZ.on(cirq.GridQubit(0, 1), cirq.GridQubit(0, 0)), cirq.CNOT.on(cirq.GridQubit(0, 2), cirq.GridQubit(0, 3)), ]), cirq.Moment(operations=[ cirq.FSimGate(theta=1, phi=1).on(cirq.GridQubit(0, 0), cirq.GridQubit(0, 2)), cirq.CNOT.on(cirq.GridQubit(0, 3), cirq.GridQubit(0, 1)), ]), cirq.Moment(operations=[ cirq.FSimGate(theta=1, phi=1).on(cirq.GridQubit(0, 0), cirq.GridQubit(0, 3)), cirq.SWAP.on(cirq.GridQubit(0, 2), cirq.GridQubit(0, 1)), ]), cirq.Moment(operations=[ cirq.Y.on(cirq.GridQubit(0, 0)), cirq.PhasedXPowGate( phase_exponent=1.0).on(cirq.GridQubit(0, 2)), cirq.FSimGate(theta=1, phi=1).on(cirq.GridQubit(0, 1), cirq.GridQubit(0, 3)), ]), ]) ] # Decompose programs. inputs = util.convert_to_tensor(circuit_batch) outputs = tfq_ps_util_ops.tfq_ps_decompose(inputs) decomposed_programs = util.from_tensor(outputs) self.assertEqual(len(decomposed_programs), len(circuit_batch)) # Original programs has parameterized ISP, PXP, FSIM, but this result # has no such gates at all. All parameterized gates have at most two # eigenvalues. There are still ISwap and PhasedX(1.0) because they are # not parameterized, which doesn't affect ParameterShift differentiation # at all. for program in decomposed_programs: for moment in program: for gate_op in moment: # Consider parameterized gates only if cirq.is_parameterized(gate_op.gate): # Check I. The gate should have _eigen_components. self.assertTrue( hasattr(gate_op.gate, '_eigen_components')) # Check II. The gate should have two eigen values. self.assertEqual(len(gate_op.gate._eigen_components()), 2, gate_op.gate) # Now all programs don't have ISWAP & PhasedXPowGate because ISWAP has # 3 eigenvalues and PhasedXPowGate doesn't have _eigen_components. # Check if two programs are identical. rand_resolver = {name: np.random.random() for name in names} self.assertAllClose(cirq.unitary( cirq.resolve_parameters(circuit_batch[0], rand_resolver)), cirq.unitary( cirq.resolve_parameters(decomposed_programs[0], rand_resolver)), atol=1e-5)
def test_resolve_parameters_consistency(self, n_qubits, symbol_names): """Compare tfq op to cirq resolving for randomized circuits.""" # Get random circuit batches qubits = cirq.GridQubit.rect(1, n_qubits) batch_size = 15 n_moments = 15 circuit_batch, resolver_batch = \ util.random_symbol_circuit_resolver_batch( qubits, symbol_names, batch_size, n_moments) # Remove one of the symbols from the resolvers symbol_names_partial = symbol_names[1:] symbol_values_array_partial = np.array( [[resolver[symbol] for symbol in symbol_names_partial] for resolver in resolver_batch]) resolver_batch_partial = [ cirq.ParamResolver( {symbol: resolver[symbol] for symbol in symbol_names_partial}) for resolver in resolver_batch ] # Resolve in two ways and compare results test_resolved_circuits = util.from_tensor( tfq_utility_ops.resolve_parameters( util.convert_to_tensor(circuit_batch), symbol_names_partial, symbol_values_array_partial)) expected_resolved_circuits = [] for circuit, resolver in zip(circuit_batch, resolver_batch_partial): expected_resolved_circuits.append( cirq.resolve_parameters(circuit, resolver)) # TODO(zaqqwerty): Find a way to eliminate parsing. for test_c, exp_c in zip(test_resolved_circuits, expected_resolved_circuits): for test_m, exp_m in zip(test_c, exp_c): for test_o, exp_o in zip(test_m, exp_m): tg = test_o.gate eg = exp_o.gate self.assertEqual(type(tg), type(eg)) # TODO(zaqqwerty): simplify parsing when cirq build parser # see core/serialize/serializer.py if isinstance(tg, cirq.IdentityGate): # all identity gates are the same continue elif isinstance(tg, cirq.EigenGate): self._compare_gate_parameters(tg._global_shift, eg._global_shift) self._compare_gate_parameters(tg._exponent, eg._exponent) elif isinstance(tg, cirq.FSimGate): self._compare_gate_parameters(tg.theta, eg.theta) self._compare_gate_parameters(tg.phi, eg.phi) elif isinstance( tg, (cirq.PhasedXPowGate, cirq.PhasedISwapPowGate)): self._compare_gate_parameters(tg._global_shift, eg._global_shift) self._compare_gate_parameters(tg._exponent, eg._exponent) self._compare_gate_parameters(tg._phase_exponent, eg._phase_exponent) else: self.assertTrue(False, msg="Some gate in the randomizer " "is not being checked: " "{}".format(type(tg)))