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

[WIP] Replace the internal networkx with retworkx #150

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
18 changes: 10 additions & 8 deletions qiskit_optimization/applications/clique.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@
# that they have been altered from the originals.

"""An application class for the clique."""
from typing import Optional, Union, List, Dict
from typing import Dict, List, Optional, Union

import networkx as nx
import numpy as np
import retworkx as rx
from docplex.mp.model import Model
from retworkx.visualization import mpl_draw

from qiskit_optimization.algorithms import OptimizationResult
from qiskit_optimization.problems.quadratic_program import QuadraticProgram

from .graph_optimization_application import GraphOptimizationApplication


Expand All @@ -31,7 +34,7 @@ class Clique(GraphOptimizationApplication):
"""

def __init__(
self, graph: Union[nx.Graph, np.ndarray, List], size: Optional[int] = None
self, graph: Union[rx.PyGraph, nx.Graph, np.ndarray, List], size: Optional[int] = None
) -> None:
"""
Args:
Expand All @@ -53,12 +56,11 @@ def to_quadratic_program(self) -> QuadraticProgram:
The :class:`~qiskit_optimization.problems.QuadraticProgram` created
from the clique problem instance.
"""
complement_g = nx.complement(self._graph)

complement_g = rx.complement(self._graph)
mdl = Model(name="Clique")
n = self._graph.number_of_nodes()
n = self._graph.num_nodes()
x = {i: mdl.binary_var(name="x_{0}".format(i)) for i in range(n)}
for w, v in complement_g.edges:
for w, v in complement_g.edge_list():
mdl.add_constraint(x[w] + x[v] <= 1)
if self.size is None:
mdl.maximize(mdl.sum(x[i] for i in x))
Expand Down Expand Up @@ -96,13 +98,13 @@ def _draw_result(
pos: The positions of nodes
"""
x = self._result_to_x(result)
nx.draw(self._graph, node_color=self._node_colors(x), pos=pos, with_labels=True)
mpl_draw(self._graph, node_color=self._node_colors(x), pos=pos, with_labels=True)

def _node_colors(self, x: np.ndarray) -> List[str]:
# Return a list of strings for draw.
# Color a node with red when the corresponding variable is 1.
# Otherwise color it with dark gray.
return ["r" if x[node] else "darkgrey" for node in self._graph.nodes]
return ["r" if x[node] else "darkgrey" for node in self._graph.nodes()]

@property
def size(self) -> int:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@
"""An abstract class for graph optimization application classes."""

from abc import abstractmethod
from typing import Union, Optional, Dict, List
from typing import Dict, Optional, Union, List

import networkx as nx
import numpy as np
from qiskit.exceptions import MissingOptionalLibraryError
import retworkx as rx
from retworkx.visualization import mpl_draw

from qiskit.exceptions import MissingOptionalLibraryError
from qiskit_optimization.algorithms import OptimizationResult

from .optimization_application import OptimizationApplication

try:
Expand All @@ -35,14 +38,21 @@ class GraphOptimizationApplication(OptimizationApplication):
An abstract class for graph optimization applications.
"""

def __init__(self, graph: Union[nx.Graph, np.ndarray, List]) -> None:
def __init__(self, graph: Union[rx.PyGraph, nx.Graph, np.ndarray, List]) -> None:
"""
Args:
graph: A graph representing a problem. It can be specified directly as a
NetworkX Graph, or as an array or list if format suitable to build out a NetworkX graph.
"""
# The view of the graph is stored which means the graph can not be changed.
self._graph = nx.Graph(graph).copy(as_view=True)
if isinstance(graph, rx.PyGraph):
self._graph = graph.copy()
elif isinstance(graph, nx.Graph):
self._graph = rx.networkx_converter(graph)
elif isinstance(graph, (np.ndarray, List)):
self._graph = rx.PyGraph.from_adjacency_matrix(graph)
t-imamichi marked this conversation as resolved.
Show resolved Hide resolved
else:
raise TypeError("graph should be rx.PyGraph or nx.Graph")

def draw(
self,
Expand All @@ -66,7 +76,7 @@ def draw(
)

if result is None:
nx.draw(self._graph, pos=pos, with_labels=True)
mpl_draw(self._graph, pos=pos, with_labels=True)
else:
self._draw_result(result, pos)

Expand All @@ -85,7 +95,7 @@ def _draw_result(
pass

@property
def graph(self) -> nx.Graph:
def graph(self) -> rx.PyGraph:
"""Getter of the graph

Returns:
Expand All @@ -94,7 +104,7 @@ def graph(self) -> nx.Graph:
return self._graph

@staticmethod
def random_graph(num_nodes: int, num_edges: int, seed: Optional[int] = None) -> nx.Graph:
def random_graph(num_nodes: int, num_edges: int, seed: Optional[int] = None) -> rx.PyGraph:
"""

Args:
Expand All @@ -105,5 +115,5 @@ def random_graph(num_nodes: int, num_edges: int, seed: Optional[int] = None) ->
Returns:
A random graph of NetworkX
"""
graph = nx.gnm_random_graph(num_nodes, num_edges, seed)
graph = rx.undirected_gnm_random_graph(num_nodes, num_edges, seed)
return graph
17 changes: 9 additions & 8 deletions qiskit_optimization/applications/graph_partition.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@

from typing import Dict, List, Optional, Union

import networkx as nx
import retworkx as rx
from retworkx.visualization import mpl_draw
import numpy as np
from docplex.mp.model import Model

Expand All @@ -39,13 +40,13 @@ def to_quadratic_program(self) -> QuadraticProgram:
from the graph partition instance.
"""
mdl = Model(name="Graph partition")
n = self._graph.number_of_nodes()
n = self._graph.num_nodes()
x = {i: mdl.binary_var(name="x_{0}".format(i)) for i in range(n)}
for w, v in self._graph.edges:
self._graph.edges[w, v].setdefault("weight", 1)
for i, j in self._graph.edge_list():
self._graph.get_edge_data(i, j).setdefault("weight", 1)
objective = mdl.sum(
self._graph.edges[i, j]["weight"] * (x[i] + x[j] - 2 * x[i] * x[j])
for i, j in self._graph.edges
self._graph.get_edge_data(i, j)["weight"] * (x[i] + x[j] - 2 * x[i] * x[j])
for i, j in self._graph.edge_list()
)
mdl.minimize(objective)
mdl.add_constraint(mdl.sum([x[i] for i in x]) == n // 2)
Expand Down Expand Up @@ -83,10 +84,10 @@ def _draw_result(
pos: The positions of nodes
"""
x = self._result_to_x(result)
nx.draw(self._graph, node_color=self._node_colors(x), pos=pos, with_labels=True)
mpl_draw(self._graph, node_color=self._node_colors(x), pos=pos, with_labels=True)

def _node_colors(self, x: np.ndarray) -> List[str]:
# Return a list of strings for draw.
# Color a node with red when the corresponding variable is 1.
# Otherwise color it with blue.
return ["r" if x[node] else "b" for node in self._graph.nodes]
return ["r" if x[node] else "b" for node in self._graph.nodes()]
18 changes: 8 additions & 10 deletions qiskit_optimization/applications/max_cut.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"""An application class for the Max-cut."""

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

Expand All @@ -40,15 +40,13 @@ def to_quadratic_program(self) -> QuadraticProgram:
from the Max-cut problem instance.
"""
mdl = Model(name="Max-cut")
x = {
i: mdl.binary_var(name="x_{0}".format(i)) for i in range(self._graph.number_of_nodes())
}
for w, v in self._graph.edges:
self._graph.edges[w, v].setdefault("weight", 1)
x = {i: mdl.binary_var(name="x_{0}".format(i)) for i in range(self._graph.num_nodes())}
for i, j in self._graph.edge_list():
self._graph.get_edge_data(i, j).setdefault("weight", 1)
objective = mdl.sum(
self._graph.edges[i, j]["weight"] * x[i] * (1 - x[j])
+ self._graph.edges[i, j]["weight"] * x[j] * (1 - x[i])
for i, j in self._graph.edges
self._graph.get_edge_data(i, j)["weight"] * x[i] * (1 - x[j])
+ self._graph.get_edge_data(i, j)["weight"] * x[j] * (1 - x[i])
for i, j in self._graph.edge_list()
)
mdl.maximize(objective)
op = QuadraticProgram()
Expand All @@ -67,7 +65,7 @@ def _draw_result(
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)
mpl_draw(self._graph, node_color=self._node_color(x), pos=pos, with_labels=True)

def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[int]]:
"""Interpret a result as two lists of node indices
Expand Down
14 changes: 7 additions & 7 deletions qiskit_optimization/applications/stable_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from typing import Dict, List, Optional, Union

import networkx as nx
from retworkx.visualization import mpl_draw
import numpy as np
from docplex.mp.model import Model

Expand All @@ -40,12 +40,12 @@ def to_quadratic_program(self) -> QuadraticProgram:
from the stable set instance.
"""
mdl = Model(name="Stable set")
n = self._graph.number_of_nodes()
n = self._graph.num_nodes()
x = {i: mdl.binary_var(name="x_{0}".format(i)) for i in range(n)}
for w, v in self._graph.edges:
self._graph.edges[w, v].setdefault("weight", 1)
for w, v in self._graph.edge_list():
self._graph.get_edge_data(w, v).setdefault("weight", 1)
objective = mdl.sum(x[i] for i in x)
for w, v in self._graph.edges:
for w, v in self._graph.edge_list():
mdl.add_constraint(x[w] + x[v] <= 1)
mdl.maximize(objective)
op = QuadraticProgram()
Expand Down Expand Up @@ -80,10 +80,10 @@ def _draw_result(
pos: The positions of nodes
"""
x = self._result_to_x(result)
nx.draw(self._graph, node_color=self._node_colors(x), pos=pos, with_labels=True)
mpl_draw(self._graph, node_color=self._node_colors(x), pos=pos, with_labels=True)

def _node_colors(self, x: np.ndarray):
# Return a list of strings for draw.
# Color a node with red when the corresponding variable is 1.
# Otherwise color it with dark gray.
return ["r" if x[node] == 1 else "darkgrey" for node in self._graph.nodes]
return ["r" if x[node] == 1 else "darkgrey" for node in self._graph.nodes()]
29 changes: 17 additions & 12 deletions qiskit_optimization/applications/tsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
"""An application class for Traveling salesman problem (TSP)."""
from typing import Dict, List, Optional, Union

import networkx as nx
import numpy as np
import retworkx as rx
from docplex.mp.model import Model
from retworkx.visualization import mpl_draw

from qiskit.utils import algorithm_globals

from ..algorithms import OptimizationResult
from ..exceptions import QiskitOptimizationError
from ..problems.quadratic_program import QuadraticProgram
Expand All @@ -42,14 +44,14 @@ def to_quadratic_program(self) -> QuadraticProgram:
from the traveling salesman problem instance.
"""
mdl = Model(name="TSP")
n = self._graph.number_of_nodes()
n = self._graph.num_nodes()
x = {
(i, k): mdl.binary_var(name="x_{0}_{1}".format(i, k))
for i in range(n)
for k in range(n)
}
tsp_func = mdl.sum(
self._graph.edges[i, j]["weight"] * x[(i, k)] * x[(j, (k + 1) % n)]
self._graph.get_edge_data(i, j)["weight"] * x[(i, k)] * x[(j, (k + 1) % n)]
for i in range(n)
for j in range(n)
for k in range(n)
Expand Down Expand Up @@ -101,11 +103,11 @@ def _draw_result(
pos: The positions of nodes
"""
x = self._result_to_x(result)
nx.draw(self._graph, with_labels=True, pos=pos)
nx.draw_networkx_edges(
mpl_draw(self._graph, with_labels=True, pos=pos)
mpl_draw(
self._graph,
pos,
edgelist=self._edgelist(x),
edge_list=self._edgelist(x),
width=8,
alpha=0.5,
edge_color="tab:red",
Expand All @@ -132,12 +134,15 @@ def create_random_instance(n: int, low: int = 0, high: int = 100, seed: int = No
"""
if seed:
algorithm_globals.random_seed = seed
coord = algorithm_globals.random.uniform(low, high, (n, 2))
pos = {i: (coord_[0], coord_[1]) for i, coord_ in enumerate(coord)}
graph = nx.random_geometric_graph(n, np.hypot(high - low, high - low) + 1, pos=pos)
for w, v in graph.edges:
delta = [graph.nodes[w]["pos"][i] - graph.nodes[v]["pos"][i] for i in range(2)]
graph.edges[w, v]["weight"] = np.rint(np.hypot(delta[0], delta[1]))
dim = 2
pos = algorithm_globals.random.uniform(low, high, (n, dim))
graph = rx.random_geometric_graph(n, np.hypot(high - low, high - low) + 1, pos=pos)
for i, j in graph.edge_list():
delta = [
graph.get_node_data(i)["pos"][d] - graph.get_node_data(j)["pos"][d]
for d in range(dim)
]
graph.update_edge(i, j, {"weight": np.rint(np.hypot(delta[0], delta[1]))})
return Tsp(graph)

@staticmethod
Expand Down
Loading