Custom Verification Workflows¶
End-to-end patterns for verifying your own neural network, from loading to result interpretation. Covers FFNNs, CNNs, GNNs, and NNCS.
Generic FFNN Workflow¶
%% 1. Load or build network
% From ONNX:
dlnet = importNetworkFromONNX('model.onnx', InputDataFormats='BC');
net = matlab2nnv(dlnet);
% Or build manually:
net = NN({FullyConnectedLayer(W1,b1), ReluLayer(), FullyConnectedLayer(W2,b2)});
%% 2. Define input set
input_set = Star(lb, ub); % Box region
%% 3. Configure and run reachability
reachOptions = struct;
reachOptions.reachMethod = 'approx-star';
output_sets = net.reach(input_set, reachOptions);
%% 4. Check safety property
unsafe_region = HalfSpace(G, g); % Gx <= g defines unsafe outputs
result = verify_specification(output_sets, unsafe_region);
% 1=safe, 0=unsafe, 2=unknown
%% 5. Extract output bounds
[lb_out, ub_out] = output_sets(1).getRanges();
CNN with ImageStar¶
net = matlab2nnv(cnn_model);
% L-inf perturbation around an image
epsilon = 0.01;
lb = max(single(img) - epsilon, 0);
ub = min(single(img) + epsilon, 1);
IS = ImageStar(lb, ub);
reachOptions.reachMethod = 'approx-star';
result = net.verify_robustness(IS, reachOptions, target_class);
GNN with GraphStar¶
gnn = GNN(layers, A_norm, adj_list, E);
% Selective node feature perturbation
eps_matrix = zeros(numNodes, numFeatures);
eps_matrix(:, perturb_features) = epsilon;
GS = GraphStar(X, -eps_matrix, eps_matrix);
reachOptions.reachMethod = 'approx-star';
output_sets = gnn.reach(GS, reachOptions);
% Per-node safety check
[lb, ub] = output_sets.getRanges();
NNCS Closed-Loop¶
controller = matlab2nnv(nn_controller);
plant = NonLinearODE(6, 1, @dynamics, reachStep, controlPeriod, C);
nncs = NonlinearNNCS(controller, plant);
nncs.feedbackMap = [0];
reachPRM.init_set = Star(x0_lb, x0_ub);
reachPRM.ref_input = Star(ref_lb, ref_ub);
reachPRM.numSteps = 50;
reachPRM.reachMethod = 'approx-star';
[R, rT] = nncs.reach(reachPRM);
Writing Custom Safety Specifications¶
Safety properties are defined as HalfSpace constraints on the output:
% Property: output y1 should be >= 0 (unsafe if y1 < 0)
% Unsafe region: y1 <= 0 → [-1 0 ... 0] * y <= 0
unsafe = HalfSpace([-1 zeros(1, output_dim-1)], 0);
% Property: class c should have highest score (robustness)
% Unsafe: any other class j has score >= class c
% For each j != c: y_j - y_c >= 0 → e_j - e_c (as row) * y <= 0
for j = 1:num_classes
if j ~= target
G = zeros(1, num_classes);
G(j) = 1; G(target) = -1;
unsafe_j = HalfSpace(G, 0);
result_j = verify_specification(output_sets, unsafe_j);
end
end
% Combined: use verify_robustness() which handles this automatically
result = net.verify_robustness(input_set, reachOptions, target);
Choosing the Right Method¶
Scenario |
Method |
Why |
|---|---|---|
Small FFNN, need proof |
|
Complete: can prove both safety and unsafety |
Medium CNN |
|
Good balance of precision and speed (default) |
Large network, quick screen |
|
Fastest, no LP solving required |
Very large / intractable |
Conformal prediction |
Probabilistic guarantees, architecture-agnostic |
Neural ODE (linear) |
|
Matrix exponential, exact for linear systems |
Neural ODE (nonlinear) |
|
CORA integration for nonlinear dynamics |