2
$\begingroup$

Hi Ive been trying to solve a custom PO problem using QAOA and I ran into this particular error. As per my understanding,this is due to the limitation of the simulation hardware. But as per the documentation the new QAOA package does not support to inject a quantum instance (from qiskit_algorithms import QAOA) but the old implementation did. (from qiskit.algorithms import QAOA). Given below is my code implementation. Can someone help me fix this issue.

from qiskit_algorithms import  QAOA
from qiskit_optimization.algorithms import MinimumEigenOptimizer
from qiskit_algorithms.optimizers import COBYLA
from qiskit_aer.primitives import Sampler


qp = from_docplex_mp(mdl)
print(qp.export_as_lp_string())
cobyla = COBYLA()
cobyla.set_options(maxiter=250)
qaoa_mes = QAOA(sampler=Sampler(), optimizer=cobyla, reps=1)
qaoa = MinimumEigenOptimizer(qaoa_mes)
result = qaoa.solve(qp)

Ive tried getting the backend using

service = QiskitRuntimeService(channel="ibm_quantum",token="TOKEN")
backend = service.least_busy(operational=True, simulator=False)

but there isnt a specific method to provide that to the QAOA.

$\endgroup$

2 Answers 2

5
$\begingroup$

The error is because the number of qubits in the hardware you are selecting, is less than what your QAOA circuit has (in this case 40). All you have to do is change the backend importing code, and assert that you want a backend with at least this many qubits. You can do this via

least_busy(min_num_qubits=None, instance=None, filters=None, **kwargs)

or in your code, you have to do the following:

service = QiskitRuntimeService(channel="ibm_quantum",token="TOKEN")
backend = service.least_busy(min_num_qubits = 40,operational=True, simulator=False)

For updated QAOA implementation, follow this

The backend is assigned in the sampler and estimator using options, like this:

# To run on local simulator:
#   1. Use the Estimator from qiskit.primitives instead.
#   2. Remove the Session context manager below.

options = Options()
options.transpilation.skip_transpilation = True
options.execution.shots = 10000

session = Session(backend=backend)

estimator = Estimator(session=session, options={"shots": int(1e4)})
sampler = Sampler(session=session, options={"shots": int(1e4)})

Recent Implementation

As per your recent comments, that error can be rectified by doing follows:

I'm following your code till this line:

print(qp.export_as_lp_string())

Selecting the backend and importing

# General imports
import numpy as np
import warnings

warnings.filterwarnings("ignore")

# Pre-defined ansatz circuit, operator class and visualization tools
from qiskit.circuit.library import QAOAAnsatz
from qiskit.quantum_info import SparsePauliOp
from qiskit.visualization import plot_distribution

# Qiskit Runtime
from qiskit_ibm_runtime import QiskitRuntimeService, Session
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime import SamplerV2 as Sampler

# SciPy minimizer routine
from scipy.optimize import minimize

# rustworkx graph library
import rustworkx as rx
from rustworkx.visualization import mpl_draw


# To run on hardware, select the backend with the fewest number of jobs in the queue
service = QiskitRuntimeService(channel="ibm_quantum")
backend = service.least_busy(min_num_qubits=40,operational=True, simulator=False)
backend.name

Since you are using a QAOA function, I'll write how it is done in the updated version:

Convert QUBO to a SparsePauliOp

op,offset = qp.to_ising()
print("offset: {}".format(offset))
print("operator:")
print(op)

Make a QAOA Ansatz

from qiskit.circuit.library import QAOAAnsatz
ansatz = QAOAAnsatz(op, reps=2)

ansatz.decompose(reps=3).draw(output="mpl", style="iqp")

Optimize problem for quantum execution

from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)

ansatz_isa = pm.run(ansatz)
ansatz_isa.draw(output="mpl", idle_wires=False, style="iqp")

Observables

hamiltonian_isa = op.apply_layout(ansatz_isa.layout)
hamiltonian_isa

Execute

def cost_func(params, ansatz, hamiltonian, estimator):
    """Return estimate of energy from estimator

    Parameters:
        params (ndarray): Array of ansatz parameters
        ansatz (QuantumCircuit): Parameterized ansatz circuit
        hamiltonian (SparsePauliOp): Operator representation of Hamiltonian
        estimator (EstimatorV2): Estimator primitive instance

    Returns:
        float: Energy estimate
    """
    pub = (ansatz, [hamiltonian], [params])
    result = estimator.run(pubs=[pub]).result()
    cost = result[0].data.evs[0]

    return cost

Quantum Backend

# To run on local simulator:
#   1. Use the StatevectorEstimator from qiskit.primitives instead.
#   2. Remove the Session instantiation below.
session = Session(backend=backend)

# Configure estimator
estimator = Estimator(session=session)
estimator.options.default_shots = 10_000
estimator.options.dynamical_decoupling.enable = True

# Configure sampler
sampler = Sampler(session=session)
sampler.options.default_shots = 10_000
sampler.options.dynamical_decoupling.enable = True

Initial Parameters

x0 = 2 * np.pi * np.random.rand(ansatz_isa.num_parameters)

and minimization

res = minimize(cost_func, x0, args=(ansatz_isa, hamiltonian_isa, estimator), method="COBYLA")
res

Post Process

# Assign solution parameters to ansatz
qc = ansatz.assign_parameters(res.x)
# Add measurements to our circuit
qc.measure_all()
qc_isa = pm.run(qc)
qc_isa.draw(output="mpl", idle_wires=False, style="iqp")
result = sampler.run([qc_isa]).result()
samp_dist = result[0].data.meas.get_counts()
# Close the session since we are now done with it
session.close()

and if your problem instance is small, you can view it as well

plot_distribution(samp_dist, figsize=(15, 5))

System Versions

Python: 3.11.7 Qiskit: 1.0.2

and the all the other Qiskit dependencies in their latest version as of today.

$\endgroup$
6
  • $\begingroup$ Thank you. This answer was helpful to submit the job to IBM. $\endgroup$ Commented Mar 20 at 2:19
  • $\begingroup$ Thank you very much for the answer. Can you also provide the python package versions that you are using. I ran into an error possible because of the miscompatability of packages. ImportError: cannot import name 'ExperimentalWarning' from 'qiskit.exceptions' (/Users/lasalhettiarachchi/Development/ResearchProject/venv/lib/python3.10/site-packages/qiskit/exceptions.py) also can you provide the import for the hamiltonian in the line hamiltonian_isa = hamiltonian.apply_layout(ansatz_isa.layout) hamiltonian_isa $\endgroup$ Commented Mar 20 at 23:35
  • $\begingroup$ I have updated the answer with the python version I'm using, and also fixed the hamiltonian_isa issue. Although for the first error, maybe don't use the code: import warnings warnings.filterwarnings("ignore") $\endgroup$ Commented Mar 21 at 0:59
  • $\begingroup$ Thank you. The job was submitted to IBM and I could obtain some result. Since my python interpreter crashed due to long wait time (after submitting the job) I used the suggested way in IBM quantum to retreive the results. but im not sure how to interpret that result. Can you help me translate that results to the original decision variables. I will add the code in the next comment: $\endgroup$ Commented Mar 22 at 0:55
  • $\begingroup$ ``` res = minimize(cost_func, x0, args=(ansatz_isa, hamiltonian_isa, estimator), method="COBYLA") ``` ``` from qiskit_ibm_runtime import QiskitRuntimeService service = QiskitRuntimeService( channel='ibm_quantum', instance='ibm-q/open/main', token='' ) job = service.job('') job_result = job.result() for idx, pub_result in enumerate(job_result): print(f"Expectation values for pub {idx}: {pub_result.data.evs}") ``` the result this yeild was : Expectation values for pub 0: [-13.91533473] Im not sure what that interprets $\endgroup$ Commented Mar 22 at 0:56
1
$\begingroup$

Regarding the warning, I am not completely sure, but it could have to do with IBM's current migration towards the usage of Qiskit Runtime primitives (see here).

Regarding the error message that you are receiving. This is likely due to a mismatch in connectivity between the circuit you want to implement and the connectivity of the device you are using. For the case of QAOA, it is most likely that the underlying graph problem (which defines the terms in your cost Hamiltonian) is not a subgraph of the device graph. As a result, you are asking the hardware to perform entangling operations between qubits that are not connected and you receive this error.

The key to circumvent this problem is to transpile your circuit. This used to be done by IBM, but since March 1st it is required that you do so manually. One way is to use the pass manager as the error message says

from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

service = QiskitRuntimeService(channel="ibm_quantum",token="TOKEN")
backend = service.least_busy(min_num_qubits = 40,operational=True, simulator=False)

pm = generate_preset_pass_manager(optimization_level=1, target=backend.target)

your_transpiled_circuit = pm.run(your_circuit)

and then pass your_transpiled_circuit to Sampler or Estimator as you were doing before. Note that the pass manager has a lot of room for customization, depending on how you want your circuit to be transpiled. You can read more about the transpilation process here and about the pass manager funcitonalities here. If you do not care much about the details of the transpilation and just want to let IBM sort out for you the execution of your circuit, it is recommended that you set optimization_level = 3. Furthermore, I believe that you can also use the transpile function from IBM to perform the transpilation process in the following way

from qiskit import transpile

your_transpiled_circuit = transpile(your_circuit, optimization_level = 3,...)

Finally, it might become handy at some point to pass other device parameters, such as the basis gates (the local quantum operations used by the device) or the coupling map (underlying device geometry), to the pass manager or the transpiler. This information, and many other things, can be accessed from the backend in the following way

# Define backend
device = 'the_ibm_device'
backend = service.get_backend(device)

# Extract configuration
config = backend.configuration()

# Useful information
basis_gates = config.basis_gates
coupling_map = config.coupling_map
$\endgroup$

Not the answer you're looking for? Browse other questions tagged or ask your own question.