Please note that this page is under construction.

Flow

This page serves to explain the overall flow of the toolbox. For examples and explanation on how to use specific verification functions, please refer to the tutorials.

In general, verification algorithms follow the paradigm of Branch and Bound. This process can be summarized into three steps:

  1. split the input set into smaller sets, which we call "branches",
  2. propagate the bound through the model for a given branch,
  3. check whether the bound of the final layer satisfies the output specificaiton.

Repeat or terminate the process based on the result.

ModelVerification.jl uses a modularized code structure to support various combinations of search methods, split methods, and solvers for a variety of neural network architectures and geometric representations for the safety specifications. After reading through this section, the user should have an overall idea of the flow of the toolbox and the design philosophy behind it. Thanks to the highly modularized structure of the toolbox, the user can add additional functionalities at any layer of the verification process.

Definition of Terms

Here, we define some terms that are unique to the toolbox or are used differently compared to the typical usage.

  • Instance: combination of all the necessary information to define an "instance" of neural network verification problem. This is consisted of:
  • Propagation Method: this is the bound propagation method used for verifying the problem. In other words, it is the choice of term to represent the "solver": all the solvers in ModelVerification.jl are represented as a propagation method. However, this is different from the methods in propagate.jl. This will be clearer in the following explanations.
  • Model / (Deep) Neural Network / Network: these terms are used interchangeably and represent the deep neural network (DNN) to be verified.
  • Node: (This is not equivalent to a "neuron" in a traditional deep learning sense.) This refers to a "node" in a computational-graph sense.
  • Layer: (This is not equivalent to a "layer" in a traditional deep learning sense.) This refers to an operation at a node, such as ReLU activation function.
  • BaB: "Branch-and-Bound" is a method that creates a binary tree for the search space where the verification is employed.
  • Set vs Bound: One set is composed of bounds. E.g., for the output reachable set is a union of output bounds. But we use these terms interchangeably throughout the toolbox.

1. Creating an instance: what kind of verification problem do you want to solve?

Let's first create an instance. An instance contains all the information required to run the verify function. This function does the heavy-lifting where the verification problem is solved. As long as the user properly defines the problem and solver methods, this is the only function the user has to call. To run verify, the user has to provide the following arguments. These collectively define an "instance":

  • SearchMethod: Algorithm for iterating through the branches, such as BFS (breadth-first search) and DFS (depth-first search).
  • SplitMethod: Algorithm for splitting an unknown branch into smaller pieces for further refinement. This is also used in the first step of verify to populate the branches bank. In other words, it splits the input specification into branches to facilitate the propagation process.
  • PropMethod: Solver used to verify the problem, such as Ai2 and Crown.
  • Problem: Problem to be verified. It consists of a Network, and input and output specifications.

This toolbox design choice allows for extensive customization of methods and solvers by defining different search or split methods. The user simply needs to add their chosen methods in the specific files (search.jl and split.jl), which the solvers will automatically use for the verification process.

SearchMethod

SearchMethod describes the strategy the solver uses when iterating through the branches. Currently, ModelVerification.jl only supports Breath-first Search (BFS). The solver can exploit parallel analysis of the nodes in the BaB by indicating a batch_size is greater than 1.

SplitMethod

SplitMethod specifies how many splits and where they are performed on a single node of the BaB. Depending on the SplitMethod, the solver will split either the input space or the ReLU nodes.

The following split methods are supported:

  • Bisectection (Bisect): splits either the input space or the ReLU nodes.
  • Branch-and-bound (BaBSR): splits the ReLU nodes.

PropMethod

PropMethod is the solver to be used for the verification.

The following solvers are supported:

  • ExactReach
  • Ai2
  • ImageStar
  • ImageZono
  • Crown
  • Alpha-Crown
  • Beta-Crown

Problem

Problem is composed by the model to be verified and the input & output specifications. Specifically, this part of the "instance" encodes what we want to verify rather than how we achieve the formal verification results.

2. Verifying the instance: spinning through the branches - where the magic happens!

verify is the main function called by ModelVerification.jl to start the verification process of the "instance" provided by the user.

ModelVerification.verifyFunction

verify(searchmethod::SearchMethod, splitmethod::SplitMethod, propmethod::PropMethod, problem::Problem; timeout=86400, attackrestart=100, collectbound=false, summary=false, pre_split=nothing)

This is the main function for verification. It takes in a search method, a split method, a propagation method, and a problem, and returns a result. The result is of type ResultInfo, which is a wrapper for the following Result types: BasicResult, CounterExampleResult, AdversarialResult, ReachabilityResult, EnumerationResult, or timeout. For each Result, the status field is either :violated, :verified, or :unknown. Optional arguments can be passed to the function to control the timeout, the number of restarts for the attack, whether to collect the bounds for each branch, whether to print a summary of the verification process, and whether to pre-split the problem.

Arguments

  • search_method (SearchMethod): The search method, such as BFS, used to search through the branches.
  • split_method (SplitMethod): The split method, such as Bisect, used to split the branches.
  • prop_method (PropMethod): The propagation method, such as Ai2, used to propagate the constraints.
  • problem (Problem): The problem to be verified - consists of a network, input set, and output set.
  • time_out (Int): The timeout in seconds. Defaults to 86400 seconds, or 24 hours. If the timeout is reached, the function returns :timeout.
  • attack_restart (Int): The number of restarts for the attack. Defaults to 100.
  • collect_bound (Bool): Whether to collect the bounds for each branch.
  • summary (Bool): Whether to print a summary of the verification process.
  • pre_split: A function that takes in a Problem and returns a Problem with the input set pre-split. Defaults to nothing.
  • search_adv_bound (Bool): Whether to search the maximal input bound that can pass the verification (get :holds) with the given setting.

Returns

  • The result is ResultInfo, the status field is either :violated, :verified, :unknown, or :timeout. The info is a dictionary that contains other information.
source

prepare_problem

The first step of verify is prepare_problem which preprocesses the Problem into a form that is compatible with the verification solver. Its main two functionalities are:

  1. Retrieves the model information and stores it inside model_info,
  2. Exploits the init_bound function which returns the geometry representation that matches the solver requirements. For instance, since CROWN is a backward propagation method, the init_bound function returns the geometry used to encode the output specification.

The result of prepare_problem are two variables:

  1. model_info: structure that contains information about the Flux model,
  2. prepared_problem: Problem with a processed input-output specification.

search_branches

ModelVerification.search_branchesMethod
search_branches(search_method::BFS, split_method, prop_method, problem, model_info; 
                collect_bound=false, 
                comp_verified_ratio=false,
                pre_split=nothing, 
                verbose=false,
                time_out=nothing)

Searches through the branches in the branch bank, branches, until the branch bank is empty or time out. In each iteration (up to search_method.max_iter), a batch of unverified branches will be extracted from the branch bank. Then, the following is performed to verify the model:

  1. prepare_method initializes the bound of the start node of the

computational graph based on the geometric representation and corresponding solver.

  1. propagate propagates the bound from the start node to the end node of the

computational graph.

  1. process_bound processes the bounds resulting from the propagation

accordingly to the solver, prop_method. For example, for Ai2-based methods, process_bound simply returns the bounds from the propagate step. However, for Crown-based methods, process_bound post-processes the bounds.

  1. check_inclusion decides whether the bound of the end node, the reachable

set, satisfies the output specification or not. 1. If not, i.e., :violated, then the counterexample is returned and the verification process terminates. 2. If yes, i.e., :holds, then the current branch is verified and the function starts Step 1 again for the next branch, if any. 3. If unknown, i.e., :unknown, further refinement of the problem is preformed using split_branch, which divides the current branch into smaller pieces and puts them into the branch bank for further verification. Such :unknown status results due to the overapproximation introduced in the verification process.

If the branch bank is empty after going through search_method.max_iter number of verification procedures, the model is verified to be valid and returns :holds. If the branch bank is not empty, the function returns :unknown.

Arguments

  • search_method (BFS): Breadth-first Search method for iteratively going through the branches.
  • split_method: Method for splitting the branches when further refinement is needed. This inclueds methods such as Bisect and BaBSR.
  • prop_method: Propagation method used for the verification process. This is one of the solvers used to verify the given model.
  • problem: Problem definition for model verification.
  • model_info: Structure containing the information of the neural network to be verified.
  • collect_bound(optional): false, whether return the verified bound.
  • comp_verified_ratio(optional): false, whether to return how much percent of the input set is verified safe
  • pre_split(optional): nothing, the number of split before any propagation. This is particularly useful for large input set that could lead to memory overflow.
  • verbose(optional): false, whether to print things
  • time_out(optional): nothing, time out to halt.

Returns

  • BasicResult(:holds) if all the reachable sets are within the corresponding output specifications in the batch.
  • BasicResult(:unknown) if the function failed to make a firm decision within the given time. This is due to the overapproximation introduced in the verification process.
  • CounterExampleResult(:violated, x) if a reachable set is not within the corresponding output specification and there is a counterexample found.
source

search_branches is the core function used for the verification process. It consists of several subfunctions that we are going to summarize in the following. At first, the function initializes the branch bank for the entire safety property's input-output domain. This can be done by advance_split, which splits the input-output domain using the given split method. Thus, each "branch" is a subpart of the input-output domain. The function seeks to verify all the branches and if it cannot provide a result (i.e., we obtain an :unknown answer), the function proceeds to split branch for a more refined verification process.. If the function verifies all the branches within the given maximum number of iterations, then we obtain a :holds answer. If the function finds any branch that does not satisfy the safety property, then it returns :violated.

For each iteration, i.e., for each branch, the function calls the following subfunctions to verify the branch:

  1. prepare_method: this function retrieves all the information to perform either the forward or backward propagation of the input domain of the branch. The result is stored in two variables called batch_out_spec, batch_info, which contain the batch of the outputs and a dictionary containing all the information of each node in the model. prepare_method calls the following functions in sequence:

    • init_propagation: Differentiates between ForwardProp and BackwardProp. If the solver being used employs a forward propagation method, then we start propagating from the input nodes. If it employs a backward propagation method, then we start from the output nodes.
    • init_batch_bound: Calls init_bound, which returns either the input or output geometry representation based on the type of propagation to perform.
  2. The result of the previous function is then used in the propagate function. This function propagates the starting bounds through the model using the specified propagation method, i.e., the solver. The propagate function acts as the overall logic for propagating the branch through the model and performs the propagation based on the layer operation (linear, ReLU, etc.) and the geometric representation for the bounds. The user can add additional layer operators in the propagate/operators folder. Moreover, the toolbox supports skip connections. The propagate function returns batch_bound, the bound of the output node, and an augmented batch_info dictionary with the output bound information added.

  3. Now, process_bounds returns the reachable bounds obtained from the propagate function. Depending on the solver, the reachable bounds may be post-processed to optimized the verification procedure.

  4. Finally, check_inclusion checks for the overlapping between the reachable bounds obtained from the propagation and the safety property's output bounds. Since we are using overapproximation of the output reachable set, the only case in which we can obtain :holds result is when the output reachable set is fully contained in the user-specified output set. On the other hand, the :violated result is only possible when the sets are completely disjoint. In all other cases, we have an :unknown answer and we proceed with the branch splitting method below to populate the branch bank.

  5. split_branch is used to split the current branch with :unknown answer into two separate branches which are split based on the split_method. The sub-branches are then added to the "branches" bank.

We continue this loop until either get a :violated result, a :hold result for all the branches, or reach the maximum number of iterations.

search_adv_input_bound

If the verification result from verify is not :holds, i.e., either :unknown or :violated, then search_adv_input_bound searches for the maximul input bound that can pass the verification, i.e., retrieves :holds, with the given setting. This information is passed to the ResultInfo as a dictionary field so that the user can check.

ModelVerification.search_adv_input_boundFunction
search_adv_input_bound(search_method::SearchMethod, 
                       split_method::SplitMethod, 
                       prop_method::PropMethod, 
                       problem::Problem;
                       eps = 1e-3)

This function is used to search the maximal input bound that can pass the verification (get :holds) with the given setting. The search is done by binary search on the input bound that is scaled by the given ratio. This function is called in verify function when search_adv_bound is set to true and the initial verification result is :unknown or :violated.

Arguments

  • search_method (SearchMethod): The search method, such as BFS, used to search through the branches.
  • split_method (SplitMethod): The split method, such as Bisect, used to split the branches.
  • prop_method (PropMethod): The propagation method, such as Ai2, used to propagate the constraints.
  • problem (Problem): The problem to be verified - consists of a network, input set, and output set.
  • eps (Real): The precision of the binary search. Defaults to 1e-3.

Returns

  • The maximal input bound that can pass the verification (get :holds) with the given setting.
source

3. Results and how to interpret them: so is my model good to go?

Once the verify function is over, it returns a ResultInfo that contains the status (either :hold, :violated, :unknown) and a dictionary that contains any other additional information needed to understand the verification results in detail, such as the verified bounds, adversarial input bounds, etc.

The following are the Result types used interally for the toolbox to differentiate between different verification results. The result is either a BasicResult, CounterExampleResult, AdversarialResult, ReachabilityResult, or EnumerationResult (to-be-supported). The status field is either :violated, :holds, or :unknown.

Output resultExplanation
[BasicResult::holds]The input-output constraint is always satisfied.
[BasicResult::violated]The input-output constraint is violated, i.e., it exists a single point in the input constraint that violates the property.
[BasicResult::unknown]Could not be determined if the property holds due to timeout in the computation.
[CounterExampleResult]Like BasicResult, but also returns a counterexample if one is found (if status = :violated). The counterexample is a point in the input set that, after the NN, lies outside the output constraint set.
[AdversarialResult]Like BasicResult, but also returns the maximum allowable disturbance in the input (if status = :violated).
[ReachabilityResult]Like BasicResult, but also returns the output reachable set given the input constraint (if status = :violated).
[EnumerationResult]Set of all the (un)safe regions in the safety property's domain.

For more information, please refer to Output (Verification Results).