import numpy as np
from qiskit.tools.jupyter import *
from qiskit import IBMQ
# ibm account manager
#   - communicate with online account 
#       - store/delete
#       - enable/disenable
#   - communicate with ibmq online devices, quantum computers
#       - get actual quantum computers as backend

IBMQ.load_account()

provider = IBMQ.ibmq.get_provider(hub='ibm-q', group='open', project='main')

provider.backends()

backend = provider.get_backend('ibmq_bogota')
back_config = backend.configuration()

print("number of qubit: ", back_config.n_qubits)
print("support open pulse?: ", back_config.open_pulse)
print("basic gate set: ", back_config.basis_gates)

dt = back_config.dt
print(f"Sampling time: {dt*1e9} ns")

back_defualt = backend.defaults()

# frequncies for all the 5 qubits
back_defualt.qubit_freq_est

GHz = 1.0e9 # Gigahertz
MHz = 1.0e6 # Megahertz

us = 1.0e-6 # Microsecond
ns = 1.0e-9 # Nanosecond

# the index of the qubit in backend that we want to find frequency with
qubitIdx = 4

mem_slot = 0

# define the search range and get a list of frequencies
center_f_Hz = back_defualt.qubit_freq_est[qubitIdx]

f_span = 50 * MHz
f_step = 1 * MHz

f_min = center_f_Hz - f_span/2
f_max = center_f_Hz + f_span/2

frequencies_GHz = np.arange(f_min/GHz, f_max/GHz, f_step/GHz)

print(f"We'll measure the {qubitIdx}th qubit in {back_config.backend_name} \n")
print(f"sweep from {frequencies_GHz[0]} GHz to {frequencies_GHz[-1]} GHz with approximated frequncy {center_f_Hz/GHz} GHz")

# samples need to be multiples of 16
def get_closest_multiple_of_16(num):
    return int(num + 8 ) - (int(num + 8 ) % 16)

from qiskit import pulse
from qiskit.circuit import  Parameter

drive_sigma_sec = 0.075 * us
drive_duration_sec = 8 * drive_sigma_sec

drive_amp =0.05
freq = Parameter('freq')

with pulse.build(backend=backend, default_alignment='sequential', name='freq_sweep') as sweep_plan:
    drive_duration = get_closest_multiple_of_16(pulse.seconds_to_samples(drive_duration_sec))
    drive_sigma = pulse.seconds_to_samples(drive_sigma_sec)
    drive_chan = pulse.drive_channel(qubitIdx)

    pulse.set_frequency(freq, drive_chan)
    pulse.play(pulse.Gaussian(
        duration=drive_duration,
        sigma=drive_sigma,
        amp=drive_amp,
        name='freq_sweep_excitation_pulse'
    ),channel=drive_chan)
    pulse.measure(qubits=[qubitIdx], registers=[pulse.MemorySlot(mem_slot)])

frequencies_Hz = frequencies_GHz * GHz
schedules = [sweep_plan.assign_parameters({freq: f}, inplace=False) for f in frequencies_Hz]

schedules[0].draw(backend=backend)