Skip to content

Commit c56f28b

Browse files
authored
Merge branch 'SMT-COMP:master' into master
2 parents ae0dff5 + 15b7725 commit c56f28b

26 files changed

+2321
-306
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,4 @@ cython_debug/
170170
schema_doc.css
171171
schema_doc.min.js
172172
*.feather
173+
tmp/

README.md

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# smtcomp
1+
# SMT Competition
22

33
[![Release](https://img.shields.io/github/v/release/smtcomp/smtcomp.github.io)](https://img.shields.io/github/v/release/smtcomp/smtcomp.github.io)
44
[![Build status](https://img.shields.io/github/actions/workflow/status/smtcomp/smtcomp.github.io/main.yml?branch=main)](https://github.com/smtcomp/smtcomp.github.io/actions/workflows/main.yml?query=branch%3Amain)
@@ -38,7 +38,7 @@ make install
3838

3939
## For starting a new SMT-COMP year
4040

41-
Edit the file `smtcomp/defs.py`, in particular `Config.current_year`, `Logic` for adding new logics and `tracks` for new divisions.
41+
Edit the file `smtcomp/defs.py`, in particular `Config.current_year`, `Logic` for adding new logics and `tracks` for new divisions. Reset `Config.NYSE_seed` to `None`, and set the date the New York Stock Exchange Index will be used in `Config.NYSE_date`.
4242

4343
Download the new benchmarks from zenodo, unpack them, unpack the .tar.zst, you should get something like:
4444

@@ -74,10 +74,9 @@ smtcomp create-benchmarks-list $DIR/zenodo ./data/
7474

7575
The directory `./data/` is the one present in this repository.
7676

77-
## Using the smtcomp tool for selecting the benchmarks
77+
## Using the `smtcomp` tool for selecting the benchmarks
7878

79-
The list of benchmarks and the previous results are in json which are human
80-
readable, but slow to parse (1min). So locally the tool use the feather format. The
79+
The list of benchmarks and the previous results are in `json` which are human-readable, but slow to parse (1min). So locally the tool use the feather format. The
8180
feather files are generated with:
8281

8382
```
@@ -87,7 +86,7 @@ smtcomp create-cache ./data/
8786
Working with the feather files with [polars](https://docs.pola.rs/) is very fast,
8887
so no more intermediate files are needed.
8988

90-
However statistics can be shown, for example for the selection of single track:
89+
However, statistics can be shown, for example for the selection of single track:
9190

9291
```
9392
smtcomp show-sq-selection-stats ./data/ 0
@@ -110,7 +109,7 @@ Which outputs:
110109
...
111110
```
112111

113-
## Using the smtcomp tool for generating benchexec
112+
## Using the `smtcomp` tool for generating `benchexec` configuration
114113

115114
#### Generate submissions [Optional]
116115

@@ -129,37 +128,37 @@ smtcomp show ../tmp/submissions/YicesQS.json
129128
The solver downloaded using:
130129

131130
```
132-
smtcomp download-archive ../tmp/submissions/*.json ../tmp/benchexec/execution
131+
smtcomp download-archive submissions/*.json ../tmp/execution
133132
```
134133

135134
Trivial tests benchmarks generated with:
136135

137136
```
138-
smtcomp generate-benchmarks ../tmp/benchexec/includes/
137+
smtcomp generate-trivial-benchmarks ../tmp/execution/benchmarks
139138
```
140139

141140
The benchexec tasks generated using:
142141

143142
```
144-
smtcomp generate-benchexec ../tmp/submissions/*.json ../tmp/benchexec/includes/all.xml ../tmp/benchexec/execution
143+
smtcomp generate-benchexec submissions/*.json ../tmp/execution
145144
```
146145

147146
The benchexec execution environment generated using:
148147

149148
```
150-
smtcomp prepare-execution ../tmp/benchexec/execution
149+
smtcomp prepare-execution ../tmp/execution
151150
```
152151

153152
Benchexec started using:
154153

155154
```
156-
(cd ../tmp/benchexec/execution; benchexec ../includes/all.xml --read-only-dir / --overlay-dir /home --full-access-dir .. --numOfThreads 8 -M 2GB -c 1)
155+
(cd ../tmp/execution; PYTHONPATH=$(pwd) benchexec SOLVERNAME.xml --read-only-dir / --overlay-dir /home --full-access-dir .. --numOfThreads 8 -M 2GB -c 1)
157156
```
158157

159158
Benchexec in verifier cloud started using:
160159

161160
```
162-
(cd ../tmp/benchexec/execution; PATH_TO_BENCHEXEC/contrib/vcloud-benchmark.py ../includes/all.xml --read-only-dir / --overlay-dir /home --full-access-dir .. --numOfThreads 8 -M 2GB -c 1 --vcloudMaster VCLOUD_MASTER --vcloudClientHeap 500)
161+
(cd ../tmp/execution; PYTHONPATH=$(pwd) PATH_TO_BENCHEXEC/contrib/vcloud-benchmark.py SOLVERNAME.xml --read-only-dir / --overlay-dir /home --full-access-dir .. --numOfThreads 8 -M 2GB -c 1 --vcloudMaster VCLOUD_MASTER --vcloudClientHeap 500)
163162
```
164163

165164
---

smtcomp/archive.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ def is_unpack_present(archive: defs.Archive, dst: Path) -> bool:
3838
return any(True for _ in d.iterdir())
3939

4040

41+
def command_path(command: defs.Command, archive: defs.Archive, dst: Path) -> Path:
42+
return archive_unpack_dir(archive, dst).joinpath(command.binary)
43+
44+
4145
def find_command(command: defs.Command, archive: defs.Archive, dst: Path) -> Path:
4246
d = archive_unpack_dir(archive, dst)
4347
if not (d.exists()):
@@ -101,3 +105,17 @@ def unpack(archive: defs.Archive, dst: Path) -> None:
101105
if not (is_unpack_present(archive, dst)):
102106
print("[red]Empty archive", archive_file)
103107
exit(1)
108+
109+
110+
def download_unpack(s: defs.Submission, dst: Path) -> None:
111+
"""
112+
Download and unpack
113+
"""
114+
dst.mkdir(parents=True, exist_ok=True)
115+
if s.archive:
116+
download(s.archive, dst)
117+
unpack(s.archive, dst)
118+
for p in s.participations.root:
119+
if p.archive:
120+
download(p.archive, dst)
121+
unpack(p.archive, dst)

smtcomp/benchexec.py

Lines changed: 138 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,40 @@
11
from pathlib import Path
2+
import rich
23
from os.path import relpath
34
from typing import List, cast, Dict, Optional
45

56
from yattag import Doc, indent
67

78
from smtcomp import defs
8-
from smtcomp.archive import find_command
9+
from smtcomp.archive import find_command, archive_unpack_dir, command_path
910
from pydantic import BaseModel
1011

1112
import shlex
13+
from re import sub
14+
15+
16+
def get_suffix(track: defs.Track) -> str:
17+
match track:
18+
case defs.Track.Incremental:
19+
return "_inc"
20+
case defs.Track.ModelValidation:
21+
return "_model"
22+
case defs.Track.UnsatCore:
23+
return "_unsatcore"
24+
case defs.Track.SingleQuery:
25+
return ""
26+
case _:
27+
raise ValueError("No Cloud or Parallel")
28+
29+
30+
def get_xml_name(s: defs.Submission, track: defs.Track) -> str:
31+
suffix = get_suffix(track)
32+
return sub(r"\W+", "", s.name.lower()) + suffix + ".xml"
33+
34+
35+
def tool_module_name(s: defs.Submission, incremental: bool) -> str:
36+
suffix = "_inc" if incremental else ""
37+
return sub(r"\W+", "", s.name.lower()) + suffix
1238

1339

1440
class CmdTask(BaseModel):
@@ -17,7 +43,66 @@ class CmdTask(BaseModel):
1743
includesfiles: List[str]
1844

1945

20-
def generate_xml(timelimit_s: int, memlimit_M: int, cpuCores: int, cmdtasks: List[CmdTask], file: Path) -> None:
46+
def generate_benchmark_yml(benchmark: Path, expected_result: Optional[bool], orig_file: Optional[Path]) -> None:
47+
ymlfile = benchmark.with_suffix(".yml")
48+
with ymlfile.open("w") as f:
49+
f.write("format_version: '2.0'\n\n")
50+
51+
f.write(f"input_files: '{str(benchmark.name)}'\n\n")
52+
53+
if orig_file is not None:
54+
f.write(f"# original_files: '{str(orig_file)}'\n\n")
55+
56+
expected_str = "true" if expected_result else "false"
57+
f.write("properties:\n")
58+
f.write(f" - property_file: '../../properties/SMT.prp'\n")
59+
if expected_result is not None:
60+
f.write(f" expected_verdict: {expected_str}\n")
61+
62+
63+
def generate_tool_module(s: defs.Submission, cachedir: Path, incremental: bool) -> None:
64+
name = tool_module_name(s, incremental)
65+
file = cachedir / "tools" / f"{name}.py"
66+
67+
with file.open("w") as f:
68+
if incremental:
69+
base_module = "incremental_tool"
70+
base_class = "IncrementalSMTCompTool"
71+
else:
72+
base_module = "tool"
73+
base_class = "SMTCompTool"
74+
f.write(f"from tools.{base_module} import {base_class}\n\n")
75+
f.write(f"class Tool({base_class}): # type: ignore\n")
76+
77+
f.write(f" NAME = '{s.name}'\n")
78+
if s.command is not None:
79+
assert s.archive is not None
80+
if s.command.compa_starexec:
81+
executable_path = find_command(s.command, s.archive, cachedir)
82+
executable = str(relpath(executable_path, start=str(cachedir)))
83+
else:
84+
executable = str(command_path(s.command, s.archive, Path()))
85+
f.write(f" EXECUTABLE = '{executable}'\n")
86+
87+
required_paths = []
88+
89+
if s.archive is not None:
90+
archive_path = relpath(archive_unpack_dir(s.archive, cachedir), start=str(cachedir))
91+
required_paths.append(str(archive_path))
92+
for p in s.participations.root:
93+
if p.archive is not None:
94+
archive_path = relpath(archive_unpack_dir(p.archive, cachedir), start=str(cachedir))
95+
required_paths.append(str(archive_path))
96+
if required_paths:
97+
f.write(f" REQUIRED_PATHS = {required_paths}\n")
98+
99+
100+
def generate_tool_modules(s: defs.Submission, cachedir: Path) -> None:
101+
generate_tool_module(s, cachedir, True)
102+
generate_tool_module(s, cachedir, False)
103+
104+
105+
def generate_xml(config: defs.Config, cmdtasks: List[CmdTask], file: Path, tool_module_name: str) -> None:
21106
doc, tag, text = Doc().tagtext()
22107

23108
doc.asis('<?xml version="1.0"?>')
@@ -27,13 +112,18 @@ def generate_xml(timelimit_s: int, memlimit_M: int, cpuCores: int, cmdtasks: Lis
27112
)
28113
with tag(
29114
"benchmark",
30-
tool=f"smtcomp.tool",
31-
timelimit=f"{timelimit_s}s",
32-
hardlimit=f"{timelimit_s+30}s",
33-
memlimit=f"{memlimit_M} MB",
34-
cpuCores=f"{cpuCores}",
35-
displayname="SC",
115+
tool=f"tools.{tool_module_name}",
116+
timelimit=f"{config.timelimit_s}s",
117+
hardlimit=f"{config.timelimit_s+30}s",
118+
memlimit=f"{config.memlimit_M} MB",
119+
cpuCores=f"{config.cpuCores}",
36120
):
121+
with tag("require", cpuModel="Intel Xeon E3-1230 v5 @ 3.40 GHz"):
122+
text()
123+
124+
with tag("resultfiles"):
125+
text("**/error.log")
126+
37127
for cmdtask in cmdtasks:
38128
for includesfile in cmdtask.includesfiles:
39129
with tag("rundefinition", name=f"{cmdtask.name},{includesfile}"):
@@ -42,71 +132,70 @@ def generate_xml(timelimit_s: int, memlimit_M: int, cpuCores: int, cmdtasks: Lis
42132
text(option)
43133
with tag("tasks", name="task"):
44134
with tag("includesfile"):
45-
text(includesfile)
135+
text(f"benchmarks/{includesfile}")
136+
137+
with tag("propertyfile"):
138+
text("benchmarks/properties/SMT.prp")
46139

47140
file.write_text(indent(doc.getvalue()))
48141

49142

50-
def cmdtask_for_submission(s: defs.Submission, cachedir: Path) -> List[CmdTask]:
143+
def cmdtask_for_submission(s: defs.Submission, cachedir: Path, target_track: defs.Track) -> List[CmdTask]:
51144
res: List[CmdTask] = []
52145
i = -1
53146
for p in s.participations.root:
54147
command = cast(defs.Command, p.command if p.command else s.command)
55148
archive = cast(defs.Archive, p.archive if p.archive else s.archive)
56149
for track, divisions in p.get().items():
150+
if track != target_track:
151+
continue
152+
57153
i = i + 1
58-
match track:
59-
case defs.Track.Incremental:
60-
suffix = "_inc"
61-
mode = "trace"
62-
case defs.Track.ModelValidation:
63-
suffix = "_model"
64-
mode = "direct"
65-
case defs.Track.UnsatCore:
66-
suffix = ""
67-
mode = "direct"
68-
case defs.Track.ProofExhibition:
69-
suffix = ""
70-
mode = "direct"
71-
case defs.Track.SingleQuery:
72-
suffix = ""
73-
mode = "direct"
74-
case defs.Track.Cloud | defs.Track.Parallel:
75-
continue
154+
suffix = get_suffix(track)
76155
tasks: list[str] = []
77156
for _, logics in divisions.items():
78157
tasks.extend([str(logic) + suffix for logic in logics])
79158
if tasks:
80-
executable_path = find_command(command, archive, cachedir)
81-
executable = str(relpath(executable_path, start=str(cachedir)))
82159
if command.compa_starexec:
83160
assert command.arguments == []
161+
executable_path = find_command(command, archive, cachedir)
162+
executable = str(relpath(executable_path, start=str(cachedir)))
84163
dirname = str(relpath(executable_path.parent, start=str(cachedir)))
85164

86-
if mode == "direct":
87-
options = [
88-
"bash",
89-
"-c",
90-
f'FILE=$(realpath $1); (cd {shlex.quote(dirname)}; exec ./{shlex.quote(executable_path.name)} "$FILE")',
91-
"compa_starexec",
92-
]
93-
else:
94-
assert mode == "trace"
95-
options = [
96-
"bash",
97-
"-c",
98-
f'ROOT=$(pwd); FILE=$(realpath $1); (cd {shlex.quote(dirname)}; exec $ROOT/smtlib2_trace_executor ./{shlex.quote(executable_path.name)} "$FILE")',
99-
"compa_starexec",
100-
]
165+
options = [
166+
"bash",
167+
"-c",
168+
f'FILE=$(realpath $1); (cd {shlex.quote(dirname)}; exec ./{shlex.quote(executable_path.name)} "$FILE")',
169+
"compa_starexec",
170+
]
101171
else:
102-
if mode == "direct":
103-
options = [executable] + command.arguments
104-
else:
105-
options = ["./smtlib2_trace_executor", executable] + command.arguments
172+
executable = str(command_path(command, archive, Path()))
173+
options = [executable] + command.arguments
106174
cmdtask = CmdTask(
107175
name=f"{s.name},{i},{track}",
108176
options=options,
109177
includesfiles=tasks,
110178
)
111179
res.append(cmdtask)
112180
return res
181+
182+
183+
def generate(s: defs.Submission, cachedir: Path, config: defs.Config) -> None:
184+
generate_tool_modules(s, cachedir)
185+
186+
for target_track in [
187+
defs.Track.SingleQuery,
188+
defs.Track.Incremental,
189+
defs.Track.ModelValidation,
190+
defs.Track.UnsatCore,
191+
]:
192+
res = cmdtask_for_submission(s, cachedir, target_track)
193+
if res:
194+
basename = get_xml_name(s, target_track)
195+
file = cachedir / basename
196+
generate_xml(
197+
config=config,
198+
cmdtasks=res,
199+
file=file,
200+
tool_module_name=tool_module_name(s, target_track == defs.Track.Incremental),
201+
)

0 commit comments

Comments
 (0)