GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/smt/optimization_solver.h Lines: 20 20 100.0 %
Date: 2021-08-17 Branches: 5 10 50.0 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Yancheng Ou, Michael Chang, Aina Niemetz
4
 *
5
 * This file is part of the cvc5 project.
6
 *
7
 * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS
8
 * in the top-level source directory and their institutional affiliations.
9
 * All rights reserved.  See the file COPYING in the top-level source
10
 * directory for licensing information.
11
 * ****************************************************************************
12
 *
13
 * The solver for optimization queries.
14
 */
15
16
#include "cvc5_private.h"
17
18
#ifndef CVC5__SMT__OPTIMIZATION_SOLVER_H
19
#define CVC5__SMT__OPTIMIZATION_SOLVER_H
20
21
#include "context/cdhashmap_forward.h"
22
#include "context/cdlist.h"
23
#include "expr/node.h"
24
#include "expr/type_node.h"
25
#include "util/result.h"
26
27
namespace cvc5 {
28
29
class SmtEngine;
30
31
namespace smt {
32
33
class OptimizationObjective;
34
class OptimizationResult;
35
36
/**
37
 * The optimization result, containing:
38
 * - the optimization result: SAT/UNSAT/UNKNOWN
39
 * - the optimal value if SAT and finite
40
 *     (optimal value reached and it's not infinity),
41
 *   or an empty node if SAT and infinite
42
 *   otherwise the value might be empty node
43
 *   or something suboptimal
44
 * - whether the result is finite/+-infinity
45
 */
46
170
class OptimizationResult
47
{
48
 public:
49
  enum IsInfinity
50
  {
51
    FINITE = 0,
52
    POSTITIVE_INF,
53
    NEGATIVE_INF
54
  };
55
  /**
56
   * Constructor
57
   * @param type the optimization outcome
58
   * @param value the optimized value
59
   * @param isInf whether the result is FINITE/POSITIVE_INF/NEGATIVE_INF
60
   **/
61
88
  OptimizationResult(Result result, TNode value, IsInfinity isInf = FINITE)
62
88
      : d_result(result), d_value(value), d_infinity(isInf)
63
  {
64
88
  }
65
56
  OptimizationResult()
66
56
      : d_result(Result::Sat::SAT_UNKNOWN,
67
                 Result::UnknownExplanation::NO_STATUS),
68
        d_value(),
69
56
        d_infinity(FINITE)
70
  {
71
56
  }
72
214
  ~OptimizationResult() = default;
73
74
  /**
75
   * Returns an enum indicating whether
76
   * the result is SAT or not.
77
   * @return whether the result is SAT, UNSAT or SAT_UNKNOWN
78
   **/
79
64
  Result getResult() const { return d_result; }
80
81
  /**
82
   * Returns the optimal value.
83
   * @return Node containing the optimal value,
84
   *   if result is infinite, this will be an empty node,
85
   *   if getResult() is UNSAT, it will return an empty node,
86
   *   if getResult() is SAT_UNKNOWN, it will return something suboptimal
87
   *   or an empty node, depending on how the solver runs.
88
   **/
89
140
  Node getValue() const { return d_value; }
90
91
  /**
92
   * Checks whether the result is infinity
93
   * @return whether the result is FINITE/POSITIVE_INF/NEGATIVE_INF
94
   **/
95
30
  IsInfinity isInfinity() const { return d_infinity; }
96
97
 private:
98
  /** indicating whether the result is SAT, UNSAT or UNKNOWN **/
99
  Result d_result;
100
  /** if the result is finite, this is storing the value **/
101
  Node d_value;
102
  /** whether the result is finite/+infinity/-infinity **/
103
  IsInfinity d_infinity;
104
};
105
106
/**
107
 * To serialize the OptimizationResult.
108
 * @param out the stream to put the serialized result
109
 * @param result the OptimizationResult object to serialize
110
 * @return the parameter out
111
 **/
112
std::ostream& operator<<(std::ostream& out, const OptimizationResult& result);
113
114
/**
115
 * The optimization objective, which contains:
116
 * - the optimization target node,
117
 * - whether it's maximize/minimize
118
 * - and whether it's signed for BitVectors
119
 */
120
class OptimizationObjective
121
{
122
 public:
123
  /**
124
   * An enum for optimization queries.
125
   * Represents whether an objective should be minimized or maximized
126
   */
127
  enum ObjectiveType
128
  {
129
    MINIMIZE,
130
    MAXIMIZE,
131
  };
132
133
  /**
134
   * Constructor
135
   * @param target the optimization target node
136
   * @param type speficies whether it's maximize/minimize
137
   * @param bvSigned specifies whether it's using signed or unsigned comparison
138
   *    for BitVectors this parameter is only valid when the type of target node
139
   *    is BitVector
140
   **/
141
40
  OptimizationObjective(TNode target, ObjectiveType type, bool bvSigned = false)
142
40
      : d_type(type), d_target(target), d_bvSigned(bvSigned)
143
  {
144
40
  }
145
40
  ~OptimizationObjective() = default;
146
147
  /** A getter for d_type **/
148
86
  ObjectiveType getType() const { return d_type; }
149
150
  /** A getter for d_target **/
151
226
  Node getTarget() const { return d_target; }
152
153
  /** A getter for d_bvSigned **/
154
78
  bool bvIsSigned() const { return d_bvSigned; }
155
156
 private:
157
  /**
158
   * The type of objective,
159
   * it's either MAXIMIZE OR MINIMIZE
160
   **/
161
  ObjectiveType d_type;
162
163
  /**
164
   * The node associated to the term that was used to construct the objective.
165
   **/
166
  Node d_target;
167
168
  /**
169
   * Specify whether to use signed or unsigned comparison
170
   * for BitVectors (only for BitVectors), this variable is defaulted to false
171
   **/
172
  bool d_bvSigned;
173
};
174
175
/**
176
 * To serialize the OptimizationObjective.
177
 * @param out the stream to put the serialized result
178
 * @param objective the OptimizationObjective object to serialize
179
 * @return the parameter out
180
 **/
181
std::ostream& operator<<(std::ostream& out,
182
                         const OptimizationObjective& objective);
183
184
/**
185
 * A solver for optimization queries.
186
 *
187
 * This class is responsible for responding to optmization queries. It
188
 * spawns a subsolver SmtEngine that captures the parent assertions and
189
 * implements a linear optimization loop. Supports activateObjective,
190
 * checkOpt, and objectiveGetValue in that order.
191
 */
192
class OptimizationSolver
193
{
194
 public:
195
  /**
196
   * An enum specifying how multiple objectives are dealt with.
197
   * Definition:
198
   *   phi(x, y): set of assertions with variables x and y
199
   *
200
   * Box: treat the objectives as independent objectives
201
   *   v_x = max(x) s.t. phi(x, y) = sat
202
   *   v_y = max(y) s.t. phi(x, y) = sat
203
   *
204
   * Lexicographic: optimize the objectives one-by-one, in the order they are
205
   * added:
206
   *   v_x = max(x) s.t. phi(x, y) = sat
207
   *   v_y = max(y) s.t. phi(v_x, y) = sat
208
   *
209
   * Pareto: optimize multiple goals to a state such that
210
   * further optimization of one goal will worsen the other goal(s)
211
   *   (v_x, v_y) s.t. phi(v_x, v_y) = sat, and
212
   *     forall (x, y), (phi(x, y) = sat) -> (x <= v_x or y <= v_y)
213
   **/
214
  enum ObjectiveCombination
215
  {
216
    BOX,
217
    LEXICOGRAPHIC,
218
    PARETO,
219
  };
220
  /**
221
   * Constructor
222
   * @param parent the smt_solver that the user added their assertions to
223
   **/
224
  OptimizationSolver(SmtEngine* parent);
225
26
  ~OptimizationSolver() = default;
226
227
  /**
228
   * Run the optimization loop for the added objective
229
   * For multiple objective combination, it defaults to lexicographic,
230
   * possible combinations: BOX, LEXICOGRAPHIC, PARETO
231
   * @param combination BOX / LEXICOGRAPHIC / PARETO
232
   */
233
  Result checkOpt(ObjectiveCombination combination = LEXICOGRAPHIC);
234
235
  /**
236
   * Add an optimization objective.
237
   * @param target Node representing the expression that will be optimized for
238
   * @param type specifies whether it's maximize or minimize
239
   * @param bvSigned specifies whether we should use signed/unsigned
240
   *   comparison for BitVectors (only effective for BitVectors)
241
   *   and its default is false
242
   **/
243
  void addObjective(TNode target,
244
                    OptimizationObjective::ObjectiveType type,
245
                    bool bvSigned = false);
246
247
  /**
248
   * Returns the values of the optimized objective after checkOpt is called
249
   * @return a vector of Optimization Result,
250
   *   each containing the outcome and the value.
251
   **/
252
  std::vector<OptimizationResult> getValues();
253
254
 private:
255
  /**
256
   * Initialize an SMT subsolver for offline optimization purpose
257
   * @param parentSMTSolver the parental solver containing the assertions
258
   * @param needsTimeout specifies whether it needs timeout for each single
259
   *    query
260
   * @param timeout the timeout value, given in milliseconds (ms)
261
   * @return a unique_pointer of SMT subsolver
262
   **/
263
  static std::unique_ptr<SmtEngine> createOptCheckerWithTimeout(
264
      SmtEngine* parentSMTSolver,
265
      bool needsTimeout = false,
266
      unsigned long timeout = 0);
267
268
  /**
269
   * Optimize multiple goals in Box order
270
   * @return SAT if all of the objectives are optimal (could be infinite);
271
   *   UNSAT if at least one objective is UNSAT and no objective is SAT_UNKNOWN;
272
   *   SAT_UNKNOWN if any of the objective is SAT_UNKNOWN.
273
   **/
274
  Result optimizeBox();
275
276
  /**
277
   * Optimize multiple goals in Lexicographic order,
278
   * using iterative implementation
279
   * @return SAT if the objectives are optimal,
280
   *     if one of the objectives is unbounded (result is infinite),
281
   *     the optimization will stop at that objective;
282
   *   UNSAT if any of the objectives is UNSAT
283
   *     and optimization will stop at that objective;
284
   *   SAT_UNKNOWN if any of the objectives is UNKNOWN
285
   *     and optimization will stop at that objective;
286
   *   If the optimization is stopped at an objective,
287
   *     all objectives following that objective will be SAT_UNKNOWN.
288
   **/
289
  Result optimizeLexicographicIterative();
290
291
  /**
292
   * Optimize multiple goals in Pareto order
293
   * Using a variant of linear search called Guided Improvement Algorithm
294
   * Could be called multiple times to iterate through the Pareto front
295
   *
296
   * Definition:
297
   * Pareto front: Set of all possible Pareto optimal solutions
298
   *
299
   * Reference:
300
   * D. Rayside, H.-C. Estler, and D. Jackson. The Guided Improvement Algorithm.
301
   *  Technical Report MIT-CSAIL-TR-2009-033, MIT, 2009.
302
   *
303
   * @return if it finds a new Pareto optimal result it will return SAT;
304
   *   if it exhausts the results in the Pareto front it will return UNSAT;
305
   *   if the underlying SMT solver returns SAT_UNKNOWN,
306
   *   it will return SAT_UNKNOWN.
307
   **/
308
  Result optimizeParetoNaiveGIA();
309
310
  /** A pointer to the parent SMT engine **/
311
  SmtEngine* d_parent;
312
313
  /** A subsolver for offline optimization **/
314
  std::unique_ptr<SmtEngine> d_optChecker;
315
316
  /** The objectives to optimize for **/
317
  context::CDList<OptimizationObjective> d_objectives;
318
319
  /** The results of the optimizations from the last checkOpt call **/
320
  std::vector<OptimizationResult> d_results;
321
};
322
323
}  // namespace smt
324
}  // namespace cvc5
325
326
#endif /* CVC5__SMT__OPTIMIZATION_SOLVER_H */