Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

moving boilerplate changes #13

Merged
merged 65 commits into from
Sep 27, 2024
Merged
Changes from 1 commit
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
13b5265
Create main.yml
jezsadler Mar 19, 2024
ccd3c89
Removing ipopt from CI workflow
jezsadler Mar 19, 2024
ff4dbcb
Implementing JuMP format scalar and indexed
jezsadler Apr 8, 2024
be74ed2
Cleaning up variables
jezsadler Apr 19, 2024
820f5f6
Cleaning up variables - MOI dependency
jezsadler Apr 19, 2024
55e338b
Cleaning up variables - MOI dependency
jezsadler Apr 19, 2024
fa7a859
Removing duplicate line
jezsadler Apr 19, 2024
5790bad
Getting dependencies lined up correctly
jezsadler Apr 19, 2024
8cc7bf6
Update var.py
jezsadler Apr 20, 2024
cad7ba2
Update var.py
jezsadler Apr 20, 2024
3abb7ff
Make test for JuMP variables conditional on presence of JuMP
jezsadler Apr 20, 2024
9621f3d
Use tensorflow-cpu for testing to save space
jezsadler Apr 21, 2024
725348b
Fix Keras version at 2.9
jezsadler Apr 21, 2024
19a7128
removing tweaked action file
jezsadler Apr 22, 2024
015324f
restoring action workflow file
jezsadler Apr 22, 2024
da6c316
Fixing some whitespace linting
jezsadler Apr 22, 2024
fcc62e2
Merge pull request #5 from cog-imperial/main
jezsadler Apr 22, 2024
e151c6c
Update Python versions in main.yml
jezsadler May 17, 2024
f7e1e7f
Update setup.cfg for Keras version
jezsadler May 17, 2024
63798c3
Update main.yml
jezsadler May 17, 2024
175f613
Update main.yml
jezsadler May 17, 2024
40ef6b1
Update main.yml
jezsadler May 17, 2024
e222424
Update main.yml
jezsadler May 17, 2024
ec8af37
Merge pull request #7 from jezsadler/main
jezsadler May 17, 2024
0babe62
wip
lukasturcani May 28, 2024
30a36d1
Add workflows
lukasturcani May 28, 2024
027703f
wip
lukasturcani May 28, 2024
3959e6a
Add stuff
lukasturcani May 29, 2024
c3b619b
Fix formatting
lukasturcani May 29, 2024
691d3a6
wip
lukasturcani May 29, 2024
8a896ba
wip
lukasturcani May 29, 2024
25d31fe
docs work
lukasturcani May 29, 2024
c02be67
wip
lukasturcani May 29, 2024
d839071
Update checks
lukasturcani May 30, 2024
77c0588
update checks
lukasturcani May 30, 2024
a61fb82
update checks
lukasturcani May 30, 2024
e0b35b2
Add conda
lukasturcani May 30, 2024
1a13431
Thing
lukasturcani May 30, 2024
e8c20f6
Add thing
lukasturcani May 30, 2024
3c6148a
wip
lukasturcani May 30, 2024
5c8be2b
update docs
lukasturcani May 30, 2024
b51ba7d
add link
lukasturcani May 30, 2024
cd892bc
wip
lukasturcani May 30, 2024
fbac1ba
thing
lukasturcani May 30, 2024
0e8bded
wip
lukasturcani May 30, 2024
1cdf89c
remove unnecessary things
lukasturcani May 30, 2024
909f86e
Add back for mypy
lukasturcani May 30, 2024
7ae13be
Including OmltExpr expressions for the OmltVars
jezsadler Jun 5, 2024
3483455
cleanup in expression.py
jezsadler Jun 6, 2024
21a63ea
tidying var.py
jezsadler Jun 6, 2024
4ae0715
fixing variable initialization
jezsadler Jun 6, 2024
b174820
further fixing
jezsadler Jun 6, 2024
e219114
adding abstract methods to expression interface
jezsadler Jun 6, 2024
7c0dcb4
Delete .github/workflows/python-package.yml
jezsadler Jun 6, 2024
b6fed2a
linting (1)
jezsadler Jun 6, 2024
bea9863
linting (2)
jezsadler Jun 6, 2024
86d8961
Merge pull request #8 from lukasturcani/lukas/cleanup
jezsadler Jun 6, 2024
09dad47
fix long line
lukasturcani Jun 7, 2024
a967418
Fixing initial batch of ruff errors
jezsadler Jun 13, 2024
051ac3b
Fixing ruff linting errors.
jezsadler Jun 23, 2024
040c858
Fixing mypy typing errors
jezsadler Jun 24, 2024
b7b1c5b
Fixing mypy typing errors
jezsadler Jun 24, 2024
568474a
Merge branch 'JuMP' into keras3
jezsadler Jun 24, 2024
0f9ab2e
Revert "Merge branch 'JuMP' into keras3"
jezsadler Jul 1, 2024
c96ffb3
Merge branch 'lukas/cleanup' into keras3
lukasturcani Jul 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fixing mypy typing errors
jezsadler committed Jun 24, 2024
commit 040c858112936134d05bf6f15dd471e46ce13e63
2 changes: 1 addition & 1 deletion src/omlt/__init__.py
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@
"""

from omlt._version import __version__
from omlt.block import OmltBlock
from omlt.block import OmltBlock # type: ignore[attr-defined]
from omlt.scaling import OffsetScaling

__all__ = [
5 changes: 3 additions & 2 deletions src/omlt/formulation.py
Original file line number Diff line number Diff line change
@@ -63,7 +63,6 @@ class _PyomoFormulation(_PyomoFormulationInterface):
"""

def __init__(self):
super().__init__()
self.__block = None

def _set_block(self, block):
@@ -76,7 +75,9 @@ def block(self):
The underlying block containing the constraints / variables for this
formulation.
"""
return self.__block()
if self.__block is not None:
return self.__block()
return None


def scalar_or_tuple(x):
3 changes: 2 additions & 1 deletion src/omlt/gbt/gbt_formulation.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import collections
from typing import Any

import numpy as np
import pyomo.environ as pe
@@ -158,7 +159,7 @@ def add_formulation_to_block(block, model_definition, input_vars, output_vars):
domain=pe.Reals,
)

branch_value_by_feature_id = {}
branch_value_by_feature_id: dict[int, Any] = {}
branch_value_by_feature_id = collections.defaultdict(list)

for f in feature_ids:
8 changes: 4 additions & 4 deletions src/omlt/gbt/model.py
Original file line number Diff line number Diff line change
@@ -44,15 +44,15 @@ def scaling_object(self):
"""Return an instance of the scaling object supporting the ScalingInterface."""
return self.__scaling_object

@scaling_object.setter
def scaling_object(self, scaling_object):
self.__scaling_object = scaling_object

@property
def scaled_input_bounds(self):
"""Return a list of tuples of lower and upper bounds of tree ensemble inputs."""
return self.__scaled_input_bounds

@scaling_object.setter
def scaling_object(self, scaling_object):
self.__scaling_object = scaling_object


def _model_num_inputs(model):
"""Returns the number of input variables."""
4 changes: 2 additions & 2 deletions src/omlt/io/keras/keras_reader.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from tensorflow import keras

from omlt.neuralnet.layer import DenseLayer, InputLayer
from omlt.neuralnet.layer import DenseLayer, InputLayer, Layer
from omlt.neuralnet.network_definition import NetworkDefinition


@@ -43,7 +43,7 @@ def load_keras_sequential(
unscaled_input_bounds=unscaled_input_bounds,
)

prev_layer = InputLayer([n_inputs])
prev_layer: Layer = InputLayer([n_inputs])
net.add_layer(prev_layer)

for layer in nn.layers:
2 changes: 1 addition & 1 deletion src/omlt/io/onnx.py
Original file line number Diff line number Diff line change
@@ -43,7 +43,7 @@ def load_onnx_neural_network_with_bounds(filename):
onnx_model = onnx.load(filename)
input_bounds_filename = Path(f"{filename}.bounds.json")
input_bounds = None
if input_bounds_filename.exists:
if input_bounds_filename.exists():
input_bounds = load_input_bounds(input_bounds_filename)

return load_onnx_neural_network(onnx_model, input_bounds=input_bounds)
17 changes: 9 additions & 8 deletions src/omlt/io/onnx_parser.py
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@
ATTR_TENSOR = 4
ATTR_INTS = 7


class NetworkParser:
"""Network Parser.

@@ -41,31 +42,31 @@ def __init__(self):

def _reset_state(self):
self._graph = None
self._initializers = None
self._constants = None
self._nodes = None
self._initializers = {}
self._constants = {}
self._nodes = {}
self._nodes_by_output = None
self._inputs = None
self._outputs = None
self._node_stack = None
self._node_map = None
self._node_stack = []
self._node_map = {}

def parse_network(self, graph, scaling_object, input_bounds):
self._reset_state()
self._graph = graph

# initializers contain constant data
initializers = {}
initializers: dict[str, Any] = {}
for initializer in self._graph.initializer:
initializers[initializer.name] = numpy_helper.to_array(initializer)

self._initializers = initializers

# Build graph
nodes = {}
nodes: dict[str, tuple[str, Any, list[Any]]] = {}
nodes_by_output = {}
inputs = set()
outputs = set()
outputs: set[Any] = set()
self._node_map = {}

network = NetworkDefinition(
4 changes: 2 additions & 2 deletions src/omlt/io/torch_geometric/torch_geometric_reader.py
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@

import numpy as np

from omlt.neuralnet.layer import DenseLayer, GNNLayer, InputLayer
from omlt.neuralnet.layer import DenseLayer, GNNLayer, InputLayer, Layer
from omlt.neuralnet.network_definition import NetworkDefinition


@@ -150,7 +150,7 @@ def load_torch_geometric_sequential(
unscaled_input_bounds=unscaled_input_bounds,
)

prev_layer = InputLayer([n_inputs])
prev_layer: Layer = InputLayer([n_inputs])
net.add_layer(prev_layer)

operations = []
8 changes: 4 additions & 4 deletions src/omlt/linear_tree/lt_definition.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Any

import lineartree
import numpy as np

@@ -178,9 +180,7 @@ def _find_all_children_leaves(split, splits_dict, leaves_dict):
# For each leaf, check if the parents appear in the list of children
# splits (all_splits). If so, it must be a leaf of the argument split

return [
leaf for leaf in leaves_dict if leaves_dict[leaf]["parent"] in all_splits
]
return [leaf for leaf in leaves_dict if leaves_dict[leaf]["parent"] in all_splits]


def _find_n_inputs(leaves):
@@ -341,7 +341,7 @@ def _parse_tree_data(model, input_bounds):

# For each variable that appears in the tree, go through all the splits
# and record its splitting threshold
splitting_thresholds = {}
splitting_thresholds: dict[int, Any] = {}
for split in splits:
var = splits[split]["col"]
splitting_thresholds[var] = {}
3 changes: 2 additions & 1 deletion src/omlt/neuralnet/activations/__init__.py
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
variable, and :math:`y` denotes post-activation variable.

"""
from typing import Any

from .linear import linear_activation_constraint, linear_activation_function
from .relu import ComplementarityReLUActivation, bigm_relu_activation_constraint
@@ -25,7 +26,7 @@
"tanh": tanh_activation_function,
}

NON_INCREASING_ACTIVATIONS = []
NON_INCREASING_ACTIVATIONS: list[Any] = []

__all__ = [
"linear_activation_constraint",
4 changes: 2 additions & 2 deletions src/omlt/scaling.py
Original file line number Diff line number Diff line change
@@ -4,8 +4,8 @@
expressions to the Pyomo model for the inputs and outputs of an ML model. An
implementation of a common scaling approach is included with `OffsetScaling`.
"""

import abc
from typing import Any


class ScalingInterface(abc.ABC):
@@ -28,7 +28,7 @@ def get_unscaled_output_expressions(self, scaled_output_vars):
# pragma: no cover


def convert_to_dict(x):
def convert_to_dict(x: Any) -> dict[Any, Any]:
if isinstance(x, dict):
return dict(x)
return dict(enumerate(x))
13 changes: 6 additions & 7 deletions tests/neuralnet/test_keras.py
Original file line number Diff line number Diff line change
@@ -8,7 +8,8 @@
from omlt.io import load_keras_sequential

from conftest import get_neural_network_data
from omlt.block import OmltBlock
from omlt import OmltBlock
from omlt.formulation import _PyomoFormulation
from omlt.neuralnet import FullSpaceNNFormulation, ReducedSpaceNNFormulation
from omlt.neuralnet.activations import ComplementarityReLUActivation
from omlt.scaling import OffsetScaling
@@ -32,10 +33,9 @@ def _test_keras_linear_131(keras_fname, *, reduced_space=False):
m = pyo.ConcreteModel()
m.neural_net_block = OmltBlock()
if reduced_space:
formulation = ReducedSpaceNNFormulation(net)
m.neural_net_block.build_formulation(ReducedSpaceNNFormulation(net))
else:
formulation = FullSpaceNNFormulation(net)
m.neural_net_block.build_formulation(formulation)
m.neural_net_block.build_formulation(FullSpaceNNFormulation(net))

nn_outputs = nn.predict(x=x_test)
for d in range(len(x_test)):
@@ -104,10 +104,9 @@ def _test_keras_linear_big(keras_fname, *, reduced_space=False):
m = pyo.ConcreteModel()
m.neural_net_block = OmltBlock()
if reduced_space:
formulation = ReducedSpaceNNFormulation(net)
m.neural_net_block.build_formulation(ReducedSpaceNNFormulation(net))
else:
formulation = FullSpaceNNFormulation(net)
m.neural_net_block.build_formulation(formulation)
m.neural_net_block.build_formulation(FullSpaceNNFormulation(net))

nn_outputs = nn.predict(x=x_test)
for d in range(len(x_test)):
2 changes: 1 addition & 1 deletion tests/neuralnet/test_network_definition.py
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
import numpy as np
import pyomo.environ as pyo
import pytest
from omlt.block import OmltBlock
from omlt import OmltBlock
from omlt.neuralnet.layer import DenseLayer, InputLayer
from omlt.neuralnet.network_definition import NetworkDefinition
from omlt.neuralnet.nn_formulation import FullSpaceNNFormulation
29 changes: 11 additions & 18 deletions tests/neuralnet/test_nn_formulation.py
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
import pyomo.environ as pyo
import pytest
from omlt import OmltBlock
from omlt.formulation import _PyomoFormulation
from omlt.neuralnet import (
FullSpaceNNFormulation,
FullSpaceSmoothNNFormulation,
@@ -31,6 +32,12 @@
from omlt.neuralnet.layers.reduced_space import reduced_space_dense_layer
from pyomo.contrib.fbbt import interval

formulations = {
"FullSpace": FullSpaceNNFormulation,
"ReducedSpace": ReducedSpaceNNFormulation,
"relu": ReluPartitionFormulation,
}

NEAR_EQUAL = 1e-6
FULLSPACE_SMOOTH_VARS = 15
FULLSPACE_SMOOTH_CONSTRAINTS = 14
@@ -41,6 +48,7 @@
THREE_NODE_VARS = 81
THREE_NODE_CONSTRAINTS = 120


def two_node_network(activation, input_value):
"""Two node network.

@@ -370,12 +378,7 @@ def _test_formulation_added_extra_input(network_formulation):
"""network_formulation can be:'FullSpace', 'ReducedSpace', 'relu'."""
net, y = two_node_network("linear", -2.0)
extra_input = InputLayer([1])
if network_formulation == "FullSpace":
formulation = FullSpaceNNFormulation(net)
elif network_formulation == "ReducedSpace":
formulation = ReducedSpaceNNFormulation(net)
elif network_formulation == "relu":
formulation = ReluPartitionFormulation(net)
formulation: _PyomoFormulation = formulations[network_formulation](net)
net.add_layer(extra_input)
expected_msg = "Multiple input layers are not currently supported."
with pytest.raises(ValueError, match=expected_msg):
@@ -386,12 +389,7 @@ def _test_formulation_build_extra_input(network_formulation):
"""network_formulation can be:'FullSpace', 'ReducedSpace', 'relu'."""
net, y = two_node_network("linear", -2.0)
extra_input = InputLayer([1])
if network_formulation == "FullSpace":
formulation = FullSpaceNNFormulation(net)
elif network_formulation == "ReducedSpace":
formulation = ReducedSpaceNNFormulation(net)
elif network_formulation == "relu":
formulation = ReluPartitionFormulation(net)
formulation: _PyomoFormulation = formulations[network_formulation](net)
net.add_layer(extra_input)
m = pyo.ConcreteModel()
m.neural_net_block = OmltBlock()
@@ -410,12 +408,7 @@ def _test_formulation_added_extra_output(network_formulation):
weights=np.array([[1.0, 0.0], [5.0, 1.0]]),
biases=np.array([3.0, 4.0]),
)
if network_formulation == "FullSpace":
formulation = FullSpaceNNFormulation(net)
elif network_formulation == "ReducedSpace":
formulation = ReducedSpaceNNFormulation(net)
elif network_formulation == "relu":
formulation = ReluPartitionFormulation(net)
formulation: _PyomoFormulation = formulations[network_formulation](net)
net.add_layer(extra_output)
net.add_edge(list(net.layers)[-2], extra_output)
expected_msg = "Multiple output layers are not currently supported."
12 changes: 6 additions & 6 deletions tests/neuralnet/test_onnx.py
Original file line number Diff line number Diff line change
@@ -59,8 +59,8 @@ def obj(mdl):
SolverFactory("cbc").solve(model, tee=False)

x_s = (x - scale_x[0]) / scale_x[1]
x_s = np.array([[x_s]], dtype=np.float32)
outputs = net_regression.run(None, {"dense_input:0": x_s})
x_s_arr = np.array([[x_s]], dtype=np.float32)
outputs = net_regression.run(None, {"dense_input:0": x_s_arr})
y_s = outputs[0][0, 0]
y = y_s * scale_y[1] + scale_y[0]

@@ -102,8 +102,8 @@ def obj(mdl):
SolverFactory("cbc").solve(model, tee=False)

x_s = (x - scale_x[0]) / scale_x[1]
x_s = np.array([[x_s]], dtype=np.float32)
outputs = net_regression.run(None, {"dense_input:0": x_s})
x_s_arr = np.array([[x_s]], dtype=np.float32)
outputs = net_regression.run(None, {"dense_input:0": x_s_arr})
y_s = outputs[0][0, 0]
y = y_s * scale_y[1] + scale_y[0]

@@ -146,8 +146,8 @@ def obj(mdl):
SolverFactory("ipopt").solve(model, tee=False)

x_s = (x - scale_x[0]) / scale_x[1]
x_s = np.array([[x_s]], dtype=np.float32)
outputs = net_regression.run(None, {"dense_2_input:0": x_s})
x_s_arr = np.array([[x_s]], dtype=np.float32)
outputs = net_regression.run(None, {"dense_2_input:0": x_s_arr})
y_s = outputs[0][0, 0]
y = y_s * scale_y[1] + scale_y[0]

2 changes: 1 addition & 1 deletion tests/neuralnet/test_relu.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import numpy as np
import pyomo.environ as pyo
import pytest
from omlt.block import OmltBlock
from omlt import OmltBlock
from omlt.dependencies import onnx_available
from omlt.neuralnet import (
FullSpaceNNFormulation,
Loading