Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding the Max-k-Cut application #299

Closed
wants to merge 29 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
3bb2d53
Adding the Max-k-cut application
clausia Sep 23, 2021
57208a7
validate if it is possible to use the matplotlib library
clausia Dec 14, 2021
4db521a
minor fix (for library validations)
clausia Dec 14, 2021
11d6f96
Merge branch 'main' into max-k-cut-app
clausia Dec 16, 2021
5fa9126
Merge branch 'main' into max-k-cut-app
manoelmarques Dec 16, 2021
6cb7536
Merge branch 'main' into max-k-cut-app
manoelmarques Dec 16, 2021
9fb9458
Merge branch 'main' into max-k-cut-app
manoelmarques Dec 16, 2021
d457732
Merge branch 'main' into max-k-cut-app
clausia Dec 23, 2021
6408781
Merge branch 'main' into max-k-cut-app
clausia Jan 6, 2022
afdabab
Merge branch 'main' into max-k-cut-app
fedonman Jan 9, 2022
2ccae04
Get colors parameter out of init, using algorithm_globals
clausia Jan 10, 2022
63ba6b1
update copyright year
fedonman Jan 10, 2022
c42e061
Merge branch 'main' into max-k-cut-app
clausia Jan 14, 2022
82c67bd
Merge branch 'main' into max-k-cut-app
clausia Jan 20, 2022
6e64655
Merge branch 'main' into max-k-cut-app
clausia Jan 23, 2022
6bec1f9
Merge branch 'main' into max-k-cut-app
manoelmarques Jan 26, 2022
4aa90a8
Merge branch 'main' into max-k-cut-app
clausia Feb 7, 2022
c8a8107
Merge branch 'main' into max-k-cut-app
clausia Feb 9, 2022
a74c686
Merge branch 'main' into max-k-cut-app
clausia Feb 11, 2022
323ec1e
Merge branch 'main' into max-k-cut-app
clausia Feb 28, 2022
980df66
Merge branch 'main' into max-k-cut-app
clausia Mar 1, 2022
e696d23
Merge branch 'main' into max-k-cut-app
clausia Mar 14, 2022
647028f
Merge branch 'main' into max-k-cut-app
clausia Mar 28, 2022
c869735
Merge branch 'main' into max-k-cut-app
clausia Apr 3, 2022
5cf0eee
Merge branch 'main' into max-k-cut-app
clausia Apr 4, 2022
2997c45
Merge branch 'main' into max-k-cut-app
clausia Apr 10, 2022
91f390c
Merge branch 'main' into max-k-cut-app
clausia Apr 19, 2022
2d1e302
Merge branch 'main' into max-k-cut-app
clausia Apr 23, 2022
d19b1d1
Merge branch 'main' into max-k-cut-app
clausia May 3, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .pylintdict
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ catol
cartan
chu
chvátal
cmap
cobyla
coeff
coeffs
Expand Down Expand Up @@ -108,6 +109,7 @@ makefile
matplotlib
maxcut
maxfun
maxkcut
maxiter
mdl
minimizer
Expand Down Expand Up @@ -150,6 +152,7 @@ py
pxd
qaoa
qasm
qce
qiskit
qiskit's
qn
Expand All @@ -164,6 +167,8 @@ qubits
qubo
readme
representable
rgb
rgba
rhobeg
rhoend
rhs
Expand Down Expand Up @@ -195,6 +200,7 @@ subspaces
sys
subproblem
summands
tabi
tavernelli
terra
th
Expand All @@ -215,6 +221,8 @@ undirected
upperbound
variational
vartype
vmax
vmin
vqe
writelines
xixj
Expand Down
4 changes: 3 additions & 1 deletion docs/tutorials/09_application_classes.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
" - Given a graph, a depot node, and the number of vehicles (routes), find a set of routes such that each node is covered exactly once except the depot and the total distance of the routes is minimized.\n",
"11. Vertex cover problem\n",
" - Given an undirected graph, find a subset of nodes with the minimum size such that each edge has at least one endpoint in the subsets.\n",
"12. Max-k-Cut problem\n",
" - Given an undirected graph, find a partition of nodes into at most k subsets such that the total weight of the edges between the k subsets is maximized.\n",
"\n",
"The application classes for graph problems (`GraphOptimizationApplication`) provide a functionality to draw graphs of an instance and a result.\n",
"Note that you need to install `matplotlib` beforehand to utilize the functionality."
Expand Down Expand Up @@ -1518,4 +1520,4 @@
},
"nbformat": 4,
"nbformat_minor": 2
}
}
3 changes: 3 additions & 0 deletions qiskit_optimization/applications/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
GraphPartition
Knapsack
Maxcut
Maxkcut
NumberPartition
SetPacking
SKModel
Expand All @@ -56,6 +57,7 @@
from .graph_partition import GraphPartition
from .knapsack import Knapsack
from .max_cut import Maxcut
from .max_k_cut import Maxkcut
from .number_partition import NumberPartition
from .optimization_application import OptimizationApplication
from .set_packing import SetPacking
Expand All @@ -72,6 +74,7 @@
"GraphOptimizationApplication",
"Knapsack",
"Maxcut",
"Maxkcut",
"NumberPartition",
"OptimizationApplication",
"SetPacking",
Expand Down
247 changes: 247 additions & 0 deletions qiskit_optimization/applications/max_k_cut.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2021, 2022.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.


"""An application class for the Max-k-cut."""

from typing import List, Dict, Tuple, Optional, Union
import networkx as nx
import numpy as np
from docplex.mp.model import Model

from qiskit.utils import algorithm_globals
from qiskit_optimization.algorithms import OptimizationResult
from qiskit_optimization.problems.quadratic_program import QuadraticProgram
from qiskit_optimization.translators import from_docplex_mp
from .graph_optimization_application import GraphOptimizationApplication

try:
from matplotlib.pyplot import cm
from matplotlib.colors import to_rgba

_HAS_MATPLOTLIB = True
except ImportError:

_HAS_MATPLOTLIB = False


class Maxkcut(GraphOptimizationApplication):
"""Optimization application for the "max-k-cut" [1] problem based on a NetworkX graph.

References:
[1]: Z. Tabi et al.,
"Quantum Optimization for the Graph Coloring Problem with Space-Efficient Embedding"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This paper addresses a graph coloring problem and does not mention max-k-cut problem.
Could you refer an appropriate paper or page?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @t-imamichi, graph colouring problem is a decision variant of max-k-cut. Graph coloring problem is about checking if there "exists colouring of the nodes which make every edge connecting nodes with a different colour." Contrary, Max-K-Cut is about finding the colouring for which the number of such edges is maximized. You could say that graph colouring to max-K-Cut shows similar relation as SAT to MAX-SAT. For this reason, and the fact max cut is already used in the package we decided to choose max-k-cut name

Copy link
Collaborator

@t-imamichi t-imamichi Feb 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as the paper mentions only coloring problem, I don't think it's an appropriate citation of max-k-cut. It would confuse users. Why not rename the class "k-coloring problem" as the paper addresses?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moreover, there are many papers directly addressing max-k-cut problem. Why do you need to refer that paper (Tabi et al.) for max-k-cut class?

Copy link

@adamglos92 adamglos92 Feb 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The notion of feasibility changes. For Max-Cut every solution is feasible, for graph colouring only those with all edges connecting different nodes are feasible. This is obviously correct but inconsistent with max-cut used in the qiskit-optimization.
Since this is for optimization purposes mostly we found Max-K-Cut more accurate. But we can also move it to k-graph-coloring
We are not aware of older papers that propose the given formulation. If there is one, we're open to replacing the reference.

Copy link
Collaborator

@t-imamichi t-imamichi Feb 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mentioned

if there "exists colouring of the nodes which make every edge connecting nodes with a different colour."

But, coloring does not take care of edge weights. So, I don't think the optimal solution of coloring does not corresponds to the optimal solution to max-k-cut problem.

You must make a mixed integer programming model of max-k-cut. Here is the definition of k-cut value (though this page addresses minimization)
https://en.wikipedia.org/wiki/Minimum_k-cut

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may perhaps try to use an solution of coloring problem as a heuristic solution of max-k-cut problem?
If so, I ask you to make a proper mixed integer programming model of max-k-cut problem.
The model of application classes should be exact. Otherwise, even if we apply CPLEX or Gurobi to the model, we may not be able to obtain the optimal solution.

2020 IEEE International Conference on Quantum Computing and Engineering (QCE),
2020, pp. 56-62, doi: 10.1109/QCE49297.2020.00018.,
https://ieeexplore.ieee.org/document/9259934
"""

def __init__(
self,
graph: Union[nx.Graph, np.ndarray, List],
k: int,
) -> None:
"""
Args:
graph: A graph representing a problem. It can be specified directly as a
`NetworkX <https://networkx.org/>`_ graph,
or as an array or list format suitable to build out a NetworkX graph.
k: The number of colors
"""
super().__init__(graph=graph)
self._subsets_num = k
self._colors: Union[List[Tuple[float, float, float, float]], List[str]] = None
self._seed: int = None

def to_quadratic_program(self) -> QuadraticProgram:
"""Convert a Max-k-cut problem instance into a
:class:`~qiskit_optimization.problems.QuadraticProgram`

Returns:
The :class:`~qiskit_optimization.problems.QuadraticProgram` created
from the Max-k-cut problem instance.
"""
for w, v in self._graph.edges:
self._graph.edges[w, v].setdefault("weight", 1)

mdl = Model(name="Max-k-cut")
n = self._graph.number_of_nodes()
k = self._subsets_num
x = {(v, i): mdl.binary_var(name=f"x_{v}_{i}") for v in range(n) for i in range(k)}
first_penalty = mdl.sum_squares((1 - mdl.sum(x[v, i] for i in range(k)) for v in range(n)))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you make a model with a standard formulation of the problem? Please do not penalize constraints by yourself.
In the application classes, we make a model with a standard formulation. The conversion to QUBO will be done within algorithm classes.

second_penalty = mdl.sum(
mdl.sum(self._graph.edges[v, w]["weight"] * x[v, i] * x[w, i] for i in range(k))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this takes sum of edges whose end nodes are same color (i). Is it a correct k-cut value?
I think k-cut value is the sum of edges whose end nodes have different colors.
Anyways, could you refer an appropriate paper or page and make a model (obj func + constraints).

for v, w in self._graph.edges
)
objective = first_penalty + second_penalty
mdl.minimize(objective)

op = from_docplex_mp(mdl)
return op

def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[int]]:
"""Interpret a result as k lists of node indices

Args:
result : The calculated result of the problem

Returns:
k lists of node indices correspond to k node sets for the Max-k-cut
"""
x = self._result_to_x(result)
n = self._graph.number_of_nodes()
cut = [[] for i in range(self._subsets_num)] # type: List[List[int]]

n_selected = x.reshape((n, self._subsets_num))
for i in range(n):
node_in_subset = np.where(n_selected[i] == 1)[0] # one-hot encoding
if len(node_in_subset) != 0:
cut[node_in_subset[0]].append(i)

return cut

def _draw_result(
self,
result: Union[OptimizationResult, np.ndarray],
pos: Optional[Dict[int, np.ndarray]] = None,
) -> None:
"""Draw the result with colors

Args:
result : The calculated result for the problem
pos: The positions of nodes
"""
x = self._result_to_x(result)
nx.draw(self._graph, node_color=self._node_color(x), pos=pos, with_labels=True)

def _node_color(
self, x: np.ndarray
) -> Union[List[Tuple[float, float, float, float]], List[str]]:
# Return a list of colors for draw.

n = self._graph.number_of_nodes()

# k colors chosen (randomly or from cm.rainbow), or from given color list
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as doing things randomly there exists an random generator in algorithm_globals in Terra that we created to have a common generator that algorithms and applications would use so that a user could create reproducible results by seeding it. Using that, instead of whats done here, would allow a user to seed it and have the same colored drawing each time if they prefer - of course they could pass in their own colors too, but I think using the common generator is preferable.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now algorithm_globals is used

if self._colors is None:
if _HAS_MATPLOTLIB:
colors = cm.rainbow(np.linspace(0, 1, self._subsets_num))
else:
if self._seed:
algorithm_globals.random_seed = self._seed
colors = [
"#"
+ "".join(
[algorithm_globals.random.choice("0123456789ABCDEF") for i in range(6)]
)
for j in range(self._subsets_num)
]
else:
colors = self._colors

gray = to_rgba("lightgray") if _HAS_MATPLOTLIB else "lightgray"
node_colors = [gray for _ in range(n)]

n_selected = x.reshape((n, self._subsets_num))
for i in range(n):
node_in_subset = np.where(n_selected[i] == 1) # one-hot encoding
if len(node_in_subset[0]) != 0:
node_colors[i] = (
to_rgba(colors[node_in_subset[0][0]])
if _HAS_MATPLOTLIB
else colors[node_in_subset[0][0]]
)

return node_colors

@property
def k(self) -> int:
"""Getter of k

Returns:
The number of colors
"""
return self._subsets_num

@k.setter
def k(self, k: int) -> None:
"""Setter of k

Args:
k: The number of colors

Raises:
ValueError: if the size of the colors is different than the k parameter.
"""
self._subsets_num = k
if self._colors and len(self._colors) != self._subsets_num:
self._colors = None
raise ValueError(
f"Number of colors in the list is different than the parameter"
f" k = {self._subsets_num} specified for this problem,"
f" the colors have not been assigned"
)

@property
def colors(self) -> Union[List[Tuple[float, float, float, float]], List[str]]:
"""Getter of colors list

Returns:
The k size color list
"""
return self._colors

@colors.setter
def colors(self, colors: Union[List[Tuple[float, float, float, float]], List[str]]) -> None:
"""Setter of colors list
Colors list must be the same length as the k parameter. Color can be a string or rgb or
rgba tuple of floats from 0-1. If numeric values are specified, they will be mapped to
colors using the cmap and vmin, vmax parameters. See matplotlib colors docs for more
details (https://matplotlib.org/stable/gallery/color/named_colors.html).

Examples:
[[0.0, 0.5, 0.0, 1.0], [1.0, 0.0, 0.0, 1.0], ...]
["g", "r", "b", ...]
["cyan", "purple", ...]

Args:
colors: The k size color list

Raises:
ValueError: if the size of the colors is different than the k parameter.
"""
if colors and len(colors) == self._subsets_num:
self._colors = colors
else:
self._colors = None
raise ValueError(
f"Number of colors in the list is different than the parameter"
f" k = {self._subsets_num} specified for this problem,"
f" the colors have not been assigned"
)

@property
def seed(self) -> int:
"""Getter of seed

Returns:
The seed value for random generation of colors
"""
return self._seed

@seed.setter
def seed(self, seed: int) -> None:
"""Setter of seed

Args:
seed: The seed value for random generation of colors
"""
self._seed = seed
14 changes: 14 additions & 0 deletions releasenotes/notes/add-max-k-cut-app-7e451a5993171175.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
features:
- |
Adding the Max-k-Cut application :class:`qiskit_optimization.applications.Maxkcut`.

Problem: Given an undirected graph, find a partition of nodes into at most k subsets such
that the total weight of the edges between the k subsets is maximized.

To solve this problem, the space-efficient quantum optimization representation (or encoding)
for the graph coloring problem proposed in [1] is used.

[1]: Z. Tabi et al., "Quantum Optimization for the Graph Coloring Problem with Space-Efficient
Embedding," 2020 IEEE International Conference on Quantum Computing and Engineering (QCE),
2020, pp. 56-62, doi: 10.1109/QCE49297.2020.00018., https://ieeexplore.ieee.org/document/9259934
Loading