├── src └── simuq │ ├── aais │ ├── __init__.py │ ├── ising.py │ ├── ibm_braiding.py │ ├── ionq_braiding.py │ ├── rydberg1d.py │ ├── ionq_arb_2q.py │ ├── heisenberg.py │ ├── rydberg1d_global.py │ ├── two_pauli.py │ ├── rydberg2d.py │ ├── rydberg2d_global.py │ ├── ibm.py │ └── rigetti.py │ ├── backends │ ├── __init__.py │ ├── bloqade_rydberg2d.py │ ├── ionq_qiskit_qis.py │ ├── ionq_qiskit_qis_qaoa.py │ ├── qiskit_iontrap.py │ ├── bloqade_rydberg.py │ ├── ionq_transpiler.py │ ├── bloqade_rydberg_aws.py │ └── ionq_braiding.py │ ├── systems │ ├── __init__.py │ ├── benchmark │ │ ├── __init__.py │ │ ├── ising_chain.py │ │ ├── ising_cycle.py │ │ ├── kitaev.py │ │ ├── heis_chain.py │ │ ├── heis_cycle.py │ │ ├── schwinger.py │ │ ├── qhd.py │ │ ├── qaoa_cycle.py │ │ ├── epdynamics.py │ │ ├── o3nlσm.py │ │ ├── mis_chain.py │ │ ├── mis_grid.py │ │ └── mzmbraid.py │ ├── single_x.py │ ├── jc.py │ ├── rabi.py │ ├── heis5.py │ ├── ising.py │ ├── heis7.py │ ├── annealing.py │ ├── ising_general.py │ ├── qwalk_chain.py │ ├── bh.py │ ├── qaoa.py │ ├── floquet.py │ ├── qiskit_Heis.py │ ├── hubbard.py │ ├── mzmbraiding.py │ └── mis.py │ ├── experimental │ ├── __init__.py │ └── aais │ │ ├── __init__.py │ │ ├── flux_sc.py │ │ ├── ibm5.py │ │ ├── ibm7.py │ │ ├── ibm_27.py │ │ └── ibm_hypothetical.py │ ├── _version.py │ ├── ibm │ ├── __init__.py │ ├── qiskit_5.py │ ├── qiskit_braiding.py │ ├── qiskit_pulse_ibm.py │ └── ibm_provider.py │ ├── dwave │ ├── __init__.py │ ├── dwave_transpiler.py │ └── dwave_provider.py │ ├── qutip │ ├── __init__.py │ └── qutip_provider.py │ ├── transpiler.py │ ├── ionq │ ├── __init__.py │ ├── ionq_api_transpiler.py │ ├── ionq_api_circuit.py │ └── ionq_provider.py │ ├── braket │ ├── __init__.py │ ├── braket_ionq_transpiler.py │ ├── braket_ionq_circuit.py │ └── braket_rydberg_transpiler.py │ ├── provider.py │ ├── __init__.py │ ├── config.py │ ├── qsystem.py │ ├── environment.py │ └── qmachine.py ├── pyproject.toml ├── AUTHORS ├── notebooks ├── tutorials │ ├── imgs │ │ ├── ising.png │ │ ├── schwinger.png │ │ ├── walk_chain.png │ │ └── figure_ising_simulation.png │ └── pulses │ │ ├── chain_state_prep_4_qubit_ket9_practical.npz │ │ └── chain_state_prep_4_qubit_ket9_high_fidelity.npz ├── experimental │ ├── test_ionq_arb_2q.ipynb │ ├── ising_braket_ionq.ipynb │ ├── ising_ibm.ipynb │ ├── qwalk_bloqade.ipynb │ ├── qwalk_julia.ipynb │ ├── ising_ionq.ipynb │ ├── mis_bloqade.ipynb │ └── test_qutip.ipynb └── artifact_evaluation │ ├── cs4-benchmark-IonQ.ipynb │ ├── cs4-benchmark-QuEra.ipynb │ └── cs3-qaoa.ipynb ├── setup.cfg ├── tox.ini ├── setup.py ├── LICENSE.md ├── .gitignore └── README.md /src/simuq/aais/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/simuq/backends/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/simuq/systems/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/simuq/experimental/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/simuq/experimental/aais/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/simuq/systems/benchmark/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/simuq/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.3.1" 2 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 100 3 | -------------------------------------------------------------------------------- /src/simuq/ibm/__init__.py: -------------------------------------------------------------------------------- 1 | from simuq.ibm.ibm_provider import IBMProvider 2 | -------------------------------------------------------------------------------- /src/simuq/dwave/__init__.py: -------------------------------------------------------------------------------- 1 | from simuq.dwave.dwave_provider import DWaveProvider 2 | -------------------------------------------------------------------------------- /src/simuq/qutip/__init__.py: -------------------------------------------------------------------------------- 1 | from simuq.qutip.qutip_provider import QuTiPProvider 2 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Authors of SimuQ: 2 | Pengyu Liu 3 | Yuxiang Peng 4 | Xiaodi Wu 5 | Jacob Young 6 | -------------------------------------------------------------------------------- /src/simuq/transpiler.py: -------------------------------------------------------------------------------- 1 | class Transpiler: 2 | def transpile(self, *args, **kwargs): 3 | pass 4 | -------------------------------------------------------------------------------- /notebooks/tutorials/imgs/ising.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicksPeng/SimuQ/HEAD/notebooks/tutorials/imgs/ising.png -------------------------------------------------------------------------------- /notebooks/tutorials/imgs/schwinger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicksPeng/SimuQ/HEAD/notebooks/tutorials/imgs/schwinger.png -------------------------------------------------------------------------------- /notebooks/tutorials/imgs/walk_chain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicksPeng/SimuQ/HEAD/notebooks/tutorials/imgs/walk_chain.png -------------------------------------------------------------------------------- /notebooks/tutorials/imgs/figure_ising_simulation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicksPeng/SimuQ/HEAD/notebooks/tutorials/imgs/figure_ising_simulation.png -------------------------------------------------------------------------------- /notebooks/tutorials/pulses/chain_state_prep_4_qubit_ket9_practical.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicksPeng/SimuQ/HEAD/notebooks/tutorials/pulses/chain_state_prep_4_qubit_ket9_practical.npz -------------------------------------------------------------------------------- /notebooks/tutorials/pulses/chain_state_prep_4_qubit_ket9_high_fidelity.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicksPeng/SimuQ/HEAD/notebooks/tutorials/pulses/chain_state_prep_4_qubit_ket9_high_fidelity.npz -------------------------------------------------------------------------------- /src/simuq/ionq/__init__.py: -------------------------------------------------------------------------------- 1 | from simuq.ionq.ionq_api_circuit import IonQAPICircuit 2 | from simuq.ionq.ionq_api_transpiler import IonQAPITranspiler 3 | from simuq.ionq.ionq_provider import IonQProvider 4 | -------------------------------------------------------------------------------- /src/simuq/braket/__init__.py: -------------------------------------------------------------------------------- 1 | from simuq.braket.braket_ionq_circuit import BraketIonQCircuit 2 | from simuq.braket.braket_ionq_transpiler import BraketIonQTranspiler 3 | from simuq.braket.braket_provider import BraketProvider 4 | from simuq.braket.braket_rydberg_transpiler import BraketRydbergTranspiler 5 | -------------------------------------------------------------------------------- /src/simuq/systems/single_x.py: -------------------------------------------------------------------------------- 1 | from simuq.environment import Qubit 2 | from simuq.qsystem import QSystem 3 | 4 | qs = QSystem() 5 | n = 3 6 | ql = [Qubit(qs) for i in range(n)] 7 | link = [(i, i + 1) for i in range(n - 1)] 8 | T = 1.0 9 | scaler = 1000 10 | h = 0 11 | for i in range(n): 12 | h += scaler * ql[i].X 13 | qs.add_evolution(h, T / scaler) 14 | -------------------------------------------------------------------------------- /src/simuq/systems/jc.py: -------------------------------------------------------------------------------- 1 | from simuq.environment import Boson, Qubit 2 | from simuq.qsystem import QSystem 3 | 4 | # Jaynes-Cummings model 5 | qs = QSystem() 6 | atom = Qubit(qs) 7 | cav = Boson(qs) 8 | 9 | omega_a = 0.1 10 | omega_c = 0.01 11 | g = 0.001 12 | h = omega_a * atom.Z / 2 + omega_c * cav.a * cav.c + g * (cav.a + cav.c) * atom.X 13 | t = 0.1 14 | qs.add_evolution(h, t) 15 | -------------------------------------------------------------------------------- /src/simuq/systems/rabi.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | from simuq.environment import Qubit 4 | from simuq.qsystem import QSystem 5 | 6 | qs = QSystem() 7 | q = Qubit(qs) 8 | n_step = 5 9 | T = 1.0 10 | omega0 = 0.1 11 | omega1 = 0.3 12 | omega = 0.2 13 | for step in range(n_step): 14 | t = step * T / n_step 15 | h = -omega0 / 2 * q.Z - omega1 / 2 * (math.cos(omega * t) * q.X - math.sin(omega * t) * q.Y) 16 | qs.add_evolution(h, T / n_step) 17 | -------------------------------------------------------------------------------- /src/simuq/provider.py: -------------------------------------------------------------------------------- 1 | class BaseProvider: 2 | def __init__(self): 3 | self.prog = None 4 | self.task = None 5 | self.qs_names = None 6 | 7 | @staticmethod 8 | def to_bin(res, n): 9 | b = bin(res)[2:] 10 | return "0" * (n - len(b)) + b 11 | 12 | def print_sites(self): 13 | if self.prog is None: 14 | raise Exception("No compiled job in record.") 15 | print("Order of sites:", self.qs_names) 16 | -------------------------------------------------------------------------------- /src/simuq/systems/heis5.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from simuq.environment import Qubit 4 | from simuq.qsystem import QSystem 5 | 6 | qs = QSystem() 7 | n = 5 8 | ql = [Qubit(qs) for i in range(n)] 9 | link = [(0, 1), (1, 2), (2, 3), (3, 4)] 10 | T = np.pi / 4 11 | h = 0 12 | for q0, q1 in link: 13 | h += ql[q0].X * ql[q1].X 14 | h += ql[q0].Y * ql[q1].Y 15 | h += ql[q0].Z * ql[q1].Z 16 | for i in range(n): 17 | h += ql[i].X 18 | qs.add_evolution(h, T) 19 | -------------------------------------------------------------------------------- /src/simuq/systems/ising.py: -------------------------------------------------------------------------------- 1 | from simuq.environment import Qubit 2 | from simuq.qsystem import QSystem 3 | 4 | 5 | # Chain or cycle transverse Ising model 6 | def GenQS(n, T, J, h, is_chain=True): 7 | qs = QSystem() 8 | q = [Qubit(qs) for i in range(n)] 9 | H = 0 10 | for i in range(n - 1 if is_chain else n): 11 | H = H + J * q[i].Z * q[(i + 1) % n].Z 12 | for i in range(n): 13 | H = H + h * q[i].X 14 | qs.add_evolution(H, T) 15 | return qs 16 | -------------------------------------------------------------------------------- /src/simuq/__init__.py: -------------------------------------------------------------------------------- 1 | from simuq.environment import BaseParticle, BaseQuantumEnvironment, BaseSite, Boson, Fermion, Qubit 2 | from simuq.expression import BaseVar, Expression 3 | from simuq.hamiltonian import TIHamiltonian, hlist_sum 4 | from simuq.provider import BaseProvider 5 | from simuq.qmachine import Instruction, QMachine, SignalLine 6 | from simuq.qsystem import QSystem 7 | from simuq.transformation import jw_transform, oh_transform 8 | from simuq.transpiler import Transpiler 9 | from simuq.config import Config 10 | -------------------------------------------------------------------------------- /src/simuq/systems/benchmark/ising_chain.py: -------------------------------------------------------------------------------- 1 | # Generate a transverse field chain Ising model 2 | # H = J Σ_{j=1}^{n-1} Z_jZ_{j+1} + h Σ_{j=1}^n X_j 3 | 4 | from simuq.environment import Qubit 5 | from simuq.qsystem import QSystem 6 | 7 | 8 | def GenQS(n, T, J, h): 9 | qs = QSystem() 10 | q = [Qubit(qs) for i in range(n)] 11 | H = 0 12 | for i in range(n - 1): 13 | H = H + J * q[i].Z * q[i + 1].Z 14 | for i in range(n): 15 | H = H + h * q[i].X 16 | qs.add_evolution(H, T) 17 | return qs 18 | -------------------------------------------------------------------------------- /src/simuq/systems/heis7.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from simuq.environment import Qubit 4 | from simuq.qsystem import QSystem 5 | 6 | qs = QSystem() 7 | n = 7 8 | ql = [Qubit(qs) for i in range(7)] 9 | link = [(0, 1), (1, 2), (1, 3), (3, 5), (4, 5), (5, 6)] 10 | T = np.pi 11 | h = 0 12 | Jx = 1 13 | Jy = 1 14 | Jz = 1 15 | for q0, q1 in link: 16 | h += Jx * ql[q0].X * ql[q1].X 17 | h += Jy * ql[q0].Y * ql[q1].Y 18 | h += Jz * ql[q0].Z * ql[q1].Z 19 | a = 1 20 | for i in range(n): 21 | h += a * ql[i].Z 22 | qs.add_evolution(h, T) 23 | -------------------------------------------------------------------------------- /src/simuq/braket/braket_ionq_transpiler.py: -------------------------------------------------------------------------------- 1 | from simuq.backends.ionq_transpiler import IonQTranspiler 2 | from simuq.backends.ionq_transpiler_2Pauli import IonQTranspiler_2Pauli 3 | from simuq.braket import BraketIonQCircuit 4 | 5 | 6 | class BraketIonQTranspiler(IonQTranspiler): 7 | def generate_circuit(self, n: int) -> BraketIonQCircuit: 8 | return BraketIonQCircuit(n) 9 | 10 | class BraketIonQTranspiler_2Pauli(IonQTranspiler_2Pauli): 11 | def generate_circuit(self, n: int) -> BraketIonQCircuit: 12 | return BraketIonQCircuit(n) 13 | -------------------------------------------------------------------------------- /src/simuq/systems/benchmark/ising_cycle.py: -------------------------------------------------------------------------------- 1 | # Generate a transverse field cycle Ising model 2 | # H = J \sum_{j=1}^{n} Z_jZ_{j+1} + h \sum_{j=1}^n X_j (assuming Z_{n+1}=Z_1) 3 | 4 | from simuq.environment import Qubit 5 | from simuq.qsystem import QSystem 6 | 7 | 8 | def GenQS(n, T, J, h): 9 | qs = QSystem() 10 | q = [Qubit(qs) for i in range(n)] 11 | H = 0 12 | for i in range(n): 13 | H = H + J * q[i].Z * q[(i + 1) % n].Z 14 | for i in range(n): 15 | H = H + h * q[i].X 16 | qs.add_evolution(H, T) 17 | return qs 18 | -------------------------------------------------------------------------------- /src/simuq/systems/annealing.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from simuq.environment import Qubit 4 | from simuq.qsystem import QSystem 5 | 6 | 7 | def anneal(h0, h1, T): 8 | def f(t): 9 | return (1 - t / T) * h0 + t / T * h1 10 | 11 | return f 12 | 13 | 14 | n = 3 # num of qubits 15 | m = 3 # discretization 16 | T = 1 # evolution time 17 | 18 | qs = QSystem() 19 | q = [Qubit(qs) for i in range(n)] 20 | h0, h1 = 0, 0 21 | for i in range(n): 22 | h0 += q[i].X 23 | for i in range(n - 1): 24 | h1 += q[i].Z * q[i + 1].Z 25 | 26 | qs.add_td_evolution(anneal(h0, h1, T), np.linspace(0, T, m)) 27 | -------------------------------------------------------------------------------- /src/simuq/systems/benchmark/kitaev.py: -------------------------------------------------------------------------------- 1 | # Generate a Kitaev chain coupled to a Z2 gauge field 2 | # The following comes from Aramthottil et al., Phys. Rev. B, L041101, 2022 3 | # H = μ/2 Σ_{j=1}^{n-1} Z_jZ_{j+1} - Σ_{j=1}^n (t X_j + h Z_j) 4 | 5 | from simuq.environment import Qubit 6 | from simuq.qsystem import QSystem 7 | 8 | 9 | def GenQS(n, T=1, mu=1, t=0.3, h=0.5): 10 | qs = QSystem() 11 | q = [Qubit(qs) for i in range(n)] 12 | H = 0 13 | for i in range(n - 1): 14 | H += mu / 2 * q[i].Z * q[i + 1].Z 15 | for i in range(n): 16 | H += -t * q[i].X - h * q[i].Z 17 | qs.add_evolution(H, T) 18 | return qs 19 | -------------------------------------------------------------------------------- /src/simuq/systems/benchmark/heis_chain.py: -------------------------------------------------------------------------------- 1 | # Generate a Heisenberg model 2 | # H = J \sum_{j=1}^{n} σ_j·σ_{j+1} + h \sum_{j=1}^n X_j (assuming σ_{n+1}=σ_1) 3 | # Here σ_j=(X_j, Y_j, Z_j) 4 | 5 | from simuq.environment import Qubit 6 | from simuq.qsystem import QSystem 7 | 8 | 9 | def GenQS(n, T, J, h): 10 | qs = QSystem() 11 | q = [Qubit(qs) for i in range(n)] 12 | H = 0 13 | for i in range(n - 1): 14 | H = H + J * q[i].X * q[(i + 1) % n].X 15 | H = H + J * q[i].Y * q[(i + 1) % n].Y 16 | H = H + J * q[i].Z * q[(i + 1) % n].Z 17 | for i in range(n): 18 | H = H + h * q[i].X 19 | qs.add_evolution(H, T) 20 | return qs 21 | -------------------------------------------------------------------------------- /src/simuq/systems/benchmark/heis_cycle.py: -------------------------------------------------------------------------------- 1 | # Generate a transverse field cycle Ising model 2 | # H = J \sum_{j=1}^{n} σ_j·σ_{j+1} + h \sum_{j=1}^n X_j (assuming σ_{n+1}=σ_1) 3 | # Here σ_j=(X_j, Y_j, Z_j) 4 | 5 | from simuq.environment import Qubit 6 | from simuq.qsystem import QSystem 7 | 8 | 9 | def GenQS(n, T, J, h): 10 | qs = QSystem() 11 | q = [Qubit(qs) for i in range(n)] 12 | H = 0 13 | for i in range(n): 14 | H = H + J * q[i].X * q[(i + 1) % n].X 15 | H = H + J * q[i].Y * q[(i + 1) % n].Y 16 | H = H + J * q[i].Z * q[(i + 1) % n].Z 17 | for i in range(n): 18 | H = H + h * q[i].X 19 | qs.add_evolution(H, T) 20 | return qs 21 | -------------------------------------------------------------------------------- /src/simuq/config.py: -------------------------------------------------------------------------------- 1 | """ 2 | The class file for global configuration. 3 | 4 | Only a few settings can be changed here, and 5 | the parameters in this file are meant for a 6 | temporary solution for experimental features. 7 | 8 | Be cautious in adding more parameters here. 9 | """ 10 | 11 | class Config: 12 | 13 | __config = { 14 | "TIHamiltonian_tol" : None 15 | } 16 | 17 | @staticmethod 18 | def value(name): 19 | return Config.__config[name] 20 | 21 | @staticmethod 22 | def set(name, value): 23 | if name not in Config.__config: 24 | raise Exception("This configuration does not exist.") 25 | Config.__config[name] = value 26 | -------------------------------------------------------------------------------- /src/simuq/ionq/ionq_api_transpiler.py: -------------------------------------------------------------------------------- 1 | from simuq.backends.ionq_transpiler import IonQTranspiler 2 | from simuq.backends.ionq_transpiler_2Pauli import IonQTranspiler_2Pauli 3 | from simuq.ionq.ionq_api_circuit import IonQAPICircuit 4 | 5 | 6 | class IonQAPITranspiler(IonQTranspiler): 7 | def generate_circuit( 8 | self, n: int, backend, noise_model=None, name: str = "test" 9 | ) -> IonQAPICircuit: 10 | return IonQAPICircuit(n, name, backend, noise_model) 11 | 12 | class IonQAPITranspiler_2Pauli(IonQTranspiler_2Pauli): 13 | def generate_circuit( 14 | self, n: int, backend, noise_model=None, name: str = "test" 15 | ) -> IonQAPICircuit: 16 | return IonQAPICircuit(n, name, backend, noise_model) 17 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [isort] 2 | line_length = 100 3 | multi_line_output = 3 4 | include_trailing_comma = true 5 | 6 | [flake8] 7 | ignore = 8 | # not pep8, black adds whitespace before ':' 9 | E203, 10 | # not pep8, https://www.python.org/dev/peps/pep-0008/#pet-peeves 11 | E231, 12 | # not pep8, black adds line break before binary operator 13 | W503, 14 | # Google Python style is not RST until after processed by Napoleon 15 | # See https://github.com/peterjc/flake8-rst-docstrings/issues/17 16 | RST201,RST203,RST301, 17 | max_line_length = 100 18 | max-complexity = 10 19 | exclude = 20 | __pycache__ 21 | .tox 22 | .git 23 | bin 24 | build 25 | venv 26 | rst-roles = 27 | # Python programming language: 28 | py:func,py:mod,mod 29 | -------------------------------------------------------------------------------- /src/simuq/dwave/dwave_transpiler.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from dwave.system import DWaveSampler, EmbeddingComposite 3 | from simuq.transpiler import Transpiler 4 | 5 | 6 | class DwaveTranspiler(Transpiler): 7 | def transpile(self, sol_gvars, boxes, edges, num_sites): 8 | h = [sol_gvars[i] for i in range(num_sites)] 9 | i = num_sites 10 | J = dict() 11 | for k in range(num_sites): 12 | for j in range(k + 1, num_sites): 13 | J[(j, k)] = num_sites[i] 14 | i += 1 15 | 16 | anneal_schedule = [] 17 | for box in boxes: 18 | s = box[0][0][3][0] 19 | anneal_schedule.append([s, box[1]]) 20 | 21 | return h, J, anneal_schedule 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/simuq/systems/ising_general.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.constants import h 3 | 4 | from simuq.environment import Qubit 5 | from simuq.qsystem import QSystem 6 | 7 | n = 3 8 | m = 3 9 | T = 1 10 | tmp = np.random.uniform(0, 1, (n, n)) 11 | J = (tmp + tmp.T) / 2 12 | qs = QSystem() 13 | qubits = [Qubit(qs) for i in range(n)] 14 | H0 = 0 15 | H1 = 0 16 | for i in range(n): 17 | for j in range(n): 18 | H0 += -J[i][j] * qubits[i].Z * qubits[j].Z 19 | 20 | for i in range(n): 21 | H0 += -h * qubits[i].Z 22 | H1 += qubits[i].X 23 | 24 | 25 | def ising_model(H0, H1, Tau, T): 26 | def f(t): 27 | return H0 - Tau(t / T) * H1 28 | 29 | return f 30 | 31 | 32 | qs.add_time_dependent_evolution(ising_model(H0, H1, lambda t: 1 - t**2, T), np.linspace(0, T, m)) 33 | -------------------------------------------------------------------------------- /src/simuq/aais/ising.py: -------------------------------------------------------------------------------- 1 | from simuq.qmachine import QMachine 2 | from simuq.environment import Qubit 3 | 4 | 5 | def generate_qmachine(n=3): 6 | Ising = QMachine() 7 | qs = [Qubit(Ising) for _ in range(n)] 8 | h = [Ising.add_global_variable() for _ in range(n)] 9 | J = {(j, k): Ising.add_global_variable() for j in range(n) for k in range(n) if j > k} 10 | L = Ising.add_signal_line() 11 | ins = L.add_instruction("native") 12 | s = ins.add_local_variable() 13 | kinetic = s * sum([q.X for q in qs]) 14 | problem = s * (sum([h[j] * qs[j].Z for j in range(n)]) + sum( 15 | [J[(j, k)] * qs[j].Z * qs[k].Z for j in range(n) for k in range(n) if 16 | j > k])) 17 | 18 | ins.set_ham(kinetic + problem) 19 | 20 | return Ising 21 | 22 | 23 | generate_qmachine() 24 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = linters 3 | 4 | [testenv:linters] 5 | basepython = python3 6 | skip_install = true 7 | deps = 8 | {[testenv:isort]deps} 9 | {[testenv:black]deps} 10 | {[testenv:flake8]deps} 11 | commands = 12 | # isort MUST come before black as it will revert any changes made by black 13 | {[testenv:isort]commands} 14 | {[testenv:black]commands} 15 | {[testenv:flake8]commands} 16 | 17 | [testenv:flake8] 18 | basepython = python3 19 | skip_install = true 20 | deps = 21 | flake8 22 | flake8-rst-docstrings 23 | commands = 24 | flake8 {posargs} 25 | 26 | [testenv:isort] 27 | basepython = python3 28 | skip_install = true 29 | deps = 30 | isort 31 | commands = 32 | isort . {posargs} 33 | 34 | [testenv:black] 35 | basepython = python3 36 | skip_install = true 37 | deps = 38 | black 39 | commands = 40 | black ./ {posargs} 41 | -------------------------------------------------------------------------------- /src/simuq/systems/benchmark/schwinger.py: -------------------------------------------------------------------------------- 1 | # Generate a Schwinger model in pure spin Hamiltonian 2 | # The following is described in [Hamer et al., Phys. Rev. D 56, 55-67, 1997] 3 | # H = m/2 Σ_j (-1)^j Z_n + w/2 Σ_{j=1}^{N-1} (X_jX_{j+1}+Y_jY_{j+1}) 4 | # + J Σ_{j=1}^{N-1} (eps_0 + 1/2 Σ_{k=1}^j (Z_k+(-1)^k))^2 5 | 6 | from simuq.environment import Qubit 7 | from simuq.qsystem import QSystem 8 | 9 | 10 | def GenQS(n, T=1, m=0.5, w=1, J=1, eps_0=0): 11 | qs = QSystem() 12 | q = [Qubit(qs) for i in range(n)] 13 | H = 0 14 | for j in range(n): 15 | H += m / 2 * (-1) ** j * q[j].Z 16 | for j in range(n - 1): 17 | H += w / 2 * (q[j].X * q[j + 1].X + q[j].Y * q[j + 1].Y) 18 | for j in range(n - 1): 19 | S = 0 20 | for k in range(j): 21 | S += q[k].Z + (-1) ** k 22 | H = J * (eps_0 + S) * (eps_0 + S) 23 | qs.add_evolution(H, T) 24 | return qs 25 | -------------------------------------------------------------------------------- /src/simuq/systems/qwalk_chain.py: -------------------------------------------------------------------------------- 1 | from simuq.environment import Qubit 2 | from simuq.qsystem import QSystem 3 | 4 | # Quantum walk on a 1D chain 5 | # Antiferromagnetic embedding n = N - 1 6 | # Hpen = \sum_{j=1}^{n-1} Z_j Z_{j+1} + Z_1 + (-1)^n Z_n 7 | # Q = \sum_{j=1}^n X_j 8 | 9 | 10 | def GenQS(N, lamb, T, encoding="antiferromagnetic"): 11 | if encoding == "antiferromagnetic": 12 | n = N - 1 13 | qs = QSystem() 14 | ql = [Qubit(qs) for i in range(n)] 15 | 16 | Hpen = 0 17 | for j in range(n - 1): 18 | Hpen += ql[j].Z * ql[j + 1].Z 19 | Hpen += ql[0].Z 20 | if n % 2 == 0: 21 | Hpen += ql[n - 1].Z 22 | else: 23 | Hpen -= ql[n - 1].Z 24 | 25 | Q = 0 26 | for j in range(n): 27 | Q += ql[j].X 28 | 29 | h = lamb * Hpen + Q 30 | 31 | qs.add_evolution(h, T) 32 | return qs 33 | -------------------------------------------------------------------------------- /src/simuq/systems/bh.py: -------------------------------------------------------------------------------- 1 | from simuq.environment import Boson 2 | from simuq.qsystem import QSystem 3 | 4 | # Bose-Hubbard model, based on arXiv:2209.11153 5 | # Assuming a square lattice 6 | n = 5 # lattice size 7 | qs = QSystem() 8 | bosons = [Boson(qs) for i in range(n)] 9 | 10 | J = 1 11 | U = 0.1 12 | mu = 1 13 | h0 = 0 14 | for x in range(n): 15 | for y in range(n): 16 | i = y * n + x 17 | if x < n: 18 | j = i + 1 # right neighbor 19 | h0 += -J * (bosons[i].a * bosons[j].c + bosons[j].a * bosons[i].c) 20 | if y < n: 21 | j = i + n # upper neighbor 22 | h0 += -J * (bosons[i].a * bosons[j].c + bosons[j].a * bosons[i].c) 23 | 24 | h1 = 0 25 | h2 = 0 26 | for i in range(n * n): 27 | n_hat = bosons[i].a * bosons[i].c 28 | h1 += U * n_hat * (n_hat - 1) / 2 29 | h2 += -mu * n_hat 30 | 31 | 32 | h = h0 + h1 + h2 33 | t = 0.1 34 | qs.add_evolution(h, t) 35 | -------------------------------------------------------------------------------- /src/simuq/systems/benchmark/qhd.py: -------------------------------------------------------------------------------- 1 | # Generate a QHD Hamiltonian for non-convex optimization 2 | # The formulation comes from arXiv: 2303.01471 3 | # Optimizing f(x, y) on [0, 1]^2 4 | # H(α, β) = α (-1/2 ∇^2) + β f(x, y) 5 | # ∇^2 = q^2 Σ_j (X_{x,j} + X_{y,j}) 6 | # x = 1/q Σ_j n_{x,j} 7 | # y = 1/q Σ_j n_{y,j} 8 | 9 | from simuq.environment import Qubit 10 | from simuq.qsystem import QSystem 11 | 12 | 13 | def GenQS(q, T, alpha, beta, f=lambda x, y: -x * x + y * y + x * y): 14 | qs = QSystem() 15 | x, y = [Qubit(qs) for i in range(q)], [Qubit(qs) for i in range(q)] 16 | H, xoper, yoper = 0, 0, 0 17 | for i in range(q): 18 | H += alpha * (-0.5) * q**2 * (x[i].X + y[i].X) 19 | for i in range(q): 20 | xoper += 1.0 / q * (x[i].I - x[i].Z) / 2 21 | yoper += 1.0 / q * (y[i].I - y[i].Z) / 2 22 | for i in range(q): 23 | H += beta * f(xoper, yoper) 24 | qs.add_evolution(H, T) 25 | return qs 26 | -------------------------------------------------------------------------------- /src/simuq/systems/benchmark/qaoa_cycle.py: -------------------------------------------------------------------------------- 1 | # The QAOA algorithm with random parameters. 2 | # H1 = Σ_{(j, k)\in E} Z_jZ_k 3 | # H2 = Σ_j X_j 4 | # The algorithm evolves alternativey under H1 and H2, 5 | # whose length follows the parameter_list 6 | 7 | import numpy as np 8 | 9 | from simuq.environment import Qubit 10 | from simuq.qsystem import QSystem 11 | 12 | 13 | def GenQS(n=12, p=3, parameter_list=None): 14 | qs = QSystem() 15 | q = [Qubit(qs) for i in range(n)] 16 | link = [(i, (i + 1) % n) for i in range(n)] 17 | if parameter_list is None: 18 | parameter_list = np.random.uniform(0, 1, 2 * p) 19 | for i in range(p): 20 | h = 0 21 | for j, k in link: 22 | h += parameter_list[2 * i] * q[j].Z * q[k].Z 23 | qs.add_evolution(h, 1) 24 | h = 0 25 | for j in range(n): 26 | h += parameter_list[2 * i + 1] * q[j].X 27 | qs.add_evolution(h, 1) 28 | return qs 29 | -------------------------------------------------------------------------------- /src/simuq/systems/benchmark/epdynamics.py: -------------------------------------------------------------------------------- 1 | # Generate an electron-photon dynamics model 2 | # The following is described in New J. Phys. 24, 9, 093017 3 | # H = Hel + Hph + Hep 4 | # Hel = 1/2 Σ_j eps_j Z_j + 1/2 Σ_k Σ_j V_jk (X_j X_k + Y_j Y_k) 5 | # Hph = 6 | 7 | from simuq.environment import Qubit 8 | from simuq.qsystem import QSystem 9 | 10 | 11 | def GenQS(n, m, Jx, Jy): 12 | qs = QSystem() 13 | q = [[Qubit(qs) for j in range(m)] for i in range(n)] 14 | H = 0 15 | for i in range(n - 1): 16 | for j in range(m): 17 | H += Jx * q[i][j].X * q[i + 1][j].X 18 | H += Jx * q[i][j].Y * q[i + 1][j].Y 19 | H += Jx * q[i][j].Z * q[i + 1][j].Z 20 | for i in range(n): 21 | for j in range(m - 1): 22 | H += Jx * q[i][j].X * q[i][j + 1].X 23 | H += Jx * q[i][j].Y * q[i][j + 1].Y 24 | H += Jx * q[i][j].Z * q[i][j + 1].Z 25 | qs.add_evolution(H, T) 26 | return qs 27 | -------------------------------------------------------------------------------- /src/simuq/systems/benchmark/o3nlσm.py: -------------------------------------------------------------------------------- 1 | # Generate an 1+1D O(3) NLσM model 2 | # The following is described in Phys. Rev. A 107, 042404 3 | # H = Jx Σ_{(j, k)} σ_{j, k}·σ_{(j+1), k} + Jy Σ_{(j, k)} σ_{j, k}·σ_{j, (k+1)} 4 | # Here σ_{j, k}=(X_{j, k}, Y_{j, k}, Z_{j, k}) 5 | 6 | from simuq.environment import Qubit 7 | from simuq.qsystem import QSystem 8 | 9 | 10 | def GenQS(n, m, T=1, Jx=0.5, Jy=0.8): 11 | qs = QSystem() 12 | q = [[Qubit(qs) for j in range(m)] for i in range(n)] 13 | H = 0 14 | for i in range(n - 1): 15 | for j in range(m): 16 | H += Jx * q[i][j].X * q[i + 1][j].X 17 | H += Jx * q[i][j].Y * q[i + 1][j].Y 18 | H += Jx * q[i][j].Z * q[i + 1][j].Z 19 | for i in range(n): 20 | for j in range(m - 1): 21 | H += Jx * q[i][j].X * q[i][j + 1].X 22 | H += Jx * q[i][j].Y * q[i][j + 1].Y 23 | H += Jx * q[i][j].Z * q[i][j + 1].Z 24 | qs.add_evolution(H, T) 25 | return qs 26 | -------------------------------------------------------------------------------- /src/simuq/systems/qaoa.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from simuq.environment import Qubit 4 | from simuq.qsystem import QSystem 5 | 6 | 7 | def GenQS(n=12, p=3): 8 | qs = QSystem() 9 | ql = [Qubit(qs) for i in range(n)] 10 | link = [(i, (i + 1) % n) for i in range(n)] 11 | parameter_list = ( 12 | np.array( 13 | [ 14 | 0.5702193 * 2, 15 | -0.58631086, 16 | 0.85160685 * 2, 17 | -1.7058538, 18 | 0.29468536 * 2, 19 | -1.132814, 20 | ] 21 | ) 22 | / 2 23 | ) 24 | """ 25 | parameter_list = np.random.uniform(0, 1, 2*p) 26 | """ 27 | for i in range(p): 28 | h = 0 29 | for q0, q1 in link: 30 | h += parameter_list[2 * i] * ql[q0].Z * ql[q1].Z 31 | qs.add_evolution(h, 1) 32 | h = 0 33 | for q0 in range(n): 34 | h += parameter_list[2 * i + 1] * ql[q0].X 35 | qs.add_evolution(h, 1) 36 | return qs 37 | -------------------------------------------------------------------------------- /src/simuq/systems/benchmark/mis_chain.py: -------------------------------------------------------------------------------- 1 | # The Maximal-independent-set Hamiltonian 2 | # H(t) = Δ(t)Σ_j n_j + Ω(t)/2 Σ_j X_j + α Σ_j n_j n_{j+1} 3 | # Here Δ(t) changes linearly from -U to U 4 | # HML discretizes it into D segments 5 | 6 | import numpy as np 7 | 8 | from simuq.environment import Qubit 9 | from simuq.qsystem import QSystem 10 | 11 | 12 | def GenQS(n=3, U=1, Omega=4, alpha=4, T=1, D=10): 13 | def adiabatic(h0, h1): 14 | def f(t): 15 | return h0 + (-1 + 2.0 * t / T) * h1 16 | 17 | return f 18 | 19 | qs = QSystem() 20 | link = [(i, (i + 1) % n) for i in range(n - 1)] 21 | q = [Qubit(qs) for i in range(n)] 22 | noper = [(q[i].I - q[i].Z) / 2 for i in range(n)] 23 | h0 = 0 24 | for q0, q1 in link: 25 | h0 += alpha * noper[q0] * noper[q1] 26 | for i in range(n): 27 | h0 += Omega / 2 * q[i].X 28 | 29 | h1 = 0 30 | for i in range(n): 31 | h1 += -U * noper[i] 32 | 33 | qs.add_time_dependent_evolution(adiabatic(h0, h1), np.linspace(0, T, D + 1)) 34 | 35 | return qs 36 | -------------------------------------------------------------------------------- /src/simuq/systems/floquet.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from simuq.environment import Qubit 4 | from simuq.qsystem import QSystem 5 | 6 | # The Floquet Hamiltonian system in arXiv:2107.07311 7 | 8 | L = 5 9 | n_repetition = 5 10 | Js = [ 11 | 2 * np.pi * 0.01084, 12 | 2 * np.pi * 0.00028, 13 | ] # We assume they are transverse here, which can be specified in detail. 14 | l = len(Js) 15 | geps = 0.1 16 | hs = [0.1, 0.2, 0.3, 0.4, 0.5] 17 | t1 = 0.1 18 | t2 = 0.2 19 | t3 = 0.5 20 | 21 | FloquetQS = QSystem() 22 | qs = FloquetQS 23 | ql = [Qubit(qs) for i in range(L)] 24 | 25 | hflip = 0 26 | for i in range(L): 27 | hflip += geps / 2 * ql[i].X 28 | 29 | hdis = 0 30 | for i in range(L): 31 | hdis += hs[i] / 2 * ql[i].Z 32 | 33 | hint = 0 34 | for j in range(1, l + 1): 35 | for i in range(L - j): 36 | hint += Js[j - 1] / 2 * (ql[i].X * ql[i + j].X + ql[i].Y * ql[i + j].Y) 37 | 38 | for i in range(n_repetition): 39 | qs.add_evolution(hflip, t1) 40 | qs.add_evolution(hdis, t2) 41 | qs.add_evolution(hflip, t1) 42 | qs.add_evolution(hdis, t2) 43 | qs.add_evolution(hint, t3) 44 | -------------------------------------------------------------------------------- /src/simuq/experimental/aais/flux_sc.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from simuq.environment import Qubit 4 | from simuq.expression import Expression 5 | from simuq.qmachine import * 6 | 7 | FluxSCMach = QMachine() 8 | mach = FluxSCMach 9 | L = 5 10 | ql = [Qubit(mach) for i in range(L)] 11 | Js = [ 12 | 2 * np.pi * 0.01084, 13 | 2 * np.pi * 0.00028, 14 | ] # We assume they are transverse here, which can be specified in detail. 15 | l = len(Js) 16 | 17 | 18 | for i in range(L): 19 | Line = SignalLine(mach) 20 | 21 | ins1 = Instruction(Line, "native", "L{}_X_Y".format(i)) 22 | amp = LocalVar(ins1) 23 | phase = LocalVar(ins1) 24 | ins1.set_ham(amp * (Expression.cos(phase) * ql[i].X + Expression.sin(phase) * ql[i].Y)) 25 | 26 | ins2 = Instruction(Line, "derived", "L{}_Z".format(i)) 27 | amp = LocalVar(ins2) 28 | ins2.set_ham(amp * ql[i].Z) 29 | 30 | Line = SignalLine(mach) 31 | 32 | ins = Instruction(Line, "native", "L_int") 33 | amp = LocalVar(ins) 34 | hint = 0 35 | for j in range(1, l + 1): 36 | for i in range(L - j): 37 | hint += Js[j - 1] / 2 * (ql[i].X * ql[i + j].X + ql[i].Y * ql[i + j].Y) 38 | ins.set_ham(amp * hint) 39 | -------------------------------------------------------------------------------- /src/simuq/systems/qiskit_Heis.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | 3 | import numpy as np 4 | from qiskit import IBMQ, QuantumCircuit, QuantumRegister, schedule, transpile 5 | from qiskit_aer import PulseSimulator, QasmSimulator 6 | from qiskit_aer.pulse import PulseSystemModel 7 | 8 | warnings.filterwarnings("ignore") 9 | 10 | if IBMQ.active_account() is None: 11 | IBMQ.load_account() 12 | 13 | provider = IBMQ.get_provider( 14 | hub="ibm-q-community", group="ibmquantumawards", project="open-science-22" 15 | ) 16 | backend = provider.get_backend("ibmq_jakarta") 17 | sim_noisy_jakarta = QasmSimulator.from_backend(backend) 18 | # sim_backend = PulseSimulator() 19 | # backend_model = PulseSystemModel.from_backend(backend) 20 | # sim_backend.set_options(system_model=backend_model) 21 | 22 | n = 7 23 | link = [(0, 1), (1, 2), (1, 3), (3, 5), (4, 5), (5, 6)] 24 | T = np.pi 25 | n_step = 1 26 | 27 | qr = QuantumRegister(7) 28 | qc = QuantumCircuit(qr) 29 | for i in range(n_step): 30 | for q0, q1 in link: 31 | qc.rxx(T / n_step, q0, q1) 32 | qc.ryy(T / n_step, q0, q1) 33 | qc.rzz(T / n_step, q0, q1) 34 | 35 | sch = schedule(transpile(qc, backend), backend) 36 | -------------------------------------------------------------------------------- /src/simuq/systems/hubbard.py: -------------------------------------------------------------------------------- 1 | from simuq.environment import Fermion 2 | from simuq.qsystem import QSystem 3 | 4 | # Fermi-Hubbard model on a 1D chain with open-boundary condition, 5 | # based on arXiv:2010.07965 (but omitted the "spin-dependent local 6 | # potentials" for simplicity). 7 | 8 | 9 | L = 5 # number of lattice sites 10 | # number of fermionic systems. x2 because of possibility of being 11 | # spin up/down at each lattice site. 2*i (2*i+1, resp.) will 12 | # represent a fermion at site i with spin up (down, resp.). 13 | N = 2 * L 14 | 15 | qs = QSystem() 16 | fermions = [Fermion(qs) for _ in range(N)] 17 | nops = [fermions[k].c * fermions[k].a for k in range(N)] # number operators 18 | 19 | J = 0.1 # hopping integral 20 | U = 0.3 # strength of on-site interaction 21 | hh = 0 # hopping term 22 | ho = 0 # on-site interaction term 23 | 24 | for i in range(L - 1): 25 | for s in range(2): 26 | k1, k2 = 2 * i + s, 2 * (i + 1) + s 27 | hh += -J * (fermions[k1].c * fermions[k2].a + fermions[k2].c * fermions[k1].a) 28 | for i in range(L): 29 | ho += U * nops[2 * i] * nops[2 * i + 1] 30 | 31 | h = hh + ho 32 | t = 0.1 33 | qs.add_evolution(h, t) 34 | -------------------------------------------------------------------------------- /src/simuq/aais/ibm_braiding.py: -------------------------------------------------------------------------------- 1 | from simuq.environment import Qubit 2 | from simuq.expression import Expression 3 | from simuq.qmachine import QMachine 4 | 5 | 6 | def generate_qmachine(n=3): 7 | mach = QMachine() 8 | ql = [Qubit(mach) for i in range(n)] 9 | link = [(i, i + 1) for i in range(n - 1)] 10 | 11 | for i in range(n): 12 | L = mach.add_signal_line() 13 | 14 | ins1 = L.add_instruction("native", "L{}_X_Y".format(i)) 15 | amp = ins1.add_local_variable() 16 | phase = ins1.add_local_variable() 17 | ins1.set_ham(amp * (Expression.cos(phase) * ql[i].X + Expression.sin(phase) * ql[i].Y)) 18 | 19 | ins2 = L.add_instruction("derived", "L{}_Z".format(i)) 20 | amp = ins2.add_local_variable() 21 | ins2.set_ham(amp * ql[i].Z) 22 | 23 | for q0, q1 in link: 24 | L = mach.add_signal_line() 25 | 26 | ins = L.add_instruction("derived", "L{}{}_YX".format(q0, q1)) 27 | amp = ins.add_local_variable() 28 | ins.set_ham(amp * ql[q0].Y * ql[q1].X) 29 | 30 | L = mach.add_signal_line() 31 | ins = L.add_instruction("derived", "L{}{}_YZX".format(q0, q1)) 32 | amp = ins.add_local_variable() 33 | ins.set_ham(amp * ql[0].Y * ql[1].Z * ql[2].X) 34 | 35 | return mach 36 | -------------------------------------------------------------------------------- /src/simuq/aais/ionq_braiding.py: -------------------------------------------------------------------------------- 1 | from simuq.environment import Qubit 2 | from simuq.expression import Expression 3 | from simuq.qmachine import QMachine 4 | 5 | 6 | def generate_qmachine(n=3): 7 | mach = QMachine() 8 | ql = [Qubit(mach) for i in range(n)] 9 | link = [[(i, j) for j in range(i)] for i in range(n)] 10 | 11 | for i in range(n): 12 | L = mach.add_signal_line() 13 | 14 | ins1 = L.add_instruction("native", "L{}_X_Y".format(i)) 15 | amp = ins1.add_local_variable() 16 | phase = ins1.add_local_variable() 17 | ins1.set_ham(amp * (Expression.cos(phase) * ql[i].X + Expression.sin(phase) * ql[i].Y)) 18 | 19 | ins2 = L.add_instruction("derived", "L{}_Z".format(i)) 20 | amp = ins2.add_local_variable() 21 | ins2.set_ham(amp * ql[i].Z) 22 | 23 | for q0, q1 in link: 24 | L = mach.add_signal_line() 25 | 26 | ins = L.add_instruction("derived", "L{}{}_YX".format(q0, q1)) 27 | amp = ins.add_local_variable() 28 | ins.set_ham(amp * ql[q0].Y * ql[q1].X) 29 | 30 | L = mach.add_signal_line() 31 | ins = L.add_instruction("derived", "L{}{}_YZX".format(q0, q1)) 32 | amp = ins.add_local_variable() 33 | ins.set_ham(amp * ql[0].Y * ql[1].Z * ql[2].X) 34 | 35 | return mach 36 | -------------------------------------------------------------------------------- /src/simuq/systems/benchmark/mis_grid.py: -------------------------------------------------------------------------------- 1 | # The Maximal-independent-set Hamiltonian 2 | # H(t) = Δ(t)Σ_j n_j + Ω(t)/2 Σ_j X_j + α Σ_{(j, k)\in E} n_j n_k 3 | # Here Δ(t) changes linearly from -U to U, E is a grid lattice 4 | # HML discretizes it into D segments 5 | 6 | import numpy as np 7 | 8 | from simuq.environment import Qubit 9 | from simuq.qsystem import QSystem 10 | 11 | 12 | def GenQS(k=3, U=1, Omega=4, alpha=4, T=1, D=10): 13 | def adiabatic(h0, h1): 14 | def f(t): 15 | return h0 + (-1 + 2.0 * t / T) * h1 16 | 17 | return f 18 | 19 | qs = QSystem() 20 | n = k * k 21 | link = [] 22 | for i in range(k): 23 | for j in range(k): 24 | if j < k - 1: 25 | link.append((i * k + j, i * k + j + 1)) 26 | if i < k - 1: 27 | link.append((i * k + j, (i + 1) * k + j)) 28 | q = [Qubit(qs) for i in range(n)] 29 | noper = [(q[i].I - q[i].Z) / 2 for i in range(n)] 30 | h0 = 0 31 | for q0, q1 in link: 32 | h0 += alpha * noper[q0] * noper[q1] 33 | for i in range(n): 34 | h0 += Omega / 2 * q[i].X 35 | 36 | h1 = 0 37 | for i in range(n): 38 | h1 += -U * noper[i] 39 | 40 | qs.add_time_dependent_evolution(adiabatic(h0, h1), np.linspace(0, T, D + 1)) 41 | 42 | return qs 43 | -------------------------------------------------------------------------------- /src/simuq/experimental/aais/ibm5.py: -------------------------------------------------------------------------------- 1 | from simuq.environment import Qubit 2 | from simuq.expression import Expression 3 | from simuq.qmachine import * 4 | 5 | mach = QMachine() 6 | n = 5 7 | ql = [Qubit(mach) for i in range(n)] 8 | link = [(i, i + 1) for i in range(n - 1)] 9 | 10 | for i in range(n): 11 | L = SignalLine(mach) 12 | 13 | ins1 = Instruction(L, "native", "L{}_X_Y".format(i)) 14 | amp = LocalVar(ins1) 15 | phase = LocalVar(ins1) 16 | ins1.set_ham(amp * (Expression.cos(phase) * ql[i].X + Expression.sin(phase) * ql[i].Y)) 17 | 18 | ins2 = Instruction(L, "derived", "L{}_Z".format(i)) 19 | amp = LocalVar(ins2) 20 | ins2.set_ham(amp * ql[i].Z) 21 | 22 | for q0, q1 in link: 23 | L = SignalLine(mach) 24 | 25 | ins = Instruction(L, "derived", "L{}{}_XX".format(q0, q1)) 26 | amp = LocalVar(ins) 27 | ins.set_ham(amp * ql[q0].X * ql[q1].X) 28 | 29 | ins = Instruction(L, "derived", "L{}{}_YY".format(q0, q1)) 30 | amp = LocalVar(ins) 31 | ins.set_ham(amp * ql[q0].Y * ql[q1].Y) 32 | 33 | ins = Instruction(L, "derived", "L{}{}_ZZ".format(q0, q1)) 34 | amp = LocalVar(ins) 35 | ins.set_ham(amp * ql[q0].Z * ql[q1].Z) 36 | 37 | ins = Instruction(L, "derived", "L{}{}_ZX".format(q0, q1)) 38 | amp = LocalVar(ins) 39 | ins.set_ham(amp * ql[q0].Z * ql[q1].X) 40 | -------------------------------------------------------------------------------- /src/simuq/aais/rydberg1d.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from simuq.environment import Qubit 4 | from simuq.expression import Expression 5 | from simuq.hamiltonian import hlist_sum 6 | from simuq.qmachine import QMachine 7 | 8 | 9 | C_6 = 862690 * 2.0 * np.pi 10 | 11 | 12 | def generate_qmachine(n=3, inits=None): 13 | rydberg = QMachine() 14 | 15 | q = [Qubit(rydberg) for i in range(n)] 16 | 17 | l = C_6 ** (1.0 / 6) 18 | if inits is None: 19 | x = [0] + [rydberg.add_global_variable(init_value=l * i) for i in range(1, n)] 20 | else: 21 | x = [0] + [rydberg.add_global_variable(init_value=inits[i]) for i in range(1, n)] 22 | 23 | noper = [(q[i].I - q[i].Z) / 2 for i in range(n)] 24 | 25 | hlist = [] 26 | for i in range(n): 27 | for j in range(i): 28 | hlist.append((C_6 / (x[i] - x[j]) ** 6) * noper[i] * noper[j]) 29 | sys_h = hlist_sum(hlist) 30 | rydberg.set_sys_ham(sys_h) 31 | 32 | for i in range(n): 33 | L = rydberg.add_signal_line() 34 | ins = L.add_instruction("native", f"Detuning of site {i}") 35 | d = ins.add_local_variable() 36 | ins.set_ham(-d * noper[i]) 37 | 38 | for i in range(n): 39 | L = rydberg.add_signal_line() 40 | ins = L.add_instruction("native") 41 | o = ins.add_local_variable() 42 | p = ins.add_local_variable() 43 | ins.set_ham(o / 2 * (Expression.cos(p) * q[i].X - Expression.sin(p) * q[i].Y)) 44 | 45 | return rydberg 46 | -------------------------------------------------------------------------------- /src/simuq/experimental/aais/ibm7.py: -------------------------------------------------------------------------------- 1 | from simuq.environment import Qubit 2 | from simuq.expression import Expression 3 | from simuq.qmachine import * 4 | 5 | mach = QMachine() 6 | n = 7 7 | ql = [Qubit(mach) for i in range(7)] 8 | link = [(0, 1), (1, 2), (1, 3), (3, 5), (4, 5), (5, 6)] 9 | 10 | for i in range(n): 11 | L = SignalLine(mach) 12 | 13 | ins1 = Instruction(L, "native", "L{}_X_Y".format(i)) 14 | amp = LocalVar(ins1) 15 | phase = LocalVar(ins1) 16 | ins1.set_ham(amp * (Expression.cos(phase) * ql[i].X + Expression.sin(phase) * ql[i].Y)) 17 | 18 | ins2 = Instruction(L, "derived", "L{}_Z".format(i)) 19 | amp = LocalVar(ins2) 20 | ins2.set_ham(amp * ql[i].Z) 21 | 22 | for q0, q1 in link: 23 | L = SignalLine(mach) 24 | 25 | ins = Instruction(L, "derived", "L{}{}_ZX".format(q0, q1)) 26 | amp = LocalVar(ins) 27 | ins.set_ham(amp * ql[q0].Z * ql[q1].X) 28 | 29 | ins = Instruction(L, "derived", "L{}{}_XX".format(q0, q1)) 30 | amp = LocalVar(ins) 31 | ins.set_ham(amp * ql[q0].X * ql[q1].X) 32 | 33 | ins = Instruction(L, "derived", "L{}{}_YY".format(q0, q1)) 34 | amp = LocalVar(ins) 35 | ins.set_ham(amp * ql[q0].Y * ql[q1].Y) 36 | 37 | ins = Instruction(L, "derived", "L{}{}_ZZ".format(q0, q1)) 38 | amp = LocalVar(ins) 39 | ins.set_ham(amp * ql[q0].Z * ql[q1].Z) 40 | 41 | L = SignalLine(mach) 42 | 43 | ins = Instruction(L, "derived", "L{}{}_ZX".format(q1, q0)) 44 | amp = LocalVar(ins) 45 | ins.set_ham(amp * ql[q0].X * ql[q1].Z) 46 | -------------------------------------------------------------------------------- /src/simuq/aais/ionq_arb_2q.py: -------------------------------------------------------------------------------- 1 | from simuq.environment import Qubit 2 | from simuq.expression import Expression 3 | from simuq.qmachine import QMachine 4 | 5 | 6 | def generate_qmachine(n=3): 7 | mach = QMachine() 8 | ql = [Qubit(mach) for i in range(n)] 9 | link = [(i, j) for i in range(n) for j in range(i + 1, n)] 10 | 11 | for i in range(n): 12 | L = mach.add_signal_line() 13 | 14 | ins1 = L.add_instruction("native", "L{}_X_Y".format(i)) 15 | amp = ins1.add_local_variable() 16 | phase = ins1.add_local_variable() 17 | ins1.set_ham(amp * (Expression.cos(phase) * ql[i].X + Expression.sin(phase) * ql[i].Y)) 18 | 19 | ins2 = L.add_instruction("derived", "L{}_Z".format(i)) 20 | amp = ins2.add_local_variable() 21 | ins2.set_ham(amp * ql[i].Z) 22 | 23 | for q0, q1 in link: 24 | L = mach.add_signal_line() 25 | 26 | ins = L.add_instruction("derived", "L{}{}_YX".format(q0, q1)) 27 | amps = [ins.add_local_variable() for i in range(9)] 28 | 29 | ins.set_ham( 30 | amps[0] * ql[q0].X * ql[q1].X 31 | + amps[1] * ql[q0].X * ql[q1].Y 32 | + amps[2] * ql[q0].X * ql[q1].Z 33 | + amps[3] * ql[q0].Y * ql[q1].X 34 | + amps[4] * ql[q0].Y * ql[q1].Y 35 | + amps[5] * ql[q0].Y * ql[q1].Z 36 | + amps[6] * ql[q0].Z * ql[q1].X 37 | + amps[7] * ql[q0].Z * ql[q1].Y 38 | + amps[8] * ql[q0].Z * ql[q1].Z 39 | ) 40 | 41 | return mach 42 | -------------------------------------------------------------------------------- /src/simuq/ibm/qiskit_5.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | from qiskit import QuantumCircuit 3 | 4 | n = 3 5 | 6 | 7 | def clean_as(n, boxes, edges): 8 | link = [(0, 1), (1, 2), (2, 3), (3, 4)] 9 | print(link) 10 | circ = QuantumCircuit(5) 11 | DG = nx.DiGraph() 12 | DG.add_nodes_from([i for i in range(len(boxes))]) 13 | DG.add_edges_from(edges) 14 | topo_order = list(nx.topological_sort(DG)) 15 | for i in range(len(boxes)): 16 | idx = topo_order[i] 17 | t = boxes[idx][1] 18 | for (line, ins), params in boxes[idx][0]: 19 | if line < n: 20 | if ins == 0: 21 | q = line 22 | # circ.rgate(2 * params[0] * t, params[1], q) 23 | circ.rz(-params[1], q) 24 | circ.rx(2 * params[0] * t, q) 25 | circ.rz(params[1], q) 26 | else: 27 | q = line 28 | lamb = 2 * params[0] * t 29 | circ.rz(lamb, q) 30 | else: 31 | (q0, q1) = link[line - n] 32 | theta = 2 * params[0] * t 33 | if ins == 0: 34 | circ.rxx(theta, q0, q1) 35 | elif ins == 1: 36 | circ.ryy(theta, q0, q1) 37 | else: 38 | circ.rzz(theta, q0, q1) 39 | circ.measure_all() 40 | return circ 41 | 42 | 43 | def transpile(alignment, sol_gvars, boxes, edges): 44 | circ = clean_as(n, boxes, edges) 45 | return circ 46 | -------------------------------------------------------------------------------- /src/simuq/aais/heisenberg.py: -------------------------------------------------------------------------------- 1 | from simuq.environment import Qubit 2 | from simuq.expression import Expression 3 | from simuq.qmachine import QMachine 4 | 5 | 6 | def generate_qmachine(n=3, e=None): 7 | mach = QMachine() 8 | ql = [Qubit(mach) for i in range(n)] 9 | 10 | if e is None: 11 | link = [(i, j) for i in range(n) for j in range(i + 1, n)] 12 | else: 13 | link = e 14 | 15 | for i in range(n): 16 | L = mach.add_signal_line() 17 | 18 | ins1 = L.add_instruction("native", "L{}_X_Y".format(i)) 19 | amp = ins1.add_local_variable() 20 | phase = ins1.add_local_variable(lower_bound=-2 * 3.1415926, upper_bound=2 * 3.1415926) 21 | ins1.set_ham(amp * (Expression.cos(phase) * ql[i].X + Expression.sin(phase) * ql[i].Y)) 22 | 23 | ins2 = L.add_instruction("derived", "L{}_Z".format(i)) 24 | amp = ins2.add_local_variable() 25 | ins2.set_ham(amp * ql[i].Z) 26 | 27 | for q0, q1 in link: 28 | L = mach.add_signal_line() 29 | 30 | ins = L.add_instruction("derived", "L{}{}_XX".format(q0, q1)) 31 | amp = ins.add_local_variable() 32 | ins.set_ham(amp * ql[q0].X * ql[q1].X) 33 | 34 | ins = L.add_instruction("derived", "L{}{}_YY".format(q0, q1)) 35 | amp = ins.add_local_variable() 36 | ins.set_ham(amp * ql[q0].Y * ql[q1].Y) 37 | 38 | ins = L.add_instruction("derived", "L{}{}_ZZ".format(q0, q1)) 39 | amp = ins.add_local_variable() 40 | ins.set_ham(amp * ql[q0].Z * ql[q1].Z) 41 | 42 | return mach 43 | -------------------------------------------------------------------------------- /src/simuq/aais/rydberg1d_global.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from simuq.environment import Qubit 4 | from simuq.expression import Expression 5 | from simuq.hamiltonian import hlist_sum 6 | from simuq.qmachine import QMachine 7 | 8 | 9 | C_6 = 862690 * 2.0 * np.pi 10 | 11 | 12 | def generate_qmachine(n=3, inits=None): 13 | rydberg = QMachine() 14 | 15 | q = [Qubit(rydberg) for i in range(n)] 16 | 17 | l = C_6 ** (1.0 / 6) 18 | if inits is None: 19 | x = [0] + [rydberg.add_global_variable(init_value=l * i) for i in range(1, n)] 20 | else: 21 | x = [0] + [rydberg.add_global_variable(init_value=inits[i]) for i in range(1, n)] 22 | 23 | noper = [(q[i].I - q[i].Z) / 2 for i in range(n)] 24 | 25 | hlist = [] 26 | for i in range(n): 27 | for j in range(i): 28 | hlist.append((C_6 / (x[i] - x[j]) ** 6) * noper[i] * noper[j]) 29 | sys_h = hlist_sum(hlist) 30 | rydberg.set_sys_ham(sys_h) 31 | 32 | L = rydberg.add_signal_line() 33 | ins = L.add_instruction("native", "Detuning") 34 | d = ins.add_local_variable() 35 | ham_detuning = 0 36 | for i in range(n): 37 | ham_detuning += -d * noper[i] 38 | ins.set_ham(ham_detuning) 39 | 40 | L = rydberg.add_signal_line() 41 | ins = L.add_instruction("native") 42 | o = ins.add_local_variable() 43 | p = ins.add_local_variable() 44 | ham_rabi = 0 45 | for i in range(n): 46 | ham_rabi += o / 2 * (Expression.cos(p) * q[i].X - Expression.sin(p) * q[i].Y) 47 | ins.set_ham(ham_rabi) 48 | 49 | return rydberg 50 | -------------------------------------------------------------------------------- /src/simuq/systems/benchmark/mzmbraid.py: -------------------------------------------------------------------------------- 1 | # A tri-junction Majorana zero mode braiding experiment 2 | # The following is decribed in [Stenger et al., Phys. Rev. Research 3 033171, 2021] 3 | 4 | from numpy import linspace 5 | 6 | from simuq.environment import Fermion 7 | from simuq.qsystem import QSystem 8 | 9 | 10 | def GenQS(tau=3.3, alpha=3, Jmax=1, D=3, segs=6): 11 | qs = QSystem() 12 | f = [Fermion(qs) for i in range(3)] 13 | 14 | gamma_x = [f[i].a + f[i].c for i in range(3)] 15 | gamma_y = [-1j * (f[i].a - f[i].c) for i in range(3)] 16 | 17 | def model(alpha, J01, J12, J20): 18 | J = [[0, J01, -J20], [-J01, 0, J12], [J20, -J12, 0]] 19 | h = 0 20 | for i in range(3): 21 | h += 1j * alpha * gamma_x[i] * gamma_y[i] 22 | for i in range(3): 23 | for j in range(3): 24 | h += 0.5j * J[i][j] * gamma_x[i] * gamma_x[j] 25 | return h 26 | 27 | def branch(J01_init, J01_end, J12_init, J12_end, J20_init, J20_end): 28 | def ret(t): 29 | J01 = J01_init + (J01_end - J01_init) * (t / tau) 30 | J12 = J12_init + (J12_end - J12_init) * (t / tau) 31 | J20 = J20_init + (J20_end - J20_init) * (t / tau) 32 | return model(alpha, J01, J12, J20) 33 | 34 | return ret 35 | 36 | params = [[Jmax, 0, 0, Jmax, 0, 0], [0, 0, Jmax, 0, 0, Jmax], [0, Jmax, 0, 0, Jmax, 0]] 37 | params = params + params 38 | 39 | for i in range(segs): 40 | qs.add_td_evolution(branch(*params[i]), linspace(0, tau, D + 1)) 41 | 42 | return qs 43 | -------------------------------------------------------------------------------- /src/simuq/systems/mzmbraiding.py: -------------------------------------------------------------------------------- 1 | from simuq.environment import Fermion 2 | from simuq.qsystem import QSystem 3 | 4 | tau = 3.3 5 | alpha = 3 6 | Jmax = 1 7 | D = 3 8 | 9 | qs = QSystem() 10 | f = [Fermion(qs) for i in range(3)] 11 | 12 | gamma_x = [f[i].a + f[i].c for i in range(3)] 13 | gamma_y = [-1j * (f[i].a - f[i].c) for i in range(3)] 14 | 15 | 16 | def model(alpha, J01, J12, J20): 17 | J = [[0, J01, -J20], [-J01, 0, J12], [J20, -J12, 0]] 18 | 19 | h = 0 20 | for i in range(3): 21 | h += 1j * alpha * gamma_x[i] * gamma_y[i] 22 | for i in range(3): 23 | for j in range(3): 24 | h += 0.5j * J[i][j] * gamma_x[i] * gamma_x[j] 25 | 26 | return h 27 | 28 | 29 | def branch(J01_init, J01_end, J12_init, J12_end, J20_init, J20_end): 30 | def ret(t): 31 | J01 = J01_init + (J01_end - J01_init) * (t / tau) 32 | J12 = J12_init + (J12_end - J12_init) * (t / tau) 33 | J20 = J20_init + (J20_end - J20_init) * (t / tau) 34 | return model(alpha, J01, J12, J20) 35 | 36 | return ret 37 | 38 | 39 | qs.add_evolution(model(3, 1, 1, 1), 1) 40 | """ 41 | qs.add_td_evolution(branch(Jmax, 0, 0, Jmax, 0, 0), linspace(0, tau, D)) 42 | qs.add_td_evolution(branch(0, 0, Jmax, 0, 0, Jmax), linspace(0, tau, D)) 43 | qs.add_td_evolution(branch(0, Jmax, 0, 0, Jmax, 0), linspace(0, tau, D)) 44 | 45 | qs.add_td_evolution(branch(Jmax, 0, 0, Jmax, 0, 0), linspace(0, tau, D)) 46 | qs.add_td_evolution(branch(0, 0, Jmax, 0, 0, Jmax), linspace(0, tau, D)) 47 | qs.add_td_evolution(branch(0, Jmax, 0, 0, Jmax, 0), linspace(0, tau, D)) 48 | """ 49 | -------------------------------------------------------------------------------- /src/simuq/aais/two_pauli.py: -------------------------------------------------------------------------------- 1 | from simuq.environment import Qubit 2 | from simuq.expression import Expression 3 | from simuq.qmachine import QMachine 4 | 5 | 6 | def generate_qmachine(n=3, e=None): 7 | mach = QMachine() 8 | ql = [Qubit(mach) for i in range(n)] 9 | 10 | if e is None: 11 | link = [(i, j) for i in range(n) for j in range(i + 1, n)] 12 | else: 13 | link = e 14 | 15 | for i in range(n): 16 | L = mach.add_signal_line() 17 | 18 | ins1 = L.add_instruction("native", "L{}_X_Y".format(i)) 19 | amp = ins1.add_local_variable() 20 | phase = ins1.add_local_variable() 21 | ins1.set_ham(amp * (Expression.cos(phase) * ql[i].X + Expression.sin(phase) * ql[i].Y)) 22 | 23 | ins2 = L.add_instruction("derived", "L{}_Z".format(i)) 24 | amp = ins2.add_local_variable() 25 | ins2.set_ham(amp * ql[i].Z) 26 | 27 | for q0, q1 in link: 28 | L = mach.add_signal_line() 29 | 30 | ins = L.add_instruction("derived", "L{}{}_2_Pauli".format(q0, q1)) 31 | amps = [ins.add_local_variable() for i in range(9)] 32 | 33 | ins.set_ham( 34 | amps[0] * ql[q0].X * ql[q1].X 35 | + amps[1] * ql[q0].X * ql[q1].Y 36 | + amps[2] * ql[q0].X * ql[q1].Z 37 | + amps[3] * ql[q0].Y * ql[q1].X 38 | + amps[4] * ql[q0].Y * ql[q1].Y 39 | + amps[5] * ql[q0].Y * ql[q1].Z 40 | + amps[6] * ql[q0].Z * ql[q1].X 41 | + amps[7] * ql[q0].Z * ql[q1].Y 42 | + amps[8] * ql[q0].Z * ql[q1].Z 43 | ) 44 | 45 | return mach 46 | -------------------------------------------------------------------------------- /src/simuq/systems/mis.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from simuq.environment import Qubit 4 | from simuq.qsystem import QSystem 5 | 6 | 7 | def GenQS(topo="Chain", k=3, dis_num=10, dis_partial=None, Delta=1, Omega=4, alpha=4, T=1): 8 | def adiabatic(h0, h1): 9 | def f(t): 10 | return h0 + (-1 + 2.0 * t / T) * h1 11 | 12 | return f 13 | 14 | qs = QSystem() 15 | 16 | if topo == "Chain": 17 | # Chain 18 | n = k 19 | link = [(i, (i + 1) % n) for i in range(n - 1)] 20 | elif topo == "Cycle": 21 | # Cycle 22 | n = k 23 | link = [(i, (i + 1) % n) for i in range(n)] 24 | elif topo == "Grid": 25 | # Grid 26 | n = k * k 27 | link = [] 28 | for i in range(k): 29 | for j in range(k): 30 | if j < k - 1: 31 | link.append((i * k + j, i * k + j + 1)) 32 | if i < k - 1: 33 | link.append((i * k + j, (i + 1) * k + j)) 34 | 35 | ql = [Qubit(qs) for i in range(n)] 36 | noper = [(ql[i].I - ql[i].Z) / 2 for i in range(n)] 37 | Omega = 4 38 | alpha = 4 39 | h0 = 0 40 | for q0, q1 in link: 41 | h0 += alpha * noper[q0] * noper[q1] 42 | for i in range(n): 43 | h0 += Omega / 2 * ql[i].X 44 | 45 | Delta = 1 46 | h1 = 0 47 | for i in range(n): 48 | h1 += -Delta * noper[i] 49 | 50 | if dis_partial is None: 51 | dis_partial = dis_num 52 | qs.add_time_dependent_evolution( 53 | adiabatic(h0, h1), np.linspace(0, T, dis_num + 1)[: dis_partial + 1] 54 | ) 55 | 56 | return qs 57 | -------------------------------------------------------------------------------- /src/simuq/ibm/qiskit_braiding.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | from qiskit import QuantumCircuit 3 | 4 | from simuq.ibm.qiskit_transpiler import RYXGate, eYZXGate, get_pm 5 | 6 | n = 3 7 | 8 | 9 | def clean_as(n, boxes, edges): 10 | link = [(0, 1), (1, 2)] 11 | print(link) 12 | circ = QuantumCircuit(3) 13 | DG = nx.DiGraph() 14 | DG.add_nodes_from([i for i in range(len(boxes))]) 15 | DG.add_edges_from(edges) 16 | topo_order = list(nx.topological_sort(DG)) 17 | for i in range(len(boxes)): 18 | idx = topo_order[i] 19 | t = boxes[idx][1] 20 | for (line, ins), params in boxes[idx][0]: 21 | if line < n: 22 | if ins == 0: 23 | q = line 24 | # circ.rgate(2 * params[0] * t, params[1], q) 25 | circ.rz(-params[1], q) 26 | circ.rx(2 * params[0] * t, q) 27 | circ.rz(params[1], q) 28 | else: 29 | q = line 30 | lamb = 2 * params[0] * t 31 | circ.rz(lamb, q) 32 | elif line < n + len(link): 33 | (q0, q1) = link[line - n] 34 | theta = 2 * params[0] * t 35 | circ.append(RYXGate(theta), [q0, q1], []) 36 | else: 37 | theta = params[0] * t 38 | circ.append(eYZXGate(theta), [0, 1, 2], []) 39 | 40 | # circ.measure_all() 41 | return circ 42 | 43 | 44 | def transpile(backend, alignment, sol_gvars, boxes, edges): 45 | circ = clean_as(n, boxes, edges) 46 | pm = get_pm(backend, for_braiding=True) 47 | return pm.run(circ) 48 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_namespace_packages, setup 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | with open("src/simuq/_version.py") as f: 7 | version = f.readlines()[-1].split()[-1].strip("\"'") 8 | 9 | setup( 10 | name="simuq", 11 | version=version, 12 | description="A Python package for quantum simulation with analog compilation", 13 | long_description=long_description, 14 | long_description_content_type="text/markdown", 15 | url="https://pickspeng.github.io/SimuQ/", 16 | license="BSD 3-Clause Clear", 17 | packages=find_namespace_packages(where="src"), 18 | package_dir={"": "src"}, 19 | install_requires=[ 20 | "numpy", 21 | "scipy", 22 | "networkx", 23 | ], 24 | extras_require={ 25 | "braket": [ 26 | "matplotlib", 27 | "amazon-braket-sdk", 28 | ], 29 | "ionq": [ 30 | "requests", 31 | ], 32 | "qiskit": [ 33 | "matplotlib", 34 | "qiskit", 35 | "qiskit-ibm-runtime", 36 | "qiskit-aer", 37 | ], 38 | "qutip": [ 39 | "qutip<5", 40 | ], 41 | "dwave": ["dwave-system"], 42 | "all": ["simuq[braket, ionq, qiskit, qutip, dev]"], 43 | "dev": ["tox"], 44 | }, 45 | classifiers=[ 46 | "Development Status :: 4 - Beta", 47 | "Intended Audience :: Science/Research", 48 | "Natural Language :: English", 49 | "License :: OSI Approved :: BSD License", 50 | "Programming Language :: Python", 51 | "Programming Language :: Python :: 3.9", 52 | "Programming Language :: Python :: 3.10", 53 | "Programming Language :: Python :: 3.11", 54 | ], 55 | ) 56 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The Clear BSD License 2 | 3 | Copyright (c) 2023-Present, University of Maryland and developers of SimuQ, 4 | as shown by the AUTHORS file. 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted (subject to the limitations in the disclaimer 9 | below) provided that the following conditions are met: 10 | 11 | * Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 14 | * Redistributions in binary form must reproduce the above copyright 15 | notice, this list of conditions and the following disclaimer in the 16 | documentation and/or other materials provided with the distribution. 17 | 18 | * Neither the name of the copyright holder nor the names of its 19 | contributors may be used to endorse or promote products derived from this 20 | software without specific prior written permission. 21 | 22 | NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY 23 | THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 24 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 27 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 28 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 29 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 30 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 31 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 | POSSIBILITY OF SUCH DAMAGE. 34 | -------------------------------------------------------------------------------- /src/simuq/qutip/qutip_provider.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import qutip as qp 3 | 4 | from simuq.provider import BaseProvider 5 | 6 | 7 | class QuTiPProvider(BaseProvider): 8 | def __init__(self): 9 | self.prog = None 10 | self.fin = None 11 | 12 | def compile(self, qs, initial_state=None, verbose=0): 13 | self.n = qs.num_sites 14 | if initial_state is None: 15 | self.init = qp.basis(1 << self.n) 16 | else: 17 | self.init = initial_state 18 | self.prog = (qs.to_qutip(), qs.total_time()) 19 | self.qs_names = qs.print_sites() 20 | if verbose >= 0: 21 | print("Compiled.") 22 | # return self.prog 23 | 24 | def evaluate_Hamiltonian(self, t): 25 | if self.prog is None: 26 | raise Exception("No compiled job in record.") 27 | M = 0 28 | for i in range(len(self.prog[0])): 29 | (H, f) = self.prog[0][i] 30 | M += H * f(t, None) 31 | return M 32 | 33 | def run(self, shots=None, on_simulator=None, nsteps = None, verbose=0): 34 | if self.prog is None: 35 | raise Exception("No compiled job in record.") 36 | if nsteps == None : 37 | options = qp.solver.Options() 38 | else : 39 | options = qp.solver.Options(nsteps = nsteps) 40 | self.fin = qp.sesolve(self.prog[0], self.init, [0, self.prog[1]], options = options) 41 | if verbose >= 0: 42 | print("Solved.") 43 | # return self.fin 44 | 45 | def results(self, verbose=0): 46 | if self.fin is None: 47 | raise Exception("No submitted job in record.") 48 | self.res = dict() 49 | for i in range(1 << self.n): 50 | self.res[QuTiPProvider.to_bin(i, self.n)] = np.abs(self.fin.states[1][i][0][0]) ** 2 51 | return self.res 52 | -------------------------------------------------------------------------------- /src/simuq/aais/rydberg2d.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from simuq.environment import Qubit 4 | from simuq.expression import Expression 5 | from simuq.hamiltonian import hlist_sum 6 | from simuq.qmachine import QMachine 7 | 8 | 9 | C_6 = 862690 * 2.0 * np.pi 10 | 11 | 12 | def generate_qmachine(n=3, inits=None): 13 | rydberg = QMachine() 14 | 15 | q = [Qubit(rydberg) for i in range(n)] 16 | 17 | l = (C_6 / 4) ** (1.0 / 6) / (2 - 2 * np.cos(2 * np.pi / n)) ** 0.5 18 | 19 | if inits is None: 20 | x = [(0.0, 0.0)] + [ 21 | ( 22 | rydberg.add_global_variable(init_value=l * (np.cos(i * 2 * np.pi / n) - 1)), 23 | rydberg.add_global_variable(init_value=l * np.sin(i * 2 * np.pi / n)), 24 | ) 25 | for i in range(1, n) 26 | ] 27 | else: 28 | x = [(0.0, 0.0)] + [ 29 | ( 30 | rydberg.add_global_variable(init_value=inits[i][0]), 31 | rydberg.add_global_variable(init_value=inits[i][1]), 32 | ) 33 | for i in range(1, n) 34 | ] 35 | noper = [(q[i].I - q[i].Z) / 2 for i in range(n)] 36 | 37 | hlist = [] 38 | for i in range(n): 39 | for j in range(i): 40 | dsqr = (x[i][0] - x[j][0]) ** 2 + (x[i][1] - x[j][1]) ** 2 41 | hlist.append((C_6 / (dsqr**3)) * noper[i] * noper[j]) 42 | sys_h = hlist_sum(hlist) 43 | 44 | rydberg.set_sys_ham(sys_h) 45 | 46 | for i in range(n): 47 | L = rydberg.add_signal_line() 48 | ins = L.add_instruction("native", f"Detuning of site {i}") 49 | d = ins.add_local_variable() 50 | ins.set_ham(-d * noper[i]) 51 | 52 | for i in range(n): 53 | L = rydberg.add_signal_line() 54 | ins = L.add_instruction("native", f"Rabi of site {i}") 55 | o = ins.add_local_variable() 56 | p = ins.add_local_variable() 57 | ins.set_ham(o / 2 * (Expression.cos(p) * q[i].X - Expression.sin(p) * q[i].Y)) 58 | 59 | return rydberg 60 | -------------------------------------------------------------------------------- /src/simuq/backends/bloqade_rydberg2d.py: -------------------------------------------------------------------------------- 1 | def gen_bloqade_code(pos2d, clocks, pulse): 2 | n = len(pos2d) 3 | code = "using Bloqade\n" 4 | code += f"atoms = AtomList({ pos2d }) \n" 5 | code_delta = "delta = [" 6 | code_omega = "omega = [" 7 | code_phi = "phi = [" 8 | for i in range(n): 9 | code_delta += f"piecewise_constant(clocks = { clocks }, values = { pulse[0][i] }), " 10 | code_omega += f"piecewise_constant(clocks = { clocks }, values = { pulse[1][i] }), " 11 | code_phi += f"piecewise_constant(clocks = { clocks }, values = { pulse[2][i] }), " 12 | code_omega += "]\n" 13 | code_phi += "]\n" 14 | code_delta += "]\n" 15 | code += code_omega + code_phi + code_delta 16 | code += "h = rydberg_h(atoms; Δ = delta, Ω = omega, ϕ = phi)" 17 | return code 18 | 19 | 20 | def gen_clocks(times): 21 | clocks = [0.0] 22 | for t in times: 23 | clocks.append(clocks[-1] + t) 24 | return clocks 25 | 26 | 27 | def clean_as(alignment, sol_gvars, boxes): 28 | # pos = [0., 0., 0.] + sol_gvars 29 | pos = [0.0, 0.0, *sol_gvars] 30 | pos2d = [] 31 | for i in range(len(pos) // 2): 32 | pos2d.append((pos[2 * i], pos[2 * i + 1])) 33 | n = len(pos2d) 34 | m = len(boxes) 35 | times = [] 36 | pulse = [[[0.0 for i in range(m)] for j in range(n)] for k in range(3)] 37 | for evo_idx in range(m): 38 | box = boxes[evo_idx] 39 | times.append(box[1]) 40 | for (i, j), ins, h, ins_lvars in box[0]: 41 | if i < n: 42 | pulse[0][i][evo_idx] = ins_lvars[0] 43 | else: 44 | pulse[1][i - n][evo_idx] = ins_lvars[0] 45 | pulse[2][i - n][evo_idx] = ins_lvars[1] 46 | 47 | return (pos2d, gen_clocks(times), pulse) 48 | 49 | 50 | def transpile(alignment, sol_gvars, boxes, edges): 51 | code = gen_bloqade_code(*clean_as(alignment, sol_gvars, boxes)) 52 | with open("transpiled.jl", "w") as f: 53 | print(code, file=f) 54 | return code 55 | -------------------------------------------------------------------------------- /src/simuq/aais/rydberg2d_global.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from simuq.environment import Qubit 4 | from simuq.expression import Expression 5 | from simuq.hamiltonian import hlist_sum 6 | from simuq.qmachine import QMachine 7 | 8 | 9 | C_6 = 862690 * 2.0 * np.pi 10 | 11 | 12 | def generate_qmachine(n=3, inits=None): 13 | rydberg = QMachine() 14 | 15 | q = [Qubit(rydberg) for i in range(n)] 16 | 17 | l = (C_6 / 4) ** (1.0 / 6) / (2 - 2 * np.cos(2 * np.pi / n)) ** 0.5 18 | if inits is None: 19 | x = [(0.0, 0.0)] + [ 20 | ( 21 | rydberg.add_global_variable(init_value=l * (np.cos(i * 2 * np.pi / n) - 1)), 22 | rydberg.add_global_variable(init_value=l * np.sin(i * 2 * np.pi / n)), 23 | ) 24 | for i in range(1, n) 25 | ] 26 | else: 27 | x = [(0.0, 0.0)] + [ 28 | ( 29 | rydberg.add_global_variable(init_value=inits[i][0]), 30 | rydberg.add_global_variable(init_value=inits[i][1]), 31 | ) 32 | for i in range(1, n) 33 | ] 34 | noper = [(q[i].I - q[i].Z) / 2 for i in range(n)] 35 | 36 | hlist = [] 37 | for i in range(n): 38 | for j in range(i): 39 | dsqr = (x[i][0] - x[j][0]) ** 2 + (x[i][1] - x[j][1]) ** 2 40 | hlist.append((C_6 / (dsqr**3)) * noper[i] * noper[j]) 41 | sys_h = hlist_sum(hlist) 42 | 43 | rydberg.set_sys_ham(sys_h) 44 | 45 | L = rydberg.add_signal_line() 46 | ins = L.add_instruction("native", "Detuning") 47 | d = ins.add_local_variable() 48 | ham_detuning = 0 49 | for i in range(n): 50 | ham_detuning += -d * noper[i] 51 | ins.set_ham(ham_detuning) 52 | 53 | L = rydberg.add_signal_line() 54 | ins = L.add_instruction("native", "Rabi") 55 | o = ins.add_local_variable() 56 | p = ins.add_local_variable() 57 | ham_rabi = 0 58 | for i in range(n): 59 | ham_rabi += o / 2 * (Expression.cos(p) * q[i].X - Expression.sin(p) * q[i].Y) 60 | ins.set_ham(ham_rabi) 61 | 62 | return rydberg 63 | -------------------------------------------------------------------------------- /src/simuq/experimental/aais/ibm_27.py: -------------------------------------------------------------------------------- 1 | from simuq.environment import Qubit 2 | from simuq.expression import Expression 3 | from simuq.qmachine import * 4 | 5 | mach = QMachine() 6 | n = 7 7 | ql = [Qubit(mach) for i in range(27)] 8 | 9 | IBMQ.load_account() 10 | 11 | provider = IBMQ.get_provider(hub="ibm-q-ornl", group="ornl", project="phy147") 12 | backend = provider.get_backend("ibm_auckland") 13 | 14 | control_line_by_system = { 15 | tuple(v["operates"]["qubits"]): int(k.strip("u")) 16 | for k, v in backend.configuration().channels.items() 17 | if v["purpose"] == "cross-resonance" 18 | } 19 | 20 | link = list(self.control_line_by_system.keys()) 21 | print("New list of connected pairs for IBM system: " + str(link)) 22 | 23 | # link = [(0, 1), (1, 2), (2, 3), (3, 5), (5, 8), (8, 9), (8, 11), (11, 14), (1, 4), (4, 7), (6, 7), (7, 10), (10, 12), (12, 13), (13, 14), (12, 15), (15, 18), (17, 18), (18, 21), (21, 23), (23, 24), (14, 16), (16, 19), (19, 20), (19, 22), (22, 25), (24, 25), (25, 26)] 24 | 25 | for i in range(n): 26 | L = SignalLine(mach) 27 | 28 | ins1 = Instruction(L, "native", "L{}_X_Y".format(i)) 29 | amp = LocalVar(ins1) 30 | phase = LocalVar(ins1) 31 | ins1.set_ham(amp * (Expression.cos(phase) * ql[i].X + Expression.sin(phase) * ql[i].Y)) 32 | 33 | ins2 = Instruction(L, "derived", "L{}_Z".format(i)) 34 | amp = LocalVar(ins2) 35 | ins2.set_ham(amp * ql[i].Z) 36 | 37 | for q0, q1 in link: 38 | L = SignalLine(mach) 39 | 40 | ins = Instruction(L, "derived", "L{}{}_ZX".format(q0, q1)) 41 | amp = LocalVar(ins) 42 | ins.set_ham(amp * ql[q0].Z * ql[q1].X) 43 | 44 | ins = Instruction(L, "derived", "L{}{}_XX".format(q0, q1)) 45 | amp = LocalVar(ins) 46 | ins.set_ham(amp * ql[q0].X * ql[q1].X) 47 | 48 | ins = Instruction(L, "derived", "L{}{}_YY".format(q0, q1)) 49 | amp = LocalVar(ins) 50 | ins.set_ham(amp * ql[q0].Y * ql[q1].Y) 51 | 52 | ins = Instruction(L, "derived", "L{}{}_ZZ".format(q0, q1)) 53 | amp = LocalVar(ins) 54 | ins.set_ham(amp * ql[q0].Z * ql[q1].Z) 55 | 56 | # L = SignalLine(mach) 57 | 58 | # ins = Instruction(L, 'derived', 'L{}{}_ZX'.format(q1, q0)) 59 | # amp = LocalVar(ins) 60 | # ins.set_ham(amp * ql[q0].X() * ql[q1].Z()) 61 | -------------------------------------------------------------------------------- /src/simuq/experimental/aais/ibm_hypothetical.py: -------------------------------------------------------------------------------- 1 | from qiskit.pulse import DriveChannel, GaussianSquare, ShiftPhase 2 | 3 | from simuq.environment import Qubit 4 | from simuq.qmachine import * 5 | 6 | 7 | def get_mach(backend): 8 | configuration = backend.configuration() 9 | defaults = backend.defaults() 10 | n = configuration.num_qubits 11 | mach = QMachine() 12 | 13 | connected_pairs_cnot = configuration.coupling_map 14 | instruction_schedule_map = defaults.instruction_schedule_map 15 | 16 | def get_control_qubit(q1, q2): # Control performs Z 17 | cx_sched = instruction_schedule_map.get("cx", qubits=(q1, q2)) 18 | 19 | for time, inst in cx_sched.instructions: 20 | if isinstance(inst.channel, DriveChannel) and not isinstance(inst, ShiftPhase): 21 | if isinstance(inst.pulse, GaussianSquare): 22 | target = inst.channel.index 23 | control = q1 if target == q2 else q2 24 | return control 25 | 26 | ql = [Qubit(mach) for i in range(n)] 27 | link = [] 28 | for item in connected_pairs_cnot: 29 | if get_control_qubit(item[0], item[1]) == item[0]: 30 | link.append(item) 31 | 32 | for i in range(n): 33 | L = SignalLine(mach) 34 | 35 | ins1 = Instruction(L, "native", "L{}_X_Y".format(i)) 36 | amp = LocalVar(ins1) 37 | phase = LocalVar(ins1) 38 | ins1.set_ham(amp * (Expression.cos(phase) * ql[i].X + Expression.sin(phase) * ql[i].Y)) 39 | 40 | ins2 = Instruction(L, "derived", "L{}_Z".format(i)) 41 | amp = LocalVar(ins2) 42 | ins2.set_ham(amp * ql[i].Z) 43 | 44 | for q0, q1 in link: 45 | L = SignalLine(mach) 46 | 47 | ins = Instruction(L, "native", "L{}{}_ZX".format(q0, q1)) 48 | amp = LocalVar(ins) 49 | ins.set_ham(amp * ql[q0].Z * ql[q1].X) 50 | 51 | ins = Instruction(L, "native", "L{}{}_XX".format(q0, q1)) 52 | amp = LocalVar(ins) 53 | ins.set_ham(amp * ql[q0].X * ql[q1].X) 54 | 55 | ins = Instruction(L, "native", "L{}{}_YY".format(q0, q1)) 56 | amp = LocalVar(ins) 57 | ins.set_ham(amp * ql[q0].Y * ql[q1].Y) 58 | 59 | ins = Instruction(L, "native", "L{}{}_ZZ".format(q0, q1)) 60 | amp = LocalVar(ins) 61 | ins.set_ham(amp * ql[q0].Z * ql[q1].Z) 62 | return mach 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | pip-wheel-metadata/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | .python-version 88 | 89 | # pipenv 90 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 91 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 92 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 93 | # install all needed dependencies. 94 | #Pipfile.lock 95 | 96 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 97 | __pypackages__/ 98 | 99 | # Celery stuff 100 | celerybeat-schedule 101 | celerybeat.pid 102 | 103 | # SageMath parsed files 104 | *.sage.py 105 | 106 | # Environments 107 | .env 108 | .venv 109 | env/ 110 | venv/ 111 | ENV/ 112 | env.bak/ 113 | venv.bak/ 114 | 115 | # IDE settings 116 | .spyderproject 117 | .spyproject 118 | *.idea 119 | *.iml 120 | .vscode/* 121 | 122 | # Rope project settings 123 | .ropeproject 124 | 125 | # mkdocs documentation 126 | /site 127 | 128 | # mypy 129 | .mypy_cache/ 130 | .dmypy.json 131 | dmypy.json 132 | 133 | # Pyre type checker 134 | .pyre/ 135 | .DS_Store -------------------------------------------------------------------------------- /src/simuq/backends/ionq_qiskit_qis.py: -------------------------------------------------------------------------------- 1 | # Currently this file is not usable: 2 | # AWS Braket does not support arbitrary-angle MS gate for now 3 | # Will be tested later 4 | # If you want to use IonQ's device through braket, 5 | # try qiskit_iontrap.py which generates a circuit in bk_c. 6 | 7 | import networkx as nx 8 | from qiskit import QuantumCircuit 9 | 10 | # import gates 11 | # import utils 12 | from qiskit_ionq import GPI2Gate, GPIGate, IonQProvider, MSGate 13 | 14 | 15 | def clean_as(n, boxes, edges): 16 | link = [(i, j) for i in range(n) for j in range(i + 1, n)] 17 | # link = [(i, (i + 1) % 12) for i in range(n)] 18 | DG = nx.DiGraph() 19 | DG.add_nodes_from([i for i in range(len(boxes))]) 20 | DG.add_edges_from(edges) 21 | topo_order = list(nx.topological_sort(DG)) 22 | circ = QuantumCircuit(n, n) 23 | accum_phase = [0 for i in range(n)] 24 | 25 | # Generate unitary GPI2(a)*GPI(b)*GPI2(c) 26 | 27 | for i in range(len(boxes)): 28 | idx = topo_order[i] 29 | t = boxes[idx][1] 30 | for (line, ins), params in boxes[idx][0]: 31 | if line < n: 32 | if ins == 0: 33 | q = line 34 | rot = params[0] * t 35 | phi = params[1] 36 | # expm(-i*rot*(cos(phi)X+sin(phi)Y))=I-i*rot*(cos(phi)X+sin(phi)Y) 37 | circ.rz(-phi, q) 38 | circ.rx(2 * rot, q) 39 | circ.rz(phi, q) 40 | 41 | else: 42 | q = line 43 | # Rz(q, 2 * params[0] * t) 44 | rot = params[0] * t 45 | circ.rz(2 * rot, q) 46 | 47 | else: 48 | (q0, q1) = link[line - n] 49 | theta = 2 * params[0] * t 50 | if ins == 0: 51 | # R_XX(theta) 52 | if abs(theta) > 1e-5: 53 | circ.rxx(theta, q0, q1) 54 | elif ins == 1: 55 | # R_YY(theta) 56 | if abs(theta) > 1e-5: 57 | # Hadamard on q0 58 | circ.ryy(theta, q0, q1) 59 | else: 60 | # R_ZZ(theta) 61 | if abs(theta) > 1e-5: 62 | # Hadamard on q0 63 | circ.rzz(theta, q0, q1) 64 | # circ.measure(np.arange(n), np.arange(n)) 65 | return circ, accum_phase 66 | 67 | 68 | def transpile(alignment, sol_gvars, boxes, edges): 69 | n = len(alignment) 70 | circ, accum_phase = clean_as(n, boxes, edges) 71 | return circ 72 | -------------------------------------------------------------------------------- /src/simuq/aais/ibm.py: -------------------------------------------------------------------------------- 1 | from qiskit.pulse import DriveChannel, GaussianSquare, ShiftPhase 2 | 3 | from simuq.environment import Qubit 4 | from simuq.expression import Expression 5 | from simuq.qmachine import QMachine 6 | 7 | 8 | def generate_qmachine(backend): 9 | configuration = backend.configuration() 10 | defaults = backend.defaults() 11 | n = configuration.num_qubits 12 | mach = QMachine() 13 | 14 | connected_pairs_cnot = configuration.coupling_map 15 | instruction_schedule_map = defaults.instruction_schedule_map 16 | 17 | def get_control_qubit(q1, q2): # Control performs Z 18 | cx_sched = instruction_schedule_map.get("ecr", qubits=(q1, q2)) 19 | supported = False 20 | for time, inst in cx_sched.instructions: 21 | if isinstance(inst.channel, DriveChannel) and not isinstance(inst, ShiftPhase): 22 | if isinstance(inst.pulse, GaussianSquare): 23 | target = inst.channel.index 24 | control = q1 if target == q2 else q2 25 | supported = True 26 | if not supported: 27 | raise ValueError("This machine is not supported!") 28 | return control 29 | 30 | ql = [Qubit(mach) for i in range(n)] 31 | link = [] 32 | for item in connected_pairs_cnot: 33 | if get_control_qubit(item[0], item[1]) == item[0]: 34 | link.append(item) 35 | 36 | for i in range(n): 37 | L = mach.add_signal_line() 38 | 39 | ins1 = L.add_instruction("native", "L{}_X_Y".format(i)) 40 | amp = ins1.add_local_variable() 41 | phase = ins1.add_local_variable() 42 | ins1.set_ham(amp * (Expression.cos(phase) * ql[i].X + Expression.sin(phase) * ql[i].Y)) 43 | 44 | ins2 = L.add_instruction("derived", "L{}_Z".format(i)) 45 | amp = ins2.add_local_variable() 46 | ins2.set_ham(amp * ql[i].Z) 47 | 48 | for q0, q1 in link: 49 | L = mach.add_signal_line() 50 | 51 | ins = L.add_instruction("derived", "L{}{}_ZX".format(q0, q1)) 52 | amp = ins.add_local_variable() 53 | ins.set_ham(amp * ql[q0].Z * ql[q1].X) 54 | 55 | ins = L.add_instruction("derived", "L{}{}_XX".format(q0, q1)) 56 | amp = ins.add_local_variable() 57 | ins.set_ham(amp * ql[q0].X * ql[q1].X) 58 | 59 | ins = L.add_instruction("derived", "L{}{}_YY".format(q0, q1)) 60 | amp = ins.add_local_variable() 61 | ins.set_ham(amp * ql[q0].Y * ql[q1].Y) 62 | 63 | ins = L.add_instruction("derived", "L{}{}_ZZ".format(q0, q1)) 64 | amp = ins.add_local_variable() 65 | ins.set_ham(amp * ql[q0].Z * ql[q1].Z) 66 | return mach 67 | -------------------------------------------------------------------------------- /src/simuq/qsystem.py: -------------------------------------------------------------------------------- 1 | """ 2 | The class for target Hamiltonian. 3 | 4 | It is a container of the desired piecewise 5 | constant evolution. 6 | """ 7 | 8 | from simuq.environment import BaseQuantumEnvironment, Boson, Fermion, Qubit 9 | from simuq.hamiltonian import TIHamiltonian 10 | 11 | 12 | class QSystem(BaseQuantumEnvironment): 13 | """A target quantum system. 14 | 15 | It contains a list of evolutions (h, t), which 16 | represents evolving under h for time duration t. 17 | 18 | We also provide a syntax sugar to discretize 19 | a continuous-time Hamiltonian. 20 | """ 21 | 22 | def __init__(self): 23 | super().__init__() 24 | self.evos = [] 25 | 26 | def add_evolution(self, h, t): 27 | #h.extend_ham_by_sites() 28 | if isinstance(h, (int, float, complex)): 29 | h = h * TIHamiltonian.identity(self.sites_type, self.sites_name) 30 | h.cleanHam() 31 | self.evos.append((h, t)) 32 | 33 | def add_time_dependent_evolution(self, ht, ts): 34 | for i in range(len(ts) - 1): 35 | self.add_evolution(ht(ts[i]), ts[i + 1] - ts[i]) 36 | 37 | def add_td_evolution(self, ht, ts): 38 | return self.add_time_dependent_evolution(ht, ts) 39 | 40 | def clear_evos(self): 41 | self.evos = [] 42 | 43 | def to_qutip(self): 44 | def time_indicator(tl, tr): 45 | def ret(t, args): 46 | if tl <= t and t < tr: 47 | return 1 48 | else: 49 | return 0 50 | 51 | return ret 52 | 53 | ret = [] 54 | sumt = 0 55 | for h, t in self.evos: 56 | #h.extend_ham_by_sites() 57 | ret.append([h.to_qutip_qobj(), time_indicator(sumt, sumt + t)]) 58 | sumt += t 59 | 60 | return ret 61 | 62 | def total_time(self): 63 | ret = 0 64 | for h, t in self.evos: 65 | ret += t 66 | return ret 67 | 68 | def print_sites(self): 69 | name_list = [] 70 | for site in self.sites: 71 | name_list.append(site.name) 72 | return name_list 73 | 74 | def __repr__(self): 75 | strl = ["Quantum system:\n", "- Sites: "] 76 | all_names = self.print_sites() 77 | strl += [s for name in all_names for s in (name, " ")] 78 | strl.append("\n- Sequentially evolves:\n") 79 | for (h, t) in self.evos: 80 | strl += [f" Time = {t}, TIHamiltonian = ", h.__repr__(), "\n"] 81 | return "".join(strl) 82 | 83 | if __name__ == "__main__": 84 | # This is to keep the classes under qsystem. 85 | tmp = Qubit(), Boson(), Fermion() 86 | -------------------------------------------------------------------------------- /src/simuq/backends/ionq_qiskit_qis_qaoa.py: -------------------------------------------------------------------------------- 1 | # Currently this file is not usable: 2 | # AWS Braket does not support arbitrary-angle MS gate for now 3 | # Will be tested later 4 | # If you want to use IonQ's device through braket, 5 | # try qiskit_iontrap.py which generates a circuit in bk_c. 6 | 7 | import networkx as nx 8 | import numpy as np 9 | from qiskit import QuantumCircuit 10 | 11 | # import gates 12 | # import utils 13 | from qiskit_ionq import GPI2Gate, GPIGate, IonQProvider, MSGate 14 | 15 | 16 | def clean_as(n, boxes, edges): 17 | link = [(i, j) for i in range(n) for j in range(i + 1, n)] 18 | # link = [(i, (i + 1) % 12) for i in range(n)] 19 | DG = nx.DiGraph() 20 | DG.add_nodes_from([i for i in range(len(boxes))]) 21 | DG.add_edges_from(edges) 22 | topo_order = list(nx.topological_sort(DG)) 23 | circ = QuantumCircuit(n, n) 24 | accum_phase = [0 for i in range(n)] 25 | 26 | # Generate unitary GPI2(a)*GPI(b)*GPI2(c) 27 | 28 | for i in range(len(boxes)): 29 | idx = topo_order[i] 30 | t = boxes[idx][1] 31 | for (line, ins), params in boxes[idx][0]: 32 | if line < n: 33 | if ins == 0: 34 | q = line 35 | rot = params[0] * t 36 | phi = params[1] 37 | # expm(-i*rot*(cos(phi)X+sin(phi)Y))=I-i*rot*(cos(phi)X+sin(phi)Y) 38 | circ.rz(-phi, q) 39 | circ.rx(2 * rot, q) 40 | circ.rz(phi, q) 41 | 42 | else: 43 | q = line 44 | # Rz(q, 2 * params[0] * t) 45 | rot = params[0] * t 46 | circ.rz(2 * rot, q) 47 | 48 | else: 49 | (q0, q1) = link[line - n] 50 | theta = 2 * params[0] * t 51 | if ins == 0: 52 | # R_XX(theta) 53 | if abs(theta) > 1e-5: 54 | circ.rxx(theta, q0, q1) 55 | elif ins == 1: 56 | # R_YY(theta) 57 | if abs(theta) > 1e-5: 58 | # Hadamard on q0 59 | circ.ryy(theta, q0, q1) 60 | else: 61 | # R_ZZ(theta) 62 | if abs(theta) > 1e-5: 63 | # Hadamard on q0 64 | circ.rzz(theta, q0, q1) 65 | for i in range(12): 66 | circ.h(i) 67 | circ.measure(np.arange(n), np.arange(n)) 68 | return circ, accum_phase 69 | 70 | 71 | def transpile(alignment, sol_gvars, boxes, edges): 72 | n = len(alignment) 73 | circ, accum_phase = clean_as(n, boxes, edges) 74 | return circ 75 | -------------------------------------------------------------------------------- /src/simuq/backends/qiskit_iontrap.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | from qiskit import QuantumCircuit 3 | from qiskit.circuit.library import RGate 4 | 5 | 6 | def clean_as(n, boxes, edges): 7 | link = [(i, j) for i in range(n) for j in range(i + 1, n)] 8 | # print(link) 9 | circ = QuantumCircuit(n) 10 | DG = nx.DiGraph() 11 | DG.add_nodes_from([i for i in range(len(boxes))]) 12 | DG.add_edges_from(edges) 13 | topo_order = list(nx.topological_sort(DG)) 14 | bk_c = """from braket.circuits import Circuit 15 | circ = Circuit()""" 16 | for i in range(len(boxes)): 17 | idx = topo_order[i] 18 | t = boxes[idx][1] 19 | for (line, ins), params in boxes[idx][0]: 20 | if line < n: 21 | if ins == 0: 22 | q = line 23 | circ.append(RGate(2 * params[0] * t, params[1]), [q]) 24 | circ.barrier() 25 | # circ.rz(-params[1],q) 26 | # circ.rx(2 * params[0] * t,q) 27 | # circ.rz(params[1],q) 28 | if abs(params[1]) > 1e-5: 29 | bk_c += f".rz({q}, {-params[1]})" 30 | if abs(2 * params[0] * t) > 1e-5: 31 | bk_c += f".rx({q}, {2 * params[0] * t})" 32 | if abs(params[1]) > 1e-5: 33 | bk_c += f".rz({q}, {params[1]})" 34 | 35 | else: 36 | q = line 37 | lamb = 2 * params[0] * t 38 | if abs(lamb) > 1e-5: 39 | circ.rz(lamb, q) 40 | bk_c += f".rz({q}, {lamb})" 41 | else: 42 | # print(line) 43 | (q0, q1) = link[line - n] 44 | theta = 2 * params[0] * t 45 | if ins == 0: 46 | if abs(theta) > 1e-5: 47 | circ.rxx(theta, q0, q1) 48 | circ.barrier() 49 | bk_c += f".xx({q0}, {q1}, {theta})" 50 | elif ins == 1: 51 | if abs(theta) > 1e-5: 52 | circ.ryy(theta, q0, q1) 53 | circ.barrier() 54 | bk_c += f".yy({q0}, {q1}, {theta})" 55 | else: 56 | if abs(theta) > 1e-5: 57 | circ.rzz(theta, q0, q1) 58 | circ.barrier() 59 | bk_c += f".zz({q0}, {q1}, {theta})" 60 | circ.measure_all() 61 | return circ, bk_c 62 | 63 | 64 | def transpile(alignment, sol_gvars, boxes, edges): 65 | n = len(alignment) 66 | circ = clean_as(n, boxes, edges) 67 | return circ 68 | -------------------------------------------------------------------------------- /src/simuq/aais/rigetti.py: -------------------------------------------------------------------------------- 1 | from simuq.environment import Qubit 2 | from simuq.expression import Expression 3 | from simuq.qmachine import QMachine 4 | 5 | 6 | def generate_qmachine(): 7 | mach = QMachine() 8 | rings_per_row = 5 9 | qubits_per_ring = 8 10 | row_count = 2 11 | qubits = {} 12 | for row in range(row_count): 13 | for r in range(rings_per_row): 14 | for q in range(qubits_per_ring): 15 | qubit_index = row * 100 + r * 10 + q 16 | qubits[qubit_index] = Qubit(mach) 17 | 18 | links = [] 19 | for row in range(row_count): 20 | for r in range(rings_per_row): 21 | for q in range(qubits_per_ring): 22 | qubit_index = row * 100 + r * 10 + q 23 | qubit_neighbor_index = row * 100 + r * 10 + ((q - 1) % qubits_per_ring) 24 | L = mach.add_signal_line() 25 | ins1 = L.add_instruction("native", "L{}_X_Y".format(qubit_index)) 26 | amp = ins1.add_local_variable() 27 | phase = ins1.add_local_variable() 28 | ins1.set_ham( 29 | amp 30 | * ( 31 | Expression.cos(phase) * qubits[qubit_index].X 32 | + Expression.sin(phase) * qubits[qubit_index].Y 33 | ) 34 | ) 35 | 36 | ins2 = L.add_instruction("derived", "L{}_Z".format(qubit_index)) 37 | amp = ins2.add_local_variable() 38 | ins2.set_ham(amp * qubits[qubit_index].Z) 39 | 40 | # All eight links between qubits in a ring 41 | links.append((qubit_index, qubit_neighbor_index)) 42 | 43 | for r in range(rings_per_row - 1): 44 | # Links between adjacent rings 45 | # Note we match indices 1-6 and 2-5, and this is hardcoded, as in an eight-qubit ring topology 46 | links.append((row * 100 + r * 10 + 1, row * 100 + r * 10 + 16)) 47 | links.append((row * 100 + r * 10 + 2, row * 100 + r * 10 + 15)) 48 | 49 | for row in range(row_count - 1): 50 | for r in range(rings_per_row): 51 | # Links between adjacent rows 52 | # Note we match indices 7-4 and 0-3, and this is hardcoded, as in an eight-qubit ring topology 53 | links.append((row * 100 + r * 10 + 7, row * 100 + r * 10 + 104)) 54 | links.append((row * 100 + r * 10, row * 100 + r * 10 + 103)) 55 | 56 | for q0, q1 in links: 57 | L = mach.add_signal_line() 58 | 59 | ins = L.add_instruction("derived", "L{}{}_XX_YY".format(q0, q1)) 60 | amp = ins.add_local_variable() 61 | ins.set_ham(amp * (qubits[q0].X * qubits[q1].X + qubits[q0].Y + qubits[q1].Y)) 62 | 63 | ins = L.add_instruction("derived", "L{}{}_ZZ".format(q0, q1)) 64 | amp = ins.add_local_variable() 65 | ins.set_ham(amp * qubits[q0].Z * qubits[q1].Z) 66 | 67 | return mach 68 | -------------------------------------------------------------------------------- /notebooks/experimental/test_ionq_arb_2q.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "90795bcd", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "%reload_ext autoreload\n", 11 | "%autoreload 2\n", 12 | "from simuq.ionq import IonQProvider\n", 13 | "ionq = IonQProvider(from_file=\"../../../ionq_API_key\")" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 2, 19 | "id": "c18022c4", 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "from simuq import QSystem, Qubit\n", 24 | "import numpy as np\n", 25 | "import scipy as sp\n", 26 | "X=np.array([[0,1],[1,0]])\n", 27 | "Y=np.array([[0,-1j],[1j,0]])\n", 28 | "Z=np.array([[1,0],[0,-1]])\n" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 3, 34 | "id": "30978389", 35 | "metadata": {}, 36 | "outputs": [ 37 | { 38 | "name": "stdout", 39 | "output_type": "stream", 40 | "text": [ 41 | "1.824619149114389e-08\n" 42 | ] 43 | } 44 | ], 45 | "source": [ 46 | "qs = QSystem()\n", 47 | "q0, q1 = Qubit(qs), Qubit(qs)\n", 48 | "h = q0.X*q1.Y+q0.X*q1.X*0.5\n", 49 | "qs.add_evolution(h, 1)\n", 50 | "ionq.compile(qs, aais=\"two_pauli\")\n", 51 | "solved_unitary=ionq.prog_obj.to_matrix()\n", 52 | "h1=np.kron(X,Y)+np.kron(X,X)*0.5\n", 53 | "ideal_unitary=sp.linalg.expm(-1j*h1)\n", 54 | "tmp=solved_unitary@ideal_unitary.T.conj()\n", 55 | "tmp/=tmp[0,0]\n", 56 | "print(np.linalg.norm(tmp-np.eye(4)))" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 4, 62 | "id": "17a40c29", 63 | "metadata": {}, 64 | "outputs": [ 65 | { 66 | "name": "stdout", 67 | "output_type": "stream", 68 | "text": [ 69 | "1.08761952647703e-07\n" 70 | ] 71 | } 72 | ], 73 | "source": [ 74 | "qs = QSystem()\n", 75 | "q0, q1 = Qubit(qs), Qubit(qs)\n", 76 | "h = q0.X*q1.X+q0.Y*q1.Y\n", 77 | "qs.add_evolution(h, 1)\n", 78 | "ionq.compile(qs, trotter_num=6, aais=\"two_pauli\", trotter_mode=1)\n", 79 | "solved_unitary=ionq.prog_obj.to_matrix()\n", 80 | "h1=np.kron(Y,Y)+np.kron(X,X)\n", 81 | "ideal_unitary=sp.linalg.expm(-1j*h1)\n", 82 | "tmp=solved_unitary@ideal_unitary.T.conj()\n", 83 | "tmp/=tmp[0,0]\n", 84 | "print(np.linalg.norm(tmp-np.eye(4)))" 85 | ] 86 | } 87 | ], 88 | "metadata": { 89 | "kernelspec": { 90 | "display_name": "simuq", 91 | "language": "python", 92 | "name": "python3" 93 | }, 94 | "language_info": { 95 | "codemirror_mode": { 96 | "name": "ipython", 97 | "version": 3 98 | }, 99 | "file_extension": ".py", 100 | "mimetype": "text/x-python", 101 | "name": "python", 102 | "nbconvert_exporter": "python", 103 | "pygments_lexer": "ipython3", 104 | "version": "3.12.4" 105 | } 106 | }, 107 | "nbformat": 4, 108 | "nbformat_minor": 5 109 | } 110 | -------------------------------------------------------------------------------- /src/simuq/ibm/qiskit_pulse_ibm.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | from qiskit import QuantumCircuit 3 | from qiskit.pulse import DriveChannel, GaussianSquare, ShiftPhase 4 | 5 | from simuq.ibm.qiskit_transpiler import get_pm 6 | 7 | 8 | def get_n_link(backend): 9 | configuration = backend.configuration() 10 | defaults = backend.defaults() 11 | n = configuration.num_qubits 12 | 13 | connected_pairs_cnot = configuration.coupling_map 14 | instruction_schedule_map = defaults.instruction_schedule_map 15 | 16 | def get_control_qubit(q1, q2): # Control performs Z 17 | cx_sched = instruction_schedule_map.get("ecr", qubits=(q1, q2)) 18 | supported = False 19 | for time, inst in cx_sched.instructions: 20 | if isinstance(inst.channel, DriveChannel) and not isinstance(inst, ShiftPhase): 21 | if isinstance(inst.pulse, GaussianSquare): 22 | target = inst.channel.index 23 | control = q1 if target == q2 else q2 24 | supported = True 25 | if not supported: 26 | raise ValueError("This machine is not supported!") 27 | return control 28 | 29 | link = [] 30 | for item in connected_pairs_cnot: 31 | if get_control_qubit(item[0], item[1]) == item[0]: 32 | link.append(item) 33 | 34 | return n, link 35 | 36 | 37 | def clean_as(boxes, edges, backend, with_measure=True): 38 | n, link = get_n_link(backend) 39 | circ = QuantumCircuit(n) 40 | DG = nx.DiGraph() 41 | DG.add_nodes_from([i for i in range(len(boxes))]) 42 | DG.add_edges_from(edges) 43 | topo_order = list(nx.topological_sort(DG)) 44 | for i in range(len(boxes)): 45 | idx = topo_order[i] 46 | t = boxes[idx][1] 47 | for (line, ins), params in boxes[idx][0]: 48 | if line < n: 49 | if ins == 0: 50 | q = line 51 | circ.rz(-params[1], q) 52 | circ.rx(2 * params[0] * t, q) 53 | circ.rz(params[1], q) 54 | else: 55 | q = line 56 | lamb = 2 * params[0] * t 57 | circ.rz(lamb, q) 58 | else: 59 | # print(line) 60 | (q0, q1) = link[line - n] 61 | theta = 2 * params[0] * t 62 | if ins == 0: 63 | circ.rzx(theta, q0, q1) 64 | elif ins == 1: 65 | circ.rxx(theta, q0, q1) 66 | elif ins == 2: 67 | circ.ryy(theta, q0, q1) 68 | else: 69 | circ.rzz(theta, q0, q1) 70 | if with_measure: 71 | circ.measure_active() 72 | return circ 73 | 74 | 75 | def transpile_to_circ(backend, alignment, sol_gvars, boxes, edges): 76 | circ = clean_as(boxes, edges, backend) 77 | return circ 78 | 79 | 80 | def transpile(backend, alignment, sol_gvars, boxes, edges, use_pulse=True, with_measure=True): 81 | if use_pulse: 82 | pm = get_pm(backend) 83 | # return clean_as(boxes, edges, backend) 84 | return pm.run(clean_as(boxes, edges, backend, with_measure)) 85 | else: 86 | return clean_as(boxes, edges, backend, with_measure) 87 | -------------------------------------------------------------------------------- /src/simuq/backends/bloqade_rydberg.py: -------------------------------------------------------------------------------- 1 | def gen_bloqade_code_constant(pos, clocks, pulse): 2 | n = len(pos) 3 | code = "using Bloqade\n" 4 | pos_1d = list(map(lambda x: (x,), pos)) 5 | code += f"atoms = AtomList({ pos_1d }) \n" 6 | code_delta = "delta = [" 7 | code_omega = "omega = [" 8 | code_phi = "phi = [" 9 | for i in range(n): 10 | code_delta += f"piecewise_constant(clocks = { clocks }, values = { pulse[0][i] }), " 11 | code_omega += f"piecewise_constant(clocks = { clocks }, values = { pulse[1][i] }), " 12 | code_phi += f"piecewise_constant(clocks = { clocks }, values = { pulse[2][i] }), " 13 | code_omega += "]\n" 14 | code_phi += "]\n" 15 | code_delta += "]\n" 16 | code += code_omega + code_phi + code_delta 17 | code += "h = rydberg_h(atoms; Δ = delta, Ω = omega, ϕ = phi)" 18 | return code 19 | 20 | 21 | def gen_bloqade_code_linear(pos, clocks, pulse): 22 | n = len(pos) 23 | code = "using Bloqade\n" 24 | pos_1d = list(map(lambda x: (x,), pos)) 25 | if len(pulse[0][0]) == 1: 26 | for i in range(3): 27 | for j in range(n): 28 | pulse[i][j].append(pulse[i][j][-1]) 29 | else: 30 | for i in range(3): 31 | for j in range(n): 32 | k = (pulse[i][j][-1] - pulse[i][j][-2]) / (clocks[-2] - clocks[-3]) 33 | pulse[i][j].append(pulse[i][j][-1] + k * (clocks[-1] - clocks[-2])) 34 | code += f"atoms = AtomList({ pos_1d }) \n" 35 | code_delta = "delta = [" 36 | code_omega = "omega = [" 37 | code_phi = "phi = [" 38 | for i in range(n): 39 | code_delta += f"piecewise_linear(clocks = { clocks }, values = { pulse[0][i] }), " 40 | code_omega += f"piecewise_linear(clocks = { clocks }, values = { pulse[1][i] }), " 41 | code_phi += f"piecewise_linear(clocks = { clocks }, values = { pulse[2][i] }), " 42 | code_omega += "]\n" 43 | code_phi += "]\n" 44 | code_delta += "]\n" 45 | code += code_omega + code_phi + code_delta 46 | code += "h = rydberg_h(atoms; Δ = delta, Ω = omega, ϕ = phi)" 47 | return code 48 | 49 | 50 | def gen_clocks(times): 51 | clocks = [0] 52 | for t in times: 53 | clocks.append(clocks[-1] + t) 54 | return clocks 55 | 56 | 57 | def clean_as(alignment, sol_gvars, boxes): 58 | pos = [0, *sol_gvars] 59 | n = len(pos) 60 | m = len(boxes) 61 | times = [] 62 | pulse = [[[0 for i in range(m)] for j in range(n)] for k in range(3)] 63 | for evo_idx in range(m): 64 | box = boxes[evo_idx] 65 | times.append(box[1]) 66 | for (i, j), ins, h, ins_lvars in box[0]: 67 | if i < n: 68 | pulse[0][i][evo_idx] = ins_lvars[0] 69 | else: 70 | pulse[1][i - n][evo_idx] = ins_lvars[0] 71 | pulse[2][i - n][evo_idx] = ins_lvars[1] 72 | 73 | return (pos, gen_clocks(times), pulse) 74 | 75 | 76 | def transpile(alignment, sol_gvars, boxes, edges, inter_order=0): 77 | if inter_order == 0: 78 | code = gen_bloqade_code_constant(*clean_as(alignment, sol_gvars, boxes)) 79 | elif inter_order == 1: 80 | code = gen_bloqade_code_linear(*clean_as(alignment, sol_gvars, boxes)) 81 | # with open('transpiled.jl', 'w') as f : 82 | # print(code, file = f) 83 | return code 84 | -------------------------------------------------------------------------------- /notebooks/experimental/ising_braket_ionq.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "42b87d5f", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "%load_ext autoreload\n", 11 | "%autoreload 2\n", 12 | "import sys\n", 13 | "sys.path.append('..')" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": null, 19 | "id": "0a2fc1e3", 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "from src.simuq.systems.ising import GenQS\n", 24 | "from src.simuq.braket import BraketProvider" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": null, 30 | "outputs": [], 31 | "source": [ 32 | "awsp = BraketProvider()" 33 | ], 34 | "metadata": { 35 | "collapsed": false 36 | }, 37 | "id": "3810a2de52e2562f" 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "id": "8574407e", 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "N = 6\n", 47 | "T = 1.0\n", 48 | "qs = GenQS(N, T, 1, 1, is_chain=True)" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": null, 54 | "id": "cfe90e6e", 55 | "metadata": {}, 56 | "outputs": [], 57 | "source": [ 58 | "awsp.compile(qs, 'ionq', 'Harmony', 'heisenberg')" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": null, 64 | "id": "82fd8a6d", 65 | "metadata": {}, 66 | "outputs": [], 67 | "source": [ 68 | "awsp.run(shots=4096, on_simulator=True)" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": null, 74 | "id": "2ffb0bad", 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "awsp.results()" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "id": "f4091b82", 85 | "metadata": {}, 86 | "outputs": [], 87 | "source": [ 88 | "type(awsp.results())" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": null, 94 | "outputs": [], 95 | "source": [ 96 | "awsp.run(shots=10, on_simulator=False)" 97 | ], 98 | "metadata": { 99 | "collapsed": false 100 | }, 101 | "id": "7e49ebd4055533e0" 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": null, 106 | "outputs": [], 107 | "source": [ 108 | "awsp.results()" 109 | ], 110 | "metadata": { 111 | "collapsed": false 112 | }, 113 | "id": "ea448af0a545beaf" 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": null, 118 | "outputs": [], 119 | "source": [ 120 | "print(awsp.prog)" 121 | ], 122 | "metadata": { 123 | "collapsed": false 124 | }, 125 | "id": "d23b978ccde80234" 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": null, 130 | "outputs": [], 131 | "source": [], 132 | "metadata": { 133 | "collapsed": false 134 | }, 135 | "id": "4f7eedcba2f98d2d" 136 | } 137 | ], 138 | "metadata": { 139 | "kernelspec": { 140 | "display_name": "Python 3 (ipykernel)", 141 | "language": "python", 142 | "name": "python3" 143 | }, 144 | "language_info": { 145 | "codemirror_mode": { 146 | "name": "ipython", 147 | "version": 3 148 | }, 149 | "file_extension": ".py", 150 | "mimetype": "text/x-python", 151 | "name": "python", 152 | "nbconvert_exporter": "python", 153 | "pygments_lexer": "ipython3", 154 | "version": "3.9.6" 155 | } 156 | }, 157 | "nbformat": 4, 158 | "nbformat_minor": 5 159 | } 160 | -------------------------------------------------------------------------------- /notebooks/experimental/ising_ibm.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "42b87d5f", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "%load_ext autoreload\n", 11 | "%autoreload 2\n", 12 | "import sys\n", 13 | "sys.path.append('../../src/')" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 2, 19 | "id": "0a2fc1e3", 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "from simuq.systems.ising import GenQS\n", 24 | "from simuq.ibm import IBMProvider" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": 14, 30 | "id": "36b90c06", 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "with open(\"../../../qiskit_APIKEY\", \"r\") as f:\n", 35 | " APIKEY = f.read().strip()\n", 36 | "ibm = IBMProvider(APIKEY)" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 15, 42 | "id": "2fe81768", 43 | "metadata": {}, 44 | "outputs": [ 45 | { 46 | "name": "stdout", 47 | "output_type": "stream", 48 | "text": [ 49 | "[, , , , , , , , , , , , , , ]\n" 50 | ] 51 | } 52 | ], 53 | "source": [ 54 | "ibm.supported_backends()" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 16, 60 | "id": "8574407e", 61 | "metadata": {}, 62 | "outputs": [], 63 | "source": [ 64 | "N = 2\n", 65 | "T = 1.0\n", 66 | "qs = GenQS(N, T, 1, 1, is_chain=True)" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": 17, 72 | "id": "cfe90e6e", 73 | "metadata": {}, 74 | "outputs": [], 75 | "source": [ 76 | "ibm.compile(qs,\"ibm_sherbrooke\",use_pulse=False)" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": 18, 82 | "id": "20ec2130", 83 | "metadata": {}, 84 | "outputs": [ 85 | { 86 | "name": "stdout", 87 | "output_type": "stream", 88 | "text": [ 89 | "\n" 90 | ] 91 | } 92 | ], 93 | "source": [ 94 | "ibm.run(shots=1024,on_simulator=True)" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": 19, 100 | "id": "2ffb0bad", 101 | "metadata": {}, 102 | "outputs": [ 103 | { 104 | "data": { 105 | "text/plain": [ 106 | "{'10': 0.1220703125, '01': 0.1240234375, '00': 0.388671875, '11': 0.365234375}" 107 | ] 108 | }, 109 | "execution_count": 19, 110 | "metadata": {}, 111 | "output_type": "execute_result" 112 | } 113 | ], 114 | "source": [ 115 | "ibm.results(on_simulator=True)" 116 | ] 117 | } 118 | ], 119 | "metadata": { 120 | "kernelspec": { 121 | "display_name": "Python 3 (ipykernel)", 122 | "language": "python", 123 | "name": "python3" 124 | }, 125 | "language_info": { 126 | "codemirror_mode": { 127 | "name": "ipython", 128 | "version": 3 129 | }, 130 | "file_extension": ".py", 131 | "mimetype": "text/x-python", 132 | "name": "python", 133 | "nbconvert_exporter": "python", 134 | "pygments_lexer": "ipython3", 135 | "version": "3.12.2" 136 | } 137 | }, 138 | "nbformat": 4, 139 | "nbformat_minor": 5 140 | } 141 | -------------------------------------------------------------------------------- /src/simuq/environment.py: -------------------------------------------------------------------------------- 1 | """ 2 | The classes describing quantum environments. 3 | 4 | A quantum environment is a container of sites. Either target quantum 5 | systems or quantum machines are described by its children classes. 6 | 7 | A site is the basic unit describing a quantized physics entity. It 8 | includes qubit / bosonic / fermionic states, or other customized 9 | types of sites. Each site contains a set of site operators. These 10 | operators will be used in the construction of Hamiltonians. 11 | 12 | Currently operators are stored as strings. In future these may be 13 | substituted by operator classes. 14 | """ 15 | 16 | from simuq.hamiltonian import TIHamiltonian 17 | 18 | 19 | class BaseQuantumEnvironment: 20 | """The basic quantum environment. 21 | 22 | It models a system to which quantum sites belong to. 23 | 24 | The sites are stored in a sequence, where their types are stored in 25 | list sites_type. 26 | """ 27 | 28 | def __init__(self): 29 | self.sites = [] 30 | self.sites_type = [] 31 | self.sites_name = [] 32 | self.num_sites = 0 33 | 34 | def identity(self): 35 | return TIHamiltonian.identity(self.sites_type, self.sites_name) 36 | 37 | def singletonOp(self, index, op): 38 | return TIHamiltonian.op(self.sites_type, self.sites_name, index, op) 39 | 40 | 41 | class BaseSite: 42 | """The basic quantum site.""" 43 | 44 | def __init__(self, qs, name=None): 45 | self.index = qs.num_sites 46 | self.qs = qs 47 | if name is None: 48 | name = f"Site{qs.num_sites}" 49 | self.name = name 50 | qs.num_sites += 1 51 | qs.sites.append(self) 52 | qs.sites_name.append(name) 53 | 54 | def createOp(self, op): 55 | h = self.qs.singletonOp(self.index, op) 56 | return h 57 | 58 | 59 | class Qubit(BaseSite): 60 | """The qubit site. 61 | 62 | By default, there are X, Y, Z, I defined as site operators 63 | of a qubit site. 64 | """ 65 | 66 | def __init__(self, qs, name=None): 67 | if name is None: 68 | name = f"Qubit{qs.num_sites}" 69 | super().__init__(qs, name) 70 | qs.sites_type.append("qubit") 71 | self.X = self.gen_X() 72 | self.Y = self.gen_Y() 73 | self.Z = self.gen_Z() 74 | self.I = self.gen_I() 75 | 76 | def gen_X(self): 77 | return self.createOp("X") 78 | 79 | def gen_Y(self): 80 | return self.createOp("Y") 81 | 82 | def gen_Z(self): 83 | return self.createOp("Z") 84 | 85 | def gen_I(self): 86 | return self.createOp("") 87 | 88 | 89 | class BaseParticle(BaseSite): 90 | """The basic particle site. 91 | 92 | By default, there are annihilation and creation operators. 93 | Additionally, to be consistent with qubit, I represents the identity. 94 | """ 95 | 96 | def __init__(self, qs, name=None): 97 | if name is None: 98 | name = f"Site{qs.num_sites}" 99 | super().__init__(qs, name) 100 | qs.sites_type.append("particle") 101 | self.a = self.gen_a() 102 | self.c = self.gen_c() 103 | self.I = self.gen_I() 104 | 105 | def gen_a(self): 106 | return self.createOp("a") 107 | 108 | def gen_c(self): 109 | return self.createOp("c") 110 | 111 | def gen_I(self): 112 | return self.createOp("") 113 | 114 | 115 | class Fermion(BaseParticle): 116 | """The fermionic site""" 117 | 118 | def __init__(self, qs, name=None): 119 | if name is None: 120 | name = f"Fermion{qs.num_sites}" 121 | super().__init__(qs, name) 122 | qs.sites_type[-1] = "fermion" 123 | 124 | 125 | class Boson(BaseParticle): 126 | """The bosonic site""" 127 | 128 | def __init__(self, qs, name=None): 129 | if name is None: 130 | name = f"Boson{qs.num_sites}" 131 | super().__init__(qs, name) 132 | qs.sites_type[-1] = "boson" 133 | -------------------------------------------------------------------------------- /src/simuq/ibm/ibm_provider.py: -------------------------------------------------------------------------------- 1 | import qiskit 2 | from qiskit import transpile 3 | from qiskit_aer import AerSimulator 4 | from qiskit_ibm_runtime import QiskitRuntimeService 5 | 6 | from simuq.provider import BaseProvider 7 | from simuq.solver import generate_as 8 | 9 | 10 | class IBMProvider(BaseProvider): 11 | def __init__(self, api_key=None, from_file=None): 12 | if from_file is not None: 13 | with open(from_file, "r") as f: 14 | api_key = f.readline().strip() 15 | self.api_key = api_key 16 | QiskitRuntimeService.save_account(channel="ibm_quantum", token=api_key, overwrite=True) 17 | self.provider = QiskitRuntimeService() 18 | super().__init__() 19 | 20 | def supported_backends(self): 21 | 22 | print(self.provider.backends()) 23 | 24 | def compile( 25 | self, 26 | qs, 27 | backend="ibmq_jakarta", 28 | aais="heisenberg", 29 | tol=0.01, 30 | trotter_num=6, 31 | verbose=0, 32 | use_pulse=True, 33 | state_prep=None, 34 | with_measure=True, 35 | ): 36 | 37 | self.backend = self.provider.backend(backend) 38 | nsite = self.backend.num_qubits 39 | 40 | if qs.num_sites > nsite: 41 | raise Exception("Device has less sites than the target quantum system.") 42 | 43 | if aais == "heisenberg": 44 | from simuq.aais import ibm 45 | from simuq.ibm.qiskit_pulse_ibm import transpile 46 | 47 | mach = ibm.generate_qmachine(self.backend) 48 | comp = transpile 49 | 50 | layout, sol_gvars, boxes, edges = generate_as( 51 | qs, 52 | mach, 53 | trotter_num, 54 | solver="least_squares", 55 | solver_args={"tol": tol}, 56 | override_layout=None, 57 | verbose=verbose, 58 | ) 59 | self.prog = comp( 60 | self.backend, 61 | layout, 62 | sol_gvars, 63 | boxes, 64 | edges, 65 | use_pulse=use_pulse, 66 | with_measure=with_measure, 67 | ) 68 | from qiskit import transpile as transpile_qiskit 69 | 70 | self.prog = transpile_qiskit(self.prog, backend=self.backend) 71 | self.layout = layout 72 | self.qs_names = qs.print_sites() 73 | if state_prep is not None: 74 | self.prog = self.prog.compose(state_prep, qubits=layout, front=True) 75 | 76 | def run(self, shots=4096, on_simulator=False, verbose=0): 77 | if on_simulator: 78 | self.backend_sim = AerSimulator(method="statevector") 79 | job = self.backend_sim.run(self.prog, shots=shots) 80 | else: 81 | job = self.backend.run(self.prog, shots=shots) 82 | self.task = job 83 | if verbose >= 0: 84 | print(self.task) 85 | 86 | def results(self, job_id=None, on_simulator=False): 87 | if job_id is None: 88 | if self.task is not None: 89 | job_id = self.task.job_id() 90 | else: 91 | raise Exception("No submitted job in record.") 92 | if on_simulator: 93 | job = self.task 94 | else: 95 | job = self.provider.job(job_id) 96 | status = job.status() 97 | if status.name == "QUEUED": 98 | print("Job is not completed") 99 | return 100 | 101 | def layout_rev(res): 102 | n = len(self.layout) 103 | # print(self.layout) 104 | b = res 105 | ret = "" 106 | for i in range(n): 107 | ret += b[-1 - self.layout[i]] 108 | return ret 109 | 110 | def results_from_data(data): 111 | ret = dict() 112 | for key in data.keys(): 113 | new_key = layout_rev(key) 114 | if new_key in ret: 115 | ret[new_key] += data[key] / n_shots 116 | else: 117 | ret[new_key] = data[key] / n_shots 118 | return ret 119 | 120 | count = job.result().get_counts() 121 | n_shots = sum(count.values()) 122 | return results_from_data(count) 123 | -------------------------------------------------------------------------------- /src/simuq/braket/braket_ionq_circuit.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from braket.circuits import Circuit, gates 3 | 4 | from simuq.backends.ionq_circuit import IonQCircuit, to_turns 5 | 6 | 7 | class BraketIonQCircuit(IonQCircuit): 8 | def __init__(self, num_qubits): 9 | super().__init__(num_qubits) 10 | self._circuit = Circuit() 11 | 12 | def gpi(self, q, phi): 13 | self._circuit.gpi(q, phi + self._accum_phases[q]) 14 | return self 15 | 16 | def gpi2(self, q, phi): 17 | self._circuit.gpi2(q, phi + self._accum_phases[q]) 18 | return self 19 | 20 | def ms_quarter(self, q0, q1, phi0, phi1, theta): 21 | self._circuit.ms( 22 | q0, 23 | q1, 24 | phi0 + self._accum_phases[q0], 25 | phi1 + self._accum_phases[q1], 26 | theta, 27 | ) 28 | return self 29 | 30 | def optimize(self): 31 | """ 32 | Optimize 1q gate sequences 33 | """ 34 | # Not self._circuit.qubit_count because sometimes that's smaller 35 | new_circ = BraketIonQCircuit(len(self._accum_phases)) 36 | 37 | n = len(self._accum_phases) 38 | 39 | unitaries = [np.eye(2)] * n 40 | 41 | for instruction in self._circuit.instructions: 42 | gate = instruction.operator 43 | target = instruction.target 44 | if isinstance(gate, gates.GPi): 45 | qubit = target[0] 46 | unitaries[qubit] = np.matmul( 47 | self._gpi_mat(to_turns(gate.angle)), 48 | unitaries[qubit], 49 | ) 50 | elif isinstance(gate, gates.GPi2): 51 | qubit = target[0] 52 | unitaries[qubit] = np.matmul( 53 | self._gpi2_mat(to_turns(gate.angle)), 54 | unitaries[qubit], 55 | ) 56 | elif isinstance(gate, gates.MS): 57 | for q in target: 58 | new_circ._add_unitary(q, unitaries[q]) 59 | # Reset accumulated matrix 60 | unitaries[q] = np.eye(2) 61 | q0, q1 = target 62 | phi0 = gate.angle_1 63 | phi1 = gate.angle_2 64 | angle = gate.angle_3 65 | new_circ.ms(q0, q1, phi0, phi1, angle) 66 | else: 67 | raise Exception("Unknown gate:", gate["gate"]) 68 | 69 | for q in range(n): 70 | new_circ._add_unitary(q, unitaries[q]) 71 | new_circ.rz(q, -self._accum_phases[q]) 72 | 73 | return new_circ 74 | 75 | def add(self, circ): 76 | """ 77 | Append a circuit behind self 78 | """ 79 | 80 | if len(circ._accum_phases) != len(self._accum_phases): 81 | raise Exception("Circuit sizes are different.") 82 | 83 | for instruction in circ._circuit.instructions: 84 | gate = instruction.operator 85 | target = instruction.target 86 | if isinstance(gate, gates.GPi): 87 | qubit = target[0] 88 | angle = self._accum_phases[qubit] + gate.angle 89 | self.gpi(qubit, angle) 90 | elif isinstance(gate, gates.GPi2): 91 | qubit = target[0] 92 | angle = self._accum_phases[qubit] + gate.angle 93 | self.gpi2(qubit, angle) 94 | elif isinstance(gate, gates.MS): 95 | q0, q1 = target 96 | phi0 = self._accum_phases[q0] + gate.angle_1 97 | phi1 = self._accum_phases[q1] + gate.angle_2 98 | angle = gate.angle_3 99 | self.ms(q0, q1, phi0, phi1, angle) 100 | else: 101 | raise Exception("Unknown gate:", gate["gate"]) 102 | 103 | for q, phi in enumerate(circ._accum_phases): 104 | self._accum_phases[q] += phi 105 | 106 | return self 107 | 108 | def copy(self): 109 | """ 110 | Generate a copy of self 111 | """ 112 | circ = BraketIonQCircuit(len(self._accum_phases)) 113 | circ.add(self) 114 | return circ 115 | 116 | @property 117 | def braket_circuit(self): 118 | return self._circuit 119 | -------------------------------------------------------------------------------- /src/simuq/backends/ionq_transpiler.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | import networkx as nx 4 | import numpy as np 5 | import scipy as sp 6 | 7 | from simuq.backends.ionq_circuit import IonQCircuit 8 | from simuq.transpiler import Transpiler 9 | 10 | 11 | def randomized_topo_sort(G): 12 | n = len(G) 13 | ret = [] 14 | cand = [] 15 | for i in range(n): 16 | if G.in_degree(i) == 0: 17 | cand.append(i) 18 | for _ in range(n): 19 | j = cand[np.random.randint(len(cand))] 20 | cand.remove(j) 21 | ret.append(j) 22 | nxts = list(G.neighbors(j)) 23 | for k in nxts: 24 | if G.in_degree(k) == 0: 25 | continue 26 | G.remove_edge(j, k) 27 | if G.in_degree(k) == 0: 28 | cand.append(k) 29 | return ret 30 | 31 | 32 | class IonQTranspiler(Transpiler, ABC): 33 | @abstractmethod 34 | def generate_circuit(self, n: int, *args, **kwargs) -> IonQCircuit: 35 | pass 36 | 37 | @staticmethod 38 | def clean_as(n, boxes, edges, circ: IonQCircuit, trotter_args) -> IonQCircuit: 39 | link = [(i, j) for i in range(n) for j in range(i + 1, n)] 40 | dg = nx.DiGraph() 41 | dg.add_nodes_from([i for i in range(len(boxes))]) 42 | dg.add_edges_from(edges) 43 | if trotter_args["randomized"]: 44 | topo_order = randomized_topo_sort(dg) 45 | else: 46 | topo_order = list(nx.topological_sort(dg)) 47 | 48 | for i in range(len(boxes)): 49 | idx = topo_order[i] 50 | t = boxes[idx][1] 51 | for (line, ins), params in boxes[idx][0]: 52 | if line < n: 53 | if ins == 0: 54 | q = line 55 | # expm(-i*(rot/2)*(cos(phi)X+sin(phi)Y)) 56 | rot = 2 * params[0] * t 57 | phi = params[1] 58 | if abs(rot) > 1e-5: 59 | cosphi = np.cos(phi) 60 | sinphi = np.sin(phi) 61 | U = sp.linalg.expm( 62 | -1j 63 | * (rot / 2) 64 | * np.array([[0, cosphi - 1j * sinphi], [cosphi + 1j * sinphi, 0]]) 65 | ) 66 | circ._add_unitary(q, U) 67 | else: 68 | q = line 69 | # expm(-i*(rot/2)*Z) 70 | rot = 2 * params[0] * t 71 | circ.rz(q, rot) 72 | else: 73 | (q0, q1) = link[line - n] 74 | theta = 2 * params[0] * t 75 | if ins == 0: 76 | # R_XX(theta) 77 | if abs(theta) > 1e-5: 78 | circ.ms(q0, q1, 0, 0, theta) 79 | elif ins == 1: 80 | # R_YY(theta) 81 | if abs(theta) > 1e-5: 82 | circ.ms(q0, q1, np.pi / 2, np.pi / 2, theta) 83 | else: 84 | # R_ZZ(theta) 85 | if abs(theta) > 1e-5: 86 | # R_X(-pi/2) 87 | circ.gpi2(q0, np.pi) 88 | circ.gpi2(q1, np.pi) 89 | # R_YY(theta) 90 | circ.ms(q0, q1, np.pi / 2, np.pi / 2, theta) 91 | # R_X(pi/2) 92 | circ.gpi2(q0, 0) 93 | circ.gpi2(q1, 0) 94 | return circ.optimize() 95 | 96 | def transpile( 97 | self, n, sol_gvars, boxes, edges, *generate_circuit_args, **generate_circuit_kwargs 98 | ): 99 | n = len(n) if isinstance(n, list) else n 100 | if "trotter_args" in generate_circuit_kwargs: 101 | trotter_args = generate_circuit_kwargs["trotter_args"] 102 | del generate_circuit_kwargs["trotter_args"] 103 | else: 104 | trotter_args = {"randomized":False} 105 | circ = self.generate_circuit(n, *generate_circuit_args, **generate_circuit_kwargs) 106 | return self.clean_as(n, boxes, edges, circ, trotter_args) 107 | -------------------------------------------------------------------------------- /src/simuq/dwave/dwave_provider.py: -------------------------------------------------------------------------------- 1 | from simuq.provider import BaseProvider 2 | from simuq.solver import generate_as 3 | import numpy as np 4 | import json 5 | import re 6 | from dwave.system import DWaveSampler, EmbeddingComposite, FixedEmbeddingComposite 7 | from dimod import ising_to_qubo 8 | 9 | from simuq.aais import ising 10 | from simuq.dwave.dwave_transpiler import DwaveTranspiler 11 | 12 | 13 | class DWaveProvider(BaseProvider): 14 | def __init__(self, api_key=None, from_file = None): 15 | # insert all log in details 16 | super().__init__() 17 | self._samples = None 18 | self.api_key = api_key 19 | if from_file is not None: 20 | with open(from_file, "r") as f: 21 | self.api_key = f.readline().strip() 22 | if self.api_key is None: 23 | raise Exception("No API key provided.") 24 | 25 | def compile(self, 26 | qs, 27 | anneal_schedule=[[0, 0], [20, 1]], 28 | chain_strength=0.5, 29 | verbose=0): 30 | h = [0 for _ in range(qs.num_sites)] 31 | J = {} 32 | for ham in qs.evos[0][0].ham: 33 | keys = list(ham[0].d.keys()) 34 | vals = list(ham[0].d.values()) 35 | if 'Z' in vals: 36 | if len(vals) == 1: 37 | h[keys[0]] = ham[1] 38 | elif len(vals) == 2 and ham[1] != 0: 39 | J[(keys[0], keys[1])] = ham[1] 40 | self.prog = h, J, anneal_schedule 41 | self.chain_strength = chain_strength 42 | return h, J 43 | 44 | def compare_qubo(self, q1, q2): 45 | numDifferent = 0 46 | if len(q1.keys()) != len(q2.keys()): return False 47 | for (d1, d2) in q1.keys(): 48 | if abs(q1[(d1, d2)] - q2[(int(d1), int(d2))]) > 10**-3: 49 | numDifferent += 1 50 | return numDifferent 51 | 52 | 53 | def run(self, shots = 100, solver="Advantage_system6.4"): 54 | self.shots = shots 55 | if self.prog is None: 56 | raise Exception("No compiled job in record.") 57 | qpu = DWaveSampler(token=self.api_key, solver=solver) 58 | sampler = EmbeddingComposite(qpu) 59 | h, J, anneal_schedule = self.prog 60 | response = sampler.sample_ising(h, J, 61 | chain_strength=self.chain_strength, 62 | num_reads=self.shots, 63 | anneal_schedule=anneal_schedule, 64 | answer_mode="raw" 65 | ) 66 | self.samples = list(response.samples()) 67 | self.time_on_machine = response.info['timing']['qpu_access_time'] * 1e-6 68 | self.avg_qpu_time = response.info['timing']['qpu_access_time'] * 10e-6 / self.shots 69 | self.num_occurrences = list(response.data_vectors['num_occurrences']) 70 | return response 71 | 72 | def isingToqubo(self, h, J): 73 | n = len(h) 74 | QUBO = {} 75 | 76 | for i in range(n): 77 | s = 0 78 | for ii in range(n): 79 | if (i,ii) in J.keys(): 80 | s += J[(i,ii)] 81 | if (ii,i) in J.keys(): 82 | s += J[(ii,i)] 83 | 84 | QUBO[(i,i)] = -2 * (h[i] + s) 85 | 86 | for j in range(i+1, n): 87 | if (i,j) in J.keys() and J[i, j] != 0: 88 | QUBO[(i,j)] = 4 * J[(i,j)] 89 | 90 | return QUBO 91 | 92 | def run_qubo(self): 93 | if self.prog is None: 94 | raise Exception("No compiled job in record.") 95 | qpu = DWaveSampler(token=self.api_key) 96 | sampler = EmbeddingComposite(qpu) 97 | h, J, anneal_schedule = self.prog 98 | h = {i: h[i] for i in range(len(h))} 99 | qubo = self.isingToqubo(h, J) 100 | max_interaction_qhd = np.max(np.abs(np.array(list(qubo.values())))) 101 | response = sampler.sample_qubo(qubo, 102 | num_reads=self.shots, 103 | anneal_schedule=anneal_schedule, 104 | chain_strength=1.1*max_interaction_qhd) 105 | self.samples = list(response.samples()) 106 | 107 | def results(self): 108 | if self.samples == None: 109 | raise Exception("Job has not been run yet.") 110 | return self.samples 111 | -------------------------------------------------------------------------------- /src/simuq/braket/braket_rydberg_transpiler.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from braket.ahs import AnalogHamiltonianSimulation, AtomArrangement, DrivingField 3 | from braket.timings.time_series import TimeSeries 4 | 5 | from simuq.transpiler import Transpiler 6 | 7 | 8 | def gen_braket_code(pos, clocks, pulse): 9 | # print(pos) 10 | # print(clocks) 11 | # # pulse = np.array(pulse) 12 | # print(pulse) 13 | # print(pulse.shape) 14 | 15 | register = AtomArrangement() 16 | for posi in pos: 17 | register.add(np.array([posi[0] * 1e-6, posi[1] * 1e-6])) 18 | 19 | delta = TimeSeries() 20 | omega = TimeSeries() 21 | phi = TimeSeries() 22 | 23 | delta.put(0, pulse[0][0]) 24 | for i in range(len(clocks) - 2): 25 | delta.put(clocks[i + 1], pulse[0][i]) 26 | delta.put(clocks[-1], pulse[0][-1]) 27 | omega.put(0, 0) 28 | for i in range(len(clocks) - 2): 29 | omega.put(clocks[i + 1], pulse[1][i]) 30 | omega.put(clocks[-1], 0) 31 | phi.put(0, 0) 32 | for i in range(len(clocks) - 2): 33 | phi.put(clocks[i + 1], pulse[2][i]) 34 | phi.put(clocks[-1], 0) 35 | 36 | drive = DrivingField(amplitude=omega, phase=phi, detuning=delta) 37 | 38 | ahs_program = AnalogHamiltonianSimulation(register=register, hamiltonian=drive) 39 | return ahs_program 40 | 41 | 42 | def gen_clocks(times, ramp_time, state_prep_time): 43 | clocks = [0, ramp_time] 44 | for t in state_prep_time: 45 | clocks.append(clocks[-1] + t) 46 | clocks.append(clocks[-1] + ramp_time) 47 | for t in times: 48 | clocks.append(clocks[-1] + t) 49 | clocks.append(clocks[-1] + ramp_time) 50 | clocks = [t * 1e-6 for t in clocks] 51 | return clocks 52 | 53 | 54 | def _initialize_positions_1d(sol_gvars): 55 | pos = [(0.0, 0.0)] 56 | for i in range(len(sol_gvars)): 57 | pos.append((sol_gvars[i], 0.0)) 58 | return pos 59 | 60 | 61 | def _initialize_positions_2d(sol_gvars, verbose): 62 | pos = [(0.0, 0.0)] 63 | for i in range(int(len(sol_gvars) / 2)): 64 | pos.append((sol_gvars[2 * i], sol_gvars[2 * i + 1])) 65 | # Fix rotation angle 66 | theta = -np.arctan2(pos[1][1], pos[1][0]) 67 | if verbose > 0: 68 | print("Fixing rotation by ", theta) 69 | for i in range(len(pos)): 70 | new_x = np.cos(theta) * pos[i][0] - np.sin(theta) * pos[i][1] 71 | new_y = np.sin(theta) * pos[i][0] + np.cos(theta) * pos[i][1] 72 | pos[i] = (new_x, new_y) 73 | return pos 74 | 75 | 76 | def clean_as(sol_gvars, boxes, ramp_time, state_prep, dimension, verbose=0): 77 | if dimension == 1: 78 | pos = _initialize_positions_1d(sol_gvars) 79 | elif dimension == 2: 80 | pos = _initialize_positions_2d(sol_gvars, verbose) 81 | else: 82 | raise NotImplementedError 83 | 84 | m = len(boxes) 85 | times = [] 86 | pulse = [[0 for i in range(m)] for k in range(3)] 87 | for evo_idx in range(m): 88 | box = boxes[evo_idx] 89 | times.append(box[1]) 90 | for (i, j), ins, h, ins_lvars in box[0]: 91 | if verbose > 0: 92 | print(f"i={i}, ins_lvars={ins_lvars}") 93 | if i == 0: 94 | pulse[0][evo_idx] = ins_lvars[0] * 1e6 95 | else: 96 | pulse[1][evo_idx] = ins_lvars[0] * 1e6 97 | pulse[2][evo_idx] = ins_lvars[1] * 1e6 98 | if len(state_prep["times"]) > 0: 99 | pulse[0] = [v * 1e6 for v in state_prep["delta"]] + [pulse[0][0]] + pulse[0] 100 | pulse[1] = [v * 1e6 for v in state_prep["omega"]] + [pulse[1][0]] + pulse[1] 101 | pulse[2] = [v * 1e6 for v in state_prep["phi"]] + [pulse[2][0]] + pulse[2] 102 | else: 103 | for i in range(3): 104 | pulse[i] = [0, pulse[i][0]] + pulse[i] 105 | for i in range(len(pulse[0])): 106 | if pulse[1][i] < 0: 107 | pulse[1][i] = -pulse[1][i] 108 | pulse[2][i] += np.pi 109 | return pos, gen_clocks(times, ramp_time, state_prep["times"]), pulse 110 | 111 | 112 | class BraketRydbergTranspiler(Transpiler): 113 | def __init__(self, dimension): 114 | self._dimension = dimension 115 | 116 | def transpile(self, sol_gvars, boxes, edges, ramp_time=0.05, state_prep=None, verbose=0): 117 | # print(sol_gvars) 118 | # print(boxes) 119 | code = gen_braket_code( 120 | *clean_as(sol_gvars, boxes, ramp_time, state_prep or {}, self._dimension, verbose) 121 | ) 122 | return code 123 | -------------------------------------------------------------------------------- /notebooks/experimental/qwalk_bloqade.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "42e26405", 7 | "metadata": { 8 | "ExecuteTime": { 9 | "end_time": "2023-08-11T23:48:10.262360Z", 10 | "start_time": "2023-08-11T23:48:10.242363Z" 11 | } 12 | }, 13 | "outputs": [], 14 | "source": [ 15 | "%load_ext autoreload\n", 16 | "%autoreload 2\n", 17 | "import sys\n", 18 | "sys.path.append('..')" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 2, 24 | "id": "a7feecc8", 25 | "metadata": { 26 | "ExecuteTime": { 27 | "end_time": "2023-08-11T23:48:12.103427Z", 28 | "start_time": "2023-08-11T23:48:10.895911Z" 29 | } 30 | }, 31 | "outputs": [], 32 | "source": [ 33 | "from simuq.solver import generate_as\n", 34 | "from simuq.systems.qwalk_chain import GenQS\n", 35 | "#from simuq.aais import rydberg1d\n", 36 | "#from simuq.backends.bloqade_rydberg import transpile\n", 37 | "from simuq.aais import heisenberg\n", 38 | "from simuq.backends.qiskit_iontrap import transpile" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": 3, 44 | "id": "5cd17f14", 45 | "metadata": { 46 | "ExecuteTime": { 47 | "end_time": "2023-08-11T23:48:12.139596Z", 48 | "start_time": "2023-08-11T23:48:12.108436Z" 49 | } 50 | }, 51 | "outputs": [], 52 | "source": [ 53 | "N = 5\n", 54 | "n = N - 1\n", 55 | "qs = GenQS(N, lamb = 5, T=1)\n", 56 | "mach = heisenberg.generate_qmachine(n)" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 4, 62 | "id": "ceeeebcc", 63 | "metadata": { 64 | "ExecuteTime": { 65 | "end_time": "2023-08-11T23:48:12.653484Z", 66 | "start_time": "2023-08-11T23:48:12.140319Z" 67 | } 68 | }, 69 | "outputs": [ 70 | { 71 | "name": "stdout", 72 | "output_type": "stream", 73 | "text": [ 74 | "0.4711589813232422\n", 75 | "(, 'from braket.circuits import Circuit\\ncirc = Circuit().zz(0, 1, 9.999999998367267).zz(2, 3, 9.999999998367276).rx(0, 2.0000000014534844).rx(3, 1.9999999965651118).zz(1, 2, 9.999999998367269).rz(0, 9.99999999836727).rx(1, 2.0000000014534813).rx(2, 2.0000000014534804).rz(3, 9.99999999836727)')\n" 76 | ] 77 | } 78 | ], 79 | "source": [ 80 | "Trot = 1\n", 81 | "tol = 0.5\n", 82 | "\n", 83 | "import time\n", 84 | "start_time = time.time()\n", 85 | "code = transpile(*generate_as(qs, mach, Trot, solver = 'least_squares', solver_tol = tol))\n", 86 | "end_time = time.time()\n", 87 | "\n", 88 | "print(end_time - start_time)\n", 89 | "print(code)" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": 5, 95 | "id": "6ff71ee8", 96 | "metadata": { 97 | "ExecuteTime": { 98 | "end_time": "2023-08-11T23:48:12.808484Z", 99 | "start_time": "2023-08-11T23:48:12.651436Z" 100 | } 101 | }, 102 | "outputs": [ 103 | { 104 | "name": "stdout", 105 | "output_type": "stream", 106 | "text": [ 107 | "First round time: 0.0947120189666748\n", 108 | "Second round time: 0.015208005905151367\n", 109 | "0.11734890937805176\n", 110 | "(, 'from braket.circuits import Circuit\\ncirc = Circuit().zz(0, 1, 9.999999999999998).zz(2, 3, 9.999999999999998).rx(0, 2.0).rx(3, 1.9999999999999993).zz(1, 2, 10.000000000000002).rz(0, 10.0).rx(1, 1.9999999999999993).rx(2, 2.000000000000001).rz(3, 10.000000000000004)')\n" 111 | ] 112 | } 113 | ], 114 | "source": [ 115 | "Trot = 1\n", 116 | "tol = 0.5\n", 117 | "\n", 118 | "import time\n", 119 | "start_time = time.time()\n", 120 | "code = transpile(*generate_as(qs, mach, Trot, solver = 'old_least_squares', solver_tol = tol))\n", 121 | "end_time = time.time()\n", 122 | "\n", 123 | "print(end_time - start_time)\n", 124 | "print(code)" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": null, 130 | "outputs": [], 131 | "source": [], 132 | "metadata": { 133 | "collapsed": false 134 | }, 135 | "id": "edd16e7b9b2bbf02" 136 | } 137 | ], 138 | "metadata": { 139 | "kernelspec": { 140 | "display_name": "Python 3 (ipykernel)", 141 | "language": "python", 142 | "name": "python3" 143 | }, 144 | "language_info": { 145 | "codemirror_mode": { 146 | "name": "ipython", 147 | "version": 3 148 | }, 149 | "file_extension": ".py", 150 | "mimetype": "text/x-python", 151 | "name": "python", 152 | "nbconvert_exporter": "python", 153 | "pygments_lexer": "ipython3", 154 | "version": "3.9.6" 155 | } 156 | }, 157 | "nbformat": 4, 158 | "nbformat_minor": 5 159 | } 160 | -------------------------------------------------------------------------------- /src/simuq/qmachine.py: -------------------------------------------------------------------------------- 1 | """ 2 | The class file for quantum machines. 3 | 4 | One can implement an AAIS in a QMachine. Signal lines 5 | and instructions are supported inside it. 6 | 7 | A quantum machine is essentially a container of signal 8 | lines, instructions and (local or global) variables. 9 | 10 | A signal line contains multiple instructions. Instructions 11 | sharing a signal line cannot be called simultaneously. 12 | 13 | Global variables are defined for the QMachine as a 14 | globally tunable parameter. They will be fixed for the 15 | whole experiment. 16 | 17 | An instruction may contain several local variables. Local 18 | variables can be tuned for each call of the instruction. 19 | Both variables can be used in the description of its 20 | Hamiltonian. 21 | TODO: Add API for variables' bounds. 22 | """ 23 | 24 | import itertools 25 | from copy import copy 26 | 27 | import numpy as np 28 | 29 | from simuq.environment import BaseQuantumEnvironment 30 | from simuq.expression import BaseVar, Expression 31 | 32 | 33 | class QMachine(BaseQuantumEnvironment): 34 | """A quantum device described by its AAIS. 35 | 36 | It stores the signal lines, instructions, local 37 | variables and global variables. 38 | """ 39 | 40 | def __init__(self): 41 | super().__init__() 42 | self.gvars = [] 43 | self.lvars = [] 44 | self.lines = [] 45 | self.num_inss = 0 46 | self.sys_ham = 0 47 | self.with_sys_ham = False 48 | self.instantiated = False 49 | 50 | def set_sys_ham(self, h): 51 | self.sys_ham = h 52 | self.with_sys_ham = True 53 | 54 | def instantiate(self): 55 | if not self.instantiated: 56 | if self.with_sys_ham: 57 | sys_line = self.add_signal_line() 58 | sys_ins = sys_line.add_instruction("native", "System Hamiltonian") 59 | sys_ins.set_ham(self.sys_ham) 60 | sys_ins.is_sys_ham = True 61 | 62 | # Add indices to global variables 63 | for index, gvar in enumerate(self.gvars): 64 | gvar.set_index(index) 65 | # Populate self.lvars and set their indices 66 | self.lvars = [lvar for line in self.lines for ins in line.inss for lvar in ins.lvars] 67 | for index, lvar in enumerate(self.lvars): 68 | lvar.set_index(len(self.gvars) + index) 69 | for line in self.lines: 70 | for ins in line.inss: 71 | ins.set_vars_index([lvar.index - len(self.gvars) for lvar in ins.lvars]) 72 | 73 | # Set indices for instructions 74 | for index, ins in enumerate(itertools.chain(*[line.inss for line in self.lines])): 75 | ins.set_index(index) 76 | self.num_inss = sum(len(line.inss) for line in self.lines) 77 | 78 | self.instantiated = True 79 | 80 | def extend_instruction_sites(self): 81 | for line in self.lines: 82 | for ins in line.inss: 83 | ins.h.extend_sites(self.sites_type, self.sites_name) 84 | 85 | def add_signal_line(self): 86 | line = SignalLine() 87 | self.lines.append(line) 88 | return line 89 | 90 | def add_global_variable(self, init_value=0, lower_bound=-np.inf, upper_bound=np.inf): 91 | var = BaseVar(init_value, lower_bound, upper_bound) 92 | self.gvars.append(var) 93 | return var 94 | 95 | 96 | class SignalLine: 97 | """A signal line. 98 | 99 | It contains all instructions belonging to it. 100 | """ 101 | 102 | def __init__(self): 103 | self.inss = [] 104 | 105 | def add_instruction(self, nativeness="native", name="ins"): 106 | ins = Instruction(nativeness, name) 107 | self.inss.append(ins) 108 | return ins 109 | 110 | 111 | class Instruction: 112 | """An instruction. 113 | 114 | It contains the local variables belonging to it, its 115 | property, and its instruction Hamiltonian. 116 | """ 117 | 118 | def __init__(self, nativeness="native", name="ins"): 119 | self.index = None 120 | self.lvars = [] 121 | # Stores the index of the local variables in the QMachine (not lvar.index itself) 122 | self.vars_index = None 123 | self.nativeness = nativeness 124 | self.name = name 125 | self.is_sys_ham = False 126 | self.h = None 127 | 128 | def set_index(self, index): 129 | self.index = index 130 | 131 | def set_vars_index(self, vars_index): 132 | self.vars_index = vars_index 133 | 134 | def set_ham(self, h): 135 | newh = copy(h) 136 | for i in range(len(h.ham)): 137 | if not isinstance(h.ham[i][1], Expression): 138 | newh.ham[i] = (h.ham[i][0], h.ham[i][1] * Expression.unit()) 139 | newh.cleanHam() 140 | self.h = newh 141 | 142 | def exp_eval(self, gvars, lvars): 143 | return self.h.exp_eval(gvars, lvars) 144 | 145 | def add_local_variable(self, init_value=0, lower_bound=-np.inf, upper_bound=np.inf): 146 | var = BaseVar(init_value, lower_bound, upper_bound) 147 | self.lvars.append(var) 148 | return var 149 | -------------------------------------------------------------------------------- /notebooks/experimental/qwalk_julia.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "f0b7a93f", 7 | "metadata": {}, 8 | "outputs": [ 9 | { 10 | "data": { 11 | "text/latex": [ 12 | "$\\sum \\frac{2π \\cdot 0.863\\times 10^{6.0}}{|r_i-r_j|^6} n_i n_j+1\\cdot\\sum Ω(t)_i ⋅ (e^{ϕ(t)_i ⋅ im} |0⟩⟨1| + e^{-ϕ(t)_i ⋅ im} |1⟩⟨0|)-\\sum Δ_i ⋅ n_i$" 13 | ], 14 | "text/plain": [ 15 | "\u001b[36mnqubits: 4\u001b[39m\n", 16 | "\u001b[31m\u001b[1m+\u001b[22m\u001b[39m\n", 17 | "├─ \u001b[33m\u001b[1m[+] \u001b[22m\u001b[39m∑ 2π ⋅ 8.627e5.0/|r_i-r_j|^6 n_i n_j\n", 18 | "├─ \u001b[33m\u001b[1m[+] \u001b[22m\u001b[39m∑ Ω(t)_i ⋅ (e^{ϕ(t)_i ⋅ im} |0⟩⟨1| + e^{-ϕ(t)_i ⋅ im} |1⟩⟨0|)\n", 19 | "└─ \u001b[33m\u001b[1m[-] \u001b[22m\u001b[39m∑ Δ_i ⋅ n_i\n" 20 | ] 21 | }, 22 | "execution_count": 1, 23 | "metadata": {}, 24 | "output_type": "execute_result" 25 | } 26 | ], 27 | "source": [ 28 | "using Bloqade\n", 29 | "atoms = AtomList([(0,), (8.04469627686766,), (16.089556260235042,), (24.134252537038854,)]) \n", 30 | "omega = [piecewise_constant(clocks = [0, 1], values = [2.0]), piecewise_constant(clocks = [0, 1], values = [2.0]), piecewise_constant(clocks = [0, 1], values = [2.0]), piecewise_constant(clocks = [0, 1], values = [2.0]), ]\n", 31 | "phi = [piecewise_constant(clocks = [0, 1], values = [4.484834868738814e-27]), piecewise_constant(clocks = [0, 1], values = [1.3127985376038034e-18]), piecewise_constant(clocks = [0, 1], values = [3.486852882750537e-18]), piecewise_constant(clocks = [0, 1], values = [-6.807026282331149e-18]), ]\n", 32 | "delta = [piecewise_constant(clocks = [0, 1], values = [20.16870981279034]), piecewise_constant(clocks = [0, 1], values = [20.15254722868919]), piecewise_constant(clocks = [0, 1], values = [20.152547229176925]), piecewise_constant(clocks = [0, 1], values = [20.168709813285663]), ]\n", 33 | "h = rydberg_h(atoms; Δ = delta, Ω = omega, ϕ = phi)" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 2, 39 | "id": "391717ce", 40 | "metadata": {}, 41 | "outputs": [ 42 | { 43 | "data": { 44 | "text/plain": [ 45 | "16×16 SparseMatrixCSC{ComplexF64, Int64} with 79 stored entries:\n", 46 | "⢞⣵⠑⢄⠑⢄⠀⠀\n", 47 | "⠑⢄⢟⣵⠀⠀⠑⢄\n", 48 | "⠑⢄⠀⠀⢟⣵⠑⢄\n", 49 | "⠀⠀⠑⢄⠑⢄⢟⣵" 50 | ] 51 | }, 52 | "execution_count": 2, 53 | "metadata": {}, 54 | "output_type": "execute_result" 55 | } 56 | ], 57 | "source": [ 58 | "M = mat(h |> attime(0.5))" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 7, 64 | "id": "bb75276e", 65 | "metadata": {}, 66 | "outputs": [], 67 | "source": [ 68 | "using Printf\n", 69 | "Base.show(io::IO, f::Float64) = @printf(io, \"%.2f\", f)" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 8, 75 | "id": "4beb992f", 76 | "metadata": { 77 | "scrolled": true 78 | }, 79 | "outputs": [ 80 | { 81 | "name": "stdout", 82 | "output_type": "stream", 83 | "text": [ 84 | "[5, 13, 9, 11, 10]" 85 | ] 86 | } 87 | ], 88 | "source": [ 89 | "N = length(atoms)+1\n", 90 | "n = N - 1\n", 91 | "s0 = 0\n", 92 | "for i in 0:n-1\n", 93 | " if i % 2 == 0\n", 94 | " s0 += 1 << i\n", 95 | " end\n", 96 | "end\n", 97 | "s = [s0]\n", 98 | "for i in 1:n\n", 99 | " si = s[i] ⊻ 1<<(n-i)\n", 100 | " push!(s, si)\n", 101 | "end\n", 102 | "print(s)" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": 9, 108 | "id": "9874b5ad", 109 | "metadata": {}, 110 | "outputs": [ 111 | { 112 | "name": "stdout", 113 | "output_type": "stream", 114 | "text": [ 115 | "-40.01 + 0.00im 1.00 - 0.00im 0.00 + 0.00im 0.00 + 0.00im 0.00 + 0.00im \n", 116 | "1.00 + 0.00im -40.15 + 0.00im 1.00 - 0.00im 0.00 + 0.00im 0.00 + 0.00im \n", 117 | "0.00 + 0.00im 1.00 + 0.00im -40.31 + 0.00im 1.00 + 0.00im 0.00 + 0.00im \n", 118 | "0.00 + 0.00im 0.00 + 0.00im 1.00 - 0.00im -40.15 + 0.00im 1.00 - 0.00im \n", 119 | "0.00 + 0.00im 0.00 + 0.00im 0.00 + 0.00im 1.00 + 0.00im -40.01 + 0.00im \n" 120 | ] 121 | } 122 | ], 123 | "source": [ 124 | "M = mat(h |> attime(0.5))\n", 125 | "for i in 1:N\n", 126 | " for j in 1:N\n", 127 | " print(M[s[i]+1, s[j]+1], \" \")\n", 128 | " end\n", 129 | " println()\n", 130 | "end" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": null, 136 | "id": "9d5a871c", 137 | "metadata": {}, 138 | "outputs": [], 139 | "source": [] 140 | } 141 | ], 142 | "metadata": { 143 | "kernelspec": { 144 | "display_name": "Julia 1.7.2", 145 | "language": "julia", 146 | "name": "julia-1.7" 147 | }, 148 | "language_info": { 149 | "file_extension": ".jl", 150 | "mimetype": "application/julia", 151 | "name": "julia", 152 | "version": "1.7.2" 153 | } 154 | }, 155 | "nbformat": 4, 156 | "nbformat_minor": 5 157 | } 158 | -------------------------------------------------------------------------------- /src/simuq/backends/bloqade_rydberg_aws.py: -------------------------------------------------------------------------------- 1 | # def gen_bloqade_code(pos, clocks, pulse) : 2 | # n = len(pos) 3 | # code = "using Bloqade\n" 4 | # pos_1d = list(map(lambda x : (x,), pos)) 5 | # code += f'atoms = AtomList({ pos_1d }) \n' 6 | # code_delta = "delta = [" 7 | # code_omega = "omega = [" 8 | # code_phi = "phi = [" 9 | # for i in range(n) : 10 | # code_delta += f'piecewise_constant(clocks = { clocks }, values = { pulse[0][i] }), ' 11 | # code_omega += f'piecewise_constant(clocks = { clocks }, values = { pulse[1][i] }), ' 12 | # code_phi += f'piecewise_constant(clocks = { clocks }, values = { pulse[2][i] }), ' 13 | # code_omega += "]\n" 14 | # code_phi += "]\n" 15 | # code_delta += "]\n" 16 | # code += code_omega + code_phi + code_delta 17 | # code += "h = rydberg_h(atoms; Δ = delta, Ω = omega, ϕ = phi)" 18 | # return code 19 | 20 | import numpy as np 21 | 22 | 23 | def gen_braket_code(pos, clocks, pulse): 24 | # print(pos) 25 | # print(clocks) 26 | # # pulse = np.array(pulse) 27 | # print(pulse) 28 | # print(pulse.shape) 29 | from braket.ahs.atom_arrangement import AtomArrangement 30 | 31 | register = AtomArrangement() 32 | for posi in pos: 33 | register.add(np.array([0, posi])) 34 | from braket.ahs.driving_field import DrivingField 35 | from braket.ahs.time_series import TimeSeries 36 | 37 | delta = TimeSeries() 38 | omega = TimeSeries() 39 | phi = TimeSeries() 40 | # TODO piecewise constant or linear? 41 | 42 | delta.put(0, pulse[0][0][0]) 43 | for i in range(len(clocks) - 1): 44 | delta.put((clocks[i] + clocks[i + 1]) / 2, pulse[0][0][i]) 45 | delta.put(clocks[-1] / 2, pulse[0][0][-1]) 46 | omega.put(0, pulse[0][1][0]) 47 | for i in range(len(clocks) - 1): 48 | omega.put((clocks[i] + clocks[i + 1]) / 2, pulse[1][0][i]) 49 | omega.put(clocks[-1] / 2, pulse[0][1][-1]) 50 | phi.put(0, pulse[0][2][0]) 51 | for i in range(len(clocks) - 1): 52 | phi.put((clocks[i] + clocks[i + 1]) / 2, pulse[2][0][i]) 53 | phi.put(clocks[-1] / 2, pulse[0][2][-1]) 54 | 55 | drive = DrivingField(amplitude=omega, phase=phi, detuning=delta) 56 | from braket.ahs.analog_hamiltonian_simulation import AnalogHamiltonianSimulation 57 | 58 | ahs_program = AnalogHamiltonianSimulation(register=register, hamiltonian=drive) 59 | from braket.aws import AwsDevice 60 | 61 | aquila_qpu = AwsDevice("arn:aws:braket:us-east-1::device/qpu/quera/Aquila") 62 | discretized_ahs_program = ahs_program.discretize(aquila_qpu) 63 | from braket.devices import LocalSimulator 64 | 65 | device = LocalSimulator("braket_ahs") 66 | 67 | result = device.run(discretized_ahs_program, shots=1_000_000).result() 68 | 69 | 70 | def gen_clocks(times): 71 | clocks = [0] 72 | for t in times: 73 | clocks.append(clocks[-1] + t) 74 | return clocks 75 | 76 | 77 | def clean_as(alignment, sol_gvars, boxes): 78 | pos = [0, *sol_gvars] 79 | n = len(pos) 80 | m = len(boxes) 81 | times = [] 82 | pulse = [[0 for i in range(m)] for k in range(3)] 83 | for evo_idx in range(m): 84 | box = boxes[evo_idx] 85 | times.append(box[1]) 86 | for (i, j), ins, h, ins_lvars in box[0]: 87 | # print(f"i={i}") 88 | if i == 0: 89 | pulse[0][evo_idx] = ins_lvars[0] 90 | else: 91 | pulse[1][evo_idx] = ins_lvars[0] 92 | pulse[2][evo_idx] = ins_lvars[1] 93 | 94 | return (pos, gen_clocks(times), pulse) 95 | 96 | 97 | def transpile(alignment, sol_gvars, boxes, edges): 98 | code = gen_braket_code(*clean_as(alignment, sol_gvars, boxes)) 99 | with open("transpiled.jl", "w") as f: 100 | print(code, file=f) 101 | return code 102 | 103 | 104 | pulse = [ 105 | [ 106 | [ 107 | -2.968997921691882, 108 | -2.302331255350557, 109 | -1.6356645886093095, 110 | -0.968997921317156, 111 | -0.30233125458228, 112 | 0.36433541203769715, 113 | 1.0310020784655602, 114 | 1.6976687450141825, 115 | 2.3643354106343843, 116 | ], 117 | [ 118 | -3.000488222942375, 119 | -2.3338215563147067, 120 | -1.66715488935505, 121 | -1.0004882231196242, 122 | -0.33382155630970944, 123 | 0.33284511042370446, 124 | 0.9995117774412948, 125 | 1.6661784439446585, 126 | 2.3328451106648456, 127 | ], 128 | [ 129 | -2.9689979249397465, 130 | -2.3023312582224844, 131 | -1.635664591334513, 132 | -0.9689979256061668, 133 | -0.30233125917951675, 134 | 0.3643354077036973, 135 | 1.0310020757343243, 136 | 1.6976687415234875, 137 | 2.3643354085635644, 138 | ], 139 | [ 140 | -1.4708215063341783e-09, 141 | -1.428046216520107e-09, 142 | -1.4831853377199497e-09, 143 | -1.570594085422478e-09, 144 | -1.6746726602059686e-09, 145 | -2.5665215817997365e-09, 146 | -1.9309168757674367e-09, 147 | -1.8174553832496394e-09, 148 | -4.579114160373652e-09, 149 | ], 150 | [ 151 | -1.8602264388175136e-09, 152 | -4.326363158835265e-10, 153 | 0, 154 | -4.667673470364462e-10, 155 | -4.113543169861989e-10, 156 | -2.8613355627097614e-09, 157 | -4.031977288615272e-09, 158 | -1.1240022769670263e-09, 159 | -4.996166859307392e-10, 160 | ], 161 | ] 162 | ] 163 | -------------------------------------------------------------------------------- /src/simuq/backends/ionq_braiding.py: -------------------------------------------------------------------------------- 1 | # Currently this file is not usable: 2 | # AWS Braket does not support arbitrary-angle MS gate for now 3 | # Will be tested later 4 | # If you want to use IonQ's device through braket, 5 | # try qiskit_iontrap.py which generates a circuit in bk_c. 6 | 7 | from math import atan2, cos, floor, pi, sin, sqrt 8 | 9 | import networkx as nx 10 | import numpy as np 11 | from qiskit import QuantumCircuit 12 | 13 | # import gates 14 | # import utils 15 | from qiskit_ionq import GPI2Gate, GPIGate, IonQProvider, MSGate 16 | 17 | 18 | def clean_as(n, boxes, edges): 19 | link = [(i, j) for i in range(n) for j in range(i + 1, n)] 20 | DG = nx.DiGraph() 21 | DG.add_nodes_from([i for i in range(len(boxes))]) 22 | DG.add_edges_from(edges) 23 | topo_order = list(nx.topological_sort(DG)) 24 | circ = QuantumCircuit(n, n) 25 | accum_phase = [0 for i in range(n)] 26 | 27 | def add_gpi2gpigpi2(q, a, b, c): 28 | circ.append(GPI2Gate((c + accum_phase[q]) / (2 * np.pi)), [q]) 29 | circ.append(GPIGate((b + accum_phase[q]) / (2 * np.pi)), [q]) 30 | circ.append(GPI2Gate((a + accum_phase[q]) / (2 * np.pi)), [q]) 31 | 32 | def add_hadamard(q): 33 | add_gpi2gpigpi2(q, np.pi, np.pi / 4, 0) 34 | 35 | # Generate unitary GPI2(a)*GPI(b)*GPI2(c) 36 | 37 | for i in range(len(boxes)): 38 | idx = topo_order[i] 39 | t = boxes[idx][1] 40 | for (line, ins), params in boxes[idx][0]: 41 | if line < n: 42 | if ins == 0: 43 | q = line 44 | rot = params[0] * t 45 | phi = params[1] 46 | # expm(-i*rot*(cos(phi)X+sin(phi)Y))=I-i*rot*(cos(phi)X+sin(phi)Y) 47 | # circ.rz(-phi, q) 48 | # circ.rx(2 * rot, q) 49 | # circ.rz(phi, q) 50 | if abs(rot) > 1e-5: 51 | add_gpi2gpigpi2(q, np.pi / 2 - phi, rot - np.pi / 2 - phi, np.pi / 2 - phi) 52 | else: 53 | q = line 54 | # Rz(q, 2 * params[0] * t) 55 | accum_phase[q] += 2 * params[0] * t 56 | elif line < n + len(link): 57 | (q0, q1) = link[line - n] 58 | theta = 2 * params[0] * t 59 | if ins == 0: 60 | # R_YX(theta) 61 | if abs(theta) > 1e-5: 62 | # Hadamard on q0 63 | add_hadamard(q0) 64 | # S on q0 65 | accum_phase[q0] += np.pi / 2 66 | 67 | circ.append( 68 | MSGate( 69 | accum_phase[q0] / (2 * np.pi), 70 | accum_phase[q1] / (2 * np.pi), 71 | theta / (2 * np.pi), 72 | ), 73 | [q0, q1], 74 | ) 75 | 76 | # Sdg on q0 77 | accum_phase[q0] -= np.pi / 2 78 | # Hadamard on q0 79 | add_hadamard(q0) 80 | else: 81 | t = params[0] * t 82 | if t < 0 or t >= 2 * pi: 83 | t -= floor(t / (2 * pi)) * 2 * pi 84 | repeat = floor(t / (pi / 2)) + 1 85 | t = t / repeat 86 | t1 = atan2(-sqrt(sin(2 * t)), 1) / 2 87 | t2 = atan2(+sqrt(sin(2 * t)), cos(t) - sin(t)) / 2 88 | 89 | def eYY(T, q0, q1): 90 | # Hadamard on q0 91 | add_hadamard(q0) 92 | # S on q0 93 | accum_phase[q0] += np.pi / 2 94 | # Hadamard on q1 95 | add_hadamard(q1) 96 | # S on q1 97 | accum_phase[q1] += np.pi / 2 98 | 99 | circ.append( 100 | MSGate( 101 | accum_phase[q0] / (2 * np.pi), 102 | accum_phase[q1] / (2 * np.pi), 103 | T / np.pi, 104 | ), 105 | [q0, q1], 106 | ) 107 | 108 | # Sdg on q0 109 | accum_phase[q0] -= np.pi / 2 110 | # Hadamard on q0 111 | add_hadamard(q0) 112 | # Sdg on q1 113 | accum_phase[q1] -= np.pi / 2 114 | # Hadamard on q1 115 | add_hadamard(q1) 116 | 117 | def eXX(T, q0, q1): 118 | circ.append( 119 | MSGate( 120 | accum_phase[q0] / (2 * np.pi), 121 | accum_phase[q1] / (2 * np.pi), 122 | T / np.pi, 123 | ), 124 | [q0, q1], 125 | ) 126 | 127 | eYY(t1, 0, 1) 128 | eXX(t2, 1, 2) 129 | eYY(t2, 0, 1) 130 | eXX(t1, 1, 2) 131 | 132 | circ.measure(np.arange(n), np.arange(n)) 133 | return circ, accum_phase 134 | 135 | 136 | def transpile(alignment, sol_gvars, boxes, edges): 137 | n = len(alignment) 138 | circ, accum_phase = clean_as(n, boxes, edges) 139 | return circ 140 | 141 | 142 | def debug_circ(): 143 | n = 2 144 | circ = QuantumCircuit(n, n) 145 | accum_phase = [0 for i in range(n)] 146 | 147 | def add_gpi2gpigpi2(q, a, b, c): 148 | circ.append(GPI2Gate((c + accum_phase[q]) / (2 * np.pi)), [q]) 149 | circ.append(GPIGate((b + accum_phase[q]) / (2 * np.pi)), [q]) 150 | circ.append(GPI2Gate((a + accum_phase[q]) / (2 * np.pi)), [q]) 151 | 152 | def add_hadamard(q): 153 | add_gpi2gpigpi2(q, np.pi, -np.pi / 4, 0) 154 | 155 | # Generate unitary GPI2(a)*GPI(b)*GPI2(c) 156 | 157 | add_hadamard(0) 158 | circ.measure(np.arange(n), np.arange(n)) 159 | return circ 160 | -------------------------------------------------------------------------------- /notebooks/artifact_evaluation/cs4-benchmark-IonQ.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "31e7177e", 6 | "metadata": {}, 7 | "source": [ 8 | "This notebook reproduces the compilation results for employing SimuQ compilation on the quantum systems in the small benchmark on IonQ devices. We employ IonQ Quantum Cloud here, so if you want to deploy the compilation results, you need an IonQ Quantum Cloud account. However, if you only test the compilation process of SimuQ, you don't need to have an account. For this case, you may put an arbitrary string as the API key since it will not be actually used." 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": 2, 14 | "id": "4c79715c", 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "import time\n", 19 | "from simuq.ionq import IonQProvider\n", 20 | "Trot = 4\n", 21 | "Repitition = 1\n", 22 | "tol = 0.5\n", 23 | "T = 1\n", 24 | "ionq=IonQProvider(from_file=\"../../../ionq_API_key\")" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": 4, 30 | "id": "898d1252", 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "def count_gates(ionq_prog) :\n", 35 | " gc1, gc2 = 0, 0\n", 36 | " circ = ionq_prog['body']['circuit']\n", 37 | " for d in circ :\n", 38 | " if d['gate'] == 'ms' :\n", 39 | " gc2 += 1\n", 40 | " else :\n", 41 | " gc1 += 1\n", 42 | " return gc1, gc2\n", 43 | "\n", 44 | "def run_benchmark(qs):\n", 45 | " n_qubits = len(qs.sites)\n", 46 | " sum_time = 0\n", 47 | " try:\n", 48 | " for _ in range(Repitition) :\n", 49 | " start_time = time.time()\n", 50 | " ionq.compile(qs, backend=\"forte\",trotter_num=Trot, tol=tol,verbose=0)\n", 51 | " end_time = time.time()\n", 52 | " this_time = end_time - start_time\n", 53 | " sum_time += this_time\n", 54 | " print(f\"Avgerage compilation time for {n_qubits} qubit using SimuQ\", sum_time / Repitition)\n", 55 | " print(\"Number of gates:\", count_gates(ionq.prog))\n", 56 | " except:\n", 57 | " print(\"Fail!\")\n" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "id": "5c7d0622", 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [ 67 | "from simuq.systems.benchmark.ising_chain import GenQS\n", 68 | "for N in [6,32,64,96]:\n", 69 | " qs = GenQS(N, T, 1, 1)\n", 70 | " run_benchmark(qs)" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "id": "d602115e", 77 | "metadata": {}, 78 | "outputs": [], 79 | "source": [ 80 | "from simuq.systems.benchmark.ising_cycle import GenQS\n", 81 | "for N in [6,12,32,64]:\n", 82 | " qs = GenQS(N, T, 1, 1)\n", 83 | " run_benchmark(qs)" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "id": "c81f280c", 90 | "metadata": {}, 91 | "outputs": [], 92 | "source": [ 93 | "from simuq.systems.benchmark.heis_chain import GenQS\n", 94 | "N=32\n", 95 | "qs = GenQS(N, T, 1, 1)\n", 96 | "run_benchmark(qs)" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "id": "f14d59b1", 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "from simuq.systems.benchmark.qaoa_cycle import GenQS\n", 107 | "N=12\n", 108 | "qs = GenQS(N, 3,[1,1,1,1,1,1])\n", 109 | "run_benchmark(qs)" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": null, 115 | "id": "14e7f5ba", 116 | "metadata": {}, 117 | "outputs": [], 118 | "source": [ 119 | "from simuq.systems.benchmark.qhd import GenQS\n", 120 | "N=16\n", 121 | "qs = GenQS(N, T, 1, 1)\n", 122 | "run_benchmark(qs)" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": null, 128 | "id": "e47c2857", 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [ 132 | "from simuq.systems.benchmark.mis_chain import GenQS\n", 133 | "for N in [12,24]:\n", 134 | " qs = GenQS(N)\n", 135 | " run_benchmark(qs)" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": null, 141 | "id": "f9c45265", 142 | "metadata": {}, 143 | "outputs": [], 144 | "source": [ 145 | "from simuq.systems.benchmark.mis_grid import GenQS\n", 146 | "for k in [4,5]:\n", 147 | " qs = GenQS(k)\n", 148 | " run_benchmark(qs)" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": null, 154 | "id": "ff6633a5", 155 | "metadata": {}, 156 | "outputs": [], 157 | "source": [ 158 | "from simuq.systems.benchmark.kitaev import GenQS\n", 159 | "N=18\n", 160 | "qs = GenQS(N)\n", 161 | "run_benchmark(qs)" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": null, 167 | "id": "6cf9f9cb", 168 | "metadata": {}, 169 | "outputs": [], 170 | "source": [ 171 | "from simuq.systems.benchmark.schwinger import GenQS\n", 172 | "N=10\n", 173 | "qs = GenQS(N)\n", 174 | "run_benchmark(qs)" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": null, 180 | "id": "8a3b142a", 181 | "metadata": {}, 182 | "outputs": [], 183 | "source": [ 184 | "from simuq.systems.benchmark.o3nlσm import GenQS\n", 185 | "N=5\n", 186 | "M=6\n", 187 | "qs = GenQS(N,M)\n", 188 | "run_benchmark(qs)" 189 | ] 190 | } 191 | ], 192 | "metadata": { 193 | "kernelspec": { 194 | "display_name": "Python 3 (ipykernel)", 195 | "language": "python", 196 | "name": "python3" 197 | }, 198 | "language_info": { 199 | "codemirror_mode": { 200 | "name": "ipython", 201 | "version": 3 202 | }, 203 | "file_extension": ".py", 204 | "mimetype": "text/x-python", 205 | "name": "python", 206 | "nbconvert_exporter": "python", 207 | "pygments_lexer": "ipython3", 208 | "version": "3.9.15" 209 | } 210 | }, 211 | "nbformat": 4, 212 | "nbformat_minor": 5 213 | } 214 | -------------------------------------------------------------------------------- /src/simuq/ionq/ionq_api_circuit.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from simuq.backends.ionq_circuit import IonQCircuit, to_turns 4 | 5 | 6 | class IonQAPICircuit(IonQCircuit): 7 | def __init__(self, n_qubits, name="circuit", backend="simulator", noise_model=None): 8 | super().__init__(n_qubits) 9 | # Circuit are stored in turns, accum_phases are stored in rads. 10 | self.job = { 11 | "lang": "json", 12 | "name": name, 13 | "target": backend, 14 | "input": { 15 | "gateset": "native", 16 | "qubits": n_qubits, 17 | "circuit": [], 18 | }, 19 | } 20 | if noise_model: 21 | self.job["noise"] = {"model": noise_model} 22 | 23 | def gpi(self, q, phi): 24 | self.job["input"]["circuit"].append( 25 | {"gate": "gpi", "target": q, "phase": to_turns(phi + self._accum_phases[q])} 26 | ) 27 | return self 28 | 29 | def gpi2(self, q, phi): 30 | self.job["input"]["circuit"].append( 31 | {"gate": "gpi2", "target": q, "phase": to_turns(phi + self._accum_phases[q])} 32 | ) 33 | return self 34 | 35 | def ms_quarter(self, q0, q1, phi0, phi1, theta): 36 | self.job["input"]["circuit"].append( 37 | { 38 | "gate": "ms", 39 | "targets": [q0, q1], 40 | "phases": [ 41 | to_turns(phi0 + self._accum_phases[q0]), 42 | to_turns(phi1 + self._accum_phases[q1]), 43 | ], 44 | "angle": to_turns(theta), 45 | } 46 | ) 47 | return self 48 | 49 | def optimize(self): 50 | """ 51 | Optimize 1q gate sequences 52 | """ 53 | if "noise" in self.job: 54 | new_circ = IonQAPICircuit( 55 | self.job["input"]["qubits"], 56 | self.job["name"], 57 | self.job["target"], 58 | self.job["noise"]["model"], 59 | ) 60 | else: 61 | new_circ = IonQAPICircuit( 62 | self.job["input"]["qubits"], 63 | self.job["name"], 64 | self.job["target"], 65 | ) 66 | 67 | n = len(self._accum_phases) 68 | 69 | unitaries = [np.eye(2)] * n 70 | 71 | for gate in self.job["input"]["circuit"]: 72 | if gate["gate"] == "gpi": 73 | unitaries[gate["target"]] = np.matmul( 74 | self._gpi_mat(gate["phase"]), 75 | unitaries[gate["target"]], 76 | ) 77 | elif gate["gate"] == "gpi2": 78 | unitaries[gate["target"]] = np.matmul( 79 | self._gpi2_mat(gate["phase"]), 80 | unitaries[gate["target"]], 81 | ) 82 | elif gate["gate"] == "ms": 83 | for q in gate["targets"]: 84 | new_circ._add_unitary(q, unitaries[q]) 85 | # Reset accumulated matrix 86 | unitaries[q] = np.eye(2) 87 | q0, q1 = gate["targets"] 88 | phi0, phi1 = np.array(gate["phases"]) * 2 * np.pi 89 | angle = gate["angle"] * 2 * np.pi 90 | new_circ.ms(q0, q1, phi0, phi1, angle) 91 | else: 92 | raise Exception("Unknown gate:", gate["gate"]) 93 | 94 | for q in range(n): 95 | new_circ._add_unitary(q, unitaries[q]) 96 | new_circ.rz(q, -self._accum_phases[q]) 97 | 98 | return new_circ 99 | 100 | def add(self, circ, inherit_from_back=False): 101 | """ 102 | Append a circuit behind self 103 | """ 104 | if len(circ._accum_phases) != len(self._accum_phases): 105 | raise Exception("Circuit sizes are different.") 106 | 107 | for gate in circ.job["input"]["circuit"]: 108 | if gate["gate"] == "gpi": 109 | qubit = gate["target"] 110 | angle = gate["phase"] * 2 * np.pi 111 | self.gpi(qubit, angle) 112 | elif gate["gate"] == "gpi2": 113 | qubit = gate["target"] 114 | angle = gate["phase"] * 2 * np.pi 115 | self.gpi2(qubit, angle) 116 | elif gate["gate"] == "ms": 117 | q0, q1 = gate["targets"] 118 | phi0 = gate["phases"][0] * 2 * np.pi 119 | phi1 = gate["phases"][1] * 2 * np.pi 120 | angle = gate["angle"] * 2 * np.pi 121 | self.ms(q0, q1, phi0, phi1, angle) 122 | else: 123 | raise Exception("Unknown gate:", gate["gate"]) 124 | 125 | for q, phi in enumerate(circ._accum_phases): 126 | self._accum_phases[q] += phi 127 | if inherit_from_back: 128 | self.job["target"] = circ.job["target"] 129 | if "noise" in circ.job: 130 | self.job["noise"] = circ.job["noise"] 131 | return self 132 | 133 | def copy(self): 134 | """ 135 | Generate a copy of self 136 | """ 137 | circ = IonQAPICircuit(len(self._accum_phases)) 138 | circ.add(self) 139 | return circ 140 | 141 | def to_matrix(self): 142 | from qiskit import QuantumCircuit 143 | from qiskit.quantum_info import Operator 144 | 145 | n = self.job["input"]["qubits"] 146 | circ = QuantumCircuit(n) 147 | 148 | from qiskit.circuit.library import RZGate 149 | from qiskit_ionq import GPI2Gate, GPIGate, IonQProvider, MSGate 150 | 151 | for ind, gate in enumerate(self.job["input"]["circuit"]): 152 | if gate["gate"] == "gpi": 153 | circ.append(GPIGate(gate["phase"]), [n - 1 - gate["target"]]) 154 | elif gate["gate"] == "gpi2": 155 | circ.append(GPI2Gate(gate["phase"]), [n - 1 - gate["target"]]) 156 | elif gate["gate"] == "ms": 157 | circ.append(MSGate(gate["phases"][1], gate["phases"][0], gate["angle"]), list(n - 1 - np.array(gate["targets"]))) 158 | else: 159 | raise Exception("Unknown gate:", gate["gate"]) 160 | for q in range(self.job["input"]["qubits"]): 161 | circ.append(RZGate(-self._accum_phases[q]), [n - 1 - q]) 162 | 163 | return Operator(circ).to_matrix() 164 | -------------------------------------------------------------------------------- /notebooks/experimental/ising_ionq.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "42b87d5f", 7 | "metadata": { 8 | "ExecuteTime": { 9 | "end_time": "2023-09-11T07:25:17.075912Z", 10 | "start_time": "2023-09-11T07:25:17.058318Z" 11 | } 12 | }, 13 | "outputs": [], 14 | "source": [ 15 | "%load_ext autoreload\n", 16 | "%autoreload 2\n", 17 | "import sys\n", 18 | "sys.path.append('..')" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 2, 24 | "id": "0a2fc1e3", 25 | "metadata": { 26 | "ExecuteTime": { 27 | "end_time": "2023-09-11T07:25:17.997994Z", 28 | "start_time": "2023-09-11T07:25:17.618969Z" 29 | } 30 | }, 31 | "outputs": [], 32 | "source": [ 33 | "from simuq.systems.ising import GenQS\n", 34 | "from simuq.ionq import IonQProvider" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 3, 40 | "outputs": [], 41 | "source": [ 42 | "ionq = IonQProvider(from_file=\"../ionq_API_key\")" 43 | ], 44 | "metadata": { 45 | "collapsed": false, 46 | "ExecuteTime": { 47 | "end_time": "2023-09-11T07:25:18.857068Z", 48 | "start_time": "2023-09-11T07:25:18.824298Z" 49 | } 50 | }, 51 | "id": "3810a2de52e2562f" 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 4, 56 | "id": "8574407e", 57 | "metadata": { 58 | "ExecuteTime": { 59 | "end_time": "2023-09-11T07:25:19.606438Z", 60 | "start_time": "2023-09-11T07:25:19.583677Z" 61 | } 62 | }, 63 | "outputs": [], 64 | "source": [ 65 | "N = 6\n", 66 | "T = 1.0\n", 67 | "qs = GenQS(N, T, 1, 1, is_chain=True)" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": 5, 73 | "id": "cfe90e6e", 74 | "metadata": { 75 | "ExecuteTime": { 76 | "end_time": "2023-09-11T07:25:21.151231Z", 77 | "start_time": "2023-09-11T07:25:20.934183Z" 78 | } 79 | }, 80 | "outputs": [], 81 | "source": [ 82 | "ionq.compile(qs)" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": 6, 88 | "id": "82fd8a6d", 89 | "metadata": {}, 90 | "outputs": [ 91 | { 92 | "name": "stdout", 93 | "output_type": "stream", 94 | "text": [ 95 | "{'id': '716d66e4-a6a6-49b8-b2df-d0acdfa5ed83', 'status': 'ready', 'request': 1689891144}\n" 96 | ] 97 | } 98 | ], 99 | "source": [ 100 | "ionq.run(shots=4096, on_simulator=True)" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 7, 106 | "id": "2ffb0bad", 107 | "metadata": {}, 108 | "outputs": [ 109 | { 110 | "data": { 111 | "text/plain": [ 112 | "{'000000': 0.1000976562,\n", 113 | " '000001': 0.04809570312,\n", 114 | " '000010': 0.01342773438,\n", 115 | " '000011': 0.0517578125,\n", 116 | " '000100': 0.01538085938,\n", 117 | " '000101': 0.0087890625,\n", 118 | " '000110': 0.01318359375,\n", 119 | " '000111': 0.03735351562,\n", 120 | " '001000': 0.02001953125,\n", 121 | " '001001': 0.009765625,\n", 122 | " '001010': 0.0029296875,\n", 123 | " '001011': 0.008056640625,\n", 124 | " '001100': 0.01708984375,\n", 125 | " '001101': 0.0087890625,\n", 126 | " '001110': 0.01196289062,\n", 127 | " '001111': 0.02172851562,\n", 128 | " '010000': 0.01611328125,\n", 129 | " '010001': 0.00634765625,\n", 130 | " '010010': 0.00146484375,\n", 131 | " '010011': 0.00732421875,\n", 132 | " '010100': 0.00390625,\n", 133 | " '010101': 0.001708984375,\n", 134 | " '010110': 0.0029296875,\n", 135 | " '010111': 0.00390625,\n", 136 | " '011000': 0.0107421875,\n", 137 | " '011001': 0.007568359375,\n", 138 | " '011010': 0.001708984375,\n", 139 | " '011011': 0.0068359375,\n", 140 | " '011100': 0.009033203125,\n", 141 | " '011101': 0.01000976562,\n", 142 | " '011110': 0.005615234375,\n", 143 | " '011111': 0.01538085938,\n", 144 | " '100000': 0.046875,\n", 145 | " '100001': 0.02221679688,\n", 146 | " '100010': 0.0087890625,\n", 147 | " '100011': 0.0224609375,\n", 148 | " '100100': 0.01049804688,\n", 149 | " '100101': 0.004638671875,\n", 150 | " '100110': 0.007080078125,\n", 151 | " '100111': 0.01977539062,\n", 152 | " '101000': 0.007568359375,\n", 153 | " '101001': 0.004638671875,\n", 154 | " '101010': 0.001220703125,\n", 155 | " '101011': 0.005126953125,\n", 156 | " '101100': 0.01220703125,\n", 157 | " '101101': 0.008056640625,\n", 158 | " '101110': 0.008544921875,\n", 159 | " '101111': 0.0126953125,\n", 160 | " '110000': 0.05419921875,\n", 161 | " '110001': 0.02734375,\n", 162 | " '110010': 0.005859375,\n", 163 | " '110011': 0.02807617188,\n", 164 | " '110100': 0.008544921875,\n", 165 | " '110101': 0.006591796875,\n", 166 | " '110110': 0.005615234375,\n", 167 | " '110111': 0.01635742188,\n", 168 | " '111000': 0.02978515625,\n", 169 | " '111001': 0.01879882812,\n", 170 | " '111010': 0.00390625,\n", 171 | " '111011': 0.0166015625,\n", 172 | " '111100': 0.02197265625,\n", 173 | " '111101': 0.01147460938,\n", 174 | " '111110': 0.0166015625,\n", 175 | " '111111': 0.02685546875}" 176 | ] 177 | }, 178 | "execution_count": 7, 179 | "metadata": {}, 180 | "output_type": "execute_result" 181 | } 182 | ], 183 | "source": [ 184 | "ionq.results()" 185 | ] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "execution_count": null, 190 | "id": "f4091b82", 191 | "metadata": {}, 192 | "outputs": [], 193 | "source": [] 194 | } 195 | ], 196 | "metadata": { 197 | "kernelspec": { 198 | "display_name": "Python 3 (ipykernel)", 199 | "language": "python", 200 | "name": "python3" 201 | }, 202 | "language_info": { 203 | "codemirror_mode": { 204 | "name": "ipython", 205 | "version": 3 206 | }, 207 | "file_extension": ".py", 208 | "mimetype": "text/x-python", 209 | "name": "python", 210 | "nbconvert_exporter": "python", 211 | "pygments_lexer": "ipython3", 212 | "version": "3.9.6" 213 | } 214 | }, 215 | "nbformat": 4, 216 | "nbformat_minor": 5 217 | } 218 | -------------------------------------------------------------------------------- /src/simuq/ionq/ionq_provider.py: -------------------------------------------------------------------------------- 1 | from simuq.aais import heisenberg, two_pauli 2 | from simuq.ionq.ionq_api_transpiler import IonQAPITranspiler, IonQAPITranspiler_2Pauli 3 | from simuq.provider import BaseProvider 4 | from simuq.solver import generate_as 5 | import time 6 | 7 | 8 | class IonQProvider(BaseProvider): 9 | def __init__(self, api_key=None, from_file=None): 10 | if api_key is None: 11 | if from_file is None: 12 | raise Exception("No API_key provided.") 13 | else: 14 | with open(from_file, "r") as f: 15 | api_key = f.readline().strip() 16 | self.API_key = api_key 17 | self.all_backends = ["harmony", "aria-1", "aria-2", "forte"] 18 | 19 | super().__init__() 20 | 21 | def supported_backends(self): 22 | print(self.all_backends) 23 | 24 | def compile( 25 | self, 26 | qs, 27 | backend="aria-1", 28 | aais="heisenberg", 29 | tol=0.01, 30 | trotter_num=6, 31 | trotter_mode=1, 32 | state_prep=None, 33 | meas_prep=None, 34 | verbose=0, 35 | ): 36 | if backend == "harmony": 37 | nsite = 11 38 | elif backend == "aria-1": 39 | nsite = 23 40 | elif backend == "aria-2": 41 | nsite = 23 42 | elif backend == "forte": 43 | nsite = 32 44 | elif isinstance(backend, int): 45 | if backend > 0: 46 | nsite = backend 47 | else: 48 | raise Exception("Backend is not supported.") 49 | 50 | if qs.num_sites > nsite: 51 | raise Exception("Device has less sites than the target quantum system.") 52 | 53 | if aais == "heisenberg": 54 | mach = heisenberg.generate_qmachine(qs.num_sites, e=None) 55 | comp = IonQAPITranspiler().transpile 56 | elif aais == "two_pauli" or "2pauli": 57 | mach = two_pauli.generate_qmachine(qs.num_sites, e=None) 58 | comp = IonQAPITranspiler_2Pauli().transpile 59 | 60 | if trotter_mode == "random": 61 | trotter_args = {"num": trotter_num, "order": 1, "sequential": False, "randomized": True} 62 | else: 63 | trotter_args = {"num": trotter_num, "order": trotter_mode, "sequential": True, "randomized": False} 64 | 65 | layout, sol_gvars, boxes, edges = generate_as( 66 | qs, 67 | mach, 68 | trotter_args=trotter_args, 69 | solver="least_squares", 70 | solver_args={"tol": tol}, 71 | override_layout=[i for i in range(qs.num_sites)], 72 | verbose=verbose, 73 | ) 74 | self.prog = comp( 75 | qs.num_sites, 76 | sol_gvars, 77 | boxes, 78 | edges, 79 | trotter_args=trotter_args, 80 | backend="qpu." + backend, 81 | noise_model=backend, 82 | ) 83 | 84 | if state_prep is not None: 85 | self.prog = state_prep.copy().add(self.prog, inherit_from_back=True) 86 | 87 | if meas_prep is not None: 88 | self.prog.add(meas_prep) 89 | 90 | self.prog = self.prog.optimize() 91 | self.prog_obj = self.prog 92 | self.prog = self.prog.job 93 | 94 | self.layout = layout 95 | self.qs_names = qs.print_sites() 96 | 97 | def print_circuit(self): 98 | if self.prog is None: 99 | raise Exception("No compiled job in record.") 100 | print(self.prog["input"]["circuit"]) 101 | 102 | def run(self, shots, on_simulator=False, with_noise=False, verbose=0): 103 | if self.prog is None: 104 | raise Exception("No compiled job in record.") 105 | 106 | import json 107 | 108 | import requests 109 | 110 | headers = { 111 | "Authorization": "apiKey " + self.API_key, 112 | "Content-Type": "application/json", 113 | } 114 | 115 | data = self.prog.copy() 116 | 117 | data["shots"] = shots 118 | if on_simulator: 119 | data["target"] = "simulator" 120 | if not with_noise: 121 | data["noise"] = {"model": "ideal"} 122 | 123 | # print(data) 124 | 125 | response = requests.post( 126 | "https://api.ionq.co/v0.3/jobs", headers=headers, data=json.dumps(data) 127 | ) 128 | self.task = response.json() 129 | 130 | if verbose >= 0: 131 | print(self.task) 132 | 133 | return self.task 134 | 135 | def results(self, job_id=None, wait=None, verbose=0): 136 | if job_id is None: 137 | if self.task is not None: 138 | job_id = self.task["id"] 139 | else: 140 | raise Exception("No submitted job in record.") 141 | 142 | import requests 143 | 144 | headers = { 145 | "Authorization": "apiKey " + self.API_key, 146 | } 147 | 148 | if wait == None : 149 | response = requests.get("https://api.ionq.co/v0.2/jobs/" + job_id, headers=headers) 150 | res = response.json() 151 | 152 | if res["status"] != "completed": 153 | if verbose >= 0: 154 | print("Job is not completed") 155 | print(res) 156 | return None 157 | elif isinstance(wait, int) or wait == True: 158 | if wait == True : 159 | wait = 5 160 | response = requests.get("https://api.ionq.co/v0.2/jobs/" + job_id, headers=headers) 161 | res = response.json() 162 | 163 | while res["status"] != "completed": 164 | time.sleep(wait) 165 | response = requests.get("https://api.ionq.co/v0.2/jobs/" + job_id, headers=headers) 166 | res = response.json() 167 | 168 | def layout_rev(res): 169 | n = len(self.layout) 170 | b = IonQProvider.to_bin(res, n) 171 | ret = "" 172 | for i in range(n): 173 | ret += b[self.layout[i]] 174 | return ret 175 | 176 | def results_from_data(data): 177 | ret = dict() 178 | for key in data.keys(): 179 | ret[layout_rev(int(key))[-1::-1]] = data[key] 180 | return ret 181 | 182 | return results_from_data(res["data"]["histogram"]) 183 | -------------------------------------------------------------------------------- /notebooks/artifact_evaluation/cs4-benchmark-QuEra.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "c0cf0f49", 6 | "metadata": {}, 7 | "source": [ 8 | "This notebook reproduces the compilation results for employing SimuQ compilation on the quantum systems in the small benchmark on QuEra devices. Note that here we consider the (future) QuEra devices with local laser controls. Since this device is not online for now, the corresponding compilation settings are not currently wrapped into a provider. We directly call the compiler methods of SimuQ in this notebook, whose compilation results are Bloqade code in Julia. You are welcome to simulate them in Julia's Bloqade extension, while we focus on the compilation itself in this notebook." 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": 1, 14 | "id": "b6404ee1", 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "import time\n", 19 | "Repitition = 1\n", 20 | "T = 1" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": 2, 26 | "id": "898d1252", 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "def run_benchmark(qs, dimension):\n", 31 | " from simuq.solver import generate_as\n", 32 | " if dimension == 1 :\n", 33 | " from simuq.aais.rydberg1d import generate_qmachine\n", 34 | " from simuq.backends.bloqade_rydberg import transpile\n", 35 | " else :\n", 36 | " from simuq.aais.rydberg2d import generate_qmachine\n", 37 | " from simuq.backends.bloqade_rydberg2d import transpile\n", 38 | " n_qubits = len(qs.sites)\n", 39 | " mach = generate_qmachine(n_qubits)\n", 40 | " sum_time = 0\n", 41 | " try:\n", 42 | " for _ in range(Repitition) :\n", 43 | " start_time = time.time()\n", 44 | " res = transpile(*generate_as(qs, mach, Trot, solver = 'least_squares', solver_tol = 2*n_qubits, override_layout = [i for i in range(N)]))\n", 45 | " end_time = time.time()\n", 46 | " this_time = end_time - start_time\n", 47 | " sum_time += this_time\n", 48 | " print(f\"Avgerage compilation time for {n_qubits} qubit using SimuQ\", sum_time / Repitition)\n", 49 | " except:\n", 50 | " print(\"Fail!\")" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": null, 56 | "id": "c68fcfc6", 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "from simuq.systems.benchmark.ising_chain import GenQS\n", 61 | "for N in [6,32,64,96]:\n", 62 | " qs = GenQS(N, T, 1, 1)\n", 63 | " run_benchmark(qs, 1)" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": null, 69 | "id": "f97db0df", 70 | "metadata": {}, 71 | "outputs": [], 72 | "source": [ 73 | "from simuq.systems.benchmark.ising_cycle import GenQS\n", 74 | "for N in [6,12,32,64]:\n", 75 | " qs = GenQS(N, T, 1, 1)\n", 76 | " run_benchmark(qs, 2)" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": null, 82 | "id": "c81f280c", 83 | "metadata": {}, 84 | "outputs": [], 85 | "source": [ 86 | "from simuq.systems.benchmark.heis_chain import GenQS\n", 87 | "N=32\n", 88 | "qs = GenQS(N, T, 1, 1)\n", 89 | "run_benchmark(qs, 1, 2)" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "id": "f14d59b1", 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "from simuq.systems.benchmark.qaoa_cycle import GenQS\n", 100 | "N=12\n", 101 | "qs = GenQS(N, 3,[1,1,1,1,1,1])\n", 102 | "run_benchmark(qs, 2)" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": null, 108 | "id": "14e7f5ba", 109 | "metadata": {}, 110 | "outputs": [], 111 | "source": [ 112 | "from simuq.systems.benchmark.qhd import GenQS\n", 113 | "N=16\n", 114 | "qs = GenQS(N, T, 1, 1)\n", 115 | "run_benchmark(qs, 2)" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": null, 121 | "id": "1bbcc8a0", 122 | "metadata": {}, 123 | "outputs": [], 124 | "source": [ 125 | "from simuq.systems.benchmark.mis_chain import GenQS\n", 126 | "for N in [12,24]:\n", 127 | " qs = GenQS(N, D=2)\n", 128 | " run_benchmark(qs, 1)" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": null, 134 | "id": "f9c45265", 135 | "metadata": {}, 136 | "outputs": [], 137 | "source": [ 138 | "from simuq.systems.benchmark.mis_grid import GenQS\n", 139 | "for k in [4,5]:\n", 140 | " qs = GenQS(k, D=2)\n", 141 | " run_benchmark(qs, 2)" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": null, 147 | "id": "92183e3a", 148 | "metadata": {}, 149 | "outputs": [], 150 | "source": [ 151 | "from simuq.systems.benchmark.kitaev import GenQS\n", 152 | "N=18\n", 153 | "qs = GenQS(N)\n", 154 | "run_benchmark(qs, 1)" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": null, 160 | "id": "f9c1e90d", 161 | "metadata": {}, 162 | "outputs": [], 163 | "source": [ 164 | "from simuq.systems.benchmark.schwinger import GenQS\n", 165 | "N=10\n", 166 | "qs = GenQS(N)\n", 167 | "run_benchmark(qs, 2)" 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": null, 173 | "id": "d88969f1", 174 | "metadata": {}, 175 | "outputs": [], 176 | "source": [ 177 | "from simuq.systems.benchmark.o3nlσm import GenQS\n", 178 | "N=5\n", 179 | "M=6\n", 180 | "qs = GenQS(N,M)\n", 181 | "run_benchmark(qs, 2)" 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": null, 187 | "id": "c408e429", 188 | "metadata": {}, 189 | "outputs": [], 190 | "source": [] 191 | } 192 | ], 193 | "metadata": { 194 | "kernelspec": { 195 | "display_name": "Python 3 (ipykernel)", 196 | "language": "python", 197 | "name": "python3" 198 | }, 199 | "language_info": { 200 | "codemirror_mode": { 201 | "name": "ipython", 202 | "version": 3 203 | }, 204 | "file_extension": ".py", 205 | "mimetype": "text/x-python", 206 | "name": "python", 207 | "nbconvert_exporter": "python", 208 | "pygments_lexer": "ipython3", 209 | "version": "3.9.15" 210 | } 211 | }, 212 | "nbformat": 4, 213 | "nbformat_minor": 5 214 | } 215 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimuQ 2 | 3 | [![Latest Version](https://img.shields.io/pypi/v/simuq.svg)](https://pypi.python.org/pypi/simuq) 4 | [![Supported Python Versions](https://img.shields.io/pypi/pyversions/simuq.svg)](https://pypi.python.org/pypi/simuq) 5 | 6 | **SimuQ** is a framework for programming quantum simulations, compiling them with analog pulses, and deploying the pulse schedules on real or simulated quantum devices. 7 | 8 | Our [project website](https://pickspeng.github.io/SimuQ/) includes use cases of SimuQ. 9 | 10 | We illustrate our design and benefits in our [arXiv paper](https://arxiv.org/abs/2303.02775). 11 | 12 | ## Installation 13 | 14 | We recommend Python 3.9 or greater since many optional dependencies have a minimum Python version requirement. 15 | 16 | We encourage using `pip` for SimuQ installation. To install the core components of SimuQ, run the following command in the shell: 17 | 18 | ```bash 19 | pip install simuq 20 | ``` 21 | 22 | Multiple platforms are supported by SimuQ through different APIs and you may optionally install them based on your needs. You may install all optional dependencies, though not recommended, by 23 | 24 | ```bash 25 | pip install "simuq[all]" 26 | ``` 27 | 28 | Note that the `[braket]` extra currently has package dependency conflicts with the `[dwave]` extra, so by installing the `[all]` extra we do not install the `[dwave]` extra. You need to manually install `[dwave]' if you want to use D-Wave backends. 29 | 30 | ### Amazon Braket 31 | 32 | SimuQ supports compilation to IonQ's trapped-ion devices and QuEra's neutral atom devices through Amazon Braket. Install the Amazon Braket provider and its dependencies by running 33 | 34 | ```bash 35 | pip install "simuq[braket]" 36 | ``` 37 | 38 | If running on QPUs, make sure that your AWS account is onboarded to Amazon Braket, as per the instructions [here](https://github.com/amazon-braket/amazon-braket-sdk-python#prerequisites) (this isn't necessary for running on the local simulator). 39 | 40 | ### IonQ Quantum Cloud 41 | 42 | SimuQ supports compilation to IonQ's trapped-ion devices through IonQ Quantum Cloud. Install the IonQ provider and its dependencies by running 43 | 44 | ```bash 45 | pip install "simuq[ionq]" 46 | ``` 47 | 48 | To run through IonQ Quantum Cloud, you must obtain an API key from [IonQ](https://ionq.com/quantum-cloud). When creating IonQ providers in SimuQ, you must provide the API key either through a string or a file storing the key. 49 | 50 | ### Qiskit 51 | 52 | SimuQ supports compilation to IBM's superconducting devices through Qiskit and IBM Quantum. Install the Qiskit provider and its dependencies by running 53 | 54 | ```bash 55 | pip install "simuq[qiskit]" 56 | ``` 57 | 58 | To run through IBM Quantum, you must obtain an API token from [IBM](https://quantum-computing.ibm.com/). 59 | 60 | ### D-Wave Leap 61 | 62 | SimuQ supports compilation to D-Wave's superconducting devices through D-Wave Leap. Install the D-Wave provider and its dependencies by running 63 | 64 | ```bash 65 | pip install "simuq[dwave]" 66 | ``` 67 | 68 | Note that this extra now has package dependency conflicts with the `[braket]` extra. Hence, users cannot simultaneously run SimuQ through D-Wave providers and Braket providers, and users need to choose which extra to install, if any. This is a known issue for the hardware providers and may need a period of time to be fixed. 69 | 70 | The programming of Hamiltonian systems for D-Wave devices is also different. Due to the limited controllability of D-Wave's devices, currently, the programming is limited to the final Hamiltonian of the evolution, and the annealing schedule can be directly passed to D-Wave providers in compilation. 71 | 72 | ### QuTiP 73 | 74 | You can simulate the dynamics of a SimuQ quantum system with QuTiP. Install the QuTiP provider and its dependencies by running 75 | 76 | ```bash 77 | pip install "simuq[qutip]" 78 | ``` 79 | 80 | ### Installing from source 81 | 82 | You can also install from the source by cloning this repository and running a pip install command in the root directory of the repository: 83 | 84 | ```bash 85 | git clone git@github.com:PicksPeng/SimuQ.git 86 | cd SimuQ 87 | pip install . 88 | ``` 89 | 90 | If installing extras, remember to include quotes around the target (for example, `pip install ".[dev]"`). To install in editable mode, run `pip install` with the `-e` flag: `pip install -e ".[dev]"`. 91 | 92 | ## Examples 93 | 94 | For examples of SimuQ usage, refer to the notebooks in the [tutorials folder](https://github.com/PicksPeng/SimuQ/tree/main/notebooks/tutorials). 95 | 96 | ## Project Structure 97 | 98 | `notebooks/`: Example notebooks of running SimuQ and obtaining results. 99 | 100 | `notebooks/tutorials`: Tutorial notebooks on usage of SimuQ. 101 | 102 | `notebooks/artifact_evaluation`: AE notebooks to reproduce the data in our paper. 103 | 104 | `src/simuq/`: The core compiler and language implementation of SimuQ. 105 | 106 | `src/simuq/aais/`: AAISs of many backends in AAIS Specification Language. 107 | 108 | `src/simuq/backends/`: The translation to API languages of different machine backends. 109 | 110 | `src/simuq/systems/`: Hamiltonian systems implemented in Hamiltonian Modeling Language. 111 | 112 | `src/simuq/systems/benchmark/`: A small benchmark of quantum Hamiltonian simulation as reported in our paper. 113 | 114 | `src/simuq/ionq/`: The IonQ provider using IonQ Quantum Cloud. 115 | 116 | `src/simuq/braket/`: The Braket provider using AWS Braket. 117 | 118 | `src/simuq/qiskit/`: The Qiskit provider using IBM Quantum. 119 | 120 | `src/simuq/dwave/`: The D-Wave provider using D-Wave Leap. 121 | 122 | `src/simuq/qutip/`: The QuTiP provider using QuTiP. 123 | 124 | ## Related projects 125 | 126 | Please also check several projects related to SimuQ: 127 | 128 | - [QHDOPT](https://github.com/jiaqileng/QHDOPT): A software package for non-linear and non-convex continuous optimization using quantum backends. It employs SimuQ to program and deploy [Quantum Hamiltonian Descent](https://jiaqileng.github.io/quantum-hamiltonian-descent/) algorithm to find the minimum value of non-convex functions with box constraints. 129 | 130 | ## Citations 131 | 132 | If you use SimuQ in your work, please cite our paper. 133 | 134 | ``` 135 | @article{peng2024simuq, 136 | author = {Peng, Yuxiang and Young, Jacob and Liu, Pengyu and Wu, Xiaodi}, 137 | title = {SimuQ: A Framework for Programming Quantum Hamiltonian Simulation with Analog Compilation}, 138 | year = {2024}, 139 | issue_date = {January 2024}, 140 | publisher = {Association for Computing Machinery}, 141 | address = {New York, NY, USA}, 142 | volume = {8}, 143 | number = {POPL}, 144 | url = {https://doi.org/10.1145/3632923}, 145 | doi = {10.1145/3632923}, 146 | month = {jan}, 147 | articleno = {81}, 148 | numpages = {31}, 149 | } 150 | ``` 151 | -------------------------------------------------------------------------------- /notebooks/experimental/mis_bloqade.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "24a177e2", 7 | "metadata": { 8 | "ExecuteTime": { 9 | "end_time": "2023-08-11T23:46:06.822103Z", 10 | "start_time": "2023-08-11T23:46:06.802215Z" 11 | } 12 | }, 13 | "outputs": [], 14 | "source": [ 15 | "%load_ext autoreload\n", 16 | "%autoreload 1\n", 17 | "import sys\n", 18 | "sys.path.append('..')" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 2, 24 | "id": "2f3bac74", 25 | "metadata": { 26 | "ExecuteTime": { 27 | "end_time": "2023-08-11T23:46:07.223853Z", 28 | "start_time": "2023-08-11T23:46:06.845507Z" 29 | } 30 | }, 31 | "outputs": [], 32 | "source": [ 33 | "from simuq.solver import generate_as\n", 34 | "from simuq.systems.mis import GenQS\n", 35 | "from simuq.aais import rydberg1d\n", 36 | "from simuq.backends.bloqade_rydberg import transpile\n", 37 | "\n", 38 | "n = 3\n", 39 | "D = 3\n", 40 | "qs = GenQS(k = n, dis_num = D)\n", 41 | "mach = rydberg1d.generate_qmachine(n)" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 3, 47 | "id": "c945c671", 48 | "metadata": { 49 | "ExecuteTime": { 50 | "end_time": "2023-08-11T23:46:07.981252Z", 51 | "start_time": "2023-08-11T23:46:07.228567Z" 52 | } 53 | }, 54 | "outputs": [ 55 | { 56 | "name": "stdout", 57 | "output_type": "stream", 58 | "text": [ 59 | "using Bloqade\n", 60 | "atoms = AtomList([(0,), (13.58206868288798,), (27.164137365723022,)]) \n", 61 | "omega = [piecewise_constant(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [0.8635591228675065, 0.8635591389602651, 0.8635593056576278]), piecewise_constant(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [0.863559122867507, 0.863559138960265, 0.8635593056576276]), piecewise_constant(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [0.8635591228675061, 0.8635591389602654, 0.8635593056576283]), ]\n", 62 | "phi = [piecewise_constant(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [7.0079131589306384e-15, -7.429368798680961e-17, 2.3339274700974157e-15]), piecewise_constant(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [-1.49189336400032e-15, 2.6018484149510833e-15, 3.651210682184916e-15]), piecewise_constant(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [-8.819121002792873e-16, -2.866223409476223e-15, 1.9554397719368374e-15]), ]\n", 63 | "delta = [piecewise_constant(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [-0.209196715756555, -0.0652702033509709, 0.07865624624034426]), piecewise_constant(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [-0.2159951139467475, -0.07206861036291254, 0.07185775216118767]), piecewise_constant(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [-0.20919671572721554, -0.06527020334325068, 0.07865624664079349]), ]\n", 64 | "h = rydberg_h(atoms; Δ = delta, Ω = omega, ϕ = phi)\n" 65 | ] 66 | } 67 | ], 68 | "source": [ 69 | "Trot = 1\n", 70 | "tol = 0.1\n", 71 | "\n", 72 | "bloqade_code = transpile(*generate_as(qs, mach, Trot, 'least_squares', tol), inter_order = 0)\n", 73 | "print(bloqade_code)" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": 4, 79 | "id": "d8ada597", 80 | "metadata": { 81 | "ExecuteTime": { 82 | "end_time": "2023-08-11T23:46:08.592961Z", 83 | "start_time": "2023-08-11T23:46:08.105866Z" 84 | } 85 | }, 86 | "outputs": [ 87 | { 88 | "name": "stdout", 89 | "output_type": "stream", 90 | "text": [ 91 | "using Bloqade\n", 92 | "atoms = AtomList([(0,), (13.58206868288798,), (27.164137365723022,)]) \n", 93 | "omega = [piecewise_linear(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [0.8635591228675065, 0.8635591389602651, 0.8635593056576278, 0.8635594723549568]), piecewise_linear(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [0.863559122867507, 0.863559138960265, 0.8635593056576276, 0.8635594723549564]), piecewise_linear(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [0.8635591228675061, 0.8635591389602654, 0.8635593056576283, 0.8635594723549574]), ]\n", 94 | "phi = [piecewise_linear(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [7.0079131589306384e-15, -7.429368798680961e-17, 2.3339274700974157e-15, 4.742148140145427e-15]), piecewise_linear(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [-1.49189336400032e-15, 2.6018484149510833e-15, 3.651210682184916e-15, 4.700572736761042e-15]), piecewise_linear(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [-8.819121002792873e-16, -2.866223409476223e-15, 1.9554397719368374e-15, 6.777101976219437e-15]), ]\n", 95 | "delta = [piecewise_linear(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [-0.209196715756555, -0.0652702033509709, 0.07865624624034426, 0.22258266666435497]), piecewise_linear(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [-0.2159951139467475, -0.07206861036291254, 0.07185775216118767, 0.21578408551800107]), piecewise_linear(clocks = [0, 1.5439977456369967, 3.087995465487108, 4.631992872439543], values = [-0.20919671572721554, -0.06527020334325068, 0.07865624664079349, 0.22258266745753313]), ]\n", 96 | "h = rydberg_h(atoms; Δ = delta, Ω = omega, ϕ = phi)\n" 97 | ] 98 | } 99 | ], 100 | "source": [ 101 | "Trot = 1\n", 102 | "tol = 0.1\n", 103 | "\n", 104 | "bloqade_code = transpile(*generate_as(qs, mach, Trot, 'least_squares', tol), inter_order = 1)\n", 105 | "print(bloqade_code)" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": null, 111 | "id": "91eabc11", 112 | "metadata": {}, 113 | "outputs": [], 114 | "source": [] 115 | } 116 | ], 117 | "metadata": { 118 | "kernelspec": { 119 | "display_name": "Python 3 (ipykernel)", 120 | "language": "python", 121 | "name": "python3" 122 | }, 123 | "language_info": { 124 | "codemirror_mode": { 125 | "name": "ipython", 126 | "version": 3 127 | }, 128 | "file_extension": ".py", 129 | "mimetype": "text/x-python", 130 | "name": "python", 131 | "nbconvert_exporter": "python", 132 | "pygments_lexer": "ipython3", 133 | "version": "3.9.6" 134 | } 135 | }, 136 | "nbformat": 4, 137 | "nbformat_minor": 5 138 | } 139 | -------------------------------------------------------------------------------- /notebooks/experimental/test_qutip.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "42b87d5f", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "%load_ext autoreload\n", 11 | "%autoreload 2\n", 12 | "import sys\n", 13 | "sys.path.append('..')" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 2, 19 | "id": "90795bcd", 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "from simuq.qutip import QuTiPProvider\n", 24 | "qpp = QuTiPProvider()" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": 3, 30 | "id": "c18022c4", 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "from simuq.qsystem import QSystem, qubit\n", 35 | "qs = QSystem()\n", 36 | "q0, q1 = qubit(qs, 'q0'), qubit(qs, 'q1')\n", 37 | "h = q0.X\n", 38 | "qs.add_evolution(h, 1)" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": 4, 44 | "id": "28c83d41", 45 | "metadata": {}, 46 | "outputs": [ 47 | { 48 | "name": "stdout", 49 | "output_type": "stream", 50 | "text": [ 51 | "Compiled.\n", 52 | "Solved.\n", 53 | "Order of sites: ['q0', 'q1']\n" 54 | ] 55 | }, 56 | { 57 | "data": { 58 | "text/plain": [ 59 | "{'00': 0.2919499925618451, '01': 0.0, '10': 0.7080500074381548, '11': 0.0}" 60 | ] 61 | }, 62 | "execution_count": 4, 63 | "metadata": {}, 64 | "output_type": "execute_result" 65 | } 66 | ], 67 | "source": [ 68 | "qpp.compile(qs)\n", 69 | "qpp.run()\n", 70 | "qpp.print_sites()\n", 71 | "qpp.results()" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 5, 77 | "id": "3606fddd", 78 | "metadata": {}, 79 | "outputs": [], 80 | "source": [ 81 | "from simuq.systems.ising import GenQS\n", 82 | "N = 6\n", 83 | "T = 1.0\n", 84 | "qs = GenQS(N, T, 1, 1, is_chain=True)" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 6, 90 | "id": "cfe90e6e", 91 | "metadata": {}, 92 | "outputs": [ 93 | { 94 | "name": "stdout", 95 | "output_type": "stream", 96 | "text": [ 97 | "Compiled.\n", 98 | "Solved.\n", 99 | "Order of sites: ['Qubit0', 'Qubit1', 'Qubit2', 'Qubit3', 'Qubit4', 'Qubit5']\n" 100 | ] 101 | }, 102 | { 103 | "data": { 104 | "text/plain": [ 105 | "{'000000': 0.13909131418319828,\n", 106 | " '000001': 0.07822605750525922,\n", 107 | " '000010': 0.0019891738016797403,\n", 108 | " '000011': 0.057755821522070594,\n", 109 | " '000100': 0.012701525310985506,\n", 110 | " '000101': 0.0010028122960455785,\n", 111 | " '000110': 0.008172081460361962,\n", 112 | " '000111': 0.03514806460942275,\n", 113 | " '001000': 0.012701525310985498,\n", 114 | " '001001': 0.01066912189723169,\n", 115 | " '001010': 0.000641148899813737,\n", 116 | " '001011': 0.001593467823500089,\n", 117 | " '001100': 0.01879554437338071,\n", 118 | " '001101': 0.006197928749543394,\n", 119 | " '001110': 0.011977895680550896,\n", 120 | " '001111': 0.0295336082233224,\n", 121 | " '010000': 0.0019891738016797433,\n", 122 | " '010001': 0.0010065660702148565,\n", 123 | " '010010': 4.503895171517624e-05,\n", 124 | " '010011': 0.0013630767087809722,\n", 125 | " '010100': 0.0006411488998137373,\n", 126 | " '010101': 0.0004487715934167598,\n", 127 | " '010110': 0.0001412216815805133,\n", 128 | " '010111': 0.000206700977356854,\n", 129 | " '011000': 0.008172081460361967,\n", 130 | " '011001': 0.00633462703209487,\n", 131 | " '011010': 0.00014122168158051387,\n", 132 | " '011011': 0.0034243823720158132,\n", 133 | " '011100': 0.011977895680550894,\n", 134 | " '011101': 0.005176742908873442,\n", 135 | " '011110': 0.0045842878227374175,\n", 136 | " '011111': 0.011639683592990061,\n", 137 | " '100000': 0.07822605750525928,\n", 138 | " '100001': 0.04561611063937826,\n", 139 | " '100010': 0.0010065660702148586,\n", 140 | " '100011': 0.02992947999016383,\n", 141 | " '100100': 0.010669121897231702,\n", 142 | " '100101': 0.0011330190338807878,\n", 143 | " '100110': 0.006334627032094861,\n", 144 | " '100111': 0.02561858746394771,\n", 145 | " '101000': 0.0010028122960455768,\n", 146 | " '101001': 0.0011330190338807865,\n", 147 | " '101010': 0.00044877159341675946,\n", 148 | " '101011': 0.00017538455630302608,\n", 149 | " '101100': 0.006197928749543394,\n", 150 | " '101101': 0.0028362515078185074,\n", 151 | " '101110': 0.0051767429088734355,\n", 152 | " '101111': 0.010078229724762109,\n", 153 | " '110000': 0.05775582152207062,\n", 154 | " '110001': 0.02992947999016384,\n", 155 | " '110010': 0.0013630767087809738,\n", 156 | " '110011': 0.02767913030254038,\n", 157 | " '110100': 0.0015934678235000911,\n", 158 | " '110101': 0.0001753845563030268,\n", 159 | " '110110': 0.00342438237201581,\n", 160 | " '110111': 0.010901441439849705,\n", 161 | " '111000': 0.03514806460942275,\n", 162 | " '111001': 0.025618587463947715,\n", 163 | " '111010': 0.00020670097735685483,\n", 164 | " '111011': 0.010901441439849714,\n", 165 | " '111100': 0.02953360822332239,\n", 166 | " '111101': 0.0100782297247621,\n", 167 | " '111110': 0.011639683592990054,\n", 168 | " '111111': 0.034979076367193364}" 169 | ] 170 | }, 171 | "execution_count": 6, 172 | "metadata": {}, 173 | "output_type": "execute_result" 174 | } 175 | ], 176 | "source": [ 177 | "qpp.compile(qs)\n", 178 | "qpp.run(shots = 4096, on_simulator = True)\n", 179 | "qpp.print_sites()\n", 180 | "qpp.results()" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": null, 186 | "id": "051b259c", 187 | "metadata": {}, 188 | "outputs": [], 189 | "source": [] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": null, 194 | "id": "a379e3f0", 195 | "metadata": {}, 196 | "outputs": [], 197 | "source": [] 198 | } 199 | ], 200 | "metadata": { 201 | "kernelspec": { 202 | "display_name": "Python 3 (ipykernel)", 203 | "language": "python", 204 | "name": "python3" 205 | }, 206 | "language_info": { 207 | "codemirror_mode": { 208 | "name": "ipython", 209 | "version": 3 210 | }, 211 | "file_extension": ".py", 212 | "mimetype": "text/x-python", 213 | "name": "python", 214 | "nbconvert_exporter": "python", 215 | "pygments_lexer": "ipython3", 216 | "version": "3.9.15" 217 | } 218 | }, 219 | "nbformat": 4, 220 | "nbformat_minor": 5 221 | } 222 | -------------------------------------------------------------------------------- /notebooks/artifact_evaluation/cs3-qaoa.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "We reproduce Section 5.3 SimuQ data in Table 2 of our paper in this notebook, which contains the cut difference of QAOA on IBM and IonQ devices.\n", 8 | "\n", 9 | "Note that for the convenience of the reviewer, the exhibited code below will generate the simulator results (for free). This case study is specifically designed to showcase the advantage of using interaction-based gates on real devices, which cannot be observed from classical simulation.\n", 10 | "\n", 11 | "If you want to reproduce the real device results, set the on_simulator variable to False. CAUTION: real device experiments may cost your money to run." 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "The IBM provider requires you to have an IBM account to access classical simulator (see README.md). You will be given a API token from the IBM account, and you may use the commented line to create a provider, although you may not have access to the ibmq_gradalupe device. As the time when this document is written, you may access ibmq_brisbane through the open plan of IBM. \n", 19 | "\n", 20 | "If you don't have an IBM account, you can remove the IBM provider and the following executions related to IBM. This merely leads to the lack of IBM (ideal) curve in the first figure." 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": 1, 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [ 29 | "import numpy as np\n", 30 | "\n", 31 | "from simuq import QSystem, Qubit\n", 32 | "from simuq.braket import BraketProvider\n", 33 | "from simuq.ibm import IBMProvider\n", 34 | "\n", 35 | "bp = BraketProvider()\n", 36 | "ibm = IBMProvider(from_file=\"../../../qiskit_APIKEY\", hub=\"ibm-q-ornl\", group=\"ornl\", project=\"phy147\")\n", 37 | "#ibm = IBMProvider(api_key=\"YOUR_API_TOKEN\", hub=\"ibm-q\", group=\"open\", project=\"main\")" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": 2, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "def GenQS(n=12, p=3):\n", 47 | " qs = QSystem()\n", 48 | " ql = [Qubit(qs) for i in range(n)]\n", 49 | " link = [(i, (i + 1) % n) for i in range(n)]\n", 50 | " if p==3: \n", 51 | " parameter_list = np.array(\n", 52 | " [\n", 53 | " 0.5702193 * 2,\n", 54 | " -0.58631086,\n", 55 | " 0.85160685 * 2,\n", 56 | " -1.7058538,\n", 57 | " 0.29468536 * 2,\n", 58 | " -1.132814,\n", 59 | " ]\n", 60 | " )/2\n", 61 | " if p==2:\n", 62 | " parameter_list = np.array([-3.8034,1.2438, -1.2467,-2.4899])/2\n", 63 | " if p==1:\n", 64 | " parameter_list = np.array([-0.7854,-2.3562])/2\n", 65 | "\n", 66 | " for i in range(p):\n", 67 | " h = 0\n", 68 | " for q0, q1 in link:\n", 69 | " h += parameter_list[2 * i] * ql[q0].Z * ql[q1].Z\n", 70 | " qs.add_evolution(h, 1)\n", 71 | " h = 0\n", 72 | " for q0 in range(n):\n", 73 | " h += parameter_list[2 * i+1] * ql[q0].X\n", 74 | " qs.add_evolution(h, 1)\n", 75 | " return qs\n", 76 | "\n", 77 | "def calc_cut(count):\n", 78 | " cut=0\n", 79 | " n_shots=sum(count.values())\n", 80 | " for key in count:\n", 81 | " cut+=count[key]*calc_weight(key)/n_shots\n", 82 | " return cut\n", 83 | "\n", 84 | "def calc_weight(key):\n", 85 | " cut=0\n", 86 | " for i in range(len(key)):\n", 87 | " if key[i]!= key[(i+1)%len(key)]:\n", 88 | " cut+=1\n", 89 | " return cut\n", 90 | "\n", 91 | "ideal_cut_size = [9, 10, 10.5]" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 5, 97 | "metadata": {}, 98 | "outputs": [ 99 | { 100 | "name": "stdout", 101 | "output_type": "stream", 102 | "text": [ 103 | "For p=1, IonQ gives cut size 8.998046875, different from ideal value 9 by 0.001953125.\n", 104 | "For p=1, IBM gives cut size 8.990234375, different from ideal value 9 by 0.009765625.\n", 105 | "For p=2, IonQ gives cut size 9.990234375, different from ideal value 10 by 0.009765625.\n", 106 | "For p=2, IBM gives cut size 10.00732421875, different from ideal value 10 by 0.00732421875.\n", 107 | "For p=3, IonQ gives cut size 10.455078125, different from ideal value 10.5 by 0.044921875.\n", 108 | "For p=3, IBM gives cut size 10.5126953125, different from ideal value 10.5 by 0.0126953125.\n" 109 | ] 110 | } 111 | ], 112 | "source": [ 113 | "on_simulator = True\n", 114 | "\n", 115 | "from simuq.braket import BraketIonQCircuit\n", 116 | "state_prep = BraketIonQCircuit(12)\n", 117 | "for i in range(12):\n", 118 | " state_prep.gpi2(i, np.pi / 2)\n", 119 | " \n", 120 | "from qiskit import QuantumCircuit\n", 121 | "all_h_circ = QuantumCircuit(12)\n", 122 | "for i in range(12):\n", 123 | " all_h_circ.h(i)\n", 124 | "\n", 125 | "for p in [1, 2, 3]:\n", 126 | " qs = GenQS(12, p)\n", 127 | " \n", 128 | " bp.compile(qs, provider=\"ionq\", device=\"Aria-1\", aais=\"heisenberg\", trotter_num=4, state_prep=state_prep, verbose=-1)\n", 129 | " bp.run(shots=1024, on_simulator=on_simulator, verbose=-1)\n", 130 | " result = bp.results()\n", 131 | " cs = calc_cut(result)\n", 132 | " print(f\"For p={p}, IonQ gives cut size {cs}, different from ideal value {ideal_cut_size[p-1]} by {abs(ideal_cut_size[p-1]-cs)}.\")\n", 133 | " \n", 134 | " ibm.compile(qs, backend=\"ibmq_guadalupe\", trotter_num=4, use_pulse=False, state_prep=all_h_circ, verbose=-1)\n", 135 | " ibm.run(on_simulator=on_simulator, verbose=-1)\n", 136 | " result = ibm.results(on_simulator=on_simulator)\n", 137 | " cs = calc_cut(result)\n", 138 | " print(f\"For p={p}, IBM gives cut size {cs}, different from ideal value {ideal_cut_size[p-1]} by {abs(ideal_cut_size[p-1]-cs)}.\")" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": null, 144 | "metadata": {}, 145 | "outputs": [], 146 | "source": [] 147 | } 148 | ], 149 | "metadata": { 150 | "kernelspec": { 151 | "display_name": "Python 3 (ipykernel)", 152 | "language": "python", 153 | "name": "python3" 154 | }, 155 | "language_info": { 156 | "codemirror_mode": { 157 | "name": "ipython", 158 | "version": 3 159 | }, 160 | "file_extension": ".py", 161 | "mimetype": "text/x-python", 162 | "name": "python", 163 | "nbconvert_exporter": "python", 164 | "pygments_lexer": "ipython3", 165 | "version": "3.9.15" 166 | } 167 | }, 168 | "nbformat": 4, 169 | "nbformat_minor": 2 170 | } 171 | --------------------------------------------------------------------------------