def test_sum_gt_one(self): op_graph = parse(artifact_dir / "sum_gt_one.onnx") output_op = op_graph.output_operations[0] # gemm prefix = OperationGraph([output_op.a]) op_graph_suffix = OperationGraph(op_graph.walk(DropPrefix(prefix))) x = np.ones((1, 1)).astype(np.float32) y = op_graph_suffix(x) y_expected = np.ones((1, 1)) self.assertTrue((np.abs(y_expected - y) <= THRESHOLD).all()) x = np.random.normal(size=(10, 1)).astype(np.float32) y = op_graph_suffix(x) y_expected = x.copy() self.assertTrue((np.abs(y_expected - y) <= THRESHOLD).all()) prefix_output_op = prefix.output_operations[0] # relu prefix = OperationGraph([prefix_output_op.x]) op_graph_suffix = OperationGraph(op_graph.walk(DropPrefix(prefix))) x = np.ones((1, 1)).astype(np.float32) y = op_graph_suffix(x) y_expected = np.ones((1, 1)) self.assertTrue((np.abs(y_expected - y) <= THRESHOLD).all()) x = np.random.normal(size=(10, 1)).astype(np.float32) y = op_graph_suffix(x) y_expected = np.clip(x, 0.0, None) self.assertTrue((np.abs(y_expected - y) <= THRESHOLD).all())
def verify(dnn: OperationGraph, phi: Expression, **kwargs: Dict[str, Any]): logger = logging.getLogger(__name__) dnn = dnn.simplify() phi.networks[0].concretize(dnn) result = UNSAT property_extractor = ConvexPolytopeExtractor() with tempfile.TemporaryDirectory() as dirname: for prop in property_extractor.extract_from(phi): layers = prop.output_constraint.as_layers( prop.network, extra_layer_types=MIPVERIFY_LAYER_TYPES, translator_error=MIPVerifyTranslatorError, ) input_interval = prop.input_constraint.as_hyperrectangle() mipverify_inputs = to_mipverify_inputs( input_interval, layers, dirname=dirname, translator_error=MIPVerifyTranslatorError, ) logger.debug("Running mipverify") executor = CommandLineExecutor( "julia", mipverify_inputs["property_path"], verifier_error=MIPVerifyError, ) out, err = executor.run() logger.debug("Parsing results") result |= parse_results(out, err) if result == SAT or result == UNKNOWN: return result return result
def suffixed_op_graph(self) -> OperationGraph: import dnnv.nn.operations as operations if not isinstance(self.output_constraint, HalfspacePolytope): raise ValueError( f"{type(self.output_constraint).__name__} constraints are not yet supported" ) if len(self.op_graph.output_operations) == 1: new_output_op = self.op_graph.output_operations[0] else: output_operations = [ operations.Flatten(o) for o in self.op_graph.output_operations ] new_output_op = operations.Concat(output_operations, axis=1) size = self.output_constraint.size() k = len(self.output_constraint.halfspaces) W = np.zeros((size, k), dtype=np.float32) b = np.zeros(k, dtype=np.float32) for n, hs in enumerate(self.output_constraint.halfspaces): b[n] = -hs.b if hs.is_open: b[n] += 1e-6 # TODO : remove magic number for i, c in zip(hs.indices, hs.coefficients): W[i, n] = c new_output_op = operations.Add(operations.MatMul(new_output_op, W), b) new_output_op = operations.Relu(new_output_op) W_mask = np.zeros((k, 1), dtype=np.float32) b_mask = np.zeros(1, dtype=np.float32) for i in range(k): W_mask[i, 0] = 1 new_output_op = operations.Add( operations.MatMul(new_output_op, W_mask), b_mask) return OperationGraph([new_output_op]).simplify()
def verify(dnn: OperationGraph, phi: Expression): logger = logging.getLogger(__name__) dnn = dnn.simplify() phi.networks[0].concretize(dnn) result = UNSAT property_extractor = ConvexPolytopeExtractor() with tempfile.TemporaryDirectory() as dirname: for prop in property_extractor.extract_from(phi): layers = prop.output_constraint.as_layers( prop.network, translator_error=ReluplexTranslatorError ) input_interval = prop.input_constraint.as_hyperrectangle() nnet_file_name = to_nnet_file( input_interval, layers, dirname=dirname, translator_error=ReluplexTranslatorError, ) logger.debug("Running reluplex") executor = CommandLineExecutor( "reluplex", f"{nnet_file_name}", verifier_error=ReluplexError ) out, err = executor.run() logger.debug("Parsing results") result |= parse_results(out, err) if result == SAT: logger.debug("SAT! Validating counter example.") validate_counter_example(prop, out, err) if result == SAT or result == UNKNOWN: return result return result
def verify(dnn: OperationGraph, phi: Expression): dnn = dnn.simplify() phi.networks[0].concretize(dnn) result = UNSAT property_extractor = ConvexPolytopeExtractor() with tempfile.TemporaryDirectory() as dirname: for prop in property_extractor.extract_from(phi): layers = prop.output_constraint.as_layers( prop.network, extra_layer_types=PLANET_LAYER_TYPES, translator_error=PlanetTranslatorError, ) input_interval = prop.input_constraint.as_hyperrectangle() rlv_file_name = to_rlv_file( input_interval, layers, dirname=dirname, translator_error=PlanetTranslatorError, ) executor = CommandLineExecutor("planet", f"{rlv_file_name}", verifier_error=PlanetError) out, err = executor.run() result |= parse_results(out, err) if result == SAT: validate_counter_example(prop, out, err) if result == SAT or result == UNKNOWN: return result return result
def verify(dnn: OperationGraph, phi: Expression): dnn = dnn.simplify() phi.networks[0].concretize(dnn) result = UNSAT property_extractor = HalfspacePolytopePropertyExtractor( HyperRectangle, HalfspacePolytope) with tempfile.TemporaryDirectory() as dirname: for prop in property_extractor.extract_from(~phi): if prop.input_constraint.num_variables > 1: raise PlanetTranslatorError( "Unsupported network: More than 1 input variable") layers = as_layers( prop.suffixed_op_graph(), extra_layer_types=PLANET_LAYER_TYPES, translator_error=PlanetTranslatorError, ) rlv_file_name = to_rlv_file( prop.input_constraint, layers, dirname=dirname, translator_error=PlanetTranslatorError, ) executor = CommandLineExecutor("planet", f"{rlv_file_name}", verifier_error=PlanetError) out, err = executor.run() result |= parse_results(out, err) if result == SAT: validate_counter_example(prop, out, err) if result == SAT or result == UNKNOWN: return result return result
def verify(dnn: OperationGraph, phi: Expression, **kwargs): dnn = dnn.simplify() phi.networks[0].concretize(dnn) result = UNSAT property_extractor = HalfspacePolytopePropertyExtractor( HyperRectangle, HalfspacePolytope ) with tempfile.TemporaryDirectory() as dirname: for prop in property_extractor.extract_from(~phi): if prop.input_constraint.num_variables > 1: raise BabTranslatorError( "Unsupported network: More than 1 input variable" ) layers = as_layers( prop.suffixed_op_graph(), translator_error=BabTranslatorError, ) rlv_file_path = to_rlv_file( prop.input_constraint, layers, dirname=dirname, translator_error=BabTranslatorError, ) result |= check(rlv_file_path, **kwargs) if result == SAT or result == UNKNOWN: return result return result
def test_a_gt_b(self): op_graph = parse(artifact_dir / "a_gt_b.onnx") output_op = op_graph.output_operations[0] # softmax prefix = OperationGraph([output_op.x]) op_graph_suffix = OperationGraph(op_graph.walk(DropPrefix(prefix))) x = np.random.normal(size=(10, 2)).astype(np.float32) y = op_graph_suffix(x) a_gt_b = x[:, 0] > x[:, 1] y_0_gt_1 = y[:, 0] > y[:, 1] self.assertTrue(np.all(~(a_gt_b ^ y_0_gt_1))) prefix_output_op = prefix.output_operations[0] # gemm prefix = OperationGraph([prefix_output_op.a]) op_graph_suffix = OperationGraph(op_graph.walk(DropPrefix(prefix))) x = np.clip(np.random.normal(size=(10, 2)), 0.0, None).astype(np.float32) y = op_graph_suffix(x) a_gt_b = x[:, 0] > x[:, 1] y_0_gt_1 = y[:, 0] > y[:, 1] self.assertTrue(np.all(~(a_gt_b ^ y_0_gt_1)))
def verify(dnn: OperationGraph, phi: Expression, domain="deeppoly", timeout_lp=1.0, timeout_milp=1.0, use_area_heuristic=True, **kwargs: Dict[str, Any]): logger = logging.getLogger(__name__) dnn = dnn.simplify() phi.networks[0].concretize(dnn) result = UNSAT property_extractor = HalfspacePolytopePropertyExtractor( HyperRectangle, HalfspacePolytope) for prop in property_extractor.extract_from(~phi): if prop.input_constraint.num_variables > 1: raise ERANTranslatorError( "Unsupported network: More than 1 input variable") with tf.Session(graph=tf.Graph()) as tf_session: layers = as_layers( prop.suffixed_op_graph(), extra_layer_types=ERAN_LAYER_TYPES, translator_error=ERANTranslatorError, ) input_interval = prop.input_constraint spec_lb = input_interval.lower_bounds[0] spec_ub = input_interval.upper_bounds[0] if len(spec_lb.shape) == 4: spec_lb = spec_lb.transpose((0, 2, 3, 1)) spec_ub = spec_ub.transpose((0, 2, 3, 1)) tf_graph = as_tf(layers, translator_error=ERANTranslatorError) eran_model = ERAN(tf_graph, session=tf_session) _, nn, nlb, nub = eran_model.analyze_box(spec_lb.flatten().copy(), spec_ub.flatten().copy(), domain, timeout_lp, timeout_milp, use_area_heuristic, **kwargs) output_lower_bound = np.asarray(nlb[-1]) output_upper_bound = np.asarray(nub[-1]) logger.debug("output lower bound: %s", output_lower_bound) logger.debug("output upper bound: %s", output_upper_bound) result |= check(output_lower_bound, output_upper_bound) if result == SAT or result == UNKNOWN: return result return result
def verify(dnn: OperationGraph, phi: Expression, **kwargs: Dict[str, Any]): logger = logging.getLogger(__name__) dnn = dnn.simplify() phi.networks[0].concretize(dnn) result = UNSAT property_extractor = HalfspacePolytopePropertyExtractor( HyperRectangle, HalfspacePolytope) with tempfile.TemporaryDirectory() as dirname: for prop in property_extractor.extract_from(~phi): if prop.input_constraint.num_variables > 1: raise NeurifyTranslatorError( "Unsupported network: More than 1 input variable") layers = as_layers( prop.suffixed_op_graph(), translator_error=NeurifyTranslatorError, ) neurify_inputs = to_neurify_inputs( prop.input_constraint, layers, dirname=dirname, translator_error=NeurifyTranslatorError, ) logger.debug("Running neurify") executor = CommandLineExecutor( "neurify", "-n", neurify_inputs["nnet_path"], "-x", neurify_inputs["input_path"], "-sl", "0.0000000000001", # TODO: remove magic number "-I", neurify_inputs["input_interval_path"], "-v", *[f"--{k}={v}" for k, v in kwargs.items() if v is not None], verifier_error=NeurifyError, ) out, err = executor.run() logger.debug("Parsing results") result |= parse_results(out, err) if result == SAT: logger.debug("SAT! Validating counter example.") validate_counter_example(prop, out, err) if result == SAT or result == UNKNOWN: return result return result
def verify(dnn: OperationGraph, phi: Expression, **kwargs: Dict[str, Any]): logger = logging.getLogger(__name__) dnn = dnn.simplify() phi.networks[0].concretize(dnn) result = UNSAT property_extractor = ConvexPolytopeExtractor() with tempfile.TemporaryDirectory() as dirname: for prop in property_extractor.extract_from(phi): layers = prop.output_constraint.as_layers( prop.network, translator_error=NeurifyTranslatorError) input_interval = prop.input_constraint.as_hyperrectangle() neurify_inputs = to_neurify_inputs( input_interval, layers, dirname=dirname, translator_error=NeurifyTranslatorError, ) epsilon = neurify_inputs["epsilon"] logger.debug("Running neurify") executor = CommandLineExecutor( "neurify", "-n", neurify_inputs["nnet_path"], "-x", neurify_inputs["input_path"], "-sl", "0.000000000001", # TODO: remove magic number f"--linf={epsilon}", "-v", *[f"--{k}={v}" for k, v in kwargs.items() if v is not None], verifier_error=NeurifyError, ) out, err = executor.run() logger.debug("Parsing results") result |= parse_results(out, err) if result == SAT: logger.debug("SAT! Validating counter example.") validate_counter_example(prop, out, err) if result == SAT or result == UNKNOWN: return result return result
def test_resnet50_multi_output(self): op_graph = parse(artifact_dir / "resnet50.onnx") first_half = op_graph[:83] self.assertEqual(len(first_half.output_operations), 2, "Expected 2 output operations.") self.assertIsInstance( first_half.output_operations[0], operations.BatchNormalization, "Expected first output operation to be BatchNormalization.", ) self.assertIsInstance( first_half.output_operations[1], operations.BatchNormalization, "Expected second output operation to be BatchNormalization.", ) second_half = op_graph[84:] self.assertEqual(len(second_half.output_operations), 1, "Only 1 output operation expected.") self.assertIsInstance( second_half.output_operations[0], operations.Gemm, "Expected first slice to end with Gemm.", ) self.assert_composition_equals( op_graph, ( first_half, OperationGraph([ operations.Add( operations.Input((-1, 1024, 14, 14), np.float32), operations.Input((-1, 1024, 14, 14), np.float32), ) ]), second_half, ), )
def verify(dnn: OperationGraph, phi: Expression, **kwargs: Dict[str, Any]): logger = logging.getLogger(__name__) dnn = dnn.simplify() phi.networks[0].concretize(dnn) result = UNSAT property_extractor = HalfspacePolytopePropertyExtractor( HyperRectangle, HalfspacePolytope) with tempfile.TemporaryDirectory() as dirname: for prop in property_extractor.extract_from(~phi): if prop.input_constraint.num_variables > 1: raise MIPVerifyTranslatorError( "Unsupported network: More than 1 input variable") layers = as_layers( prop.suffixed_op_graph(), extra_layer_types=MIPVERIFY_LAYER_TYPES, translator_error=MIPVerifyTranslatorError, ) mipverify_inputs = to_mipverify_inputs( prop.input_constraint, layers, dirname=dirname, translator_error=MIPVerifyTranslatorError, ) logger.debug("Running mipverify") executor = CommandLineExecutor( "julia", mipverify_inputs["property_path"], verifier_error=MIPVerifyError, ) out, err = executor.run() logger.debug("Parsing results") result |= parse_results(out, err) if result == SAT or result == UNKNOWN: return result return result
def merge(self, operation_graphs: List[OperationGraph]): for op_graph in operation_graphs: for op in op_graph.output_operations: self.output_operations.append(self.visit(op)) return OperationGraph(self.output_operations)