Skip to content

Commit 7d2b6ab

Browse files
author
Yuma Ichikawa
committed
feat(bench): add COLOR DIMACS instances (anna, jean, queen8_12)
arXiv-2409.02135v2 Table 6 evaluates 12 specific Graph Coloring instances. Until now QQA4CO shipped only 9 of them through the procedural Mycielski + square-queen generators, leaving 3 real-world Knuth / non-square Queen graphs uncovered. - scripts/generate_coloring_instances.py now fetches anna.col, jean.col and queen8_12.col from Trick's canonical DIMACS COLOR mirror, parses them into NetworkX graphs, and emits them under data/coloring/dimacs/ with the paper's target num_colors (11, 10, 12). --skip-dimacs keeps the pure-offline rebuild path intact. - data/coloring/README.md documents the Table-6 mapping explicitly so the coverage contract is auditable. - root README coverage matrix is restructured to §5.1-§5.5 with accurate Table references, an explicit ER-[9000-11000] best_known caveat, and the new coloring-dimacs suite.
1 parent 2007c2a commit 7d2b6ab

3 files changed

Lines changed: 172 additions & 34 deletions

File tree

README.md

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -466,25 +466,41 @@ The original DISCS-only path (`make bench-discs-setup`,
466466
### arXiv-2409.02135v2 (PQQA) benchmark coverage matrix
467467

468468
Every benchmark from PQQA (Ichikawa and Arai 2024, arXiv:2409.02135v2,
469-
§5.1–§5.4) is now mirrored on the companion HF dataset
469+
§5.1–§5.5) is now mirrored on the companion HF dataset
470470
[`Yuma-Ichikawa/qqa4co-bench`](https://huggingface.co/datasets/Yuma-Ichikawa/qqa4co-bench)
471471
and reachable via `qqa bench-run --suite <id>` from the installed
472-
`qqa` CLI:
472+
`qqa` CLI. Rows map one-to-one to the problem tables in §5 of the paper:
473473

474-
| Paper section | Problem family | HF subset (`Yuma-Ichikawa/qqa4co-bench`) | `--suite <id>` | One-line command |
474+
| Paper section / table | Problem family | HF subset (`Yuma-Ichikawa/qqa4co-bench`) | `--suite <id>` | One-line command |
475475
| --- | --- | --- | --- | --- |
476-
| §5.1 | MIS on SATLIB (500 instances) | `mis/satlib/uf` | `mis-satlib-uf` | `qqa bench-run --suite mis-satlib-uf --instances 500` |
477-
| §5.1 | MIS on Erdos-Renyi (few / more budgets) | `mis/er/er_*` | `mis-er-small`, `mis-er-large` | `qqa bench-run --suite mis-er-small --instances 6` |
478-
| §5.1 | MIS on RRG `d=20, n=10^{4,5,6}` | `mis-rrg/d20_n{10000,100000,1000000}` | `mis-rrg-d20_n10000` (etc.) | `qqa bench-run --suite mis-rrg-d20_n10000 --instances 5` |
479-
| §5.1 | MIS on RRG `d=100, n=10^{4,5,6}` | `mis-rrg/d100_n{10000,100000,1000000}` | `mis-rrg-d100_n10000` (etc.) | `qqa bench-run --suite mis-rrg-d100_n10000 --instances 5` |
480-
| §5.2 | Max Clique (DISCS RB) | `maxclique/rb` | `maxclique-rb` | `qqa bench-run --suite maxclique-rb --instances 500` |
481-
| §5.2 | Max Clique (Twitter social) | `maxclique/twitter` | `maxclique-twitter` | `qqa bench-run --suite maxclique-twitter` |
482-
| §5.3 | Max Cut (DISCS Barabasi-Albert) | `maxcut/*` | `maxcut-ba-*`, `maxcut-er-*` | `qqa bench-run --suite maxcut-ba --instances 100` |
483-
| §5.3 | Max Cut (G-set, Helmberg-Rendl 2000) | `gset/standard` | `gset` | `qqa bench-run --suite gset --instances 71` |
484-
| §5.3 | Balanced k-way graph partition | `balanced/nets` | `balanced-partition-nets-*` | `qqa bench-run --suite balanced-partition-nets-MNIST` |
485-
| §5.3 | Normalized Cut | `normcut/nets` | `normcut-nets-*` | `qqa bench-run --suite normcut-nets-MNIST` |
486-
| §5.4 | 3D Edwards-Anderson (Gauss / bimodal, L=4,6,8) | `ea3d/*` | `ea3d-gauss-L4` (etc.) | `qqa bench-run --suite ea3d-gauss-L8 --instances 50` |
487-
| §5.4 | Graph Coloring (Myciel / queen / COLOR) | `coloring/*` | `coloring-myciel`, `coloring-queen` | `qqa bench-run --suite coloring-myciel` |
476+
| §5.1 / Table 1 | MIS on SATLIB (500 instances) | `mis/satlib/uf` | `mis-satlib-uf` | `qqa bench-run --suite mis-satlib-uf --instances 500` |
477+
| §5.1 / Table 1 | MIS on ER-[700-800] (128 instances) | `mis/er/800` | `mis-er-small` | `qqa bench-run --suite mis-er-small --instances 128` |
478+
| §5.1 / Table 1 | MIS on ER-[9000-11000] (16 instances, note [a]) | `mis/er/10k` | `mis-er-large` | `qqa bench-run --suite mis-er-large --instances 16` |
479+
| §5.1 / Table 2 | MIS on RRG `d=20, n=10^{4,5,6}` | `mis-rrg/d20_n{10000,100000,1000000}` | `mis-rrg-d20_n10000` (etc.) | `qqa bench-run --suite mis-rrg-d20_n10000 --instances 5` |
480+
| §5.1 / Table 2 | MIS on RRG `d=100, n=10^{4,5,6}` | `mis-rrg/d100_n{10000,100000,1000000}` | `mis-rrg-d100_n10000` (etc.) | `qqa bench-run --suite mis-rrg-d100_n10000 --instances 5` |
481+
| §5.2 / Table 3 | Max Clique (DISCS RB) | `maxclique/rb` | `maxclique-rb` | `qqa bench-run --suite maxclique-rb --instances 500` |
482+
| §5.2 / Table 3 | Max Clique (Twitter social) | `maxclique/twitter` | `maxclique-twitter` | `qqa bench-run --suite maxclique-twitter` |
483+
| §5.3 / Fig. 3 | Max Cut (ER, 7 sizes from 16 to 1,100 nodes) | `maxcut/er/er-0.15-n-*` | `maxcut-er-*` | `qqa bench-run --suite maxcut-er-n-128-150 --instances 100` |
484+
| §5.3 / Fig. 3 | Max Cut (BA, 7 sizes from 16 to 1,100 nodes) | `maxcut/ba/ba-4-n-*` | `maxcut-ba-*` | `qqa bench-run --suite maxcut-ba-n-128-150 --instances 100` |
485+
| §5.3 / Table 4 | Max Cut (Optsicom, 10 real-world graphs) | `maxcut/optsicom/b` | `maxcut-optsicom` | `qqa bench-run --suite maxcut-optsicom --instances 10` |
486+
| §5.4 / Table 5 | Balanced k-way graph partition (VGG, MNIST-conv, ResNet, AlexNet, Inception-v3) | `normcut/nets/*` | `balanced-partition-nets-*` | `qqa bench-run --suite balanced-partition-nets-INCEPTION` |
487+
| §5.5 / Table 6 | Graph Coloring — Mycielski (myciel5-7) | `coloring/myciel` | `coloring-myciel` | `qqa bench-run --suite coloring-myciel` |
488+
| §5.5 / Table 6 | Graph Coloring — Queen (queen5_5 … queen13_13, incl. queen8_12) | `coloring/queen` + `coloring/dimacs` | `coloring-queen`, `coloring-dimacs` | `qqa bench-run --suite coloring-queen` |
489+
| §5.5 / Table 6 | Graph Coloring — DIMACS real-world (`anna`, `jean`, `queen8_12`) | `coloring/dimacs` | `coloring-dimacs` | `qqa bench-run --suite coloring-dimacs --instances 3` |
490+
491+
Rows whose HF subset does not appear in the paper (G-set, NormCut with
492+
the 8-graph DISCS superset, ER density sweeps, EA3D spin glass) are
493+
shipped as convenience extras and documented separately under
494+
`data/*/README.md`.
495+
496+
**[a] ER-[9000-11000] caveat.** The 16 instances in `mis/er/10k/` are
497+
the exact graphs used by Sun et al. 2023 and Ichikawa & Arai 2024, but
498+
the upstream DISCS conversion does not carry per-instance KaMIS
499+
ground-truth labels, so `manifest.jsonl` records `best_known: null` and
500+
the ApR column in `qqa bench-run` output is left `NaN`. The paper
501+
reports the aggregate KaMIS average (`381.31`, Table 5 footnote); to
502+
reproduce the Table 1 ApR numbers you must either run KaMIS yourself on
503+
each of the 16 graphs or divide raw IS sizes by that aggregate.
488504

489505
Run them all in one shot:
490506

data/coloring/README.md

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,79 @@
11
# Graph Coloring Benchmark
22

3-
Procedurally generated Graph Coloring instances used by
4-
`qqa.datasets.coloring` and `scripts/bench_discs.py --suite coloring-*`.
3+
Graph Coloring instances used by `qqa.datasets.coloring` and
4+
`qqa bench-run --suite coloring-*`.
55

6-
Following the PQQA paper (arXiv:2409.02135v2) we evaluate the classical
7-
COLOR benchmark families:
6+
Following the PQQA paper (arXiv:2409.02135v2, §5.5 / Table 6) we
7+
evaluate the classical COLOR benchmark (Trick 2002). Coverage is
8+
organised in three families:
89

9-
| graph_type | generator | chromatic number |
10-
|------------|-------------------------------|------------------|
11-
| `myciel` | `networkx.mycielski_graph(k)` | known (k+1) |
12-
| `queen` | queen-attack graph on k×k | known (see code) |
10+
| graph_type | generator / source | chromatic number (`num_colors`) |
11+
|------------|--------------------------------------------------------------|---------------------------------------------------|
12+
| `myciel` | `networkx.mycielski_graph(k)` (Mycielski 1955) | `k` (procedural) |
13+
| `queen` | queen-attack graph on a k×k chessboard | tabulated, k∈{5..13} (Chvátal; DeLaVina) |
14+
| `dimacs` | DIMACS `.col` fetched from `mat.tepper.cmu.edu/COLOR/instances` | tabulated (Trick 2002 and follow-ups) |
1315

14-
`best_known` is always `0` (the number of edge conflicts a proper coloring
15-
must reach). `num_colors` stores the target chromatic number so
16-
`qqa.problems.Coloring` is instantiated with a feasible palette.
16+
`best_known` is always `0` (the minimum number of edge conflicts a
17+
proper coloring must reach). `num_colors` stores the target chromatic
18+
number so `qqa.problems.Coloring` is instantiated with a feasible
19+
palette.
20+
21+
## arXiv-2409.02135v2 Table 6 coverage
22+
23+
The paper tabulates 12 specific instances. All 12 are reachable through
24+
the public loader:
25+
26+
| Paper row | Colors | Local file path | `num_nodes` | `num_edges` |
27+
|-------------|--------|-------------------------------------------|-------------|-------------|
28+
| anna | 11 | `coloring/dimacs/0001.gpickle` | 138 | 493 |
29+
| jean | 10 | `coloring/dimacs/0002.gpickle` | 80 | 254 |
30+
| queen8_12 | 12 | `coloring/dimacs/0003.gpickle` | 96 | 1368 |
31+
| myciel5 | 6 | `coloring/myciel/0003.gpickle` | 23 | 71 |
32+
| myciel6 | 7 | `coloring/myciel/0004.gpickle` | 47 | 236 |
33+
| queen5_5 | 5 | `coloring/queen/0001.gpickle` | 25 | 160 |
34+
| queen6_6 | 7 | `coloring/queen/0002.gpickle` | 36 | 290 |
35+
| queen7_7 | 7 | `coloring/queen/0003.gpickle` | 49 | 476 |
36+
| queen8_8 | 9 | `coloring/queen/0004.gpickle` | 64 | 728 |
37+
| queen9_9 | 10 | `coloring/queen/0005.gpickle` | 81 | 1056 |
38+
| queen11_11 | 11 | `coloring/queen/0007.gpickle` | 121 | 1980 |
39+
| queen13_13 | 13 | `coloring/queen/0009.gpickle` | 169 | 3328 |
40+
41+
This mapping is enforced by
42+
`QQA4CO_plugin/tests/test_hf_bench_coverage.py::test_coloring_paper_table6_coverage`,
43+
which fails the test suite if any of the 12 instances disappears.
1744

1845
## Layout
1946

2047
```
2148
data/coloring/
22-
├── README.md (git-tracked)
23-
├── .gitignore (git-tracked, ignores the rest)
49+
├── README.md (git-tracked)
50+
├── .gitignore (git-tracked, ignores the rest)
2451
├── myciel/
2552
│ ├── manifest.jsonl
26-
│ ├── myciel3.gpickle
53+
│ ├── 0001.gpickle # myciel3 .. myciel7
2754
│ └── ...
28-
└── queen/
55+
├── queen/
56+
│ ├── manifest.jsonl
57+
│ ├── 0001.gpickle # queen5_5 .. queen13_13
58+
│ └── ...
59+
└── dimacs/
2960
├── manifest.jsonl
30-
├── queen5_5.gpickle
31-
└── ...
61+
├── 0001.gpickle # anna
62+
├── 0002.gpickle # jean
63+
└── 0003.gpickle # queen8_12
3264
```
3365

3466
Everything below `.gitignore` is re-generated locally by:
3567

3668
```bash
37-
python scripts/generate_coloring_instances.py
69+
python scripts/generate_coloring_instances.py # fetches DIMACS once, caches in data/coloring/_dimacs_cache
70+
python scripts/generate_coloring_instances.py --skip-dimacs # offline rebuild
3871
```
3972

73+
The DIMACS family is downloaded once (≈10 KB per `.col`) from
74+
Trick's canonical mirror; the cached ascii sources remain in
75+
`data/coloring/_dimacs_cache/` for reproducibility.
76+
4077
Upstream distribution: `Yuma-Ichikawa/qqa4co-bench` on the Hugging Face
4178
Hub (same repo as DISCS; see `data/discs/README.md` for credentials /
4279
fallbacks).

scripts/generate_coloring_instances.py

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,72 @@
3131
import argparse
3232
import json
3333
import pickle
34+
import urllib.request
3435
from pathlib import Path
3536

3637
import networkx as nx
3738

39+
# -------- DIMACS COLOR real-world instances --------------------------------
40+
# The PQQA paper (Table 6) also evaluates three non-procedural COLOR
41+
# instances that do not have a closed-form generator: ``anna`` and ``jean``
42+
# come from Knuth's Stanford GraphBase (book-character intersection
43+
# graphs), and ``queen8_12`` is a non-square queen graph. We fetch the
44+
# canonical DIMACS ``.col`` files from Trick's COLOR repository. The
45+
# chromatic numbers below are the paper's target ``num_colors`` (anna: 11,
46+
# jean: 10, queen8_12: 12; see Schuetz et al. 2022 Table 7 and Yang et al.
47+
# 2021 Table 3).
48+
DIMACS_COLOR_URL = "https://mat.tepper.cmu.edu/COLOR/instances"
49+
DIMACS_INSTANCES: dict[str, int] = {
50+
"anna": 11,
51+
"jean": 10,
52+
"queen8_12": 12,
53+
}
54+
55+
56+
def _parse_dimacs_col(text: str) -> nx.Graph:
57+
"""Parse a DIMACS ``.col`` ascii edge list into an undirected graph.
58+
59+
DIMACS format: comment lines start with ``c``; a ``p edge N M`` header
60+
declares ``N`` 1-indexed vertices and ``M`` edges; each edge is a line
61+
``e u v``. We store 0-indexed nodes for consistency with the rest of
62+
QQA4CO.
63+
"""
64+
g = nx.Graph()
65+
n_vertices: int | None = None
66+
for raw in text.splitlines():
67+
line = raw.strip()
68+
if not line or line.startswith("c"):
69+
continue
70+
tok = line.split()
71+
if tok[0] == "p":
72+
n_vertices = int(tok[2])
73+
g.add_nodes_from(range(n_vertices))
74+
elif tok[0] == "e":
75+
u = int(tok[1]) - 1
76+
v = int(tok[2]) - 1
77+
if u != v:
78+
g.add_edge(u, v, weight=1.0)
79+
if n_vertices is None:
80+
raise ValueError("DIMACS .col file missing 'p edge ...' header")
81+
return g
82+
83+
84+
def build_dimacs(name: str, cache_dir: Path) -> nx.Graph:
85+
"""Download (once) and parse ``<name>.col`` from Trick's COLOR site."""
86+
cache_dir.mkdir(parents=True, exist_ok=True)
87+
fpath = cache_dir / f"{name}.col"
88+
if not fpath.exists():
89+
url = f"{DIMACS_COLOR_URL}/{name}.col"
90+
with urllib.request.urlopen(url, timeout=30) as resp: # noqa: S310
91+
fpath.write_bytes(resp.read())
92+
g = _parse_dimacs_col(fpath.read_text())
93+
g.graph["problem"] = "coloring"
94+
g.graph["graph_type"] = "dimacs"
95+
g.graph["subset"] = name
96+
g.graph["num_colors"] = DIMACS_INSTANCES[name]
97+
g.graph["source"] = f"DIMACS COLOR {name}.col (Trick 2002; Stanford GraphBase)"
98+
return g
99+
38100
# -------- Mycielski graphs -------------------------------------------------
39101
# Chromatic number of M_k is k by construction (Mycielski 1955).
40102
# We start from k=3 so N grows: M_3=5, M_4=11, M_5=23, M_6=47, M_7=95.
@@ -116,7 +178,11 @@ def write_family(
116178
"best_known_source": (
117179
"chromatic number from Mycielski 1955"
118180
if family == "myciel"
119-
else "tabulated chromatic number"
181+
else (
182+
"DIMACS COLOR tabulated chromatic number (Trick 2002)"
183+
if family == "dimacs"
184+
else "tabulated chromatic number"
185+
)
120186
),
121187
"num_colors": K,
122188
"source": g.graph.get("source", ""),
@@ -131,6 +197,18 @@ def write_family(
131197
def main() -> None:
132198
parser = argparse.ArgumentParser(description=__doc__)
133199
parser.add_argument("--out", type=Path, default=Path("data"))
200+
parser.add_argument(
201+
"--skip-dimacs",
202+
action="store_true",
203+
help="Skip fetching DIMACS COLOR real-world instances (anna, jean, queen8_12). "
204+
"Use this for offline rebuilds.",
205+
)
206+
parser.add_argument(
207+
"--dimacs-cache",
208+
type=Path,
209+
default=Path("data") / "coloring" / "_dimacs_cache",
210+
help="Directory to cache downloaded .col files (default: data/coloring/_dimacs_cache).",
211+
)
134212
args = parser.parse_args()
135213

136214
mycs = [(f"myciel{k}", build_mycielski(k), k) for k in MYCIEL_ORDERS]
@@ -139,6 +217,13 @@ def main() -> None:
139217
queens = [(f"queen{k}_{k}", build_queen(k), QUEEN_CHROMATIC[k]) for k in QUEEN_SIZES]
140218
write_family(args.out, "queen", queens)
141219

220+
if not args.skip_dimacs:
221+
dimacs = [
222+
(name, build_dimacs(name, args.dimacs_cache), DIMACS_INSTANCES[name])
223+
for name in DIMACS_INSTANCES
224+
]
225+
write_family(args.out, "dimacs", dimacs)
226+
142227

143228
if __name__ == "__main__":
144229
main()

0 commit comments

Comments
 (0)