Skip to content

Commit 0f8af56

Browse files
authored
Merge pull request #532 from EvgSkv/main
Catching up dev.
2 parents 4fc91ac + 9cb9af7 commit 0f8af56

7 files changed

Lines changed: 265 additions & 3 deletions

File tree

common/concertina_lib.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,11 @@ def __init__(self, config, engine, display_mode='colab', iterations=None):
176176
self.all_actions = {a["name"] for a in self.config}
177177
self.complete_actions = set()
178178
self.running_actions = set()
179-
assert display_mode in ('colab', 'terminal', 'colab-text', 'silent'), (
179+
self.show_only_running = False
180+
if os.getenv('LOGICA_TERMINAL_ONELINE', 'no') == 'yes':
181+
self.show_only_running = True
182+
assert display_mode in ('colab', 'terminal',
183+
'colab-text', 'silent'), (
180184
'Unrecognized display mode: %s' % display_mode)
181185
self.display_mode = display_mode
182186
self.display_id = self.GetDisplayId()
@@ -293,6 +297,13 @@ def AsArtGraph():
293297
extra_lines = self.ProgressBar().split('\n')
294298
return AsArtGraph().GetPicture(updating=updating,
295299
extra_lines=extra_lines)
300+
def ShowRunning(self, updating):
301+
nodes, edges = self.AsNodesAndEdges()
302+
running = [n for n in nodes if n.startswith('\033[1m')]
303+
if not running:
304+
return '*'
305+
return '[%d / %d] ' % (len(self.complete_actions),
306+
len(self.all_actions)) + running[0]
296307

297308
def AsNodesAndEdges(self):
298309
"""Nodes and edges to display in terminal."""
@@ -405,14 +416,18 @@ def UpdateDisplay(self, final=False):
405416
self.display_update_period = min(0.5, self.display_update_period * 1.2)
406417
if (now - self.recent_display_update_seconds <
407418
self.display_update_period and
408-
not final):
419+
not final and
420+
not self.show_only_running):
409421
# Avoid frequent display updates slowing down execution.
410422
return
411423
self.recent_display_update_seconds = now
412424
if self.display_mode == 'colab':
413425
update_display(self.AsGraphViz(), display_id=self.display_id)
414426
elif self.display_mode == 'terminal':
415-
print(self.AsTextPicture(updating=True))
427+
if self.show_only_running:
428+
print(self.ShowRunning(updating=True))
429+
else:
430+
print(self.AsTextPicture(updating=True))
416431
elif self.display_mode == 'colab-text':
417432
update_display(
418433
self.StateAsSimpleHTML(),
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
#!/usr/bin/env python3
2+
"""Run all TC/SG benchmarks on Logica and Nemo, collect times into CSV + ASCII table."""
3+
4+
import csv
5+
import os
6+
import re
7+
import resource
8+
import subprocess
9+
import sys
10+
import time
11+
12+
13+
BENCHMARKS = [
14+
# (problem, dataset, csv_file)
15+
("TC", "g1k", "g1k.csv"),
16+
("TC", "g2k", "g2k.csv"),
17+
("TC", "g3k", "g3k.csv"),
18+
("TC", "g4k", "g4k.csv"),
19+
("TC", "g5k", "g5k.csv"),
20+
("SG", "tree7", "tree7.csv"),
21+
("SG", "tree8", "tree8.csv"),
22+
("SG", "tree9", "tree9.csv"),
23+
("SG", "tree10", "tree10.csv"),
24+
("SG", "tree11", "tree11.csv"),
25+
("SG", "tree12", "tree12.csv"),
26+
]
27+
28+
29+
LOGICA_TEMPLATES = {
30+
"TC": '''@Ground(G);
31+
G(a, b) :- `("{csv}")`(a, b);
32+
33+
@Recursive(TC, ∞, stop: Stop);
34+
TC(a, b) distinct :- G(a, b);
35+
TC(a, c) distinct :- TC(a, b), G(b, c);
36+
37+
OldN() += 1 :- TC();
38+
Stop() :- OldN() == Sum{{1 :- TC()}};
39+
40+
N() += 1 :- TC(a, b);
41+
''',
42+
"SG": '''G(a, b) :- `("{csv}")`(a, b);
43+
44+
@Recursive(SG, -1, stop: Done);
45+
SG(x, y) distinct :- G(a, x), G(a, y);
46+
SG(x, y) distinct :- SG(a, b), G(a, x), G(b, y);
47+
PrevSG(x, y) :- SG(x, y);
48+
Done() :- Sum{{ 1 :- SG(x, y) }} == Sum{{ 1 :- PrevSG(x, y) }};
49+
50+
N() += 1 :- SG(x, y);
51+
''',
52+
}
53+
54+
NEMO_TEMPLATES = {
55+
"TC": '''@import edge :- csv{{resource="{csv}", ignore_headers=true}}.
56+
57+
TC(?A, ?B) :- edge(?A, ?B).
58+
TC(?A, ?C) :- TC(?A, ?B), edge(?B, ?C).
59+
60+
N(#count(?A, ?B)) :- TC(?A, ?B).
61+
62+
@export N :- csv{{resource="n.csv"}}.
63+
''',
64+
"SG": '''@import tree :- csv{{resource="{csv}", ignore_headers=true, format=(string,string)}}.
65+
66+
SG(?X, ?Y) :- tree(?A, ?X), tree(?A, ?Y).
67+
SG(?X, ?Y) :- SG(?A, ?B), tree(?A, ?X), tree(?B, ?Y).
68+
69+
N(#count(?X, ?Y)) :- SG(?X, ?Y).
70+
71+
@export N :- csv{{resource="n.csv"}}.
72+
''',
73+
}
74+
75+
76+
def generate_programs(problem, dataset, csv_file):
77+
"""Write <problem>_<dataset>.l and .nemo files from templates."""
78+
base = f"{problem.lower()}_{dataset}"
79+
l_file = f"{base}.l"
80+
nemo_file = f"{base}.nemo"
81+
with open(l_file, "w") as f:
82+
f.write(LOGICA_TEMPLATES[problem].format(csv=csv_file))
83+
with open(nemo_file, "w") as f:
84+
f.write(NEMO_TEMPLATES[problem].format(csv=csv_file))
85+
return l_file, nemo_file
86+
87+
88+
def run_timed(cmd):
89+
"""Run a command, return (wall, user, sys, stdout, stderr)."""
90+
r0 = resource.getrusage(resource.RUSAGE_CHILDREN)
91+
t0 = time.time()
92+
proc = subprocess.run(cmd, capture_output=True, text=True)
93+
wall = time.time() - t0
94+
r1 = resource.getrusage(resource.RUSAGE_CHILDREN)
95+
user = r1.ru_utime - r0.ru_utime
96+
sys_t = r1.ru_stime - r0.ru_stime
97+
return wall, user, sys_t, proc.stdout, proc.stderr
98+
99+
100+
def parse_logica_n(stdout):
101+
"""Extract the N value from Logica's artistic_table output."""
102+
# Look for a number inside a table row like "| 12345 |"
103+
for line in stdout.splitlines():
104+
m = re.match(r"\|\s*(\d+)\s*\|", line)
105+
if m:
106+
return int(m.group(1))
107+
return None
108+
109+
110+
def parse_nemo_n(results_path="results/n.csv"):
111+
"""Nemo writes N to results/n.csv (one number per file)."""
112+
try:
113+
with open(results_path) as f:
114+
line = f.readline().strip().strip('"')
115+
return int(line)
116+
except (FileNotFoundError, ValueError):
117+
return None
118+
119+
120+
def run_logica(l_file):
121+
cmd = ["python3", "logica/logica.py", l_file, "run_in_terminal", "N"]
122+
wall, user, sys_t, out, err = run_timed(cmd)
123+
n = parse_logica_n(out)
124+
return wall, user + sys_t, n
125+
126+
127+
def run_nemo(nemo_file):
128+
cmd = ["nemo", nemo_file, "--overwrite-results"]
129+
wall, user, sys_t, out, err = run_timed(cmd)
130+
n = parse_nemo_n()
131+
return wall, user + sys_t, n
132+
133+
134+
def ascii_table(rows, header):
135+
"""Render rows as +---+---+ style table."""
136+
all_rows = [header] + [[str(c) for c in r] for r in rows]
137+
widths = [max(len(r[i]) for r in all_rows) for i in range(len(header))]
138+
sep = "+" + "+".join("-" * (w + 2) for w in widths) + "+"
139+
def fmt(r):
140+
return "| " + " | ".join(c.ljust(w) for c, w in zip(r, widths)) + " |"
141+
lines = [sep, fmt(header), sep]
142+
for r in all_rows[1:]:
143+
lines.append(fmt(r))
144+
lines.append(sep)
145+
return "\n".join(lines)
146+
147+
148+
def main():
149+
os.chdir(os.path.dirname(os.path.abspath(__file__)))
150+
os.makedirs("results", exist_ok=True)
151+
152+
rows = []
153+
for problem, dataset, csv_file in BENCHMARKS:
154+
print(f"=== {problem} {dataset} ===", flush=True)
155+
156+
l_file, nemo_file = generate_programs(problem, dataset, csv_file)
157+
print(f" Generated: {l_file}, {nemo_file}", flush=True)
158+
159+
print(f" Logica: {l_file}", flush=True)
160+
l_wall, l_cpu, l_n = run_logica(l_file)
161+
print(f" wall={l_wall:.2f}s cpu={l_cpu:.2f}s N={l_n}", flush=True)
162+
163+
print(f" Nemo: {nemo_file}", flush=True)
164+
n_wall, n_cpu, n_n = run_nemo(nemo_file)
165+
print(f" wall={n_wall:.2f}s cpu={n_cpu:.2f}s N={n_n}", flush=True)
166+
167+
rows.append([
168+
problem, dataset,
169+
f"{l_wall:.2f}", f"{l_cpu:.2f}",
170+
f"{n_wall:.2f}", f"{n_cpu:.2f}",
171+
l_n if l_n is not None else "?",
172+
n_n if n_n is not None else "?",
173+
])
174+
175+
header = ["Problem", "Dataset",
176+
"Logica wall", "Logica CPU",
177+
"Nemo wall", "Nemo CPU",
178+
"Logica N", "Nemo N"]
179+
180+
with open("benchmark_results.csv", "w", newline="") as f:
181+
w = csv.writer(f)
182+
w.writerow(header)
183+
w.writerows(rows)
184+
185+
table = ascii_table(rows, header)
186+
with open("benchmark_results.txt", "w") as f:
187+
f.write(table + "\n")
188+
189+
print()
190+
print(table)
191+
print()
192+
print("Wrote benchmark_results.csv and benchmark_results.txt")
193+
194+
195+
if __name__ == "__main__":
196+
main()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
+---------+---------+-------------+------------+-----------+----------+-----------+-----------+
2+
| Problem | Dataset | Logica wall | Logica CPU | Nemo wall | Nemo CPU | Logica N | Nemo N |
3+
+---------+---------+-------------+------------+-----------+----------+-----------+-----------+
4+
| TC | g1k | 1.71 | 9.06 | 3.35 | 3.35 | 1000000 | 1000000 |
5+
| TC | g2k | 2.93 | 37.81 | 14.87 | 14.87 | 4000000 | 4000000 |
6+
| TC | g3k | 4.53 | 66.86 | 36.40 | 36.40 | 9000000 | 9000000 |
7+
| TC | g4k | 6.55 | 105.75 | 70.53 | 70.52 | 16000000 | 16000000 |
8+
| TC | g5k | 9.91 | 162.22 | 116.35 | 116.34 | 24995000 | 24995000 |
9+
| SG | tree7 | 1.13 | 4.00 | 0.02 | 0.02 | 17506 | 17506 |
10+
| SG | tree8 | 1.40 | 4.48 | 0.11 | 0.11 | 106907 | 106907 |
11+
| SG | tree9 | 1.98 | 6.46 | 0.99 | 0.99 | 672411 | 672411 |
12+
| SG | tree10 | 2.50 | 15.77 | 5.93 | 5.93 | 4263436 | 4263436 |
13+
| SG | tree11 | 6.00 | 85.04 | 38.10 | 38.10 | 25802317 | 25802317 |
14+
| SG | tree12 | 24.99 | 453.25 | 276.70 | 276.69 | 161827886 | 161827886 |
15+
+---------+---------+-------------+------------+-----------+----------+-----------+-----------+

examples/graph/tgdk/sg_tree7.l

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
G(a, b) :- `("tree7.csv")`(a, b);
2+
3+
@Recursive(SG, -1, stop: Done);
4+
SG(x, y) distinct :- G(a, x), G(a, y);
5+
SG(x, y) distinct :- SG(a, b), G(a, x), G(b, y);
6+
PrevSG(x, y) :- SG(x, y);
7+
Done() :- Sum{ 1 :- SG(x, y) } == Sum{ 1 :- PrevSG(x, y) };
8+
9+
N() += 1 :- SG(x, y);

examples/graph/tgdk/sg_tree7.nemo

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@import tree :- csv{resource="tree7.csv", ignore_headers=true, format=(string,string)}.
2+
3+
SG(?X, ?Y) :- tree(?A, ?X), tree(?A, ?Y).
4+
SG(?X, ?Y) :- SG(?A, ?B), tree(?A, ?X), tree(?B, ?Y).
5+
6+
N(#count(?X, ?Y)) :- SG(?X, ?Y).
7+
8+
@export N :- csv{resource="n.csv"}.

examples/graph/tgdk/tc_g1k.l

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@Ground(G);
2+
G(a, b) :- `("g1k.csv")`(a, b);
3+
4+
@Recursive(TC, ∞, stop: Stop);
5+
TC(a, b) distinct :- G(a, b);
6+
TC(a, c) distinct :- TC(a, b), G(b, c);
7+
8+
OldN() += 1 :- TC();
9+
Stop() :- OldN() == Sum{1 :- TC()};
10+
11+
N() += 1 :- TC(a, b);

examples/graph/tgdk/tc_g1k.nemo

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@import edge :- csv{resource="g1k.csv", ignore_headers=true}.
2+
3+
TC(?A, ?B) :- edge(?A, ?B).
4+
TC(?A, ?C) :- TC(?A, ?B), edge(?B, ?C).
5+
6+
N(#count(?A, ?B)) :- TC(?A, ?B).
7+
8+
@export N :- csv{resource="n.csv"}.

0 commit comments

Comments
 (0)