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 3 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
3 changes: 2 additions & 1 deletion qiskit_optimization/applications/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
"""

from .clique import Clique
from .exact_cover import ExactCover

# from .exact_cover import ExactCover
from .graph_optimization_application import GraphOptimizationApplication
from .graph_partition import GraphPartition
from .knapsack import Knapsack
Expand Down
20 changes: 10 additions & 10 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 @@ -30,9 +33,7 @@ class Clique(GraphOptimizationApplication):
https://en.wikipedia.org/wiki/Clique_(graph_theory)
"""

def __init__(
self, graph: Union[nx.Graph, np.ndarray, List], size: Optional[int] = None
) -> None:
def __init__(self, graph: Union[rx.PyGraph, nx.Graph], size: Optional[int] = None) -> None:
"""
Args:
graph: A graph representing a clique problem. It can be specified directly as a
Expand All @@ -53,12 +54,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 +96,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

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,19 @@ 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]) -> None:
t-imamichi marked this conversation as resolved.
Show resolved Hide resolved
"""
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)
else:
raise TypeError("graph should be rx.PyGraph or nx.Graph")

def draw(
self,
Expand All @@ -66,7 +74,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 +93,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 +102,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 +113,14 @@ 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

@staticmethod
def random_geometric_graph(
num_nodes: int, radius: float, seed: Optional[int] = None
) -> rx.PyGraph:
""" """
graph = rx.PyGraph()
graph.add_nodes_from(range(num_nodes))
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
2 changes: 1 addition & 1 deletion 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
import retworkx as nx
import numpy as np
from docplex.mp.model import Model

Expand Down
30 changes: 20 additions & 10 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,8 +103,8 @@ 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),
Expand Down Expand Up @@ -132,12 +134,20 @@ 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))
dim = 2
coord = algorithm_globals.random.uniform(low, high, (n, dim))
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]))
graph = rx.PyGraph()
graph.add_nodes_from([{"pos": pos[i]} for i in range(n)])
#for i, coord in pos.items():
# graph.get_node_data(i)["pos"] = coord
for i in range(n):
for j in range(i + 1, n):
delta = [
graph.get_node_data(i)["pos"][d] - graph.get_node_data(j)["pos"][d]
for d in range(dim)
]
graph.add_edge(i, j, {"weight": np.rint(np.hypot(*delta))})
return Tsp(graph)

@staticmethod
Expand Down
18 changes: 10 additions & 8 deletions qiskit_optimization/applications/vehicle_routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
from typing import List, Dict, Union, Optional

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 @@ -34,7 +36,7 @@ class VehicleRouting(GraphOptimizationApplication):

def __init__(
self,
graph: Union[nx.Graph, np.ndarray, List],
graph: Union[rx.PyGraph, nx.Graph],
num_vehicles: int = 2,
depot: int = 0,
) -> None:
Expand All @@ -59,15 +61,15 @@ def to_quadratic_program(self) -> QuadraticProgram:
from the vehicle routing problem instance.
"""
mdl = Model(name="Vehicle routing")
n = self._graph.number_of_nodes()
n = self._graph.num_nodes()
x = {}
for i in range(n):
for j in range(n):
if i != j:
x[(i, j)] = mdl.binary_var(name="x_{0}_{1}".format(i, j))
mdl.minimize(
mdl.sum(
self._graph.edges[i, j]["weight"] * x[(i, j)]
self._graph.get_edge_data(i, j)["weight"] * x[(i, j)]
for i in range(n)
for j in range(n)
if i != j
Expand Down Expand Up @@ -113,7 +115,7 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[
A list of the routes for each vehicle
"""
x = self._result_to_x(result)
n = self._graph.number_of_nodes()
n = self._graph.num_nodes()
idx = 0
edge_list = []
for i in range(n):
Expand Down Expand Up @@ -158,8 +160,8 @@ def _draw_result(
import matplotlib.pyplot as plt

route_list = self.interpret(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(route_list),
Expand Down Expand Up @@ -240,8 +242,8 @@ def create_random_instance(
"""
random.seed(seed)
pos = {i: (random.randint(low, high), random.randint(low, high)) for i in range(n)}
graph = nx.random_geometric_graph(n, np.hypot(high - low, high - low) + 1, pos=pos)
graph = rx.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]))
graph.get_edge_data(w, v)["weight"] = np.rint(np.hypot(delta[0], delta[1]))
return VehicleRouting(graph, num_vehicle, depot)
Loading