torchmodal.verify¶
torchmodal.verify ¶
torchmodal.verify ~~~~~~~~~~~~~~~~~
Turning a learned relation into a checkable answer.
The README says soundness is a property you can check. This module is where
that becomes true end to end: round a learned relation, evaluate it with
:func:torchmodal.functional.necessity's exact mode, and get back a verdict
that owes nothing to the temperature — together with an honest UNDECIDED
when the bracket does not settle the question.
The workflow is:
- Train a graded relation in soft mode, as usual.
- Measure how safe rounding it would be, with :func:
rounding_margin— the distance of each truth midpoint from the 0.5 decision boundary. A margin near zero means the crisp label is a coin flip and the certificate is not worth having. - Round and certify with :func:
round_and_certify, which thresholds the relation, re-evaluates exactly, and reportsPROVEN/REFUTED/UNDECIDEDper world, with a witness path where one exists. - Optionally export to nuXmv or NuSMV with :func:
to_smvand have an external checker confirm it.
:func:certificate_gap measures how often the soft answer and the exact one
disagree, which is the empirical version of the gap statement: if it is zero
on your frames, the soft model is already making the decisions the exact
checker would.
.. note:: The SMV export is structurally validated here — the generated module is parsed back and checked for well-formedness — but this package does not bundle nuXmv, so the round trip against a real model checker is left to the caller. Treat the exporter as producing input for a tool you then run, not as a verified oracle in itself.
Verdict ¶
CertificateResult ¶
Bases: NamedTuple
The outcome of :func:round_and_certify.
Attributes:
| Name | Type | Description |
|---|---|---|
verdicts |
List[str]
|
One of :class: |
bounds |
Tensor
|
The exact bounds the verdicts were read from. |
margin |
Tensor
|
Per-world rounding margin of the soft evaluation, i.e. how far each midpoint sat from the 0.5 boundary before rounding. Small values mean the certificate rests on a near-tie. |
relation |
Tensor
|
The rounded relation the certificate is about — not the relation passed in. A certificate is a statement about this frame. |
n_flipped |
int
|
How many entries of the relation the rounding moved by more than 0.25, a coarse measure of how much the certificate's frame differs from the learned one. |
Source code in torchmodal/verify.py
round_relation ¶
Threshold a graded relation into a crisp one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
accessibility
|
Tensor
|
|
required |
threshold
|
float
|
Entries at or above this become 1, the rest 0. |
0.5
|
Returns:
| Type | Description |
|---|---|
Tensor
|
A relation of the same shape and dtype containing only 0 and 1. |
Source code in torchmodal/verify.py
rounding_margin ¶
How far each truth value sits from the 0.5 decision boundary.
The crisp label a bound implies is midpoint >= 0.5. This returns
|midpoint - 0.5|, so 0.5 is maximally safe and 0.0 is a coin flip.
This is the counterpart, for the rounding step, of what
:func:torchmodal.functional.box_width_entropy is for the modal step: it
turns "is this certificate trustworthy?" into a number rather than a
judgement. A certificate read off a world whose margin is near zero should
not be reported without saying so.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bounds
|
Tensor
|
|
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
|
Example
import torch from torchmodal.verify import rounding_margin rounding_margin(torch.tensor([[0.5, 0.5], [0.0, 0.0]])).tolist() [0.0, 0.5]
Source code in torchmodal/verify.py
certify ¶
Read a per-world verdict off a truth interval.
The point of carrying an interval is that it can decline to answer:
PROVENwhen the whole interval sits at or abovethreshold, so every value it admits is true;REFUTEDwhen the whole interval sits below, so every value is false;UNDECIDEDwhen it straddles the boundary.
A library that says "I don't know" when it does not know is more useful
than one that rounds, which is why UNDECIDED is a first-class outcome
rather than an error.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bounds
|
Tensor
|
|
required |
threshold
|
float
|
Decision boundary. Default 0.5. |
0.5
|
Returns:
| Type | Description |
|---|---|
List[str]
|
A list of |
Source code in torchmodal/verify.py
certificate_gap ¶
Disagreement rate between the soft rounding and the exact answer.
The empirical form of the gap statement. Both arguments are rounded to crisp labels and compared; the result is the fraction of worlds where they differ. Zero means the soft model is already making exactly the decisions the exact checker would, which is the condition under which training against the soft operators is safe.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
soft_bounds
|
Tensor
|
|
required |
exact_bounds
|
Tensor
|
|
required |
threshold
|
float
|
Decision boundary. Default 0.5. |
0.5
|
Returns:
| Type | Description |
|---|---|
float
|
A float in [0, 1]. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the two shapes differ. |
Source code in torchmodal/verify.py
witness_path ¶
witness_path(accessibility: Tensor, start: int, goal: Tensor, threshold: float = 0.5) -> Optional[List[int]]
A shortest path from start to a world satisfying goal.
The concrete evidence behind a PROVEN reachability verdict. Breadth-
first, so the path is shortest, and None when no path exists — which
is itself the evidence behind a REFUTED one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
accessibility
|
Tensor
|
|
required |
start
|
int
|
Index of the world to start from. |
required |
goal
|
Tensor
|
|
required |
threshold
|
float
|
Edge and goal threshold. Default 0.5. |
0.5
|
Returns:
| Type | Description |
|---|---|
Optional[List[int]]
|
A list of world indices beginning with |
Source code in torchmodal/verify.py
round_and_certify ¶
round_and_certify(prop_bounds: Tensor, accessibility: Tensor, operator: str = 'ef', threshold: float = 0.5, tau: float = 0.1, serial: bool = True, **kwargs: Any) -> CertificateResult
Round a learned relation and certify a CTL property on it exactly.
The whole point of the exercise: a learned, graded relation is not a Kripke frame, so a statement about it is not a statement about anything checkable. Rounding produces a frame; evaluating exactly on that frame produces an answer with no temperature in it.
The certificate is about the rounded relation, not the learned one.
That frame is returned in the result so it can be reported alongside, and
n_flipped says how far it moved. If the margin is small or many
entries flipped, the certificate is about a frame the model did not quite
learn, and saying so is part of reporting it honestly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prop_bounds
|
Tensor
|
|
required |
accessibility
|
Tensor
|
|
required |
operator
|
str
|
A unary CTL operator from :mod: |
'ef'
|
threshold
|
float
|
Rounding and decision boundary. Default 0.5. |
0.5
|
tau
|
float
|
Temperature for the soft evaluation used to compute the margin. |
0.1
|
serial
|
bool
|
Repair dead ends before evaluating. Default |
True
|
**kwargs
|
Any
|
Forwarded to the fixpoint operator. |
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
CertificateResult
|
class: |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in torchmodal/verify.py
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | |
to_smv ¶
to_smv(accessibility: Tensor, labels: Dict[str, Sequence[float]], spec: Optional[str] = None, threshold: float = 0.5, module_name: str = 'main') -> str
Export a rounded Kripke frame as an SMV module for nuXmv or NuSMV.
The relation is thresholded into a transition relation over a single
state variable, and each entry of labels becomes a DEFINE
predicate over that variable.
.. warning:: This produces input for a model checker; it does not run one. The output is checked here for structural well-formedness only, since this package does not bundle nuXmv. Run the file through the checker before treating its answer as confirmation of anything.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
accessibility
|
Tensor
|
|
required |
labels
|
Dict[str, Sequence[float]]
|
Proposition name to per-world truth values, each of length
|
required |
spec
|
Optional[str]
|
An optional CTL specification, e.g. |
None
|
threshold
|
float
|
Edge and label threshold. Default 0.5. |
0.5
|
module_name
|
str
|
SMV module name. Default |
'main'
|
Returns:
| Type | Description |
|---|---|
str
|
The SMV source as a string. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If a label has the wrong length, or a dead end is present
— SMV requires a total transition relation, so run
:func: |
Example
import torch from torchmodal.verify import to_smv A = torch.tensor([[0.0, 1.0], [1.0, 0.0]]) "MODULE main" in to_smv(A, {"p": [1.0, 0.0]}) True
Source code in torchmodal/verify.py
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 | |