Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Firstly, you should create the database:
graphreveal create-database
```

This process should take less than two seconds and will create a database of graphs with an order no greater than 7. To use a larger database, add the `--n 8` or `--n 9` flag to this command (it should take no more than half an hour).
This process should take less than a second and will create a database of graphs with order no greater than 7. To use a larger database, add the `--n 8` or `--n 9` flag to this command (it should take no more than a couple of minutes).

### Some examples

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ license = "MIT"
requires-python = ">=3.10"
dependencies = [
"antlr4-python3-runtime>=4.13.2",
"igraph>=1.0.0",
"networkx>=3.4.2",
"platformdirs>=4.3.6",
"rich>=13.9.4",
Expand Down
2 changes: 1 addition & 1 deletion src/graphreveal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from platformdirs import user_data_dir

__version__ = "1.2.0"
__version__ = "1.3.0"

DATABASE_PATH = os.path.join(
user_data_dir(appname="graphreveal", appauthor="graphreveal"), "graphs.db"
Expand Down
20 changes: 11 additions & 9 deletions src/graphreveal/db_creator/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import sqlite3

import igraph as ig
import networkx as nx
from rich.progress import track

Expand Down Expand Up @@ -45,20 +46,21 @@ def create_db(max_n):
all_graphs += f.read().strip().split("\n")

for graph_g6 in track(all_graphs, description="Creating the database"):
graph = nx.from_graph6_bytes(str.encode(graph_g6))
graph_nx = nx.from_graph6_bytes(str.encode(graph_g6))
graph = ig.Graph.from_networkx(graph_nx)
cur.execute(
"INSERT INTO graphs VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
[
graph_g6,
graph.number_of_nodes(),
graph.number_of_edges(),
nx.is_forest(graph),
nx.is_bipartite(graph),
nx.is_eulerian(graph),
graph.vcount(),
graph.ecount(),
graph.is_acyclic(),
graph.is_bipartite(),
util.is_eulerian(graph),
util.is_hamiltonian(graph),
nx.is_planar(graph),
len(list(nx.biconnected_components(graph))),
nx.number_connected_components(graph),
nx.is_planar(graph_nx),
len(graph.biconnected_components()),
len(graph.connected_components()),
util.max_degree(graph),
util.min_degree(graph),
],
Expand Down
65 changes: 38 additions & 27 deletions src/graphreveal/db_creator/util.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,65 @@
import networkx as nx
import igraph as ig


def _find_closure(G: nx.Graph) -> nx.Graph:
def _find_closure(G: ig.Graph) -> ig.Graph:
"""Returns a graph cl(G) defined as in Bondy-Chvátal Theorem."""
G = G.copy()
n = G.number_of_nodes()
n = G.vcount()
edges_to_add = []
for v in G.nodes:
for u in G.nodes - G.neighbors(v) - {v}:
if G.degree[v] + G.degree[u] >= n:
edges_to_add.append((v, u))
for v in range(n):
for u in range(v + 1, n):
if not G.are_adjacent(u, v):
if G.degree(v) + G.degree(u) >= n:
edges_to_add.append((v, u))
Comment thread
mdbrnowski marked this conversation as resolved.

while edges_to_add:
v, u = edges_to_add.pop()
G.add_edge(v, u)
for w in G.nodes - G.neighbors(v) - {v}:
if G.degree[w] + G.degree[v] >= n:
edges_to_add.append((w, v))
for w in G.nodes - G.neighbors(u) - {u}:
if G.degree[w] + G.degree[u] >= n:
edges_to_add.append((w, u))
if not G.are_adjacent(v, u): # check if edge already exists
G.add_edges([(v, u)])
for w in range(n):
if w != v and not G.are_adjacent(w, v):
if G.degree(w) + G.degree(v) >= n:
edges_to_add.append((w, v))
for w in range(n):
if w != u and not G.are_adjacent(w, u):
if G.degree(w) + G.degree(u) >= n:
edges_to_add.append((w, u))

return G


def is_hamiltonian(G: nx.Graph) -> bool:
def is_hamiltonian(G: ig.Graph) -> bool:
"""Checks whether a graph G is hamiltonian or not."""
n = G.number_of_nodes()
n = G.vcount()
if n == 1:
return True
if n == 2:
return False
if not nx.is_connected(G) or nx.is_tree(G):
if not G.is_connected():
return False
if not nx.is_biconnected(G):
if G.ecount() == n - 1: # is a tree
return False
if len(G.articulation_points()) > 0: # is not biconnected
return False

cl_G = _find_closure(G)
if (
cl_G.number_of_edges()
== cl_G.number_of_nodes() * (cl_G.number_of_nodes() - 1) / 2
):
if cl_G.ecount() == n * (n - 1) / 2: # is a complete graph
return True

return nx.isomorphism.GraphMatcher(G, nx.cycle_graph(n)).subgraph_is_monomorphic()
cycle = ig.Graph.Ring(n)
return G.subisomorphic_vf2(cycle)


def is_eulerian(graph: ig.Graph) -> bool:
"""Checks whether a graph has an Eulerian circuit."""
if not graph.is_connected():
return False
return all(degree % 2 == 0 for degree in graph.degree())


def max_degree(graph: nx.Graph) -> int:
return max(d for _, d in graph.degree())
def max_degree(graph: ig.Graph) -> int:
return max(graph.degree())


def min_degree(graph: nx.Graph) -> int:
return min(d for _, d in graph.degree())
def min_degree(graph: ig.Graph) -> int:
return min(graph.degree())
Loading