vnnlib-test-solver 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vnnlib_test_solver-2.0.0/.gitignore +218 -0
- vnnlib_test_solver-2.0.0/CHANGELOG.md +252 -0
- vnnlib_test_solver-2.0.0/CONTRIBUTING.md +186 -0
- vnnlib_test_solver-2.0.0/LICENSE +21 -0
- vnnlib_test_solver-2.0.0/PKG-INFO +176 -0
- vnnlib_test_solver-2.0.0/README.md +142 -0
- vnnlib_test_solver-2.0.0/docs/CONFIGURATION.md +636 -0
- vnnlib_test_solver-2.0.0/examples/README.md +61 -0
- vnnlib_test_solver-2.0.0/examples/capabilities.toml +45 -0
- vnnlib_test_solver-2.0.0/examples/f.onnx +0 -0
- vnnlib_test_solver-2.0.0/examples/g.onnx +0 -0
- vnnlib_test_solver-2.0.0/examples/misbehaving.toml +48 -0
- vnnlib_test_solver-2.0.0/examples/results.toml +37 -0
- vnnlib_test_solver-2.0.0/examples/worked-example.toml +35 -0
- vnnlib_test_solver-2.0.0/pyproject.toml +143 -0
- vnnlib_test_solver-2.0.0/src/vnnlib_test_solver/__init__.py +1 -0
- vnnlib_test_solver-2.0.0/src/vnnlib_test_solver/assignments.py +163 -0
- vnnlib_test_solver-2.0.0/src/vnnlib_test_solver/cli.py +504 -0
- vnnlib_test_solver-2.0.0/src/vnnlib_test_solver/config.py +130 -0
- vnnlib_test_solver-2.0.0/src/vnnlib_test_solver/dtypes.py +67 -0
- vnnlib_test_solver-2.0.0/src/vnnlib_test_solver/errors.py +30 -0
- vnnlib_test_solver-2.0.0/src/vnnlib_test_solver/injection.py +119 -0
- vnnlib_test_solver-2.0.0/src/vnnlib_test_solver/py.typed +0 -0
- vnnlib_test_solver-2.0.0/src/vnnlib_test_solver/querymodel.py +250 -0
- vnnlib_test_solver-2.0.0/src/vnnlib_test_solver/rules.py +119 -0
- vnnlib_test_solver-2.0.0/src/vnnlib_test_solver/spec.py +79 -0
- vnnlib_test_solver-2.0.0/src/vnnlib_test_solver/supports.py +151 -0
- vnnlib_test_solver-2.0.0/src/vnnlib_test_solver/validation.py +495 -0
- vnnlib_test_solver-2.0.0/src/vnnlib_test_solver/verify.py +417 -0
- vnnlib_test_solver-2.0.0/tests/conftest.py +256 -0
- vnnlib_test_solver-2.0.0/tests/fixtures/all_real.vnnlib +19 -0
- vnnlib_test_solver-2.0.0/tests/fixtures/duplicate_network_name.vnnlib +22 -0
- vnnlib_test_solver-2.0.0/tests/fixtures/equal_to.vnnlib +16 -0
- vnnlib_test_solver-2.0.0/tests/fixtures/isomorphic.vnnlib +19 -0
- vnnlib_test_solver-2.0.0/tests/fixtures/mixed_types.vnnlib +19 -0
- vnnlib_test_solver-2.0.0/tests/fixtures/unusual_but_legal.vnnlib +22 -0
- vnnlib_test_solver-2.0.0/tests/fixtures/worked_example.vnnlib +16 -0
- vnnlib_test_solver-2.0.0/tests/golden/injected_extra_output.stdout +2 -0
- vnnlib_test_solver-2.0.0/tests/golden/injected_nothing.stdout +0 -0
- vnnlib_test_solver-2.0.0/tests/golden/result_sat.stdout +1 -0
- vnnlib_test_solver-2.0.0/tests/golden/result_timed_out.stdout +1 -0
- vnnlib_test_solver-2.0.0/tests/golden/result_unknown.stdout +1 -0
- vnnlib_test_solver-2.0.0/tests/golden/result_unsat.stdout +1 -0
- vnnlib_test_solver-2.0.0/tests/golden/supports_boolean.stdout +1 -0
- vnnlib_test_solver-2.0.0/tests/golden/supports_element_types.stdout +4 -0
- vnnlib_test_solver-2.0.0/tests/golden/supports_operators.stdout +6 -0
- vnnlib_test_solver-2.0.0/tests/golden/supports_opset_versions.stdout +2 -0
- vnnlib_test_solver-2.0.0/tests/golden/worked_example.stdout +20 -0
- vnnlib_test_solver-2.0.0/tests/test_assignments.py +489 -0
- vnnlib_test_solver-2.0.0/tests/test_cli.py +127 -0
- vnnlib_test_solver-2.0.0/tests/test_config.py +690 -0
- vnnlib_test_solver-2.0.0/tests/test_conformance.py +432 -0
- vnnlib_test_solver-2.0.0/tests/test_contract.py +732 -0
- vnnlib_test_solver-2.0.0/tests/test_determinism.py +293 -0
- vnnlib_test_solver-2.0.0/tests/test_error_conditions.py +524 -0
- vnnlib_test_solver-2.0.0/tests/test_injection.py +615 -0
- vnnlib_test_solver-2.0.0/tests/test_network_mapping.py +399 -0
- vnnlib_test_solver-2.0.0/tests/test_not_over_policed.py +272 -0
- vnnlib_test_solver-2.0.0/tests/test_output_bytes.py +98 -0
- vnnlib_test_solver-2.0.0/tests/test_packaging.py +30 -0
- vnnlib_test_solver-2.0.0/tests/test_querymodel.py +369 -0
- vnnlib_test_solver-2.0.0/tests/test_rules.py +336 -0
- vnnlib_test_solver-2.0.0/tests/test_supports.py +599 -0
- vnnlib_test_solver-2.0.0/tests/test_timeout.py +165 -0
- vnnlib_test_solver-2.0.0/tests/test_verify.py +341 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# Version 2.0.0
|
|
2
|
+
|
|
3
|
+
A breaking release, and every break in it is a client ruling rather than a change of
|
|
4
|
+
mind here. Anything written against 1.0.0 needs all three of the following.
|
|
5
|
+
|
|
6
|
+
**The command is renamed `vnnlib-test-solver` to `vnnlibTestSolver`**, and the
|
|
7
|
+
configuration file it looks for in the working directory follows it to
|
|
8
|
+
`vnnlibTestSolver.toml`. There is no alias for either old spelling. The distribution is
|
|
9
|
+
still installed as `vnnlib-test-solver` and the import package is still
|
|
10
|
+
`vnnlib_test_solver`; the three differ deliberately, because a distribution name is
|
|
11
|
+
normalised to lower case by every installer that reads one and an import package cannot
|
|
12
|
+
contain a dash at all.
|
|
13
|
+
|
|
14
|
+
**There is no longer a default configuration.** Running with no `--config`, no
|
|
15
|
+
`VNNLIB_TEST_SOLVER_CONFIG` and no `vnnlibTestSolver.toml` in the working directory is
|
|
16
|
+
now an error, exiting 2 with nothing on standard output. It previously answered
|
|
17
|
+
`unknown` for every query and reported a full set of capabilities. This applies to
|
|
18
|
+
`--name` and `--version` as much as to `verify`: they are configured answers like any
|
|
19
|
+
other. The reasoning is that a test solver must never give an answer nobody chose,
|
|
20
|
+
because that is the one reply in the system indistinguishable from a deliberate one.
|
|
21
|
+
|
|
22
|
+
**The `real` exemption is now a property of the whole query rather than of each
|
|
23
|
+
declaration.** The standard exempts a query written in `real` from two of the three
|
|
24
|
+
error conditions the verify command must report, and defines a real-valued query as one
|
|
25
|
+
that uses that type exclusively. A query declaring `real` alongside any other type is
|
|
26
|
+
therefore not exempt at all, including for its `real` variables, where previously each
|
|
27
|
+
such variable was exempt on its own. A query written entirely in `real` is exempt in
|
|
28
|
+
full, exactly as before. A configuration wanting a mixed query to answer names `real` in
|
|
29
|
+
its soundness claim, which is the same explicit claim every other element type already
|
|
30
|
+
needed. Mixing element types is still not itself refused.
|
|
31
|
+
|
|
32
|
+
Also in this release, and neither of them breaking:
|
|
33
|
+
|
|
34
|
+
* Reporting the `real` element type no longer warns that it is unclaimed. The standard
|
|
35
|
+
makes `real` the answer a solver gives precisely when it cannot promise sound
|
|
36
|
+
reasoning over floating point, so a configuration reporting it alone and claiming
|
|
37
|
+
nothing was being warned at for following the standard's own advice. Reporting
|
|
38
|
+
`float32` or `float64` without the matching claim still warns, which is the case the
|
|
39
|
+
clause actually names.
|
|
40
|
+
* The exit codes are unchanged and are now confirmed conformant rather than assumed to
|
|
41
|
+
be. The standard says only that zero means the solver ran and terminated normally,
|
|
42
|
+
`unsat` and `timed-out` included, and is deliberately not opinionated about what any
|
|
43
|
+
non-zero value means.
|
|
44
|
+
|
|
45
|
+
**This version is the first published to PyPI**, so it installs with `pip install
|
|
46
|
+
vnnlib-test-solver` instead of from a clone of the repository. Nothing about the solver
|
|
47
|
+
changed to make that true and no behaviour here depends on how it was installed; the
|
|
48
|
+
distribution keeps the name it already had, and the command it installs is still
|
|
49
|
+
`vnnlibTestSolver`.
|
|
50
|
+
|
|
51
|
+
**Still not in this release.** The three limitations listed under *Not in this release* in
|
|
52
|
+
the 1.0.0 entry below all carry forward unchanged: serialised assignments are not written,
|
|
53
|
+
a red continuous-integration result does not mechanically block a merge, and assignment
|
|
54
|
+
values are not checked beside a result other than `sat`. None of them was resolved here.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
# Version 1.0.0
|
|
59
|
+
|
|
60
|
+
The first release.
|
|
61
|
+
|
|
62
|
+
A configurable test solver for the VNN-LIB standard: it implements the command-line
|
|
63
|
+
interface of chapter 5 of the standard and answers entirely from a TOML configuration
|
|
64
|
+
file, performing no verification of its own. It exists so that a tool driving VNN-LIB
|
|
65
|
+
solvers can be tested against a solver whose every response it controls.
|
|
66
|
+
|
|
67
|
+
What a caller gets from this release:
|
|
68
|
+
|
|
69
|
+
* Both global options, `--name` and `--version`, answered from configuration.
|
|
70
|
+
* The `supports` command answering all eleven of the capabilities the standard makes
|
|
71
|
+
mandatory, in the four response shapes it defines.
|
|
72
|
+
* The `verify` command accepting the full invocation of section 5.3 and returning any of
|
|
73
|
+
`sat`, `unsat`, `unknown` or `timed-out`, chosen by matching the query file's name
|
|
74
|
+
against configured rules.
|
|
75
|
+
* A configurable delay, so that a caller's own timeout handling can be exercised against
|
|
76
|
+
a solver that answers on the caller's deadline rather than one that genuinely takes
|
|
77
|
+
minutes.
|
|
78
|
+
* The satisfying assignment of section 5.3.1 in the command-line format, printed from
|
|
79
|
+
configured values and the query file's own declarations, and compared byte for byte
|
|
80
|
+
against the standard's worked example.
|
|
81
|
+
* All three of the error conditions section 5.3.2 requires, each distinguishable from
|
|
82
|
+
the others and checked in the order the standard lists them.
|
|
83
|
+
* Four controls that make the solver misbehave on request - noise on `stderr`, an
|
|
84
|
+
arbitrary exit status, deliberately malformed output, and abnormal termination - so
|
|
85
|
+
that a caller can exercise its own handling of a solver behaving badly.
|
|
86
|
+
* Output guaranteed to be deterministic and `LF`-terminated on every platform, with
|
|
87
|
+
results on `stdout` and everything else on `stderr`.
|
|
88
|
+
|
|
89
|
+
Installing needs no compiler and no build tooling on Linux `x86_64`, macOS and Windows.
|
|
90
|
+
There is no `aarch64` Linux wheel for the pinned parser dependency, so that one platform
|
|
91
|
+
builds it from source; the readme's requirements section has the detail.
|
|
92
|
+
|
|
93
|
+
## Not in this release
|
|
94
|
+
|
|
95
|
+
* **Serialised assignments.** Section 5.3.1's optional `.pb` `TensorProto` output is not
|
|
96
|
+
written. The standard requires the option only of a solver that reports supporting it,
|
|
97
|
+
and this solver reports `false`; the option is still accepted, and says on `stderr`
|
|
98
|
+
that nothing was written rather than exiting zero having silently done nothing. The
|
|
99
|
+
command-line assignment format, which the standard makes the default and requires of
|
|
100
|
+
every solver, is implemented.
|
|
101
|
+
* **Enforcement of the continuous integration result.** The suite runs on nine platform
|
|
102
|
+
and version combinations on every pull request, but a red result does not block a
|
|
103
|
+
merge. That is branch protection rather than a workflow, and it needs repository
|
|
104
|
+
administration this team does not hold.
|
|
105
|
+
* **A check on assignment values beside a result other than `sat`.** A value array of the
|
|
106
|
+
wrong length, or one naming a variable the query does not declare, is refused beside
|
|
107
|
+
`result = "sat"` and accepted in silence beside any other result, because values that
|
|
108
|
+
will never be printed are never measured. Documented rather than corrected: changing it
|
|
109
|
+
moves an exit code that other code in this project already tests against.
|
|
110
|
+
|
|
111
|
+
## How this release was built
|
|
112
|
+
|
|
113
|
+
Everything below is the record of the work, newest first. None of it describes a change
|
|
114
|
+
to a previously published version, because there is not one - 0.1.0 was never tagged and
|
|
115
|
+
never published. It is kept because what was decided, and why, is worth more than a
|
|
116
|
+
summary of it.
|
|
117
|
+
|
|
118
|
+
Each entry describes the package as it stood when that piece of work landed. An entry
|
|
119
|
+
saying something is not implemented yet has been overtaken by one above it; the Version
|
|
120
|
+
1.0.0 entry above is what 1.0.0 actually does.
|
|
121
|
+
|
|
122
|
+
* Added continuous integration. The test suite now runs on Ubuntu, macOS and Windows
|
|
123
|
+
against Python 3.9, 3.11 and 3.13 on every pull request, with `fail-fast` disabled so
|
|
124
|
+
that one platform's failure does not hide another's. Two of the defects found in this
|
|
125
|
+
package so far are invisible on Linux, and the declared Python floor of 3.9 had never
|
|
126
|
+
been executed anywhere until now.
|
|
127
|
+
* Added a job that installs the package into a container with no compiler, no CMake and
|
|
128
|
+
no pybind11 present, and which refuses to run at all if one of them is on the path. It
|
|
129
|
+
also forbids a source build of the parser, so the pure-Python claim cannot be satisfied
|
|
130
|
+
by a machine that happened to be able to compile something.
|
|
131
|
+
* Added a byte-for-byte conformance suite comparing standard output against eleven
|
|
132
|
+
committed expected-output files: the standard's own worked example, one per verify
|
|
133
|
+
result, the four capability response shapes transcribed from the standard's own
|
|
134
|
+
examples, and the two cases where injected output replaces the answer. Every comparison
|
|
135
|
+
is made on raw bytes, and a mismatch names the line that differs rather than printing
|
|
136
|
+
two blobs.
|
|
137
|
+
* Added tests asserting that five consecutive runs of every command that writes to
|
|
138
|
+
standard output produce identical bytes. The five runs use five different hash seeds,
|
|
139
|
+
so an unordered collection reaching the output fails every time rather than most of the
|
|
140
|
+
time.
|
|
141
|
+
* Added a self-test, selectable with `-m contract`, asserting that the published
|
|
142
|
+
interface is exactly what is documented: the global options, both commands and their
|
|
143
|
+
options, the eleven capability flags, every configuration file key, the configuration
|
|
144
|
+
discovery order and the exit codes. It checks both directions - that everything
|
|
145
|
+
documented exists, and that nothing undocumented exists.
|
|
146
|
+
* Documented, without changing it, that a rule's assignment values are only checked
|
|
147
|
+
against the query when that rule answers `sat`. A value array of the wrong length, or a
|
|
148
|
+
name the query does not declare, is refused beside `result = "sat"` and accepted in
|
|
149
|
+
silence beside `result = "unsat"`. This has been true since assignments were added and
|
|
150
|
+
had simply never been stated.
|
|
151
|
+
* Corrected the description of the `verify` command in the README, which said two of the
|
|
152
|
+
standard's three error conditions were not implemented. All three have been implemented
|
|
153
|
+
since the previous release, and the same file documented them at length two sections
|
|
154
|
+
further down.
|
|
155
|
+
* Added regression tests asserting what the solver deliberately does not reject. The
|
|
156
|
+
standard leaves anything outside its three named error conditions to the solver's
|
|
157
|
+
discretion, and a conformance baseline that invents a fourth rejection teaches that
|
|
158
|
+
invented rule to every test written against it. Covered: a query mixing the `real`
|
|
159
|
+
element type with another one, two networks declared under the same name, hidden nodes,
|
|
160
|
+
comments and irregular whitespace, a mapped model file that does not exist or is not an
|
|
161
|
+
ONNX file at all, and a query file with any name or none. No behaviour changed.
|
|
162
|
+
* The four failure-injection controls now do something. `stderr` writes the configured
|
|
163
|
+
noise before the answer and before any delay, `exit_code` returns the configured status,
|
|
164
|
+
`raw_stdout` replaces standard output entirely, and `crash` terminates the process
|
|
165
|
+
abnormally after the output has been written and flushed. Each was accepted and
|
|
166
|
+
validated from the first release and had no effect until now.
|
|
167
|
+
* Added a top-level `[injection]` table carrying the same four controls and applying to
|
|
168
|
+
whichever command runs. A rule is selected by matching the query file's name and the
|
|
169
|
+
`supports` command has no query file, so nothing hanging off a rule could reach a
|
|
170
|
+
capability response, and a malformed one was not producible at all. Where a rule and the
|
|
171
|
+
table both set a control, the rule wins for that control alone.
|
|
172
|
+
* Nothing is injected into a run that is refused. A configuration mistake, a usage mistake
|
|
173
|
+
and each of the standard's error conditions keep their own exit code and write nothing
|
|
174
|
+
to standard output, so the solver can still report its own errors.
|
|
175
|
+
* Fixed: `exit_code` is now refused unless it is between 0 and 255. A process exit status
|
|
176
|
+
carries eight bits on a Unix-like system, so `exit_code = 256` reached the caller as
|
|
177
|
+
`0` - a run configured to fail reporting that it had succeeded. Invisible on Windows,
|
|
178
|
+
where the whole value survives. The refusal names what the value would have become.
|
|
179
|
+
* Setting `crash` and `exit_code` in the same table is refused, because a process that
|
|
180
|
+
terminates abnormally reports no exit status and only one of the two can happen. Set on
|
|
181
|
+
a rule and in the table respectively, the rule's wins.
|
|
182
|
+
* Added the second and third error conditions the standard requires of `verify`. The
|
|
183
|
+
element types the supplied model files expose must not disagree with the types the query
|
|
184
|
+
declares, and a query declaring a type the solver cannot analyse soundly is refused
|
|
185
|
+
unless the user consented by declaring it `real`. Both write nothing to `stdout`, exit
|
|
186
|
+
`1`, and carry messages distinct from each other and from the model-mapping condition,
|
|
187
|
+
so a caller can tell which one it triggered without matching on wording. All three are
|
|
188
|
+
checked in the order the standard lists them.
|
|
189
|
+
* Added `[rules.model_element_types]`, a table keyed by variable name stating what the
|
|
190
|
+
supplied model files expose. Nothing here opens a model file, so this is the only source
|
|
191
|
+
the element-type condition can have; the query supplies the declared types it is compared
|
|
192
|
+
against. A variable the table does not name is taken to match, and a name the query does
|
|
193
|
+
not declare is a configuration error rather than one of the standard's conditions.
|
|
194
|
+
* `[soundness] sound-for` now drives an error as well as a warning. A configuration with no
|
|
195
|
+
`[soundness]` table is not checked at all, because reading silence as a claim of total
|
|
196
|
+
unsoundness would make every such configuration refuse every query; an explicitly empty
|
|
197
|
+
`sound-for` is a claim, and refuses everything not declared `real`.
|
|
198
|
+
* A declaration written in `real` is exempt from both new conditions, applied to each
|
|
199
|
+
declaration on its own rather than to the query as a whole. The standard describes a
|
|
200
|
+
real-valued query as using `real` exclusively, but the query parser accepts a query
|
|
201
|
+
mixing `real` with another element type, so a decision was needed and refusing such a
|
|
202
|
+
query would have invented a rejection the standard leaves to solver discretion.
|
|
203
|
+
* The packaged default configuration now reports and claims soundness over `int32`.
|
|
204
|
+
Without it the default refused the standard's own worked example as soon as a model file
|
|
205
|
+
was mapped, since that example declares an `int32` variable.
|
|
206
|
+
* Added the `verify` command, as far as its result line: the query file path, a
|
|
207
|
+
repeatable `--network <name>=<path>`, `--timeout` in whole seconds, and
|
|
208
|
+
`--serialise-assignments`. `--assignments` is accepted alongside the latter, because
|
|
209
|
+
section 5.3 spells the same option both ways. The result is the first line of `stdout`
|
|
210
|
+
and is one of `sat`, `unsat`, `unknown` or `timed-out`. Variable assignments for a `sat`
|
|
211
|
+
result are not implemented yet, and the query file is not read at all.
|
|
212
|
+
* Which result a query receives is decided by the `[[rules]]` array. The first rule whose
|
|
213
|
+
`match` pattern matches the query file's **name** wins; patterns are compared against the
|
|
214
|
+
name alone, case sensitively on every platform. A query that no rule matches is an error
|
|
215
|
+
rather than a silently chosen default, and the message names the file and every pattern
|
|
216
|
+
it tried.
|
|
217
|
+
* A rule's `delay_seconds` now interacts with `--timeout`. A delay longer than the timeout
|
|
218
|
+
waits out the timeout and answers `timed-out`, so a caller's own deadline handling can be
|
|
219
|
+
exercised against a solver that reliably runs long. A delay equal to the timeout answers
|
|
220
|
+
normally, and an absent `--timeout` is not a timeout of zero.
|
|
221
|
+
* Fixed: an empty query path is refused. `verify ""` was accepted, matched the catch-all
|
|
222
|
+
rule pattern, and answered for a file that cannot exist.
|
|
223
|
+
* Fixed: `--timeout` and a rule's `delay_seconds` are bounded. Above roughly 68 years the
|
|
224
|
+
wait itself raises, so a large value reached the caller as an unhandled traceback rather
|
|
225
|
+
than a message. The bound is a fixed constant rather than the platform's own ceiling, so
|
|
226
|
+
a configuration that loads on one machine cannot raise on another.
|
|
227
|
+
* `--serialise-assignments` now reports on `stderr` that assignment files are not written
|
|
228
|
+
yet. It was previously accepted and silently ignored, which to a caller testing
|
|
229
|
+
serialisation was indistinguishable from success.
|
|
230
|
+
* Fixed: `stdout` is written as bytes and uses `LF` line endings on every platform. On
|
|
231
|
+
Windows every command emitted `CRLF`, which made recorded output impossible to compare
|
|
232
|
+
byte for byte between platforms and left a trailing carriage return inside every value a
|
|
233
|
+
consumer read by splitting on newlines.
|
|
234
|
+
* Fixed: a configuration value that would corrupt the shape of a response is refused when
|
|
235
|
+
the file loads rather than emitted. An operator name containing whitespace was the
|
|
236
|
+
damaging case, because it produced output that parsed cleanly and meant something else:
|
|
237
|
+
an operator named `Conv Relu` was read back as the operator `Conv` supporting an element
|
|
238
|
+
type called `Relu`. Empty and whitespace-bearing version strings, and line breaks in the
|
|
239
|
+
solver name or version, are refused for the same reason. The failure-injection keys are
|
|
240
|
+
deliberately exempt, since producing unparseable output is their purpose.
|
|
241
|
+
* Added the `supports` command, answering all eleven mandatory capabilities of the
|
|
242
|
+
standard's section 5.4 from configuration, in the four response shapes it defines.
|
|
243
|
+
`capabilities` is accepted as an undocumented alias, because section 5.1 names the
|
|
244
|
+
command that way while section 5.4 defines it as `supports`.
|
|
245
|
+
* A configuration reporting an ONNX element type it does not also claim sound analysis
|
|
246
|
+
over now warns on `stderr`, per section 5.4.1.
|
|
247
|
+
* Added the installable package: console entry point, configuration discovery across four
|
|
248
|
+
sources, schema validation, and the `--name` and `--version` global options.
|
|
249
|
+
* Added `.gitattributes` to force LF line endings and mark golden/fixture files
|
|
250
|
+
binary, so line-ending conversion can never corrupt a byte-exact comparison.
|
|
251
|
+
* Wrote the first `README.md`, replacing the two-line placeholder the repository was
|
|
252
|
+
created with.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
This repository implements the VNN-LIB 2.0 command-line interface (Chapter 5 of the
|
|
4
|
+
standard) as a configurable test solver.
|
|
5
|
+
|
|
6
|
+
## Setting up a working copy
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
git clone <repo-url>
|
|
10
|
+
cd VNNLIB-Test-Solver
|
|
11
|
+
python -m venv .venv
|
|
12
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
13
|
+
pip install -e ".[dev]"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The `[dev]` extra pulls in the test, lint and type-checking tools alongside the package
|
|
17
|
+
itself. `pip install .` on its own is enough to run the solver, but not enough to run its
|
|
18
|
+
test suite - if `pytest` cannot be found, this is why.
|
|
19
|
+
|
|
20
|
+
Python 3.9 or later. This package is pure Python, but its one dependency is a compiled
|
|
21
|
+
extension and there is no Linux `aarch64` wheel for the pinned version. On Linux
|
|
22
|
+
`x86_64`, macOS and Windows an install needs no compiler, CMake or other build tooling;
|
|
23
|
+
on arm64 Linux it builds the parser from source and does need them. The readme's
|
|
24
|
+
requirements section carries the platform table.
|
|
25
|
+
|
|
26
|
+
## The parser dependency
|
|
27
|
+
|
|
28
|
+
`vnnlib` is confined to a single module, which is the only file permitted to import it.
|
|
29
|
+
Everything crossing out of that module is a plain string, integer or tuple - no parser
|
|
30
|
+
object and no name from the parser's vocabulary reaches the rest of the package. That
|
|
31
|
+
confinement is what makes the dependency reversible: were this package ever required to
|
|
32
|
+
be standalone, one file would change and every other module would keep its interface.
|
|
33
|
+
|
|
34
|
+
## Running the checks
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
python -m pytest -q # the test suite
|
|
38
|
+
python -m pytest -q -m contract # the published interface, on its own
|
|
39
|
+
ruff check . # lint
|
|
40
|
+
ruff format --check . # formatting
|
|
41
|
+
mypy src/ # type checking
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`ruff format --check` is not the same command as `ruff check`: the first looks at
|
|
45
|
+
formatting and the second does not, so running one is not running the other.
|
|
46
|
+
|
|
47
|
+
`vnnlibTestSolver --version` should also work once installed. The test suite shells out
|
|
48
|
+
to that installed console script rather than importing the package, because what this
|
|
49
|
+
project delivers is a binary, so a suite that called into Python directly would prove
|
|
50
|
+
nothing about what happens after `pip install`.
|
|
51
|
+
|
|
52
|
+
## What runs on a pull request
|
|
53
|
+
|
|
54
|
+
`.github/workflows/buildAndTest.yml` runs the suite on Ubuntu, macOS and Windows against
|
|
55
|
+
Python 3.9, 3.11 and 3.13 - nine combinations, with `fail-fast` off so one platform's
|
|
56
|
+
failure does not hide another's. Two further jobs run alongside it: lint, formatting and
|
|
57
|
+
type checking; and an installation into a container with no compiler, no CMake and no
|
|
58
|
+
pybind11 present, which is how the pure-Python claim is proven rather than asserted.
|
|
59
|
+
|
|
60
|
+
A failing check is visible on the pull request but does not mechanically prevent a merge,
|
|
61
|
+
because branch protection is not configured on this repository. Read the checks before
|
|
62
|
+
merging.
|
|
63
|
+
|
|
64
|
+
## How correctness is established
|
|
65
|
+
|
|
66
|
+
This package is a conformance baseline: other people's tests measure against its output.
|
|
67
|
+
If it is wrong, every one of those tests is measuring against a broken ruler and still
|
|
68
|
+
passing. That makes "the suite is green" a weaker claim here than it is in most projects,
|
|
69
|
+
and the practices below exist to close the gap.
|
|
70
|
+
|
|
71
|
+
### The suite tests the delivered artifact
|
|
72
|
+
|
|
73
|
+
Tests shell out to the installed console script rather than importing the package. What
|
|
74
|
+
this project ships is a binary, so a suite that called into Python directly would prove
|
|
75
|
+
nothing about what happens after `pip install` - it would not catch a broken entry point,
|
|
76
|
+
a missing dependency, or packaging metadata that disagrees with the code.
|
|
77
|
+
`tests/test_packaging.py` checks the installed distribution specifically.
|
|
78
|
+
|
|
79
|
+
### Output is compared as bytes, never as text
|
|
80
|
+
|
|
81
|
+
`tests/golden/` holds eleven expected-output files: the standard's own worked example, one
|
|
82
|
+
per `verify` result, the four capability response shapes, and the two cases where injected
|
|
83
|
+
output replaces the answer. Every comparison is on raw bytes, so line endings, spacing
|
|
84
|
+
inside `[2,2]` and trailing newlines are all part of what is asserted.
|
|
85
|
+
`tests/test_output_bytes.py` reads streams as bytes rather than decoded text, because a
|
|
86
|
+
decoded comparison silently hides exactly the platform differences most worth catching.
|
|
87
|
+
|
|
88
|
+
**Nothing regenerates the golden files.** If a change makes one fail, the question is which
|
|
89
|
+
of the two is wrong, and the answer belongs in the pull request description line by line.
|
|
90
|
+
An expected-output file that nobody has read is not a specification, it is a snapshot of
|
|
91
|
+
whatever the code did last.
|
|
92
|
+
|
|
93
|
+
`tests/test_determinism.py` runs the same input repeatedly and requires the bytes to be
|
|
94
|
+
identical, which is what stops a set iteration or a timestamp reaching output unnoticed.
|
|
95
|
+
|
|
96
|
+
### Tests assert what is *not* rejected
|
|
97
|
+
|
|
98
|
+
Section 5.3.2 makes three things errors and leaves everything else to the solver's
|
|
99
|
+
discretion. A package that quietly grew a fourth error condition would break callers
|
|
100
|
+
relying on that discretion, and no ordinary test would notice, because nothing was
|
|
101
|
+
asserting the absence. `tests/test_not_over_policed.py` exists to hold that line: it feeds
|
|
102
|
+
in unusual but legal queries and requires them to be answered rather than refused.
|
|
103
|
+
|
|
104
|
+
### A claim is verified or it is labelled an assumption
|
|
105
|
+
|
|
106
|
+
Where the standard is silent or contradicts itself, this package implements the most
|
|
107
|
+
conservative reading and says so, rather than encoding a guess as though it were settled.
|
|
108
|
+
The inconsistencies found while implementing Chapter 5 were reported upstream -
|
|
109
|
+
[VNNLIB-Standard#179](https://github.com/VNNLIB/VNNLIB-Standard/issues/179) and
|
|
110
|
+
[VNNLIB-Standard#181](https://github.com/VNNLIB/VNNLIB-Standard/issues/181) - rather than
|
|
111
|
+
resolved privately, so that the next implementer meets a documented question instead of
|
|
112
|
+
rediscovering it.
|
|
113
|
+
|
|
114
|
+
### Claims about the build are proven, not asserted
|
|
115
|
+
|
|
116
|
+
"Pure Python, no toolchain needed" is easy to believe on a machine that has a compiler
|
|
117
|
+
sitting on its path. The container job refuses to run at all if `cc`, `gcc`, `g++`,
|
|
118
|
+
`clang`, `make` or `cmake` is present, and forbids pip from building the parser from
|
|
119
|
+
source, so it cannot pass for the wrong reason. The platform where that claim does *not*
|
|
120
|
+
hold - Linux `aarch64` - is named in the readme rather than left to be discovered.
|
|
121
|
+
|
|
122
|
+
### When reviewing a test, check it can fail
|
|
123
|
+
|
|
124
|
+
A check comparing two things to each other rather than to an expected value passes when
|
|
125
|
+
both are broken. This has happened here: two spellings of one command were compared to
|
|
126
|
+
each other, and with no capabilities configured both failed identically, so the check
|
|
127
|
+
passed while measuring nothing. Before trusting a new test, break the thing it names and
|
|
128
|
+
confirm it goes red.
|
|
129
|
+
|
|
130
|
+
## Making a release
|
|
131
|
+
|
|
132
|
+
1. Update the version in `pyproject.toml` and `src/vnnlib_test_solver/__init__.py`.
|
|
133
|
+
Tests assert that both agree with the installed distribution, so missing one fails
|
|
134
|
+
the suite rather than shipping.
|
|
135
|
+
|
|
136
|
+
2. Add a row to the version compatibility table in `README.md`.
|
|
137
|
+
|
|
138
|
+
3. Add the version and its changes to `CHANGELOG.md`.
|
|
139
|
+
|
|
140
|
+
4. Merge to `main`, then tag the merge commit once its checks are green:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
git checkout main && git pull
|
|
144
|
+
git tag -a v1.0.0 -m "Version 1.0.0"
|
|
145
|
+
git push origin v1.0.0
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
5. Create a Release on GitHub against that tag, using the changelog entry as its
|
|
149
|
+
description. Publishing the Release runs `Build-and-Deploy`, which builds the source
|
|
150
|
+
distribution and the wheel, checks them, and uploads both to PyPI.
|
|
151
|
+
|
|
152
|
+
To rehearse an upload first, run `Build-and-Deploy` by hand and choose TestPyPI. It
|
|
153
|
+
builds and checks exactly as the real run does, and uploads to an index nobody installs
|
|
154
|
+
from.
|
|
155
|
+
|
|
156
|
+
To see what would be built without uploading anything:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
pip install build twine
|
|
160
|
+
python -m build
|
|
161
|
+
twine check --strict dist/*
|
|
162
|
+
python .github/scripts/check_sdist.py
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`build` and `twine` are deliberately not in the `dev` extra. They inspect the package
|
|
166
|
+
rather than forming part of it, and what this package installs is a constraint it is
|
|
167
|
+
held to.
|
|
168
|
+
|
|
169
|
+
### What an upload commits you to
|
|
170
|
+
|
|
171
|
+
A version number on PyPI can never be reused. Deleting a release does not free it, and
|
|
172
|
+
yanking one hides it from resolvers while leaving the files downloadable by anyone who
|
|
173
|
+
asks for that version exactly. There is no correcting a release afterwards, only
|
|
174
|
+
releasing again with a higher number.
|
|
175
|
+
|
|
176
|
+
Two checks therefore run before every upload and both refuse it outright rather than
|
|
177
|
+
warning. One compares the source distribution against `git ls-files` and fails if it
|
|
178
|
+
carries a single file this repository does not track. The other fails if the version
|
|
179
|
+
built disagrees with the tag the Release names, which is what a Release created against
|
|
180
|
+
an older tag looks like.
|
|
181
|
+
|
|
182
|
+
The first is not a formality. The build backend ships whatever is in the project
|
|
183
|
+
directory unless told otherwise, and the files kept out of this repository through
|
|
184
|
+
`.git/info/exclude` are invisible to it, because that file is local to one clone. Built
|
|
185
|
+
without the explicit list in `pyproject.toml`, this package produced a 1.3 MB archive
|
|
186
|
+
carrying working notes, meeting minutes and a copy of the standard.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 VNNLIB
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|