-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconvert_benchmarks_to_dzn.py
More file actions
133 lines (111 loc) · 4.25 KB
/
convert_benchmarks_to_dzn.py
File metadata and controls
133 lines (111 loc) · 4.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import os
import re
from pathlib import Path
BENCHMARK_DIR = "benchmarks"
OUTPUT_DIR = "benchmarks_dzn" # separate folder for clarity
os.makedirs(OUTPUT_DIR, exist_ok=True)
# Regexes for ASP facts
GRID_SIZE_RE = re.compile(r"grid_size\(\s*(\d+)\s*,\s*(\d+)\s*\)\.")
NUM_DISTRICTS_RE = re.compile(r"num_districts\(\s*(\d+)\s*\)\.")
CELL_RE = re.compile(r"cell\(\s*(\d+)\s*,\s*(\d+)\s*\)\.")
VOTE_RE = re.compile(r"vote\(\s*(\d+)\s*,\s*(\d+)\s*,\s*([01])\s*\)\.")
def parse_instance(filepath: Path):
"""
Parse one .lp instance file and return:
R, C, K, cell_exists (set of (r,c)), votes dict keyed by (r,c,p).
"""
R = C = K = None
cells = set()
votes = {} # (r,c,p) -> 0/1
with filepath.open("r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line or line.startswith("%"):
continue
m = GRID_SIZE_RE.match(line)
if m:
R = int(m.group(1))
C = int(m.group(2))
continue
m = NUM_DISTRICTS_RE.match(line)
if m:
K = int(m.group(1))
continue
m = CELL_RE.match(line)
if m:
r = int(m.group(1))
c = int(m.group(2))
cells.add((r, c))
continue
m = VOTE_RE.match(line)
if m:
r = int(m.group(1))
c = int(m.group(2))
p = int(m.group(3))
votes[(r, c, p)] = 1
# if there is a vote, the cell exists even if no explicit cell/2 fact
cells.add((r, c))
continue
if R is None or C is None or K is None:
raise ValueError(f"Missing grid_size/num_districts in {filepath}")
# If generator always creates full grids, cells will be everything 1..R x 1..C.
# We still respect the 'cells' set in case of irregular instances.
return R, C, K, cells, votes
def write_dzn(instance_name: str, R: int, C: int, K: int, cells, votes):
"""
Create a .dzn file for the given instance parameters.
votes is a dict (r,c,p) -> 1 (missing entries mean 0).
cells is a set of (r,c) that actually exist.
"""
# Build dense arrays
# Party index is 0..1
parties = [0, 1]
# Votes: array3d(1..R, 1..C, 0..1, [ ... ])
votes_flat = []
for r in range(1, R + 1):
for c in range(1, C + 1):
for p in parties:
val = votes.get((r, c, p), 0)
votes_flat.append(str(val))
# cell_exists: array2d(1..R,1..C,[...])
cell_exists_flat = []
for r in range(1, R + 1):
for c in range(1, C + 1):
cell_exists_flat.append("true" if (r, c) in cells else "false")
output_path = Path(OUTPUT_DIR) / (instance_name + ".dzn")
with output_path.open("w", encoding="utf-8") as out:
out.write(f"R = {R};\n")
out.write(f"C = {C};\n")
out.write(f"K = {K};\n\n")
# you can uncomment the next line to fix a default party, or leave it to -D at runtime
# out.write("party_to_optimize = 0;\n\n")
# votes array
out.write(
f"votes = array3d(1..{R}, 1..{C}, 0..1, [\n "
+ ", ".join(votes_flat)
+ "\n]);\n\n"
)
# cell_exists array
out.write(
f"cell_exists = array2d(1..{R}, 1..{C}, [\n "
+ ", ".join(cell_exists_flat)
+ "\n]);\n"
)
print(f"Wrote {output_path}")
def main():
bench_dir = Path(BENCHMARK_DIR)
if not bench_dir.exists():
print(f"Benchmark directory '{BENCHMARK_DIR}' not found.")
return
lp_files = sorted(p for p in bench_dir.iterdir() if p.suffix == ".lp")
if not lp_files:
print(f"No .lp files found in {BENCHMARK_DIR}")
return
for lp_file in lp_files:
print(f"Processing {lp_file} ...")
R, C, K, cells, votes = parse_instance(lp_file)
instance_name = lp_file.stem # e.g., instance_001_m3n4k2_random
write_dzn(instance_name, R, C, K, cells, votes)
print("\nConversion complete.")
if __name__ == "__main__":
main()