def test_error(self): """Ensure if a symbol can't be found the op errors.""" bit = cirq.GridQubit(0, 0) circuit = cirq.Circuit(cirq.X(bit)**(sympy.Symbol('delta') * 2)) inputs = util.convert_to_tensor([circuit]) symbols = tf.convert_to_tensor(['alpha', 'delta']) tfq_ps_util_ops.tfq_ps_weights_from_symbols(inputs, symbols) symbols = tf.convert_to_tensor(['alpha']) with self.assertRaisesRegex(Exception, expected_regex='sympy.Symbol'): tfq_ps_util_ops.tfq_ps_weights_from_symbols(inputs, symbols) symbols = tf.convert_to_tensor([['delta']]) with self.assertRaisesRegex(Exception, expected_regex='rank 1. Got rank 2.'): tfq_ps_util_ops.tfq_ps_weights_from_symbols(inputs, symbols) inputs = tf.convert_to_tensor(['junk']) symbols = tf.convert_to_tensor(['delta']) with self.assertRaisesRegex(Exception, expected_regex='Unparseable proto:'): tfq_ps_util_ops.tfq_ps_weights_from_symbols(inputs, symbols) inputs = util.convert_to_tensor([[circuit]]) with self.assertRaisesRegex(Exception, expected_regex='rank 1. Got rank 2.'): tfq_ps_util_ops.tfq_ps_weights_from_symbols(inputs, symbols)
def test_out_of_order(self): """Test that discovery order of symbols in circuits doesn't matter.""" bit = cirq.GridQubit(0, 0) circuit = cirq.Circuit( cirq.X(bit)**(sympy.Symbol('alpha') * 2), cirq.Y(bit)**(sympy.Symbol('beta') * 3)) inputs = util.convert_to_tensor([circuit]) symbols = tf.convert_to_tensor(['alpha', 'beta']) res = tfq_ps_util_ops.tfq_ps_weights_from_symbols(inputs, symbols) self.assertAllClose(res, np.array([[[2.0], [3.0]]])) symbols = tf.convert_to_tensor(['beta', 'alpha']) res = tfq_ps_util_ops.tfq_ps_weights_from_symbols(inputs, symbols) self.assertAllClose(res, np.array([[[3.0], [2.0]]]))
def test_padding_with_non_parameterized_gates(self): """Ensure that the padding is correct in a complex example.""" bit = cirq.GridQubit(0, 0) circuits = [ cirq.Circuit( cirq.X(bit)**(sympy.Symbol('alpha') * 2.0), cirq.Y(bit)**3.0, cirq.Z(bit)**(sympy.Symbol('beta') * 4.0), ), cirq.Circuit( cirq.X(bit)**(sympy.Symbol('alpha') * 2.0), cirq.Y(bit)**(sympy.Symbol('beta') * 3.0), cirq.Z(bit)**4.0, ), cirq.Circuit( cirq.X(bit)**2.0, cirq.Y(bit)**(sympy.Symbol('beta') * 3.0), cirq.Z(bit)**(sympy.Symbol('gamma') * 4.0), ) ] inputs = util.convert_to_tensor(circuits) symbols = tf.convert_to_tensor(['alpha', 'beta', 'gamma']) res = tfq_ps_util_ops.tfq_ps_weights_from_symbols(inputs, symbols) self.assertAllClose( res, np.array([[[2.0], [4.0], [0.0]], [[2.0], [3.0], [0.0]], [[0.0], [3.0], [4.0]]]))
def test_ignorance(self): """Test ignorance of ISP, PXP, FSIM gates.""" circuit_batch = _complex_test_circuit() inputs = util.convert_to_tensor(circuit_batch) symbols = tf.convert_to_tensor(['r', 't']) res = tfq_ps_util_ops.tfq_ps_weights_from_symbols(inputs, symbols) # Because there are no weights to be gathered, the last dimension = 0 self.assertAllClose(tf.shape(res), [len(circuit_batch), 2, 0])
def test_rotation_gates(self): """Test that rotation gates work.""" bit = cirq.GridQubit(0, 0) circuit = cirq.Circuit(cirq.rx(sympy.Symbol('alpha') * 5.0)(bit)) inputs = util.convert_to_tensor([circuit]) symbols = tf.convert_to_tensor(['alpha']) res = tfq_ps_util_ops.tfq_ps_weights_from_symbols(inputs, symbols) self.assertAllClose(res, np.array([[[5.0 / np.pi]]]))
def test_simple(self): """Ensure that weight extraction works.""" 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(['alpha']) res = tfq_ps_util_ops.tfq_ps_weights_from_symbols(inputs, symbols) self.assertAllClose(res, np.array([[[2.0]]]))
def test_empty(self): """Test empty circuit. and symbol free circuit. does nothing.""" bit = cirq.GridQubit(0, 0) circuit = cirq.Circuit(cirq.X(bit)) circuit2 = cirq.Circuit() inputs = util.convert_to_tensor([circuit, circuit2]) symbols = tf.convert_to_tensor(['alpha']) res = tfq_ps_util_ops.tfq_ps_weights_from_symbols(inputs, symbols) self.assertAllClose(res, np.array([[[]], [[]]]))
def test_many_symbols(self): """Ensure that padding with few values and many symbols works.""" bit = cirq.GridQubit(0, 0) circuits = [ cirq.Circuit(cirq.X(bit)**(sympy.Symbol('alpha') * 2.0)), cirq.Circuit(cirq.X(bit)**(sympy.Symbol('beta') * 6)), cirq.Circuit(cirq.X(bit)**(sympy.Symbol('alpha') * 5.0)), cirq.Circuit(cirq.X(bit)**(sympy.Symbol('gamma') * 8)), cirq.Circuit(cirq.X(bit)**(sympy.Symbol('delta') * 9)) ] inputs = util.convert_to_tensor(circuits) symbols = tf.convert_to_tensor(['alpha', 'beta', 'gamma', 'delta']) res = tfq_ps_util_ops.tfq_ps_weights_from_symbols(inputs, symbols) self.assertAllClose( res, np.array([[[2.0], [0.0], [0.0], [0.0]], [[0.0], [6.0], [0.0], [0.0]], [[5.0], [0.0], [0.0], [0.0]], [[0.0], [0.0], [8.0], [0.0]], [[0.0], [0.0], [0.0], [9.0]]]))
def test_many_values(self): """Ensure that padding with few symbols and many values works.""" bit = cirq.GridQubit(0, 0) circuits = [ cirq.Circuit( cirq.X(bit)**(sympy.Symbol('alpha') * 2.0), cirq.Y(bit)**(sympy.Symbol('alpha') * 3.0), cirq.Z(bit)**(sympy.Symbol('alpha')), cirq.X(bit)**(sympy.Symbol('alpha') * 4.0)), cirq.Circuit(cirq.X(bit)**(sympy.Symbol('alpha') * 9.0)), cirq.Circuit(cirq.X(bit)**sympy.Symbol('beta')) ] inputs = util.convert_to_tensor(circuits) symbols = tf.convert_to_tensor(['alpha', 'beta']) res = tfq_ps_util_ops.tfq_ps_weights_from_symbols(inputs, symbols) self.assertAllClose( res, np.array([[[2.0, 3.0, 1.0, 4.0], [0.0, 0.0, 0.0, 0.0]], [[9.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]], [[0.0, 0.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0]]]))
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