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_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 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 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