dReal4
contractor_forall.h
1 #pragma once
2 
3 #include <cmath>
4 #include <memory>
5 #include <ostream>
6 #include <stdexcept>
7 #include <utility>
8 #include <vector>
9 
10 #include "ThreadPool/ThreadPool.h"
11 
12 #include "dreal/contractor/contractor.h"
13 #include "dreal/contractor/contractor_cell.h"
14 #include "dreal/contractor/counterexample_refiner.h"
15 #include "dreal/contractor/generic_contractor_generator.h"
16 #include "dreal/util/assert.h"
17 #include "dreal/util/box.h"
18 #include "dreal/util/exception.h"
19 #include "dreal/util/interrupt.h"
20 #include "dreal/util/logging.h"
21 #include "dreal/util/nnfizer.h"
22 #include "dreal/util/optional.h"
23 
24 namespace dreal {
25 
26 /// Add Doc.
27 Box RefineCounterexample(const Formula& query,
28  const Variables& quantified_variables, Box b,
29  double precision);
30 
31 /// Contractor for forall constraints. See the following problem
32 /// definition and our approach.
33 ///
34 /// <pre>
35 /// Problem: Given a box B ∈ IRⁿ and a formula F =
36 /// ∃x₁...xₙ. ∀y₁....yₘ. φ(x₁, ..., xₙ, y₁, ..., yₘ), design a
37 /// contractor reducing B into B' where B' ⊆ B.
38 ///
39 /// Approach: Find a counterexample (a₁, ..., aₙ, b₁, ..., bₘ) such
40 /// that ¬φ(a₁, ..., aₙ, b₁, ..., bₘ) holds while (a₁, ..., aₙ) ∈ B.
41 /// We do this by computing Solve(strengthen(¬φ, ε), δ) where ε > δ.
42 ///
43 /// - Case 1: No CE found.
44 /// This means that any point in B satisfies the quantified
45 /// portion of F.
46 ///
47 /// - Case 2: Found a CE, (a₁, ..., aₙ, b₁, ..., bₘ).
48 /// Use the CE to reduce B into B'. That is compute,
49 ///
50 /// B' = Contract(φ(x₁, ..., xₙ, b₁, ..., bₘ), B)
51 ///
52 /// </pre>
53 template <typename ContextType>
54 class ContractorForall : public ContractorCell {
55  public:
56  /// Constructs Forall contractor using @p f and @p box. @p epsilon is
57  /// used to strengthen ¬φ and @p inner_delta is used to solve (¬φ)⁻ᵟ¹.
58  ///
59  /// @pre 0.0 < inner_delta < epsilon < config.precision().
60  ContractorForall(Formula f, const Box& box, double epsilon,
61  double inner_delta, const Config& config)
62  : ContractorCell{Contractor::Kind::FORALL,
63  DynamicBitset(box.size()), config},
64  f_{std::move(f)},
65  quantified_variables_{get_quantified_variables(f_)},
66  strengthend_negated_nested_f_{Nnfizer{}.Convert(
67  DeltaStrengthen(!get_quantified_formula(f_), epsilon), true)},
68  contractor_{config /* This one will be updated anyway. */},
69  context_for_counterexample_{config} {
70  DREAL_ASSERT(epsilon > 0.0);
71  DREAL_ASSERT(inner_delta > 0.0);
72  DREAL_ASSERT(config.precision() > epsilon);
73  DREAL_ASSERT(epsilon > inner_delta);
74  DREAL_ASSERT(!is_false(strengthend_negated_nested_f_));
75 
77 
78  // Setup context:
79  // 0. Setup context, config, and the contractor for finding counterexamples.
80  context_for_counterexample_.mutable_config().mutable_precision() =
81  inner_delta;
82  context_for_counterexample_.mutable_config().mutable_use_polytope() =
84  contractor_ = GenericContractorGenerator{}.Generate(
85  get_quantified_formula(f_), ExtendBox(box, quantified_variables_),
86  context_for_counterexample_.config());
87  // 1. Add exist/forall variables.
88  for (const Variable& exist_var : box.variables()) {
89  context_for_counterexample_.DeclareVariable(exist_var);
90  }
91  for (const Variable& forall_var : get_quantified_variables(f_)) {
92  context_for_counterexample_.DeclareVariable(forall_var);
93  }
94  // 2. Assert strengthen(¬φ, ε).
95  if (is_conjunction(strengthend_negated_nested_f_)) {
96  // Optimizations
97  for (const Formula& formula :
98  get_operands(strengthend_negated_nested_f_)) {
99  context_for_counterexample_.Assert(formula);
100  }
101  } else {
102  context_for_counterexample_.Assert(strengthend_negated_nested_f_);
103  }
104 
105  // Build input.
106  DynamicBitset& input{mutable_input()};
107  for (const Variable& v : f_.GetFreeVariables()) {
108  input.set(box.index(v));
109  }
110  if (this->config().use_local_optimization()) {
111  refiner_ = std::make_unique<CounterexampleRefiner>(
112  strengthend_negated_nested_f_, quantified_variables_,
113  context_for_counterexample_.config());
114  }
115  }
116 
117  /// Deleted copy constructor.
118  ContractorForall(const ContractorForall&) = delete;
119 
120  /// Deleted move constructor.
122 
123  /// Deleted copy assign operator.
124  ContractorForall& operator=(const ContractorForall&) = delete;
125 
126  /// Deleted move assign operator.
128 
129  bool PruneWithCounterexample(ContractorStatus* cs, Box* const current_box,
130  const Box& counterexample) const {
131  // Need to prune the current_box using counterexample.
132  ContractorStatus contractor_status(counterexample);
133  // 1.1.1. Set up exist_var parts for pruning
134  for (const Variable& exist_var : current_box->variables()) {
135  contractor_status.mutable_box()[exist_var] = (*current_box)[exist_var];
136  }
137  // 1.1.2. Set up universal_var parts from
138  // counterexample. Narrow down the forall variables part by
139  // taking the mid-points of counterexample.
140  for (const Variable& forall_var : get_quantified_variables(f_)) {
141  contractor_status.mutable_box()[forall_var] =
142  counterexample[forall_var].mid();
143  }
144  contractor_.Prune(&contractor_status);
145  if (contractor_status.box().empty()) {
146  // If the pruning result is empty, there is nothing more to do. Exit
147  // the loop.
148  cs->mutable_output().set();
149  current_box->set_empty();
150  return true;
151  } else {
152  // Otherwise, we update the current box and keep looping.
153  bool changed = false;
154  for (int i = 0; i < cs->box().size(); ++i) {
155  if (cs->box()[i] != contractor_status.box()[i]) {
156  cs->mutable_output().set(i);
157  (*current_box)[i] = contractor_status.box()[i];
158  changed = true;
159  }
160  }
161  if (!changed) {
162  // We reached at a fixed-point.
163  return true;
164  }
165  }
166  return false;
167  }
168 
169  /// Default destructor.
170  ~ContractorForall() override = default;
171 
172  void Prune(ContractorStatus* cs) const override {
173  Box& current_box = cs->mutable_box();
174  Config& config_for_counterexample{
175  context_for_counterexample_.mutable_config()};
176  while (true) {
177  // Note that 'DREAL_CHECK_INTERRUPT' is only defined in setup.py,
178  // when we build dReal python package.
179 #ifdef DREAL_CHECK_INTERRUPT
180  if (g_interrupted) {
181  DREAL_LOG_DEBUG("KeyboardInterrupt(SIGINT) Detected.");
182  throw std::runtime_error("KeyboardInterrupt(SIGINT) Detected.");
183  }
184 #endif
185 
186  // 1. Find Counterexample.
187  for (const Variable& exist_var : current_box.variables()) {
188  context_for_counterexample_.SetInterval(exist_var,
189  current_box[exist_var].lb(),
190  current_box[exist_var].ub());
191  }
192  // Alternate the stacking order.
193  config_for_counterexample.mutable_stack_left_box_first() =
194  !config_for_counterexample.stack_left_box_first();
195  optional<Box> counterexample_opt = context_for_counterexample_.CheckSat();
196  if (counterexample_opt) {
197  Box& counterexample{*counterexample_opt};
198  // 1.1. Counterexample found.
199  DREAL_LOG_DEBUG("ContractorForall::Prune: Counterexample found:\n{}",
200  counterexample);
201 
202  if (config().use_local_optimization()) {
203  counterexample = refiner_->Refine(counterexample);
204  }
205  bool need_to_break_the_loop =
206  PruneWithCounterexample(cs, &current_box, counterexample);
207  if (need_to_break_the_loop) {
208  break;
209  }
210  } else {
211  // 1.2. No counterexample found.
212  DREAL_LOG_DEBUG("ContractorForall::Prune: No counterexample found.");
213  break;
214  }
215  }
216  cs->AddUsedConstraint(f_);
217  }
218 
219  std::ostream& display(std::ostream& os) const override {
220  return os << "ContractorForall(" << f_ << ")";
221  }
222 
223  private:
224  static Box ExtendBox(Box box, const Variables& vars) {
225  for (const Variable& v : vars) {
226  box.Add(v);
227  }
228  return box;
229  }
230 
231  const Formula f_; // ∀X.φ
232  const Variables quantified_variables_; // X
233  const Formula strengthend_negated_nested_f_; // (¬φ)⁻ᵟ¹
234  // To compute `B' = Contract(φ(x₁, ..., xₙ, b₁, ..., bₘ), B)`.
235  Contractor contractor_;
236  // Context to do `Solve(¬φ', δ₂)`.
237  mutable ContextType context_for_counterexample_;
238  const bool use_local_optimization_{false};
239 
240  std::unique_ptr<CounterexampleRefiner> refiner_;
241 };
242 
243 template <typename ContextType>
245  public:
246  /// Deleted default constructor.
247  ContractorForallMt() = delete;
248 
249  /// Constructs ForallMt contractor using @p f and @p box.
250  ContractorForallMt(Formula f, const Box& box, double epsilon,
251  double inner_delta, const Config& config)
252  : ContractorCell{Contractor::Kind::FORALL,
253  DynamicBitset(box.size()), config},
254  f_{std::move(f)},
255  epsilon_{epsilon},
256  inner_delta_{inner_delta},
257  ctc_ready_(config.number_of_jobs(), 0),
258  ctcs_(ctc_ready_.size()) {
259  ContractorForall<ContextType>* const ctc{GetCtcOrCreate(box)};
260  DREAL_ASSERT(ctc);
261  // Build input.
262  mutable_input() = ctc->input();
263  }
264 
265  /// Deleted copy constructor.
266  ContractorForallMt(const ContractorForallMt&) = delete;
267 
268  /// Deleted move constructor.
270 
271  /// Deleted copy assign operator.
273 
274  /// Deleted move assign operator.
276 
277  ~ContractorForallMt() override = default;
278 
279  void Prune(ContractorStatus* cs) const override {
280  ContractorForall<ContextType>* const ctc{GetCtcOrCreate(cs->box())};
281  DREAL_ASSERT(ctc);
282  return ctc->Prune(cs);
283  }
284 
285  std::ostream& display(std::ostream& os) const override {
286  return os << "ContractorForall(" << f_ << ")";
287  }
288 
289  private:
290  ContractorForall<ContextType>* GetCtcOrCreate(const Box& box) const {
291  thread_local const int kThreadId{ThreadPool::get_thread_id()};
292  DREAL_ASSERT(kThreadId == ThreadPool::get_thread_id());
293  DREAL_ASSERT(0 <= kThreadId &&
294  kThreadId <= static_cast<int>(ctc_ready_.size()));
295  if (ctc_ready_[kThreadId]) {
296  return ctcs_[kThreadId].get();
297  }
298  Config inner_config{config()};
299  inner_config.mutable_number_of_jobs() = 1; // FORCE SEQ ICP in INNER LOOP
300  auto ctc_unique_ptr = std::make_unique<ContractorForall<ContextType>>(
301  f_, box, epsilon_, inner_delta_, inner_config);
302  ContractorForall<ContextType>* ctc{ctc_unique_ptr.get()};
303  DREAL_ASSERT(ctc);
304  ctcs_[kThreadId] = std::move(ctc_unique_ptr);
305  ctc_ready_[kThreadId] = 1;
306  return ctc;
307  }
308 
309  const Formula f_;
310  const double epsilon_{};
311  const double inner_delta_{};
312 
313  mutable std::vector<int> ctc_ready_;
314  mutable std::vector<std::unique_ptr<ContractorForall<ContextType>>> ctcs_;
315 };
316 
317 template <typename ContextType>
318 Contractor make_contractor_forall(Formula f, const Box& box, double epsilon,
319  double inner_delta, const Config& config) {
320  if (config.number_of_jobs() > 1) {
321  return Contractor{std::make_shared<ContractorForallMt<ContextType>>(
322  std::move(f), box, epsilon, inner_delta, config)};
323  } else {
324  return Contractor{std::make_shared<ContractorForall<ContextType>>(
325  std::move(f), box, epsilon, inner_delta, config)};
326  }
327 }
328 
329 /// Converts @p contractor to ContractorForall.
330 template <typename ContextType>
331 std::shared_ptr<ContractorForall<ContextType>> to_forall(
332  const Contractor& contractor) {
333  DREAL_ASSERT(is_forall(contractor));
334  return std::static_pointer_cast<ContractorForall<ContextType>>(
335  contractor.ptr_);
336 }
337 
338 } // namespace dreal
std::ostream & display(std::ostream &os) const override
Outputs this contractor to os.
Definition: contractor_forall.h:285
Formula Convert(const Formula &f, bool push_negation_into_relationals=false) const
Converts f into an equivalent formula f&#39; in NNF.
Definition: nnfizer.cc:9
const Config & config() const
Returns config.
Definition: contractor_cell.cc:33
Box & mutable_box()
Returns a mutable reference of the embedded box.
Definition: contractor_status.cc:65
Contractor Generate(const Formula &f, const Box &box, const Config &config) const
Generates f into a contractor using box.
Definition: generic_contractor_generator.cc:15
OptionValue< int > & mutable_number_of_jobs()
Returns a mutable OptionValue for &#39;number_of_jobs&#39;.
Definition: config.cc:51
Abstract base class of contractors.
Definition: contractor_cell.h:29
Contractor make_contractor_forall(Formula f, const Box &box, double epsilon, double inner_delta, const Config &config)
Returns a forall contractor.
Definition: contractor_forall.h:318
ContractorForall & operator=(const ContractorForall &)=delete
Deleted copy assign operator.
Sum type of symbolic::Expression and symbolic::Formula.
Definition: api.cc:9
void AddUsedConstraint(const Formula &f)
Add a formula f into the used constraints.
Definition: contractor_status.cc:75
bool use_polytope_in_forall() const
Returns whether it uses polytope contractors inside forall contractors.
Definition: config.cc:29
bool use_local_optimization() const
Returns whether it uses local optimization algorithm in exist-forall problems.
Definition: config.cc:43
ContractorForallMt(Formula f, const Box &box, double epsilon, double inner_delta, const Config &config)
Constructs ForallMt contractor using f and box.
Definition: contractor_forall.h:250
int number_of_jobs() const
Returns the number of parallel jobs.
Definition: config.cc:50
Contractor for forall constraints.
Definition: contractor.h:24
std::shared_ptr< ContractorForall< ContextType > > to_forall(const Contractor &contractor)
Converts contractor to ContractorForall.
Definition: contractor_forall.h:331
Represents a symbolic variable.
Definition: symbolic_variable.h:16
DynamicBitset & mutable_output()
Returns a mutable reference of the output field.
Definition: contractor_status.cc:73
Box RefineCounterexample(const Formula &query, const Variables &quantified_variables, Box b, double precision)
Add Doc.
const DynamicBitset & input() const
Returns its input.
Definition: contractor_cell.cc:29
volatile std::atomic_bool g_interrupted
Flag to indicate an interrupt (SIGINT) is received.
Definition: interrupt.cc:4
void Prune(ContractorStatus *cs) const
Prunes cs.
Definition: contractor.cc:88
void set_include_forall()
Sets include_forall true.
Definition: contractor_cell.cc:37
bool empty() const
Checks if this box is empty.
Definition: box.cc:100
std::ostream & display(std::ostream &os) const override
Outputs this contractor to os.
Definition: contractor_forall.h:219
Definition: contractor_forall.h:244
double precision() const
Returns the precision option.
Definition: config.cc:20
void set_empty()
Make this box empty.
Definition: box.cc:102
void Add(const Variable &v)
Adds v to the box.
Definition: box.cc:51
Represents a n-dimensional interval vector.
Definition: box.h:17
Definition: config.h:12
int size() const
Returns the size of the box.
Definition: box.cc:104
const Box & box() const
Returns a const reference of the embedded box.
Definition: contractor_status.cc:63
A class implementing NNF (Negation Normal Form) conversion.
Definition: nnfizer.h:12
const Variables & GetFreeVariables() const
Gets free variables (unquantified variables).
Definition: symbolic_formula.cc:77
Formula DeltaStrengthen(const Formula &f, const double delta)
Strengthen the input formula $p f by delta.
Definition: symbolic.cc:504
Converts an arbitrary formula into a contractor.
Definition: generic_contractor_generator.h:11
void Prune(ContractorStatus *cs) const override
Performs pruning on cs.
Definition: contractor_forall.h:279
bool is_forall(const Contractor &contractor)
Returns true if contractor is forall contractor.
Definition: contractor.cc:221
Represents a set of variables.
Definition: symbolic_variables.h:25
Contractor status.
Definition: contractor_status.h:13
Definition: contractor.h:30
Represents a symbolic form of a first-order logic formula.
Definition: symbolic_formula.h:101
DynamicBitset & mutable_input()
Returns its input.
Definition: contractor_cell.cc:31
const std::vector< Variable > & variables() const
Returns the variables in the box.
Definition: box.cc:121
~ContractorForall() override=default
Default destructor.
void Prune(ContractorStatus *cs) const override
Performs pruning on cs.
Definition: contractor_forall.h:172
ContractorForall(Formula f, const Box &box, double epsilon, double inner_delta, const Config &config)
Constructs Forall contractor using f and box.
Definition: contractor_forall.h:60