Coverage for picos/reforms/reform_options.py: 80.00%
40 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-26 07:46 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-26 07:46 +0000
1# ------------------------------------------------------------------------------
2# Copyright (C) 2019 Maximilian Stahlberg
3#
4# This file is part of PICOS.
5#
6# PICOS is free software: you can redistribute it and/or modify it under the
7# terms of the GNU General Public License as published by the Free Software
8# Foundation, either version 3 of the License, or (at your option) any later
9# version.
10#
11# PICOS is distributed in the hope that it will be useful, but WITHOUT ANY
12# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License along with
16# this program. If not, see <http://www.gnu.org/licenses/>.
17# ------------------------------------------------------------------------------
19"""Implements a helper reformulation to apply temporary options."""
21from ..apidoc import api_end, api_start
22from .reformulation import Reformulation
24_API_START = api_start(globals())
25# -------------------------------
28class ExtraOptions(Reformulation):
29 """Helper reformulation to apply temporary options.
31 This reformulation is different from all others in a number of ways:
33 - It doesn't change the footprint of any problem.
34 - It is automatically the first reformulation in any strategy.
35 - It is the only reformulation whose :meth:`execute` accepts a keyword
36 argument sequence of additional options to use.
37 - It is the only reformulation that can be skipped entirely (by setting
38 ``self.output = self.input``).
40 The job of this reformulation is to apply temporary options passed to
41 :meth:`~.problem.Problem.solve` so that subsequent reformulations can find
42 their options stored in their input problem.
43 """
45 @classmethod
46 def supports(cls, footprint):
47 """Implement :meth:`~.reformulation.Reformulation.supports`."""
48 return True
50 @classmethod
51 def predict(cls, footprint):
52 """Implement :meth:`~.reformulation.Reformulation.predict`."""
53 # The additional options are already part of the footprint!
54 # (They are applied before the first prediction takes place.)
55 return footprint
57 def execute(self, **extra_options):
58 """Override :meth:`~.reformulation.Reformulation.execute`.
60 Adds the ``extra_options`` argument and attempts to perform as little
61 reformulation work as possible.
62 """
63 newOptions = self.input.options.self_or_updated(**extra_options)
64 verbose = newOptions.verbosity > 0
66 if newOptions == self.input.options:
67 # No options need to be applied; shortcut this reformulation so that
68 # no forwarding or updating work needs to be done.
69 if verbose:
70 print("Skipping {}.".format(self.__class__.__name__))
72 # Shortcut the whole problem.
73 self.output = self.input
75 self._reset_knowns()
76 elif self.output and self.output is not self.input:
77 # We made a problem clone before and we still can't shortcut, so
78 # perform a regular update and apply the new options to the copy.
79 if verbose:
80 print("Updating {}.".format(self.__class__.__name__))
82 # Update the options.
83 self.output.options = newOptions
85 self._pass_updated_objective()
86 self._pass_updated_vars()
87 self._pass_updated_cons()
88 else:
89 # We need to apply temporary options and this is either the first
90 # execution or the previous execution was a shortcut. Perform a
91 # regular forward and apply the new options to the problem clone.
92 if verbose:
93 print("Applying {}.".format(self.__class__.__name__))
95 # Copy the problem and update the options.
96 self.output = self.input.clone(copyOptions=False)
97 self.output.options = newOptions
99 self._set_knowns()
101 # Make sure there is a successor to obtain a solution from.
102 assert self.successor, \
103 "The reformulation being executed has no successor."
105 # Advance one step and return any solution as-is.
106 return self.successor.execute()
108 def forward(self):
109 """Dummy-implement :meth:`~.reformulation.Reformulation.forward`."""
110 pass
112 def update(self):
113 """Dummy-implement :meth:`~.reformulation.Reformulation.update`."""
114 pass
116 def backward(self, solution):
117 """Dummy-implement :meth:`~.reformulation.Reformulation.backward`."""
118 pass
121# --------------------------------------
122__all__ = api_end(_API_START, globals())