# Configuring VNNLib Properties for Neural Network Verification in VERONA

> Learn how to configure VNNLib properties for neural network verification in VERONA. This guide details using modular generators to create SMT-LIB constraints for robustness checks.

- Repository: [ADA research/verona](https://github.com/ada-research/verona)
- Tags: how-to-guide
- Published: 2026-02-23

---

**VERONA translates neural network verification requests into VNNLib property files by orchestrating modular property generators that emit SMT-LIB constraints for targeted or untargeted robustness checks.**

The VERONA framework (`ada-research/verona`) provides a structured pipeline for verifying neural network robustness. When configuring VNNLib properties for neural network verification in VERONA, users interact with a hierarchy of Python classes that abstract the complexity of SMT-LIB syntax while exposing fine-grained control over perturbation bounds and verification objectives.

## Core Components for VNNLib Property Generation

VERONA’s architecture decouples property specification from persistence and execution. The following components collaborate to produce valid VNNLib files:

### VNNLibProperty Dataclass

The `VNNLibProperty` class in [`ada_verona/database/vnnlib_property.py`](https://github.com/ada-research/verona/blob/main/ada_verona/database/vnnlib_property.py) serves as a simple container for generated specifications. It stores the property name, the textual VNNLib content, and an optional file path. Instances of this class are what generators return and what backend solvers consume.

### PropertyGenerator Abstract Base Class

All property generators inherit from `PropertyGenerator` defined in [`ada_verona/verification_module/property_generator/property_generator.py`](https://github.com/ada-research/verona/blob/main/ada_verona/verification_module/property_generator/property_generator.py). This abstract base enforces a consistent interface:

- `create_vnnlib_property`: Generates the actual VNNLib string given an input image, true label, and epsilon.
- `get_dict_for_epsilon_result`: Serializes results for database storage.
- `to_dict` / `from_dict`: Enables persistence of generator configuration.

### Concrete Generator Implementations

VERONA ships with two primary implementations for robustness verification:

**One2OnePropertyGenerator** ([`ada_verona/verification_module/property_generator/one2one_property_generator.py`](https://github.com/ada-research/verona/blob/main/ada_verona/verification_module/property_generator/one2one_property_generator.py)) handles **targeted** verification. It generates a property that is violated when the network’s output for a specific target class exceeds that of the true class. This is useful for checking if an adversary can force a specific misclassification.

**One2AnyPropertyGenerator** ([`ada_verona/verification_module/property_generator/one2any_property_generator.py`](https://github.com/ada-research/verona/blob/main/ada_verona/verification_module/property_generator/one2any_property_generator.py)) handles **untargeted** verification. It generates a property that is violated when *any* class other than the true class achieves a higher score than the true class. This checks for any possible adversarial example without specifying the destination class.

### VerificationContext Orchestration

The `VerificationContext` class in [`ada_verona/database/verification_context.py`](https://github.com/ada-research/verona/blob/main/ada_verona/database/verification_context.py) acts as the orchestration hub. It stores the network (`ONNXNetwork`), data point, temporary directory, and the chosen generator. Crucially, it provides `save_vnnlib_property`, which persists the generated property to disk and records the file path for downstream solver consumption.

## How VERONA Constructs a VNNLib Property

When configuring VNNLib properties for neural network verification in VERONA, the framework executes a five-step pipeline to translate a raw image into SMT-LIB constraints:

1. **Input preprocessing** – The raw image (`np.ndarray`) is clipped to user-specified data bounds (`data_lb`, `data_ub`) and optionally normalized using provided mean and standard deviation values.

2. **Variable declaration** – For each input pixel, a real-valued variable `X_i` is declared. For each output class, a real-valued variable `Y_i` is declared.

3. **Input constraints** – Upper- and lower-bound constraints are emitted for every `X_i` based on the perturbation magnitude `epsilon`, creating a box constraint around the preprocessed input.

4. **Output constraint** – 
   - **One2One**: Emits `(assert (or (and (>= Y_target Y_true))))` – the property is falsified if the target class can be made at least as large as the true class.
   - **One2Any**: Emits an `or` over all non-true classes `i` of `(and (>= Y_i Y_true))`.

5. **Packaging** – The generated string is wrapped in a `VNNLibProperty` instance, which `VerificationContext.save_vnnlib_property` can persist to a temporary file for the backend solver.

## Practical Example: Creating a VNNLib Property in Python

The following example demonstrates configuring VNNLib properties for neural network verification in VERONA using the `One2OnePropertyGenerator` for a targeted attack scenario:

```python

# 1️⃣ Choose a property generator

from ada_verona.verification_module.property_generator.one2one_property_generator import One2OnePropertyGenerator
generator = One2OnePropertyGenerator(
    target_class=3, 
    number_classes=10, 
    data_lb=0, 
    data_ub=1
)

# 2️⃣ Load a network and a data point (mocked here for brevity)

from ada_verona.database.machine_learning_model.onnx_network import ONNXNetwork
network = ONNXNetwork(name="mnist_onnx", path="models/mnist.onnx")

from ada_verona.database.dataset.data_point import DataPoint
dp = DataPoint(id="sample_001", label=7, data="path/to/image.npy")

# 3️⃣ Create a verification context with a temporary folder

from pathlib import Path
from ada_verona.database.verification_context import VerificationContext
tmp_dir = Path("/tmp/verona_run")
ctx = VerificationContext(
    network=network, 
    data_point=dp, 
    tmp_path=tmp_dir,
    property_generator=generator, 
    save_epsilon_results=True
)

# 4️⃣ Build a VNNLib property for a concrete epsilon

import numpy as np
image = np.load(dp.data)                     # shape (1, 28, 28) for MNIST

epsilon = 0.05
vnn_prop = generator.create_vnnlib_property(image, dp.label, epsilon)

# 5️⃣ Persist the property – the context records the file path automatically

ctx.save_vnnlib_property(vnn_prop)
print(f"Saved VNNLib file at: {vnn_prop.path}")

```

For untargeted verification, replace the generator import with `One2AnyPropertyGenerator` and omit the `target_class` parameter.

## Summary

- **VNNLibProperty** ([`ada_verona/database/vnnlib_property.py`](https://github.com/ada-research/verona/blob/main/ada_verona/database/vnnlib_property.py)) stores the textual specification and file path.
- **PropertyGenerator** defines the interface, with **One2OnePropertyGenerator** and **One2AnyPropertyGenerator** providing targeted and untargeted robustness checks respectively.
- **VerificationContext** ([`ada_verona/database/verification_context.py`](https://github.com/ada-research/verona/blob/main/ada_verona/database/verification_context.py)) orchestrates the pipeline, handling temporary file persistence via `save_vnnlib_property`.
- The VNNLib construction pipeline preprocesses inputs, declares variables, emits box constraints for epsilon perturbations, and asserts output inequalities based on the verification objective.

## Frequently Asked Questions

### How do I choose between One2One and One2Any property generators?

Use **One2OnePropertyGenerator** when you want to verify whether an adversary can force the network to classify an input as a specific target class different from the true label. Use **One2AnyPropertyGenerator** for untargeted verification, which checks if *any* misclassification is possible regardless of the destination class. The former is stricter and useful for targeted attack analysis, while the latter provides a general robustness certificate.

### What data bounds should I specify when instantiating a property generator?

The `data_lb` and `data_ub` parameters should match the valid input range of your dataset after any normalization. For standard image datasets like MNIST or CIFAR-10, use `data_lb=0.0` and `data_ub=1.0` if pixels are normalized to [0,1], or `data_lb=0` and `data_ub=255` for raw byte values. These bounds are used in [`ada_verona/verification_module/property_generator/property_generator.py`](https://github.com/ada-research/verona/blob/main/ada_verona/verification_module/property_generator/property_generator.py) to clip inputs and define valid perturbation ranges.

### How does VERONA handle temporary file storage for VNNLib properties?

The `VerificationContext` class manages temporary directories through its `tmp_path` parameter. When you call `ctx.save_vnnlib_property(vnn_prop)`, the context writes the VNNLib content to a file within that temporary directory and updates the `path` attribute of the `VNNLibProperty` instance. This ensures that backend solvers can locate the property file while keeping the filesystem organized during batch verification runs.

### Can I extend VERONA to support custom verification properties beyond robustness?

Yes, by subclassing `PropertyGenerator` in [`ada_verona/verification_module/property_generator/property_generator.py`](https://github.com/ada-research/verona/blob/main/ada_verona/verification_module/property_generator/property_generator.py) and implementing the `create_vnnlib_property` method. Your subclass must emit valid SMT-LIB constraints following the VNNLib format. The modular design means you only need to implement the logic for declaring variables and asserting constraints; the `VerificationContext` will handle file I/O and result aggregation automatically.