-
Notifications
You must be signed in to change notification settings - Fork 93
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
Add Guroby encoder for max constratints. #402
Add Guroby encoder for max constratints. #402
Conversation
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
src/engine/MILPEncoder.cpp
Outdated
} | ||
|
||
return ubs[umaxIndex]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a new line here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
src/engine/MILPEncoder.cpp
Outdated
unsigned i = 0; | ||
for ( const auto &x : xs ) { | ||
// add binary variable | ||
gurobi.addVariable( Stringf( "a%u", x ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Naming the auxiliary variable like this would result in duplicated variable names. For instance, if we have two Max constraints Max([x1,x2], x4) and Max([x1,x3], x5), we would introduce the variable a1 twice, while we actually need two fresh variables.
One solution is to keep track of the latest index of the auxiliary variable. Please check the encodeReLUConstraint() method above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
src/engine/MILPEncoder.cpp
Outdated
terms.clear(); | ||
|
||
// add constraint: y >= x_i | ||
terms.append( GurobiWrapper::Term( 1, Stringf( "x%u", y ) ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"y >= x_i" is already encoded as an inequality here:
Marabou/src/engine/MaxConstraint.cpp
Lines 489 to 515 in 3bf23be
void MaxConstraint::addAuxiliaryEquations( InputQuery &inputQuery ) | |
{ | |
for ( auto element : _elements ) | |
{ | |
// If element is equal to _f, skip this step. | |
// The reason is to avoid adding equations like `1.00x00 -1.00x00 -1.00x01 = 0.00`. | |
if ( element == _f ) | |
continue; | |
// Create an aux variable | |
unsigned auxVariable = inputQuery.getNumberOfVariables(); | |
inputQuery.setNumberOfVariables( auxVariable + 1 ); | |
// f >= element, or f - elemenet - aux = 0, for non-negative aux | |
Equation equation( Equation::EQ ); | |
equation.addAddend( 1.0, _f ); | |
equation.addAddend( -1.0, element ); | |
equation.addAddend( -1.0, auxVariable ); | |
equation.setScalar( 0 ); | |
inputQuery.addEquation( equation ); | |
// Set the bounds for the aux variable | |
inputQuery.setLowerBound( auxVariable, 0 ); | |
// Todo: upper bound for aux? | |
} | |
} |
I think you could just remove line 182 - 188.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
src/engine/MILPEncoder.cpp
Outdated
} | ||
|
||
return ubs[umaxIndex]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a newline here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
src/engine/MILPEncoder.cpp
Outdated
i = 0; | ||
for ( const auto &x : xs ) { | ||
// add constraint: y <= x_i + (1 - a_i) * (umax - l) | ||
double umax = getUmax( ubs, i, m ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we store the <upper bound, variable> pair in a priority queue, we should be able to compute each Umax in O(log n) time. Currently it is computed in linear time, which might create overhead as the network size increases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did.
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
I changed this status to WIP since Test_preprocessor failed after merging master.
|
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Evaluation result for CNN having max pooling layer:1. MILPModelThe onnx file (
softmax was changed to linear function after compile. Test program (onnx_cnn_max_mnist2.py)from maraboupy import Marabou
import numpy as np
options = Marabou.createOptions(verbosity = 2, solveWithMILP=True)
filename = 'cnn_max_mninst2.onnx'
network = Marabou.read_onnx(filename)
inputVars = network.inputVars[0][0]
outputVars = network.outputVars[0]
delta = 0.1
for h in range(inputVars.shape[0]):
for w in range(inputVars.shape[1]):
network.setLowerBound(inputVars[h][w][0], 0.5-delta)
network.setUpperBound(inputVars[h][w][0], 0.5+delta)
# network.setLowerBound(outputVars[0], 1.0)
vals, stats = network.solve(options = options)
assert len(vals) > 0
dataONNX = np.zeros(inputVars.shape)
for i in range(inputVars.shape[0]):
for j in range(inputVars.shape[1]):
dataONNX[i][j][0] = vals[28 * i + j]
onnxEval = network.evaluateWithoutMarabou([dataONNX])
print('\nonnx output:\n', onnxEval) Execution$ /usr/bin/time -l python3 onnx_cnn_max_mnist2.py
(removed)
output 0 = -0.11791291526924269
output 1 = -0.1608994968618801
output 2 = -3.053131166312996
output 3 = -1.9990824676561814
output 4 = 1.0521385098435299
output 5 = -1.4255639982065038
output 6 = 0.02008915031516549
output 7 = -1.215866175485384
output 8 = -0.7083881322782134
output 9 = -0.47855364129358235
onnx output:
[[-0.11791354 -0.1609008 -3.0531301 -1.9990811 1.0521375 -1.4255633
0.02008897 -1.2158666 -0.7083874 -0.47855455]]
96692.76 real 93300.76 user 412.88 sys
12301275136 maximum resident set size
0 average shared memory size
0 average unshared data size
0 average unshared stack size
8717705 page reclaims
1576 page faults
0 swaps
0 block input operations
0 block output operations
0 messages sent
0 messages received
0 signals received
12856 voluntary context switches
19258691 involuntary context switches Elapsed timeTotal 27 hours. (20 hours until preprocess was done) 2. Marabou7 DAYS has passed, but it did not finish. |
_plConstraints.append( newPlc ); | ||
++numberOfDisjunctions; | ||
} | ||
else if ( constraint->getType() == MAX && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we assume that network level reasoner can pick up all the Max constraint. Maybe just remove the logic for Max here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your understanding is right.
However, when the network reasoner is true, the below part also picks up max constraints.
To avoid to pick up doubly, this neglects max constraints here.
for ( const auto &constraint : other._networkLevelReasoner->
getConstraintsInTopologicalOrder() )
{
auto *newPlc = constraint->duplicateConstraint();
_plConstraints.append( newPlc );
_networkLevelReasoner->addConstraintInTopologicalOrder( newPlc );
}
Does it make sense?
for ( const auto &x : xs ) | ||
{ | ||
// add binary variable | ||
gurobi.addVariable( Stringf( "a%u_%u", _binVarIndex, x ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please explain the name convention for future reference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did.
@@ -51,6 +51,8 @@ class MILPEncoder | |||
*/ | |||
Map<unsigned, String> _variableToVariableName; | |||
|
|||
unsigned _binVarIndex = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add documentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did.
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
Signed-off-by: tagomaru <tagomaru@users.noreply.github.com>
* Add Guroby encoder for max constratints. Signed-off-by: tagomaru <tagomaru@users.noreply.github.com> * change indents Signed-off-by: tagomaru <tagomaru@users.noreply.github.com> * fix error on windows Signed-off-by: tagomaru <tagomaru@users.noreply.github.com> * cosmetic Signed-off-by: tagomaru <tagomaru@users.noreply.github.com> * improvement Signed-off-by: tagomaru <tagomaru@users.noreply.github.com> * do not count max constratints doubly Signed-off-by: tagomaru <tagomaru@users.noreply.github.com> * fix bug Signed-off-by: tagomaru <tagomaru@users.noreply.github.com> * change comment Signed-off-by: tagomaru <tagomaru@users.noreply.github.com> * add h5 and onnx files Signed-off-by: tagomaru <tagomaru@users.noreply.github.com> * add comments for bin vars Signed-off-by: tagomaru <tagomaru@users.noreply.github.com> * add onnx and keras files for evaluation Signed-off-by: tagomaru <tagomaru@users.noreply.github.com> * Update InputQuery.h Co-authored-by: Haoze(Andrew) Wu <haozewu@stanford.edu>
Signed-off-by: tagomaru tagomaru@users.noreply.github.com
This PR is going to add Guroby encoder for max constraints.
Sample NNET:
Sample Code:
Test with the above sample code:
command
result