qiskit-community / qiskit-dynamics

Tools for building and solving models of quantum systems in Qiskit
https://qiskit-community.github.io/qiskit-dynamics/
Apache License 2.0
106 stars 60 forks source link

Update dynamics_backend.rst tutorial after next terra release #205

Closed DanPuzzuoli closed 1 year ago

DanPuzzuoli commented 1 year ago

In the DynamicsBackend tutorial, it was necessary to add the CRPulseGate instruction to the target to make sure the transpiler functions as desired when running the CrossResonanceHamiltonian experiment. This is a work around that shouldn't b necessary.

This behaviour is fixed in Qiskit terra PR 9783, so we should remove this line from the tutorial after the next terra release.

DanPuzzuoli commented 1 year ago

The dynamics_backend.rst tutorial is also now failing to build due to issues with transpilation, so this should be fixed. @wshanks pointed to this PR as introducing the change in the Experiments code that is involved in the error.

DanPuzzuoli commented 1 year ago

Edit: the change described here can be moved to another issue if it makes sense.

In addition to this, a section showing how to get primitives to work with DynamicsBackend should be added to the tutorial. A self contained exampled by @wshanks:

from qiskit_ibm_provider import IBMProvider
from qiskit_dynamics import DynamicsBackend
from qiskit.circuit import ParameterVector, Parameter, QuantumCircuit, Gate
from qiskit.circuit.library import XGate, SXGate, RZGate
from qiskit.primitives import BackendEstimator
from qiskit import pulse
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler import InstructionProperties
import numpy as np
​
provider = IBMProvider()
real_backend = provider.get_backend("ibm_perth")
qubit_tgt_register = [0]
dynamics_options = {'seed_simulator': 5000, "configuration": real_backend.configuration()}
dynamics_backend = DynamicsBackend.from_backend(real_backend, subsystem_list=qubit_tgt_register, **dynamics_options)
​
backend = dynamics_backend
​
​
amp_params = np.array([[0.2], [0.22], [0.23]])
batch_size = len(amp_params)
params = ParameterVector("theta", 1)
​
observables = SparsePauliOp.from_list([("X", 1.), ("Y", 1.)])
estimator = BackendEstimator(backend=backend)
​
qc = QuantumCircuit(1)
​
parametrized_circ = QuantumCircuit(1)
​
parametrized_X_gate = Gate("my_x", 1, params=[params[0]])
​
default_schedule = real_backend.instruction_schedule_map.get("x", qubit_tgt_register)
pulse_ref = default_schedule.instructions[0][1].pulse
​
with pulse.build(backend=backend, name="parametrized_schedule") as parametrized_schedule:
    pulse.play(pulse.Drag(duration=pulse_ref.duration, amp = params[0], sigma=pulse_ref.sigma, 
                          beta=pulse_ref.beta, angle=pulse_ref.angle), channel=pulse.DriveChannel(0))

with pulse.build(backend=backend, name="sx_schedule") as sx_schedule:
    pulse.play(pulse.Drag(duration=pulse_ref.duration, amp = 0.1, sigma=pulse_ref.sigma, 
                          beta=pulse_ref.beta, angle=pulse_ref.angle), channel=pulse.DriveChannel(0))
​

rz_theta = Parameter("theta")
with pulse.build(backend=backend, name="rz") as rz_sched:
    pulse.shift_phase(-rz_theta, channel=pulse.DriveChannel(0))
​
backend.target.add_instruction(XGate(), properties={(0,): InstructionProperties(calibration=sx_schedule)})
backend.target.add_instruction(SXGate(), properties={(0,): InstructionProperties(calibration=sx_schedule)})
backend.target.add_instruction(RZGate(rz_theta), properties={(0,): InstructionProperties(calibration=rz_sched)})
​
parametrized_circ.add_calibration(parametrized_X_gate, qubit_tgt_register, parametrized_schedule)
parametrized_circ.append(parametrized_X_gate, qubit_tgt_register)
​
qc.append(parametrized_circ.to_instruction(), qubit_tgt_register)
​
job = estimator.run(circuits=[parametrized_circ]*batch_size, observables=[observables]*batch_size, parameter_values=amp_params,
                   shots=100)
​
results= job.result().values
print(results)

Everything mentioned here would be a good to include in a 0.4.1 release.