LearnTA  0.0.1
zone.hh
1 #pragma once
2 
3 #include <cmath>
4 #include <limits>
5 #include <algorithm>
6 #include <unordered_map>
7 #include <boost/unordered_map.hpp>
8 #include <boost/range/adaptor/reversed.hpp>
9 
10 #include "common_types.hh"
11 #include "bounds.hh"
12 #include "constraint.hh"
13 
14 #include <Eigen/Core>
15 #include <utility>
16 
17 namespace learnta {
18 
24  struct Zone {
26  Eigen::Matrix <Bounds, Eigen::Dynamic, Eigen::Dynamic> value;
28  Bounds M;
30  std::vector<double> maxConstraints;
31 
32  Zone() = default;
33 
35  explicit Zone(const Eigen::Matrix <Bounds, Eigen::Dynamic, Eigen::Dynamic> &value) :
36  Zone(Eigen::Matrix<Bounds, Eigen::Dynamic, Eigen::Dynamic>(value)) {}
37 
39  explicit Zone(Eigen::Matrix <Bounds, Eigen::Dynamic, Eigen::Dynamic> &&value) : value(std::move(value)) {}
40 
42  Zone(Eigen::Matrix <Bounds, Eigen::Dynamic, Eigen::Dynamic> value, Bounds m) :
43  value(std::move(value)), M(std::move(m)) {
44  maxConstraints.resize(this->value.cols() - 1);
45  std::fill(maxConstraints.begin(), maxConstraints.end(), M.first);
46  }
47 
51  explicit Zone(const std::vector<double> &valuation, Bounds M) : M(std::move(M)) {
52  value.resize(valuation.size() + 1, valuation.size() + 1);
53  value.fill(Bounds(std::numeric_limits<double>::max(), false));
54  for (std::size_t i = 0; i < valuation.size(); ++i) {
55  this->tighten(i, -1, Bounds{valuation.at(i), true});
56  this->tighten(-1, i, Bounds{-valuation.at(i), true});
57  }
58  maxConstraints.resize(this->value.cols() - 1);
59  std::fill(maxConstraints.begin(), maxConstraints.end(), this->M.first);
60  }
61 
66  [[nodiscard]] inline std::size_t getNumOfVar() const noexcept {
67  return value.cols() - 1;
68  }
69 
71  static Zone zero(int size) {
72  static Zone zeroZone;
73  if (zeroZone.value.cols() == size) {
74  return zeroZone;
75  }
76  zeroZone.value.resize(size, size);
77  zeroZone.value.fill(Bounds(0, true));
78  return zeroZone;
79  }
80 
84  static Zone top(std::size_t size) {
85  static Zone topZone;
86  if (static_cast<std::size_t>(topZone.value.cols()) == size) {
87  return topZone;
88  }
89  topZone.value.resize(size, size);
90  topZone.value.fill(Bounds(std::numeric_limits<double>::max(), false));
91  return topZone;
92  }
93 
95  void tighten(ClockVariables x, ClockVariables y, Bounds c) {
96  x++;
97  y++;
98  value(x, y) = std::min(value(x, y), c);
99  close1(x);
100  close1(y);
101  }
102 
104  void tighten(const Constraint &constraint) {
105  switch (constraint.odr) {
106  case learnta::Constraint::Order::ge:
107  // lower bound
108  this->tighten(-1, constraint.x, Bounds{-constraint.c, true});
109  break;
110  case learnta::Constraint::Order::gt:
111  // lower bound
112  this->tighten(-1, constraint.x, Bounds{-constraint.c, false});
113  break;
114  case learnta::Constraint::Order::le:
115  // upper bound
116  this->tighten(constraint.x, -1, Bounds{constraint.c, true});
117  break;
118  case learnta::Constraint::Order::lt:
119  // upper bound
120  this->tighten(constraint.x, -1, Bounds{constraint.c, false});
121  break;
122  }
123  }
124 
126  void tighten(const std::vector<Constraint> &constraints) {
127  for (const auto &constraint: constraints) {
128  this->tighten(constraint);
129  }
130  }
131 
132  void applyResets(const std::vector<std::pair<ClockVariables, std::variant<double, ClockVariables>>> &resets) {
133  // We apply renaming first
134  for (const auto &[resetVariable, updatedVariable]: resets) {
135  if (updatedVariable.index() == 1 && resetVariable != std::get<ClockVariables>(updatedVariable)) {
136  this->unconstrain(resetVariable);
137  this->value(resetVariable + 1, std::get<ClockVariables>(updatedVariable) + 1) = Bounds{0.0, true};
138  this->value(std::get<ClockVariables>(updatedVariable) + 1, resetVariable + 1) = Bounds{0.0, true};
139  canonize();
140  }
141  }
142  // Then, assign a value
143  for (const auto &[resetVariable, updatedVariable]: resets) {
144  if (updatedVariable.index() == 0) {
145  this->unconstrain(resetVariable);
146  this->value(0, resetVariable + 1) = Bounds(-std::get<double>(updatedVariable), true);
147  this->value(resetVariable + 1, 0) = Bounds(std::get<double>(updatedVariable), true);
148  canonize();
149  }
150  }
151  }
152 
158  void revertResets(const std::vector<std::pair<ClockVariables, std::variant<double, ClockVariables>>> &resets) {
159  std::vector<ClockVariables> resetVariables;
160  resetVariables.reserve(resets.size());
161  std::unordered_map<ClockVariables, ClockVariables> reverseAssignments;
162  for (const auto &[resetVariable, updatedVariable]: resets) {
163  resetVariables.push_back(resetVariable);
164  if (updatedVariable.index() == 1 && resetVariable != std::get<ClockVariables>(updatedVariable)) {
165  reverseAssignments[std::get<ClockVariables>(updatedVariable)] = resetVariable;
166  }
167  }
168  for (const auto &resetVariable: boost::adaptors::reverse(resetVariables)) {
169  this->unconstrain(resetVariable);
170  auto it = reverseAssignments.find(resetVariable);
171  if (it != reverseAssignments.end()) {
172  this->value(it->first + 1, it->second + 1) = Bounds{0.0, true};
173  this->value(it->second + 1, it->first + 1) = Bounds{0.0, true};
174  }
175  canonize();
176  }
177  }
178 
182  Zone operator&&(const Zone &another) const {
183  assert(this->value.cols() == another.value.cols());
184  assert(this->value.rows() == another.value.rows());
185  auto result = Zone{this->value.array().min(another.value.array())};
186  result.M = this->M;
187  result.canonize();
188 
189  return result;
190  }
191 
195  Zone operator&=(const Zone &another) {
196  assert(this->value.cols() == another.value.cols());
197  assert(this->value.rows() == another.value.rows());
198  this->value = this->value.cwiseMin(another.value);
199  canonize();
200 
201  return *this;
202  }
203 
207  Zone operator^(const Zone &another) const {
208  Zone result;
209  const size_t N = this->getNumOfVar();
210  const size_t O = another.getNumOfVar();
211  result = Zone::top(N + O);
212  // Copy \f$\mathbb{T}'\f$
213  result.value.block(N, N, O, O) = another.value.block(1, 1, O, O);
214  result.value.block(0, N + 1, 1, O - 1) = another.value.block(0, 1, 1, O - 1);
215  result.value.block(N + 1, 0, O - 1, 1) = another.value.block(1, 0, O - 1, 1);
216  // Copy \f$\mathbb{T}\f$
217  result.value.block(0, 0, N + 1, N + 1) = this->value;
218  result.canonize();
219 
220  return result;
221  }
222 
226  [[nodiscard]] std::vector<double> sample() {
227  this->canonize();
228  assert(this->isSatisfiableNoCanonize());
229  std::vector<double> valuation;
230  std::size_t N = this->getNumOfVar();
231  valuation.resize(N);
232  for (std::size_t i = 0; i < N; i++) {
233  Bounds lowerBound = this->value(0, i + 1);
234  Bounds upperBound = this->value(i + 1, 0);
235  if (isPoint(upperBound, lowerBound)) {
236  valuation[i] = upperBound.first;
237  } else {
238  double lower = std::max(0.0, -lowerBound.first);
239  double upper = upperBound.first;
240  for (std::size_t j = 0; j < i; j++) {
241  Bounds tmpLowerBound = this->value(j + 1, i + 1);
242  Bounds tmpUpperBound = this->value(i + 1, j + 1);
243  lower = std::max(lower, -tmpLowerBound.first + valuation[j]);
244  upper = std::min(upper, tmpUpperBound.first + valuation[j]);
245  }
246  assert(lower <= upper);
247  if (lowerBound.second) {
248  valuation[i] = lower;
249  } else if (upper - lower > 0.5) {
250  valuation[i] = lower + 0.5;
251  } else {
252  valuation[i] = (lower + upper) * 0.5;
253  }
254  }
255  }
256 
257  return valuation;
258  }
259 
261  void close1(ClockVariables x) {
262  for (int i = 0; i < value.rows(); i++) {
263  value.row(i) = value.row(i).array().min(value.row(x).array() + value(i, x));
264  // for (int j = 0; j < value.cols(); j++) {
265  // value(i, j) = std::min(value(i, j), value(i, x) + value(x, j));
266  // }
267  }
268  }
269 
271  void reset(ClockVariables x) {
272  // 0 is the special variable here
273  x++;
274  value(0, x) = Bounds(-0.0, true);
275  value(x, 0) = Bounds(0.0, true);
276  value.col(x).tail(value.rows() - 1) = value.col(0).tail(value.rows() - 1);
277  value.row(x).tail(value.cols() - 1) = value.row(0).tail(value.cols() - 1);
278  }
279 
283  void unconstrain(ClockVariables x) {
284  // 0 is the special variable here
285  x++;
286  value.col(x).fill(Bounds(std::numeric_limits<double>::max(), false));
287  value.row(x).fill(Bounds(std::numeric_limits<double>::max(), false));
288  }
289 
295  void elapse() {
296  static constexpr Bounds infinity = Bounds(std::numeric_limits<double>::max(), false);
297  value.col(0).fill(Bounds(infinity));
298  }
299 
307  void reverseElapse() {
308  value.row(0).fill(Bounds(0, true));
309  }
310 
314  void canonize() {
315  for (int k = 0; k < value.cols(); k++) {
316  close1(k);
317  }
318  }
319 
323  bool isSatisfiable() {
324  canonize();
325  return this->isSatisfiableNoCanonize();
326  }
327 
333  [[nodiscard]] bool isSatisfiableNoCanonize() const {
334  return (value + value.transpose()).minCoeff() >= Bounds(0.0, true);
335  }
336 
340  explicit operator bool() {
341  return isSatisfiable();
342  }
343 
347  void abstractize() {
348  static constexpr Bounds infinity = Bounds(std::numeric_limits<double>::max(), false);
349  for (auto it = value.data(); it < value.data() + value.size(); it++) {
350  if (*it > Bounds{M.first, true}) {
351  *it = Bounds(infinity);
352  } else if (*it < Bounds{-M.first, false}) {
353  *it = Bounds(-M.first, false);
354  }
355  }
356  }
357 
364  void extrapolate() {
365  static constexpr Bounds infinity = Bounds(std::numeric_limits<double>::max(), false);
366  for (std::size_t i = 0; i < this->maxConstraints.size(); ++i) {
367  if (value(i + 1, 0).first > this->maxConstraints.at(i)) {
368  value(i + 1, 0) = infinity;
369  }
370  if (-value(0, i + 1).first > this->maxConstraints.at(i)) {
371  value(0, i + 1) = Bounds{-this->maxConstraints.at(i), false};
372  }
373  for (std::size_t j = 0; j < this->maxConstraints.size(); ++j) {
374  if (value(i + 1, j + 1).first > this->maxConstraints.at(i)) {
375  value(i + 1, j + 1) = infinity;
376  } else if (-value(0, i + 1).first > this->maxConstraints.at(i)) {
377  value(i + 1, j + 1) = infinity;
378  } else if (-value(0, j + 1).first > this->maxConstraints.at(j)) {
379  value(i + 1, j + 1) = infinity;
380  }
381  }
382  }
383  }
384 
388  void makeUnsat() {
389  value(0, 0) = Bounds(-std::numeric_limits<double>::infinity(), false);
390  }
391 
397  [[nodiscard]] bool includes(const Zone &zone) const {
398  return this->value.cwiseMax(zone.value) == this->value;
399  };
400 
406  bool operator==(const Zone &z) const {
407  return value.cols() == z.value.cols() && value == z.value;
408  }
409 
415  [[nodiscard]] bool equalIgnoreZero(Zone z) const {
416  z.value(0, 0) = value(0, 0);
417  return value == z.value;
418  }
419 
425  [[nodiscard]] bool strictEqual(Zone z) const {
426  z.value.diagonal() = value.diagonal();
427  return value == z.value;
428  }
429  };
430 
432  static inline std::ostream &print(std::ostream &os, const learnta::Zone &zone) {
433  for (int i = 0; i < zone.value.cols(); ++i) {
434  for (int j = 0; j < zone.value.rows(); ++j) {
435  print(os, zone.value(i, j));
436  os << " ";
437  }
438  os << "\n";
439  }
440 
441  return os;
442  }
443 
444  static inline std::ostream &operator<<(std::ostream &os, const learnta::Zone &zone) {
445  return learnta::print(os, zone);
446  }
447 
448  inline std::size_t hash_value(learnta::Zone const &zone) {
449  std::size_t seed = zone.value.array().size();
450  const auto asVector = zone.value.array();
451 
452  union DI {
453  double asD;
454  uint64_t asI;
455  };
456 
457  for (auto it = asVector.data(); it != asVector.data() + asVector.size(); it++) {
458  DI value{};
459  value.asD = it->first;
460  seed ^= it->second + value.asI + 0x9e3779b9 + (seed << 6) + (seed >> 2);
461  }
462  return seed;
463  }
464 }
Definition: experiment_runner.hh:23
T first(const std::pair< T, U > &pair)
Return the first element of a pair.
Definition: common_types.hh:69
static std::ostream & print(std::ostream &os, const learnta::Zone &zone)
Print the zone.
Definition: zone.hh:432
static bool isPoint(const Bounds &upperBound, const Bounds &lowerBound)
Check if the upper and lower bounds define a point.
Definition: bounds.hh:17
Definition: external_transition_maker.hh:149
A constraint in a guard of transitions.
Definition: constraint.hh:32
Implementation of a zone with DBM.
Definition: zone.hh:24
std::vector< double > maxConstraints
the threshold of each clock variable
Definition: zone.hh:30
Zone(const Eigen::Matrix< Bounds, Eigen::Dynamic, Eigen::Dynamic > &value)
Construct a zone from a matrix representing the zone.
Definition: zone.hh:35
void makeUnsat()
make the zone unsatisfiable
Definition: zone.hh:388
void unconstrain(ClockVariables x)
Unconstrain the constraint on this clock.
Definition: zone.hh:283
std::vector< double > sample()
Return a clock valuation in this zone.
Definition: zone.hh:226
std::size_t getNumOfVar() const noexcept
Definition: zone.hh:66
static Zone zero(int size)
Make the zone of size size such that all the values are zero.
Definition: zone.hh:71
Zone(const std::vector< double > &valuation, Bounds M)
Construct a zone containing only the given valuation.
Definition: zone.hh:51
Zone(Eigen::Matrix< Bounds, Eigen::Dynamic, Eigen::Dynamic > value, Bounds m)
Construct a zone from a matrix representing the zone and the bound.
Definition: zone.hh:42
void elapse()
Assign the strongest post-condition of the delay.
Definition: zone.hh:295
Zone operator&&(const Zone &another) const
Returns the intersection of two zones.
Definition: zone.hh:182
bool strictEqual(Zone z) const
Check the equivalence of two zones.
Definition: zone.hh:425
bool includes(const Zone &zone) const
Return if this zone includes the given zone.
Definition: zone.hh:397
void canonize()
make the zone canonical
Definition: zone.hh:314
void close1(ClockVariables x)
Close using only x.
Definition: zone.hh:261
Bounds M
The threshold for the normalization.
Definition: zone.hh:28
void extrapolate()
Extrapolate the zone using the diagonal extrapolation based on maximum constants.
Definition: zone.hh:364
bool isSatisfiableNoCanonize() const
check if the zone is satisfiable
Definition: zone.hh:333
bool operator==(const Zone &z) const
Check the equivalence of two zones.
Definition: zone.hh:406
Eigen::Matrix< Bounds, Eigen::Dynamic, Eigen::Dynamic > value
The matrix representing the DBM.
Definition: zone.hh:26
void reset(ClockVariables x)
Assign a constant value to the clock variable x.
Definition: zone.hh:271
static Zone top(std::size_t size)
Make the zone of size size with no constraints.
Definition: zone.hh:84
Zone operator&=(const Zone &another)
Assign the intersection of two zones.
Definition: zone.hh:195
void revertResets(const std::vector< std::pair< ClockVariables, std::variant< double, ClockVariables >>> &resets)
Make it the weakest precondition of the reset.
Definition: zone.hh:158
void tighten(const Constraint &constraint)
Add a guard of a timed automaton.
Definition: zone.hh:104
void abstractize()
truncate the constraints compared with a constant greater than or equal to M
Definition: zone.hh:347
void tighten(const std::vector< Constraint > &constraints)
Add a set of guards of a timed automaton.
Definition: zone.hh:126
void reverseElapse()
Assign the weakest pre-condition of the delay.
Definition: zone.hh:307
Zone(Eigen::Matrix< Bounds, Eigen::Dynamic, Eigen::Dynamic > &&value)
Construct a zone from a matrix representing the zone.
Definition: zone.hh:39
bool equalIgnoreZero(Zone z) const
Check the equivalence of two zones.
Definition: zone.hh:415
bool isSatisfiable()
check if the zone is satisfiable
Definition: zone.hh:323
Zone operator^(const Zone &another) const
Returns the juxtaposition of two zones.
Definition: zone.hh:207
void tighten(ClockVariables x, ClockVariables y, Bounds c)
add the constraint
Definition: zone.hh:95