-
Notifications
You must be signed in to change notification settings - Fork 522
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
Adding (parameterized) linear programming dual transformation! #3402
Conversation
… with where the mappings are stored
…ing transformed model match
…that assume that the data is numeric. Creating my own csr and csc classes that are almost complete
…ssions in the data
…rized_standard_form itself.
…use the correct converstion to a CSC matrix depending on the circumstances, testing the conversion to nonnegative vars
…ut parameterized standard repn doesn't
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.
Adding comments from an in-person review with @jsiirola
pyomo/util/config_domains.py
Outdated
def __call__(self, x): | ||
if hasattr(x, 'ctype') and x.ctype is self._ctype: | ||
if not x.is_indexed(): | ||
return ComponentSet([x]) | ||
ans = ComponentSet() | ||
for j in x.index_set(): | ||
ans.add(x[j]) | ||
return ans | ||
elif hasattr(x, '__iter__'): | ||
ans = ComponentSet() | ||
for i in x: | ||
ans.update(self(i)) | ||
return ans | ||
else: | ||
_ctype_name = str(self._ctype) | ||
raise ValueError( | ||
f"Expected {_ctype_name} or iterable of " | ||
f"{_ctype_name}s.\n\tReceived {type(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.
There are some issues with this implementation:
- it doesn't actually return a list. ;)
- it doesn't actually validate the ctype for list-like inputs
Maybe the following would be a more robust alternative:
def __init__(self, ctype):
if isinstance(ctype, Sequence):
self._ctypes = set(ctype)
else:
self._ctypes = set([ctype])
def __call__(self, x):
return list(self._process(x))
def _process(self, x):
if hasattr(x, 'ctype'):
if x.ctype not in self._ctypes:
raise ValueError(" .... ")
if x.is_indexed():
yield from x.values()
else:
yield x
elif isinstance(x, Sequence):
for y in x:
yield from self._process(y)
else:
raise ValueError(" ... ")
def domain_name(self):
_names = ', '.join(ct.__name__ for ct in self._ctypes)
if len(self._ctypes) > 1:
_names = '[' + _names + ']'
return f"ComponentDataList({_names})")
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 actually really want it to both return and accept ComponentSets, but just took your suggestions otherwise (and renamed it accordingly)
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 is very, very close
return _ParameterizedLinearStandardFormCompiler_impl(config).write(model) | ||
|
||
|
||
class _SparseMatrixBase(object): |
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.
Some day (not today) we should refactor the sparse sets into pyomo.common
pyomo/util/config_domains.py
Outdated
# Ordering for determinism | ||
_ctypes = sorted([ct.__name__ for ct in self._ctypes]) | ||
_names = ', '.join(_ctypes) |
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.
We don't want to convert the set() to a list: that is unnecessary and converts O(1) lookups into O(n). We should sort the names ... but again, we should only generate the names string if we are going to actually use it (i.e. before raising an exception).
Fixes the complaints of several reasonable people
Summary/Motivation:
This PR adds a transformation in
core
to take the dual of a linear program. For the purposes of supporting bilevel programming, it also allows for this dual to be "parameterized" by certain variables--that is, the user can specify a list of Vars that are treated as data for the purposes of taking the dual (e.g., if they are the outer variables of a bilevel program with an LP inner problem).Changes proposed in this PR:
core.lp_dual
transformation.Legal Acknowledgement
By contributing to this software project, I have read the contribution guide and agree to the following terms and conditions for my contribution: