1 // -*- coding: utf-8 -*-
2 // Copyright (C) 2017-2021 Laboratoire de Recherche et Développement
3 // de l'Epita (LRDE).
4 //
5 // This file is part of Spot, a model checking library.
6 //
7 // Spot is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // Spot is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
15 // License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20 #include <config.h>
21
22 #include "argmatch.h"
23
24 #include "common_aoutput.hh"
25 #include "common_finput.hh"
26 #include "common_setup.hh"
27 #include "common_sys.hh"
28
29 #include <spot/misc/bddlt.hh>
30 #include <spot/misc/escape.hh>
31 #include <spot/misc/timer.hh>
32 #include <spot/tl/formula.hh>
33 #include <spot/tl/apcollect.hh>
34 #include <spot/twa/twagraph.hh>
35 #include <spot/twaalgos/aiger.hh>
36 #include <spot/twaalgos/game.hh>
37 #include <spot/twaalgos/hoa.hh>
38 #include <spot/twaalgos/minimize.hh>
39 #include <spot/twaalgos/mealy_machine.hh>
40 #include <spot/twaalgos/product.hh>
41 #include <spot/twaalgos/synthesis.hh>
42 #include <spot/twaalgos/translate.hh>
43
44 enum
45 {
46 OPT_ALGO = 256,
47 OPT_CSV,
48 OPT_DECOMPOSE,
49 OPT_INPUT,
50 OPT_OUTPUT,
51 OPT_PRINT,
52 OPT_PRINT_AIGER,
53 OPT_PRINT_HOA,
54 OPT_REAL,
55 OPT_SIMPLIFY,
56 OPT_VERBOSE,
57 OPT_VERIFY
58 };
59
60 static const argp_option options[] =
61 {
62 /**************************************************/
63 { nullptr, 0, nullptr, 0, "Input options:", 1 },
64 { "outs", OPT_OUTPUT, "PROPS", 0,
65 "comma-separated list of controllable (a.k.a. output) atomic"
66 " propositions", 0},
67 { "ins", OPT_INPUT, "PROPS", 0,
68 "comma-separated list of controllable (a.k.a. output) atomic"
69 " propositions", 0},
70 /**************************************************/
71 { nullptr, 0, nullptr, 0, "Fine tuning:", 10 },
72 { "algo", OPT_ALGO, "sd|ds|ps|lar|lar.old", 0,
73 "choose the algorithm for synthesis:"
74 " \"sd\": translate to tgba, split, then determinize;"
75 " \"ds\": translate to tgba, determinize, then split;"
76 " \"ps\": translate to dpa, then split;"
77 " \"lar\": translate to a deterministic automaton with arbitrary"
78 " acceptance condition, then use LAR to turn to parity,"
79 " then split (default);"
80 " \"lar.old\": old version of LAR, for benchmarking.\n", 0 },
81 { "decompose", OPT_DECOMPOSE, "yes|no", 0,
82 "whether to decompose the specification as multiple output-disjoint "
83 "problems to solve independently (enabled by default)", 0 },
84 { "simplify", OPT_SIMPLIFY, "no|bisim|bwoa|sat|bisim-sat|bwoa-sat", 0,
85 "simplification to apply to the controler (no) nothing, "
86 "(bisim) bisimulation-based reduction, (bwoa) bissimulation-based "
87 "reduction with output assignment, (sat) SAT-based minimization, "
88 "(bisim-sat) SAT after bisim, (bwoa-sat) SAT after bwoa. Defaults "
89 "to 'bwoa'.", 0 },
90 /**************************************************/
91 { nullptr, 0, nullptr, 0, "Output options:", 20 },
92 { "print-pg", OPT_PRINT, nullptr, 0,
93 "print the parity game in the pgsolver format, do not solve it", 0},
94 { "print-game-hoa", OPT_PRINT_HOA, "options", OPTION_ARG_OPTIONAL,
95 "print the parity game in the HOA format, do not solve it", 0},
96 { "realizability", OPT_REAL, nullptr, 0,
97 "realizability only, do not compute a winning strategy", 0},
98 { "aiger", OPT_PRINT_AIGER, "ite|isop|both[+ud][+dc]"
99 "[+sub0|sub1|sub2]", OPTION_ARG_OPTIONAL,
100 "prints a winning strategy as an AIGER circuit. The first, and only "
101 "mandatory option defines the method to be used. \"ite\" for "
102 "If-then-else normal form; "
103 "\"isop\" for irreducible sum of producs; "
104 "\"both\" tries both encodings and keeps the smaller one. "
105 "The other options further "
106 "refine the encoding, see aiger::encode_bdd.", 0},
107 { "verbose", OPT_VERBOSE, nullptr, 0,
108 "verbose mode", -1 },
109 { "verify", OPT_VERIFY, nullptr, 0,
110 "verifies the strategy or (if demanded) aiger against the spec.", -1 },
111 { "csv", OPT_CSV, "[>>]FILENAME", OPTION_ARG_OPTIONAL,
112 "output statistics as CSV in FILENAME or on standard output "
113 "(if '>>' is used to request append mode, the header line is "
114 "not output)", 0 },
115 /**************************************************/
116 { nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
117 { "extra-options", 'x', "OPTS", 0,
118 "fine-tuning options (see spot-x (7))", 0 },
119 { nullptr, 0, nullptr, 0, nullptr, 0 },
120 };
121
122 static const struct argp_child children[] =
123 {
124 { &finput_argp_headless, 0, nullptr, 0 },
125 { &aoutput_argp, 0, nullptr, 0 },
126 //{ &aoutput_o_format_argp, 0, nullptr, 0 },
127 { &misc_argp, 0, nullptr, 0 },
128 { nullptr, 0, nullptr, 0 }
129 };
130
131 const char argp_program_doc[] = "\
132 Synthesize a controller from its LTL specification.\v\
133 Exit status:\n\
134 0 if the input problem is realizable\n\
135 1 if the input problem is not realizable\n\
136 2 if any error has been reported";
137
138 static std::vector<std::string> all_output_aps;
139 static std::vector<std::string> all_input_aps;
140
141 static const char* opt_csv = nullptr;
142 static bool opt_print_pg = false;
143 static bool opt_print_hoa = false;
144 static const char* opt_print_hoa_args = nullptr;
145 static bool opt_real = false;
146 static bool opt_do_verify = false;
147 static const char* opt_print_aiger = nullptr;
148
149 static spot::synthesis_info* gi;
150
151 static char const *const algo_names[] =
152 {
153 "ds",
154 "sd",
155 "ps",
156 "lar",
157 "lar.old"
158 };
159
160 static char const *const algo_args[] =
161 {
162 "detsplit", "ds",
163 "splitdet", "sd",
164 "dpasplit", "ps",
165 "lar",
166 "lar.old",
167 nullptr
168 };
169 static spot::synthesis_info::algo const algo_types[] =
170 {
171 spot::synthesis_info::algo::DET_SPLIT, spot::synthesis_info::algo::DET_SPLIT,
172 spot::synthesis_info::algo::SPLIT_DET, spot::synthesis_info::algo::SPLIT_DET,
173 spot::synthesis_info::algo::DPA_SPLIT, spot::synthesis_info::algo::DPA_SPLIT,
174 spot::synthesis_info::algo::LAR,
175 spot::synthesis_info::algo::LAR_OLD,
176 };
177 ARGMATCH_VERIFY(algo_args, algo_types);
178
179 static const char* const decompose_args[] =
180 {
181 "yes", "true", "enabled", "1",
182 "no", "false", "disabled", "0",
183 nullptr
184 };
185 static bool decompose_values[] =
186 {
187 true, true, true, true,
188 false, false, false, false,
189 };
190 ARGMATCH_VERIFY(decompose_args, decompose_values);
191 bool opt_decompose_ltl = true;
192
193 static const char* const simplify_args[] =
194 {
195 "no", "false", "disabled", "0",
196 "bisim", "1",
197 "bwoa", "bisim-with-output-assignment", "2",
198 "sat", "3",
199 "bisim-sat", "4",
200 "bwoa-sat", "5",
201 nullptr
202 };
203 static unsigned simplify_values[] =
204 {
205 0, 0, 0, 0,
206 1, 1,
207 2, 2, 2,
208 3, 3,
209 4, 4,
210 5, 5,
211 };
212 ARGMATCH_VERIFY(simplify_args, simplify_values);
213
214 namespace
215 {
216 auto str_tolower = [] (std::string s)
__anon8920dfc70302(std::string s) 217 {
218 std::transform(s.begin(), s.end(), s.begin(),
219 [](unsigned char c){ return std::tolower(c); });
220 return s;
221 };
222
223 static void
print_csv(const spot::formula & f)224 print_csv(const spot::formula& f)
225 {
226 auto& vs = gi->verbose_stream;
227 auto& bv = gi->bv;
228 if (not bv)
229 error(2, 0, "no information available for csv (please send bug report)");
230 if (vs)
231 *vs << "writing CSV to " << opt_csv << '\n';
232
233 output_file outf(opt_csv);
234 std::ostream& out = outf.ostream();
235
236 // Do not output the header line if we append to a file.
237 // (Even if that file was empty initially.)
238 if (!outf.append())
239 {
240 out << ("\"formula\",\"algo\",\"tot_time\",\"trans_time\","
241 "\"split_time\",\"todpa_time\"");
242 if (!opt_print_pg && !opt_print_hoa)
243 {
244 out << ",\"solve_time\"";
245 if (!opt_real)
246 out << ",\"strat2aut_time\"";
247 if (opt_print_aiger)
248 out << ",\"aig_time\"";
249 out << ",\"realizable\""; //-1: Unknown, 0: Unreal, 1: Real
250 }
251 out << ",\"dpa_num_states\",\"dpa_num_states_env\""
252 << ",\"strat_num_states\",\"strat_num_edges\"";
253 if (opt_print_aiger)
254 out << ",\"nb latches\",\"nb gates\"";
255 out << '\n';
256 }
257 std::ostringstream os;
258 os << f;
259 spot::escape_rfc4180(out << '"', os.str());
260 out << "\",\"" << algo_names[(int) gi->s]
261 << "\"," << bv->total_time
262 << ',' << bv->trans_time
263 << ',' << bv->split_time
264 << ',' << bv->paritize_time;
265 if (!opt_print_pg && !opt_print_hoa)
266 {
267 out << ',' << bv->solve_time;
268 if (!opt_real)
269 out << ',' << bv->strat2aut_time;
270 if (opt_print_aiger)
271 out << ',' << bv->aig_time;
272 out << ',' << bv->realizable;
273 }
274 out << ',' << bv->nb_states_arena
275 << ',' << bv->nb_states_arena_env
276 << ',' << bv->nb_strat_states
277 << ',' << bv->nb_strat_edges;
278
279 if (opt_print_aiger)
280 {
281 out << ',' << bv->nb_latches
282 << ',' << bv->nb_gates;
283 }
284 out << '\n';
285 outf.close(opt_csv);
286 }
287
288 int
solve_formula(const spot::formula & f,const std::vector<std::string> & input_aps,const std::vector<std::string> & output_aps)289 solve_formula(const spot::formula& f,
290 const std::vector<std::string>& input_aps,
291 const std::vector<std::string>& output_aps)
292 {
293 spot::stopwatch sw;
294 if (gi->bv)
295 sw.start();
296
297 auto safe_tot_time = [&]()
298 {
299 if (gi->bv)
300 gi->bv->total_time = sw.stop();
301 };
302
303 std::vector<spot::formula> sub_form;
304 std::vector<std::set<spot::formula>> sub_outs;
305 if (opt_decompose_ltl)
306 {
307 auto subs = split_independant_formulas(f, output_aps);
308 if (subs.first.size() > 1)
309 {
310 sub_form = subs.first;
311 sub_outs = subs.second;
312 }
313 }
314 // When trying to split the formula, we can apply transformations that
315 // increase its size. This is why we will use the original formula if it
316 // has not been cut.
317 if (sub_form.empty())
318 {
319 sub_form = { f };
320 sub_outs.resize(1);
321 std::transform(output_aps.begin(), output_aps.end(),
322 std::inserter(sub_outs[0], sub_outs[0].begin()),
323 [](const std::string& name) {
324 return spot::formula::ap(name);
325 });
326 }
327 std::vector<std::vector<std::string>> sub_outs_str;
328 std::transform(sub_outs.begin(), sub_outs.end(),
329 std::back_inserter(sub_outs_str),
330 [](const auto& forms)
331 {
332 std::vector<std::string> r;
333 r.reserve(forms.size());
334 for (auto f : forms)
335 r.push_back(f.ap_name());
336 return r;
337 });
338
339 assert((sub_form.size() == sub_outs.size())
340 && (sub_form.size() == sub_outs_str.size()));
341
342 const bool want_game = opt_print_pg || opt_print_hoa;
343
344 std::vector<spot::twa_graph_ptr> arenas;
345
346 auto sub_f = sub_form.begin();
347 auto sub_o = sub_outs_str.begin();
348 std::vector<spot::mealy_like> mealy_machines;
349
350 auto print_game = want_game ?
351 [](const spot::twa_graph_ptr& game)->void
352 {
353 if (opt_print_pg)
354 pg_print(std::cout, game);
355 else
356 spot::print_hoa(std::cout, game, opt_print_hoa_args) << '\n';
357 }
358 :
359 [](const spot::twa_graph_ptr&)->void{};
360
361 for (; sub_f != sub_form.end(); ++sub_f, ++sub_o)
362 {
363 spot::mealy_like m_like
364 {
365 spot::mealy_like::realizability_code::UNKNOWN,
366 nullptr,
367 bddfalse
368 };
369 // If we want to print a game,
370 // we never use the direct approach
371 if (!want_game)
372 m_like =
373 spot::try_create_direct_strategy(*sub_f, *sub_o, *gi);
374
375 switch (m_like.success)
376 {
377 case spot::mealy_like::realizability_code::UNREALIZABLE:
378 {
379 std::cout << "UNREALIZABLE" << std::endl;
380 safe_tot_time();
381 return 1;
382 }
383 case spot::mealy_like::realizability_code::UNKNOWN:
384 {
385 auto arena = spot::ltl_to_game(*sub_f, *sub_o, *gi);
386 if (gi->bv)
387 {
388 gi->bv->nb_states_arena += arena->num_states();
389 auto spptr =
390 arena->get_named_prop<std::vector<bool>>("state-player");
391 assert(spptr);
392 gi->bv->nb_states_arena_env +=
393 std::count(spptr->cbegin(), spptr->cend(), false);
394 assert((spptr->at(arena->get_init_state_number()) == false)
395 && "Env needs first turn");
396 }
397 print_game(arena);
398 if (!spot::solve_game(arena, *gi))
399 {
400 std::cout << "UNREALIZABLE" << std::endl;
401 safe_tot_time();
402 return 1;
403 }
404 // Create the (partial) strategy
405 // only if we need it
406 if (!opt_real)
407 {
408 spot::mealy_like ml;
409 ml.success =
410 spot::mealy_like::realizability_code::REALIZABLE_REGULAR;
411 ml.mealy_like =
412 spot::solved_game_to_separated_mealy(arena, *gi);
413 ml.glob_cond = bddfalse;
414 mealy_machines.push_back(ml);
415 }
416 break;
417 }
418 case spot::mealy_like::realizability_code::REALIZABLE_REGULAR:
419 {
420 // the direct approach yielded a strategy
421 // which can now be minimized
422 // We minimize only if we need it
423 assert(m_like.mealy_like && "Expected success but found no mealy!");
424 if (!opt_real)
425 {
426 spot::stopwatch sw_direct;
427 sw_direct.start();
428
429 if ((0 < gi->minimize_lvl) && (gi->minimize_lvl < 3))
430 // Uses reduction or not,
431 // both work with mealy machines (non-separated)
432 reduce_mealy_here(m_like.mealy_like, gi->minimize_lvl == 2);
433
434 auto delta = sw_direct.stop();
435
436 sw_direct.start();
437 // todo better algo here?
438 m_like.mealy_like =
439 split_2step(m_like.mealy_like,
440 spot::get_synthesis_outputs(m_like.mealy_like),
441 false);
442 if (gi->bv)
443 gi->bv->split_time += sw_direct.stop();
444
445 sw_direct.start();
446 if (gi->minimize_lvl >= 3)
447 {
448 sw_direct.start();
449 // actual minimization, works on split mealy
450 m_like.mealy_like = minimize_mealy(m_like.mealy_like,
451 gi->minimize_lvl - 4);
452 delta = sw_direct.stop();
453 }
454
455 // Unsplit to have separated mealy
456 m_like.mealy_like = unsplit_mealy(m_like.mealy_like);
457
458 if (gi->bv)
459 gi->bv->strat2aut_time += delta;
460 if (gi->verbose_stream)
461 *gi->verbose_stream << "final strategy has "
462 << m_like.mealy_like->num_states()
463 << " states and "
464 << m_like.mealy_like->num_edges()
465 << " edges\n"
466 << "minimization took " << delta
467 << " seconds\n";
468 }
469 SPOT_FALLTHROUGH;
470 }
471 case spot::mealy_like::realizability_code::REALIZABLE_DTGBA:
472 if (!opt_real)
473 mealy_machines.push_back(m_like);
474 break;
475 default:
476 error(2, 0, "unexpected success code during mealy machine generation "
477 "(please send bug report)");
478 }
479 }
480
481 // If we only wanted to print the game we are done
482 if (want_game)
483 {
484 safe_tot_time();
485 return 0;
486 }
487
488 std::cout << "REALIZABLE" << std::endl;
489 if (opt_real)
490 {
491 safe_tot_time();
492 return 0;
493 }
494 // If we reach this line
495 // a strategy was found for each subformula
496 assert(mealy_machines.size() == sub_form.size()
497 && "There are subformula for which no mealy like object"
498 "has been created.");
499
500 spot::aig_ptr saig = nullptr;
501 spot::twa_graph_ptr tot_strat = nullptr;
502 automaton_printer printer;
503 spot::process_timer timer_printer_dummy;
504
505 if (opt_print_aiger)
506 {
507 spot::stopwatch sw2;
508 if (gi->bv)
509 sw2.start();
510 saig = spot::mealy_machines_to_aig(mealy_machines, opt_print_aiger,
511 input_aps,
512 sub_outs_str);
513 if (gi->bv)
514 {
515 gi->bv->aig_time = sw2.stop();
516 gi->bv->nb_latches = saig->num_latches();
517 gi->bv->nb_gates = saig->num_gates();
518 }
519 if (gi->verbose_stream)
520 {
521 *gi->verbose_stream << "AIG circuit was created in "
522 << gi->bv->aig_time
523 << " seconds and has " << saig->num_latches()
524 << " latches and "
525 << saig->num_gates() << " gates\n";
526 }
527 spot::print_aiger(std::cout, saig) << '\n';
528 }
529 else
530 {
531 assert(std::all_of(
532 mealy_machines.begin(), mealy_machines.end(),
533 [](const auto& ml)
534 {return ml.success ==
535 spot::mealy_like::realizability_code::REALIZABLE_REGULAR; })
536 && "ltlsynt: Cannot handle TGBA as strategy.");
537 tot_strat = mealy_machines.front().mealy_like;
538 for (size_t i = 1; i < mealy_machines.size(); ++i)
539 tot_strat = spot::product(tot_strat, mealy_machines[i].mealy_like);
540 printer.print(tot_strat, timer_printer_dummy);
541 }
542
543 // Final step: Do verification if demanded
544 if (!opt_do_verify)
545 {
546 safe_tot_time();
547 return 0;
548 }
549
550
551 // TODO: different options to speed up verification?!
552 spot::translator trans(gi->dict, &gi->opt);
553 auto neg_spec = trans.run(spot::formula::Not(f));
554 if (saig)
555 {
556 // Test the aiger
557 auto saigaut = saig->as_automaton(false);
558 if (neg_spec->intersects(saigaut))
559 error(2, 0, "Aiger and negated specification do intersect: "
560 "circuit is not OK.");
561 std::cout << "c\nCircuit was verified\n";
562 }
563 else if (tot_strat)
564 {
565 // Test the strategy
566 if (neg_spec->intersects(tot_strat))
567 error(2, 0, "Strategy and negated specification do intersect: "
568 "strategy is not OK.");
569 std::cout << "/*Strategy was verified*/\n";
570 }
571 // Done
572
573 safe_tot_time();
574 return 0;
575 }
576
577 class ltl_processor final : public job_processor
578 {
579 private:
580 std::vector<std::string> input_aps_;
581 std::vector<std::string> output_aps_;
582
583 public:
ltl_processor(std::vector<std::string> input_aps_,std::vector<std::string> output_aps_)584 ltl_processor(std::vector<std::string> input_aps_,
585 std::vector<std::string> output_aps_)
586 : input_aps_(std::move(input_aps_)),
587 output_aps_(std::move(output_aps_))
588 {
589 }
590
process_formula(spot::formula f,const char * filename,int linenum)591 int process_formula(spot::formula f,
592 const char* filename, int linenum) override
593 {
594 auto unknown_aps = [](spot::formula f,
595 const std::vector<std::string>& known,
596 const std::vector<std::string>* known2 = nullptr)
597 {
598 std::vector<std::string> unknown;
599 std::set<spot::formula> seen;
600 f.traverse([&](const spot::formula& s)
601 {
602 if (s.is(spot::op::ap))
603 {
604 if (!seen.insert(s).second)
605 return false;
606 const std::string& a = s.ap_name();
607 if (std::find(known.begin(), known.end(), a) == known.end()
608 && (!known2
609 || std::find(known2->begin(),
610 known2->end(), a) == known2->end()))
611 unknown.push_back(a);
612 }
613 return false;
614 });
615 return unknown;
616 };
617
618 // Decide which atomic propositions are input or output.
619 int res;
620 if (input_aps_.empty() && !output_aps_.empty())
621 {
622 res = solve_formula(f, unknown_aps(f, output_aps_), output_aps_);
623 }
624 else if (output_aps_.empty() && !input_aps_.empty())
625 {
626 res = solve_formula(f, input_aps_, unknown_aps(f, input_aps_));
627 }
628 else if (output_aps_.empty() && input_aps_.empty())
629 {
630 for (const std::string& ap: unknown_aps(f, input_aps_, &output_aps_))
631 error_at_line(2, 0, filename, linenum,
632 "one of --ins or --outs should list '%s'",
633 ap.c_str());
634 res = solve_formula(f, input_aps_, output_aps_);
635 }
636 else
637 {
638 for (const std::string& ap: unknown_aps(f, input_aps_, &output_aps_))
639 error_at_line(2, 0, filename, linenum,
640 "both --ins and --outs are specified, "
641 "but '%s' is unlisted",
642 ap.c_str());
643 res = solve_formula(f, input_aps_, output_aps_);
644 }
645
646 if (opt_csv)
647 print_csv(f);
648 return res;
649 }
650 };
651 }
652
653 static int
parse_opt(int key,char * arg,struct argp_state *)654 parse_opt(int key, char *arg, struct argp_state *)
655 {
656 // Called from C code, so should not raise any exception.
657 BEGIN_EXCEPTION_PROTECT;
658 switch (key)
659 {
660 case OPT_ALGO:
661 gi->s = XARGMATCH("--algo", arg, algo_args, algo_types);
662 break;
663 case OPT_CSV:
664 opt_csv = arg ? arg : "-";
665 if (not gi->bv)
666 gi->bv = spot::synthesis_info::bench_var();
667 break;
668 case OPT_DECOMPOSE:
669 opt_decompose_ltl = XARGMATCH("--decompose", arg,
670 decompose_args, decompose_values);
671 break;
672 case OPT_INPUT:
673 {
674 std::istringstream aps(arg);
675 std::string ap;
676 while (std::getline(aps, ap, ','))
677 {
678 ap.erase(remove_if(ap.begin(), ap.end(), isspace), ap.end());
679 all_input_aps.push_back(str_tolower(ap));
680 }
681 break;
682 }
683 case OPT_OUTPUT:
684 {
685 std::istringstream aps(arg);
686 std::string ap;
687 while (std::getline(aps, ap, ','))
688 {
689 ap.erase(remove_if(ap.begin(), ap.end(), isspace), ap.end());
690 all_output_aps.push_back(str_tolower(ap));
691 }
692 break;
693 }
694 case OPT_PRINT:
695 opt_print_pg = true;
696 gi->force_sbacc = true;
697 break;
698 case OPT_PRINT_HOA:
699 opt_print_hoa = true;
700 opt_print_hoa_args = arg;
701 break;
702 case OPT_PRINT_AIGER:
703 opt_print_aiger = arg ? arg : "ite";
704 break;
705 case OPT_REAL:
706 opt_real = true;
707 break;
708 case OPT_SIMPLIFY:
709 gi->minimize_lvl = XARGMATCH("--simplify", arg,
710 simplify_args, simplify_values);
711 break;
712 case OPT_VERBOSE:
713 gi->verbose_stream = &std::cerr;
714 if (not gi->bv)
715 gi->bv = spot::synthesis_info::bench_var();
716 break;
717 case OPT_VERIFY:
718 opt_do_verify = true;
719 break;
720 case 'x':
721 {
722 const char* opt = gi->opt.parse_options(arg);
723 if (opt)
724 error(2, 0, "failed to parse --options near '%s'", opt);
725 }
726 break;
727 }
728 END_EXCEPTION_PROTECT;
729 return 0;
730 }
731
732 int
main(int argc,char ** argv)733 main(int argc, char **argv)
734 {
735 return protected_main(argv, [&] {
736 // Create gi_ as a local variable, so make sure it is destroyed
737 // before all global variables.
738 spot::synthesis_info gi_;
739 gi = &gi_;
740 //gi_.opt.set("simul", 0); // no simulation, except...
741 //gi_.opt.set("dpa-simul", 1); // ... after determinization
742 gi_.opt.set("tls-impl", 1); // no automata-based implication check
743 gi_.opt.set("wdba-minimize", 2); // minimize only syntactic oblig
744 const argp ap = { options, parse_opt, nullptr,
745 argp_program_doc, children, nullptr, nullptr };
746 if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
747 exit(err);
748 check_no_formula();
749
750 // Check if inputs and outputs are distinct
751 for (const std::string& ai : all_input_aps)
752 if (std::find(all_output_aps.begin(), all_output_aps.end(), ai)
753 != all_output_aps.end())
754 error(2, 0, "'%s' appears both in --ins and --outs", ai.c_str());
755
756 ltl_processor processor(all_input_aps, all_output_aps);
757 if (int res = processor.run(); res == 0 || res == 1)
758 {
759 // Diagnose unused -x options
760 gi_.opt.report_unused_options();
761 return res;
762 }
763 return 2;
764 });
765 }
766