-
Notifications
You must be signed in to change notification settings - Fork 1
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 branching priorities to CPLEXSHELL #1
Conversation
- Include `branch_priority` and `branch_direction` attributes of variable data - Read off these attributes as part of the CPLEXSHELL solve and write them to a CPLEX .ord file - Update the CPLEX OPL script to read these .ord file should it exist
- End-to-end tests of `opt.solve()` using branching priorities with CPLEXSHELL
- Declare `priority` and `direction` suffixes on the model instead of attributes on the variable data in order to represent branching decisions
9bbba62
to
5e44956
Compare
- Looks like there's some circular imports failing specifically for Py2.7 - Follow the same (hacky) local import design chosen in 0935e75
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.
See comments. Main bits:
- Pulling out direction class into a more global enum and making the translation CPLEX specific
- Splitting up
_write_priorities_file
(if it makes sense)
class CPLEXBranchDirection: | ||
default = 0 | ||
down = -1 | ||
up = 1 | ||
|
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.
This could probably be pulled out to be a 'global' enum so it can be used for all solvers that support branching priorities. The only solver specific stuff here is the translation so that can be done in each individual solver.
That way you can build your model agnostic of what solver it will run on, which I don't think would be possible with the current implementation.
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 see you have to specify a data type when declaring the Suffix. I guess you should add a 'data_type' attribute or something as well in that case that can be used.
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.
The only solver specific stuff here is the translation
Ah not quite 😉 These magic integer values themselves are CPLEX-specific. See here. Having said that, it turns out that Gurobi also just happens to share the same magic values... So I'll do the refactor!
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.
Where are these magic integer values being used for CLEX specific stuff? Aren't they being translated in to_str
?
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.
As discussed, they will be used in the CPLEXDirect and GurobiDirect solver implementations which have not been included here. For now, I've removed CPLEXBranchDirection
and moved the translation to ORDFileSchema
try: | ||
return {CPLEXBranchDirection.down: "DN", CPLEXBranchDirection.up: "UP"}[ | ||
branch_direction | ||
] | ||
except KeyError: | ||
return "" |
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.
No idea if more efficient/readable but dict.get
is an option here. But Python tends to favour 'easier to ask for forgiveness' over 'look before you leap' so I guess this is fine too.
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.
But Python tends to favour 'easier to ask for forgiveness'
Yeah, that was my intention
|
||
def _write_priorities_file(self, instance) -> None: | ||
""" Write a variable priorities file in the CPLEX ORD format. """ | ||
from pyomo.core.base import Var |
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.
Is there a reason this is an inline import?
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 was copying _warm_start()
where this is a local import too... I'll try removing both and 🤞 nothing breaks
SUFFIX_PRIORITY_NAME = "priority" | ||
SUFFIX_DIRECTION_NAME = "direction" | ||
|
||
def _write_priorities_file(self, instance) -> None: |
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.
NB: This has a return type (I'm assuming not Python 2.7 compliant)
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.
Ah perfect, that's probably the reason 2.7 is breaking. Thanks!
SUFFIX_PRIORITY_NAME = "priority" | ||
SUFFIX_DIRECTION_NAME = "direction" | ||
|
||
def _write_priorities_file(self, instance) -> None: |
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.
What is instance? Is it worth documenting or is it Pyomo lingo?
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.
Yup, it's standard (see _warm_start()
)
ORDFileSchema.ROW(byObject[id(child_var)], priority, child_var_direction) | ||
) | ||
|
||
ord_file.write(ORDFileSchema.FOOTER) |
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.
Worth splitting this method up a bit? To me it seems like there's 2-3 steps here:
- input preparation
- translation
- writing to the file
It looks to me like some of this logic is not CPLEX specific so if we manage to split this method up into parts it'll most likely be easier to implement extra solvers later.
This is based on my assumption that it is mainly the translation that is solver specific.
' UP x11 2\n' | ||
'ENDATA\n' | ||
) | ||
|
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.
For these tests, please use the priority constant instead of magic numbers. That'll make them easier to maintain, e.g. if we decide to represent them with different numbers or something.
And there is no ambiguity of what they mean.
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.
Updated, however I felt this was a bit too convoluted for test_use_variable_priorities()
assert priorities_file == ( | ||
'* ENCODING=ISO-8859-1\n' | ||
'NAME Priority Order\n' | ||
' x1 1\n' | ||
' DN x2 2\n' | ||
' DN x3 2\n' | ||
' DN x4 2\n' | ||
' DN x5 2\n' | ||
' DN x6 2\n' | ||
' DN x7 2\n' | ||
' DN x8 2\n' | ||
' DN x9 2\n' | ||
' DN x10 2\n' | ||
' UP x11 2\n' | ||
'ENDATA\n' | ||
) |
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.
Perhaps this assertion is neater if you read the contents of priorities_file_name
before doing the final solve and comparing that against priorities_file
? They should be the same right?
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.
Sure
- Update tests to avoid usage of "magic numbers" and reference `BranchDirection` where possible
Remove Port.Conservative, be more selective for evar bounds
merge master into pynumero_mumps
Minor formatting changes to consolidate unix & mpi drivers
Cleaning up sensitivity_toolbox tests
Further minor polishing fixes
Fixes Pyomo#1299
Summary/Motivation:
To be able to use the branching priority capability of commercial solvers when solving MIP problems
Changes proposed in this PR:
branch_priority
andbranch_direction
attributes of variable dataLegal Acknowledgement
By contributing to this software project, I agree to the following terms and conditions for my contribution: