def operations(self, qubits: Sequence[cirq.QubitId]) -> cirq.OP_TREE: """Produce the operations of the ansatz circuit.""" # TODO implement asymmetric ansatzes? for i in range(self.iterations): suffix = '-{}'.format(i) if self.iterations > 1 else '' # Apply one- and two-body interactions with a swap network that # reverses the order of the modes def one_and_two_body_interaction(p, q, a, b) -> cirq.OP_TREE: if 'T{}_{}'.format(p, q) + suffix in self.params: yield XXYYGate( half_turns=self.params['T{}_{}'.format(p, q) + suffix]).on(a, b) if 'W{}_{}'.format(p, q) + suffix in self.params: yield YXXYGate( half_turns=self.params['W{}_{}'.format(p, q) + suffix]).on(a, b) if 'V{}_{}'.format(p, q) + suffix in self.params: yield cirq.Rot11Gate( half_turns=self.params['V{}_{}'.format(p, q) + suffix]).on(a, b) yield swap_network(qubits, one_and_two_body_interaction, fermionic=True) qubits = qubits[::-1] # Apply one-body potential yield (cirq.RotZGate(half_turns=self.params['U{}'.format(p) + suffix]).on(qubits[p]) for p in range(len(qubits)) if 'U{}'.format(p) + suffix in self.params) # Apply one- and two-body interactions again. This time, reorder # them so that the entire iteration is symmetric def one_and_two_body_interaction_reversed_order(p, q, a, b) -> cirq.OP_TREE: if 'V{}_{}'.format(p, q) + suffix in self.params: yield cirq.Rot11Gate( half_turns=self.params['V{}_{}'.format(p, q) + suffix]).on(a, b) if 'W{}_{}'.format(p, q) + suffix in self.params: yield YXXYGate( half_turns=self.params['W{}_{}'.format(p, q) + suffix]).on(a, b) if 'T{}_{}'.format(p, q) + suffix in self.params: yield XXYYGate( half_turns=self.params['T{}_{}'.format(p, q) + suffix]).on(a, b) yield swap_network(qubits, one_and_two_body_interaction_reversed_order, fermionic=True, offset=True) qubits = qubits[::-1]
def operations(self, qubits): param_set = set(self.params()) for i in range(self.iterations): # Apply one- and two-body interactions with a swap network that # reverses the order of the modes def one_and_two_body_interaction(p, q, a, b) -> cirq.OP_TREE: tv_symbol = LetterWithSubscripts('Tv', i) t_symbol = LetterWithSubscripts('T', p, q, i) w_symbol = LetterWithSubscripts('W', p, q, i) v_symbol = LetterWithSubscripts('V', p, q, i) if custom_is_vertical_edge(p, q, self.hubbard.lattice.x_dimension, self.hubbard.lattice.y_dimension, self.hubbard.lattice.periodic): yield cirq.ISwapPowGate(exponent=-tv_symbol).on(a, b) if t_symbol in param_set: yield cirq.ISwapPowGate(exponent=-t_symbol).on(a, b) if w_symbol in param_set: yield cirq.PhasedISwapPowGate(exponent=w_symbol).on(a, b) if v_symbol in param_set: yield cirq.CZPowGate(exponent=v_symbol).on(a, b) yield swap_network(qubits, one_and_two_body_interaction, fermionic=True) qubits = qubits[::-1] # Apply one- and two-body interactions again. This time, reorder # them so that the entire iteration is symmetric def one_and_two_body_interaction_reversed_order(p, q, a, b) -> cirq.OP_TREE: tv_symbol = LetterWithSubscripts('Tv', i) t_symbol = LetterWithSubscripts('T', p, q, i) w_symbol = LetterWithSubscripts('W', p, q, i) v_symbol = LetterWithSubscripts('V', p, q, i) if v_symbol in param_set: yield cirq.CZPowGate(exponent=v_symbol).on(a, b) if w_symbol in param_set: yield cirq.PhasedISwapPowGate(exponent=w_symbol).on(a, b) if t_symbol in param_set: yield cirq.ISwapPowGate(exponent=-t_symbol).on(a, b) if custom_is_vertical_edge(p, q, self.hubbard.lattice.x_dimension, self.hubbard.lattice.y_dimension, self.hubbard.lattice.periodic): yield cirq.ISwapPowGate(exponent=-tv_symbol).on(a, b) yield swap_network(qubits, one_and_two_body_interaction_reversed_order, fermionic=True, offset=True) qubits = qubits[::-1]
def trotter_step( self, qubits: Sequence[cirq.QubitId], time: float, control_qubit: Optional[cirq.QubitId] = None) -> cirq.OP_TREE: n_qubits = len(qubits) # Apply one- and two-body interactions for half of the full time def one_and_two_body_interaction(p, q, a, b) -> cirq.OP_TREE: yield ControlledXXYYGate(duration=0.5 * self.hamiltonian.one_body[p, q].real * time).on(control_qubit, a, b) yield ControlledYXXYGate(duration=0.5 * self.hamiltonian.one_body[p, q].imag * time).on(control_qubit, a, b) yield Rot111Gate(rads=-self.hamiltonian.two_body[p, q] * time).on( control_qubit, a, b) yield swap_network(qubits, one_and_two_body_interaction, fermionic=True) qubits = qubits[::-1] # Apply one-body potential for the full time yield (cirq.Rot11Gate(rads=-self.hamiltonian.one_body[i, i].real * time).on(control_qubit, qubits[i]) for i in range(n_qubits)) # Apply one- and two-body interactions for half of the full time # This time, reorder the operations so that the entire Trotter step is # symmetric def one_and_two_body_interaction_reverse_order(p, q, a, b) -> cirq.OP_TREE: yield Rot111Gate(rads=-self.hamiltonian.two_body[p, q] * time).on( control_qubit, a, b) yield ControlledYXXYGate(duration=0.5 * self.hamiltonian.one_body[p, q].imag * time).on(control_qubit, a, b) yield ControlledXXYYGate(duration=0.5 * self.hamiltonian.one_body[p, q].real * time).on(control_qubit, a, b) yield swap_network(qubits, one_and_two_body_interaction_reverse_order, fermionic=True, offset=True) # Apply phase from constant term yield cirq.RotZGate(rads=-self.hamiltonian.constant * time).on(control_qubit)
def operations(self, qubits: Sequence[cirq.Qid]) -> cirq.OP_TREE: """Produce the operations of the ansatz circuit.""" # TODO implement asymmetric ansatz param_set = set(self.params()) for i in range(self.iterations): # Apply one- and two-body interactions with a swap network that # reverses the order of the modes def one_and_two_body_interaction(p, q, a, b) -> cirq.OP_TREE: t_symbol = LetterWithSubscripts('T', p, q, i) w_symbol = LetterWithSubscripts('W', p, q, i) v_symbol = LetterWithSubscripts('V', p, q, i) if t_symbol in param_set: yield XXYYPowGate(exponent=t_symbol).on(a, b) if w_symbol in param_set: yield YXXYPowGate(exponent=w_symbol).on(a, b) if v_symbol in param_set: yield cirq.CZPowGate(exponent=v_symbol).on(a, b) yield swap_network(qubits, one_and_two_body_interaction, fermionic=True) qubits = qubits[::-1] # Apply one-body potential for p in range(len(qubits)): u_symbol = LetterWithSubscripts('U', p, i) if u_symbol in param_set: yield cirq.ZPowGate(exponent=u_symbol).on(qubits[p]) # Apply one- and two-body interactions again. This time, reorder # them so that the entire iteration is symmetric def one_and_two_body_interaction_reversed_order(p, q, a, b) -> cirq.OP_TREE: t_symbol = LetterWithSubscripts('T', p, q, i) w_symbol = LetterWithSubscripts('W', p, q, i) v_symbol = LetterWithSubscripts('V', p, q, i) if v_symbol in param_set: yield cirq.CZPowGate(exponent=v_symbol).on(a, b) if w_symbol in param_set: yield YXXYPowGate(exponent=w_symbol).on(a, b) if t_symbol in param_set: yield XXYYPowGate(exponent=t_symbol).on(a, b) yield swap_network(qubits, one_and_two_body_interaction_reversed_order, fermionic=True, offset=True) qubits = qubits[::-1]
def finish(self, qubits: Sequence[cirq.QubitId], n_steps: int, control_qubit: Optional[cirq.QubitId] = None, omit_final_swaps: bool = False) -> cirq.OP_TREE: if not omit_final_swaps: # If the number of fermionic swap networks was odd, # swap the modes back if n_steps & 1: yield swap_network(qubits, fermionic=True) # If the total number of swap networks was odd, # swap the qubits back if len(self.eigenvalues) & 1: yield swap_network(qubits)
def trotter_step( self, qubits: Sequence[cirq.QubitId], time: float, control_qubit: Optional[cirq.QubitId] = None) -> cirq.OP_TREE: n_qubits = len(qubits) # Apply one- and two-body interactions for the full time def one_and_two_body_interaction(p, q, a, b) -> cirq.OP_TREE: yield XXYYGate(duration=self.hamiltonian.one_body[p, q].real * time).on(a, b) yield YXXYGate(duration=self.hamiltonian.one_body[p, q].imag * time).on(a, b) yield cirq.Rot11Gate(rads=-2 * self.hamiltonian.two_body[p, q] * time).on(a, b) yield swap_network(qubits, one_and_two_body_interaction, fermionic=True) qubits = qubits[::-1] # Apply one-body potential for the full time yield (cirq.RotZGate(rads=-self.hamiltonian.one_body[i, i].real * time).on(qubits[i]) for i in range(n_qubits))
def trotter_step(self, qubits: Sequence[cirq.Qid], time: float, control_qubit: Optional[cirq.Qid] = None) -> cirq.OP_TREE: n_qubits = len(qubits) if not isinstance(control_qubit, cirq.Qid): raise TypeError('Control qudit must be specified.') # Simulate the two-body terms for the full time def two_body_interaction(p, q, a, b) -> cirq.OP_TREE: yield rot111(-2 * self.hamiltonian.two_body[p, q] * time).on( cast(cirq.Qid, control_qubit), a, b) yield swap_network(qubits, two_body_interaction) # The qubit ordering has been reversed qubits = qubits[::-1] # Rotate to the basis in which the one-body term is diagonal yield cirq.inverse( bogoliubov_transform(qubits, self.basis_change_matrix)) # Simulate the one-body terms for the full time yield (rot11(rads=-self.orbital_energies[i] * time).on( control_qubit, qubits[i]) for i in range(n_qubits)) # Rotate back to the computational basis yield bogoliubov_transform(qubits, self.basis_change_matrix) # Apply phase from constant term yield cirq.rz(rads=-self.hamiltonian.constant * time).on(control_qubit)
def trotter_step(self, qubits: Sequence[cirq.Qid], time: float, control_qubit: Optional[cirq.Qid] = None) -> cirq.OP_TREE: n_qubits = len(qubits) # Apply one- and two-body interactions for the full time def one_and_two_body_interaction(p, q, a, b) -> cirq.OP_TREE: yield CRxxyy(self.hamiltonian.one_body[p, q].real * time).on( control_qubit, a, b) yield CRyxxy(self.hamiltonian.one_body[p, q].imag * time).on( control_qubit, a, b) yield rot111(-2 * self.hamiltonian.two_body[p, q] * time).on( control_qubit, a, b) yield swap_network(qubits, one_and_two_body_interaction, fermionic=True) qubits = qubits[::-1] # Apply one-body potential for the full time yield (rot11(rads=-self.hamiltonian.one_body[i, i].real * time).on( control_qubit, qubits[i]) for i in range(n_qubits)) # Apply phase from constant term yield cirq.rz(rads=-self.hamiltonian.constant * time).on(control_qubit)
def trotter_step( self, qubits: Sequence[cirq.QubitId], time: float, control_qubit: Optional[cirq.QubitId] = None) -> cirq.OP_TREE: n_qubits = len(qubits) # Simulate the one-body terms for half of the full time yield (cirq.Rz(rads=-0.5 * self.orbital_energies[i] * time).on( qubits[i]) for i in range(n_qubits)) # Rotate to the computational basis yield bogoliubov_transform(qubits, self.basis_change_matrix) # Simulate the two-body terms for the full time def two_body_interaction(p, q, a, b) -> cirq.OP_TREE: yield rot11(rads=-2 * self.hamiltonian.two_body[p, q] * time).on( a, b) yield swap_network(qubits, two_body_interaction) # The qubit ordering has been reversed qubits = qubits[::-1] # Rotate back to the basis in which the one-body term is diagonal yield cirq.inverse( bogoliubov_transform(qubits, self.basis_change_matrix)) # Simulate the one-body terms for half of the full time yield (cirq.Rz(rads=-0.5 * self.orbital_energies[i] * time).on( qubits[i]) for i in range(n_qubits))
def trotter_step(self, qubits: Sequence[cirq.Qid], time: float, control_qubit: Optional[cirq.Qid] = None) -> cirq.OP_TREE: n_qubits = len(qubits) if not isinstance(control_qubit, cirq.Qid): raise TypeError('Control qudit must be specified.') # Apply one- and two-body interactions for half of the full time def one_and_two_body_interaction(p, q, a, b) -> cirq.OP_TREE: yield CRxxyy(0.5 * self.hamiltonian.one_body[p, q].real * time).on( cast(cirq.Qid, control_qubit), a, b) yield CRyxxy(0.5 * self.hamiltonian.one_body[p, q].imag * time).on( cast(cirq.Qid, control_qubit), a, b) yield rot111(-self.hamiltonian.two_body[p, q] * time).on( cast(cirq.Qid, control_qubit), a, b) yield swap_network(qubits, one_and_two_body_interaction, fermionic=True) qubits = qubits[::-1] # Apply one-body potential for the full time yield (rot11(rads=-self.hamiltonian.one_body[i, i].real * time).on( control_qubit, qubits[i]) for i in range(n_qubits)) # Apply one- and two-body interactions for half of the full time # This time, reorder the operations so that the entire Trotter step is # symmetric def one_and_two_body_interaction_reverse_order(p, q, a, b) -> cirq.OP_TREE: yield rot111(-self.hamiltonian.two_body[p, q] * time).on( cast(cirq.Qid, control_qubit), a, b) yield CRyxxy(0.5 * self.hamiltonian.one_body[p, q].imag * time).on( cast(cirq.Qid, control_qubit), a, b) yield CRxxyy(0.5 * self.hamiltonian.one_body[p, q].real * time).on( cast(cirq.Qid, control_qubit), a, b) yield swap_network(qubits, one_and_two_body_interaction_reverse_order, fermionic=True, offset=True) # Apply phase from constant term yield cirq.rz(rads=-self.hamiltonian.constant * time).on(control_qubit)
def trotter_step( self, qubits: Sequence[cirq.Qid], time: float, control_qubit: Optional[cirq.Qid]=None ) -> cirq.OP_TREE: if not isinstance(control_qubit, cirq.Qid): raise NotImplementedError('Control qudit must be specified.') n_qubits = len(qubits) # Change to the basis in which the one-body term is diagonal yield bogoliubov_transform( qubits, self.one_body_basis_change_matrix.T.conj()) # Simulate the one-body terms. for p in range(n_qubits): yield rot11(rads= -self.one_body_energies[p] * time ).on(control_qubit, qubits[p]) # Simulate each singular vector of the two-body terms. prior_basis_matrix = self.one_body_basis_change_matrix for j in range(len(self.eigenvalues)): # Get the two-body coefficients and basis change matrix. two_body_coefficients = self.scaled_density_density_matrices[j] basis_change_matrix = self.basis_change_matrices[j] # Merge previous basis change matrix with the inverse of the # current one merged_basis_change_matrix = numpy.dot(prior_basis_matrix, basis_change_matrix.T.conj()) yield bogoliubov_transform(qubits, merged_basis_change_matrix) # Simulate the off-diagonal two-body terms. yield swap_network( qubits, lambda p, q, a, b: rot111( -2 * two_body_coefficients[p, q] * time).on( control_qubit, a, b)) qubits = qubits[::-1] # Simulate the diagonal two-body terms. yield (rot11(rads= -two_body_coefficients[k, k] * time).on( control_qubit, qubits[k]) for k in range(n_qubits)) # Update prior basis change matrix. prior_basis_matrix = basis_change_matrix # Undo final basis transformation. yield bogoliubov_transform(qubits, prior_basis_matrix) # Apply phase from constant term yield cirq.rz(rads= -self.hamiltonian.constant * time).on(control_qubit)
def operations(self, qubits: Sequence[cirq.Qid]) -> cirq.OP_TREE: """Produce the operations of the ansatz circuit.""" for i in range(self.iterations): # Apply one- and two-body interactions with a swap network that # reverses the order of the modes def one_and_two_body_interaction(p, q, a, b) -> cirq.OP_TREE: th_symbol = LetterWithSubscripts('Th', i) tv_symbol = LetterWithSubscripts('Tv', i) v_symbol = LetterWithSubscripts('V', i) if _is_horizontal_edge(p, q, self.x_dim, self.y_dim, self.periodic): yield cirq.ISwapPowGate(exponent=-th_symbol).on(a, b) if _is_vertical_edge(p, q, self.x_dim, self.y_dim, self.periodic): yield cirq.ISwapPowGate(exponent=-tv_symbol).on(a, b) if _are_same_site_opposite_spin(p, q, self.x_dim * self.y_dim): yield cirq.CZPowGate(exponent=v_symbol).on(a, b) yield swap_network(qubits, one_and_two_body_interaction, fermionic=True) qubits = qubits[::-1] # Apply one- and two-body interactions again. This time, reorder # them so that the entire iteration is symmetric def one_and_two_body_interaction_reversed_order(p, q, a, b) -> cirq.OP_TREE: th_symbol = LetterWithSubscripts('Th', i) tv_symbol = LetterWithSubscripts('Tv', i) v_symbol = LetterWithSubscripts('V', i) if _are_same_site_opposite_spin(p, q, self.x_dim * self.y_dim): yield cirq.CZPowGate(exponent=v_symbol).on(a, b) if _is_vertical_edge(p, q, self.x_dim, self.y_dim, self.periodic): yield cirq.ISwapPowGate(exponent=-tv_symbol).on(a, b) if _is_horizontal_edge(p, q, self.x_dim, self.y_dim, self.periodic): yield cirq.ISwapPowGate(exponent=-th_symbol).on(a, b) yield swap_network(qubits, one_and_two_body_interaction_reversed_order, fermionic=True, offset=True) qubits = qubits[::-1]
def finish(self, qubits: Sequence[cirq.QubitId], n_steps: int, control_qubit: Optional[cirq.QubitId] = None, omit_final_swaps: bool = False) -> cirq.OP_TREE: # If the number of Trotter steps is odd, possibly swap qubits back if n_steps & 1 and not omit_final_swaps: yield swap_network(qubits)
def finish(self, qubits: Sequence[cirq.Qid], n_steps: int, control_qubit: Optional[cirq.Qid] = None, omit_final_swaps: bool = False) -> cirq.OP_TREE: if not omit_final_swaps: # If the number of swap networks was odd, swap the qubits back if n_steps & 1 and len(self.eigenvalues) & 1: yield swap_network(qubits)
def finish(self, qubits: Sequence[cirq.QubitId], n_steps: int, control_qubit: Optional[cirq.QubitId] = None, omit_final_swaps: bool = False) -> cirq.OP_TREE: # Rotate back to the computational basis yield bogoliubov_transform(qubits, self.basis_change_matrix) # If the number of Trotter steps is odd, possibly swap qubits back if n_steps & 1 and not omit_final_swaps: yield swap_network(qubits)
def operations(self, qubits: Sequence[cirq.Qid]) -> cirq.OP_TREE: """Produce the operations of the ansatz circuit.""" n_qubits = len(qubits) param_set = set(self.params()) for i in range(self.iterations): # Change to the basis in which the one-body term is diagonal yield bogoliubov_transform( qubits, self.one_body_basis_change_matrix.T.conj()) # Simulate the one-body terms. for p in range(n_qubits): u_symbol = LetterWithSubscripts('U', p, i) if u_symbol in param_set: yield cirq.ZPowGate(exponent=u_symbol).on(qubits[p]) # Simulate each singular vector of the two-body terms. prior_basis_matrix = self.one_body_basis_change_matrix for j in range(len(self.eigenvalues)): # Get the basis change matrix. basis_change_matrix = self.basis_change_matrices[j] # Merge previous basis change matrix with the inverse of the # current one merged_basis_change_matrix = numpy.dot( prior_basis_matrix, basis_change_matrix.T.conj()) yield bogoliubov_transform(qubits, merged_basis_change_matrix) # Simulate the off-diagonal two-body terms. def two_body_interaction(p, q, a, b) -> cirq.OP_TREE: v_symbol = LetterWithSubscripts('V', p, q, j, i) if v_symbol in param_set: yield cirq.CZPowGate(exponent=v_symbol).on(a, b) yield swap_network(qubits, two_body_interaction) qubits = qubits[::-1] # Simulate the diagonal two-body terms. for p in range(n_qubits): u_symbol = LetterWithSubscripts('U', p, j, i) if u_symbol in param_set: yield cirq.ZPowGate(exponent=u_symbol).on(qubits[p]) # Update prior basis change matrix. prior_basis_matrix = basis_change_matrix # Undo final basis transformation. yield bogoliubov_transform(qubits, prior_basis_matrix)
def trotter_step( self, qubits: Sequence[cirq.Qid], time: float, control_qubit: Optional[cirq.Qid]=None ) -> cirq.OP_TREE: n_qubits = len(qubits) # Change to the basis in which the one-body term is diagonal yield bogoliubov_transform( qubits, self.one_body_basis_change_matrix.T.conj()) # Simulate the one-body terms. for p in range(n_qubits): yield cirq.Rz(rads= -self.one_body_energies[p] * time ).on(qubits[p]) # Simulate each singular vector of the two-body terms. prior_basis_matrix = self.one_body_basis_change_matrix for j in range(len(self.eigenvalues)): # Get the two-body coefficients and basis change matrix. two_body_coefficients = self.scaled_density_density_matrices[j] basis_change_matrix = self.basis_change_matrices[j] # Merge previous basis change matrix with the inverse of the # current one merged_basis_change_matrix = numpy.dot(prior_basis_matrix, basis_change_matrix.T.conj()) yield bogoliubov_transform(qubits, merged_basis_change_matrix) # Simulate the off-diagonal two-body terms. yield swap_network( qubits, lambda p, q, a, b: rot11(rads= -2 * two_body_coefficients[p, q] * time).on(a, b)) qubits = qubits[::-1] # Simulate the diagonal two-body terms. for p in range(n_qubits): yield cirq.Rz(rads= -two_body_coefficients[p, p] * time ).on(qubits[p]) # Update prior basis change matrix prior_basis_matrix = basis_change_matrix # Undo final basis transformation yield bogoliubov_transform(qubits, prior_basis_matrix)
def operations(self, qubits: Sequence[cirq.QubitId]) -> cirq.OP_TREE: """Produce the operations of the ansatz circuit.""" # TODO implement asymmetric ansatz # Change to the basis in which the one-body term is diagonal yield cirq.inverse( bogoliubov_transform(qubits, self.basis_change_matrix)) for i in range(self.iterations): suffix = '-{}'.format(i) if self.iterations > 1 else '' # Simulate one-body terms yield (cirq.RotZGate(half_turns=self.params['U{}'.format(p) + suffix]).on(qubits[p]) for p in range(len(qubits)) if 'U{}'.format(p) + suffix in self.params) # Rotate to the computational basis yield bogoliubov_transform(qubits, self.basis_change_matrix) # Simulate the two-body terms def two_body_interaction(p, q, a, b) -> cirq.OP_TREE: if 'V{}_{}'.format(p, q) + suffix in self.params: yield cirq.Rot11Gate( half_turns=self.params['V{}_{}'.format(p, q) + suffix]).on(a, b) yield swap_network(qubits, two_body_interaction) qubits = qubits[::-1] # Rotate back to the basis in which the one-body term is diagonal yield cirq.inverse( bogoliubov_transform(qubits, self.basis_change_matrix)) # Simulate one-body terms again yield (cirq.RotZGate(half_turns=self.params['U{}'.format(p) + suffix]).on(qubits[p]) for p in range(len(qubits)) if 'U{}'.format(p) + suffix in self.params) # Rotate to the computational basis yield bogoliubov_transform(qubits, self.basis_change_matrix)
def operations(self, qubits: Sequence[cirq.QubitId]) -> cirq.OP_TREE: """Produce the operations of the ansatz circuit.""" # TODO implement asymmetric ansatz param_set = set(self.params()) # Change to the basis in which the one-body term is diagonal yield cirq.inverse( bogoliubov_transform(qubits, self.basis_change_matrix)) for i in range(self.iterations): # Simulate one-body terms for p in range(len(qubits)): u_symbol = LetterWithSubscripts('U', p, i) if u_symbol in param_set: yield cirq.RotZGate(half_turns=u_symbol).on(qubits[p]) # Rotate to the computational basis yield bogoliubov_transform(qubits, self.basis_change_matrix) # Simulate the two-body terms def two_body_interaction(p, q, a, b) -> cirq.OP_TREE: v_symbol = LetterWithSubscripts('V', p, q, i) if v_symbol in param_set: yield cirq.Rot11Gate(half_turns=v_symbol).on(a, b) yield swap_network(qubits, two_body_interaction) qubits = qubits[::-1] # Rotate back to the basis in which the one-body term is diagonal yield cirq.inverse( bogoliubov_transform(qubits, self.basis_change_matrix)) # Simulate one-body terms again for p in range(len(qubits)): u_symbol = LetterWithSubscripts('U', p, i) if u_symbol in param_set: yield cirq.RotZGate(half_turns=u_symbol).on(qubits[p]) # Rotate to the computational basis yield bogoliubov_transform(qubits, self.basis_change_matrix)
def test_swap_network(): n_qubits = 4 qubits = cirq.LineQubit.range(n_qubits) circuit = cirq.Circuit.from_ops( swap_network(qubits, lambda i, j, q0, q1: XXYY(q0, q1)), strategy=cirq.InsertStrategy.EARLIEST) cirq.testing.assert_has_diagram(circuit, """ 0: ───XXYY───×──────────────XXYY───×────────────── │ │ │ │ 1: ───XXYY───×───XXYY───×───XXYY───×───XXYY───×─── │ │ │ │ 2: ───XXYY───×───XXYY───×───XXYY───×───XXYY───×─── │ │ │ │ 3: ───XXYY───×──────────────XXYY───×────────────── """) circuit = cirq.Circuit.from_ops( swap_network(qubits, lambda i, j, q0, q1: XXYY(q0, q1), fermionic=True, offset=True), strategy=cirq.InsertStrategy.EARLIEST) cirq.testing.assert_has_diagram(circuit, """ 0 1 2 3 │ │ │ │ │ XXYY─XXYY │ │ │ │ │ │ ×ᶠ───×ᶠ │ │ │ │ │ XXYY─XXYY XXYY─XXYY │ │ │ │ ×ᶠ───×ᶠ ×ᶠ───×ᶠ │ │ │ │ │ XXYY─XXYY │ │ │ │ │ │ ×ᶠ───×ᶠ │ │ │ │ │ XXYY─XXYY XXYY─XXYY │ │ │ │ ×ᶠ───×ᶠ ×ᶠ───×ᶠ │ │ │ │ """, transpose=True) n_qubits = 5 qubits = cirq.LineQubit.range(n_qubits) circuit = cirq.Circuit.from_ops( swap_network(qubits, lambda i, j, q0, q1: (), fermionic=True), strategy=cirq.InsertStrategy.EARLIEST) cirq.testing.assert_has_diagram(circuit, """ 0: ───×ᶠ────────×ᶠ────────×ᶠ─── │ │ │ 1: ───×ᶠ───×ᶠ───×ᶠ───×ᶠ───×ᶠ─── │ │ 2: ───×ᶠ───×ᶠ───×ᶠ───×ᶠ───×ᶠ─── │ │ │ 3: ───×ᶠ───×ᶠ───×ᶠ───×ᶠ───×ᶠ─── │ │ 4: ────────×ᶠ────────×ᶠ──────── """) circuit = cirq.Circuit.from_ops( swap_network(qubits, lambda i, j, q0, q1: (), offset=True), strategy=cirq.InsertStrategy.EARLIEST) cirq.testing.assert_has_diagram(circuit, """ 0 1 2 3 4 │ │ │ │ │ │ ×─× ×─× │ │ │ │ │ ×─× ×─× │ │ │ │ │ │ │ ×─× ×─× │ │ │ │ │ ×─× ×─× │ │ │ │ │ │ │ ×─× ×─× │ │ │ │ │ """, transpose=True)
def trotter_step( self, qubits: Sequence[cirq.QubitId], time: float, control_qubit: Optional[cirq.QubitId]=None ) -> cirq.OP_TREE: n_qubits = len(qubits) # Simulate the off-diagonal one-body terms. yield swap_network( qubits, lambda p, q, a, b: ControlledXXYYGate(duration= self.one_body_coefficients[p, q].real * time).on( control_qubit, a, b), fermionic=True) qubits = qubits[::-1] # Simulate the diagonal one-body terms. yield (cirq.Rot11Gate(rads= -self.one_body_coefficients[j, j].real * time).on( control_qubit, qubits[j]) for j in range(n_qubits)) # Simulate each singular vector of the two-body terms. prior_basis_matrix = numpy.identity(n_qubits, complex) for j in range(len(self.eigenvalues)): # Get the basis change matrix and its inverse. two_body_coefficients = self.scaled_density_density_matrices[j] basis_change_matrix = self.basis_change_matrices[j] # If the basis change matrix is unitary, merge rotations. # Otherwise, you must simulate two basis rotations. is_unitary = cirq.is_unitary(basis_change_matrix) if is_unitary: inverse_basis_matrix = numpy.conjugate( numpy.transpose(basis_change_matrix)) merged_basis_matrix = numpy.dot(prior_basis_matrix, inverse_basis_matrix) yield bogoliubov_transform(qubits, merged_basis_matrix) else: # TODO add LiH test to cover this. # coverage: ignore if j: yield bogoliubov_transform(qubits, prior_basis_matrix) yield cirq.inverse( bogoliubov_transform(qubits, basis_change_matrix)) # Simulate the off-diagonal two-body terms. yield swap_network( qubits, lambda p, q, a, b: Rot111Gate(rads= -2 * two_body_coefficients[p, q] * time).on( control_qubit, a, b)) qubits = qubits[::-1] # Simulate the diagonal two-body terms. yield (cirq.Rot11Gate(rads= -two_body_coefficients[k, k] * time).on( control_qubit, qubits[k]) for k in range(n_qubits)) # Undo basis transformation non-unitary case. # Else, set basis change matrix to prior matrix for merging. if is_unitary: prior_basis_matrix = basis_change_matrix else: # TODO add LiH test to cover this. # coverage: ignore yield bogoliubov_transform(qubits, basis_change_matrix) prior_basis_matrix = numpy.identity(n_qubits, complex) # Undo final basis transformation in unitary case. if is_unitary: yield bogoliubov_transform(qubits, prior_basis_matrix) # Apply phase from constant term yield cirq.RotZGate(rads=-self.constant * time).on(control_qubit)
def test_swap_network(): n_qubits = 4 qubits = cirq.LineQubit.range(n_qubits) circuit = cirq.Circuit.from_ops(swap_network( qubits, lambda i, j, q0, q1: XXYY(q0, q1)), strategy=cirq.InsertStrategy.EARLIEST) assert circuit.to_text_diagram(transpose=False).strip() == """ 0: ───XXYY───×──────────────XXYY───×────────────── │ │ │ │ 1: ───XXYY───×───XXYY───×───XXYY───×───XXYY───×─── │ │ │ │ 2: ───XXYY───×───XXYY───×───XXYY───×───XXYY───×─── │ │ │ │ 3: ───XXYY───×──────────────XXYY───×────────────── """.strip() circuit = cirq.Circuit.from_ops( swap_network(qubits, lambda i, j, q0, q1: XXYY(q0, q1), fermionic=True, offset=True), strategy=cirq.InsertStrategy.EARLIEST) assert circuit.to_text_diagram(transpose=True).strip() == """ 0 1 2 3 │ │ │ │ │ XXYY─XXYY │ │ │ │ │ │ ×ᶠ───×ᶠ │ │ │ │ │ XXYY─XXYY XXYY─XXYY │ │ │ │ ×ᶠ───×ᶠ ×ᶠ───×ᶠ │ │ │ │ │ XXYY─XXYY │ │ │ │ │ │ ×ᶠ───×ᶠ │ │ │ │ │ XXYY─XXYY XXYY─XXYY │ │ │ │ ×ᶠ───×ᶠ ×ᶠ───×ᶠ │ │ │ │ """.strip() n_qubits = 5 qubits = cirq.LineQubit.range(n_qubits) circuit = cirq.Circuit.from_ops(swap_network(qubits, lambda i, j, q0, q1: (), fermionic=True), strategy=cirq.InsertStrategy.EARLIEST) assert circuit.to_text_diagram(transpose=False).strip() == """ 0: ───×ᶠ────────×ᶠ────────×ᶠ─── │ │ │ 1: ───×ᶠ───×ᶠ───×ᶠ───×ᶠ───×ᶠ─── │ │ 2: ───×ᶠ───×ᶠ───×ᶠ───×ᶠ───×ᶠ─── │ │ │ 3: ───×ᶠ───×ᶠ───×ᶠ───×ᶠ───×ᶠ─── │ │ 4: ────────×ᶠ────────×ᶠ──────── """.strip() circuit = cirq.Circuit.from_ops(swap_network(qubits, lambda i, j, q0, q1: (), offset=True), strategy=cirq.InsertStrategy.EARLIEST) assert circuit.to_text_diagram(transpose=True).strip() == """ 0 1 2 3 4 │ │ │ │ │ │ ×─× ×─× │ │ │ │ │ ×─× ×─× │ │ │ │ │ │ │ ×─× ×─× │ │ │ │ │ ×─× ×─× │ │ │ │ │ │ │ ×─× ×─× │ │ │ │ │ """.strip()
def test_swap_network(): n_qubits = 4 qubits = cirq.LineQubit.range(n_qubits) circuit = cirq.Circuit( swap_network(qubits, lambda i, j, q0, q1: cirq.ISWAP(q0, q1)**-1)) cirq.testing.assert_has_diagram( circuit, """ 0: ───iSwap──────×──────────────────iSwap──────×────────────────── │ │ │ │ 1: ───iSwap^-1───×───iSwap──────×───iSwap^-1───×───iSwap──────×─── │ │ │ │ 2: ───iSwap──────×───iSwap^-1───×───iSwap──────×───iSwap^-1───×─── │ │ │ │ 3: ───iSwap^-1───×──────────────────iSwap^-1───×────────────────── """) circuit = cirq.Circuit( swap_network(qubits, lambda i, j, q0, q1: cirq.ISWAP(q0, q1)**-1, fermionic=True, offset=True)) cirq.testing.assert_has_diagram(circuit, """ 0 1 2 3 │ │ │ │ │ iSwap────iSwap^-1 │ │ │ │ │ │ ×ᶠ───────×ᶠ │ │ │ │ │ iSwap─iSwap^-1 iSwap────iSwap^-1 │ │ │ │ ×ᶠ────×ᶠ ×ᶠ───────×ᶠ │ │ │ │ │ iSwap────iSwap^-1 │ │ │ │ │ │ ×ᶠ───────×ᶠ │ │ │ │ │ iSwap─iSwap^-1 iSwap────iSwap^-1 │ │ │ │ ×ᶠ────×ᶠ ×ᶠ───────×ᶠ │ │ │ │ """, transpose=True) n_qubits = 5 qubits = cirq.LineQubit.range(n_qubits) circuit = cirq.Circuit(swap_network(qubits, lambda i, j, q0, q1: (), fermionic=True), strategy=cirq.InsertStrategy.EARLIEST) cirq.testing.assert_has_diagram( circuit, """ 0: ───×ᶠ────────×ᶠ────────×ᶠ─── │ │ │ 1: ───×ᶠ───×ᶠ───×ᶠ───×ᶠ───×ᶠ─── │ │ 2: ───×ᶠ───×ᶠ───×ᶠ───×ᶠ───×ᶠ─── │ │ │ 3: ───×ᶠ───×ᶠ───×ᶠ───×ᶠ───×ᶠ─── │ │ 4: ────────×ᶠ────────×ᶠ──────── """) circuit = cirq.Circuit(swap_network(qubits, lambda i, j, q0, q1: (), offset=True), strategy=cirq.InsertStrategy.EARLIEST) cirq.testing.assert_has_diagram(circuit, """ 0 1 2 3 4 │ │ │ │ │ │ ×─× ×─× │ │ │ │ │ ×─× ×─× │ │ │ │ │ │ │ ×─× ×─× │ │ │ │ │ ×─× ×─× │ │ │ │ │ │ │ ×─× ×─× │ │ │ │ │ """, transpose=True)
def test_reusable(): ops = swap_network(cirq.LineQubit.range(5)) assert list(ops) == list(ops)