# Create circuit to test transpiler on
from qiskit import QuantumCircuit
from qiskit.circuit.library import GroverOperator, Diagonal
oracle = Diagonal([1]*7 + [-1])
qc = QuantumCircuit(3)
qc.h([0,1,2])
qc = qc.compose(GroverOperator(oracle))

# Use Statevector object to calculate the ideal output
from qiskit.quantum_info import Statevector
ideal_distribution = Statevector.from_instruction(qc).probabilities_dict()

from qiskit.visualization import plot_histogram
plot_histogram(ideal_distribution)

from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
backend = service.backend('ibm_algiers')

# Need to add measurements to the circuit
qc.measure_all()

from qiskit import transpile
circuits = []
for optimization_level in [0, 3]:
    t_qc = transpile(qc,
                     backend,
                     optimization_level=optimization_level,
                     seed_transpiler=0)
    print(f'CNOTs (optimization_level={optimization_level}): ',
          t_qc.count_ops()['cx'])
    circuits.append(t_qc)

from qiskit.transpiler import PassManager, InstructionDurations
from qiskit.transpiler.passes import ASAPSchedule, DynamicalDecoupling
from qiskit.circuit.library import XGate

# Get gate durations so the transpiler knows how long each operation takes
durations = InstructionDurations.from_backend(backend)

# This is the sequence we'll apply to idling qubits
dd_sequence = [XGate(), XGate()]

# Run scheduling and dynamic decoupling passes on circuit
pm = PassManager([ASAPSchedule(durations),
                  DynamicalDecoupling(durations, dd_sequence)]
                )
circ_dd = pm.run(circuits[1])

# Add this new circuit to our list
circuits.append(circ_dd)

from qiskit_ibm_runtime import Sampler, Session

with Session(service=service, backend=backend):
    sampler = Sampler()
    job = sampler.run(
        circuits=circuits,  # sample all three circuits
        skip_transpilation=True, 
        shots=8000)
    result = job.result()

from qiskit.visualization import plot_histogram
binary_prob = [quasi_dist.binary_probabilities() for quasi_dist in result.quasi_dists]
plot_histogram(binary_prob+[ideal_distribution],
               bar_labels=False,
               legend=['optimization_level=0',
                       'optimization_level=3',
                       'optimization_level=3 + dd',
                       'ideal distribution'])

from qiskit.quantum_info import hellinger_fidelity
for counts in result.quasi_dists:
    print(
        f"{hellinger_fidelity(counts.binary_probabilities(), ideal_distribution):.3f}"
    )

import qiskit_ibm_runtime
qiskit_ibm_runtime.version.get_version_info()

from qiskit.tools.jupyter import *
%qiskit_version_table
%qiskit_copyright