ltl2taa.cc 10.4 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_formula* 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_formula*
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
	  to_free_.push_back(f);
	}
72
73
74
	init_ = f;
	std::vector<const formula*> dst;
	std::vector<const formula*> a;
75

76
77
78
	// A little hack to define a sink
	dst.push_back(unop::instance(unop::Finish, constant::true_instance()));
	to_free_.push_back(dst[0]);
79
	taa_tgba::transition* t = res_->create_transition(init_, dst);
80
	res_->add_condition(t, f->clone());
81
	succ_state ss = { dst, f, a };
Damien Lefortier's avatar
Damien Lefortier committed
82
	succ_.push_back(ss);
83
84
85
86
87
      }

      void
      visit(const constant* node)
      {
88
	init_ = node;
89
90
91
	switch (node->val())
	{
	  case constant::True:
Damien Lefortier's avatar
Damien Lefortier committed
92
	  {
93
94
95
96
97
	    std::vector<const formula*> dst;
	    std::vector<const formula*> a;
	    // A little hack to define a sink
	    dst.push_back(unop::instance(unop::Finish,
					 constant::true_instance()));
98
	    res_->create_transition(init_, dst);
99
	    succ_state ss = { dst, node, a };
Damien Lefortier's avatar
Damien Lefortier committed
100
	    succ_.push_back(ss);
101
	    to_free_.push_back(dst[0]);
102
	    return;
Damien Lefortier's avatar
Damien Lefortier committed
103
	  }
104
105
106
107
108
109
110
111
112
113
114
115
116
	  case constant::False:
	    return;
	}
	/* Unreachable code.  */
	assert(0);
      }

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

117
	init_ = node;
118
119
120
	switch (node->op())
	{
	  case unop::X:
Damien Lefortier's avatar
Damien Lefortier committed
121
	  {
122
123
	    std::vector<const formula*> dst;
	    std::vector<const formula*> a;
Damien Lefortier's avatar
Damien Lefortier committed
124
	    if (v.succ_.empty()) // Handle X(0)
125
126
127
	      return;
	    dst.push_back(v.init_);
	    res_->create_transition(init_, dst);
Damien Lefortier's avatar
Damien Lefortier committed
128
	    succ_state ss =
129
	      { dst, constant::true_instance(), a };
Damien Lefortier's avatar
Damien Lefortier committed
130
	    succ_.push_back(ss);
131
	    return;
Damien Lefortier's avatar
Damien Lefortier committed
132
	  }
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
	  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());

154
	init_ = node;
155
156
	std::vector<succ_state>::iterator i1;
	std::vector<succ_state>::iterator i2;
157
	taa_tgba::transition* t = 0;
Damien Lefortier's avatar
Damien Lefortier committed
158
	bool contained = false;
159
160
161
	switch (node->op())
	{
	  case binop::U: // Strong
Damien Lefortier's avatar
Damien Lefortier committed
162
163
	    if (refined_)
	      contained = lcc_->contained(node->second(), node->first());
164
165
	    for (i1 = v1.succ_.begin(); i1 != v1.succ_.end(); ++i1)
	    {
Damien Lefortier's avatar
Damien Lefortier committed
166
167
168
169
170
171
	      // 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
172
	      i1->acc.push_back(node->second());
Damien Lefortier's avatar
Damien Lefortier committed
173
	      t = res_->create_transition(init_, i1->Q);
174
	      res_->add_condition(t, i1->condition->clone());
175
	      res_->add_acceptance_condition(t, node->second()->clone());
176
177
178
179
	      succ_.push_back(*i1);
	    }
	    for (i2 = v2.succ_.begin(); i2 != v2.succ_.end(); ++i2)
	    {
Damien Lefortier's avatar
Damien Lefortier committed
180
	      t = res_->create_transition(init_, i2->Q);
181
	      res_->add_condition(t, i2->condition->clone());
182
183
184
185
	      succ_.push_back(*i2);
	    }
	    return;
	  case binop::R: // Weak
Damien Lefortier's avatar
Damien Lefortier committed
186
187
	    if (refined_)
	      contained = lcc_->contained(node->first(), node->second());
188
189
190
191
	    for (i2 = v2.succ_.begin(); i2 != v2.succ_.end(); ++i2)
	    {
	      for (i1 = v1.succ_.begin(); i1 != v1.succ_.end(); ++i1)
	      {
192
193
194
		std::vector<const formula*> u; // Union
		std::vector<const formula*> a; // Acceptance conditions
		std::copy(i1->Q.begin(), i1->Q.end(), ii(u, u.end()));
195
		formula* f = i1->condition->clone(); // Refined rule
Damien Lefortier's avatar
Damien Lefortier committed
196
197
		if (!refined_ || !contained)
		{
198
		  std::copy(i2->Q.begin(), i2->Q.end(), ii(u, u.end()));
199
		  f = multop::instance(multop::And, f, i2->condition->clone());
Damien Lefortier's avatar
Damien Lefortier committed
200
		}
201
202
203
		to_free_.push_back(f);

		t = res_->create_transition(init_, u);
204
		res_->add_condition(t, f->clone());
205
		succ_state ss = { u, f, a };
Damien Lefortier's avatar
Damien Lefortier committed
206
		succ_.push_back(ss);
207
208
	      }

Damien Lefortier's avatar
Damien Lefortier committed
209
210
211
212
213
214
	      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);
215
	      res_->add_condition(t, i2->condition->clone());
216
217
218
	      if (refined_)
		for (unsigned i = 0; i < i2->acc.size(); ++i)
		  res_->add_acceptance_condition(t, i2->acc[i]->clone());
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
	      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)
      {
	std::vector<ltl2taa_visitor> vs;
	for (unsigned n = 0; n < node->size(); ++n)
	  vs.push_back(recurse(node->nth(n)));

238
	init_ = node;
239
	std::vector<succ_state>::iterator i;
240
	taa_tgba::transition* t = 0;
241
242
243
244
245
	switch (node->op())
	{
	  case multop::And:
	  {
	    std::vector<succ_state> p = all_n_tuples(vs);
246
	    for (unsigned n = 0; n < p.size(); ++n)
247
	    {
Damien Lefortier's avatar
Damien Lefortier committed
248
249
	      if (refined_)
	      {
250
		std::vector<const formula*> v; // All sub initial states.
Damien Lefortier's avatar
Damien Lefortier committed
251
252
253
254
255
256
257
258
259
260
		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())
		{
261
		  std::vector<const formula*> Q;
Damien Lefortier's avatar
Damien Lefortier committed
262
263
264
265
266
267
		  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);
268
		  res_->add_condition(t, p[n].condition->clone());
269
270
		  for (unsigned i = 0; i < p[n].acc.size(); ++i)
		    res_->add_acceptance_condition(t, p[n].acc[i]->clone());
Damien Lefortier's avatar
Damien Lefortier committed
271
		  succ_.push_back(p[n]);
272
		  continue;
Damien Lefortier's avatar
Damien Lefortier committed
273
274
275
		}
	      }
	      t = res_->create_transition(init_, p[n].Q);
276
	      res_->add_condition(t, p[n].condition->clone());
277
278
279
280
281
282
283
284
	      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
285
		t = res_->create_transition(init_, i->Q);
286
		res_->add_condition(t, i->condition->clone());
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
		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
305
	ltl2taa_visitor v(res_, lcc_, refined_, negated_);
306
307
308
309
310
311
312
	f->accept(v);
	for (unsigned i = 0; i < v.to_free_.size(); ++i)
	  to_free_.push_back(v.to_free_[i]);
	return v;
      }

    private:
313
      taa_tgba_formula* res_;
Damien Lefortier's avatar
Damien Lefortier committed
314
      bool refined_;
315
      bool negated_;
Damien Lefortier's avatar
Damien Lefortier committed
316
      language_containment_checker* lcc_;
317
318

      typedef std::insert_iterator<
319
	std::vector<const formula*>
320
321
      > ii;

Damien Lefortier's avatar
Damien Lefortier committed
322
323
      struct succ_state
      {
324
	std::vector<const formula*> Q; // States
Damien Lefortier's avatar
Damien Lefortier committed
325
	const formula* condition;
326
	std::vector<const formula*> acc;
Damien Lefortier's avatar
Damien Lefortier committed
327
      };
328

329
      const formula* init_;
330
331
332
333
334
335
336
337
338
339
      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;

340
	std::vector<int> pos(vs.size());
341
	for (unsigned i = 0; i < vs.size(); ++i)
342
	  pos[i] = vs[i].succ_.size();
343
344
345

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

	  for (int i = vs.size() - 1; i >= 0; --i)
	  {
Damien Lefortier's avatar
Damien Lefortier committed
370
	    if (vs[i].succ_.empty())
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
	      continue;
	    if (pos[i] > 1 || (i == 0 && pos[0] == 1))
	    {
	      --pos[i];
	      break;
	    }
	    else
	      pos[i] = vs[i].succ_.size();
	  }
	}
	return product;
      }
    };
  } // anonymous

386
  taa_tgba*
Damien Lefortier's avatar
Damien Lefortier committed
387
  ltl_to_taa(const ltl::formula* f, bdd_dict* dict, bool refined_rules)
388
389
390
391
  {
    // TODO: s/unabbreviate_ltl/unabbreviate_logic/
    const ltl::formula* f1 = ltl::unabbreviate_ltl(f);
    const ltl::formula* f2 = ltl::negative_normal_form(f1);
392
    f1->destroy();
393

394
    spot::taa_tgba_formula* res = new spot::taa_tgba_formula(dict);
395
    bdd_dict b;
Damien Lefortier's avatar
Damien Lefortier committed
396
    language_containment_checker* lcc =
397
      new language_containment_checker(&b, false, false, false, false);
Damien Lefortier's avatar
Damien Lefortier committed
398
    ltl2taa_visitor v(res, lcc, refined_rules);
399
    f2->accept(v);
400
    f2->destroy();
Damien Lefortier's avatar
Damien Lefortier committed
401
402
    delete lcc;

403
404
405
    return v.result();
  }
}