Qiskit/qiskit_humaneval_hard huggingface dataset:
Create a quantum circuit with one qubit and two classical bits. The qubit's operation depends on its measurement outcome: if it measures to 1 (|1> state), it flips the qubit's state back to |0> using an X gate. The qubit's initial state is randomized using a Hadamard gate. When building the quantum circuit make sure the classical registers is named 'c'.
You must implement this using a function named `conditional_two_qubit_circuit` with no arguments.

Solution:
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister

def conditional_two_qubit_circuit():
    qr = QuantumRegister(1)
    cr = ClassicalRegister(2, 'c')
    qc = QuantumCircuit(qr, cr)

    qc.h(qr[0])
    qc.measure(qr[0], cr[0])
    with qc.if_test((cr[0], 1)):
        qc.x(qr[0])
    qc.measure(qr[0], cr[1])
    return qc


Test:
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
def check(candidate):
    from qiskit_aer import AerSimulator
    from qiskit_ibm_runtime import Sampler
    from qiskit_ibm_runtime.options import SamplerOptions
    qc = candidate()
    assert isinstance(qc, QuantumCircuit)
    assert qc.num_qubits == 1
    assert qc.num_clbits == 2
    ops = dict(qc.count_ops())
    assert "h" in ops and "if_else" in ops
    backend = AerSimulator()
    options = SamplerOptions()
    options.simulator.seed_simulator=42
    sampler = Sampler(mode=backend, options=options)
    result = sampler.run([qc]).result()
    counts = result[0].data.c.get_counts()
    assert "10" not in counts and "11" not in counts

check(conditional_two_qubit_circuit)
Generate an EfficientSU2 circuit with the given number of qubits, 1 reps and make entanglement circular. 
Then use the Qiskit Transpiler service with the AI flag turned on, use the ibm_brisbane backend and an optimization level of 3 and transpile the generated circuit.
You must implement this using a function named `ai_transpiling` with the following arguments: num_qubits.

Solution:
from qiskit.circuit.library import EfficientSU2
from qiskit_ibm_transpiler.transpiler_service import TranspilerService

def ai_transpiling(num_qubits):
    circuit = EfficientSU2(num_qubits, entanglement="circular", reps=1)
    transpiler_ai_true = TranspilerService(
        backend_name="ibm_brisbane",
        ai=True,
        optimization_level=3
    )

    transpiled_circuit = transpiler_ai_true.run(circuit)
    return transpiled_circuit


Test:
from qiskit.circuit.library import EfficientSU2
from qiskit_ibm_transpiler.transpiler_service import TranspilerService
def check(candidate):
    import qiskit
    num_qubits = 3
    backend_name = "ibm_brisbane"
    ai_flag = True
    optimization_level = 3
    og_circuit = EfficientSU2(num_qubits, entanglement="circular", reps=1)
    gen_transpiled_circuit = candidate(num_qubits)
    assert isinstance(gen_transpiled_circuit, qiskit.circuit.QuantumCircuit)
    transpiler_service = TranspilerService(
        backend_name=backend_name,
        ai=ai_flag,
        optimization_level=optimization_level
    )

    expected_transpiled_circuit = transpiler_service.run(og_circuit)

    # We can add the following check once we have the random_state/seed feature in the transpiler service
    # assert gen_transpiled_circuit == expected_transpiled_circuit

check(ai_transpiling)
Instantiate a FakeBelemV2 backend and return the plot of its error_map.
You must implement this using a function named `backend_error_map` with no arguments.

Solution:
from qiskit.visualization import plot_error_map
from qiskit_ibm_runtime.fake_provider import FakeBelemV2

def backend_error_map():
    backend = FakeBelemV2()
    return plot_error_map(backend)


Test:
from qiskit.visualization import plot_error_map
from qiskit_ibm_runtime.fake_provider import FakeBelemV2
def check(candidate):
    from matplotlib.figure import Figure
    result = candidate()
    assert type(result) == Figure
    assert len(result.axes) == 5
    assert result.get_suptitle() == "fake_belem Error Map"

check(backend_error_map)
Generate a noise model from the Fake Cairo V2 backend.
You must implement this using a function named `gen_noise_model` with no arguments.

Solution:
from qiskit_ibm_runtime.fake_provider import FakeCairoV2
from qiskit_aer.noise import NoiseModel

def gen_noise_model():
    backend = FakeCairoV2()
    noise_model = NoiseModel.from_backend(backend)
    return noise_model


Test:
from qiskit_ibm_runtime.fake_provider import FakeCairoV2
from qiskit_aer.noise import NoiseModel
def check(candidate):
    backend = FakeCairoV2()
    expected_nm = NoiseModel.from_backend(backend)
    noise_model = candidate()
    assert type(noise_model) == NoiseModel
    assert noise_model == expected_nm

check(gen_noise_model)
Given a QuantumCircuit, convert it into a gate equivalent to the action of the input circuit and return it.
You must implement this using a function named `circ_to_gate` with the following arguments: circ.

Solution:
from qiskit.converters import circuit_to_gate

def circ_to_gate(circ):
    circ_gate = circuit_to_gate(circ)
    return circ_gate


Test:
from qiskit.converters import circuit_to_gate
def check(candidate):
    from qiskit import QuantumCircuit, QuantumRegister
    from qiskit.circuit.gate import Gate
    from qiskit.quantum_info import Operator
    from qiskit.circuit.library import ZGate
    q = QuantumRegister(3, "q")
    circ = QuantumCircuit(q)
    circ.h(q[0])
    circ.cx(q[0], q[1])
    custom_gate = candidate(circ)
    assert type(custom_gate) == Gate
    assert custom_gate.num_qubits == 3
    assert custom_gate.num_clbits == 0

    q = QuantumRegister(1, "q")
    circ = QuantumCircuit(q)
    circ.h(q)
    circ.x(q)
    circ.h(q)
    hxh_gate = circ_to_gate(circ)
    hxh_op = Operator(hxh_gate)
    z_op = Operator(ZGate())
    assert hxh_op.equiv(z_op)

check(circ_to_gate)
Create two quantum operators using Hadamard gate that differ only by a global phase. Calculate the process fidelity between these two operators and return the process fidelity value.
You must implement this using a function named `calculate_phase_difference_fidelity` with no arguments.

Solution:
from qiskit.circuit.library import HGate
from qiskit.quantum_info import Operator, process_fidelity
import numpy as np

def calculate_phase_difference_fidelity():
    op_a = Operator(HGate())
    op_b = np.exp(1j * 0.5) * Operator(HGate())
    
    fidelity = process_fidelity(op_a, op_b)
    
    return fidelity


Test:
from qiskit.circuit.library import HGate
from qiskit.quantum_info import Operator, process_fidelity
import numpy as np
def check(candidate):
    result = candidate()
    assert isinstance(result, float)
    assert abs(result - 1.0) < 1e-6

check(calculate_phase_difference_fidelity)
Using qiskit's random_circuit function, generate a circuit with 4 qubits and a depth of 3 that measures all qubits at the end. Use the seed value 17 and return the generated circuit.
You must implement this using a function named `random_circuit_depth` with no arguments.

Solution:
from qiskit.circuit.random import random_circuit
from qiskit import QuantumCircuit

def random_circuit_depth():
    circuit = random_circuit(4, 3, measure=True, seed = 17)
    return circuit


Test:
from qiskit.circuit.random import random_circuit
from qiskit import QuantumCircuit
def check(candidate):
    from qiskit.circuit.random import random_circuit
    from qiskit import QuantumCircuit

    # Run the candidate function
    result = candidate()

    # Check if the output is a QuantumCircuit
    assert isinstance(result, QuantumCircuit), "Output should be a QuantumCircuit"

    # Generate the expected circuit using the same parameters
    expected_qc = random_circuit(4, 3, measure=True, seed=17)

    # Ensure the circuit has 4 qubits
    assert result.num_qubits == 4, f"Expected 4 qubits, but got {result.num_qubits}"

    # Ensure the circuit depth is within an acceptable range (allowing small variations)
    expected_depth = expected_qc.depth()
    assert abs(result.depth() - expected_depth) <= 1, f"Expected depth around {expected_depth}, but got {result.depth()}"

    # Check if all qubits are measured at the end
    measured_qubits = [op for op, qargs, _ in result.data if op.name == "measure"]
    assert len(measured_qubits) == 4, "All qubits should be measured at the end"

    # Ensure the generated circuit matches the expected circuit structure
    assert result == expected_qc, "Generated circuit does not match expected circuit"

check(random_circuit_depth)
Create a quantum circuit with 4 qubits and 4 classical bits. Apply Hadamard gates to the first three qubits, measure them with three classical registers,
and conditionally apply an X gate to the fourth qubit based on the XOR of the three classical bits. Finally, measure the fourth qubit into another classical register.
You must implement this using a function named `conditional_quantum_circuit` with no arguments.

Solution:
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.classical import expr

def conditional_quantum_circuit():
    qr = QuantumRegister(4, "q")
    cr = ClassicalRegister(4, "c")
    circ = QuantumCircuit(qr, cr)

    circ.h(qr[0:3])
    circ.measure(qr[0:3], cr[0:3])

    _condition = expr.bit_xor(expr.bit_xor(cr[0], cr[1]), cr[2])
    with circ.if_test(_condition):
        circ.x(qr[3])

    circ.measure(qr[3], cr[3])

    return circ


Test:
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.classical import expr
def check(candidate):
    from qiskit_aer import AerSimulator
    from qiskit_ibm_runtime import Sampler, SamplerOptions
    from qiskit_ibm_runtime.options import SamplerOptions
    circ_res = candidate()
    assert type(circ_res) == QuantumCircuit 
    assert circ_res.num_qubits == 4
    assert circ_res.num_clbits == 4
    assert any(instr.operation.name == "if_else" and instr.operation.num_qubits == 1 and instr.operation.num_clbits == 3 for instr in circ_res.data)
    
    sim = AerSimulator()
    options = SamplerOptions()
    options.simulator.seed_simulator=17
    sampler = Sampler(mode=sim, options=options)
    counts = sampler.run([circ_res], shots=1024).result()[0].data.c.get_counts()
    for outcome, _ in counts.items():
        qubit_states = outcome[::-1]
        q3_state = int(qubit_states[3])
        q0 = int(qubit_states[0])
        q1 = int(qubit_states[1])
        q2 = int(qubit_states[2])
        expected_q3_state = (q0 ^ q1 ^ q2)
        assert q3_state == expected_q3_state
    
check(conditional_quantum_circuit)
Given the name of a quantum backend, retrieve the properties of the specified backend and identify the qubit pair with the highest error rate among its RZ gates. 
Return this qubit pair along with the corresponding error rate as a tuple. If the backend doesn't support the RZ gate, return None.
You must implement this using a function named `find_highest_rz_error_rate` with the following arguments: backend_name.

Solution:
from qiskit_ibm_runtime import QiskitRuntimeService

def find_highest_rz_error_rate(backend_name):
    service = QiskitRuntimeService(channel="ibm_quantum")
    backend = service.backend(backend_name)
    backend_properties = backend.properties()

    try:
        rz_props = backend_properties.gate_property("rz")
    except:
        return None

    qubit_pair = max(rz_props, key=lambda x: rz_props[x]["gate_error"][0])
    max_rz_error = rz_props[qubit_pair]["gate_error"][0]
    return (qubit_pair, max_rz_error)


Test:
from qiskit_ibm_runtime import QiskitRuntimeService
def check(candidate):
    service = QiskitRuntimeService(channel="ibm_quantum")
    backend = service.least_busy(filters=lambda b : ("rz" in b.basis_gates))
    max_rz_error_pair, max_rz_error_rate = candidate(backend.name)
    
    assert isinstance(max_rz_error_pair, (tuple, list))
    assert len(max_rz_error_pair) == 1
    assert all(isinstance(qubit, int) for qubit in max_rz_error_pair)
    assert max_rz_error_rate >= 0 and max_rz_error_rate <= 1

    backend_properties = backend.properties()
    rz_props = backend_properties.gate_property("rz")
    expected_max_rz_error_pair = max(rz_props, key=lambda x: rz_props[x]["gate_error"][0])
    expected_max_rz_error_rate = rz_props[expected_max_rz_error_pair]["gate_error"][0]
    assert (expected_max_rz_error_pair, expected_max_rz_error_rate) == (max_rz_error_pair, max_rz_error_rate)

check(find_highest_rz_error_rate)
Create a quantum circuit with 'n' qubits. Apply Hadamard gates to the second and third qubits.
Then apply CNOT gates between the second and fourth qubits, and between the third and fifth qubits.
Finally give the inverse of the quantum circuit.
You must implement this using a function named `inv_circuit` with the following arguments: n.

Solution:
from qiskit.circuit import QuantumCircuit

def inv_circuit(n):
    qc = QuantumCircuit(n)
    for i in range(2):
        qc.h(i+1)

    for i in range(2):
        qc.cx(i+1, i+2+1)
    
    return qc.inverse()


Test:
from qiskit.circuit import QuantumCircuit
def check(candidate):
    n = 5
    qc_inv = candidate(n)

    expected_qc = QuantumCircuit(n)
    for i in range(2):
        expected_qc.h(i+1)

    for i in range(2):
        expected_qc.cx(i+1, i+2+1)
    
    expected_qc_inv = expected_qc.inverse()

    assert qc_inv, QuantumCircuit
    assert qc_inv == expected_qc_inv

check(inv_circuit)
Given the fake backend name, retrieve information about the backend's number of qubits, coupling map, and supported instructions using the Qiskit Runtime Fake Provider, 
and create a dictionary containing the info. The dictionary must have the following keys: 'num_qubits' (the number of qubits), 'coupling_map' 
(the coupling map of the backend), and 'supported_instructions' (the supported instructions of the backend).
You must implement this using a function named `backend_info` with the following arguments: backend_name.

Solution:
from qiskit_ibm_runtime.fake_provider import FakeCairoV2

def backend_info(backend_name):
    backend = FakeCairoV2()
    config = backend.configuration()
    dict_result = {"num_qubits": config.num_qubits, "coupling_map": config.coupling_map, "supported_instructions": config.supported_instructions}

    return dict_result


Test:
from qiskit_ibm_runtime.fake_provider import FakeCairoV2
def check(candidate):
    backend = FakeCairoV2()
    binfo_dict = candidate(backend.name)
    backend_config = backend.configuration()
    assert isinstance(binfo_dict, dict)
    assert binfo_dict["num_qubits"] == backend_config.num_qubits
    assert binfo_dict["coupling_map"] == backend_config.coupling_map
    assert binfo_dict["supported_instructions"] == backend_config.supported_instructions

check(backend_info)
Generate 6 random quantum circuits, each with 3 qubits, depth of 2 and measure set to True; using the random_circuit Qiskit function, with seed 17. Then use a 
preset pass manager to optimize these circuits with an optimization level of 1, the backend for the pass manager should be FakeManilaV2, and set the seed value 
to 1. Partition these optimized circuits into batches of 3 circuits each, and execute these batches using Qiskit Runtime's Batch mode, and return a list containing 
the measurement outcome for each batch. For the execution of each batch set the seed to 42 for the sampler primitive.
You must implement this using a function named `run_batched_random_circuits` with no arguments.

Solution:
from qiskit.circuit.random import random_circuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Batch, Sampler
from qiskit_ibm_runtime.fake_provider import FakeManilaV2

def run_batched_random_circuits():
    fake_manila = FakeManilaV2()
    pm = generate_preset_pass_manager(backend=fake_manila, optimization_level=1, seed_transpiler = 1)
    circuits = [pm.run(random_circuit(3, 2, measure=True, seed=17)) for _ in range(6)]
    batch_size = 3
    partitions = [circuits[i : i + batch_size] for i in range(0, len(circuits), batch_size)]
    sampler_options = {"simulator": {"seed_simulator": 42}}
    with Batch(backend=fake_manila):
        sampler = Sampler(mode=fake_manila, options=sampler_options)
        results = []
        for partition in partitions:
            job = sampler.run(partition)
            results.append(job.result()[0].data.c.get_counts())
    return results


Test:
from qiskit.circuit.random import random_circuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Batch, Sampler
from qiskit_ibm_runtime.fake_provider import FakeManilaV2
def check(candidate):
    result = candidate()
    assert isinstance(result, list)
    assert len(result) == 2
    for counts in result:
        assert isinstance(counts, dict)
        assert counts == {"110": 423, "100": 474, "000": 58, "010": 52, "101": 8, "111": 8, "011": 1}

check(run_batched_random_circuits)
Find and return jobs submitted in the last three months using QiskitRuntimeService.
You must implement this using a function named `find_recent_jobs` with no arguments.

Solution:
import datetime
from qiskit_ibm_runtime import QiskitRuntimeService

def find_recent_jobs():
    three_months_ago = datetime.datetime.now() - datetime.timedelta(days=90)
    service = QiskitRuntimeService()
    jobs_in_last_three_months = service.jobs(created_after=three_months_ago)
    return jobs_in_last_three_months


Test:
import datetime
from qiskit_ibm_runtime import QiskitRuntimeService
def check(candidate):
    result = candidate()
    assert isinstance(result, list)
    for job in result:
        assert hasattr(job, "job_id")
        assert hasattr(job, "creation_date")
        assert job.creation_date.replace(tzinfo=None) >= (datetime.datetime.now() - datetime.timedelta(days=90)).replace(tzinfo=None)

check(find_recent_jobs)
Using the QiskitRuntimeService, retrieve the backends that meet the following criteria: they are real quantum devices, they are operational, and they have a 
minimum of 20 qubits. Then, return a list of dictionaries, each containing the backend's name, number of qubits, and the list of supported instruction names.
Ensure that the list of dictionaries is sorted by the backend name.
You must implement this using a function named `backend_info` with no arguments.

Solution:
from qiskit_ibm_runtime import QiskitRuntimeService

def backend_info():
    service = QiskitRuntimeService()
    backends = service.backends(simulator=False, operational=True, min_num_qubits=20)
    backend_info = []
    for b in backends:
        backend_info.append({"backend_name": b.name, "num_qubits": b.num_qubits, "instructions": b.operation_names})        
    sorted_backend_info = sorted(backend_info, key=lambda x: x["backend_name"])
    return sorted_backend_info


Test:
from qiskit_ibm_runtime import QiskitRuntimeService
def check(candidate):
    result = candidate()
    assert isinstance(result, list)
    
    service = QiskitRuntimeService()
    backends = service.backends(simulator=False, operational=True, min_num_qubits=20)
    backend_info = []
    for b in backends:
        backend_info.append({"backend_name": b.name, "num_qubits": b.num_qubits, "instructions": b.operation_names})        
    sorted_backend_info_expected = sorted(backend_info, key=lambda x: x["backend_name"])
    assert sorted_backend_info_expected == result

check(backend_info)
Construct a noise model with specific readout error properties for different qubits. For qubit 0, a readout of 1 has a 20% probability 
of being erroneously read as 0, and a readout of 0 has a 30% probability of being erroneously read as 1. For all other qubits, a readout of 1 has a 3% 
probability of being erroneously read as 0, and a readout of 0 has a 2% probability of being erroneously read as 1.
You must implement this using a function named `noise_model_with_readouterror` with no arguments.

Solution:
from qiskit_aer.noise import NoiseModel, ReadoutError

def noise_model_with_readouterror():
    noise_model = NoiseModel()
    p0given1_other = 0.03
    p1given0_other = 0.02
    readout_error_other = ReadoutError(
        [
            [1 - p1given0_other, p1given0_other],
            [p0given1_other, 1 - p0given1_other],
        ]
    )
    noise_model.add_all_qubit_readout_error(readout_error_other)

    p0given1_q0 = 0.2
    p1given0_q0 = 0.3
    readout_error_q0 = ReadoutError(
        [
            [1 - p1given0_q0, p1given0_q0],
            [p0given1_q0, 1 - p0given1_q0],
        ]
    )
    noise_model.add_readout_error(readout_error_q0, [0])

    return noise_model


Test:
from qiskit_aer.noise import NoiseModel, ReadoutError
def check(candidate):
    result = candidate()
    
    assert isinstance(result, NoiseModel), "Result should be a NoiseModel instance"
    
    global_error = result.to_dict()["errors"][0]['probabilities']
    assert global_error == [[0.98, 0.02], [0.03, 0.97]]
    
    qubit0_error = result.to_dict()["errors"][1]
    assert qubit0_error["gate_qubits"][0][0] == 0
    assert qubit0_error["probabilities"] == [[0.7, 0.3], [0.2, 0.8]]
    
    assert len(result.to_dict()["errors"]) == 2

check(noise_model_with_readouterror)
Return a list of ten density matrices which are pure up to a tolerance of ε.
You must implement this using a function named `pure_states` with the following arguments: ε.

Solution:
from qiskit.quantum_info import entropy, random_density_matrix, DensityMatrix

def pure_states(ε):
    entropy_list = []
    while len(entropy_list) <= 9:
        density_matrix = random_density_matrix(dims = 2)
        if entropy(density_matrix) < ε:
            entropy_list.append(density_matrix)
    return entropy_list


Test:
from qiskit.quantum_info import entropy, random_density_matrix, DensityMatrix
def check(candidate):
    tol = 0.01
    list_can = candidate(tol)
    assert len(list_can) == 10," Length of the list is not 10"
    for _, item in enumerate(list_can):
        assert isinstance(item, DensityMatrix), "The list doesn't contain density matrices"
        assert entropy(item) < tol, "Entropy of density matrix is not within tolerance"

check(pure_states)
Return a dataset of density matrices whose 2-qubit entanglement of formation is within given tolerance.
You must implement this using a function named `entanglement_dataset` with the following arguments: ε.

Solution:
from qiskit.quantum_info import random_density_matrix, entanglement_of_formation

def entanglement_dataset(ε):
    entanglement_data = []
    while len(entanglement_data) <= 9:
        density_matrix = random_density_matrix(dims = 4)
        if entanglement_of_formation(density_matrix)>=ε:
            entanglement_data.append(density_matrix)
    return entanglement_data


Test:
from qiskit.quantum_info import random_density_matrix, entanglement_of_formation
def check(candidate):
    from qiskit.quantum_info import DensityMatrix
    tol = 0.1
    can_list = candidate(tol)
    assert len(can_list) == 10, "Length of list is not 10"
    for _, item in enumerate(can_list):
        assert isinstance(item, DensityMatrix), "The list doesn't contain density matrices"
        assert entanglement_of_formation(item) >= tol , " The entanglement of formation is not within tolerance"

check(entanglement_dataset)