Source code for picos.reforms.reform_options
# ------------------------------------------------------------------------------
# Copyright (C) 2019 Maximilian Stahlberg
#
# This file is part of PICOS.
#
# PICOS is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PICOS is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ------------------------------------------------------------------------------
"""Implements a helper reformulation to apply temporary options."""
from ..apidoc import api_end, api_start
from .reformulation import Reformulation
_API_START = api_start(globals())
# -------------------------------
[docs]class ExtraOptions(Reformulation):
"""Helper reformulation to apply temporary options.
This reformulation is different from all others in a number of ways:
- It doesn't change the footprint of any problem.
- It is automatically the first reformulation in any strategy.
- It is the only reformulation whose :meth:`execute` accepts a keyword
argument sequence of additional options to use.
- It is the only reformulation that can be skipped entirely (by setting
``self.output = self.input``).
The job of this reformulation is to apply temporary options passed to
:meth:`Problem.solve <picos.Problem.solve>` so that subsequent
reformulations can find their options stored in their input problem.
"""
[docs] @classmethod
def supports(cls, footprint):
"""Implement :meth:`~.reformulation.Reformulation.supports`."""
return True
[docs] @classmethod
def predict(cls, footprint):
"""Implement :meth:`~.reformulation.Reformulation.predict`."""
# The additional options are already part of the footprint!
# (They are applied before the first prediction takes place.)
return footprint
[docs] def execute(self, **extra_options):
"""Override :meth:`~.reformulation.Reformulation.execute`.
Adds the ``extra_options`` argument and attempts to perform as little
reformulation work as possible.
"""
newOptions = self.input.options.self_or_updated(**extra_options)
verbose = newOptions.verbosity > 0
if newOptions == self.input.options:
# No options need to be applied; shortcut this reformulation so that
# no forwarding or updating work needs to be done.
if verbose:
print("Skipping {}.".format(self.__class__.__name__))
# Shortcut the whole problem.
self.output = self.input
self._reset_knowns()
elif self.output and self.output is not self.input:
# We made a problem clone before and we still can't shortcut, so
# perform a regular update and apply the new options to the copy.
if verbose:
print("Updating {}.".format(self.__class__.__name__))
# Update the options.
self.output.options = newOptions
self._pass_updated_objective()
self._pass_updated_vars()
self._pass_updated_cons()
else:
# We need to apply temporary options and this is either the first
# execution or the previous execution was a shortcut. Perform a
# regular forward and apply the new options to the problem clone.
if verbose:
print("Applying {}.".format(self.__class__.__name__))
# Copy the problem and update the options.
self.output = self.input.clone(copyOptions=False)
self.output.options = newOptions
self._set_knowns()
# Make sure there is a successor to obtain a solution from.
assert self.successor, \
"The reformulation being executed has no successor."
# Advance one step and return any solution as-is.
return self.successor.execute()
[docs] def backward(self, solution):
"""Dummy-implement :meth:`~.reformulation.Reformulation.backward`."""
pass
# --------------------------------------
__all__ = api_end(_API_START, globals())