ltl2taa.cc 10.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Copyright (C) 2009 Laboratoire d'Informatique de Paris
// 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
// Université Pierre et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Spot; see the file COPYING.  If not, write to the Free
// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.

#include <utility>
#include "ltlast/visitor.hh"
#include "ltlast/allnodes.hh"
#include "ltlvisit/lunabbrev.hh"
#include "ltlvisit/tunabbrev.hh"
#include "ltlvisit/nenoform.hh"
#include "ltlvisit/tostring.hh"
Damien Lefortier's avatar
Damien Lefortier committed
29
#include "ltlvisit/contain.hh"
30
31
32
33
34
35
36
37
38
39
40
41
#include "ltl2taa.hh"

namespace spot
{
  namespace
  {
    using namespace ltl;

    /// \brief Recursively translate a formula into a TAA.
    class ltl2taa_visitor : public const_visitor
    {
    public:
42
      ltl2taa_visitor(taa_tgba* res, language_containment_checker* lcc,
Damien Lefortier's avatar
Damien Lefortier committed
43
44
45
		      bool refined = false, bool negated = false)
	: res_(res), refined_(refined), negated_(negated),
	  lcc_(lcc), init_(), succ_(), to_free_()
46
47
48
49
50
51
52
53
      {
      }

      virtual
      ~ltl2taa_visitor()
      {
      }

54
      taa_tgba*
55
56
57
      result()
      {
	for (unsigned i = 0; i < to_free_.size(); ++i)
58
	  to_free_[i]->destroy();
59
60
61
62
63
64
65
66
67
68
	res_->set_init_state(init_);
	return res_;
      }

      void
      visit(const atomic_prop* node)
      {
	const formula* f = node; // Handle negation
	if (negated_)
	{
69
	  f = unop::instance(unop::Not, node->clone());
70
71
72
73
74
	  to_free_.push_back(f);
	}
	init_ = to_string(f);
	std::vector<std::string> dst;

75
	dst.push_back(std::string("sink"));
76
	taa_tgba::transition* t = res_->create_transition(init_, dst);
77
	res_->add_condition(t, f->clone());
Damien Lefortier's avatar
Damien Lefortier committed
78
79
	succ_state ss = { dst, f, constant::true_instance() };
	succ_.push_back(ss);
80
81
82
83
84
85
86
87
88
89
      }

      void
      visit(const constant* node)
      {
	init_ = to_string(node);
	std::vector<std::string> dst;
	switch (node->val())
	{
	  case constant::True:
Damien Lefortier's avatar
Damien Lefortier committed
90
	  {
91
	    dst.push_back(std::string("sink"));
92
	    res_->create_transition(init_, dst);
Damien Lefortier's avatar
Damien Lefortier committed
93
94
	    succ_state ss = { dst, node, constant::true_instance() };
	    succ_.push_back(ss);
95
	    return;
Damien Lefortier's avatar
Damien Lefortier committed
96
	  }
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
	  case constant::False:
	    return;
	}
	/* Unreachable code.  */
	assert(0);
      }

      void
      visit(const unop* node)
      {
	negated_ = node->op() == unop::Not;
	ltl2taa_visitor v = recurse(node->child());

	init_ = to_string(node);
	std::vector<std::string> dst;
	switch (node->op())
	{
	  case unop::X:
Damien Lefortier's avatar
Damien Lefortier committed
115
	  {
Damien Lefortier's avatar
Damien Lefortier committed
116
	    if (v.succ_.empty()) // Handle X(0)
117
118
119
	      return;
	    dst.push_back(v.init_);
	    res_->create_transition(init_, dst);
Damien Lefortier's avatar
Damien Lefortier committed
120
121
122
	    succ_state ss =
	      { dst, constant::true_instance(), constant::true_instance() };
	    succ_.push_back(ss);
123
	    return;
Damien Lefortier's avatar
Damien Lefortier committed
124
	  }
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
	  case unop::F:
	  case unop::G:
	    assert(0); // TBD
	    return;
	  case unop::Not:
	    // Done in recurse
	    succ_ = v.succ_;
	    return;
	  case unop::Finish:
	    assert(!"unsupported operator");
	}
	/* Unreachable code.  */
	assert(0);
      }

      void
      visit(const binop* node)
      {
	ltl2taa_visitor v1 = recurse(node->first());
	ltl2taa_visitor v2 = recurse(node->second());

	init_ = to_string(node);
	std::vector<succ_state>::iterator i1;
	std::vector<succ_state>::iterator i2;
149
	taa_tgba::transition* t = 0;
Damien Lefortier's avatar
Damien Lefortier committed
150
	bool contained = false;
151
152
153
	switch (node->op())
	{
	  case binop::U: // Strong
Damien Lefortier's avatar
Damien Lefortier committed
154
155
	    if (refined_)
	      contained = lcc_->contained(node->second(), node->first());
156
157
	    for (i1 = v1.succ_.begin(); i1 != v1.succ_.end(); ++i1)
	    {
Damien Lefortier's avatar
Damien Lefortier committed
158
159
160
161
162
163
164
165
	      // Refined rule
	      if (refined_ && contained)
		i1->Q.erase
		  (remove(i1->Q.begin(), i1->Q.end(), v1.init_), i1->Q.end());

	      i1->Q.push_back(init_); // Add the initial state
	      i1->acc = node;
	      t = res_->create_transition(init_, i1->Q);
166
167
	      res_->add_condition(t, i1->condition->clone());
	      res_->add_acceptance_condition(t, node->clone());
168
169
170
171
	      succ_.push_back(*i1);
	    }
	    for (i2 = v2.succ_.begin(); i2 != v2.succ_.end(); ++i2)
	    {
Damien Lefortier's avatar
Damien Lefortier committed
172
	      t = res_->create_transition(init_, i2->Q);
173
	      res_->add_condition(t, i2->condition->clone());
174
175
176
177
	      succ_.push_back(*i2);
	    }
	    return;
	  case binop::R: // Weak
Damien Lefortier's avatar
Damien Lefortier committed
178
179
	    if (refined_)
	      contained = lcc_->contained(node->first(), node->second());
180
181
182
183
184
	    for (i2 = v2.succ_.begin(); i2 != v2.succ_.end(); ++i2)
	    {
	      for (i1 = v1.succ_.begin(); i1 != v1.succ_.end(); ++i1)
	      {
		std::vector<std::string> u; // Union
Damien Lefortier's avatar
Damien Lefortier committed
185
		std::copy(i1->Q.begin(), i1->Q.end(), ii(u, u.begin()));
186
		formula* f = i1->condition->clone(); // Refined rule
Damien Lefortier's avatar
Damien Lefortier committed
187
188
189
		if (!refined_ || !contained)
		{
		  std::copy(i2->Q.begin(), i2->Q.end(), ii(u, u.begin()));
190
		  f = multop::instance(multop::And, f, i2->condition->clone());
Damien Lefortier's avatar
Damien Lefortier committed
191
		}
192
193
194
		to_free_.push_back(f);

		t = res_->create_transition(init_, u);
195
		res_->add_condition(t, f->clone());
Damien Lefortier's avatar
Damien Lefortier committed
196
197
		succ_state ss = { u, f, constant::true_instance() };
		succ_.push_back(ss);
198
199
	      }

Damien Lefortier's avatar
Damien Lefortier committed
200
201
202
203
204
205
	      if (refined_) // Refined rule
		i2->Q.erase
		  (remove(i2->Q.begin(), i2->Q.end(), v2.init_), i2->Q.end());

	      i2->Q.push_back(init_); // Add the initial state
	      t = res_->create_transition(init_, i2->Q);
206
	      res_->add_condition(t, i2->condition->clone());
Damien Lefortier's avatar
Damien Lefortier committed
207
	      if (refined_ && i2->acc != constant::true_instance())
208
		res_->add_acceptance_condition(t, i2->acc->clone());
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
	      succ_.push_back(*i2);
	    }
	    return;
	  case binop::Xor:
	  case binop::Implies:
	  case binop::Equiv:
	    assert(0); // TBD
	}
	/* Unreachable code.  */
	assert(0);
      }

      void
      visit(const multop* node)
      {
	bool ok = true;
	std::vector<ltl2taa_visitor> vs;
	for (unsigned n = 0; n < node->size(); ++n)
	{
	  vs.push_back(recurse(node->nth(n)));
Damien Lefortier's avatar
Damien Lefortier committed
229
	  if (vs[n].succ_.empty()) // Handle 0
230
231
232
233
234
	    ok = false;
	}

	init_ = to_string(node);
	std::vector<succ_state>::iterator i;
235
	taa_tgba::transition* t = 0;
236
237
238
239
240
241
242
	switch (node->op())
	{
	  case multop::And:
	  {
	    std::vector<succ_state> p = all_n_tuples(vs);
	    for (unsigned n = 0; n < p.size() && ok; ++n)
	    {
Damien Lefortier's avatar
Damien Lefortier committed
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
	      if (refined_)
	      {
		std::vector<std::string> v; // All sub initial states.
		sort(p[n].Q.begin(), p[n].Q.end());
		for (unsigned m = 0; m < node->size(); ++m)
		{
		  if (!binary_search(p[n].Q.begin(), p[n].Q.end(), vs[m].init_))
		    break;
		  v.push_back(vs[m].init_);
		}

		if (v.size() == node->size())
		{
		  std::vector<std::string> Q;
		  sort(v.begin(), v.end());
		  for (unsigned m = 0; m < p[n].Q.size(); ++m)
		    if (!binary_search(v.begin(), v.end(), p[n].Q[m]))
		      Q.push_back(p[n].Q[m]);
		  Q.push_back(init_);
		  t = res_->create_transition(init_, Q);
263
		  res_->add_condition(t, p[n].condition->clone());
Damien Lefortier's avatar
Damien Lefortier committed
264
		  if (p[n].acc != constant::true_instance())
265
		    res_->add_acceptance_condition(t, p[n].acc->clone());
Damien Lefortier's avatar
Damien Lefortier committed
266
267
268
269
270
		  succ_.push_back(p[n]);
		  return;
		}
	      }
	      t = res_->create_transition(init_, p[n].Q);
271
	      res_->add_condition(t, p[n].condition->clone());
272
273
274
275
276
277
278
279
	      succ_.push_back(p[n]);
	    }
	    return;
	  }
	  case multop::Or:
	    for (unsigned n = 0; n < node->size(); ++n)
	      for (i = vs[n].succ_.begin(); i != vs[n].succ_.end(); ++i)
	      {
Damien Lefortier's avatar
Damien Lefortier committed
280
		t = res_->create_transition(init_, i->Q);
281
		res_->add_condition(t, i->condition->clone());
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
		succ_.push_back(*i);
	      }
	    return;
	}
	/* Unreachable code.  */
	assert(0);
      }

      void
      visit(const automatop* node)
      {
	(void) node;
	assert(!"unsupported operator");
      }

      ltl2taa_visitor
      recurse(const formula* f)
      {
Damien Lefortier's avatar
Damien Lefortier committed
300
	ltl2taa_visitor v(res_, lcc_, refined_, negated_);
301
302
303
304
305
306
307
	f->accept(v);
	for (unsigned i = 0; i < v.to_free_.size(); ++i)
	  to_free_.push_back(v.to_free_[i]);
	return v;
      }

    private:
308
      taa_tgba* res_;
Damien Lefortier's avatar
Damien Lefortier committed
309
      bool refined_;
310
      bool negated_;
Damien Lefortier's avatar
Damien Lefortier committed
311
      language_containment_checker* lcc_;
312
313
314
315
316

      typedef std::insert_iterator<
	std::vector<std::string>
      > ii;

Damien Lefortier's avatar
Damien Lefortier committed
317
318
319
320
321
322
      struct succ_state
      {
	std::vector<std::string> Q; // States
	const formula* condition;
	const formula* acc;
      };
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342

      std::string init_;
      std::vector<succ_state> succ_;

      std::vector<const formula*> to_free_;

    public:
      std::vector<succ_state>
      all_n_tuples(const std::vector<ltl2taa_visitor>& vs)
      {
	std::vector<succ_state> product;

	std::vector<int> pos;
	for (unsigned i = 0; i < vs.size(); ++i)
	  pos.push_back(vs[i].succ_.size());

	while (pos[0] != 0)
	{
	  std::vector<std::string> u; // Union
	  formula* f = constant::true_instance();
Damien Lefortier's avatar
Damien Lefortier committed
343
	  formula* a = constant::true_instance();
344
345
	  for (unsigned i = 0; i < vs.size(); ++i)
	  {
Damien Lefortier's avatar
Damien Lefortier committed
346
	    if (vs[i].succ_.empty())
347
348
	      continue;
	    const succ_state& ss(vs[i].succ_[pos[i] - 1]);
Damien Lefortier's avatar
Damien Lefortier committed
349
	    std::copy(ss.Q.begin(), ss.Q.end(), ii(u, u.begin()));
350
351
	    f = multop::instance(multop::And, ss.condition->clone(), f);
	    a = multop::instance(multop::And, ss.acc->clone(), a);
352
353
	  }
	  to_free_.push_back(f);
Damien Lefortier's avatar
Damien Lefortier committed
354
355
356
	  to_free_.push_back(a);
	  succ_state ss = { u, f, a };
	  product.push_back(ss);
357
358
359

	  for (int i = vs.size() - 1; i >= 0; --i)
	  {
Damien Lefortier's avatar
Damien Lefortier committed
360
	    if (vs[i].succ_.empty())
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
	      continue;
	    if (pos[i] > 1 || (i == 0 && pos[0] == 1))
	    {
	      --pos[i];
	      break;
	    }
	    else
	      pos[i] = vs[i].succ_.size();
	  }
	}
	return product;
      }
    };
  } // anonymous

376
  taa_tgba*
Damien Lefortier's avatar
Damien Lefortier committed
377
  ltl_to_taa(const ltl::formula* f, bdd_dict* dict, bool refined_rules)
378
379
380
381
  {
    // TODO: s/unabbreviate_ltl/unabbreviate_logic/
    const ltl::formula* f1 = ltl::unabbreviate_ltl(f);
    const ltl::formula* f2 = ltl::negative_normal_form(f1);
382
    f1->destroy();
383

384
    spot::taa_tgba* res = new spot::taa_tgba(dict);
385
    bdd_dict b;
Damien Lefortier's avatar
Damien Lefortier committed
386
    language_containment_checker* lcc =
387
      new language_containment_checker(&b, false, false, false, false);
Damien Lefortier's avatar
Damien Lefortier committed
388
    ltl2taa_visitor v(res, lcc, refined_rules);
389
    f2->accept(v);
390
    f2->destroy();
Damien Lefortier's avatar
Damien Lefortier committed
391
392
    delete lcc;

393
394
395
    return v.result();
  }
}