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_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_error(self): """Ensure that errors happen with bad inputs.""" bit = cirq.GridQubit(0, 0) circuit = cirq.Circuit(cirq.X(bit)**(sympy.Symbol('alpha') * 2)) inputs = util.convert_to_tensor([[circuit]]) symbols = tf.convert_to_tensor(['test']) replacements = tf.convert_to_tensor(['nothing']) with self.assertRaisesRegex(Exception, expected_regex='rank 1. Got rank 2.'): tfq_ps_util_ops.tfq_ps_symbol_replace(inputs, symbols, replacements) inputs = tf.convert_to_tensor(['junk']) with self.assertRaisesRegex(Exception, expected_regex='Unparseable proto:'): tfq_ps_util_ops.tfq_ps_symbol_replace(inputs, symbols, replacements) inputs = util.convert_to_tensor([circuit]) symbols = tf.convert_to_tensor([['test']]) replacements = tf.convert_to_tensor(['nothing']) with self.assertRaisesRegex(Exception, expected_regex='rank 1. Got rank 2.'): tfq_ps_util_ops.tfq_ps_symbol_replace(inputs, symbols, replacements) symbols = tf.convert_to_tensor(['test']) replacements = tf.convert_to_tensor([['nothing']]) with self.assertRaisesRegex(Exception, expected_regex='rank 1. Got rank 2.'): tfq_ps_util_ops.tfq_ps_symbol_replace(inputs, symbols, replacements) symbols = tf.convert_to_tensor(['test']) replacements = tf.convert_to_tensor(['nothing', 'too long']) with self.assertRaisesRegex( Exception, expected_regex= 'symbols.shape is not equal to replacement_symbols.shape'): tfq_ps_util_ops.tfq_ps_symbol_replace(inputs, symbols, replacements)
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_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 parse_programs(programs, symbol_names, symbol_values, n_symbols, n_shifts=2): """Helper function to get parameter-shifted programs after parsing programs. It follows: 1. Decomposes given programs with `tfq_ps_decompose` c++ op. 2. Construct new_programs with parameter-shifted copies of decomposed programs by `tfq_ps_symbol_replace` c++ op. 3. Weights and shifts are also obtained by `tfq_ps_weights_from_symbols` 3. Transpose the results to fed them into TensorFlow Quantum simulator. Args: programs: `tf.Tensor` of strings with shape [n_programs] containing the string representations of the circuits to be executed. symbol_names: `tf.Tensor` of strings with shape [n_symbols], 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 [n_programs, n_symbols] specifying parameter values to resolve into the circuits specified by programs, following the ordering dictated by `symbol_names`. n_symbols: `tf.Tensor` of a positive integer representing the number of symbols. n_shifts: `tf.Tensor` of a positive integer representing the number of parameter-shift terms. Defaults to 2. Returns: new_programs: the new programs whose program has only one gate with impurity parameter-shift symbol name. [n_symbols, n_programs, n_param_gates, n_shifts] weights: parameter-shift coefficients of estimated observables. [n_symbols, n_programs, n_param_gates, n_shifts] shifts: parameter-shifted values (= matrix of symbol_values +/-shift) [n_symbols, n_programs, n_param_gates, n_shifts] n_param_gates: bypass of input n_param_gates to export it outside """ decomposed_programs = tfq_ps_util_ops.tfq_ps_decompose(programs) delta_eig = 2.0 # Collecting doped programs with impurity sympy.Symbol from all programs # with parameterized gates. impurity = tf.tile(tf.convert_to_tensor([PARAMETER_IMPURITY_NAME]), [n_symbols]) symbols = tf.convert_to_tensor(symbol_names) # Doping impurity sympy.Symbol into programs per gate per symbol. new_programs = tf.tile( tf.expand_dims(tf.transpose( tfq_ps_util_ops.tfq_ps_symbol_replace(decomposed_programs, symbols, impurity), [1, 0, 2]), axis=-1), [1, 1, 1, n_shifts]) n_param_gates = tf.cast(tf.gather(tf.shape(new_programs), 2), dtype=tf.int32) # This is a tensor of the `exponent_scalar`s of the shifted gates. coeff = tf.expand_dims(tf.transpose( tfq_ps_util_ops.tfq_ps_weights_from_symbols(decomposed_programs, symbols), [1, 0, 2]), axis=-1) weights_plus = coeff * np.pi * 0.5 * 0.5 * delta_eig weights = tf.concat([weights_plus, -weights_plus], axis=-1) shifts_plus = tf.math.divide_no_nan(tf.math.divide(1.0, delta_eig), coeff) val = tf.tile( tf.expand_dims(tf.expand_dims(tf.transpose(symbol_values, [1, 0]), axis=-1), axis=-1), [1, 1, n_param_gates, n_shifts]) shifts = val + tf.concat([shifts_plus, -shifts_plus], axis=-1) return new_programs, weights, shifts, n_param_gates