def boundEqConflict(): ''' Simple presecion exmaple. Only two nodes that are conncted with ReLU, and an equation that asks if the ReLU output is very small negative :return: ''' network = MarabouCore.InputQuery() network.setNumberOfVariables(2) network.setLowerBound(0, -5) network.setUpperBound(0, 5) network.setLowerBound(1, 0) network.setUpperBound(1, 5) MarabouCore.addReluConstraint(network, 0, 1) eq = MarabouCore.Equation(MarabouCore.Equation.LE) eq.addAddend(1, 1) eq.setScalar(-10**-4) # -10 ** -4 works network.addEquation(eq) verbose = 2 vars1, stats1 = MarabouCore.solve(network, "", 0, verbose) if len(vars1) > 0: print("SAT") print(vars1) return False else: print("UNSAT") return True
def add_hidden_state_equations(inputQuery, variables_first_index, input_weight, hidden_weight, num_iterations): ''' add all hidden state equations: input_weight * x1 = s1b for each k > 1 input_weight * xi + hidden_weight * s(k-1)f = sib and ReLu's :param inputQuery: query to append to :param variables_first_index: the first index of the hidden vector variable :param input_weight: the weight in the input :param hidden_weight: the weight for the hidden vector :param num_iterations: number of iterations :return: ''' equation1 = MarabouCore.Equation() equation1.addAddend(input_weight, 0) equation1.addAddend(-1, variables_first_index) equation1.setScalar(0) inputQuery.addEquation(equation1) for k in range(1, num_iterations): cur_equation = MarabouCore.Equation() cur_equation.addAddend(input_weight, k) # xk cur_equation.addAddend(hidden_weight, variables_first_index + (2 * k) - 1) # s(k-1)f cur_equation.addAddend(-1, variables_first_index + (2 * k)) # skb cur_equation.setScalar(0) inputQuery.addEquation(cur_equation) # ReLu's for k in range(variables_first_index, variables_first_index + 2 * num_iterations, 2): MarabouCore.addReluConstraint(inputQuery, k, k + 1)
def add_output_equations(network, rnn_output_idxs, output_weight, output_bias): ''' build equations for the output :param network: network to append equations and variables to :param rnn_output_idxs: the output indices of the previous layer :param output_weight: Weights to multiply the previous layer :param output_bias: The bias of each equation :return: list of indices of output classes ''' assert (len(rnn_output_idxs) == output_weight.shape[1]) assert (output_weight.shape[0] == len(output_bias)) last_idx = network.getNumberOfVariables() output_idxs = [] network.setNumberOfVariables( last_idx + (output_weight.shape[0] * 2)) # *2 because of the relu for i in range(output_weight.shape[0]): b_variable_idx = last_idx + (2 * i) f_variable_idx = last_idx + 1 + (2 * i) output_idxs.append(f_variable_idx) network.setLowerBound(b_variable_idx, -large) network.setUpperBound(b_variable_idx, large) network.setLowerBound(f_variable_idx, 0) network.setUpperBound(f_variable_idx, large) MarabouCore.addReluConstraint(network, b_variable_idx, f_variable_idx) output_eq = MarabouCore.Equation() for j in range(output_weight.shape[1]): output_eq.addAddend(output_weight[i, j], rnn_output_idxs[j]) output_eq.addAddend(-1, b_variable_idx) output_eq.setScalar(-output_bias[i]) network.addEquation(output_eq) return output_idxs
def define_network(): network = MarabouCore.InputQuery() network.setNumberOfVariables(3) # x network.setLowerBound(0, -1) network.setUpperBound(0, 1) network.setLowerBound(1, 1) network.setUpperBound(1, 2) # y network.setLowerBound(2, -large) network.setUpperBound(2, large) MarabouCore.addReluConstraint(network, 0, 1) # y - relu(x) >= 0 output_equation = MarabouCore.Equation() output_equation.addAddend(1, 2) output_equation.addAddend(-1, 1) output_equation.setScalar(0) # output_equation.dump() network.addEquation(output_equation) # y <= n * 0.01 property_eq = MarabouCore.Equation(MarabouCore.Equation.LE) property_eq.addAddend(1, 1) property_eq.setScalar(3) return network
def add_intermediate_layer_equations(): first_idx = self.network.getNumberOfVariables() # times 2 for the b and f variables self.network.setNumberOfVariables(first_idx + (output_weights.shape[1] * 2)) b_indices = range(first_idx, first_idx + (output_weights.shape[1] * 2), 2) f_indices = range(first_idx + 1, first_idx + (output_weights.shape[1] * 2), 2) for i in range(output_weights.shape[1]): cur_b_idx = b_indices[i] cur_f_idx = f_indices[i] # b variable self.network.setLowerBound(cur_b_idx, -LARGE) self.network.setUpperBound(cur_b_idx, LARGE) # f variable self.network.setLowerBound(cur_f_idx, 0) self.network.setUpperBound(cur_f_idx, LARGE) MarabouCore.addReluConstraint(self.network, cur_b_idx, cur_f_idx) # b equation eq = MarabouCore.Equation() for j, w in enumerate(output_weights[:, i]): eq.addAddend(w, prev_layer_idx[j]) eq.setScalar(-output_bias_weights[i]) eq.addAddend(-1, cur_b_idx) self.network.addEquation(eq) return f_indices
def define_positive_sum_network(xlim=(-1, 1)): ''' Defines the positive_sum network in a marabou way, without the recurrent part i.e. we define: s_i b = s_i-1 f + x_i y = s_i f :param xlim: how to limit the input to the network :return: query to marabou that defines the positive_sum rnn network (without recurent) ''' num_params_for_cell = 5 # Plus one is for the invariant proof, we will add a slack variable positive_sum_rnn_query = MarabouCore.InputQuery() positive_sum_rnn_query.setNumberOfVariables(num_params_for_cell) # + extra_params) # x positive_sum_rnn_query.setLowerBound(0, xlim[0]) positive_sum_rnn_query.setUpperBound(0, xlim[1]) # s_i-1 f (or temp in some of my notes) positive_sum_rnn_query.setLowerBound(1, 0) positive_sum_rnn_query.setUpperBound(1, large) # s_i b positive_sum_rnn_query.setLowerBound(2, -large) positive_sum_rnn_query.setUpperBound(2, large) # s_i f positive_sum_rnn_query.setLowerBound(3, 0) positive_sum_rnn_query.setUpperBound(3, large) # y positive_sum_rnn_query.setLowerBound(4, -large) positive_sum_rnn_query.setUpperBound(4, large) # s_i b = x_i * 1 + s_i-1 f * 1 update_eq = MarabouCore.Equation() update_eq.addAddend(1, 0) update_eq.addAddend(1, 1) update_eq.addAddend(-1, 2) update_eq.setScalar(0) # update_eq.dump() positive_sum_rnn_query.addEquation(update_eq) # s_i f = ReLu(s_i b) MarabouCore.addReluConstraint(positive_sum_rnn_query, 2, 3) # y - skf = 0 output_equation = MarabouCore.Equation() output_equation.addAddend(1, 4) output_equation.addAddend(-1, 3) output_equation.setScalar(0) # output_equation.dump() positive_sum_rnn_query.addEquation(output_equation) return positive_sum_rnn_query
def getMarabouQuery(self): """Function to convert network into Marabou InputQuery Returns: :class:`~maraboupy.MarabouCore.InputQuery` """ ipq = MarabouCore.InputQuery() ipq.setNumberOfVariables(self.numVars) i = 0 for inputVarArray in self.inputVars: for inputVar in inputVarArray.flatten(): ipq.markInputVariable(inputVar, i) i += 1 i = 0 for outputVar in self.outputVars.flatten(): ipq.markOutputVariable(outputVar, i) i += 1 for e in self.equList: eq = MarabouCore.Equation(e.EquationType) for (c, v) in e.addendList: assert v < self.numVars eq.addAddend(c, v) eq.setScalar(e.scalar) ipq.addEquation(eq) for r in self.reluList: assert r[1] < self.numVars and r[0] < self.numVars MarabouCore.addReluConstraint(ipq, r[0], r[1]) for m in self.maxList: assert m[1] < self.numVars for e in m[0]: assert e < self.numVars MarabouCore.addMaxConstraint(ipq, m[0], m[1]) for b, f in self.absList: MarabouCore.addAbsConstraint(ipq, b, f) for b, f in self.signList: MarabouCore.addSignConstraint(ipq, b, f) for disjunction in self.disjunctionList: MarabouCore.addDisjunctionConstraint(ipq, disjunction) for l in self.lowerBounds: assert l < self.numVars ipq.setLowerBound(l, self.lowerBounds[l]) for u in self.upperBounds: assert u < self.numVars ipq.setUpperBound(u, self.upperBounds[u]) return ipq
def getMarabouQuery(self): """ Function to convert network into Marabou Query Returns: ipq: (MarabouCore.InputQuery) representing query """ ipq = MarabouCore.InputQuery() ipq.setNumberOfVariables(self.numVars) print("num vars = ", self.numVars) i = 0 # TODO: this is necessary, so IF should be added (if user define -> use the userdefined, else use regular inputs) if len(self.userDefineInputVars) > 0: for inputVar in self.userDefineInputVars: ipq.markInputVariable(inputVar, i) i += 1 print("userDefineInputVar", inputVar) else: for inputVarArray in self.inputVars: for inputVar in inputVarArray.flatten(): # ipq.markInputVariable(inputVar, i) i += 1 print("inputVar", inputVar) i = 0 for outputVar in self.outputVars.flatten(): ipq.markOutputVariable(outputVar, i) i += 1 print("outputVar", outputVar) for e in self.equList: eq = MarabouCore.Equation(e.EquationType) for (c, v) in e.addendList: assert v < self.numVars eq.addAddend(c, v) eq.setScalar(e.scalar) ipq.addEquation(eq) for r in self.reluList: assert r[1] < self.numVars and r[0] < self.numVars MarabouCore.addReluConstraint(ipq, r[0], r[1]) for m in self.maxList: assert m[1] < self.numVars for e in m[0]: assert e < self.numVars MarabouCore.addMaxConstraint(ipq, m[0], m[1]) for l in self.lowerBounds: assert l < self.numVars ipq.setLowerBound(l, self.lowerBounds[l]) for u in self.upperBounds: assert u < self.numVars ipq.setUpperBound(u, self.upperBounds[u]) return ipq
def add_rnn_cell(query, input_weights, hidden_weight, num_iterations, bias=0, print_debug=False): ''' Create rnn cell --> add 4 parameters to the query and the equations that describe the cell The added parameters are (same order): i, s_i-1 f, s_i b, s_i f :param query: the network so far (will add to this) :param input_weights: list of tuples, each tuple (variable_idx, weight) :param hidden_weight: the weight inside the cell :param num_iterations: Number of iterations the cell runs :return: the index of the last parameter (which is the output of the cell) ''' last_idx = query.getNumberOfVariables() query.setNumberOfVariables(last_idx + 4) # i, s_i-1 f, s_i b, s_i f # i # TODO: when doing this we make the number of iterations to be n_iterations + 1 query.setLowerBound(last_idx, 0) query.setUpperBound(last_idx, num_iterations) # s_i-1 f query.setLowerBound(last_idx + 1, 0) query.setUpperBound(last_idx + 1, large) # s_i b query.setLowerBound(last_idx + 2, -large) query.setUpperBound(last_idx + 2, large) # s_i f query.setLowerBound(last_idx + 3, 0) query.setUpperBound(last_idx + 3, large) # s_i f = ReLu(s_i b) MarabouCore.addReluConstraint(query, last_idx + 2, last_idx + 3) # s_i-1 f >= i * \sum (x_j_min * w_j) # prev_min_eq = MarabouCore.Equation(MarabouCore.Equation.LE) # prev_min_eq.addAddend(1, last_idx + 1) # prev_min_eq.addAddend(1, last_idx + 1) # s_i b = x_j * w_j for all j connected + s_i-1 f * hidden_weight update_eq = MarabouCore.Equation() for var_idx, weight in input_weights: update_eq.addAddend(weight, var_idx) update_eq.addAddend(hidden_weight, last_idx + 1) update_eq.addAddend(-1, last_idx + 2) update_eq.setScalar(-bias) # if print_debug: # update_eq.dump() query.addEquation(update_eq) return last_idx + 3
def assert_relu_constraint(self, relu): if len(np.array(relu.varin).flatten()) > 1: print("ERROR: relu.varin is not scalar! It has length", len(relu.varin)) raise NotImplementedError else: # truly, the ok case self.num_relu += 1 MarabouCore.addReluConstraint(self.ipq, self.get_new_var(relu.varin), self.get_new_var(relu.varout))
def getMarabouQuery(self): """ Function to convert network into Marabou Query Returns: ipq: (MarabouCore.InputQuery) representing query """ ipq = MarabouCore.InputQuery() ipq.setNumberOfVariables(self.numVars) i = 0 for inputVarArray in self.inputVars: for inputVar in inputVarArray.flatten(): ipq.markInputVariable(inputVar, i) i += 1 i = 0 for outputVar in self.outputVars.flatten(): ipq.markOutputVariable(outputVar, i) i += 1 for e in self.equList: eq = MarabouCore.Equation(e.EquationType) for (c, v) in e.addendList: assert v < self.numVars eq.addAddend(c, v) eq.setScalar(e.scalar) ipq.addEquation(eq) for r in self.reluList: assert r[1] < self.numVars and r[0] < self.numVars MarabouCore.addReluConstraint(ipq, r[0], r[1]) for m in self.maxList: assert m[1] < self.numVars for e in m[0]: assert e < self.numVars MarabouCore.addMaxConstraint(ipq, m[0], m[1]) for l in self.lowerBounds: assert l < self.numVars ipq.setLowerBound(l, self.lowerBounds[l]) for u in self.upperBounds: assert u < self.numVars ipq.setUpperBound(u, self.upperBounds[u]) for i, var in enumerate(self.inputVars[0]): ipq.markInputVariable(i, var) for i, var in enumerate(self.outputVars[0]): ipq.markOutputVariable(i, var) return ipq
def define_ipq(property_bound): """ This function defines a simple input query directly through MarabouCore Arguments: property_bound: (float) value of upper bound for x + y Returns: ipq (MarabouCore.InputQuery) input query object representing network and constraints """ ipq = MarabouCore.InputQuery() ipq.setNumberOfVariables(3) # x ipq.setLowerBound(0, -1) ipq.setUpperBound(0, 1) # relu(x) ipq.setLowerBound(1, 0) ipq.setUpperBound(1, LARGE) # y ipq.setLowerBound(2, -LARGE) # if an upper/lower bound is not supplied to Marabou, Marabou uses float min/max MarabouCore.addReluConstraint(ipq, 0, 1) # y - relu(x) = 0 output_equation = MarabouCore.Equation() output_equation.addAddend(1, 2) output_equation.addAddend(-1, 1) output_equation.setScalar(0) ipq.addEquation(output_equation) # x + y <= property_bound property_eq = MarabouCore.Equation(MarabouCore.Equation.LE) property_eq.addAddend(1, 0) property_eq.addAddend(1, 2) property_eq.setScalar(property_bound) ipq.addEquation(property_eq) return ipq
def getMarabouQuery(self): """ Function to convert network into Marabou Query Returns: ipq: (MarabouCore.InputQuery) representing query """ ipq = MarabouCore.InputQuery() ipq.setNumberOfVariables(self.numVars) for e in self.equList: eq = MarabouCore.Equation(e.EquationType) for (c, v) in e.addendList: assert v < self.numVars eq.addAddend(c, v) eq.setScalar(e.scalar) ipq.addEquation(eq) for r in self.reluList: assert r[1] < self.numVars and r[0] < self.numVars MarabouCore.addReluConstraint(ipq, r[0], r[1]) for m in self.maxList: assert m[1] < self.numVars for e in m[0]: assert e < self.numVars MarabouCore.addMaxConstraint(ipq, m[0], m[1]) for l in self.lowerBounds: assert l < self.numVars ipq.setLowerBound(l, self.lowerBounds[l]) for u in self.upperBounds: assert u < self.numVars ipq.setUpperBound(u, self.upperBounds[u]) return ipq
equation1 = MarabouCore.Equation() equation1.addAddend(1, 0) equation1.addAddend(-1, 1) equation1.setScalar(0) inputQuery.addEquation(equation1) equation2 = MarabouCore.Equation() equation2.addAddend(1, 0) equation2.addAddend(1, 3) equation2.setScalar(0) inputQuery.addEquation(equation2) equation3 = MarabouCore.Equation() equation3.addAddend(1, 2) equation3.addAddend(1, 4) equation3.addAddend(-1, 5) equation3.setScalar(0) inputQuery.addEquation(equation3) MarabouCore.addReluConstraint(inputQuery, 1, 2) MarabouCore.addReluConstraint(inputQuery, 3, 4) options = createOptions() vars1, stats1 = MarabouCore.solve(inputQuery, options, "") if len(vars1) > 0: print("SAT") print(vars1) else: print("UNSAT")
def define_sum_network(xlim=(-1, 1)): ''' Defines the sum network in a marabou way, without the recurrent part i.e. we define: s_i b = s_i-1 f + x_i y = s_i f :param xlim: how to limit the input to the network :return: query to marabou that defines the sum rnn network (without recurent) ''' num_params_for_cell = 8 sum_rnn_query = MarabouCore.InputQuery() sum_rnn_query.setNumberOfVariables(num_params_for_cell) # x sum_rnn_query.setLowerBound(0, xlim[0]) sum_rnn_query.setUpperBound(0, xlim[1]) # s_i-1 f (or temp in some of my notes) sum_rnn_query.setLowerBound(1, 0) sum_rnn_query.setUpperBound(1, large) # s_i b sum_rnn_query.setLowerBound(2, -large) sum_rnn_query.setUpperBound(2, large) # s_i f sum_rnn_query.setLowerBound(3, 0) sum_rnn_query.setUpperBound(3, large) # z_i-1 f sum_rnn_query.setLowerBound(4, 0) sum_rnn_query.setUpperBound(4, large) # z_i b sum_rnn_query.setLowerBound(5, -large) sum_rnn_query.setUpperBound(5, large) # z_i f sum_rnn_query.setLowerBound(6, 0) sum_rnn_query.setUpperBound(6, large) # y sum_rnn_query.setLowerBound(7, -large) sum_rnn_query.setUpperBound(7, large) # s_i b = x_i * 1 + s_i-1 f * 1 update_eq = MarabouCore.Equation() update_eq.addAddend(1, 0) update_eq.addAddend(1, 1) update_eq.addAddend(-1, 2) update_eq.setScalar(0) sum_rnn_query.addEquation(update_eq) # s_i f = ReLu(s_i b) MarabouCore.addReluConstraint(sum_rnn_query, 2, 3) # z_i b = -x_i + z_i-1 f update_eq = MarabouCore.Equation() update_eq.addAddend(-1, 0) update_eq.addAddend(1, 4) update_eq.addAddend(-1, 5) update_eq.setScalar(0) sum_rnn_query.addEquation(update_eq) # z_i f = ReLu(z_i b) MarabouCore.addReluConstraint(sum_rnn_query, 5, 6) # - y + skf + zkf = 0 output_equation = MarabouCore.Equation() output_equation.addAddend(1, 3) output_equation.addAddend(1, 6) output_equation.addAddend(-1, 7) output_equation.setScalar(0) sum_rnn_query.addEquation(output_equation) return sum_rnn_query
def add_rnn_multidim_cells(query, input_idx, input_weights, hidden_weights, bias, num_iterations, print_debug=False): ''' Create n rnn cells, where n is hidden_weights.shape[0] == hidden_weights.shape[1] == len(bias) The added parameters are (same order): i, s_i-1 f, s_i b, s_i f for each of the n added cells (i.e. adding 4*n variables) :param query: the network so far (will add to this) :param input_idx: list of input id's, length m :param input_weights: matrix of input weights, size m x n :param hidden_weights: matrix of weights :param bias: vector of biases to add to each equation, length should be n, if None use 0 bias :param num_iterations: Number of iterations :return: list of output cells, the length will be the same n ''' assert type(hidden_weights) == np.ndarray assert len(hidden_weights.shape) == 2 assert hidden_weights.shape[0] == hidden_weights.shape[1] assert len(input_idx) == input_weights.shape[1], "{}, {}".format(len(input_idx), input_weights.shape[1]) assert hidden_weights.shape[0] == input_weights.shape[0] n = hidden_weights.shape[0] if bias is None: bias = [0] * n else: assert len(bias) == n last_idx = query.getNumberOfVariables() prev_iteration_idxs = [i + 1 for i in range(last_idx, last_idx + (4 * n), 4)] output_idxs = [i + 3 for i in range(last_idx, last_idx + (4 * n), 4)] query.setNumberOfVariables(last_idx + (4 * n)) # i, s_i-1 f, s_i b, s_i f cell_idx = last_idx for i in range(n): # i query.setLowerBound(cell_idx, 0) # we bound the memory unit, and num_iterations is for the output which is doing one more calculation query.setUpperBound(cell_idx, num_iterations - 1) # s_i-1 f query.setLowerBound(cell_idx + 1, 0) query.setUpperBound(cell_idx + 1, LARGE) # s_i b query.setLowerBound(cell_idx + 2, -LARGE) query.setUpperBound(cell_idx + 2, LARGE) # s_i f query.setLowerBound(cell_idx + 3, 0) query.setUpperBound(cell_idx + 3, LARGE) # s_i f = ReLu(s_i b) MarabouCore.addReluConstraint(query, cell_idx + 2, cell_idx + 3) # s_i-1 f >= i * \sum (x_j_min * w_j) # prev_min_eq = MarabouCore.Equation(MarabouCore.Equation.LE) # prev_min_eq.addAddend(1, last_idx + 1) # prev_min_eq.addAddend(1, last_idx + 1) # s_i b = x_j * w_j for all j connected + s_i-1 f * hidden_weight update_eq = MarabouCore.Equation() for j in range(len(input_weights[i, :])): w = input_weights[i, j] # round(input_weights[i, j], 2) update_eq.addAddend(w, input_idx[j]) for j, w in enumerate(hidden_weights[i, :]): # w = round(w, 2) update_eq.addAddend(w, prev_iteration_idxs[j]) update_eq.addAddend(-1, cell_idx + 2) update_eq.setScalar(-bias[i]) # if print_debug: # update_eq.dump() query.addEquation(update_eq) cell_idx += 4 return output_idxs