You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

473 lines
14 KiB

  1. /*
  2. * This file is part of the program ltl2dstar (http://www.ltl2dstar.de/).
  3. * Copyright (C) 2005-2007 Joachim Klein <j.klein@ltl2dstar.de>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License version 2 as
  7. * published by the Free Software Foundation.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17. */
  18. #ifndef DAUNIONALGORITHM_H
  19. #define DAUNIONALGORITHM_H
  20. #include "NBA2DA.hpp"
  21. #include "StutteredNBA2DA.hpp"
  22. #include <memory>
  23. /** @file
  24. * Provides DAUnionAlgorithm for calculating the union of two DA.
  25. */
  26. // TODO: trueloop again
  27. /**
  28. * Generic dummy UnionAcceptanceCalculator functor.
  29. * Any specialization should provide (see RabinAcceptance for example):
  30. * <ul>
  31. * <li>Constructor taking the acceptance for the two DA</li>
  32. * <li>prepareAcceptance(&acceptance): prepare the acceptance condition in the result DA</li>
  33. * <li>calculateAcceptance: calculating the resulting acceptance signature for two states</li>
  34. * </ul>
  35. */
  36. template <typename Acceptance>
  37. struct UnionAcceptanceCalculator {
  38. };
  39. /**
  40. * Specialized UnionAcceptanceCalculator for RabinAcceptance, for calculating the acceptance in the union automaton.
  41. * This approach merges the acceptance signatures of the two states in the union tuple, the union is provided by
  42. * the semantics of the Rabin acceptance condition (There <i>exists</i> an acceptance pair ....)
  43. */
  44. template <>
  45. struct UnionAcceptanceCalculator<RabinAcceptance> {
  46. /** RabinAcceptance signature type. */
  47. typedef RabinAcceptance::signature_type signature_t;
  48. /** shared_ptr of signature type. */
  49. typedef std::shared_ptr<signature_t> signature_ptr;
  50. /**
  51. * Constructor.
  52. * @param acc_1 The RabinAcceptance condition from automaton 1
  53. * @param acc_2 The RabinAcceptance condition from automaton 2
  54. */
  55. UnionAcceptanceCalculator(RabinAcceptance& acc_1,
  56. RabinAcceptance& acc_2) :
  57. _acc_1(acc_1), _acc_2(acc_2) {
  58. _acc_size_1=_acc_1.size();
  59. _acc_size_2=_acc_2.size();
  60. }
  61. /**
  62. * Prepares the acceptance condition in the result union automaton. If the two automata have k1 and k2
  63. * acceptance pairs, this function allocates k1+k2 acceptance pairs in the result automaton.
  64. * @param acceptance_result The acceptance condition in the result automaton.
  65. */
  66. void prepareAcceptance(RabinAcceptance& acceptance_result) {
  67. acceptance_result.newAcceptancePairs(_acc_size_1+_acc_size_2);
  68. }
  69. /**
  70. * Calculate the acceptance signature for the union of two states.
  71. * @param da_state_1 index of the state in the first automaton
  72. * @param da_state_2 index of the state in the second automaton
  73. * @return A shared_ptr Rabin acceptance signature
  74. */
  75. signature_ptr calculateAcceptance(unsigned int da_state_1,
  76. unsigned int da_state_2) {
  77. signature_ptr signature_p=signature_ptr(new signature_t(_acc_size_1+
  78. _acc_size_2));
  79. signature_t& signature=*signature_p;
  80. for (unsigned int i=0;
  81. i<_acc_size_1;
  82. i++) {
  83. if (_acc_1.isStateInAcceptance_L(i, da_state_1)) {
  84. signature.setL(i, true);
  85. }
  86. if (_acc_1.isStateInAcceptance_U(i, da_state_1)) {
  87. signature.setU(i, true);
  88. }
  89. }
  90. for (unsigned int j=0;
  91. j<_acc_size_2;
  92. j++) {
  93. if (_acc_2.isStateInAcceptance_L(j, da_state_2)) {
  94. signature.setL(j + _acc_size_1, true);
  95. }
  96. if (_acc_2.isStateInAcceptance_U(j, da_state_2)) {
  97. signature.setU(j + _acc_size_1, true);
  98. }
  99. }
  100. return signature_p;
  101. }
  102. /** The acceptance condition of the first automaton. */
  103. RabinAcceptance& _acc_1;
  104. /** The acceptance condition of the second automaton. */
  105. RabinAcceptance& _acc_2;
  106. /** The size of the acceptance condition in the original automaton. */
  107. unsigned int _acc_size_1, _acc_size_2;
  108. };
  109. /**
  110. * An algorithm calculating the union of two DA. Requires the existance of a UnionAcceptanceCalculator for the
  111. * AcceptanceCondition.
  112. * @param DA_t the Deterministic Automaton class.
  113. */
  114. template <typename DA_t>
  115. class DAUnionAlgorithm {
  116. public:
  117. /** shared_ptr for DA_t type*/
  118. typedef typename std::shared_ptr<DA_t> DA_ptr;
  119. /** state_type from DA_t */
  120. typedef typename DA_t::state_type da_state_t;
  121. /** acceptance condition type from DA_t */
  122. typedef typename DA_t::acceptance_condition_type da_acceptance_t;
  123. /** acceptance signature type from DA_t */
  124. typedef typename da_acceptance_t::signature_type da_signature_t;
  125. /** A state representing a union state from two DA. */
  126. struct UnionState {
  127. /** Index of the state from the first automaton */
  128. unsigned int da_state_1;
  129. /** Index of the state from the second automaton */
  130. unsigned int da_state_2;
  131. /** A shared_ptr with the acceptance signature of this state */
  132. std::shared_ptr<da_signature_t> signature;
  133. /** A shared_ptr to a string containing a detailed description of this state */
  134. std::shared_ptr<std::string> description;
  135. /** Constructor.
  136. * @param da_state_1_ index of the state in the first automaton
  137. * @param da_state_2_ index of the state in the second automaton
  138. * @param acceptance_calculator UnionAcceptanceCalculator
  139. */
  140. template <typename AcceptanceCalc>
  141. UnionState(unsigned int da_state_1_,
  142. unsigned int da_state_2_,
  143. AcceptanceCalc& acceptance_calculator) :
  144. da_state_1(da_state_1_), da_state_2(da_state_2_) {
  145. signature=acceptance_calculator.calculateAcceptance(da_state_1,
  146. da_state_2);
  147. }
  148. /** Compare this state to another for equality.
  149. * @param other the other UnionState
  150. * @returns true iff the two states are equal
  151. */
  152. bool operator==(const UnionState& other) const{
  153. return (da_state_1==other.da_state_1 &&
  154. da_state_2==other.da_state_2);
  155. // we don't have to check the signature as there is a
  156. // 1-on-1 mapping between <da_state_1, da_state_2> -> signature
  157. }
  158. /** Compare this state to another for less-than-relationship. Uses the natural order on
  159. * the two indizes (da_state_1, da_state_2)
  160. * @param other the other UnionState
  161. * @returns true iff this state is 'smaller' than the other
  162. */
  163. bool operator<(const UnionState& other) const {
  164. if (da_state_1<other.da_state_1)
  165. return true;
  166. if (da_state_1==other.da_state_1) {
  167. return (da_state_2<other.da_state_2);
  168. // we don't have to check the signature as there is a
  169. // 1-on-1 mapping between <da_state_1, da_state_2> -> signature
  170. }
  171. return false;
  172. }
  173. /** Copy acceptance signature for this state
  174. * @param acceptance (<b>out</b>) AcceptanceForState for the state in the result automaton
  175. */
  176. void generateAcceptance(typename da_acceptance_t::AcceptanceForState acceptance) const {
  177. acceptance.setSignature(*signature);
  178. }
  179. /** Copy acceptance signature for this state
  180. * @param acceptance (<b>out</b>) acceptance signature for the state in the result automaton
  181. */
  182. void generateAcceptance(da_signature_t& acceptance) const {
  183. acceptance=*signature;
  184. }
  185. /** Return the acceptance acceptance signature for this state
  186. * @return the acceptance signature for this state
  187. */
  188. const RabinAcceptance::RabinSignature& generateAcceptance() const {
  189. return *signature;
  190. }
  191. /**
  192. * Set the detailed description for this state
  193. * @param description_ the description
  194. */
  195. void setDescription(const std::string& description_) {
  196. description=std::shared_ptr<std::string>(new std::string(description_));
  197. }
  198. /** Generate a simple representation of this state
  199. * @return a string with the representation
  200. */
  201. std::string toString() const {
  202. return "("+boost::lexical_cast<std::string>(da_state_1)+","+boost::lexical_cast<std::string>(da_state_1)+")";
  203. }
  204. /** Return the detailed description
  205. * @return the detailed description
  206. */
  207. const std::string& toHTML() const {
  208. if (!description) {
  209. THROW_EXCEPTION(Exception, "No description");
  210. } else {
  211. return *description;
  212. }
  213. }
  214. /** Calculates the hash for the union state.
  215. * @param hash the HashFunction functor used for the calculation
  216. */
  217. template <class HashFunction>
  218. void hashCode(HashFunction& hash) {
  219. hash.hash(da_state_1);
  220. hash.hash(da_state_2);
  221. // we don't have to consider the signature as there is a
  222. // 1-on-1 mapping between <da_state_1, da_state_2> -> signature
  223. }
  224. };
  225. /** shared_ptr of UnionState */
  226. typedef std::shared_ptr<UnionState> UnionState_ptr;
  227. /** A simple wrapper for UnionState_Result to accomodate the plugging in with fuzzy matching */
  228. struct UnionState_Result {
  229. UnionState_ptr state;
  230. UnionState_Result(UnionState_ptr state_) : state(state_) {}
  231. UnionState_ptr getState() {
  232. return state;
  233. }
  234. };
  235. /** shared_ptr for UnionState_Result */
  236. typedef std::shared_ptr<UnionState_Result> UnionState_Result_ptr;
  237. typedef UnionState_Result_ptr result_t;
  238. typedef UnionState_ptr state_t;
  239. /** Constructor.
  240. * @param da_1 The first DA
  241. * @param da_2 the second DA
  242. * @param trueloop_check Check for trueloops?
  243. * @param detailed_states Generate detailed descriptions of the states? */
  244. DAUnionAlgorithm(DA_t& da_1, DA_t& da_2,
  245. bool trueloop_check=true,
  246. bool detailed_states=false) :
  247. _da_1(da_1), _da_2(da_2),
  248. _acceptance_calculator(da_1.acceptance(), da_2.acceptance()),
  249. _trueloop_check(trueloop_check),
  250. _detailed_states(detailed_states) {
  251. if (! (_da_1.getAPSet() == _da_2.getAPSet()) ) {
  252. THROW_EXCEPTION(IllegalArgumentException, "Can't create union of DAs: APSets don't match");
  253. }
  254. APSet_cp combined_ap=da_1.getAPSet_cp();
  255. if (!_da_1.isCompact() || !_da_2.isCompact()) {
  256. THROW_EXCEPTION(IllegalArgumentException, "Can't create union of DAs: Not compact");
  257. }
  258. _result_da=DA_ptr((DA_t*)da_1.createInstance(combined_ap));
  259. }
  260. /** Destructor */
  261. ~DAUnionAlgorithm() {}
  262. /** Get the resulting DA
  263. * @return a shared_ptr to the resulting union DA.
  264. */
  265. DA_ptr getResultDA() {
  266. return _result_da;
  267. }
  268. /** Calculate the successor state.
  269. * @param from_state The from state
  270. * @param elem The edge label
  271. * @return result_t the shared_ptr of the successor state
  272. */
  273. result_t delta(state_t from_state, APElement elem) {
  274. da_state_t* state1_to=_da_1[from_state->da_state_1]->edges().get(elem);
  275. da_state_t* state2_to=_da_2[from_state->da_state_2]->edges().get(elem);
  276. UnionState_ptr to=createState(state1_to->getName(),
  277. state2_to->getName());
  278. return UnionState_Result_ptr(new UnionState_Result(to));
  279. }
  280. /** Get the start state.
  281. * @return a shared_ptr to the start state
  282. */
  283. state_t getStartState() {
  284. if (_da_1.getStartState()==0 ||
  285. _da_2.getStartState()==0) {
  286. THROW_EXCEPTION(IllegalArgumentException, "DA has no start state!");
  287. }
  288. return createState(_da_1.getStartState()->getName(),
  289. _da_2.getStartState()->getName());
  290. }
  291. /** Prepare the acceptance condition
  292. * @param acceptance the acceptance condition in the result DA
  293. */
  294. void prepareAcceptance(da_acceptance_t& acceptance) {
  295. _acceptance_calculator.prepareAcceptance(acceptance);
  296. }
  297. /** Check if the automaton is a-priori empty */
  298. bool checkEmpty() {
  299. return false;
  300. }
  301. /** Calculate the union of two DA. If the DAs are not compact, they are made compact.
  302. * @param da_1 The first DA
  303. * @param da_2 the second DA
  304. * @param trueloop_check Check for trueloops?
  305. * @param detailed_states Generate detailed descriptions of the states?
  306. * @return shared_ptr to result DA
  307. */
  308. static
  309. DA_ptr calculateUnion(DA_t& da_1,
  310. DA_t& da_2,
  311. bool trueloop_check=true,
  312. bool detailed_states=false) {
  313. if (!da_1.isCompact()) {
  314. da_1.makeCompact();
  315. }
  316. if (!da_2.isCompact()) {
  317. da_2.makeCompact();
  318. }
  319. typedef DAUnionAlgorithm<DA_t> algo_t;
  320. algo_t dua(da_1, da_2, trueloop_check, detailed_states);
  321. NBA2DA<algo_t, DA_t> generator(detailed_states);
  322. generator.convert(dua, *dua.getResultDA(), 0);
  323. return dua.getResultDA();
  324. }
  325. /** Calculate the union of two DA, using stuttering if possible. If the DAs are not compact, they are made compact.
  326. * @param da_1 The first DA
  327. * @param da_2 the second DA
  328. * @param stutter_information information about the symbols where stuttering is allowed
  329. * @param trueloop_check Check for trueloops?
  330. * @param detailed_states Generate detailed descriptions of the states? */
  331. static
  332. DA_ptr calculateUnionStuttered(DA_t& da_1,
  333. DA_t& da_2,
  334. StutterSensitivenessInformation::ptr stutter_information,
  335. bool trueloop_check=true,
  336. bool detailed_states=false) {
  337. if (!da_1.isCompact()) {
  338. da_1.makeCompact();
  339. }
  340. if (!da_2.isCompact()) {
  341. da_2.makeCompact();
  342. }
  343. typedef DAUnionAlgorithm<DA_t> algo_t;
  344. algo_t dua(da_1, da_2, trueloop_check, detailed_states);
  345. StutteredNBA2DA<algo_t, DA_t> generator(detailed_states, stutter_information);
  346. generator.convert(dua, *dua.getResultDA(), 0);
  347. return dua.getResultDA();
  348. }
  349. private:
  350. /** The first DA */
  351. DA_t& _da_1;
  352. /** The second DA */
  353. DA_t& _da_2;
  354. /** The result DA */
  355. DA_ptr _result_da;
  356. /** The acceptance calculator */
  357. UnionAcceptanceCalculator<da_acceptance_t> _acceptance_calculator;
  358. /** Perform trueloop check? */
  359. bool _trueloop_check;
  360. /** Generate detailed descriptions? */
  361. bool _detailed_states;
  362. /** Create a UnionState
  363. * @param da_state_1
  364. * @param da_state_2
  365. * @return the corresponding UnionState
  366. */
  367. UnionState_ptr createState(unsigned int da_state_1,
  368. unsigned int da_state_2) {
  369. UnionState_ptr state(new UnionState(da_state_1, da_state_2, _acceptance_calculator));
  370. // Generate detailed description
  371. if (_detailed_states) {
  372. std::ostringstream s;
  373. s << "<TABLE BORDER=\"1\" CELLBORDER=\"0\"><TR><TD>";
  374. if (_da_1[da_state_1]->hasDescription()) {
  375. s << _da_1[da_state_1]->getDescription();
  376. } else {
  377. s << da_state_1;
  378. }
  379. s << "</TD><TD>U</TD><TD>";
  380. if (_da_2[da_state_2]->hasDescription()) {
  381. s << _da_2[da_state_2]->getDescription();
  382. } else {
  383. s << da_state_2;
  384. }
  385. s << "</TD></TR></TABLE>";
  386. state->setDescription(s.str());
  387. }
  388. return state;
  389. }
  390. };
  391. #endif