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

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# ------------------------------------------------------------------------------ 

18 

19"""Implements a helper reformulation to apply temporary options.""" 

20 

21from ..apidoc import api_end, api_start 

22from .reformulation import Reformulation 

23 

24_API_START = api_start(globals()) 

25# ------------------------------- 

26 

27 

28class ExtraOptions(Reformulation): 

29 """Helper reformulation to apply temporary options. 

30 

31 This reformulation is different from all others in a number of ways: 

32 

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``). 

39 

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 """ 

44 

45 @classmethod 

46 def supports(cls, footprint): 

47 """Implement :meth:`~.reformulation.Reformulation.supports`.""" 

48 return True 

49 

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 

56 

57 def execute(self, **extra_options): 

58 """Override :meth:`~.reformulation.Reformulation.execute`. 

59 

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 

65 

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__)) 

71 

72 # Shortcut the whole problem. 

73 self.output = self.input 

74 

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__)) 

81 

82 # Update the options. 

83 self.output.options = newOptions 

84 

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__)) 

94 

95 # Copy the problem and update the options. 

96 self.output = self.input.clone(copyOptions=False) 

97 self.output.options = newOptions 

98 

99 self._set_knowns() 

100 

101 # Make sure there is a successor to obtain a solution from. 

102 assert self.successor, \ 

103 "The reformulation being executed has no successor." 

104 

105 # Advance one step and return any solution as-is. 

106 return self.successor.execute() 

107 

108 def forward(self): 

109 """Dummy-implement :meth:`~.reformulation.Reformulation.forward`.""" 

110 pass 

111 

112 def update(self): 

113 """Dummy-implement :meth:`~.reformulation.Reformulation.update`.""" 

114 pass 

115 

116 def backward(self, solution): 

117 """Dummy-implement :meth:`~.reformulation.Reformulation.backward`.""" 

118 pass 

119 

120 

121# -------------------------------------- 

122__all__ = api_end(_API_START, globals())