From d890b26cd5b65b7e5e832a070bfdbba11fbc7c2c Mon Sep 17 00:00:00 2001 From: Jeremy Sadler <53983960+jezsadler@users.noreply.github.com> Date: Fri, 3 Nov 2023 04:47:58 +0000 Subject: [PATCH] Some linting --- src/omlt/neuralnet/nn_formulation.py | 10 +-- tests/io/test_onnx_parser.py | 92 +++++++++++++------- tests/neuralnet/test_nn_formulation.py | 116 +++++++++++++++++-------- 3 files changed, 145 insertions(+), 73 deletions(-) diff --git a/src/omlt/neuralnet/nn_formulation.py b/src/omlt/neuralnet/nn_formulation.py index 1b6bc187..464498bb 100644 --- a/src/omlt/neuralnet/nn_formulation.py +++ b/src/omlt/neuralnet/nn_formulation.py @@ -308,10 +308,10 @@ def __init__(self, network_structure, activation_functions=None): ) if activation_functions is not None: self._activation_functions.update(activation_functions) - + # If we want to do network input/output validation at initialize time instead # of build time, as it is for FullSpaceNNFormulation: - # + # # network_inputs = list(self.__network_definition.input_nodes) # if len(network_inputs) != 1: # raise ValueError("Multiple input layers are not currently supported.") @@ -513,10 +513,10 @@ def layer(b, layer_id): else: raise ValueError("ReluPartitionFormulation supports only Dense layers") - # This check is never hit. The formulation._build_formulation() function is - # only ever called by an OmltBlock.build_formulation(), and that runs the + # This check is never hit. The formulation._build_formulation() function is + # only ever called by an OmltBlock.build_formulation(), and that runs the # input_indexes and output_indexes first, which will catch any formulations - # with multiple input or output layers. + # with multiple input or output layers. # setup input variables constraints # currently only support a single input layer diff --git a/tests/io/test_onnx_parser.py b/tests/io/test_onnx_parser.py index c1438264..7bc13373 100644 --- a/tests/io/test_onnx_parser.py +++ b/tests/io/test_onnx_parser.py @@ -107,26 +107,29 @@ def test_maxpool(datadir): for layer in layers[1:]: assert layer.kernel_depth == 3 + @pytest.mark.skipif(not onnx_available, reason="Need ONNX for this test") def test_input_tensor_invalid_dims(datadir): model = onnx.load(datadir.file("keras_linear_131.onnx")) model.graph.input[0].type.tensor_type.shape.dim[1].dim_value = 0 parser = NetworkParser() with pytest.raises(ValueError) as excinfo: - parser.parse_network(model.graph,None,None) + parser.parse_network(model.graph, None, None) expected_msg = "All dimensions in graph \"tf2onnx\" input tensor have 0 value." assert str(excinfo.value) == expected_msg + @pytest.mark.skipif(not onnx_available, reason="Need ONNX for this test") def test_no_input_layers(datadir): model = onnx.load(datadir.file("keras_linear_131.onnx")) model.graph.input.remove(model.graph.input[0]) parser = NetworkParser() with pytest.raises(ValueError) as excinfo: - parser.parse_network(model.graph,None,None) + parser.parse_network(model.graph, None, None) expected_msg = "No valid input layer found in graph \"tf2onnx\"." assert str(excinfo.value) == expected_msg + @pytest.mark.skipif(not onnx_available, reason="Need ONNX for this test") def test_node_no_inputs(datadir): model = onnx.load(datadir.file("keras_linear_131.onnx")) @@ -134,93 +137,120 @@ def test_node_no_inputs(datadir): model.graph.node[0].input.pop() parser = NetworkParser() with pytest.raises(ValueError) as excinfo: - parser.parse_network(model.graph,None,None) - expected_msg = "Nodes must have inputs or have op_type \"Constant\". Node \"StatefulPartitionedCall/keras_linear_131/dense/MatMul\" has no inputs and op_type \"MatMul\"." + parser.parse_network(model.graph, None, None) + expected_msg = """Nodes must have inputs or have op_type \"Constant\". + Node \"StatefulPartitionedCall/keras_linear_131/dense/MatMul\" has + no inputs and op_type \"MatMul\".""" assert str(excinfo.value) == expected_msg + @pytest.mark.skipif(not onnx_available, reason="Need ONNX for this test") def test_consume_wrong_node_type(datadir): model = onnx.load(datadir.file("keras_linear_131.onnx")) parser = NetworkParser() - parser.parse_network(model.graph,None,None) - + parser.parse_network(model.graph, None, None) + with pytest.raises(ValueError) as excinfo: - parser._consume_dense_nodes(parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][1],parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][2]) + parser._consume_dense_nodes(parser._nodes[ + 'StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][1], + parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][2]) expected_msg_dense = "StatefulPartitionedCall/keras_linear_131/dense/BiasAdd is a Add node, only MatMul nodes can be used as starting points for consumption." assert str(excinfo.value) == expected_msg_dense - + with pytest.raises(ValueError) as excinfo: - parser._consume_gemm_dense_nodes(parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][1],parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][2]) + parser._consume_gemm_dense_nodes(parser._nodes[ + 'StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][1], + parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][2]) expected_msg_gemm = "StatefulPartitionedCall/keras_linear_131/dense/BiasAdd is a Add node, only Gemm nodes can be used as starting points for consumption." assert str(excinfo.value) == expected_msg_gemm - + with pytest.raises(ValueError) as excinfo: - parser._consume_conv_nodes(parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][1],parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][2]) + parser._consume_conv_nodes(parser._nodes[ + 'StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][1], + parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][2]) expected_msg_conv = "StatefulPartitionedCall/keras_linear_131/dense/BiasAdd is a Add node, only Conv nodes can be used as starting points for consumption." assert str(excinfo.value) == expected_msg_conv - + with pytest.raises(ValueError) as excinfo: - parser._consume_reshape_nodes(parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][1],parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][2]) + parser._consume_reshape_nodes(parser._nodes[ + 'StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][1], + parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][2]) expected_msg_reshape = "StatefulPartitionedCall/keras_linear_131/dense/BiasAdd is a Add node, only Reshape nodes can be used as starting points for consumption." assert str(excinfo.value) == expected_msg_reshape with pytest.raises(ValueError) as excinfo: - parser._consume_pool_nodes(parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][1],parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][2]) - expected_msg_pool = "StatefulPartitionedCall/keras_linear_131/dense/BiasAdd is a Add node, only MaxPool nodes can be used as starting points for consumption." + parser._consume_pool_nodes(parser._nodes[ + 'StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][1], + parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/BiasAdd'][2]) + expected_msg_pool = """StatefulPartitionedCall/keras_linear_131/dense/BiasAdd + is a Add node, only MaxPool nodes can be used as starting points + for consumption.""" assert str(excinfo.value) == expected_msg_pool + @pytest.mark.skipif(not onnx_available, reason="Need ONNX for this test") def test_consume_dense_wrong_dims(datadir): model = onnx.load(datadir.file("keras_linear_131.onnx")) parser = NetworkParser() - parser.parse_network(model.graph,None,None) + parser.parse_network(model.graph, None, None) parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/MatMul'][1].input.append('abcd') with pytest.raises(ValueError) as excinfo: - parser._consume_dense_nodes(parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/MatMul'][1],parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/MatMul'][2]) + parser._consume_dense_nodes(parser._nodes[ + 'StatefulPartitionedCall/keras_linear_131/dense/MatMul'][1], + parser._nodes['StatefulPartitionedCall/keras_linear_131/dense/MatMul'][2]) expected_msg_dense = "StatefulPartitionedCall/keras_linear_131/dense/MatMul input has 3 dimensions, only nodes with 2 input dimensions can be used as starting points for consumption." assert str(excinfo.value) == expected_msg_dense + @pytest.mark.skipif(not onnx_available, reason="Need ONNX for this test") def test_consume_gemm_wrong_dims(datadir): model = onnx.load(datadir.file("gemm.onnx")) parser = NetworkParser() - parser.parse_network(model.graph,None,None) + parser.parse_network(model.graph, None, None) parser._nodes['Gemm_0'][1].input.append('abcd') with pytest.raises(ValueError) as excinfo: - parser._consume_gemm_dense_nodes(parser._nodes['Gemm_0'][1],parser._nodes['Gemm_0'][2]) + parser._consume_gemm_dense_nodes(parser._nodes['Gemm_0'][1], + parser._nodes['Gemm_0'][2]) expected_msg_gemm = "Gemm_0 input has 4 dimensions, only nodes with 3 input dimensions can be used as starting points for consumption." assert str(excinfo.value) == expected_msg_gemm + @pytest.mark.skipif(not onnx_available, reason="Need ONNX for this test") def test_consume_conv_wrong_dims(datadir): - model = onnx.load(datadir.file("convx1_gemmx1.onnx")) + model = onnx.load(datadir.file("convx1_gemmx1.onnx")) parser = NetworkParser() - parser.parse_network(model.graph,None,None) + parser.parse_network(model.graph, None, None) parser._nodes['Conv_0'][1].input.append('abcd') with pytest.raises(ValueError) as excinfo: - parser._consume_conv_nodes(parser._nodes['Conv_0'][1],parser._nodes['Conv_0'][2]) + parser._consume_conv_nodes(parser._nodes['Conv_0'][1], + parser._nodes['Conv_0'][2]) expected_msg_conv = "Conv_0 input has 4 dimensions, only nodes with 2 or 3 input dimensions can be used as starting points for consumption." assert str(excinfo.value) == expected_msg_conv + @pytest.mark.skipif(not onnx_available, reason="Need ONNX for this test") def test_consume_reshape_wrong_dims(datadir): - model = onnx.load(datadir.file("convx1_gemmx1.onnx")) + model = onnx.load(datadir.file("convx1_gemmx1.onnx")) parser = NetworkParser() - parser.parse_network(model.graph,None,None) + parser.parse_network(model.graph, None, None) parser._nodes['Reshape_2'][1].input.append('abcd') with pytest.raises(ValueError) as excinfo: - parser._consume_reshape_nodes(parser._nodes['Reshape_2'][1],parser._nodes['Reshape_2'][2]) - expected_msg_reshape = "Reshape_2 input has 3 dimensions, only nodes with 2 input dimensions can be used as starting points for consumption." + parser._consume_reshape_nodes(parser._nodes['Reshape_2'][1], + parser._nodes['Reshape_2'][2]) + expected_msg_reshape = """Reshape_2 input has 3 dimensions, only nodes with 2 input + dimensions can be used as starting points for consumption.""" assert str(excinfo.value) == expected_msg_reshape + @pytest.mark.skipif(not onnx_available, reason="Need ONNX for this test") def test_consume_maxpool_wrong_dims(datadir): - model = onnx.load(datadir.file("maxpool_2d.onnx")) + model = onnx.load(datadir.file("maxpool_2d.onnx")) parser = NetworkParser() - parser.parse_network(model.graph,None,None) + parser.parse_network(model.graph, None, None) parser._nodes['node1'][1].input.append('abcd') with pytest.raises(ValueError) as excinfo: - parser._consume_pool_nodes(parser._nodes['node1'][1],parser._nodes['node1'][2]) - expected_msg_maxpool = "node1 input has 2 dimensions, only nodes with 1 input dimension can be used as starting points for consumption." - assert str(excinfo.value) == expected_msg_maxpool \ No newline at end of file + parser._consume_pool_nodes(parser._nodes['node1'][1], parser._nodes['node1'][2]) + expected_msg_maxpool = """node1 input has 2 dimensions, only nodes with 1 input + dimension can be used as starting points for consumption.""" + assert str(excinfo.value) == expected_msg_maxpool diff --git a/tests/neuralnet/test_nn_formulation.py b/tests/neuralnet/test_nn_formulation.py index 4b345735..66cb0ed8 100644 --- a/tests/neuralnet/test_nn_formulation.py +++ b/tests/neuralnet/test_nn_formulation.py @@ -1,6 +1,7 @@ import numpy as np import pyomo.environ as pyo import pytest +from pyomo.contrib.fbbt import interval from omlt import OmltBlock from omlt.neuralnet import ( @@ -18,12 +19,16 @@ InputLayer, PoolingLayer2D, ) -from omlt.neuralnet.layers.full_space import full_space_maxpool2d_layer, _input_layer_and_block -from omlt.neuralnet.layers.partition_based import partition_based_dense_relu_layer,default_partition_split_func +from omlt.neuralnet.layers.full_space import ( + full_space_maxpool2d_layer, + _input_layer_and_block +) +from omlt.neuralnet.layers.partition_based import ( + partition_based_dense_relu_layer, + default_partition_split_func +) from omlt.neuralnet.layers.reduced_space import reduced_space_dense_layer -from pyomo.contrib.fbbt import interval - def two_node_network(activation, input_value): """ @@ -254,7 +259,8 @@ def _maxpool_conv_network(inputs): net.add_layer(conv_layer_2) net.add_edge(conv_layer_1, conv_layer_2) - # test normal ConvLayer -> MaxPoolLayer structure, with monotonic increasing activation part of ConvLayer + # test normal ConvLayer -> MaxPoolLayer structure, with monotonic increasing + # activation part of ConvLayer maxpool_layer_1 = PoolingLayer2D( conv_layer_2.output_size, [1, 1, 2], [2, 2], "max", [3, 2], 1 ) @@ -268,7 +274,8 @@ def _maxpool_conv_network(inputs): net.add_layer(conv_layer_3) net.add_edge(maxpool_layer_1, conv_layer_3) - # test ConvLayer -> MaxPoolLayer when nonlinear activation function is already part of max pooling layer + # test ConvLayer -> MaxPoolLayer when nonlinear activation function is + # already part of max pooling layer # also test index mapping logic in max pooling layers maxpool_layer_2_input_size = [1, 2, 1] maxpool_layer_2_index_mapper = IndexMapper( @@ -332,6 +339,7 @@ def test_maxpool_FullSpaceNNFormulation(): status = pyo.SolverFactory("cbc").solve(m, tee=False) assert abs(pyo.value(m.neural_net_block.outputs[0, 0, 0]) - y[0, 0, 0]) < 1e-6 + def _test_formulation_initialize_extra_input(network_formulation): """ network_formulation can be: @@ -343,12 +351,13 @@ def _test_formulation_initialize_extra_input(network_formulation): net.add_layer(extra_input) with pytest.raises(ValueError) as excinfo: if network_formulation == 'FullSpace': - formulation = FullSpaceNNFormulation(net) + FullSpaceNNFormulation(net) elif network_formulation == 'ReducedSpace': - formulation = ReducedSpaceNNFormulation(net) + ReducedSpaceNNFormulation(net) expected_msg = "Multiple input layers are not currently supported." assert str(excinfo.value) == expected_msg + def _test_formulation_added_extra_input(network_formulation): """ network_formulation can be: @@ -370,6 +379,7 @@ def _test_formulation_added_extra_input(network_formulation): expected_msg = "Multiple input layers are not currently supported." assert str(excinfo.value) == expected_msg + def _test_formulation_build_extra_input(network_formulation): """ network_formulation can be: @@ -393,6 +403,7 @@ def _test_formulation_build_extra_input(network_formulation): expected_msg = "Multiple input layers are not currently supported." assert str(excinfo.value) == expected_msg + def _test_formulation_added_extra_output(network_formulation): """ network_formulation can be: @@ -409,18 +420,19 @@ def _test_formulation_added_extra_output(network_formulation): biases=np.array([3.0, 4.0]), ) if network_formulation == 'FullSpace': - formulation = FullSpaceNNFormulation(net) + formulation = FullSpaceNNFormulation(net) elif network_formulation == 'ReducedSpace': formulation = ReducedSpaceNNFormulation(net) elif network_formulation == 'relu': formulation = ReluPartitionFormulation(net) net.add_layer(extra_output) - net.add_edge(list(net.layers)[-2],extra_output) + net.add_edge(list(net.layers)[-2], extra_output) with pytest.raises(ValueError) as excinfo: formulation.output_indexes expected_msg = "Multiple output layers are not currently supported." assert str(excinfo.value) == expected_msg + def _test_formulation_initialize_extra_output(network_formulation): """ network_formulation can be: @@ -436,15 +448,16 @@ def _test_formulation_initialize_extra_output(network_formulation): biases=np.array([3.0, 4.0]), ) net.add_layer(extra_output) - net.add_edge(list(net.layers)[-2],extra_output) + net.add_edge(list(net.layers)[-2], extra_output) with pytest.raises(ValueError) as excinfo: if network_formulation == 'FullSpace': - formulation = FullSpaceNNFormulation(net) + FullSpaceNNFormulation(net) elif network_formulation == 'ReducedSpace': - formulation = ReducedSpaceNNFormulation(net) + ReducedSpaceNNFormulation(net) expected_msg = "Multiple output layers are not currently supported." assert str(excinfo.value) == expected_msg + def test_FullSpaceNNFormulation_invalid_network(): _test_formulation_initialize_extra_input("FullSpace") _test_formulation_added_extra_input("FullSpace") @@ -452,6 +465,7 @@ def test_FullSpaceNNFormulation_invalid_network(): _test_formulation_initialize_extra_output("FullSpace") _test_formulation_added_extra_output("FullSpace") + def test_ReducedSpaceNNFormulation_invalid_network(): # _test_formulation_initialize_extra_input("ReducedSpace") _test_formulation_added_extra_input("ReducedSpace") @@ -459,11 +473,13 @@ def test_ReducedSpaceNNFormulation_invalid_network(): # _test_formulation_initialize_extra_output("ReducedSpace") _test_formulation_added_extra_output("ReducedSpace") + def test_ReluPartitionFormulation_invalid_network(): _test_formulation_added_extra_input("relu") _test_formulation_build_extra_input("relu") _test_formulation_added_extra_output("relu") + def _test_dense_layer_multiple_predecessors(layer_type): m = pyo.ConcreteModel() m.neural_net_block = OmltBlock() @@ -471,15 +487,16 @@ def _test_dense_layer_multiple_predecessors(layer_type): extra_input = InputLayer([1]) test_layer = list(net.layers)[2] net.add_layer(extra_input) - net.add_edge(extra_input,test_layer) + net.add_edge(extra_input, test_layer) with pytest.raises(ValueError) as excinfo: if layer_type == 'PartitionBased': - partition_based_dense_relu_layer(m,net,m,test_layer,None) + partition_based_dense_relu_layer(m, net, m, test_layer, None) elif layer_type == 'ReducedSpace': - reduced_space_dense_layer(m,net,m,test_layer,None) + reduced_space_dense_layer(m, net, m, test_layer, None) expected_msg = f"Layer {test_layer} has multiple predecessors." assert str(excinfo.value) == expected_msg + def _test_dense_layer_no_predecessors(layer_type): """ Layer type can be "ReducedSpace", or "PartitionBased". @@ -497,20 +514,23 @@ def _test_dense_layer_no_predecessors(layer_type): net.add_layer(test_layer) with pytest.raises(ValueError) as excinfo: if layer_type == 'PartitionBased': - partition_based_dense_relu_layer(m,net,m,test_layer,None) + partition_based_dense_relu_layer(m, net, m, test_layer, None) elif layer_type == 'ReducedSpace': - reduced_space_dense_layer(m,net,m,test_layer,None) + reduced_space_dense_layer(m, net, m, test_layer, None) expected_msg = f"Layer {test_layer} is not an input layer, but has no predecessors." assert str(excinfo.value) == expected_msg + def test_partition_based_dense_layer_predecessors(): _test_dense_layer_multiple_predecessors("PartitionBased") _test_dense_layer_no_predecessors("PartitionBased") + def test_reduced_space_dense_layer_predecessors(): _test_dense_layer_multiple_predecessors("ReducedSpace") _test_dense_layer_no_predecessors("ReducedSpace") + def test_partition_based_unbounded_below(): m = pyo.ConcreteModel() m.neural_net_block = OmltBlock() @@ -526,10 +546,12 @@ def test_partition_based_unbounded_below(): split_func = lambda w: default_partition_split_func(w, 2) with pytest.raises(ValueError) as excinfo: - partition_based_dense_relu_layer(m.neural_net_block,net,m.neural_net_block,test_layer,split_func) + partition_based_dense_relu_layer(m.neural_net_block, net, + m.neural_net_block, test_layer, split_func) expected_msg = "Expression is unbounded below." assert str(excinfo.value) == expected_msg + def test_partition_based_unbounded_above(): m = pyo.ConcreteModel() m.neural_net_block = OmltBlock() @@ -545,10 +567,12 @@ def test_partition_based_unbounded_above(): split_func = lambda w: default_partition_split_func(w, 2) with pytest.raises(ValueError) as excinfo: - partition_based_dense_relu_layer(m.neural_net_block,net,m.neural_net_block,test_layer,split_func) + partition_based_dense_relu_layer(m.neural_net_block, net, m.neural_net_block, + test_layer, split_func) expected_msg = "Expression is unbounded above." assert str(excinfo.value) == expected_msg + def test_partition_based_bias_unbounded_below(): m = pyo.ConcreteModel() m.neural_net_block = OmltBlock() @@ -562,10 +586,12 @@ def test_partition_based_bias_unbounded_below(): split_func = lambda w: default_partition_split_func(w, 2) with pytest.raises(ValueError) as excinfo: - partition_based_dense_relu_layer(m.neural_net_block,net,m.neural_net_block,test_layer,split_func) + partition_based_dense_relu_layer(m.neural_net_block, net, + m.neural_net_block, test_layer, split_func) expected_msg = "Expression is unbounded below." assert str(excinfo.value) == expected_msg + def test_partition_based_bias_unbounded_above(): m = pyo.ConcreteModel() m.neural_net_block = OmltBlock() @@ -579,10 +605,12 @@ def test_partition_based_bias_unbounded_above(): split_func = lambda w: default_partition_split_func(w, 2) with pytest.raises(ValueError) as excinfo: - partition_based_dense_relu_layer(m.neural_net_block,net,m.neural_net_block,test_layer,split_func) + partition_based_dense_relu_layer(m.neural_net_block, net, m.neural_net_block, + test_layer, split_func) expected_msg = "Expression is unbounded above." assert str(excinfo.value) == expected_msg + def test_fullspace_internal_extra_input(): m = pyo.ConcreteModel() m.neural_net_block = OmltBlock() @@ -592,12 +620,13 @@ def test_fullspace_internal_extra_input(): formulation = FullSpaceNNFormulation(net) m.neural_net_block.build_formulation(formulation) net.add_layer(extra_input) - net.add_edge(extra_input,test_layer) + net.add_edge(extra_input, test_layer) with pytest.raises(ValueError) as excinfo: - _input_layer_and_block(m.neural_net_block,net,test_layer) + _input_layer_and_block(m.neural_net_block, net, test_layer) expected_msg = "Multiple input layers are not currently supported." assert str(excinfo.value) == expected_msg + def test_conv2d_extra_activation(): m = pyo.ConcreteModel() m.neural_net_block = OmltBlock() @@ -625,24 +654,29 @@ def test_conv2d_extra_activation(): conv_layer_2 = ConvLayer2D( conv_layer_1.output_size, [1, 3, 4], - [1, 1], + [1, 1], conv_layer_2_kernel, activation="relu", ) net.add_layer(conv_layer_2) net.add_edge(conv_layer_1, conv_layer_2) - # test normal ConvLayer -> MaxPoolLayer structure, with monotonic increasing activation part of ConvLayer + # test normal ConvLayer -> MaxPoolLayer structure, with monotonic + # increasing activation part of ConvLayer maxpool_layer_1 = PoolingLayer2D( - conv_layer_2.output_size, [1, 1, 2], [2, 2], "max", [3, 2], 1,activation="relu" + conv_layer_2.output_size, [1, 1, 2], [2, 2], "max", [3, 2], 1, activation="relu" ) net.add_layer(maxpool_layer_1) net.add_edge(conv_layer_2, maxpool_layer_1) with pytest.raises(ValueError) as excinfo: m.neural_net_block.build_formulation(FullSpaceNNFormulation(net)) - expected_msg = "Activation is applied after convolution layer, but the successor max pooling layer PoolingLayer(input_size=[1, 3, 4], output_size=[1, 1, 2], strides=[2, 2], kernel_shape=[3, 2]), pool_func_name=max has an activation function also." + expected_msg = """Activation is applied after convolution layer, but the successor + max pooling layer PoolingLayer(input_size=[1, 3, 4], output_size=[1, 1, 2], + strides=[2, 2], kernel_shape=[3, 2]), pool_func_name=max has an activation function + also.""" assert str(excinfo.value) == expected_msg + def test_maxpool2d_bad_input_activation(): m = pyo.ConcreteModel() m.neural_net_block = OmltBlock() @@ -670,16 +704,18 @@ def test_maxpool2d_bad_input_activation(): conv_layer_2 = ConvLayer2D( conv_layer_1.output_size, [1, 3, 4], - [1, 1], + [1, 1], conv_layer_2_kernel, activation="relu", ) net.add_layer(conv_layer_2) net.add_edge(conv_layer_1, conv_layer_2) - # test normal ConvLayer -> MaxPoolLayer structure, with monotonic increasing activation part of ConvLayer + # test normal ConvLayer -> MaxPoolLayer structure, with monotonic increasing + # activation part of ConvLayer maxpool_layer_1 = PoolingLayer2D( - conv_layer_2.output_size, [1, 1, 2], [2, 2], "max", [3, 2], 1,activation="linear" + conv_layer_2.output_size, [1, 1, 2], [2, 2], "max", [3, 2], + 1, activation="linear" ) net.add_layer(maxpool_layer_1) net.add_edge(conv_layer_2, maxpool_layer_1) @@ -689,10 +725,13 @@ def test_maxpool2d_bad_input_activation(): conv_layer_2.activation = 'relu' with pytest.raises(ValueError) as excinfo: - full_space_maxpool2d_layer(m.neural_net_block, net, m.neural_net_block, maxpool_layer_1) - expected_msg = "Non-increasing activation functions on the preceding convolutional layer are not supported." + full_space_maxpool2d_layer(m.neural_net_block, net, m.neural_net_block, + maxpool_layer_1) + expected_msg = """Non-increasing activation functions on the preceding + convolutional layer are not supported.""" assert str(excinfo.value) == expected_msg + def test_maxpool2d_bad_input_layer(): m = pyo.ConcreteModel() m.neural_net_block = OmltBlock() @@ -720,22 +759,25 @@ def test_maxpool2d_bad_input_layer(): conv_layer_2 = ConvLayer2D( conv_layer_1.output_size, [1, 3, 4], - [1, 1], + [1, 1], conv_layer_2_kernel, activation="relu", ) net.add_layer(conv_layer_2) net.add_edge(conv_layer_1, conv_layer_2) - # test normal ConvLayer -> MaxPoolLayer structure, with monotonic increasing activation part of ConvLayer + # test normal ConvLayer -> MaxPoolLayer structure, with monotonic increasing + # activation part of ConvLayer maxpool_layer_1 = PoolingLayer2D( - conv_layer_2.output_size, [1, 1, 2], [2, 2], "max", [3, 2], 1,activation="linear" + conv_layer_2.output_size, [1, 1, 2], [2, 2], "max", + [3, 2], 1, activation="linear" ) net.add_layer(maxpool_layer_1) net.add_edge(conv_layer_2, maxpool_layer_1) maxpool_layer_2 = PoolingLayer2D( - maxpool_layer_1.output_size, [1, 1, 2], [2, 2], "max", [3, 2], 1,activation="linear" + maxpool_layer_1.output_size, [1, 1, 2], [2, 2], "max", + [3, 2], 1, activation="linear" ) net.add_layer(maxpool_layer_2) net.add_edge(maxpool_layer_1, maxpool_layer_2)