1
0
Fork 0

Add generators: chain, hardcard.f, t.f, tt.f

This commit is contained in:
Joris van Rantwijk 2023-02-17 14:18:15 +01:00
parent 3f5d61d0e7
commit 71a7dfc9a3
3 changed files with 200 additions and 10 deletions

View File

@ -0,0 +1,62 @@
#!/usr/bin/env python3
"""
Generate a graph that belongs to a class of worst-case graphs
described by Gabow.
Reference: H. N. Gabow, "An efficient implementation of Edmonds'
algorithm for maximum matching on graphs", JACM 23
(1976), pp. 221-234.
Based on Fortran program "hardcard.f" by R. Bruce Mattingly, 1991.
Rewritten in Python by Joris van Rantwijk, 2023.
For the original Fortran code, see
http://archive.dimacs.rutgers.edu/pub/netflow/generators/matching/hardcard.f
Output to stdout in DIMACS edge format.
All edges have weight 1.
Input parameter: K
Number of vertices: N = 6*K
Number of edges: M = 8*K*K
The graph is constructed so that vertices 1 - 4*K form a complete subgraph.
For 1 <= I <= 2*K, vertex (2*I-1) is joined to vertex (4*K+I).
"""
import argparse
def main():
parser = argparse.ArgumentParser()
parser.description = "Generate a difficult graph"
parser.add_argument("k",
action="store",
type=int,
help="size parameter; N = 6*K, M = 4*K*K")
args = parser.parse_args()
if args.k < 1:
print("ERROR: K must be at least 1", file=sys.stderr)
sys.exit(1)
k = args.k
n = 6 * k
m = 8 * k * k
print(f"p edge {n} {m}")
for i in range(1, 4*k):
for j in range(i + 1, 4*k + 1):
print(f"e {i} {j} 1")
if i % 2 == 1:
j = 4 * k + (i + 1) // 2
print(f"e {i} {j} 1")
if __name__ == "__main__":
main()

View File

@ -70,12 +70,14 @@ def write_dimacs_graph(
def make_dense_slow_graph(n: int) -> list[tuple[int, int, int]]:
"""Generate a dense (not complete) graph with N vertices.
"""Generate a dense graph with N vertices.
The graph contains O(n**2) edges and triggers O(n**2) delta steps.
N must be divisible by 4.
Number of edges = M = (N**2/16 + N/2).
Number of delta steps required to solve the matching = (M - 1).
Number of edges = M = N**2/16 + N/2
Number of delta steps required to solve the matching = M - 1
"""
assert n % 4 == 0
@ -118,10 +120,12 @@ def make_dense_slow_graph(n: int) -> list[tuple[int, int, int]]:
def make_sparse_slow_graph(n: int) -> list[tuple[int, int, int]]:
"""Generate a sparse graph with N vertices.
The graph contains just O(n) edges but still triggers O(n**2) delta steps.
N must be 4 modulo 8.
Number of edges = M = (5/4 * N - 3).
Number of delta steps required to solve the matching ~ (N**2 / 16).
Number of edges = M = 5/4 * N - 3
Number of delta steps required to solve the matching = N**2/16 + 3/4*N - 3
"""
assert n >= 12
@ -233,20 +237,40 @@ def make_sparse_slow_graph(n: int) -> list[tuple[int, int, int]]:
return edges
def make_chain_graph(n: int) -> list[tuple[int, int, int]]:
"""Generate a graph with N vertices connected into a simple chain.
The graph contains O(n) edges and triggers O(n) delta steps.
To force a large number of delta steps, N must be even.
Number of edges = M = N - 1
Number of delta steps required to solve the matching = N - 2
"""
assert n >= 2
edges: list[tuple[int, int, int]] = []
for i in range(n - 1):
w = n + i % 2
edges.append((i, i + 1, w))
return edges
def main() -> int:
"""Main program."""
parser = argparse.ArgumentParser()
parser.description = "Generate a difficult graph."
parser.add_argument("--structure",
action="store",
choices=("sparse", "dense"),
default="sparse",
help="choose graph structure")
parser.add_argument("--check",
action="store_true",
help="solve the matching and count delta steps")
parser.add_argument("structure",
action="store",
choices=("sparse", "dense", "chain"),
help="graph structure")
parser.add_argument("n",
action="store",
type=int,
@ -283,6 +307,18 @@ def main() -> int:
edges = make_dense_slow_graph(args.n)
elif args.structure == "chain":
if args.n < 2:
print("ERROR: Number of vertices must be >= 2", file=sys.stderr)
return 1
if args.n % 2 != 0:
print("ERROR: Number of vertices must be even", file=sys.stderr)
return 1
edges = make_chain_graph(args.n)
else:
assert False

View File

@ -0,0 +1,92 @@
#!/usr/bin/env python3
r"""
Generate a graph that consists of interconnected triangles.
The graph is a chain of triangles, connected either at 1 vertex
or at 3 vertices.
Example of triangles connected at one vertex:
[1]-------[4] [7] [10]-------[13]
| \ | \ | \ / | | \
| [3] | [6] | [9]---[12] | | [15]
| / | / | / \ | | /
[2] [5]-------[8] [11] [14]-----
Example of triangles connected at 3 vertices:
[1]-------[4]-------[7]-------[10]-------[13]
| \ | \ | \ | \ | \
| [3]----|--[6]----|--[9]----|--[12]----|--[15]
| / | / | / | / | /
[2]-------[5]-------[8]-------[11]-------[14]
Based on Fortran programs "t.f" and "tt.f"
by N. Ritchey and B. Mattingly, Youngstown State University, 1991.
Rewritten in Python by Joris van Rantwijk, 2023.
For the original Fortran code, see
http://archive.dimacs.rutgers.edu/pub/netflow/generators/matching/t.f
http://archive.dimacs.rutgers.edu/pub/netflow/generators/matching/tt.f
Output to stdout in DIMACS edge format.
All edges have weight 1.
Input parameter: K = number of triangles
Input parameter: C = 1 to connect triangles by 1 corner
C = 3 to connect triangles by 3 corners
Number of vertices: N = 3*K
Number of edges: M = 3*K + C*(K-1)
"""
import argparse
def main():
parser = argparse.ArgumentParser()
parser.description = (
"Generate a graph that consists of interconnected triangles.")
parser.add_argument("k",
action="store",
type=int,
help="size parameter; N = 3*K, M = 3*K+C*(K-1)")
parser.add_argument("c",
action="store",
type=int,
choices=(1, 3),
help="number of corners to connect")
args = parser.parse_args()
if args.k < 1:
print("ERROR: K must be at least 1", file=sys.stderr)
sys.exit(1)
k = args.k
n = 3 * k
m = 3 * k + args.c * (k - 1)
print(f"p edge {n} {m}")
for i in range(k):
x = 3 * i + 1
print(f"e {x} {x+1} 1")
print(f"e {x} {x+2} 1")
print(f"e {x+1} {x+2} 1")
if args.c == 1:
for i in range(k - 1):
x = 3 * i + i % 3 + 1
print(f"e {x} {x+3} 1")
elif args.c == 3:
for x in range(1, 3 * k - 2):
print(f"e {x} {x+3} 1")
if __name__ == "__main__":
main()