diff --git a/frontend/test/test_oqd/oqd/calibration_data/device.toml b/frontend/test/test_oqd/oqd/calibration_data/device.toml new file mode 100644 index 0000000000..42c9d77588 --- /dev/null +++ b/frontend/test/test_oqd/oqd/calibration_data/device.toml @@ -0,0 +1,743 @@ +# OQD Device Parameters +# ~~~~~~~~~~~~~~~~~~~~~ +# +# A database containing parameters relating to the hardware and experiment workflow used in an Open +# Quantum Design (OQD) trapped-ion quantum computer device. + +oqd_config_schema = "v0.1" + +[parameters.N_load] +description = "Number of ions" +stage = "Loading" +process = "Ablation" +equation = "" +value = "1" +unit = "" + +[parameters.w_ablation] +description = "Ablation laser frequency" +stage = "Loading" +process = "Ablation" +equation = "" +value = "532" +unit = "nm" + +[parameters.t_ablation] +description = "Ablation laser pulse duration" +stage = "Loading" +process = "Ablation" +equation = "" +value = "1" +unit = "ns" + +[parameters.I_ablation] +description = "Ablation laser intensity" +stage = "Loading" +process = "Ablation" +equation = "" +value = "0.57" +unit = "J/cm^2" + +[parameters.rate_ablation] +description = "Ablation laser pulse rate" +stage = "Loading" +process = "Ablation" +equation = "" +value = "" +unit = "" + +[parameters.N_ablation] +description = "Ablation laser # of pulses" +stage = "Loading" +process = "Ablation" +equation = "rate_ablation * t_ablation" +value = "" +unit = "" + +[parameters."w_ionization^1"] +description = "Ionization laser 1 frequency" +stage = "Loading" +process = "Ablation" +equation = "" +value = "554" +unit = "nm" + +[parameters."t_ionization^1"] +description = "Ionization laser 1 pulse duration" +stage = "Loading" +process = "Ablation" +equation = "" +value = "" +unit = "" + +[parameters."I_ionization^1"] +description = "Ionization laser 1 intensity" +stage = "Loading" +process = "Ablation" +equation = "" +value = "" +unit = "" + +[parameters."w_ionization^2"] +description = "Ionization laser 2 frequency" +stage = "Loading" +process = "Ablation" +equation = "" +value = "390" +unit = "nm" + +[parameters."t_ionization^2"] +description = "Ionization laser 2 pulse duration" +stage = "Loading" +process = "Ablation" +equation = "" +value = "" +unit = "" + +[parameters."I_ionization^2"] +description = "Ionization laser 2 intensity" +stage = "Loading" +process = "Ablation" +equation = "" +value = "" +unit = "" + +[parameters.x_target] +description = "Target x position" +stage = "Loading" +process = "Ablation" +equation = "" +value = "" +unit = "" + +[parameters.y_target] +description = "Target y position" +stage = "Loading" +process = "Ablation" +equation = "" +value = "" +unit = "" + +[parameters.N_trap] +description = "Number of ions" +stage = "Trapping" +process = "Ion characteristics" +equation = "" +value = "1" +unit = "" + +[parameters.m] +description = "Ion mass" +stage = "Trapping" +process = "Ion characteristics" +equation = "" +value = "133" +unit = "AMU" + +[parameters.q] +description = "Ion charge" +stage = "Trapping" +process = "Ion characteristics" +equation = "" +value = "+e" +unit = "" + +[parameters.w_x] +description = "Radial frequency, x" +stage = "Trapping" +process = "Ion characteristics" +equation = "" +value = "" +unit = "" + +[parameters.w_y] +description = "Radial frequency, y" +stage = "Trapping" +process = "Ion characteristics" +equation = "" +value = "" +unit = "" + +[parameters.w_z] +description = "Axial frequency" +stage = "Trapping" +process = "Ion characteristics" +equation = "" +value = "" +unit = "" + +[parameters.V_RFT] +description = "RF trapping voltage" +stage = "Trapping" +process = "Electric Potential" +equation = "" +value = "250" +unit = "V" + +[parameters.V_RFC] +description = "RF confinement voltage" +stage = "Trapping" +process = "Electric Potential" +equation = "" +value = "250" +unit = "V" + +[parameters.Omega_RF] +description = "RF signal frequency" +stage = "Trapping" +process = "Electric Potential" +equation = "" +value = "36" +unit = "MHz" + +[parameters."V_DC^X"] +description = "DC electrode voltage (x X electrodes)" +stage = "Trapping" +process = "Electric Potential" +equation = "" +value = "94" +unit = "channels" + +[parameters.micromotion_x] +description = "Micromotion in x" +stage = "Trapping" +process = "Electric Potential" +equation = "" +value = "" +unit = "" + +[parameters.micromotion_y] +description = "Micromotion in y" +stage = "Trapping" +process = "Electric Potential" +equation = "" +value = "" +unit = "" + +[parameters.micromotion_z] +description = "Micromotion in z" +stage = "Trapping" +process = "Electric Potential" +equation = "" +value = "" +unit = "" + +[parameters.theta] +description = "Principal axes rotation, theta" +stage = "Trapping" +process = "Electric Potential" +equation = "" +value = "" +unit = "" + +[parameters.phi] +description = "Principal axes rotation, phi" +stage = "Trapping" +process = "Electric Potential" +equation = "" +value = "" +unit = "" + +[parameters.position_N] +description = "Ion equilibrium position(s)" +stage = "Trapping" +process = "Electric Potential" +equation = "" +value = "" +unit = "" + +[parameters."mode_x^N"] +description = "Radial motional mode(s), x" +stage = "Trapping" +process = "Electric Potential" +equation = "" +value = "" +unit = "" + +[parameters."mode_y^N"] +description = "Radial motional mode(s), y" +stage = "Trapping" +process = "Electric Potential" +equation = "" +value = "" +unit = "" + +[parameters."mode_z^N"] +description = "Axial motional mode(s)" +stage = "Trapping" +process = "Electric Potential" +equation = "" +value = "" +unit = "" + +[parameters.TODO] +description = "Engineered voltage fluctuations" +stage = "Trapping" +process = "Electric Potential" +equation = "" +value = "" +unit = "" + +[parameters.n] +description = "Phonon cooling target" +stage = "Trapping" +process = "Cooling" +equation = "" +value = "Doppler limit" +unit = "" + +[parameters.n_doppler] +description = "Doppler limit" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "Doppler limit" +unit = "" + +[parameters.w_doppler] +description = "Doppler cooling laser frequency" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "493 nm - 7.506 GHz, 493 nm + 4.259 GHz" +unit = "" + +[parameters.sigma_doppler] +description = "Doppler cooling laser polarization(s)" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "" +unit = "" + +[parameters.I_doppler_saturation] +description = "Doppler cooling laser saturation intensity" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "" +unit = "" + +[parameters.I_doppler] +description = "Doppler cooling laser intensity" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "<< I_Doppler_saturation" +unit = "" + +[parameters.w_doppler_sideband_1] +description = "Doppler cooling laser sideband 1 frequency modulation" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "" +unit = "" + +[parameters.P_doppler_1] +description = "Doppler cooling laser sideband 1 power" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "" +unit = "" + +[parameters.w_doppler_sideband_2] +description = "Doppler cooling laser sideband 2 frequency modulation" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "" +unit = "" + +[parameters.P_doppler_2] +description = "Doppler cooling laser sideband 2 power" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "" +unit = "" + +[parameters.w_doppler_repump] +description = "Doppler cooling repump laser frequency" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "650 nm - 1.840 GHz, 650 nm - 0.903 GHz" +unit = "" + +[parameters.sigma_doppler_repump] +description = "Doppler cooling repump laser polarization(s)" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "" +unit = "" + +[parameters.I_Doppler_saturation_repump] +description = "Doppler cooling repump laser saturation intensity" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "" +unit = "" + +[parameters.I_Doppler_repump] +description = "Doppler cooling repump laser intensity" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "" +unit = "" + +[parameters.P_Dopper_sideband] +description = "Doppler cooling repump sideband power" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "" +unit = "" + +[parameters.t_Doppler] +description = "Doppler cooling time duration" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "" +unit = "" + +[parameters."Delta_Doppler^?"] +description = "Detuning(s)?" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "" +unit = "" + +[parameters.TODO_45] +description = "Narrow linewidth cooling laser frequency" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "" +unit = "" + +[parameters.TODO_46] +description = "Narrow linewidth cooling laser polarization" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "" +unit = "" + +[parameters.TODO_47] +description = "Narrow linewidth cooling laser power" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "" +unit = "" + +[parameters.TODO_48] +description = "Narrow linewidth cooling laser saturation intensity" +stage = "Trapping" +process = "Cooling (Doppler)" +equation = "" +value = "" +unit = "" + +[parameters.n_rsc] +description = "RSB cooling limit" +stage = "Trapping" +process = "Cooling (Resolved sideband)" +equation = "" +value = "" +unit = "" + +[parameters.omega_RSBC] +description = "RSB cooling laser frequency" +stage = "Trapping" +process = "Cooling (Resolved sideband)" +equation = "" +value = "" +unit = "" + +[parameters.sigma_RSBC] +description = "RSB cooling laser polarization(s)" +stage = "Trapping" +process = "Cooling (Resolved sideband)" +equation = "" +value = "" +unit = "" + +[parameters.I_RSBC_saturation] +description = "RSB cooling laser saturation intensity" +stage = "Trapping" +process = "Cooling (Resolved sideband)" +equation = "" +value = "" +unit = "" + +[parameters.I_RSBC] +description = "RSB cooling laser intensity" +stage = "Trapping" +process = "Cooling (Resolved sideband)" +equation = "" +value = "" +unit = "" + +[parameters.TODO_54] +description = "TODO" +stage = "Trapping" +process = "Cooling (EIT)" +equation = "" +value = "" +unit = "" + +[parameters.omega_preparation] +description = "Optical pumping laser frequency" +stage = "Initialization" +process = "State Preparation" +equation = "" +value = "493" +unit = "nm" + +[parameters.sigma_opt_pump] +description = "Optical pumping laser polarization(s)" +stage = "Initialization" +process = "State Preparation" +equation = "" +value = "" +unit = "" + +[parameters.I_opt_pump_saturation] +description = "Optical pumping laser saturation intensity" +stage = "Initialization" +process = "State Preparation" +equation = "" +value = "" +unit = "" + +[parameters.I_opt_pump] +description = "Optical pumping laser intensity" +stage = "Initialization" +process = "State Preparation" +equation = "" +value = "" +unit = "" + +[parameters.TODO_59] +description = "Optical pumping laser sideband frequency" +stage = "Initialization" +process = "State Preparation" +equation = "" +value = "" +unit = "" + +[parameters.TODO_60] +description = "Optical pumping laser sideband power" +stage = "Initialization" +process = "State Preparation" +equation = "" +value = "" +unit = "" + +[parameters.omega_RSB_repump] +description = "Optical pumping repump laser frequency" +stage = "Initialization" +process = "State Preparation" +equation = "" +value = "650" +unit = "nm" + +[parameters.I_RSB_repump_saturation] +description = "Optical pumping repump laser saturation intensity" +stage = "Initialization" +process = "State Preparation" +equation = "" +value = "" +unit = "" + +[parameters.I_RSB_repump] +description = "Optical pumping repump laser intensity" +stage = "Initialization" +process = "State Preparation" +equation = "" +value = "" +unit = "" + +[parameters.TODO_64] +description = "Optical pumping repump laser sideband frequency" +stage = "Initialization" +process = "State Preparation" +equation = "" +value = "" +unit = "" + +[parameters.TODO_65] +description = "Optical pumping repumplaser sideband power" +stage = "Initialization" +process = "State Preparation" +equation = "" +value = "" +unit = "" + +[parameters.TODO_66] +description = "Detuning(s)?" +stage = "Initialization" +process = "State Preparation" +equation = "" +value = "" +unit = "" + +[parameters.t_RSBC] +description = "Optical pumping time duration" +stage = "Initialization" +process = "State Preparation" +equation = "" +value = "" +unit = "" + +[parameters.omega_q] +description = "Qubit Carrier Frequency" +stage = "Experiment" +process = "X Gate" +equation = "2*pi*(12.6 GHz)" +value = "" +unit = "" + +[parameters.omega_e] +description = "Excited State Frequency" +stage = "Experiment" +process = "X Gate" +equation = "" +value = "" +unit = "" + +[parameters.omega_A] +description = "Raman Frequency A" +stage = "Experiment" +process = "X Gate" +equation = "" +value = "" +unit = "" + +[parameters.omega_B] +description = "Raman Frequency B" +stage = "Experiment" +process = "X Gate" +equation = "omega_e + delta - omega_q" +value = "" +unit = "" + +[parameters.Delta] +description = "Raman Detuning" +stage = "Experiment" +process = "X Gate" +equation = "omega_e - omega_a" +value = "" +unit = "" + +[parameters.Omega_A] +description = "" +stage = "Experiment" +process = "X Gate" +equation = "Omega_A = Omega_B" +value = "" +unit = "" + +[parameters.Omega_B] +description = "" +stage = "Experiment" +process = "X Gate" +equation = "Omega_A = Omega_B" +value = "" +unit = "" + +[parameters.Omega] +description = "Rabi Frequency" +stage = "Experiment" +process = "X Gate" +equation = "(omega_a*Omega_B)/(2*Delta)" +value = "" +unit = "" + +[parameters.gate_phi] +description = "Gate Phase" +stage = "Experiment" +process = "X Gate" +equation = "TODO" +value = "0" +unit = "rad" + +[parameters.phi_a] +description = "Laser A phase" +stage = "Experiment" +process = "X Gate" +equation = "phi_a = phi_b =0" +value = "" +unit = "" + +[parameters.phi_b] +description = "Laser B phase" +stage = "Experiment" +process = "X Gate" +equation = "phi_a = phi_b =0" +value = "" +unit = "" + +[parameters.t_gate] +description = "Gate Duration" +stage = "Experiment" +process = "X Gate" +equation = "" +value = "" +unit = "" + +[parameters.H] +description = "Hamiltonian" +stage = "Experiment" +process = "X Gate" +equation = "" +value = "" +unit = "" + +[parameters.omega_measurement] +description = "Measurement laser frequency" +stage = "Detection" +process = "Measurement" +equation = "" +value = "493" +unit = "nm" + +[parameters.I_measurement] +description = "Measurement laser saturation" +stage = "Detection" +process = "Measurement" +equation = "I_saturation" +value = "" +unit = "" + +[parameters.t_measurement] +description = "Measurement duration" +stage = "Detection" +process = "Measurement" +equation = "" +value = "" +unit = "" + +[parameters.omega_repump] +description = "Measurement repump frequency" +stage = "Detection" +process = "Measurement" +equation = "" +value = "650" +unit = "nm" + +[parameters.I_repump] +description = "Measurement repump Intensity" +stage = "Detection" +process = "Measurement" +equation = "" +value = "Doppler limit" +unit = "" diff --git a/frontend/test/test_oqd/oqd/calibration_data/gate.toml b/frontend/test/test_oqd/oqd/calibration_data/gate.toml new file mode 100644 index 0000000000..e431f6f668 --- /dev/null +++ b/frontend/test/test_oqd/oqd/calibration_data/gate.toml @@ -0,0 +1,118 @@ +# OQD Gate-Decomposition Parameters +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# A database containing parameters relating to decompositions of gates to pulse operations used in +# an Open Quantum Design (OQD) trapped-ion quantum computer device. + +oqd_config_schema = "v0.1" + + +# Beams +# A beam contains 4 properties that will be calibrated: +# rabi = float, +# detuning = float, +# polarization = int array, +# wavevector = int array, +# +# A pulse operation will consist of a beam, a transition, a phase, a time (duration of application), and a target qubit +# A list of beams is recorded in this toml. +# +# `beams1` are the beams used for single-qubit gates. +# The i-th beams1 is usable on the i-th qubit. +# +# `beams2` are the beams used for 2-qubit gates. +# In a system with n qubits, there are (n choose 2) = n*(n-1)/2 possible two-qubit +# combinations. Each combination is represented by a unique index. For example, in a 4-qubit system, where the qubits are indexed as [0, 1, 2, 3], +# the possible two-qubit combinations and their indices are: +# 0: (0, 1) +# 1: (0, 2) +# 2: (0, 3) +# 3: (1, 2) +# 4: (1, 3) +# 5: (2, 3) +# The i-th beams2 is usable on a gate whose input qubit indices +# correspond to i in the above encoding combination. +# ------- + +[[beams1]] +rabi = 31.4159 +detuning = 157.0796 +polarization = [1,0,0] +wavevector = [0,1,0] + +[[beams1]] +rabi = 31.4159 +detuning = 157.0796 +polarization = [1,0,0] +wavevector = [0,1,0] + +[[beams1]] +rabi = 31.4159 +detuning = 157.0796 +polarization = [1,0,0] +wavevector = [0,1,0] + + +[[beams2]] +rabi = 31.4159 +detuning = 157.0796 +polarization = [0,1,0] +wavevector = [-1, 0, 0] + +[[beams2]] +rabi = 31.4159 +detuning = 7.89 +polarization = [0,1,0] +wavevector = [1, 0, 0] + +[[beams2]] +rabi = 31.4159 +detuning = 157.0796 +polarization = [0,1,0] +wavevector = [1, 0, 0] + + +# Phonon modes +# Each qubit has a phonon triplet (x,y,z) +# A list of phonon triplets, one triplet per qubit, is stored here +# On a single direction, a phonon has a calibrated energy. +# phonons1 are the phonons used for single-qubit gates. +# phonons2 are the phonons used for 2-qubit gates. +# ------- + +[[phonons1]] +energy = 0.1 +eigenvector = [1.0, 0.0, 0.0] + +[[phonons1]] +energy = 0.1 +eigenvector = [1.0, 0.0, 0.0] + +[[phonons1]] +energy = 0.1 +eigenvector = [1.0, 0.0, 0.0] + + +[[phonons2]] +energy = 0.1 +eigenvector = [0.1, 0.0, 0.0, 0.1, 0.0, 0.0] + +[[phonons2]] +energy = 0.1 +eigenvector = [0.0, 0.1, 0.0, 0.0, 0.1, 0.0] + +[[phonons2]] +energy = 0.1 +eigenvector = [0.0, 0.0, 0.1, 0.0, 0.0, 0.1] + +[[phonons2]] +energy = 0.1 +eigenvector = [0.1, 0.0, 0.0, 0.1, 0.0, 0.0] + +[[phonons2]] +energy = 0.1 +eigenvector = [0.0, 0.1, 0.0, 0.0, 0.1, 0.0] + +[[phonons2]] +energy = 0.1 +eigenvector = [0.0, 0.0, 0.1, 0.0, 0.0, 0.1] diff --git a/frontend/test/test_oqd/oqd/calibration_data/qubit.toml b/frontend/test/test_oqd/oqd/calibration_data/qubit.toml new file mode 100644 index 0000000000..0c61142575 --- /dev/null +++ b/frontend/test/test_oqd/oqd/calibration_data/qubit.toml @@ -0,0 +1,91 @@ +# OQD Qubit Parameters +# ~~~~~~~~~~~~~~~~~~~~ +# +# A database containing parameters relating to the qubit(s) used in an Open Quantum Design (OQD) +# trapped-ion quantum computer device. + +oqd_config_schema = "v0.1" + +# ----- TODO: Is this needed? -------------------------------------------------- +# [ion.Yb_171_II] +# level.q0_m0 = "0" +# level.q1_m0 = "12.643e9" +# level.q1_m1 = "12.643e9 + 1.4e6" +# level.q1_mneg1 = "12.643e9 - 1.4e6" +# level.e0_m0 = "811.302e12" +# level.e1_m0 = "811.302e12 + 2.105e9" +# level.e1_m1 = "811.302e12 + 2.105e9 + 0.47e6" +# level.e1_mneg1 = "811.302e12 + 2.105e9 - 0.47e6" +# ------------------------------------------------------------------------------ + +# Ions +# ---- + +[ions.Yb171] +mass = 171.0 +charge = +1.0 +position = [0.0, 0.0, 0.0] + +levels.downstate.label = "downstate" +levels.downstate.principal = 6 +levels.downstate.spin = 0.5 +levels.downstate.orbital = 0.0 +levels.downstate.nuclear = 0.5 +levels.downstate.spin_orbital = 0.5 +levels.downstate.spin_orbital_nuclear = 0.0 +levels.downstate.spin_orbital_nuclear_magnetization = 0.0 +levels.downstate.energy = 0.0 + +levels.upstate.label = "upstate" +levels.upstate.principal = 6 +levels.upstate.spin = 0.5 +levels.upstate.orbital = 0.0 +levels.upstate.nuclear = 0.5 +levels.upstate.spin_orbital = 0.5 +levels.upstate.spin_orbital_nuclear = 1.0 +levels.upstate.spin_orbital_nuclear_magnetization = 0.0 +levels.upstate.energy = 62.83185 + +levels.estate.label = "estate" +levels.estate.principal = 5 +levels.estate.spin = 0.5 +levels.estate.orbital = 1.0 +levels.estate.nuclear = 0.5 +levels.estate.spin_orbital = 0.5 +levels.estate.spin_orbital_nuclear = 1.0 +levels.estate.spin_orbital_nuclear_magnetization = -1.0 +levels.estate.energy = 628.3185 + +levels.estate2.label = "estate2" +levels.estate2.principal = 5 +levels.estate2.spin = 0.5 +levels.estate2.orbital = 1.0 +levels.estate2.nuclear = 0.5 +levels.estate2.spin_orbital = 0.5 +levels.estate2.spin_orbital_nuclear = 1.0 +levels.estate2.spin_orbital_nuclear_magnetization = 1.0 +levels.estate2.energy = 1256.637 + +[ions.Yb171.transitions.downstate_estate] +level1 = "downstate" +level2 = "estate" +einstein_a = 1.0 +multipole = "E1" + +[ions.Yb171.transitions.downstate_estate2] +level1 = "downstate" +level2 = "estate2" +einstein_a = 1.0 +multipole = "E1" + +[ions.Yb171.transitions.upstate_estate] +level1 = "upstate" +level2 = "estate" +einstein_a = 1.0 +multipole = "E1" + +[ions.Yb171.transitions.upstate_estate2] +level1 = "upstate" +level2 = "estate2" +einstein_a = 1.0 +multipole = "E1" diff --git a/frontend/test/test_oqd/oqd/test_openapl_generation.py b/frontend/test/test_oqd/oqd/test_openapl_generation.py new file mode 100644 index 0000000000..25f0857fa5 --- /dev/null +++ b/frontend/test/test_oqd/oqd/test_openapl_generation.py @@ -0,0 +1,119 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Test the OpenAPL generation.""" + +import json +import os + +import pennylane as qml +import pytest + +from catalyst import qjit +from catalyst.third_party.oqd import OQDDevice + + +class TestOpenAPL: + """Test that the OQD device correctly generates an OpenAPL program.""" + + toml_path = os.path.join(os.path.dirname(__file__), "calibration_data/") + oqd_pipelines = [ + ( + "device-agnostic-pipeline", + [ + "enforce-runtime-invariants-pipeline", + "hlo-lowering-pipeline", + "quantum-compilation-pipeline", + "bufferization-pipeline", + ], + ), + ( + "oqd_pipeline", + [ + "func.func(ions-decomposition)", + "func.func(quantum-to-ion{" + + "device-toml-loc=" + + toml_path + + "device.toml " + + "qubit-toml-loc=" + + toml_path + + "qubit.toml " + + "gate-to-pulse-toml-loc=" + + toml_path + + "gate.toml" + + "})", + "convert-ion-to-llvm", + ], + ), + ( + "llvm-dialect-lowering-pipeline", + [ + "llvm-dialect-lowering-pipeline", + ], + ), + ] + + output_f = "__openapl__output.json" + + def test_RX_gate(self): + """Test OpenAPL generation for a circuit with a single RX Gate.""" + oqd_dev = OQDDevice(backend="default", shots=4, wires=1) + + @qjit(pipelines=self.oqd_pipelines) + @qml.qnode(oqd_dev) + def circuit(x): + qml.RX(x, wires=0) + return qml.counts() + + circuit(1.5708) + + expected_f = "frontend/test/test_oqd/oqd/test_single_RX.json" + with open(self.output_f, "r", encoding="utf-8") as f: + catalyst_json = json.load(f) + + with open( + expected_f, + "r", + encoding="utf-8", + ) as f: + expected_json = json.load(f) + + assert sorted(catalyst_json.items()) == sorted(expected_json.items()) + os.remove(self.output_f) + + def test_CNOT_gate(self): + """Test OpenAPL generation for a circuit with a single CNOT circuit.""" + oqd_dev = OQDDevice(backend="default", shots=4, wires=2) + + @qjit(pipelines=self.oqd_pipelines) + @qml.qnode(oqd_dev) + def circuit(): + qml.CNOT(wires=[0, 1]) + return qml.counts() + + circuit() + + with open(self.output_f, "r", encoding="utf-8") as f: + catalyst_json = json.load(f) + + expected_f = "frontend/test/test_oqd/oqd/test_single_CNOT.json" + with open(expected_f, "r", encoding="utf-8") as f: + expected_json = json.load(f) + + assert sorted(catalyst_json.items()) == sorted(expected_json.items()) + os.remove("__openapl__output.json") + + +if __name__ == "__main__": + pytest.main(["-x", __file__]) diff --git a/frontend/test/test_oqd/oqd/test_single_CNOT.json b/frontend/test/test_oqd/oqd/test_single_CNOT.json new file mode 100644 index 0000000000..b9adbd0a0b --- /dev/null +++ b/frontend/test/test_oqd/oqd/test_single_CNOT.json @@ -0,0 +1,1256 @@ +{ + "class_": "AtomicCircuit", + "protocol": { + "class_": "SequentialProtocol", + "sequence": [ + { + "class_": "ParallelProtocol", + "sequence": [ + { + "beam": { + "class_": "Beam", + "detuning": { + "class_": "MathNum", + "value": 157.0796 + }, + "phase": { + "class_": "MathNum", + "value": 0.0 + }, + "polarization": [ + 1, + 0, + 0 + ], + "rabi": { + "class_": "MathNum", + "value": 31.4159 + }, + "target": 0, + "transition": { + "class_": "Transition", + "einsteinA": 1.0, + "label": "downstate->estate", + "level1": { + "class_": "Level", + "energy": 0.0, + "label": "downstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 0.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + "wavevector": [ + 0, + 1, + 0 + ] + }, + "class_": "Pulse", + "duration": 0.050000042233228924 + }, + { + "beam": { + "class_": "Beam", + "detuning": { + "class_": "MathNum", + "value": 157.0796 + }, + "phase": { + "class_": "MathNum", + "value": 3.141592653589793 + }, + "polarization": [ + 1, + 0, + 0 + ], + "rabi": { + "class_": "MathNum", + "value": 31.4159 + }, + "target": 0, + "transition": { + "class_": "Transition", + "einsteinA": 1.0, + "label": "upstate->estate", + "level1": { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + "wavevector": [ + 0, + 1, + 0 + ] + }, + "class_": "Pulse", + "duration": 0.050000042233228924 + } + ] + }, + { + "class_": "ParallelProtocol", + "sequence": [ + { + "beam": { + "class_": "Beam", + "detuning": { + "class_": "MathNum", + "value": 157.0796 + }, + "phase": { + "class_": "MathNum", + "value": 0.0 + }, + "polarization": [ + 0, + 1, + 0 + ], + "rabi": { + "class_": "MathNum", + "value": 31.4159 + }, + "target": 0, + "transition": { + "class_": "Transition", + "einsteinA": 1.0, + "label": "downstate->estate", + "level1": { + "class_": "Level", + "energy": 0.0, + "label": "downstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 0.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + "wavevector": [ + -1, + 0, + 0 + ] + }, + "class_": "Pulse", + "duration": 0.050000042233228924 + }, + { + "beam": { + "class_": "Beam", + "detuning": { + "class_": "MathNum", + "value": 157.1796 + }, + "phase": { + "class_": "MathNum", + "value": 0.0 + }, + "polarization": [ + 0, + 1, + 0 + ], + "rabi": { + "class_": "MathNum", + "value": 31.4159 + }, + "target": 0, + "transition": { + "class_": "Transition", + "einsteinA": 1.0, + "label": "upstate->estate", + "level1": { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + "wavevector": [ + 1, + 0, + 0 + ] + }, + "class_": "Pulse", + "duration": 0.050000042233228924 + }, + { + "beam": { + "class_": "Beam", + "detuning": { + "class_": "MathNum", + "value": 156.9796 + }, + "phase": { + "class_": "MathNum", + "value": 0.0 + }, + "polarization": [ + 0, + 1, + 0 + ], + "rabi": { + "class_": "MathNum", + "value": 31.4159 + }, + "target": 0, + "transition": { + "class_": "Transition", + "einsteinA": 1.0, + "label": "upstate->estate", + "level1": { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + "wavevector": [ + 1, + 0, + 0 + ] + }, + "class_": "Pulse", + "duration": 0.050000042233228924 + }, + { + "beam": { + "class_": "Beam", + "detuning": { + "class_": "MathNum", + "value": 157.0796 + }, + "phase": { + "class_": "MathNum", + "value": 0.0 + }, + "polarization": [ + 0, + 1, + 0 + ], + "rabi": { + "class_": "MathNum", + "value": 31.4159 + }, + "target": 1, + "transition": { + "class_": "Transition", + "einsteinA": 1.0, + "label": "downstate->estate", + "level1": { + "class_": "Level", + "energy": 0.0, + "label": "downstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 0.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + "wavevector": [ + -1, + 0, + 0 + ] + }, + "class_": "Pulse", + "duration": 0.050000042233228924 + }, + { + "beam": { + "class_": "Beam", + "detuning": { + "class_": "MathNum", + "value": 157.1796 + }, + "phase": { + "class_": "MathNum", + "value": 0.0 + }, + "polarization": [ + 0, + 1, + 0 + ], + "rabi": { + "class_": "MathNum", + "value": 31.4159 + }, + "target": 1, + "transition": { + "class_": "Transition", + "einsteinA": 1.0, + "label": "upstate->estate", + "level1": { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + "wavevector": [ + 1, + 0, + 0 + ] + }, + "class_": "Pulse", + "duration": 0.050000042233228924 + }, + { + "beam": { + "class_": "Beam", + "detuning": { + "class_": "MathNum", + "value": 156.9796 + }, + "phase": { + "class_": "MathNum", + "value": 0.0 + }, + "polarization": [ + 0, + 1, + 0 + ], + "rabi": { + "class_": "MathNum", + "value": 31.4159 + }, + "target": 1, + "transition": { + "class_": "Transition", + "einsteinA": 1.0, + "label": "upstate->estate", + "level1": { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + "wavevector": [ + 1, + 0, + 0 + ] + }, + "class_": "Pulse", + "duration": 0.050000042233228924 + } + ] + }, + { + "class_": "ParallelProtocol", + "sequence": [ + { + "beam": { + "class_": "Beam", + "detuning": { + "class_": "MathNum", + "value": 157.0796 + }, + "phase": { + "class_": "MathNum", + "value": 0.0 + }, + "polarization": [ + 1, + 0, + 0 + ], + "rabi": { + "class_": "MathNum", + "value": 31.4159 + }, + "target": 0, + "transition": { + "class_": "Transition", + "einsteinA": 1.0, + "label": "downstate->estate", + "level1": { + "class_": "Level", + "energy": 0.0, + "label": "downstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 0.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + "wavevector": [ + 0, + 1, + 0 + ] + }, + "class_": "Pulse", + "duration": 0.3500002956326025 + }, + { + "beam": { + "class_": "Beam", + "detuning": { + "class_": "MathNum", + "value": 157.0796 + }, + "phase": { + "class_": "MathNum", + "value": 0.0 + }, + "polarization": [ + 1, + 0, + 0 + ], + "rabi": { + "class_": "MathNum", + "value": 31.4159 + }, + "target": 0, + "transition": { + "class_": "Transition", + "einsteinA": 1.0, + "label": "upstate->estate", + "level1": { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + "wavevector": [ + 0, + 1, + 0 + ] + }, + "class_": "Pulse", + "duration": 0.3500002956326025 + } + ] + }, + { + "class_": "ParallelProtocol", + "sequence": [ + { + "beam": { + "class_": "Beam", + "detuning": { + "class_": "MathNum", + "value": 157.0796 + }, + "phase": { + "class_": "MathNum", + "value": 0.0 + }, + "polarization": [ + 1, + 0, + 0 + ], + "rabi": { + "class_": "MathNum", + "value": 31.4159 + }, + "target": 1, + "transition": { + "class_": "Transition", + "einsteinA": 1.0, + "label": "downstate->estate", + "level1": { + "class_": "Level", + "energy": 0.0, + "label": "downstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 0.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + "wavevector": [ + 0, + 1, + 0 + ] + }, + "class_": "Pulse", + "duration": 0.3500002956326025 + }, + { + "beam": { + "class_": "Beam", + "detuning": { + "class_": "MathNum", + "value": 157.0796 + }, + "phase": { + "class_": "MathNum", + "value": 3.141592653589793 + }, + "polarization": [ + 1, + 0, + 0 + ], + "rabi": { + "class_": "MathNum", + "value": 31.4159 + }, + "target": 1, + "transition": { + "class_": "Transition", + "einsteinA": 1.0, + "label": "upstate->estate", + "level1": { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + "wavevector": [ + 0, + 1, + 0 + ] + }, + "class_": "Pulse", + "duration": 0.3500002956326025 + } + ] + }, + { + "class_": "ParallelProtocol", + "sequence": [ + { + "beam": { + "class_": "Beam", + "detuning": { + "class_": "MathNum", + "value": 157.0796 + }, + "phase": { + "class_": "MathNum", + "value": 0.0 + }, + "polarization": [ + 1, + 0, + 0 + ], + "rabi": { + "class_": "MathNum", + "value": 31.4159 + }, + "target": 0, + "transition": { + "class_": "Transition", + "einsteinA": 1.0, + "label": "downstate->estate", + "level1": { + "class_": "Level", + "energy": 0.0, + "label": "downstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 0.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + "wavevector": [ + 0, + 1, + 0 + ] + }, + "class_": "Pulse", + "duration": 0.3500002956326025 + }, + { + "beam": { + "class_": "Beam", + "detuning": { + "class_": "MathNum", + "value": 157.0796 + }, + "phase": { + "class_": "MathNum", + "value": 3.141592653589793 + }, + "polarization": [ + 1, + 0, + 0 + ], + "rabi": { + "class_": "MathNum", + "value": 31.4159 + }, + "target": 0, + "transition": { + "class_": "Transition", + "einsteinA": 1.0, + "label": "upstate->estate", + "level1": { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + "wavevector": [ + 0, + 1, + 0 + ] + }, + "class_": "Pulse", + "duration": 0.3500002956326025 + } + ] + } + ] + }, + "system": { + "class_": "System", + "ions": [ + { + "charge": 1.0, + "class_": "Ion", + "levels": [ + { + "class_": "Level", + "energy": 0.0, + "label": "downstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 0.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + { + "class_": "Level", + "energy": 1256.637, + "label": "estate2", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 1.0 + } + ], + "mass": 171.0, + "position": [ + 0.0, + 0.0, + 0.0 + ], + "transitions": [ + { + "class_": "Transition", + "einsteinA": 1.0, + "label": "downstate->estate", + "level1": { + "class_": "Level", + "energy": 0.0, + "label": "downstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 0.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + { + "class_": "Transition", + "einsteinA": 1.0, + "label": "downstate->estate2", + "level1": { + "class_": "Level", + "energy": 0.0, + "label": "downstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 0.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 1256.637, + "label": "estate2", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 1.0 + }, + "multipole": "E1" + }, + { + "class_": "Transition", + "einsteinA": 1.0, + "label": "upstate->estate", + "level1": { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + { + "class_": "Transition", + "einsteinA": 1.0, + "label": "upstate->estate2", + "level1": { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 1256.637, + "label": "estate2", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 1.0 + }, + "multipole": "E1" + } + ] + }, + { + "charge": 1.0, + "class_": "Ion", + "levels": [ + { + "class_": "Level", + "energy": 0.0, + "label": "downstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 0.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + { + "class_": "Level", + "energy": 1256.637, + "label": "estate2", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 1.0 + } + ], + "mass": 171.0, + "position": [ + 0.0, + 0.0, + 0.0 + ], + "transitions": [ + { + "class_": "Transition", + "einsteinA": 1.0, + "label": "downstate->estate", + "level1": { + "class_": "Level", + "energy": 0.0, + "label": "downstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 0.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + { + "class_": "Transition", + "einsteinA": 1.0, + "label": "downstate->estate2", + "level1": { + "class_": "Level", + "energy": 0.0, + "label": "downstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 0.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 1256.637, + "label": "estate2", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 1.0 + }, + "multipole": "E1" + }, + { + "class_": "Transition", + "einsteinA": 1.0, + "label": "upstate->estate", + "level1": { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + { + "class_": "Transition", + "einsteinA": 1.0, + "label": "upstate->estate2", + "level1": { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 1256.637, + "label": "estate2", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 1.0 + }, + "multipole": "E1" + } + ] + } + ], + "modes": [ + { + "class_": "Phonon", + "eigenvector": [ + 0.1, + 0.0, + 0.0, + 0.1, + 0.0, + 0.0 + ], + "energy": 0.1 + } + ] + } +} diff --git a/frontend/test/test_oqd/oqd/test_single_RX.json b/frontend/test/test_oqd/oqd/test_single_RX.json new file mode 100644 index 0000000000..1236603b39 --- /dev/null +++ b/frontend/test/test_oqd/oqd/test_single_RX.json @@ -0,0 +1,331 @@ +{ + "class_": "AtomicCircuit", + "protocol": { + "class_": "SequentialProtocol", + "sequence": [ + { + "class_": "ParallelProtocol", + "sequence": [ + { + "beam": { + "class_": "Beam", + "detuning": { + "class_": "MathNum", + "value": 157.0796 + }, + "phase": { + "class_": "MathNum", + "value": 0.0 + }, + "polarization": [ + 1, + 0, + 0 + ], + "rabi": { + "class_": "MathNum", + "value": 31.4159 + }, + "target": 0, + "transition": { + "class_": "Transition", + "einsteinA": 1.0, + "label": "downstate->estate", + "level1": { + "class_": "Level", + "energy": 0.0, + "label": "downstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 0.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + "wavevector": [ + 0, + 1, + 0 + ] + }, + "class_": "Pulse", + "duration": 0.05000015915507752 + }, + { + "beam": { + "class_": "Beam", + "detuning": { + "class_": "MathNum", + "value": 157.0796 + }, + "phase": { + "class_": "MathNum", + "value": 0.0 + }, + "polarization": [ + 1, + 0, + 0 + ], + "rabi": { + "class_": "MathNum", + "value": 31.4159 + }, + "target": 0, + "transition": { + "class_": "Transition", + "einsteinA": 1.0, + "label": "upstate->estate", + "level1": { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + "wavevector": [ + 0, + 1, + 0 + ] + }, + "class_": "Pulse", + "duration": 0.05000015915507752 + } + ] + } + ] + }, + "system": { + "class_": "System", + "ions": [ + { + "charge": 1.0, + "class_": "Ion", + "levels": [ + { + "class_": "Level", + "energy": 0.0, + "label": "downstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 0.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + { + "class_": "Level", + "energy": 1256.637, + "label": "estate2", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 1.0 + } + ], + "mass": 171.0, + "position": [ + 0.0, + 0.0, + 0.0 + ], + "transitions": [ + { + "class_": "Transition", + "einsteinA": 1.0, + "label": "downstate->estate", + "level1": { + "class_": "Level", + "energy": 0.0, + "label": "downstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 0.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + { + "class_": "Transition", + "einsteinA": 1.0, + "label": "downstate->estate2", + "level1": { + "class_": "Level", + "energy": 0.0, + "label": "downstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 0.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 1256.637, + "label": "estate2", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 1.0 + }, + "multipole": "E1" + }, + { + "class_": "Transition", + "einsteinA": 1.0, + "label": "upstate->estate", + "level1": { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 628.3185, + "label": "estate", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": -1.0 + }, + "multipole": "E1" + }, + { + "class_": "Transition", + "einsteinA": 1.0, + "label": "upstate->estate2", + "level1": { + "class_": "Level", + "energy": 62.83185, + "label": "upstate", + "nuclear": 0.5, + "orbital": 0.0, + "principal": 6, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 0.0 + }, + "level2": { + "class_": "Level", + "energy": 1256.637, + "label": "estate2", + "nuclear": 0.5, + "orbital": 1.0, + "principal": 5, + "spin": 0.5, + "spin_orbital": 0.5, + "spin_orbital_nuclear": 1.0, + "spin_orbital_nuclear_magnetization": 1.0 + }, + "multipole": "E1" + } + ] + } + ], + "modes": [ + { + "class_": "Phonon", + "eigenvector": [ + 1.0, + 0.0, + 0.0 + ], + "energy": 0.1 + } + ] + } +} diff --git a/mlir/include/Ion/IR/IonOps.td b/mlir/include/Ion/IR/IonOps.td index 41b4464eca..c686a43146 100644 --- a/mlir/include/Ion/IR/IonOps.td +++ b/mlir/include/Ion/IR/IonOps.td @@ -86,14 +86,16 @@ def TransitionAttr : Ion_Attr<"Transition", "transition"> { let parameters = (ins "mlir::StringAttr":$level_0, "mlir::StringAttr":$level_1, - "mlir::FloatAttr":$einstein_a + "mlir::FloatAttr":$einstein_a, + "mlir::StringAttr":$multipole ); let builders = [ AttrBuilderWithInferredContext<(ins "mlir::StringAttr":$level_0, "mlir::StringAttr":$level_1, - "mlir::FloatAttr":$einstein_a), [{ - return $_get(einstein_a.getContext(), level_0, level_1, einstein_a); + "mlir::FloatAttr":$einstein_a, + "mlir::StringAttr":$multipole), [{ + return $_get(einstein_a.getContext(), level_0, level_1, einstein_a, multipole); }]> ]; @@ -115,6 +117,22 @@ def BeamAttr : Ion_Attr<"Beam", "beam"> { let assemblyFormat = "`<` struct(params) `>`"; } +def PhononAttr : Ion_Attr<"Phonon", "phonon"> { + let summary = "A class to represent a Phonon mode."; + + let parameters = (ins + "mlir::FloatAttr":$energy, + "mlir::DenseF64ArrayAttr": $eigenvector + ); + + let assemblyFormat = "`<` struct(params) `>`"; +} + +def PhononArrayAttr : TypedArrayAttrBase { + let constBuilderCall = ?; +} + def LevelArrayAttr : TypedArrayAttrBase { let constBuilderCall = ?; @@ -151,6 +169,18 @@ def IonOp : Ion_Op<"ion"> { }]; } +def ModesOp : Ion_Op<"mode"> { + let summary = "A class to represent an Phonon modes of the system."; + + let arguments = (ins + PhononArrayAttr: $modes + ); + + let assemblyFormat = [{ + attr-dict + }]; +} + def PulseOp : Ion_Op<"pulse"> { let summary = "Represent a pulse (a laser beam and some time)."; diff --git a/mlir/include/Ion/Transforms/Passes.td b/mlir/include/Ion/Transforms/Passes.td index 1de880ff4e..71acb336da 100644 --- a/mlir/include/Ion/Transforms/Passes.td +++ b/mlir/include/Ion/Transforms/Passes.td @@ -37,7 +37,8 @@ def QuantumToIonPass : Pass<"quantum-to-ion"> { let dependentDialects = [ "quantum::QuantumDialect", - "ion::IonDialect" + "ion::IonDialect", + "scf::SCFDialect" ]; let constructor = "catalyst::createQuantumToIonPass()"; @@ -49,7 +50,7 @@ def IonConversionPass : Pass<"convert-ion-to-llvm"> { let dependentDialects = [ "mlir::func::FuncDialect", "mlir::LLVM::LLVMDialect", - "catalyst::quantum::QuantumDialect" + "catalyst::quantum::QuantumDialect", ]; let constructor = "catalyst::createIonConversionPass()"; diff --git a/mlir/include/Ion/Transforms/oqd_database_managers.hpp b/mlir/include/Ion/Transforms/oqd_database_managers.hpp index 4acab42234..20c89459b7 100644 --- a/mlir/include/Ion/Transforms/oqd_database_managers.hpp +++ b/mlir/include/Ion/Transforms/oqd_database_managers.hpp @@ -29,7 +29,7 @@ namespace ion { class OQDDatabaseManager { public: OQDDatabaseManager(const std::string &DeviceTomlLoc, const std::string &QubitTomlLoc, - const std::string &Gate2PulseDecompTomlLoc) + const std::string &Gate2PulseDecompTomlLoc, size_t n_qubits) { sourceTomlDevice = toml::parse_file(DeviceTomlLoc); sourceTomlQubit = toml::parse_file(QubitTomlLoc); @@ -42,7 +42,7 @@ class OQDDatabaseManager { loadBeams1Params(); loadBeams2Params(); - loadPhononParams(); + loadPhononParams(n_qubits); loadIonParams(); } @@ -50,7 +50,7 @@ class OQDDatabaseManager { const std::vector &getBeams1Params() const { return beams1; } const std::vector &getBeams2Params() const { return beams2; } - const std::vector &getPhononParams() const { return phonons; } + const std::vector &getPhononParams() const { return phonons; } const std::map &getIonParams() const { return ions; } @@ -62,7 +62,7 @@ class OQDDatabaseManager { std::vector beams1; std::vector beams2; - std::vector phonons; + std::vector phonons; std::map ions; @@ -106,26 +106,25 @@ class OQDDatabaseManager { } } - void loadPhononParams() + void loadPhononParams(size_t n_qubits) { - toml::node_view phononsToml = sourceTomlGateDecomposition["phonons"]; - size_t numPhononModes = phononsToml.as_array()->size(); - - auto parseSingleDirection = [](auto direction) { + // TODO: The fact that loading phonons depend on the number of qubits is a bit of a hack. + // This is not ideal since we want to support dynamic number of qubits in the future. + // We should find a better way to handle this in the database. + std::string phonon_str = "phonons" + std::to_string(n_qubits); + toml::node_view phononsToml = sourceTomlGateDecomposition[phonon_str]; + size_t numPhonons = phononsToml.as_array()->size(); + + auto parseSinglePhonon = [](auto direction) { double energy = direction["energy"].as_floating_point()->get(); - std::vector eigenvector = - tomlArray2StdVector(*(direction["eigenvector"].as_array())); + std::vector eigenvector = + tomlArray2StdVector(*(direction["eigenvector"].as_array())); return Phonon(energy, eigenvector); }; - for (size_t i = 0; i < numPhononModes; i++) { - auto phononMode = phononsToml[i]; - - Phonon COM_x = parseSingleDirection(phononMode["COM_x"]); - Phonon COM_y = parseSingleDirection(phononMode["COM_y"]); - Phonon COM_z = parseSingleDirection(phononMode["COM_z"]); - - phonons.push_back(PhononMode(COM_x, COM_y, COM_z)); + for (size_t i = 0; i < numPhonons; i++) { + Phonon phonon = parseSinglePhonon(phononsToml[i]); + phonons.push_back(phonon); } } @@ -160,13 +159,15 @@ class OQDDatabaseManager { double einstein_a = transition_entry["einstein_a"].as_floating_point()->get(); std::string level1 = transition_entry["level1"].as_string()->get(); std::string level2 = transition_entry["level2"].as_string()->get(); + std::string multipole = transition_entry["multipole"].as_string()->get(); - std::set levelEncodings{"downstate", "upstate", "estate"}; + std::set levelEncodings{"downstate", "upstate", "estate", "estate2"}; assert((levelEncodings.count(level1) & levelEncodings.count(level2)) && - "Only \"downstate\", \"upstate\" and \"estate\" are allowed in the atom's " + "Only \"downstate\", \"upstate\", \"estate\" and \"estate2\" are allowed in the " + "atom's " "transition levels."); - return Transition(level1, level2, einstein_a); + return Transition(level1, level2, multipole, einstein_a); }; for (auto &ion_it : *(ionsToml.as_table())) { @@ -182,7 +183,8 @@ class OQDDatabaseManager { Level downstate = parseSingleLevel(data->at_path("levels")["downstate"]); Level upstate = parseSingleLevel(data->at_path("levels")["upstate"]); Level estate = parseSingleLevel(data->at_path("levels")["estate"]); - std::vector levels{downstate, upstate, estate}; + Level estate2 = parseSingleLevel(data->at_path("levels")["estate2"]); + std::vector levels{downstate, upstate, estate, estate2}; std::vector transitions; auto *transitionsTable = data->at_path("transitions").as_table(); diff --git a/mlir/include/Ion/Transforms/oqd_database_types.hpp b/mlir/include/Ion/Transforms/oqd_database_types.hpp index 3418c121a5..f590ee3182 100644 --- a/mlir/include/Ion/Transforms/oqd_database_types.hpp +++ b/mlir/include/Ion/Transforms/oqd_database_types.hpp @@ -39,23 +39,14 @@ struct Beam { struct Phonon { // This struct contains the calibrated phonon parameters on one axis. double energy; - std::vector eigenvector; + std::vector eigenvector; - Phonon(double _energy, std::vector _eigenvector) + Phonon(double _energy, std::vector _eigenvector) : energy(_energy), eigenvector(_eigenvector) { } }; -struct PhononMode { - // This struct contains the calibrated phonon parameters for one ion. - Phonon COM_x; - Phonon COM_y; - Phonon COM_z; - - PhononMode(Phonon x, Phonon y, Phonon z) : COM_x(x), COM_y(y), COM_z(z) {} -}; - // // Innate atomic parameters // @@ -81,11 +72,12 @@ struct Level { struct Transition { // This class represents a transition between two atomic levels. // It contains the innate properties of the qubit. - std::string level_0, level_1; + std::string level_0, level_1, multipole; double einstein_a; - Transition(std::string _level_0, std::string _level_1, double _einstein_a) - : level_0(_level_0), level_1(_level_1), einstein_a(_einstein_a) + Transition(std::string _level_0, std::string _level_1, std::string _multipole, + double _einstein_a) + : level_0(_level_0), level_1(_level_1), multipole(_multipole), einstein_a(_einstein_a) { } }; diff --git a/mlir/lib/Ion/Transforms/ConversionPatterns.cpp b/mlir/lib/Ion/Transforms/ConversionPatterns.cpp index 2e61a460d5..68949da008 100644 --- a/mlir/lib/Ion/Transforms/ConversionPatterns.cpp +++ b/mlir/lib/Ion/Transforms/ConversionPatterns.cpp @@ -154,6 +154,7 @@ struct IonOpPattern : public OpConversionPattern { for (size_t i = 0; i < transitionsAttr.size(); i++) { auto transitionAttr = cast(transitionsAttr[i]); + std::string multipole = transitionAttr.getMultipole().getValue().str(); std::string level0_label = transitionAttr.getLevel_0().getValue().str(); std::string level1_label = transitionAttr.getLevel_1().getValue().str(); @@ -169,7 +170,8 @@ struct IonOpPattern : public OpConversionPattern { {"einsteinA", transitionAttr.getEinsteinA().getValue().convertToDouble()}, {"level1", level1}, {"level2", level2}, - {"label", level0_label + "->" + level1_label}}; + {"label", level0_label + "->" + level1_label}, + {"multipole", multipole}}; ion_json["transitions"].push_back(this_transition); } deviceInitOp.setKwargs(deviceKwargs.str() + "ION:" + std::string(ion_json.dump())); @@ -181,6 +183,40 @@ struct IonOpPattern : public OpConversionPattern { } }; +struct ModesOpPattern : public OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + // Create the modes JSON and pass it into the device kwargs as a JSON string + LogicalResult matchAndRewrite(catalyst::ion::ModesOp op, catalyst::ion::ModesOpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override + { + func::FuncOp funcOp = op->getParentOfType(); + + DeviceInitOp deviceInitOp = *funcOp.getOps().begin(); + + auto modesAttr = op.getModes(); + for (size_t i = 0; i < modesAttr.size(); i++) { + StringRef deviceKwargs = deviceInitOp.getKwargs(); + auto phononAttr = cast(modesAttr[i]); + + json phonon_json = R"({ + "class_": "Phonon", + "eigenvector" : [] + })"_json; + phonon_json["energy"] = phononAttr.getEnergy().getValue().convertToDouble(); + auto eigenvector = phononAttr.getEigenvector(); + for (int j = 0; j < eigenvector.size(); j++) { + phonon_json["eigenvector"].push_back(eigenvector[j]); + } + deviceInitOp.setKwargs(deviceKwargs.str() + + "PHONON:" + std::string(phonon_json.dump())); + } + + rewriter.eraseOp(op); + return success(); + } +}; + struct ParallelProtocolOpPattern : public OpConversionPattern { using OpConversionPattern::OpConversionPattern; @@ -305,6 +341,7 @@ namespace ion { void populateConversionPatterns(LLVMTypeConverter &typeConverter, RewritePatternSet &patterns) { patterns.add(typeConverter, patterns.getContext()); + patterns.add(typeConverter, patterns.getContext()); patterns.add(typeConverter, patterns.getContext()); patterns.add(typeConverter, patterns.getContext()); } diff --git a/mlir/lib/Ion/Transforms/QuantumToIonPatterns.cpp b/mlir/lib/Ion/Transforms/QuantumToIonPatterns.cpp index 39151d956b..618035f908 100644 --- a/mlir/lib/Ion/Transforms/QuantumToIonPatterns.cpp +++ b/mlir/lib/Ion/Transforms/QuantumToIonPatterns.cpp @@ -18,6 +18,7 @@ #include "mlir/Dialect/Arith/IR/Arith.h" #include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/Dialect/SCF/IR/SCF.h" #include "mlir/IR/Value.h" #include "mlir/Transforms/DialectConversion.h" @@ -37,7 +38,9 @@ enum LevelTransition { // Encoding of level transitions for a pulse // For example, "DOWN_E" means the transition from downstate to estate DOWN_E = 0, - UP_E = 1, + DOWN_E2 = 1, + UP_E = 2, + UP_E2 = 3, }; /** @@ -130,6 +133,43 @@ int64_t getTwoQubitCombinationIndex(int64_t nQubits, int64_t idx1, int64_t idx2) return (idx1 * nQubits) - (idx1 * (idx1 + 1) / 2) + (idx2 - idx1 - 1); } +mlir::Value CreateNormalizedAngle(mlir::PatternRewriter &rewriter, mlir::Location loc, + mlir::Value angle) +{ + constexpr double PI = llvm::numbers::pi; + constexpr double FOUR_PI = 4.0 * PI; + + auto four_pi_attr = rewriter.getF64FloatAttr(FOUR_PI); + auto four_pi_const = rewriter.create(loc, angle.getType(), four_pi_attr); + + // Find angle fmod 4pi. + mlir::Value remainder = + rewriter.create(loc, angle.getType(), angle, four_pi_const); + + // Find if the remainder is less than 0. + auto zero_attr = rewriter.getZeroAttr(angle.getType()); + auto zero_const = rewriter.create(loc, angle.getType(), zero_attr); + auto less_than_zero = rewriter.create(loc, arith::CmpFPredicate::OLT, remainder, + zero_const); // Signed less than + + // Create a conditional add (if remainder < 0, add 4*PI) + auto normalized_angle = + rewriter + .create( + loc, less_than_zero, + [&](OpBuilder &builder, Location loc) { // then + mlir::Value add_op = rewriter.create( + loc, angle.getType(), remainder, four_pi_const); // or AddIOp for integers + builder.create(loc, add_op); + }, + [&](OpBuilder &builder, Location loc) { // else + builder.create(loc, remainder); + }) + .getResult(0); + + return normalized_angle; +} + /** * @brief Computes the pulse duration given the rotation angle and the Rabi frequency. * @@ -149,9 +189,10 @@ int64_t getTwoQubitCombinationIndex(int64_t nQubits, int64_t idx1, int64_t idx2) mlir::Value computePulseDuration(mlir::PatternRewriter &rewriter, mlir::Location &loc, const mlir::Value &angle, double rabi) { + auto normalizedAngle = CreateNormalizedAngle(rewriter, loc, angle); TypedAttr rabiAttr = rewriter.getF64FloatAttr(rabi); mlir::Value rabiValue = rewriter.create(loc, rabiAttr).getResult(); - return rewriter.create(loc, angle, rabiValue).getResult(); + return rewriter.create(loc, normalizedAngle, rabiValue).getResult(); } mlir::LogicalResult oneQubitGateToPulse(CustomOp op, mlir::PatternRewriter &rewriter, double phase1, @@ -205,7 +246,7 @@ mlir::LogicalResult oneQubitGateToPulse(CustomOp op, mlir::PatternRewriter &rewr mlir::LogicalResult MSGateToPulse(CustomOp op, mlir::PatternRewriter &rewriter, const std::vector &beams2, - const std::vector &phonons) + const std::vector &phonons) { auto qnode = op->getParentOfType(); MLIRContext *ctx = op.getContext(); @@ -223,7 +264,7 @@ mlir::LogicalResult MSGateToPulse(CustomOp op, mlir::PatternRewriter &rewriter, auto qubitIndex1Value = qubitIndex1.value(); auto nQubits = allocOp.getNqubitsAttr(); if (nQubits.has_value()) { - if (static_cast(qubitIndex0Value) >= phonons.size()) { + if (static_cast(qubitIndex0Value) * 3 >= phonons.size()) { op.emitError() << "Missing phonon parameters for qubit " << qubitIndex0Value << " used as input to MS gate; there are only " << phonons.size() << " phonon parameters in the database." @@ -232,7 +273,7 @@ mlir::LogicalResult MSGateToPulse(CustomOp op, mlir::PatternRewriter &rewriter, return failure(); } - if (static_cast(qubitIndex1Value) >= phonons.size()) { + if (static_cast(qubitIndex1Value) * 3 >= phonons.size()) { op.emitError() << "Missing phonon parameters for qubit " << qubitIndex1Value << " used as input to MS gate; there are only " << phonons.size() << " phonon parameters in the database." @@ -241,9 +282,8 @@ mlir::LogicalResult MSGateToPulse(CustomOp op, mlir::PatternRewriter &rewriter, return failure(); } - // Assume that each ion has 3 phonons (x, y, z) - const Phonon &phonon0ComX = phonons[qubitIndex0Value].COM_x; - const Phonon &phonon1ComX = phonons[qubitIndex1Value].COM_x; + const Phonon &phonon0ComX = phonons[qubitIndex0Value * 3]; + const Phonon &phonon1ComX = phonons[qubitIndex1Value * 3]; auto twoQubitComboIndex = getTwoQubitCombinationIndex(nQubits.value(), qubitIndex0Value, qubitIndex1Value); @@ -438,7 +478,7 @@ struct QuantumToIonRewritePattern : public mlir::OpConversionPattern { std::vector beams1; std::vector beams2; - std::vector phonons; + std::vector phonons; QuantumToIonRewritePattern(mlir::MLIRContext *ctx, const OQDDatabaseManager &dataManager) : mlir::OpConversionPattern::OpConversionPattern(ctx) diff --git a/mlir/lib/Ion/Transforms/ion-to-llvm.cpp b/mlir/lib/Ion/Transforms/ion-to-llvm.cpp index a5daca7b30..b8a79b2c12 100644 --- a/mlir/lib/Ion/Transforms/ion-to-llvm.cpp +++ b/mlir/lib/Ion/Transforms/ion-to-llvm.cpp @@ -18,6 +18,7 @@ #include "mlir/Dialect/Arith/IR/Arith.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Dialect/SCF/IR/SCF.h" #include "mlir/Pass/Pass.h" #include "mlir/Transforms/DialectConversion.h" @@ -64,6 +65,7 @@ struct IonConversionPass : impl::IonConversionPassBase { target.addLegalDialect(); target.addLegalDialect(); target.addLegalDialect(); + target.addLegalDialect(); if (failed(applyPartialConversion(getOperation(), target, std::move(patterns)))) { signalPassFailure(); diff --git a/mlir/lib/Ion/Transforms/quantum_to_ion.cpp b/mlir/lib/Ion/Transforms/quantum_to_ion.cpp index 64aec2e159..82b600bacc 100644 --- a/mlir/lib/Ion/Transforms/quantum_to_ion.cpp +++ b/mlir/lib/Ion/Transforms/quantum_to_ion.cpp @@ -57,7 +57,14 @@ struct QuantumToIonPass : impl::QuantumToIonPassBase { { return TransitionAttr::get(ctx, builder.getStringAttr(transition.level_0), builder.getStringAttr(transition.level_1), - builder.getF64FloatAttr(transition.einstein_a)); + builder.getF64FloatAttr(transition.einstein_a), + builder.getStringAttr(transition.multipole)); + } + + PhononAttr getPhononAttr(MLIRContext *ctx, IRRewriter &builder, Phonon phonon) + { + return PhononAttr::get(ctx, builder.getF64FloatAttr(phonon.energy), + builder.getDenseF64ArrayAttr(phonon.eigenvector)); } bool canScheduleOn(RegisteredOperationName opInfo) const override @@ -78,7 +85,11 @@ struct QuantumToIonPass : impl::QuantumToIonPassBase { target.addLegalDialect(); target.markUnknownOpDynamicallyLegal([](Operation *) { return true; }); - OQDDatabaseManager dataManager(DeviceTomlLoc, QubitTomlLoc, Gate2PulseDecompTomlLoc); + auto allocOp = *op.getOps().begin(); + auto nQubits = allocOp.getNqubitsAttr().value(); + + OQDDatabaseManager dataManager(DeviceTomlLoc, QubitTomlLoc, Gate2PulseDecompTomlLoc, + nQubits); if (LoadIon) { // FIXME(?): we only load Yb171 ion since the hardware ion species is unlikely to change @@ -99,6 +110,14 @@ struct QuantumToIonPass : impl::QuantumToIonPassBase { op->getLoc(), IonType::get(ctx), builder.getStringAttr(ion.name), builder.getF64FloatAttr(ion.mass), builder.getF64FloatAttr(ion.charge), ion.position, builder.getArrayAttr(levels), builder.getArrayAttr(transitions)); + + SmallVector phonons; + for (const Phonon &phonon : dataManager.getPhononParams()) { + phonons.push_back(cast(getPhononAttr(ctx, builder, phonon))); + } + // TODO: For now, we only print one phonon to be consistent with TriCal examples, + // but we should print all of them eventually + builder.create(op->getLoc(), builder.getArrayAttr(phonons[0])); } RewritePatternSet ionPatterns(&getContext()); diff --git a/mlir/test/Ion/Dialect.mlir b/mlir/test/Ion/Dialect.mlir index f033b50ea7..32dff0d19b 100644 --- a/mlir/test/Ion/Dialect.mlir +++ b/mlir/test/Ion/Dialect.mlir @@ -183,12 +183,14 @@ func.func @example_ion() -> !ion.ion { #ion.transition< level_0 = "downstate", level_1 = "upstate", - einstein_a=10.10 + einstein_a=10.10, + multipole="M1" >, #ion.transition< level_0 = "upstate", level_1 = "downstate", - einstein_a=10.10 + einstein_a=10.10, + multipole="E1" > ] }: !ion.ion diff --git a/mlir/test/Ion/IonOpLowering.mlir b/mlir/test/Ion/IonOpLowering.mlir index 5840eba0f8..a07b432e7a 100644 --- a/mlir/test/Ion/IonOpLowering.mlir +++ b/mlir/test/Ion/IonOpLowering.mlir @@ -234,17 +234,20 @@ func.func public @ion_op(%arg0: tensor, %arg1: tensor) attributes {dif #ion.transition< level_0 = "l0", level_1 = "l2", - einstein_a = 2.200000e+00 : f64 + einstein_a = 2.200000e+00 : f64, + multipole = "M1" >, #ion.transition< level_0 = "l1", level_1 = "l2", - einstein_a = 1.100000e+00 : f64 + einstein_a = 1.100000e+00 : f64, + multipole = "E1" >, #ion.transition< level_0 = "l0", level_1 = "l3", - einstein_a = 3.300000e+00 : f64 + einstein_a = 3.300000e+00 : f64, + multipole = "E2" > ] } : !ion.ion diff --git a/mlir/test/Ion/QuantumToIon.mlir b/mlir/test/Ion/QuantumToIon.mlir index 845906963e..32c1ef12b3 100644 --- a/mlir/test/Ion/QuantumToIon.mlir +++ b/mlir/test/Ion/QuantumToIon.mlir @@ -32,35 +32,46 @@ func.func @example_ion_two_qubit(%arg0: f64) -> !quantum.bit attributes {qnode} // CHECK-SAME: #ion.level< // CHECK-SAME: label = "downstate", // CHECK-SAME: principal = 6 - // CHECK-SAME: spin = 4.000000e-01 - // CHECK-SAME: orbital = 5.000000e-01 - // CHECK-SAME: nuclear = 6.000000e-01 - // CHECK-SAME: spin_orbital = 8.000000e-01 - // CHECK-SAME: spin_orbital_nuclear = 9.000000e-01 - // CHECK-SAME: spin_orbital_nuclear_magnetization = 1.000000e+00 + // CHECK-SAME: spin = 5.000000e-01 + // CHECK-SAME: orbital = 1.000000e+00 + // CHECK-SAME: nuclear = 1.500000e+00 + // CHECK-SAME: spin_orbital = 2.000000e+00 + // CHECK-SAME: spin_orbital_nuclear = 2.500000e+00 + // CHECK-SAME: spin_orbital_nuclear_magnetization = -3.000000e+00 // CHECK-SAME: energy = 0.000000e+00 // CHECK-SAME: >, // CHECK-SAME: #ion.level< // CHECK-SAME: label = "upstate", // CHECK-SAME: principal = 6 - // CHECK-SAME: spin = 1.400000e+00 - // CHECK-SAME: orbital = 1.500000e+00 - // CHECK-SAME: nuclear = 1.600000e+00 - // CHECK-SAME: spin_orbital = 1.800000e+00 - // CHECK-SAME: spin_orbital_nuclear = 1.900000e+00 - // CHECK-SAME: spin_orbital_nuclear_magnetization = 2.000000e+00 + // CHECK-SAME: spin = 1.500000e+00 + // CHECK-SAME: orbital = 2.000000e+00 + // CHECK-SAME: nuclear = 2.500000e+00 + // CHECK-SAME: spin_orbital = 3.000000e+00 + // CHECK-SAME: spin_orbital_nuclear = 3.500000e+00 + // CHECK-SAME: spin_orbital_nuclear_magnetization = -4.000000e+00 // CHECK-SAME: energy = 1.264300e+10 // CHECK-SAME: >, // CHECK-SAME: #ion.level< // CHECK-SAME: label = "estate", // CHECK-SAME: principal = 5 - // CHECK-SAME: spin = 2.400000e+00 - // CHECK-SAME: orbital = 2.500000e+00 - // CHECK-SAME: nuclear = 2.600000e+00 - // CHECK-SAME: spin_orbital = 2.800000e+00 - // CHECK-SAME: spin_orbital_nuclear = 2.900000e+00 - // CHECK-SAME: spin_orbital_nuclear_magnetization = 3.000000e+00 + // CHECK-SAME: spin = 2.500000e+00 + // CHECK-SAME: orbital = 3.000000e+00 + // CHECK-SAME: nuclear = 3.500000e+00 + // CHECK-SAME: spin_orbital = 4.000000e+00 + // CHECK-SAME: spin_orbital_nuclear = 4.500000e+00 + // CHECK-SAME: spin_orbital_nuclear_magnetization = -5.000000e+00 // CHECK-SAME: energy = 8.115200e+14 + // CHECK-SAME: >, + // CHECK-SAME: #ion.level< + // CHECK-SAME: label = "estate2", + // CHECK-SAME: principal = 5 + // CHECK-SAME: spin = 5.000000e-01 + // CHECK-SAME: orbital = 1.000000e+00 + // CHECK-SAME: nuclear = 5.000000e-01 + // CHECK-SAME: spin_orbital = 5.000000e-01 + // CHECK-SAME: spin_orbital_nuclear = 1.000000e+00 + // CHECK-SAME: spin_orbital_nuclear_magnetization = 1.000000e+00 + // CHECK-SAME: energy = 1256.6369999999999 // CHECK-SAME: > // CHECK-SAME: ], // CHECK-SAME: mass = 1.710000e+02 @@ -70,21 +81,33 @@ func.func @example_ion_two_qubit(%arg0: f64) -> !quantum.bit attributes {qnode} // CHECK-SAME: #ion.transition< // CHECK-SAME: level_0 = "downstate", // CHECK-SAME: level_1 = "estate", - // CHECK-SAME: einstein_a = 2.200000e+00 + // CHECK-SAME: einstein_a = 2.200000e+00 : f64, + // CHECK-SAME: multipole = "E2" // CHECK-SAME: >, // CHECK-SAME: #ion.transition< // CHECK-SAME: level_0 = "downstate", // CHECK-SAME: level_1 = "upstate", - // CHECK-SAME: einstein_a = 1.100000e+00 + // CHECK-SAME: einstein_a = 1.100000e+00 : f64, + // CHECK-SAME: multipole = "M1" // CHECK-SAME: >, // CHECK-SAME: #ion.transition< // CHECK-SAME: level_0 = "estate", // CHECK-SAME: level_1 = "upstate", - // CHECK-SAME: einstein_a = 3.300000e+00 + // CHECK-SAME: einstein_a = 3.300000e+00 : f64, + // CHECK-SAME: multipole = "E1" // CHECK-SAME: > // CHECK-SAME: ] // CHECK-SAME: } : !ion.ion + // CHECK: ion.mode { + // CHECK-SAME: modes = [ + // CHECK-SAME: #ion.phonon< + // CHECK-SAME: energy = 1.100000e+00 : f64, + // CHECK-SAME: eigenvector = [1.000000e+00, 0.000000e+00, 0.000000e+00, 1.000000e+00, 0.000000e+00, 0.000000e+00] + // CHECK-SAME: > + // CHECK-SAME: ] + // CHECK-SAME: } + %1 = quantum.alloc( 2) : !quantum.reg // CHECK: [[qubit0:%.+]] = quantum.extract %1[ 0] : !quantum.reg -> !quantum.bit @@ -94,23 +117,33 @@ func.func @example_ion_two_qubit(%arg0: f64) -> !quantum.bit attributes {qnode} // CHECK: [[rx1out:%.+]] = ion.parallelprotocol([[qubit0]]) : !quantum.bit { // CHECK-NEXT: ^{{.*}}(%arg1: !quantum.bit): + // CHECK-NEXT:%cst = arith.constant 12.566370614359172 : f64 + // CHECK-NEXT: [[remainder:%.+]] = arith.remf %arg0, %cst : f64 + // CHECK-NEXT: %cst_0 = arith.constant 0.000000e+00 : f64 + // CHECK-NEXT: [[negative:%.+]] = arith.cmpf olt, [[remainder:%.+]], %cst_0 : f64 + // CHECK-NEXT: [[normalized_angle:%.+]] = scf.if [[negative:%.+]] -> (f64) { + // CHECK-NEXT: [[adjusted:%.+]] = arith.addf [[remainder:%.+]], %cst : f64 + // CHECK-NEXT: scf.yield [[adjusted:%.+]] : f64 + // CHECK-NEXT: } else { + // CHECK-NEXT: scf.yield [[remainder:%.+]] : f64 + // CHECK-NEXT: } // CHECK-NEXT: [[rabi1:%.+]] = arith.constant 1.100000e+00 : f64 - // CHECK-NEXT: [[timerx1:%.+]] = arith.divf %arg0, [[rabi1]] : f64 + // CHECK-NEXT: [[timerx1:%.+]] = arith.divf [[normalized_angle:%.+]], [[rabi1]] : f64 // CHECK-NEXT: ion.pulse([[timerx1]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< // CHECK-SAME: transition_index = 0 : i64, // CHECK-SAME: rabi = 1.100000e+00 : f64, // CHECK-SAME: detuning = 2.200000e+00 : f64, - // CHECK-SAME: polarization = [0, 1], - // CHECK-SAME: wavevector = [-2, 3]>, + // CHECK-SAME: polarization = [0, 1, 2], + // CHECK-SAME: wavevector = [-2, 3, 4]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timerx1]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 1.100000e+00 : f64, // CHECK-SAME: detuning = 2.200000e+00 : f64, - // CHECK-SAME: polarization = [0, 1], - // CHECK-SAME: wavevector = [-2, 3]>, + // CHECK-SAME: polarization = [0, 1, 2], + // CHECK-SAME: wavevector = [-2, 3, 4]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.yield %arg1 : !quantum.bit // CHECK-NEXT: } @@ -118,23 +151,33 @@ func.func @example_ion_two_qubit(%arg0: f64) -> !quantum.bit attributes {qnode} // CHECK: [[ry1out:%.+]] = ion.parallelprotocol([[rx1out]]) : !quantum.bit { // CHECK-NEXT: ^{{.*}}(%arg1: !quantum.bit): + // CHECK-NEXT:%cst = arith.constant 12.566370614359172 : f64 + // CHECK-NEXT: [[remainder:%.+]] = arith.remf %arg0, %cst : f64 + // CHECK-NEXT: %cst_0 = arith.constant 0.000000e+00 : f64 + // CHECK-NEXT: [[negative:%.+]] = arith.cmpf olt, [[remainder:%.+]], %cst_0 : f64 + // CHECK-NEXT: [[normalized_angle:%.+]] = scf.if [[negative:%.+]] -> (f64) { + // CHECK-NEXT: [[adjusted:%.+]] = arith.addf [[remainder:%.+]], %cst : f64 + // CHECK-NEXT: scf.yield [[adjusted:%.+]] : f64 + // CHECK-NEXT: } else { + // CHECK-NEXT: scf.yield [[remainder:%.+]] : f64 + // CHECK-NEXT: } // CHECK-NEXT: [[rabi1:%.+]] = arith.constant 1.100000e+00 : f64 - // CHECK-NEXT: [[timery1:%.+]] = arith.divf %arg0, [[rabi1]] : f64 + // CHECK-NEXT: [[timery1:%.+]] = arith.divf [[normalized_angle:%.+]], [[rabi1]] : f64 // CHECK-NEXT: ion.pulse([[timery1]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< // CHECK-SAME: transition_index = 0 : i64, // CHECK-SAME: rabi = 1.100000e+00 : f64, // CHECK-SAME: detuning = 2.200000e+00 : f64, - // CHECK-SAME: polarization = [0, 1], - // CHECK-SAME: wavevector = [-2, 3]>, + // CHECK-SAME: polarization = [0, 1, 2], + // CHECK-SAME: wavevector = [-2, 3, 4]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timery1]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 1.100000e+00 : f64, // CHECK-SAME: detuning = 2.200000e+00 : f64, - // CHECK-SAME: polarization = [0, 1], - // CHECK-SAME: wavevector = [-2, 3]>, + // CHECK-SAME: polarization = [0, 1, 2], + // CHECK-SAME: wavevector = [-2, 3, 4]>, // CHECK-SAME: phase = 3.1415926535{{[0-9]*}} : f64} // CHECK-NEXT: ion.yield %arg1 : !quantum.bit // CHECK-NEXT: } @@ -142,23 +185,33 @@ func.func @example_ion_two_qubit(%arg0: f64) -> !quantum.bit attributes {qnode} // CHECK: [[rx2out:%.+]] = ion.parallelprotocol([[ry1out]]) : !quantum.bit { // CHECK-NEXT: ^{{.*}}(%arg1: !quantum.bit): + // CHECK-NEXT:%cst = arith.constant 12.566370614359172 : f64 + // CHECK-NEXT: [[remainder:%.+]] = arith.remf %arg0, %cst : f64 + // CHECK-NEXT: %cst_0 = arith.constant 0.000000e+00 : f64 + // CHECK-NEXT: [[negative:%.+]] = arith.cmpf olt, [[remainder:%.+]], %cst_0 : f64 + // CHECK-NEXT: [[normalized_angle:%.+]] = scf.if [[negative:%.+]] -> (f64) { + // CHECK-NEXT: [[adjusted:%.+]] = arith.addf [[remainder:%.+]], %cst : f64 + // CHECK-NEXT: scf.yield [[adjusted:%.+]] : f64 + // CHECK-NEXT: } else { + // CHECK-NEXT: scf.yield [[remainder:%.+]] : f64 + // CHECK-NEXT: } // CHECK-NEXT: [[rabi1:%.+]] = arith.constant 1.100000e+00 : f64 - // CHECK-NEXT: [[timerx2:%.+]] = arith.divf %arg0, [[rabi1]] : f64 + // CHECK-NEXT: [[timerx2:%.+]] = arith.divf [[normalized_angle:%.+]], [[rabi1]] : f64 // CHECK-NEXT: ion.pulse([[timerx2]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< // CHECK-SAME: transition_index = 0 : i64, // CHECK-SAME: rabi = 1.100000e+00 : f64, // CHECK-SAME: detuning = 2.200000e+00 : f64, - // CHECK-SAME: polarization = [0, 1], - // CHECK-SAME: wavevector = [-2, 3]>, + // CHECK-SAME: polarization = [0, 1, 2], + // CHECK-SAME: wavevector = [-2, 3, 4]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timerx2]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 1.100000e+00 : f64, // CHECK-SAME: detuning = 2.200000e+00 : f64, - // CHECK-SAME: polarization = [0, 1], - // CHECK-SAME: wavevector = [-2, 3]>, + // CHECK-SAME: polarization = [0, 1, 2], + // CHECK-SAME: wavevector = [-2, 3, 4]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.yield %arg1 : !quantum.bit // CHECK-NEXT: } @@ -166,55 +219,65 @@ func.func @example_ion_two_qubit(%arg0: f64) -> !quantum.bit attributes {qnode} // CHECK: [[msout:%.+]] = ion.parallelprotocol([[rx2out]], [[qubit1]]) : !quantum.bit, !quantum.bit { // CHECK-NEXT: ^{{.*}}(%arg1: !quantum.bit, %arg2: !quantum.bit): + // CHECK-NEXT:%cst = arith.constant 12.566370614359172 : f64 + // CHECK-NEXT: [[remainder:%.+]] = arith.remf %arg0, %cst : f64 + // CHECK-NEXT: %cst_0 = arith.constant 0.000000e+00 : f64 + // CHECK-NEXT: [[negative:%.+]] = arith.cmpf olt, [[remainder:%.+]], %cst_0 : f64 + // CHECK-NEXT: [[normalized_angle:%.+]] = scf.if [[negative:%.+]] -> (f64) { + // CHECK-NEXT: [[adjusted:%.+]] = arith.addf [[remainder:%.+]], %cst : f64 + // CHECK-NEXT: scf.yield [[adjusted:%.+]] : f64 + // CHECK-NEXT: } else { + // CHECK-NEXT: scf.yield [[remainder:%.+]] : f64 + // CHECK-NEXT: } // CHECK-NEXT: [[rabi2:%.+]] = arith.constant 1.230000e+00 : f64 - // CHECK-NEXT: [[timems:%.+]] = arith.divf %arg0, [[rabi2]] : f64 + // CHECK-NEXT: [[timems:%.+]] = arith.divf [[normalized_angle:%.+]], [[rabi2]] : f64 // CHECK-NEXT: ion.pulse([[timems]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< // CHECK-SAME: transition_index = 0 : i64, // CHECK-SAME: rabi = 1.230000e+00 : f64, // CHECK-SAME: detuning = 4.560000e+00 : f64, - // CHECK-SAME: polarization = [7, 8], - // CHECK-SAME: wavevector = [9, 10]>, + // CHECK-SAME: polarization = [7, 8, 9], + // CHECK-SAME: wavevector = [9, 10, 11]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timems]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 1.230000e+00 : f64, // CHECK-SAME: detuning = 5.660000e+00 : f64, - // CHECK-SAME: polarization = [7, 8], - // CHECK-SAME: wavevector = [-9, -10]>, + // CHECK-SAME: polarization = [7, 8, 9], + // CHECK-SAME: wavevector = [-9, -10, -11]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timems]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 1.230000e+00 : f64, // CHECK-SAME: detuning = 3.4{{.*}} : f64, - // CHECK-SAME: polarization = [7, 8], - // CHECK-SAME: wavevector = [-9, -10]>, + // CHECK-SAME: polarization = [7, 8, 9], + // CHECK-SAME: wavevector = [-9, -10, -11]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timems]] : f64) %arg2 { // CHECK-SAME: beam = #ion.beam< // CHECK-SAME: transition_index = 0 : i64, // CHECK-SAME: rabi = 1.230000e+00 : f64, // CHECK-SAME: detuning = 4.560000e+00 : f64, - // CHECK-SAME: polarization = [7, 8], - // CHECK-SAME: wavevector = [9, 10]>, + // CHECK-SAME: polarization = [7, 8, 9], + // CHECK-SAME: wavevector = [9, 10, 11]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timems]] : f64) %arg2 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 1.230000e+00 : f64, // CHECK-SAME: detuning = 8.960000e+00 : f64, - // CHECK-SAME: polarization = [7, 8], - // CHECK-SAME: wavevector = [-9, -10]>, + // CHECK-SAME: polarization = [7, 8, 9], + // CHECK-SAME: wavevector = [-9, -10, -11]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timems]] : f64) %arg2 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 1.230000e+00 : f64, // CHECK-SAME: detuning = 0.1{{.*}} : f64, - // CHECK-SAME: polarization = [7, 8], - // CHECK-SAME: wavevector = [-9, -10]>, + // CHECK-SAME: polarization = [7, 8, 9], + // CHECK-SAME: wavevector = [-9, -10, -11]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.yield %arg1, %arg2 : !quantum.bit, !quantum.bit // CHECK-NEXT: } @@ -243,55 +306,65 @@ func.func @example_ion_three_qubit(%arg0: f64) -> (!quantum.bit, !quantum.bit, ! // CHECK: [[ms1out:%.+]]:2 = ion.parallelprotocol([[qubit0]], [[qubit1]]) : !quantum.bit, !quantum.bit { // CHECK-NEXT: ^{{.*}}(%arg1: !quantum.bit, %arg2: !quantum.bit): + // CHECK-NEXT:%cst = arith.constant 12.566370614359172 : f64 + // CHECK-NEXT: [[remainder:%.+]] = arith.remf %arg0, %cst : f64 + // CHECK-NEXT: %cst_0 = arith.constant 0.000000e+00 : f64 + // CHECK-NEXT: [[negative:%.+]] = arith.cmpf olt, [[remainder:%.+]], %cst_0 : f64 + // CHECK-NEXT: [[normalized_angle:%.+]] = scf.if [[negative:%.+]] -> (f64) { + // CHECK-NEXT: [[adjusted:%.+]] = arith.addf [[remainder:%.+]], %cst : f64 + // CHECK-NEXT: scf.yield [[adjusted:%.+]] : f64 + // CHECK-NEXT: } else { + // CHECK-NEXT: scf.yield [[remainder:%.+]] : f64 + // CHECK-NEXT: } // CHECK-NEXT: [[rabi1:%.+]] = arith.constant 1.230000e+00 : f64 - // CHECK-NEXT: [[timems1:%.+]] = arith.divf %arg0, [[rabi1]] : f64 + // CHECK-NEXT: [[timems1:%.+]] = arith.divf [[normalized_angle:%.+]], [[rabi1]] : f64 // CHECK-NEXT: ion.pulse([[timems1]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< // CHECK-SAME: transition_index = 0 : i64, // CHECK-SAME: rabi = 1.230000e+00 : f64, // CHECK-SAME: detuning = 4.560000e+00 : f64, - // CHECK-SAME: polarization = [7, 8], - // CHECK-SAME: wavevector = [9, 10]>, + // CHECK-SAME: polarization = [7, 8, 9], + // CHECK-SAME: wavevector = [9, 10, 11]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timems1]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 1.230000e+00 : f64, // CHECK-SAME: detuning = 5.660000e+00 : f64, - // CHECK-SAME: polarization = [7, 8], - // CHECK-SAME: wavevector = [-9, -10]>, + // CHECK-SAME: polarization = [7, 8, 9], + // CHECK-SAME: wavevector = [-9, -10, -11]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timems1]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 1.230000e+00 : f64, // CHECK-SAME: detuning = 3.4599999999999995 : f64, - // CHECK-SAME: polarization = [7, 8], - // CHECK-SAME: wavevector = [-9, -10]>, + // CHECK-SAME: polarization = [7, 8, 9], + // CHECK-SAME: wavevector = [-9, -10, -11]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timems1]] : f64) %arg2 { // CHECK-SAME: beam = #ion.beam< // CHECK-SAME: transition_index = 0 : i64, // CHECK-SAME: rabi = 1.230000e+00 : f64, // CHECK-SAME: detuning = 4.560000e+00 : f64, - // CHECK-SAME: polarization = [7, 8], - // CHECK-SAME: wavevector = [9, 10]>, + // CHECK-SAME: polarization = [7, 8, 9], + // CHECK-SAME: wavevector = [9, 10, 11]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timems1]] : f64) %arg2 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 1.230000e+00 : f64, // CHECK-SAME: detuning = 8.960000e+00 : f64, - // CHECK-SAME: polarization = [7, 8], - // CHECK-SAME: wavevector = [-9, -10]>, + // CHECK-SAME: polarization = [7, 8, 9], + // CHECK-SAME: wavevector = [-9, -10, -11]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timems1]] : f64) %arg2 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 1.230000e+00 : f64, // CHECK-SAME: detuning = 0.15999999999999925 : f64, - // CHECK-SAME: polarization = [7, 8], - // CHECK-SAME: wavevector = [-9, -10]>, + // CHECK-SAME: polarization = [7, 8, 9], + // CHECK-SAME: wavevector = [-9, -10, -11]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.yield %arg1, %arg2 : !quantum.bit, !quantum.bit // CHECK-NEXT: } @@ -299,55 +372,65 @@ func.func @example_ion_three_qubit(%arg0: f64) -> (!quantum.bit, !quantum.bit, ! // CHECK: [[ms2out:%.+]]:2 = ion.parallelprotocol([[ms1out]]#0, [[qubit2]]) : !quantum.bit, !quantum.bit { // CHECK-NEXT: ^{{.*}}(%arg1: !quantum.bit, %arg2: !quantum.bit): + // CHECK-NEXT:%cst = arith.constant 12.566370614359172 : f64 + // CHECK-NEXT: [[remainder:%.+]] = arith.remf %arg0, %cst : f64 + // CHECK-NEXT: %cst_0 = arith.constant 0.000000e+00 : f64 + // CHECK-NEXT: [[negative:%.+]] = arith.cmpf olt, [[remainder:%.+]], %cst_0 : f64 + // CHECK-NEXT: [[normalized_angle:%.+]] = scf.if [[negative:%.+]] -> (f64) { + // CHECK-NEXT: [[adjusted:%.+]] = arith.addf [[remainder:%.+]], %cst : f64 + // CHECK-NEXT: scf.yield [[adjusted:%.+]] : f64 + // CHECK-NEXT: } else { + // CHECK-NEXT: scf.yield [[remainder:%.+]] : f64 + // CHECK-NEXT: } // CHECK-NEXT: [[rabi2:%.+]] = arith.constant 4.560000e+00 : f64 - // CHECK-NEXT: [[timems2:%.+]] = arith.divf %arg0, [[rabi2]] : f64 + // CHECK-NEXT: [[timems2:%.+]] = arith.divf [[normalized_angle:%.+]], [[rabi2]] : f64 // CHECK-NEXT: ion.pulse([[timems2]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< // CHECK-SAME: transition_index = 0 : i64, // CHECK-SAME: rabi = 4.560000e+00 : f64, // CHECK-SAME: detuning = 7.8899999999999996 : f64, - // CHECK-SAME: polarization = [1, 2], - // CHECK-SAME: wavevector = [-3, 4]>, + // CHECK-SAME: polarization = [1, 2, 3], + // CHECK-SAME: wavevector = [-3, 4, 5]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timems2]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 4.560000e+00 : f64, // CHECK-SAME: detuning = 8.990000e+00 : f64, - // CHECK-SAME: polarization = [1, 2], - // CHECK-SAME: wavevector = [3, -4]>, + // CHECK-SAME: polarization = [1, 2, 3], + // CHECK-SAME: wavevector = [3, -4, -5]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timems2]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 4.560000e+00 : f64, // CHECK-SAME: detuning = 6.7899999999999991 : f64, - // CHECK-SAME: polarization = [1, 2], - // CHECK-SAME: wavevector = [3, -4]>, + // CHECK-SAME: polarization = [1, 2, 3], + // CHECK-SAME: wavevector = [3, -4, -5]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timems2]] : f64) %arg2 { // CHECK-SAME: beam = #ion.beam< // CHECK-SAME: transition_index = 0 : i64, // CHECK-SAME: rabi = 4.560000e+00 : f64, // CHECK-SAME: detuning = 7.8899999999999996 : f64, - // CHECK-SAME: polarization = [1, 2], - // CHECK-SAME: wavevector = [-3, 4]>, + // CHECK-SAME: polarization = [1, 2, 3], + // CHECK-SAME: wavevector = [-3, 4, 5]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timems2]] : f64) %arg2 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 4.560000e+00 : f64, // CHECK-SAME: detuning = 1.559000e+01 : f64, - // CHECK-SAME: polarization = [1, 2], - // CHECK-SAME: wavevector = [3, -4]>, + // CHECK-SAME: polarization = [1, 2, 3], + // CHECK-SAME: wavevector = [3, -4, -5]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.pulse([[timems2]] : f64) %arg2 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 4.560000e+00 : f64, // CHECK-SAME: detuning = 0.1899999999999995 : f64, - // CHECK-SAME: polarization = [1, 2], - // CHECK-SAME: wavevector = [3, -4]>, + // CHECK-SAME: polarization = [1, 2, 3], + // CHECK-SAME: wavevector = [3, -4, -5]>, // CHECK-SAME: phase = 0.000000e+00 : f64} // CHECK-NEXT: ion.yield %arg1, %arg2 : !quantum.bit, !quantum.bit // CHECK-NEXT: } @@ -355,55 +438,65 @@ func.func @example_ion_three_qubit(%arg0: f64) -> (!quantum.bit, !quantum.bit, ! // CHECK: [[ms3out:%.+]]:2 = ion.parallelprotocol([[ms1out]]#1, [[ms2out]]#1) : !quantum.bit, !quantum.bit { // CHECK-NEXT: ^{{.*}}(%arg1: !quantum.bit, %arg2: !quantum.bit): + // CHECK-NEXT:%cst = arith.constant 12.566370614359172 : f64 + // CHECK-NEXT: [[remainder:%.+]] = arith.remf %arg0, %cst : f64 + // CHECK-NEXT: %cst_0 = arith.constant 0.000000e+00 : f64 + // CHECK-NEXT: [[negative:%.+]] = arith.cmpf olt, [[remainder:%.+]], %cst_0 : f64 + // CHECK-NEXT: [[normalized_angle:%.+]] = scf.if [[negative:%.+]] -> (f64) { + // CHECK-NEXT: [[adjusted:%.+]] = arith.addf [[remainder:%.+]], %cst : f64 + // CHECK-NEXT: scf.yield [[adjusted:%.+]] : f64 + // CHECK-NEXT: } else { + // CHECK-NEXT: scf.yield [[remainder:%.+]] : f64 + // CHECK-NEXT: } // CHECK-NEXT: [[rabi3:%.+]] = arith.constant 99.989999999999994 : f64 - // CHECK-NEXT: [[timems3:%.+]] = arith.divf %arg0, [[rabi3]] : f64 + // CHECK-NEXT: [[timems3:%.+]] = arith.divf [[normalized_angle:%.+]], [[rabi3]] : f64 // CHECK-NEXT: [[p1:%.+]] = ion.pulse([[timems3]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< // CHECK-SAME: transition_index = 0 : i64, // CHECK-SAME: rabi = 99.989999999999994 : f64, // CHECK-SAME: detuning = 1.001000e+02 : f64, - // CHECK-SAME: polarization = [37, 42], - // CHECK-SAME: wavevector = [-42, -37]>, + // CHECK-SAME: polarization = [37, 42, 43], + // CHECK-SAME: wavevector = [-42, -37, -43]>, // CHECK-SAME: phase = 0.000000e+00 : f64} : !ion.pulse // CHECK-NEXT: [[p2:%.+]] = ion.pulse([[timems3]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 99.989999999999994 : f64, // CHECK-SAME: detuning = 1.045000e+02 : f64, - // CHECK-SAME: polarization = [37, 42], - // CHECK-SAME: wavevector = [42, 37]>, + // CHECK-SAME: polarization = [37, 42, 43], + // CHECK-SAME: wavevector = [42, 37, 43]>, // CHECK-SAME: phase = 0.000000e+00 : f64} : !ion.pulse // CHECK-NEXT: [[p3:%.+]] = ion.pulse([[timems3]] : f64) %arg1 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 99.989999999999994 : f64, // CHECK-SAME: detuning = 95.699999999999989 : f64, - // CHECK-SAME: polarization = [37, 42], - // CHECK-SAME: wavevector = [42, 37]>, + // CHECK-SAME: polarization = [37, 42, 43], + // CHECK-SAME: wavevector = [42, 37, 43]>, // CHECK-SAME: phase = 0.000000e+00 : f64} : !ion.pulse // CHECK-NEXT: [[p4:%.+]] = ion.pulse([[timems3]] : f64) %arg2 { // CHECK-SAME: beam = #ion.beam< // CHECK-SAME: transition_index = 0 : i64, // CHECK-SAME: rabi = 99.989999999999994 : f64, // CHECK-SAME: detuning = 1.001000e+02 : f64, - // CHECK-SAME: polarization = [37, 42], - // CHECK-SAME: wavevector = [-42, -37]>, + // CHECK-SAME: polarization = [37, 42, 43], + // CHECK-SAME: wavevector = [-42, -37, -43]>, // CHECK-SAME: phase = 0.000000e+00 : f64} : !ion.pulse // CHECK-NEXT: [[p5:%.+]] = ion.pulse([[timems3]] : f64) %arg2 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 99.989999999999994 : f64, // CHECK-SAME: detuning = 1.078000e+02 : f64, - // CHECK-SAME: polarization = [37, 42], - // CHECK-SAME: wavevector = [42, 37]>, + // CHECK-SAME: polarization = [37, 42, 43], + // CHECK-SAME: wavevector = [42, 37, 43]>, // CHECK-SAME: phase = 0.000000e+00 : f64} : !ion.pulse // CHECK-NEXT: [[p6:%.+]] = ion.pulse([[timems3]] : f64) %arg2 { // CHECK-SAME: beam = #ion.beam< - // CHECK-SAME: transition_index = 1 : i64, + // CHECK-SAME: transition_index = 2 : i64, // CHECK-SAME: rabi = 99.989999999999994 : f64, // CHECK-SAME: detuning = 92.399999999999991 : f64, - // CHECK-SAME: polarization = [37, 42], - // CHECK-SAME: wavevector = [42, 37]>, + // CHECK-SAME: polarization = [37, 42, 43], + // CHECK-SAME: wavevector = [42, 37, 43]>, // CHECK-SAME: phase = 0.000000e+00 : f64} : !ion.pulse // CHECK-NEXT: ion.yield %arg1, %arg2 : !quantum.bit, !quantum.bit // CHECK-NEXT: } diff --git a/mlir/test/Ion/oqd_gate_decomposition_parameters.toml b/mlir/test/Ion/oqd_gate_decomposition_parameters.toml index c57f9096c7..a5b380f7e0 100644 --- a/mlir/test/Ion/oqd_gate_decomposition_parameters.toml +++ b/mlir/test/Ion/oqd_gate_decomposition_parameters.toml @@ -37,84 +37,125 @@ oqd_config_schema = "v0.1" [[beams1]] rabi = 1.1 detuning = 2.2 -polarization = [0,1] -wavevector = [-2,3] +polarization = [0,1,2] +wavevector = [-2,3,4] [[beams1]] rabi = 4.4 detuning = 5.5 -polarization = [6,7] -wavevector = [8,9] +polarization = [6,7,8] +wavevector = [8,9,10] [[beams1]] rabi = 10.10 detuning = 11.11 -polarization = [12,13] -wavevector = [14,15] +polarization = [12,13,14] +wavevector = [14,15,16] [[beams2]] rabi = 1.23 detuning = 4.56 -polarization = [7,8] -wavevector = [9,10] +polarization = [7,8,9] +wavevector = [9,10,11] [[beams2]] rabi = 4.56 detuning = 7.89 -polarization = [1,2] -wavevector = [-3,4] +polarization = [1,2,3] +wavevector = [-3,4,5] [[beams2]] rabi = 99.99 detuning = 100.100 -polarization = [37,42] -wavevector = [-42,-37] +polarization = [37,42,43] +wavevector = [-42,-37,-43] -# Phonons +# Phonon modes # Each qubit has a phonon triplet (x,y,z) # A list of phonon triplets, one triplet per qubit, is stored here # On a single direction, a phonon has a calibrated energy. +# phonons1 are the phonons modes of a single-ion system. +# phonons2 are the phonons modes of a two-ion system. +# phonons3 are the phonons modes of a three-ion system. # ------- -[[phonons]] -[phonons.COM_x] +[[phonons1]] energy = 1.1 -eigenvector = [1, 0, 0] +eigenvector = [1.0, 0.0, 0.0] -[phonons.COM_y] +[[phonons1]] energy = 2.2 -eigenvector = [0, 1, 0] +eigenvector = [0.0, 1.0, 0.0] -[phonons.COM_z] +[[phonons1]] energy = 3.3 -eigenvector = [0, 0, 1] +eigenvector = [0.0, 0.0, 1.0] -[[phonons]] -[phonons.COM_x] + +[[phonons2]] +energy = 1.1 +eigenvector = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0] + +[[phonons2]] +energy = 2.2 +eigenvector = [0.0, 1.0, 0.0, 0.0, 1.0, 0.0] + +[[phonons2]] +energy = 3.3 +eigenvector = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0] + + +[[phonons2]] +energy = 4.4 +eigenvector = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0] + +[[phonons2]] +energy = 5.5 +eigenvector = [0.0, 1.0, 0.0, 0.0, 1.0, 0.0] + +[[phonons2]] +energy = 6.6 +eigenvector = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0] + + + +[[phonons3]] +energy = 1.1 +eigenvector = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0] + +[[phonons3]] +energy = 2.2 +eigenvector = [0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0] + +[[phonons3]] +energy = 3.3 +eigenvector = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0] + + +[[phonons3]] energy = 4.4 -eigenvector = [1, 0, 0] +eigenvector = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0] -[phonons.COM_y] +[[phonons3]] energy = 5.5 -eigenvector = [0, 1, 0] +eigenvector = [0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0] -[phonons.COM_z] +[[phonons3]] energy = 6.6 -eigenvector = [0, 0, 1] +eigenvector = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0] -[[phonons]] -[phonons.COM_x] +[[phonons3]] energy = 7.7 -eigenvector = [1, 0, 0] +eigenvector = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0] -[phonons.COM_y] +[[phonons3]] energy = 8.8 -eigenvector = [0, 1, 0] +eigenvector = [0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0] -[phonons.COM_z] +[[phonons3]] energy = 9.9 -eigenvector = [0, 0, 1] +eigenvector = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0] diff --git a/mlir/test/Ion/oqd_qubit_parameters.toml b/mlir/test/Ion/oqd_qubit_parameters.toml index 75b0f3ff02..08f6ed4389 100644 --- a/mlir/test/Ion/oqd_qubit_parameters.toml +++ b/mlir/test/Ion/oqd_qubit_parameters.toml @@ -28,45 +28,59 @@ position = [1.0, 2.0, -1.0] levels.downstate.label = "downstate" levels.downstate.principal = 6 -levels.downstate.spin = 0.4 -levels.downstate.orbital = 0.5 -levels.downstate.nuclear = 0.6 -levels.downstate.spin_orbital = 0.8 -levels.downstate.spin_orbital_nuclear = 0.9 -levels.downstate.spin_orbital_nuclear_magnetization = 1.0 +levels.downstate.spin = 0.5 +levels.downstate.orbital = 1.0 +levels.downstate.nuclear = 1.5 +levels.downstate.spin_orbital = 2.0 +levels.downstate.spin_orbital_nuclear = 2.5 +levels.downstate.spin_orbital_nuclear_magnetization = -3.0 levels.downstate.energy = 0.0 levels.upstate.label = "upstate" levels.upstate.principal = 6 -levels.upstate.spin = 1.4 -levels.upstate.orbital = 1.5 -levels.upstate.nuclear = 1.6 -levels.upstate.spin_orbital = 1.8 -levels.upstate.spin_orbital_nuclear = 1.9 -levels.upstate.spin_orbital_nuclear_magnetization = 2.0 +levels.upstate.spin = 1.5 +levels.upstate.orbital = 2.0 +levels.upstate.nuclear = 2.5 +levels.upstate.spin_orbital = 3.0 +levels.upstate.spin_orbital_nuclear = 3.5 +levels.upstate.spin_orbital_nuclear_magnetization = -4.0 levels.upstate.energy = 12.643e9 levels.estate.label = "estate" levels.estate.principal = 5 -levels.estate.spin = 2.4 -levels.estate.orbital = 2.5 -levels.estate.nuclear = 2.6 -levels.estate.spin_orbital = 2.8 -levels.estate.spin_orbital_nuclear = 2.9 -levels.estate.spin_orbital_nuclear_magnetization = 3.0 +levels.estate.spin = 2.5 +levels.estate.orbital = 3.0 +levels.estate.nuclear = 3.5 +levels.estate.spin_orbital = 4.0 +levels.estate.spin_orbital_nuclear = 4.5 +levels.estate.spin_orbital_nuclear_magnetization = -5.0 levels.estate.energy = 811.52e12 +levels.estate2.label = "estate2" +levels.estate2.principal = 5 +levels.estate2.spin = 0.5 +levels.estate2.orbital = 1.0 +levels.estate2.nuclear = 0.5 +levels.estate2.spin_orbital = 0.5 +levels.estate2.spin_orbital_nuclear = 1.0 +levels.estate2.spin_orbital_nuclear_magnetization = 1.0 +levels.estate2.energy = 1256.637 + [ions.Yb171.transitions.downstate_upstate] level1 = "downstate" level2 = "upstate" einstein_a = 1.1 +multipole = "M1" [ions.Yb171.transitions.downstate_estate] level1 = "downstate" level2 = "estate" einstein_a = 2.2 +multipole = "E2" [ions.Yb171.transitions.estate_upstate] level1 = "estate" level2 = "upstate" einstein_a = 3.3 +multipole = "E1" + diff --git a/runtime/include/OQDRuntimeCAPI.h b/runtime/include/OQDRuntimeCAPI.h index a0d065d87b..48aed5a1d2 100644 --- a/runtime/include/OQDRuntimeCAPI.h +++ b/runtime/include/OQDRuntimeCAPI.h @@ -45,6 +45,7 @@ struct Pulse { void __catalyst__oqd__rt__initialize(); void __catalyst__oqd__rt__finalize(); void __catalyst__oqd__ion(const std::string &ion_specs); +void __catalyst__oqd__modes(const std::vector &phonon_specs); Pulse *__catalyst__oqd__pulse(QUBIT *qubit, double duration, double phase, Beam *beam); void __catalyst__oqd__ParallelProtocol(Pulse **pulses, size_t n); diff --git a/runtime/lib/OQDcapi/OQDRuntimeCAPI.cpp b/runtime/lib/OQDcapi/OQDRuntimeCAPI.cpp index 002085a4ea..788e4dd520 100644 --- a/runtime/lib/OQDcapi/OQDRuntimeCAPI.cpp +++ b/runtime/lib/OQDcapi/OQDRuntimeCAPI.cpp @@ -68,7 +68,8 @@ void __catalyst__oqd__rt__initialize() json system = R"({ "class_": "System", - "ions": [] + "ions": [], + "modes": [] })"_json; (*JSON)["system"] = system; @@ -97,6 +98,13 @@ void __catalyst__oqd__ion(const std::string &ion_specs) (*JSON)["system"]["ions"].push_back(json::parse(ion_specs)); } +void __catalyst__oqd__modes(const std::vector &phonon_specs) +{ + for (auto phonon_spec : phonon_specs) { + (*JSON)["system"]["modes"].push_back(json::parse(phonon_spec)); + } +} + Pulse *__catalyst__oqd__pulse(QUBIT *qubit, double duration, double phase, Beam *beam) { size_t wire = reinterpret_cast(qubit); diff --git a/runtime/lib/backend/oqd/OQDDevice.cpp b/runtime/lib/backend/oqd/OQDDevice.cpp index 3cd6be65cc..8f0a68e2b7 100644 --- a/runtime/lib/backend/oqd/OQDDevice.cpp +++ b/runtime/lib/backend/oqd/OQDDevice.cpp @@ -26,6 +26,7 @@ auto OQDDevice::AllocateQubits(size_t num_qubits) -> std::vector for (size_t i = 0; i < num_qubits; i++) { __catalyst__oqd__ion(this->ion_specs); } + __catalyst__oqd__modes(this->phonon_specs); // need to return a vector from 0 to num_qubits std::vector result(num_qubits); @@ -37,6 +38,7 @@ auto OQDDevice::AllocateQubits(size_t num_qubits) -> std::vector void OQDDevice::ReleaseAllQubits() { this->ion_specs = ""; + this->phonon_specs.clear(); this->qubit_manager.ReleaseAll(); } @@ -79,7 +81,7 @@ void OQDDevice::NamedOperation(const std::string &name, const std::vector &eigvals, DataView &counts, const std::vector &wires, size_t shots) { - RT_FAIL("Unsupported functionality"); + return; } void OQDDevice::PrintState() { RT_FAIL("Unsupported functionality"); } diff --git a/runtime/lib/backend/oqd/OQDDevice.hpp b/runtime/lib/backend/oqd/OQDDevice.hpp index 9c73a1b248..f3a1cf968e 100644 --- a/runtime/lib/backend/oqd/OQDDevice.hpp +++ b/runtime/lib/backend/oqd/OQDDevice.hpp @@ -45,6 +45,7 @@ class OQDDevice final : public Catalyst::Runtime::QuantumDevice { bool tape_recording{false}; size_t device_shots; std::string ion_specs; + std::vector phonon_specs; std::unordered_map device_kwargs; @@ -63,12 +64,28 @@ class OQDDevice final : public Catalyst::Runtime::QuantumDevice { __catalyst__oqd__rt__initialize(); // The OQD kwarg string format is: - // deviceKwargs.str() + "ION:" + std::string(ion_json.dump()) - // where deviceKwargs are the usual kwargs like {'shots': 0, 'mcmc': False} - // and ion_json is a JSON spec string for the ion + // deviceKwargs.str() + "ION:" + std::string(ion_json.dump()) + "PHONON:" + + // std::string(phonon_json1.dump()) + ... where deviceKwargs are the usual keyword arguments + // like {'shots': 0, 'mcmc': False}, ion_json is a JSON string specifying the ion + // configuration, and phonon_json1, phonon_json2, etc. are JSON strings specifying phonon + // configurations. std::string ion_token = "ION:"; + std::string phonon_token = "PHONON:"; size_t ion_token_pos = kwargs.find(ion_token); - ion_specs = kwargs.substr(ion_token_pos + ion_token.length()); + if (ion_token_pos != std::string::npos) { + size_t ion_start_pos = ion_token_pos + ion_token.length(); + size_t phonon_token_pos = kwargs.find(phonon_token); + ion_specs = kwargs.substr(ion_start_pos, phonon_token_pos - ion_start_pos); + } + + phonon_specs.clear(); + size_t phonon_token_pos = kwargs.find(phonon_token); + while (phonon_token_pos != std::string::npos) { + size_t phonon_start_pos = phonon_token_pos + phonon_token.length(); + phonon_token_pos = kwargs.find(phonon_token, phonon_start_pos); + phonon_specs.push_back( + kwargs.substr(phonon_start_pos, phonon_token_pos - phonon_start_pos)); + } device_kwargs = Catalyst::Runtime::parse_kwargs(kwargs.substr(0, ion_token_pos)); device_shots = device_kwargs.contains("shots") diff --git a/runtime/tests/Test_OQDDevice.cpp b/runtime/tests/Test_OQDDevice.cpp index 30ad11b2cc..2fb67f48d1 100644 --- a/runtime/tests/Test_OQDDevice.cpp +++ b/runtime/tests/Test_OQDDevice.cpp @@ -90,6 +90,23 @@ TEST_CASE("Test OpenAPL Program generation", "[oqd]") "class_": "AtomicCircuit", "system": { "class_": "System", + "modes":[ + { + "class_": "Phonon", + "eigenvector": [1.0,0.0,0.0], + "energy": 3.3 + }, + { + "class_": "Phonon", + "eigenvector": [0.0,1.0,0.0], + "energy": 4.4 + }, + { + "class_": "Phonon", + "eigenvector": [0.0,0.0,1.0], + "energy": 5.5 + } + ], "ions": [ { "class_": "Ion", @@ -883,6 +900,21 @@ TEST_CASE("Test OpenAPL Program generation", "[oqd]") 0.0, 0.0 ] + }PHONON: + { + "class_": "Phonon", + "eigenvector": [1.0,0.0,0.0], + "energy": 3.3 + }PHONON: + { + "class_": "Phonon", + "eigenvector": [0.0,1.0,0.0], + "energy": 4.4 + }PHONON: + { + "class_": "Phonon", + "eigenvector": [0.0,0.0,1.0], + "energy": 5.5 })"}; size_t num_qubits = 2;