Skip to main contentIBM Quantum Documentation Preview
This is a preview build of IBM Quantum® documentation. Refer to quantum.cloud.ibm.com/docs for the official documentation.

Get started with Iskay Quantum Optimizer

See the API reference

Note
  • Qiskit Functions are an experimental feature available only to IBM Quantum® Premium Plan, Flex Plan, and On-Prem (via IBM Quantum Platform API) Plan users. They are in preview release status and subject to change.

For an overview of this function, see the Iskay Quantum Optimizer documentation home.


Set up

In this documentation, we will go through the steps of using the Iskay Quantum Optimizer. In the process we will quickly show how to load the function from the catalog and how to convert your problem to a valid input, all while showing how you can experiment with different optional parameters.

For a more detailed example, please check out the tutorial Solve the Market Split problem with Kipu Quantum's Iskay Quantum Optimizer, where we work through the whole process of using Iskay Solver to address the Market Split problem, which represents a real-world resource allocation challenge where markets must be partitioned into balanced sales regions to meet exact demand targets.

Authenticate using your API key, found on the IBM Quantum Platform dashboard, and select the Qiskit Function as follows:

Note

The following code assumes that you have saved your credentials. If you have not, follow the instructions in save your IBM Cloud account to authenticate with your API key.

from qiskit_ibm_catalog import QiskitFunctionsCatalog

catalog = QiskitFunctionsCatalog(
    channel="ibm_quantum_platform",
    instance="INSTANCE_CRN",
    # For `token`, use the 44-character API_KEY you created
    # and saved from the IBM Quantum Platform Home dashboard
    token="YOUR_API_KEY",
)

# verify that you have access to the function
catalog.list()

Output:

[QiskitFunction(qunova/hivqe-chemistry),
 QiskitFunction(global-data-quantum/quantum-portfolio-optimizer),
 QiskitFunction(algorithmiq/tem),
 QiskitFunction(qedma/qesem),
 QiskitFunction(multiverse/singularity),
 QiskitFunction(ibm/circuit-function),
 QiskitFunction(q-ctrl/optimization-solver),
 QiskitFunction(colibritd/quick-pde),
 QiskitFunction(q-ctrl/performance-management),
 QiskitFunction(kipu-quantum/iskay-quantum-optimizer)]
# Access Function
optimizer = catalog.load("kipu-quantum/iskay-quantum-optimizer")

Custom configuration example

Here's how you might configure Iskay with different settings:

custom_options = {
    "shots": 15_000,  # Higher shot count for better statistics
    "num_iterations": 12,  # More iterations for solution refinement
    "preprocessing_level": 1,  # Light preprocessing for problem simplification
    "postprocessing_level": 2,  # Maximum postprocessing for solution quality
    "transpilation_level": 3,  # Use higher transpilation level to optimize circuit
    "seed_transpiler": 42,  # Fixed seed for reproducible results
    "job_tags": ["custom_config"],  # Custom tracking tags
}

Seed optimization: Note that seed_transpiler is set to None by default. This enables the transpiler's automatic optimization process. When None, the system will start a trial with multiple seeds and select the one that produces the best circuit depth, leveraging the full power of the max_trials parameter for each transpilation level.

Transpilation level performance: Increasing the number of max_trials with higher values for transpilation_level will unavoidably increase the transpilation time, but it might not always change the final circuit - this depends heavily on the specific circuit structure and complexity. For some circuits/problems, however, the difference between 10 trials (level 1) and 50 trials (level 5) can be dramatic, so exploring these parameters might be the key to successfully finding a solution.


Example 1: Simple cost function

Consider the cost function in spin formulation:

C(x0,x1,x2,x3,x4)=1+1.5x0+2x1+1.3x2+2.5x0x3+3.5x1x4+4x0x1x2C(x_0, x_1, x_2, x_3, x_4) = 1 + 1.5x_0 + 2x_1 + 1.3x_2 + 2.5x_0x_3 + 3.5x_1x_4 + 4x_0x_1x_2

where (x0,...,x4)∈{−1,1}5(x_0, ..., x_4) \in \{-1, 1\}^5.

The solution to this simple cost function is

(x0,x1,x2,x3,x4)=(−1,−1,−1,1,1)(x_0, x_1, x_2, x_3, x_4) = (-1, -1, -1, 1, 1)

with minimum value C∗=−6C^{*} = -6

1. Create the objective function

We start by creating a dictionary with the coefficients of the objective function as follows:

objective_func = {
    "()": 1,
    "(0,)": 1.5,
    "(1,)": 2,
    "(2,)": 1.3,
    "(0, 3)": 2.5,
    "(1, 4)": 3.5,
    "(0, 1, 2)": 4,
}

2. Run the Optimizer

We solve the problem by running the optimizer. Since (x0,...,x4)∈{−1,1}5(x_0, ..., x_4) \in \{-1, 1\}^5 we must set problem_type=spin.

# Setup options to run the optimizer
options = {"shots": 5000, "num_iterations": 5, "use_session": True}

arguments = {
    "problem": objective_func,
    "problem_type": "spin",
    "backend_name": backend_name,  # such as "ibm_fez"
    "options": options,
}

job = optimizer.run(**arguments)

# Print the ID so you can use it later, if necessary
print(job.job_id)

3. Retrieve the result

The solution of the optimization problem is provided directly from the optimizer.

print(job.result())

This will show a dictionary of the form:

{'solution': {'0': -1, '1': -1, '2': -1, '3': 1, '4': 1},
 'solution_info': {'bitstring': '11100',
  'cost': -13.8,
  'seed_transpiler': 42,
  'mapping': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4}},
 'prob_type': 'spin'}

Notice that the dictionary solution displays the result vector (x0,x1,x2,x3,x4)=(−1,−1,−1,1,1)(x_0, x_1, x_2, x_3, x_4) = (-1, -1, -1, 1, 1).


Example 2: MaxCut

Many graph problems like MaxCut or Maximum independent set are NP-hard problems and ideal candidates for testing quantum algorithms and hardware. This example demonstrates solving the MaxCut problem of a 3-regular graph with the Quantum Optimizer.

To run this example you must install the networkx package in addition to the qiskit-ibm-catalog. To install it, run the following command:

# %pip install networkx numpy

1. Create the objective function

Start by generating a random 3-regular graph. For this graph, we define the objective function of the MaxCut problem.

import networkx as nx

# Create a random 3-regular graph
G = nx.random_regular_graph(3, 10, seed=42)


# Create the objective function for MaxCut in Ising formulation
def graph_to_ising_maxcut(G):
    """
    Convert a NetworkX graph to an Ising Hamiltonian for the max-cut problem.
    Args:
        G (networkx.Graph): The input graph.
    Returns:
        dict: The objective function of the Ising model
    """
    # Initialize the linear and quadratic coefficients
    objective_func = {}
    # Populate the coefficients
    for i, j in G.edges:
        objective_func[f"({i}, {j})"] = 0.5
    return objective_func


objective_func = graph_to_ising_maxcut(G)

2. Run the Optimizer

Solve the problem by running the optimizer.

options = {"shots": 5000, "num_iterations": 5, "use_session": True}

arguments = {
    "problem": objective_func,
    "problem_type": "spin",
    "backend_name": backend_name,  # such as "ibm_fez"
    "options": options,
}

job = optimizer.run(**arguments)

3. Retrieve the result

Retrieve the result and map the solution bitstring back to the original graph nodes.

print(job.result())

The solution to the Maxcut problem is directly contained in the solution sub-dictionary of the result object

maxcut_solution = job.result()["solution"]

Example 3: Benchmark instances

The benchmark instances are available on GitHub: Kipu benchmark instances.

The instances can be loaded using the pygithub library. To install it, run the following command:

# %pip install pygithub

The paths for the benchmark instances are:

Maxcut:

  • 'maxcut/maxcut_regular_3_100_nodes_weighted.json'
  • 'maxcut/maxcut_regular_3_140_nodes_weighted.json'
  • 'maxcut/maxcut_regular_3_150_nodes_weighted.json'
  • 'maxcut/maxcut_regular_4_130_nodes_weighted.json'

HUBO:

  • 'HUBO/hubo1_marrakesh.json'
  • 'HUBO/hubo2_marrakesh.json'

To reproduce the performance of the benchmark for the HUBO instances, select the backend ibm_marrakesh and set direct_qubit_mapping to True in the options sub-dictionary.

The following example runs the Maxcut instance with 150 nodes.

from github import Github
import urllib
import json
import ast

repo = "Kipu-Quantum-GmbH/benchmark-instances"
path = "maxcut/maxcut_regular_3_150_nodes_weighted.json"
gh = Github()
repo = gh.get_repo(repo)
branch = "main"
file = repo.get_contents(urllib.parse.quote(path), ref=branch)

# load json file with benchmark problem
problem_json = json.loads(file.decoded_content)

# convert objective function to compatible format
objective_func = {
    key: ast.literal_eval(value) for key, value in problem_json.items()
}


# Setup configuration to run the optimizer
options = {
    "shots": 5_000,
    "num_iterations": 5,
    "use_session": True,
    "direct_qubit_mapping": False,
}

arguments = {
    "problem": objective_func,
    "problem_type": "spin",
    "backend_name": "<BACKEND-NAME>",
    "options": options,
}

job = optimizer.run(**arguments)

result = job.result()