Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify and refine printing of Hamiltonian circuits; add size limit #549

Merged
merged 4 commits into from
Jul 4, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 87 additions & 67 deletions hamiltonian-simulation/qiskit/hamiltonian_simulation_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
pi = math.pi

# Gates to be saved for printing purpose.
# DEVNOTE: these are global for now, for convenience; improve later
XX_ = None
YY_ = None
ZZ_ = None
Expand All @@ -33,6 +34,9 @@

## use initial state in the abstract class
class HamiltonianKernel(object):

MAX_PRINT_SIZE = 6 # default maximum size to print circuit

def __init__(self, n_spins, K, t, hamiltonian, w, hx, hz, use_XX_YY_ZZ_gates, method, random_pauli_flag, init_state):
self.n_spins = n_spins
self.K = K
Expand All @@ -46,6 +50,7 @@ def __init__(self, n_spins, K, t, hamiltonian, w, hx, hz, use_XX_YY_ZZ_gates, me
self.random_pauli_flag = random_pauli_flag
self.method = method
self.init_state = init_state


self.QCI_ = None # Initial Circuit
self.QC_ = None # Total Circuit
Expand All @@ -63,11 +68,12 @@ def overall_circuit(self):
circuit = self.create_hamiltonian() #create the hamiltonian circuit
self.qc.append(circuit, self.qr) #append the hamiltoniain to the quantum circuit

inverse_circuit = None
if self.method == 3:
#checks if random pauli flag is true to apply quasi inverse.
if self.random_pauli_flag:
quasi_inverse_circuit = self.create_quasi_inverse_hamiltonian()
self.qc.append(quasi_inverse_circuit, self.qr)
inverse_circuit = self.create_quasi_inverse_hamiltonian()
self.qc.append(inverse_circuit, self.qr)
else:
#applies regular inverse.
inverse_circuit = self.create_inverse_hamiltonian()
Expand All @@ -77,14 +83,17 @@ def overall_circuit(self):
for i_qubit in range(self.n_spins):
self.qc.measure(self.qr[i_qubit], self.cr[i_qubit])

#Save smaller circuit example for display
# Save circuits and subcircuits for possible display
# if self.QC_ is None or self.n_spins <= 6:
# if self.n_spins < 9:
# self.QC_ = self.qc
self.QC_ = self.qc
self.QCI_ = i_state
self.QCH_ = circuit
self.QC2D_ = inverse_circuit

# Collapse the sub-circuits used in this benchmark (for Qiskit)
qc2 = self.qc.decompose().decompose()

qc2 = self.qc.decompose().decompose()

return qc2

Expand All @@ -102,7 +111,7 @@ def initial_state(self) -> QuantumCircuit:
qc.h(0)
for k in range(1, self.n_spins):
qc.cx(k-1, k)
self.QCI_ = qc
#self.QCI_ = qc
return qc

def create_hamiltonian(self) -> QuantumCircuit:
Expand All @@ -126,7 +135,7 @@ def random_paulis_list(self):
pauli_tracker_list.append("z")
return pauli_tracker_list

#### Resultant Pauli after applying quasi inverse Hamiltonain.
#### Resultant Pauli after applying quasi inverse Hamiltonian
def ResultantPauli(self)-> QuantumCircuit:
"""Create a quantum oracle that is the result of applying quasi inverse Hamiltonain and random Pauli to Hamiltonian."""
qr = QuantumRegister(self.n_spins)
Expand All @@ -138,31 +147,39 @@ def ResultantPauli(self)-> QuantumCircuit:
self.QCRS_ = qc
return qc

#### Draw the circuits of this benchmark program
#### Draw the circuits of this kernel
def kernel_draw(self):
if self.n_spins == 6:
# Print a sample circuit
print("Sample Circuit:")
print(self.QC_ if self.QC_ is not None else " ... too large!")

# we don't restrict save of large sub-circuits, so skip printing if num_qubits too large
if self.QCI_ is not None and self.n_spins > 6:
print("... subcircuits too large to print")

print(" Initial State:")
if self.QCI_ is not None: print(self.QCI_)
# Print a sample circuit
print("Sample Circuit:")
if self.QC_ is not None and self.n_spins <= self.MAX_PRINT_SIZE:
print(self.QC_)
else:
print(" ... too large to print!")
return False

# we don't restrict save of large sub-circuits, so skip printing if num_qubits too large
#if self.QCI_ is not None and self.n_spins > 6:
#print("... subcircuits too large to print")

if self.QCI_ is not None:
print(" Initial State:")
print(self.QCI_)

if self.QCH_ is not None:
print(f" Hamiltonian ({self.QCH_.name if self.QCH_ is not None else '?'}):")
if self.QCH_ is not None: print(self.QCH_)
print(self.QCH_)

if self.QC2D_ is not None:
print(f" Inverse Hamiltonian ({self.QC2D_.name if self.QC2D_ is not None else '?'}):")
print(self.QC2D_)

if self.QC2D_ is not None:
print("Quasi-Hamiltonian:")
print(self.QC2D_)

if self.QCRS_ is not None:
print(" Resultant Paulis:")
print(self.QCRS_)

if self.QCRS_ is not None:
print(" Resultant Paulis:")
print(self.QCRS_)

return True

########################
####*****************###
########################
Expand Down Expand Up @@ -194,14 +211,13 @@ def create_hamiltonian(self) -> QuantumCircuit:
for i in range(j % 2, self.n_spins - 1, 2):
qc.append(xxyyzz_opt_gate(self.tau).to_instruction(), [qr[i], qr[(i + 1) % self.n_spins]])
qc.barrier()

self.QCH_ = qc

return qc

#apply inverse of the hamiltonian to simulate negative time evolution.
def create_inverse_hamiltonian(self) -> QuantumCircuit:
qr = QuantumRegister(self.n_spins)
qc = QuantumCircuit(qr, name="InverseHeisenberg")
qc = QuantumCircuit(qr, name="Heisenberg\u2020")
for k in range(self.K):
if self.use_XX_YY_ZZ_gates:
for j in range(2):
Expand All @@ -221,13 +237,13 @@ def create_inverse_hamiltonian(self) -> QuantumCircuit:
[qc.rz(-2 * self.tau * self.w * self.h_z[i], qr[i]) for i in range(self.n_spins)]
[qc.rx(-2 * self.tau * self.w * self.h_x[i], qr[i]) for i in range(self.n_spins)]
qc.barrier()
QC2D_ = qc

return qc

#create quasi inverse hamiltonian to simulate negative time evolution with randomized paulis applied.
def create_quasi_inverse_hamiltonian(self) -> QuantumCircuit:
qr = QuantumRegister(self.n_spins)
qc = QuantumCircuit(qr, name = "QuasiInverseHeisenberg")
qc = QuantumCircuit(qr, name = "Quasi-Heisenberg\u2020")

# Apply random paulis
pauli_list = self.random_paulis_list()
Expand Down Expand Up @@ -300,32 +316,34 @@ def create_quasi_inverse_hamiltonian(self) -> QuantumCircuit:
qc.barrier()

qc.append(self.QCRS_,qr)
self.QC2D_ = qc
#self.QC2D_ = qc
return qc

#apply extra circuit printing specific to Heisenberg.
# draw circuit and apply extra circuit printing specific to this kernel type.
def kernel_draw(self):
if self.n_spins == 6:
super().kernel_draw()
if self.use_XX_YY_ZZ_gates:
print("\nXX, YY, ZZ = ")
print(XX_)
print(YY_)
print(ZZ_)
if self.method == 3:
print("\nXX, YY, ZZ \u2020 = ")
print(XX_mirror_)
print(YY_mirror_)
print(ZZ_mirror_)
else:
print("\nXXYYZZ = ")
print(XXYYZZ_)

if super().kernel_draw() == False:
return

if self.use_XX_YY_ZZ_gates:
print("\nXX, YY, ZZ = ")
print(XX_)
print(YY_)
print(ZZ_)
if self.method == 3:
print("\nXXYYZZ\u2020 = ")
print(XXYYZZ_mirror_)
if self.random_pauli_flag:
print("Qusai Inverse XXYYZZ:")
print(XXYYZZ_quasi_mirror_)
print("\nXX, YY, ZZ \u2020 = ")
print(XX_mirror_)
print(YY_mirror_)
print(ZZ_mirror_)
else:
print("\nXXYYZZ = ")
print(XXYYZZ_)
if self.method == 3:
print("\nXXYYZZ\u2020 = ")
print(XXYYZZ_mirror_)
if self.random_pauli_flag:
print("Qusai Inverse XXYYZZ:")
print(XXYYZZ_quasi_mirror_)


########################
Expand All @@ -348,13 +366,13 @@ def create_hamiltonian(self) -> QuantumCircuit:
for i in range(j % 2, self.n_spins - 1, 2):
qc.append(zz_gate(self.tau).to_instruction(), [qr[i], qr[(i + 1) % self.n_spins]])
qc.barrier()
self.QCH_ = qc

return qc

#apply inverse of the hamiltonian to simulate negative time evolution.
def create_inverse_hamiltonian(self) -> QuantumCircuit:
qr = QuantumRegister(self.n_spins)
qc = QuantumCircuit(qr, name="InverseTFIM")
qc = QuantumCircuit(qr, name="TFIM\u2020")
for k in range(self.K):
for j in range(2):
for i in range(j % 2, self.n_spins - 1, 2):
Expand All @@ -363,13 +381,13 @@ def create_inverse_hamiltonian(self) -> QuantumCircuit:
for i in range(self.n_spins):
qc.rx(-2 * self.tau * self.h, qr[i])
qc.barrier()
self.QC2D_ = qc

return qc

#create quasi inverse hamiltonian to simulate negative time evolution with randomized paulis applied.
def create_quasi_inverse_hamiltonian(self) -> QuantumCircuit:
qr = QuantumRegister(self.n_spins)
qc = QuantumCircuit(qr, name="QuasiInverseTFIM")
qc = QuantumCircuit(qr, name="Quasi-TFIM\u2020")
for k in range(self.K):
for j in range(2):
for i in range(j % 2, self.n_spins - 1, 2):
Expand All @@ -378,18 +396,20 @@ def create_quasi_inverse_hamiltonian(self) -> QuantumCircuit:
for i in range(self.n_spins):
qc.rx(-2 * self.tau * self.h, qr[i])
qc.barrier()
self.QC2D_ = qc

return qc

#apply extra circuit printing specific to tfim.
# draw circuit and apply extra circuit printing specific to this kernel type.
def kernel_draw(self):
if self.n_spins == 6:
super().kernel_draw()
print("\nZZ = ")
print(ZZ_)
if self.method == 3:
print("\nZZ\u2020 = ")
print(ZZ_mirror_)

if super().kernel_draw() == False:
return

print("\nZZ = ")
print(ZZ_)
if self.method == 3:
print("\nZZ\u2020 = ")
print(ZZ_mirror_)


########################
Expand Down
Loading