1#  ___________________________________________________________________________
2#
3#  Pyomo: Python Optimization Modeling Objects
4#  Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC
5#  Under the terms of Contract DE-NA0003525 with National Technology and
6#  Engineering Solutions of Sandia, LLC, the U.S. Government retains certain
7#  rights in this software.
8#  This software is distributed under the 3-clause BSD License.
9#  ___________________________________________________________________________
10
11import time
12import pyomo.opt
13from pyomo.core import TransformationFactory, Var, Set
14from pyomo.common.collections import Bunch
15
16
17@pyomo.opt.SolverFactory.register('bilevel_bqp',
18    doc='Global solver for bilevel quadratic problems')
19class BILEVEL_Solver4(pyomo.opt.OptSolver):
20
21    def __init__(self, **kwds):
22        kwds['type'] = 'bilevel_bqp'
23        pyomo.opt.OptSolver.__init__(self,**kwds)
24        self._metasolver = True
25
26    def _presolve(self, *args, **kwds):
27        self._instance = args[0]
28        pyomo.opt.OptSolver._presolve(self, *args, **kwds)
29
30    def _apply_solver(self):
31        start_time = time.time()
32        if not self.options.bigM:
33            self._bigM = 999
34        else:
35            self._bigM = self.options.bigM
36        #
37        # Cache the instance
38        #
39        xfrm = TransformationFactory('bilevel.linear_mpec')
40        xfrm.apply_to(self._instance)
41        xfrm = TransformationFactory('mpec.simple_disjunction')
42        xfrm.apply_to(self._instance)
43        xfrm = TransformationFactory('gdp.bigm')
44        xfrm.apply_to(self._instance, bigM=self.options.get('bigM',self._bigM))
45        #
46        ##self._instance.pprint()
47
48        xfrm = TransformationFactory('gdp.bilinear')
49        xfrm.apply_to(self._instance)
50        xfrm = TransformationFactory('gdp.bigm')
51        #xfrm.apply_to(self._instance, bigM=self.options.get('bigM',self._bigM))
52        xfrm.apply_to(self._instance, bigM=8888)
53
54        ##self._instance.pprint()
55        #
56        # Solve with a specified solver
57        #
58        solver = self.options.solver
59        if not solver:
60            solver = 'glpk'
61        # use the with block here so that deactivation of the
62        # solver plugin always occurs thereby avoiding memory
63        # leaks caused by plugins!
64        with pyomo.opt.SolverFactory(solver) as opt:
65            #
66            self.results = []
67            #
68            # **NOTE: It would be better to override _presolve on the
69            #         base class of this solver as you might be
70            #         missing a number of keywords that were passed
71            #         into the solve method (e.g., none of the
72            #         io_options are getting relayed to the subsolver
73            #         here).
74            #
75            self.results.append(opt.solve(self._instance,
76                                          tee=self._tee,
77                                          timelimit=self._timelimit,
78                                          symbolic_solver_labels=False,
79                                          keepfiles=False
80                                          ))
81        #
82        stop_time = time.time()
83        self.wall_time = stop_time - start_time
84        #self._instance.pprint()
85        #self._instance.display()
86        #
87        # Deactivate the block that contains the optimality conditions,
88        # and reactivate SubModel
89        #
90        submodel = self._instance._transformation_data[
91            'bilevel.linear_mpec'].submodel_cuid.find_component_on(
92                self._instance)
93        for (name, data) in submodel.component_map(active=False).items():
94            if not isinstance(data,Var) and not isinstance(data,Set):
95                data.activate()
96        #
97        # TODO: delete this subblock
98        # TODO: Remove bilinear and bigM blocks
99        #
100        self._instance._transformation_data[
101            'bilevel.linear_mpec'].block_cuid.find_component_on(
102                self._instance).deactivate()
103        #
104        # Return the sub-solver return condition value and log
105        #
106        return Bunch(rc=getattr(opt,'_rc', None),
107                                   log=getattr(opt,'_log',None))
108
109    def _postsolve(self):
110        #
111        # Create a results object
112        #
113        results = pyomo.opt.SolverResults()
114        #
115        # SOLVER
116        #
117        solv = results.solver
118        solv.name = self.options.subsolver
119        #solv.status = self._glpk_get_solver_status()
120        #solv.memory_used = "%d bytes, (%d KiB)" % (peak_mem, peak_mem/1024)
121        solv.wallclock_time = self.wall_time
122        cpu_ = []
123        for res in self.results:
124            if not getattr(res.solver, 'cpu_time', None) is None:
125                cpu_.append( res.solver.cpu_time )
126        if len(cpu_) > 0:
127            solv.cpu_time = sum(cpu_)
128        #
129        # TODO: detect infeasibilities, etc
130        #
131        solv.termination_condition = pyomo.opt.TerminationCondition.optimal
132        #
133        # PROBLEM
134        #
135        prob = results.problem
136        prob.name = self._instance.name
137        prob.number_of_constraints = self._instance.statistics.number_of_constraints
138        prob.number_of_variables = self._instance.statistics.number_of_variables
139        prob.number_of_binary_variables = self._instance.statistics.number_of_binary_variables
140        prob.number_of_integer_variables = self._instance.statistics.number_of_integer_variables
141        prob.number_of_continuous_variables = self._instance.statistics.number_of_continuous_variables
142        prob.number_of_objectives = self._instance.statistics.number_of_objectives
143        #
144        ##from pyomo.core import maximize
145        ##if self._instance.sense == maximize:
146            ##prob.sense = pyomo.opt.ProblemSense.maximize
147        ##else:
148            ##prob.sense = pyomo.opt.ProblemSense.minimize
149        #
150        # SOLUTION(S)
151        #
152        self._instance.solutions.store_to(results)
153        #
154        # Uncache the instance
155        #
156        self._instance = None
157        return results
158
159