mCRL2
Loading...
Searching...
No Matches
linearise.cpp
Go to the documentation of this file.
1// Author(s): Jan Friso Groote
2// Copyright: see the accompanying file COPYING or copy at
3// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
4//
5// Distributed under the Boost Software License, Version 1.0.
6// (See accompanying file LICENSE_1_0.txt or copy at
7// http://www.boost.org/LICENSE_1_0.txt)
8//
12
13/* This file contains the implementation of an mCRL2 lineariser.
14
15 It is based on the implementation of the mCRL lineariser, on which work
16 started on 12 juli 1997. This lineariser was based on the CWI technical
17 report "The Syntax and Semantics of Timed mCRL", by J.F. Groote.
18
19 Everybody is free to use this software, provided it is not changed.
20
21 In case problems are encountered when using this software, please report
22 them to J.F. Groote, TU/e, Eindhoven, J.F.Groote@tue.nl
23
24 This software comes as it is. I.e. the author assumes no responsibility for
25 the use of this software.
26*/
27
28//mCRL2 data
33
34// linear process libraries.
36#include "mcrl2/lps/linearise.h"
37#include "mcrl2/lps/sumelm.h"
38#include "mcrl2/lps/constelm.h"
40
41// Process libraries.
44
45
46// For Aterm library extension functions
47using namespace atermpp;
48using namespace mcrl2;
49using namespace mcrl2::core;
50using namespace mcrl2::core::detail;
51using namespace mcrl2::data;
52using namespace mcrl2::data::detail;
53using namespace mcrl2::lps;
54using namespace mcrl2::process;
55
56/* Preamble */
57
58typedef enum { unknown,
68 error
70
71
72
73/**************** Definitions of object class ***********************/
74
75typedef enum { none,
84
86{
87 public:
92 process_identifier process_representing_action; /* for actions target sort is used to
93 indicate the process representing this action. */
95 std::set <variable> free_variables;
103
105 {
106 constructor=false;
108 object=none;
109 canterminate=0;
110 containstime=false;
111 }
112
114 {
125 object=o.object;
128 }
129
131 {
142 object=o.object;
145 return (*this);
146 }
147
149 {
150 }
151};
152
153
155{
156 public:
157 process::action_label_list acts; /* storage place for actions */
158 std::set < variable > global_variables; /* storage place for free variables occurring
159 in processes ranging over data */
160 variable_list initdatavars; /* storage place for free variables in
161 init clause */
162 data_specification data; /* contains the data specification for the current process. */
163
166
167 private:
168 class stackoperations;
169 class stacklisttype;
170 class enumtype;
171 class enumeratedtype;
172
173 std::vector < process_equation > procs;
174 /* storage place for processes,
175 uses alt, seq, par, lmer, cond,sum,
176 com, bound, at, name, delta,
177 tau, hide, rename, encap */
178 mcrl2::data::rewriter rewr; /* The rewriter used while linearising */
179 action terminationAction; /* A list of length one with the action that denotes termination */
180 process_identifier terminatedProcId; /* A process identifier of which the body consists of the termination
181 action */
184 std::vector < process_identifier > seq_varnames; /* Contains names of processes which represent a sequence
185 of process variables */
186 std::vector < std::vector < process_instance_assignment > > representedprocesses; /* contains the sequences of process
187 instances that are represented by the variables in seq_varnames */
192 std::map < aterm, objectdatatype > objectdata; // It is important to guarantee that the objects will not
193 // be moved to another place when the object data structure grows. This
194 // is because objects in this datatype are passed around by reference.
195
197 std::vector < enumeratedtype > enumeratedtypes;
199
200 public:
202 const std::vector< process_equation >& ps,
203 const variable_list& idvs,
204 const data_specification& ds,
205 const std::set < data::variable >& glob_vars,
206 const t_lin_options& opt,
207 const process_specification& procspec):
208 acts(),
209 global_variables(glob_vars),
210 data(ds),
212 options(opt),
213 timeIsBeingUsed(false),
216 {
217 // find_identifiers does not find the identifiers in the enclosed data specification.
218 fresh_identifier_generator.add_identifiers(process::find_identifiers(procspec));
219 // So, the identifiers in the data type must be added explicitly.
220 fresh_identifier_generator.add_identifiers(data::find_identifiers(ds.equations()));
222 fresh_identifier_generator.add_identifiers(data::find_identifiers(ds.sorts()));
223 fresh_identifier_generator.add_identifiers(data::find_identifiers(ds.constructors()));
224 fresh_identifier_generator.add_identifiers(data::find_identifiers(ds.mappings()));
225
226 stack_operations_list=nullptr;
227 acts=as;
228 storeact(acts);
229 procs=ps;
231 initdatavars=idvs;
232 // The terminationAction and the terminatedProcId must be defined after initialisation of
233 // data as otherwise fresh name does not work properly.
236// /* Changed delta() to DeltaAtZero on 24/12/2006. Moved back in spring 2007, as this introduces unwanted time constraints. */
241 pCRL,0,false);
244 }
245
247 {
248 for (; stack_operations_list!=nullptr;)
249 {
253 }
254
255 }
256
257 private:
258 /* data_expression real_zero()
259 {
260 static data_expression zero=sort_real::creal(sort_int::cint(sort_nat::c0()),sort_pos::c1());
261 return zero;
262 }
263
264 data_expression real_one()
265 {
266 static data_expression one=sort_real::creal(sort_int::cint(sort_nat::cnat(sort_pos::c1())),sort_pos::c1());
267 return one;
268 } */
269
271 {
273 {
274 return sort_real::real_zero();
275 }
276 if (r1==sort_real::real_one())
277 {
278 return r2;
279 }
280 if (r2==sort_real::real_one())
281 {
282 return r1;
283 }
284 return sort_real::times(r1,r2);
285 }
286
288 {
289 return at(delta(), sort_real::real_(0));
290 }
291
293 {
294 if (!is_at(t))
295 {
296 return false;
297 }
298 if (!is_delta(at(t).operand()))
299 {
300 return false;
301 }
302 return RewriteTerm(at(t).time_stamp())==sort_real::real_one();
303 }
304
305 /***************** temporary helper function to compare substitutions ******************/
306
307 template <class Expression, class Substitution>
308 Expression replace_variables_capture_avoiding_alt(const Expression& e, Substitution& sigma)
309 {
310 return process::replace_variables_capture_avoiding_with_an_identifier_generator(e, sigma, fresh_identifier_generator);
311 }
312
313
314 /***************** retrieve basic objects ******************/
315
316 void detail_check_objectdata(const aterm& o) const
317 {
318 if (objectdata.count(o)==0)
319 {
321 {
322 throw mcrl2::runtime_error("Fail to recognize " + process::pp(process_identifier(o)) + ". Most likely due to unguarded recursion in a process equation. ");
323 }
324 else
325 {
326 throw mcrl2::runtime_error("Fail to recognize " + process::pp(o) + ". This is an internal error in the lineariser. ");
327 }
328 }
329 }
331 {
333 return objectdata.find(o)->second;
334 }
335
336 const objectdatatype& objectIndex(const aterm& o) const
337 {
339 return objectdata.find(o)->second;
340 }
341
343 {
345 }
346
348 {
349 if (ma.size()==0)
350 {
351 return tau();
352 }
353 if (ma.size()==1)
354 {
355 return ma.front();
356 }
358 }
359
361 {
362 if (is_tau(p))
363 {
364 return action_list();
365 }
366
367 if (is_action(p))
368 {
369 return action_list({ action(p) });
370 }
371
372 if (is_sync(p))
373 {
374 return to_action_list(process::sync(p).left())+to_action_list(process::sync(p).right());
375 }
376 assert(0);
377 return action_list();
378 }
379
381 {
383 {
385 }
386 assert(is_sync(multiAction));
388 }
389
390 // Returns a list of variables with the same sort as the expressions in the list.
391 // If the expression is a variable not occurring in the occurs_set that variable
392 // is used.
394 std::set < variable>& occurs_set)
395 {
396 variable_list result;
397 for (const data_expression& e: l)
398 {
399 /* if the current argument of the multi-action is a variable that does
400 not occur in result, use this variable. This is advantageous, when joining
401 processes to one linear process where variable names are joined. If this
402 is not being done (as happened before 4/1/2008) very long lists of parameters
403 can occur when linearising using regular2 */
404 if (is_variable(e) && std::find(occurs_set.begin(),occurs_set.end(),e)==occurs_set.end())
405 {
406 const variable& v = atermpp::down_cast<variable>(e);
407 result.push_front(v);
408 occurs_set.insert(v);
409 }
410 else
411 {
412 result.push_front(variable(get_fresh_variable("a",e.sort())));
413 }
414 }
415 return reverse(result);
416 }
417
419 std::set < variable>& occurs_set)
420 {
422 {
423 return make_parameters_rec(action(multiAction).arguments(),occurs_set);
424 }
425 assert(is_sync(multiAction));
426 return getparameters_rec(process::sync(multiAction).left(),occurs_set)+
427 getparameters_rec(process::sync(multiAction).right(),occurs_set);
428 }
429
431 {
432 std::set < variable > occurs_set;
433 return getparameters_rec(multiAction,occurs_set);
434 }
435
437 {
439 for (const action& a: multiAction)
440 {
441 result=reverse(a.arguments()) + result;
442 }
443 return reverse(result);
444 }
445
447 {
448 action_list result;
450 for (const process::action_label& l: actionIds)
451 {
452 std::size_t arity=l.sorts().size();
453 data_expression_list temp_args;
454 for (std::size_t i=0 ; i< arity; ++i,++e_walker)
455 {
456 assert(e_walker!=args.end());
457 temp_args.push_front(*e_walker);
458 }
459 temp_args=reverse(temp_args);
460 result.push_front(action(l,temp_args));
461 }
462 assert(e_walker==args.end());
463 return reverse(result);
464 }
465
467 {
469
470 isnew=(objectdata.count(actionnames)==0);
471
472 if (isnew)
473 {
474 objectdatatype object;
475
476 // tempvar is needed as objectdata can change during a call
477 // of getparameters.
479 object.parameters=templist;
480 object.object=multiact;
481 // must separate assignment below as
482 // objectdata may change as a side effect of make
483 // multiaction.
484 const action_list tempvar=makemultiaction(actionnames, variable_list_to_data_expression_list(object.parameters));
485 object.processbody=action_list_to_process(tempvar);
486 object.free_variables=std::set<variable>(object.parameters.begin(), object.parameters.end());
487 object.free_variables_defined=true;
488
489 objectdata[actionnames]=object;
490 }
491 return objectdata.find(actionnames)->second;
492 }
493
494 const std::set<variable>& get_free_variables(objectdatatype& object)
495 {
496 if (!object.free_variables_defined)
497 {
498 object.free_variables=find_free_variables_process(object.processbody);
499 object.free_variables_defined=true;
500 }
501 return object.free_variables;
502 }
503
504 void insertvariable(const variable& var, const bool mustbenew)
505 {
506 addString(var.name());
507
508 if (objectdata.count(var.name())>0 && mustbenew)
509 {
510 throw mcrl2::runtime_error("Variable " + data::pp(var) + " already exists. ");
511 }
512
513 objectdatatype object;
514 object.objectname=var.name();
515 object.object=variable_;
516 objectdata[var.name()]=object;
517 }
518
519 void insertvariables(const variable_list& vars, const bool mustbenew)
520 {
521 for (const variable& v: vars)
522 {
523 insertvariable(v,mustbenew);
524 }
525 }
526
527 template <class SUBSTITUTION>
528 std::set<data::variable> sigma_variables(const SUBSTITUTION& sigma)
529 {
530 std::set<data::variable> result;
531 for (typename SUBSTITUTION::const_iterator i = sigma.begin(); i != sigma.end(); ++i)
532 {
533 std::set<data::variable> V = data::find_free_variables(i->second);
534 V.erase(i->first);
535 result.insert(V.begin(), V.end());
536 }
537 return result;
538 }
539
540
541 /************ upperpowerof2 *********************************************/
542
543 std::size_t upperpowerof2(std::size_t i)
544 /* function yields n for the smallest value n such that
545 2^n>=i. This constitutes the number of bits necessary
546 to represent a number smaller than i. i is assumed to
547 be at least 1. */
548 {
549 std::size_t n=0;
550 std::size_t powerof2=1;
551 for (; powerof2< i ; n++)
552 {
553 powerof2=2*powerof2;
554 }
555 return n;
556 }
557
559 {
560 if (!options.norewrite)
561 {
563 {
566 }
567 return rewr(t);
568 }
569 return t;
570 }
571
573 {
575 for(const data_expression& d: t)
576 {
577 v.push_back(RewriteTerm(d));
578 }
579 return data_expression_list(v.begin(),v.end());
580 }
581
583 {
585 for(const assignment& a: t)
586 {
587 v.push_back(assignment(a.lhs(), RewriteTerm(a.rhs())));
588 }
589 return assignment_list(v.begin(),v.end());
590 }
591
593 {
594 return action(t.label(),RewriteTermList(t.arguments()));
595 }
596
598 {
600 }
601
603 {
604 if (is_tau(t))
605 {
606 return t;
607 }
608
609 if (is_action(t))
610 {
611 return RewriteAction(action(t));
612 }
613
614 assert(is_sync(t)); // A multi action is a sequence of actions with a sync operator in between.
616 }
617
618
620 {
621 if (options.norewrite)
622 {
623 return t;
624 }
625
626 if (is_if_then(t))
627 {
628 const data_expression new_cond=RewriteTerm(down_cast<if_then>(t).condition());
629 const process_expression new_then_case=pCRLrewrite(down_cast<if_then>(t).then_case());
630 if (new_cond==sort_bool::true_())
631 {
632 return new_then_case;
633 }
634 return if_then(new_cond,new_then_case);
635 }
636
637 if (is_seq(t))
638 {
639 /* only one summand is needed */
640 return seq(
641 pCRLrewrite(seq(t).left()),
642 pCRLrewrite(seq(t).right()));
643 }
644
645 if (is_at(t))
646 {
647 const data_expression& atTime=RewriteTerm(down_cast<at>(t).time_stamp());
648 const process_expression t1=pCRLrewrite(down_cast<at>(t).operand());
649 return at(t1,atTime);
650 }
651
653 {
654 const stochastic_operator& sto=atermpp::down_cast<const stochastic_operator>(t);
656 }
657
658 if (is_delta(t) || is_tau(t))
659 {
660 return t;
661 }
662
663 if (is_action(t))
664 {
665 return RewriteAction(atermpp::down_cast<process::action>(t));
666 }
667
669 {
671 }
672
673 if (is_sync(t))
674 {
675 return RewriteMultAct(t);
676 }
677
678 assert(0); // Expected a term in pCRL format, using only basic process operators.
679 return t;
680 }
681
682 /************ storeact ****************************************************/
683
685 {
686 if (objectdata.count(actionId)>0)
687 {
688 throw mcrl2::runtime_error("Action " + process::pp(actionId) + " is added twice. This is an internal error in the lineariser. Please report. ");
689 }
690
691 const identifier_string& str=actionId.name();
692 addString(str);
693
694 objectdatatype object;
695 object.objectname=str;
696 object.object=act;
697 object.process_representing_action=process_identifier();
698
699 objectdata[actionId]=object;
700 return objectdata.find(actionId)->second;
701 }
702
704 {
705 for (const process::action_label& l: acts)
706 {
707 insertAction(l);
708 }
709 }
710
711 /************ storeprocs *************************************************/
712
714 const process_identifier& procId,
715 const variable_list& parameters,
716 const process_expression& body,
718 const bool canterminate,
719 const bool containstime)
720 {
721 assert(procId.variables().size()==parameters.size());
722 const std::string str=procId.name();
723 addString(str);
724
725 if (objectdata.count(procId)>0)
726 {
727 throw mcrl2::runtime_error("Process " + process::pp(procId) + " is added twice. This is an internal error in the lineariser. Please report. ");
728 }
729
730 objectdatatype object;
731 object.objectname=procId.name();
732 object.object=proc;
733 object.processbody=body;
734 object.free_variables_defined=false;
735 object.canterminate=canterminate;
736 object.containstime=containstime;
737 object.processstatus=s;
738 object.parameters=parameters;
739 insertvariables(parameters,false);
740 objectdata[procId]=object;
741 return objectdata.find(procId)->second;
742 }
743
744 void storeprocs(const std::vector< process_equation >& procs)
745 {
746 for (const process_equation& e: procs)
747 {
749 e.identifier(),
750 e.formal_parameters(),
751 e.expression(),
752 unknown,0,false);
753 }
754 }
755
757 const variable_list& parameters,
758 const process_expression& body,
759 const processstatustype s,
760 const bool canterminate,
761 const bool containstime,
763 {
764 for(const std::pair<const aterm,objectdatatype>& d: objectdata)
765 {
766 if (d.second.object==proc &&
767 d.second.parameters==parameters &&
768 d.second.processbody==body &&
769 d.second.canterminate==canterminate &&
770 d.second.containstime==containstime &&
771 d.second.processstatus==s)
772 {
773 p=process_identifier(d.second.objectname,d.second.parameters);
774 return true;
775 }
776 }
777 return false;
778 }
779
780
781 /************ storeinit *************************************************/
782
783 public:
785 {
786 /* init is used as the name of the initial process,
787 because it cannot occur as a string in the input */
788
789 process_identifier initprocess(std::string("init"), variable_list());
791 return initprocess;
792 }
793
794 private:
795
796 /********** various functions on action and multi actions ***************/
797 bool actioncompare(const action_label& a1, const action_label& a2)
798 {
799 /* first compare the strings in the actions */
800 if (std::string(a1.name())<std::string(a2.name()))
801 {
802 return true;
803 }
804
805 if (a1.name()==a2.name())
806 {
807 /* the strings are equal; the sorts are used to
808 determine the ordering */
809 return a1.sorts()<a2.sorts();
810 }
811
812 return false;
813 }
814
816 const action& act,
818 {
819 /* store the action in the multiAction, alphabetically
820 sorted on the actionname in the actionId. Note that
821 the empty multiAction represents tau. */
822
823 if (multiAction.empty())
824 {
825 return action_list({ act });
826 }
827 const action firstAction=multiAction.front();
828
829 /* Actions are compared on the basis of their position
830 in memory, to order them. As the aterm library maintains
831 pointers to objects that are not garbage collected, this
832 is a safe way to do this. */
833 if (actioncompare(act.label(),firstAction.label()))
834 {
835 multiAction.push_front(act);
836 return multiAction;
837 }
839 act,
840 multiAction.tail());
841 result.push_front(firstAction);
842 return result;
843 }
844
846 {
847 action_list result=ma2;
848 for (const action& a: ma1)
849 {
850 result=linInsertActionInMultiActionList(a,result);
851 }
852 return result;
853 }
854
855
857 {
859 }
860
861 /************** determine_process_status ********************************/
862
864 const process_expression& body, // intentionally not a reference.
865 const processstatustype status)
866 {
867 /* In this procedure it is determined whether a process
868 is of type mCRL, pCRL or a multiAction. pCRL processes
869 occur strictly within mCRL processes, and multiActions
870 occur strictly within pCRL processes. Processes that pass
871 this procedure can be linearised. Bounded initialisation,
872 the leftmerge and synchronization merge on the highest
873 level are filtered out. */
874 if (is_choice(body))
875 {
876 if (status==multiAction)
877 {
878 throw mcrl2::runtime_error("Choice operator occurs in a multi-action in " + process::pp(body) + ". The lineariser cannot handle such a pattern. ");
879 }
882 if ((s1==mCRL)||(s2==mCRL))
883 {
884 throw mcrl2::runtime_error("An operator ||, allow, hide, rename, or comm occurs within the scope of a choice operator in " + process::pp(body) +
885 ". The lineariser cannot handle such processes. ");
886 }
887 return pCRL;
888 }
889
890 if (is_seq(body))
891 {
892 if (status==multiAction)
893 {
894 throw mcrl2::runtime_error("Sequential operator occurs in a multi-action in " + process::pp(body) +". The lineariser cannot handle such a pattern. ");
895 }
898 if ((s1==mCRL)||(s2==mCRL))
899 {
900 throw mcrl2::runtime_error("An operator ||, allow, hide, rename, or comm occurs in the scope of a sequential operator in " + process::pp(body) +". "
901 + "The lineariser cannot handle such processes. ");
902 }
903 return pCRL;
904 }
905
906 if (is_merge(body))
907 {
908 if (status!=mCRL)
909 {
910 throw mcrl2::runtime_error("The parallel operator occurs in the scope of recursion, or the condition, sequential or choice operator in " +
911 process::pp(body) + ". The lineariser cannot handle such processes. ");
912 }
915 return mCRL;
916 }
917
918 if (is_left_merge(body))
919 {
920 throw mcrl2::runtime_error("Cannot linearise because the specification contains a leftmerge. ");
921 }
922
923 if (is_if_then(body))
924 {
925 if (status==multiAction)
926 {
927 throw mcrl2::runtime_error("If-then occurs in a multi-action in " + process::pp(body) + ". The lineariser cannot linearise this. ");
928 }
930 if (s1==mCRL)
931 {
932 throw mcrl2::runtime_error("An operator ||, allow, hide, rename, or comm occurs in the scope of the if-then operator in " + process::pp(body) + ". "
933 + "The lineariser cannot handle such processes. ");
934 }
935 return pCRL;
936 }
937
938 if (is_if_then_else(body))
939 {
940 if (status==multiAction)
941 {
942 throw mcrl2::runtime_error("If-then-else occurs in a multi-action in " + process::pp(body) +".");
943 }
946 if ((s1==mCRL)||(s2==mCRL))
947 {
948 throw mcrl2::runtime_error("An operator ||, allow, hide, rename, or comm occurs in the scope of the if-then-else operator in " + process::pp(body) +
949 ". " + "The lineariser cannot handle such processes. ");
950 }
951 return pCRL;
952 }
953
954 if (is_sum(body))
955 {
956 /* insert the variable names of variables, to avoid
957 that this variable name will be reused later on */
958 insertvariables(sum(body).variables(),false);
959 if (status==multiAction)
960 {
961 throw mcrl2::runtime_error("The sum operator occurs within a multi-action in " + process::pp(body) + ". "
962 + "The lineariser cannot handle such processes. ");
963 }
964 const processstatustype s1=determine_process_statusterm(sum(body).operand(),pCRL);
965 if (s1==mCRL)
966 {
967 throw mcrl2::runtime_error("An operator ||, allow, hide, rename, or comm occurs in the scope of the sum operator in " + process::pp(body) + ". "
968 "The lineariser cannot handle such processes. ");
969 }
970 return pCRL;
971 }
972
973 if (is_stochastic_operator(body))
974 {
976 /* insert the variable names of variables, to avoid
977 that this variable name will be reused later on */
978 const stochastic_operator& sto=down_cast<const stochastic_operator>(body);
979 insertvariables(sto.variables(),false);
980 if (status==multiAction)
981 {
982 throw mcrl2::runtime_error("Stochastic operator occurs within a multi-action in " + process::pp(body) +".");
983 }
985 if (s1==mCRL)
986 {
987 throw mcrl2::runtime_error("An operator ||, allow, hide, rename, or comm occurs in the scope of the stochastic operator in " + process::pp(body) + ". "
988 + "The lineariser cannot handle such processes. ");
989 }
990 return s1;
991 }
992
993 if (is_comm(body))
994 {
995 if (status!=mCRL)
996 {
997 throw mcrl2::runtime_error("The communication operator occurs in the scope of recursion, the condition, the sequential operation or the choice in "
998 + process::pp(body) + ". The lineariser cannot linearise such processes. ");
999 }
1000 determine_process_statusterm(comm(body).operand(),mCRL);
1001 return mCRL;
1002 }
1003
1004 if (is_bounded_init(body))
1005 {
1006 throw mcrl2::runtime_error("Cannot linearise a specification with the bounded initialization operator.");
1007 }
1008
1009 if (is_at(body))
1010 {
1011 timeIsBeingUsed = true;
1012 if (status==multiAction)
1013 {
1014 throw mcrl2::runtime_error("Time operator occurs in a multi-action in " + process::pp(body) +".");
1015 }
1016 const processstatustype s1=determine_process_statusterm(at(body).operand(),pCRL);
1017 if (s1==mCRL)
1018 {
1019 throw mcrl2::runtime_error("An operator ||, allow, hide, rename, or comm occurs in the scope of a time operator in " + process::pp(body) + "."
1020 + "The lineariser cannot handle such processes. ");
1021 }
1022 return pCRL;
1023 }
1024
1025 if (is_sync(body))
1026 {
1029 if ((s1!=multiAction)||(s2!=multiAction))
1030 {
1031 throw mcrl2::runtime_error("Other objects than multi-actions occur in the scope of a synch operator in " + process::pp(body) +".");
1032 }
1033 return multiAction;
1034 }
1035
1036 if (is_action(body))
1037 {
1038 return multiAction;
1039 }
1040
1042 {
1043 determine_process_status(process_instance_assignment(body).identifier(),status);
1044 return status;
1045 }
1046
1047 if (is_delta(body))
1048 {
1049 return pCRL;
1050 }
1051
1052 if (is_tau(body))
1053 {
1054 return multiAction;
1055 }
1056
1057 if (is_hide(body))
1058 {
1059 if (status!=mCRL)
1060 {
1061 throw mcrl2::runtime_error("A hide operator occurs in the scope of recursion, or a condition, choice or sequential operator in " + process::pp(body) +".");
1062 }
1063 determine_process_statusterm(hide(body).operand(),mCRL);
1064 return mCRL;
1065 }
1066
1067 if (is_rename(body))
1068 {
1069 if (status!=mCRL)
1070 {
1071 throw mcrl2::runtime_error("A rename operator occurs in the scope of recursion, or a condition, choice or sequential operator in " + process::pp(body) +".");
1072 }
1074 return mCRL;
1075 }
1076
1077 if (is_allow(body))
1078 {
1079 if (status!=mCRL)
1080 {
1081 throw mcrl2::runtime_error("An allow operator occurs in the scope of recursion, or a condition, choice or sequential operator in " + process::pp(body) +".");
1082 }
1083 determine_process_statusterm(allow(body).operand(),mCRL);
1084 return mCRL;
1085 }
1086
1087 if (is_block(body))
1088 {
1089 if (status!=mCRL)
1090 {
1091 throw mcrl2::runtime_error("A block operator occurs in the scope of recursion, or a condition, choice or sequential operator in " + process::pp(body) +".");
1092 }
1093 determine_process_statusterm(block(body).operand(),mCRL);
1094 return mCRL;
1095 }
1096
1097 throw mcrl2::runtime_error("Process has unexpected format (2) " + process::pp(body) +".");
1098 return error;
1099 }
1100
1101
1103 const process_identifier& procDecl,
1104 const processstatustype status)
1105 {
1107 objectdatatype& object=objectIndex(procDecl);
1108 s=object.processstatus;
1109
1110 if (s==unknown)
1111 {
1112 object.processstatus=status;
1113 if (status==pCRL)
1114 {
1115 determine_process_statusterm(object.processbody,pCRL);
1116 return;
1117 }
1118 /* status==mCRL */
1119 s=determine_process_statusterm(object.processbody,mCRL);
1120 if (s!=status)
1121 {
1122 /* s==pCRL and status==mCRL */
1123 object.processstatus=s;
1124 determine_process_statusterm(object.processbody,pCRL);
1125 }
1126 }
1127 if (s==mCRL)
1128 {
1129 if (status==pCRL)
1130 {
1131 object.processstatus=pCRL;
1132 determine_process_statusterm(object.processbody,pCRL);
1133 }
1134 }
1135 }
1136
1137 /*********** collect pcrlprocessen **********************************/
1138
1139 void collectPcrlProcesses_term(const process_expression& body, // Intentionally not a reference.
1140 std::vector <process_identifier>& pcrlprocesses,
1141 std::set <process_identifier>& visited)
1142 {
1143 if (is_if_then(body))
1144 {
1145 collectPcrlProcesses_term(if_then(body).then_case(),pcrlprocesses,visited);
1146 return;
1147 }
1148
1149 if (is_if_then_else(body))
1150 {
1151 collectPcrlProcesses_term(if_then_else(body).then_case(),pcrlprocesses,visited);
1152 collectPcrlProcesses_term(if_then_else(body).else_case(),pcrlprocesses,visited);
1153 return;
1154 }
1155
1156 if (is_choice(body))
1157 {
1158 collectPcrlProcesses_term(choice(body).left(),pcrlprocesses,visited);
1159 collectPcrlProcesses_term(choice(body).right(),pcrlprocesses,visited);
1160 return ;
1161 }
1162
1163 if (is_seq(body))
1164 {
1165 collectPcrlProcesses_term(seq(body).left(),pcrlprocesses,visited);
1166 collectPcrlProcesses_term(seq(body).right(),pcrlprocesses,visited);
1167 return ;
1168 }
1169
1170 if (is_merge(body))
1171 {
1172 collectPcrlProcesses_term(process::merge(body).left(),pcrlprocesses,visited);
1173 collectPcrlProcesses_term(process::merge(body).right(),pcrlprocesses,visited);
1174 return ;
1175 }
1176
1177 if (is_sync(body))
1178 {
1179 collectPcrlProcesses_term(process::sync(body).left(),pcrlprocesses,visited);
1180 collectPcrlProcesses_term(process::sync(body).right(),pcrlprocesses,visited);
1181 return ;
1182 }
1183
1184 if (is_sum(body))
1185 {
1186 collectPcrlProcesses_term(sum(body).operand(),pcrlprocesses,visited);
1187 return;
1188 }
1189
1190 if (is_stochastic_operator(body))
1191 {
1192 collectPcrlProcesses_term(stochastic_operator(body).operand(),pcrlprocesses,visited);
1193 return;
1194 }
1195
1196 if (is_at(body))
1197 {
1198 collectPcrlProcesses_term(at(body).operand(),pcrlprocesses,visited);
1199 return;
1200 }
1201
1203 {
1204 collectPcrlProcesses(process_instance_assignment(body).identifier(),pcrlprocesses,visited);
1205 return;
1206 }
1207
1208 if (is_hide(body))
1209 {
1210 collectPcrlProcesses_term(hide(body).operand(),pcrlprocesses,visited);
1211 return;
1212 }
1213
1214 if (is_rename(body))
1215 {
1216 collectPcrlProcesses_term(process::rename(body).operand(),pcrlprocesses,visited);
1217 return;
1218 }
1219
1220 if (is_allow(body))
1221 {
1222 collectPcrlProcesses_term(allow(body).operand(),pcrlprocesses,visited);
1223 return;
1224 }
1225
1226 if (is_block(body))
1227 {
1228 collectPcrlProcesses_term(block(body).operand(),pcrlprocesses,visited);
1229 return;
1230 }
1231
1232 if (is_comm(body))
1233 {
1234 collectPcrlProcesses_term(comm(body).operand(),pcrlprocesses,visited);
1235 return;
1236 }
1237
1238 if ((is_delta(body))||
1239 (is_tau(body))||
1240 (is_action(body)))
1241 {
1242 return;
1243 }
1244
1245 throw mcrl2::runtime_error("process has unexpected format (1) " + process::pp(body) +".");
1246 }
1247
1249 const process_identifier& procDecl,
1250 std::vector <process_identifier>& pcrlprocesses,
1251 std::set <process_identifier>& visited)
1252 {
1253 if (visited.count(procDecl)==0)
1254 {
1255 visited.insert(procDecl);
1256 objectdatatype& object=objectIndex(procDecl);
1257 if (object.processstatus==pCRL)
1258 {
1259 pcrlprocesses.push_back(procDecl);
1260 }
1261 collectPcrlProcesses_term(object.processbody,pcrlprocesses,visited);
1262 }
1263 }
1264
1266 const process_identifier& procDecl,
1267 std::vector <process_identifier>& pcrlprocesses)
1268 {
1269 std::set <process_identifier> visited;
1270 collectPcrlProcesses(procDecl, pcrlprocesses, visited);
1271 }
1272
1273 /**************** occursinterm *** occursintermlist ***********/
1274
1275 bool occursinterm(const variable& var, const data_expression& t) const
1276 {
1277 return data::search_free_variable(t, var);
1278 }
1279
1281 const data_expression& t,
1282 const std::set < variable >& vars_set,
1283 std::set < variable >& vars_result_set)
1284 {
1285 if (is_variable(t))
1286 {
1287 const variable& v = atermpp::down_cast<variable>(t);
1288 if (vars_set.find(v)!=vars_set.end())
1289 {
1290 vars_result_set.insert(v);
1291 }
1292 return;
1293 }
1294
1295 if (is_function_symbol(t))
1296 {
1297 return;
1298 }
1299
1300 if (is_machine_number(t))
1301 {
1302 return;
1303 }
1304
1305 if (is_abstraction(t))
1306 {
1307 // mCRL2log(mcrl2::log::warning) << "filtering of variables expression with binders" << std::endl;
1308 return;
1309 }
1310
1311 if (is_where_clause(t))
1312 {
1313 // mCRL2log(mcrl2::log::warning) << "filtering of variables expression with where clause" << std::endl;
1314 return;
1315 }
1316
1317 if (!is_application(t))
1318 {
1319 mCRL2log(mcrl2::log::error) << "term of unexpected type " << t << std::endl;
1320 }
1321
1322 assert(is_application(t));
1323
1324 const application& a=atermpp::down_cast<const application>(t);
1325 filter_vars_by_term(a.head(),vars_set,vars_result_set);
1326 filter_vars_by_termlist(a.begin(),a.end(),vars_set,vars_result_set);
1327 }
1328
1329 bool occursintermlist(const variable& var, const data_expression_list& r) const
1330 {
1331 for (const data_expression& d: r)
1332 {
1333 if (occursinterm(var,d))
1334 {
1335 return true;
1336 }
1337 }
1338 return false;
1339 }
1340
1341 bool occursintermlist(const variable& var, const assignment_list& r, const process_identifier& proc_name) const
1342 {
1343 std::set<variable> assigned_variables;
1344 for (const assignment& l: r)
1345 {
1346 if (occursinterm(var,l.rhs()))
1347 {
1348 return true;
1349 }
1350 assigned_variables.insert(l.lhs());
1351 }
1352 // Check whether x does not occur in the assignment list. Then variable x is assigned to
1353 // itself, and it occurs in the process.
1354 for (const variable& v: objectIndex(proc_name).parameters)
1355 {
1356 if (var==v)
1357 {
1358 if (assigned_variables.count(var)==0) // This variable is not assigned, so it does occur!
1359 {
1360 return true;
1361 }
1362 }
1363 }
1364 return false;
1365 }
1366
1367 template <typename Iterator>
1369 Iterator begin,
1370 const Iterator& end,
1371 const std::set < variable >& vars_set,
1372 std::set < variable >& vars_result_set)
1373 {
1374 for (; begin != end; ++begin)
1375 {
1376 filter_vars_by_term(*begin,vars_set,vars_result_set);
1377 }
1378 }
1379
1381 const action_list& multiaction,
1382 const std::set < variable >& vars_set,
1383 std::set < variable >& vars_result_set)
1384 {
1385 for (const action& ma: multiaction)
1386 {
1387 filter_vars_by_termlist(ma.arguments().begin(), ma.arguments().end(),vars_set,vars_result_set);
1388 }
1389 return;
1390 }
1391
1393 const assignment_list& assignments,
1394 const variable_list& parameters,
1395 const std::set < variable >& vars_set,
1396 std::set < variable >& vars_result_set)
1397 {
1398 const data_expression_list& l=atermpp::container_cast<data_expression_list>(parameters);
1399 filter_vars_by_termlist(l.begin(),l.end(),vars_set,vars_result_set);
1400 for (const assignment& a: assignments)
1401 {
1402 filter_vars_by_term(a.rhs(),vars_set,vars_result_set);
1403 }
1404 }
1405
1407 const process_expression& p,
1408 const bool strict)
1409 {
1410 if (is_choice(p))
1411 {
1412 return occursinpCRLterm(var,choice(p).left(),strict)||
1413 occursinpCRLterm(var,choice(p).right(),strict);
1414 }
1415 if (is_seq(p))
1416 {
1417 return occursinpCRLterm(var,seq(p).left(),strict)||
1418 occursinpCRLterm(var,seq(p).right(),strict);
1419 }
1420 if (is_if_then(p))
1421 {
1422 return occursinterm(var,if_then(p).condition())||
1423 occursinpCRLterm(var,if_then(p).then_case(),strict);
1424 }
1425
1426 if (is_sum(p))
1427 {
1428 if (strict)
1429 return occursintermlist(var,variable_list_to_data_expression_list(sum(p).variables())) ||
1430 occursinpCRLterm(var,sum(p).operand(),strict);
1431 /* below appears better? , but leads
1432 to errors. Should be investigated. */
1433 else
1434 return
1436 occursinpCRLterm(var,sum(p).operand(),strict);
1437 }
1439 {
1440 const stochastic_operator& sto=atermpp::down_cast<const stochastic_operator>(p);
1441 if (strict)
1442 {
1444 occursinterm(var,sto.distribution()) ||
1445 occursinpCRLterm(var,sto.operand(),strict);
1446 }
1447 else
1448 {
1450 (occursinterm(var,sto.distribution()) ||
1451 occursinpCRLterm(var,sto.operand(),strict));
1452 }
1453 }
1455 {
1456 return occursintermlist(var,process_instance_assignment(p).assignments(),process_instance_assignment(p).identifier());
1457 }
1458 if (is_action(p))
1459 {
1460 return occursintermlist(var,action(p).arguments());
1461 }
1462 if (is_sync(p))
1463 {
1464 return occursinpCRLterm(var,process::sync(p).left(),strict)||
1465 occursinpCRLterm(var,process::sync(p).right(),strict);
1466 }
1467 if (is_at(p))
1468 {
1469 return occursinterm(var,at(p).time_stamp()) ||
1470 occursinpCRLterm(var,at(p).operand(),strict);
1471 }
1472 if (is_delta(p))
1473 {
1474 return false;
1475 }
1476 if (is_tau(p))
1477 {
1478 return false;
1479 }
1480 throw mcrl2::runtime_error("unexpected process format in occursinCRLterm " + process::pp(p));
1481 return false;
1482 }
1483
1484 template <class MutableSubstitution>
1486 variable_list& sumvars,
1487 MutableSubstitution& sigma,
1488 const process_expression& p)
1489 {
1490 /* This function replaces the variables in sumvars
1491 by unique ones if these variables occur in the process expression p.
1492 The substitution sigma contains the renaming. */
1493 variable_vector newsumvars;
1494
1495 for (const variable& var: sumvars)
1496 {
1497 if (occursinpCRLterm(var,p,true))
1498 {
1499 const variable newvar=get_fresh_variable(var.name(),var.sort());
1500 newsumvars.push_back(newvar);
1501 sigma[var]=newvar;
1502 }
1503 else
1504 {
1505 newsumvars.push_back(var);
1506 }
1507 }
1508 sumvars=variable_list(newsumvars.begin(), newsumvars.end());
1509 }
1510
1511 template <class MutableSubstitution>
1513 variable_list& sumvars,
1514 MutableSubstitution& sigma,
1515 const variable_list& occurvars,
1516 const data_expression_list& occurterms)
1517 {
1518 /* This function replaces the variables in sumvars
1519 by unique ones if these variables occur in occurvars
1520 or occurterms. It extends rename_vars and rename
1521 terms to rename the replaced variables to new ones. */
1522 variable_list newsumvars;
1523
1524 for (const variable& var: sumvars)
1525 {
1527 occursintermlist(var,occurterms))
1528 {
1529 const variable newvar=get_fresh_variable(var.name(),var.sort());
1530 newsumvars.push_front(newvar);
1531 sigma[var]=newvar;
1532 }
1533 else
1534 {
1535 newsumvars.push_front(var);
1536 }
1537 }
1538 sumvars=reverse(newsumvars);
1539 }
1540
1541 /******************* find_free_variables_process *****************************************/
1542
1543
1544 /* We define our own variant of the standard function find_free_variables, because
1545 find_free_variables is not correctly defined on processes, due to process_instance_assignments,
1546 where variables can occur by not being mentioned. It is necessary to know the parameters of
1547 a process to retrieve these. Concrete example in P() defined by P(x:Nat)= ..., the variable x
1548 appears as a free variable, although it is not explicitly mentioned.
1549 If the standard function find_free_variable on processes is repaired, this function can
1550 be removed */
1551 void find_free_variables_process(const process_expression& p, std::set< variable >& free_variables_in_p)
1552 {
1553 if (is_choice(p))
1554 {
1555 find_free_variables_process(choice(p).left(),free_variables_in_p);
1556 find_free_variables_process(choice(p).right(),free_variables_in_p);
1557 return;
1558 }
1559 if (is_seq(p))
1560 {
1561 find_free_variables_process(seq(p).left(),free_variables_in_p);
1562 find_free_variables_process(seq(p).right(),free_variables_in_p);
1563 return;
1564 }
1565 if (is_sync(p))
1566 {
1567 find_free_variables_process(process::sync(p).left(),free_variables_in_p);
1568 find_free_variables_process(process::sync(p).right(),free_variables_in_p);
1569 return;
1570 }
1571 if (is_if_then(p))
1572 {
1573 for(const variable& v: find_free_variables(if_then(p).condition()))
1574 {
1575 free_variables_in_p.insert(v);
1576 }
1577 find_free_variables_process(if_then(p).then_case(),free_variables_in_p);
1578 return;
1579 }
1580 if (is_if_then_else(p))
1581 {
1582 for(const variable& v: find_free_variables(if_then(p).condition()))
1583 {
1584 free_variables_in_p.insert(v);
1585 }
1586 find_free_variables_process(if_then_else(p).then_case(),free_variables_in_p);
1587 find_free_variables_process(if_then_else(p).else_case(),free_variables_in_p);
1588 return;
1589 }
1590
1591 if (is_sum(p))
1592 {
1593 find_free_variables_process(sum(p).operand(),free_variables_in_p);
1594
1595 for(const variable& v: static_cast<const sum&>(p).variables())
1596 {
1597 free_variables_in_p.erase(v);
1598 }
1599 return;
1600 }
1601
1603 {
1604 const stochastic_operator& sto=down_cast<const stochastic_operator>(p);
1605 find_free_variables_process(sto.operand(),free_variables_in_p);
1606 for(const variable& v: find_free_variables(sto.distribution()))
1607 {
1608 free_variables_in_p.insert(v);
1609 }
1610
1611 for(const variable& v: sto.variables())
1612 {
1613 free_variables_in_p.erase(v);
1614 }
1615 return;
1616 }
1617
1618 if (is_process_instance(p))
1619 {
1620 const process_instance q = atermpp::down_cast<process_instance>(p);
1622 {
1623 free_variables_in_p.insert(v);
1624 }
1625 return;
1626 }
1628 {
1629 const process_instance_assignment q(p);
1631 const variable_list parameters=object.parameters;
1632 std::set<variable> parameter_set(parameters.begin(),parameters.end());
1633 const assignment_list& assignments=q.assignments();
1634 for(const assignment& a: assignments)
1635 {
1636 for(const variable& v: find_free_variables(a.rhs()))
1637 {
1638 free_variables_in_p.insert(v);
1639 }
1640 parameter_set.erase(a.lhs());
1641 }
1642 // Add all remaining variables in the parameter_set, as they have an identity assignment.
1643 for(const variable& v: parameter_set)
1644 {
1645 free_variables_in_p.insert(v);
1646 }
1647 return;
1648 }
1649
1650 if (is_action(p))
1651 {
1652 for(const variable& v: process::find_free_variables(p))
1653 {
1654 free_variables_in_p.insert(v);
1655 }
1656 return;
1657 }
1658
1659 if (is_at(p))
1660 {
1661 for(const variable& v: data::find_free_variables(at(p).time_stamp()))
1662 {
1663 free_variables_in_p.insert(v);
1664 }
1665 find_free_variables_process(at(p).operand(),free_variables_in_p);
1666 return;
1667 }
1668
1669 if (is_delta(p))
1670 {
1671 return;
1672 }
1673
1674 if (is_tau(p))
1675 {
1676 return;
1677 }
1678
1679 if (is_sync(p))
1680 {
1681 find_free_variables_process(process::sync(p).left(),free_variables_in_p);
1682 find_free_variables_process(process::sync(p).right(),free_variables_in_p);
1683 return;
1684 }
1685
1686 if (is_left_merge(p))
1687 {
1688 find_free_variables_process(process::left_merge(p).left(),free_variables_in_p);
1689 find_free_variables_process(process::left_merge(p).right(),free_variables_in_p);
1690 return;
1691 }
1692
1693 if (is_merge(p))
1694 {
1695 find_free_variables_process(process::merge(p).left(),free_variables_in_p);
1696 find_free_variables_process(process::merge(p).right(),free_variables_in_p);
1697 return;
1698 }
1699
1700 if (is_allow(p))
1701 {
1702 find_free_variables_process(process::allow(p).operand(),free_variables_in_p);
1703 return;
1704 }
1705
1706 if (is_comm(p))
1707 {
1708 find_free_variables_process(process::comm(p).operand(),free_variables_in_p);
1709 return;
1710 }
1711
1712 if (is_block(p))
1713 {
1714 find_free_variables_process(process::block(p).operand(),free_variables_in_p);
1715 return;
1716 }
1717
1718 if (is_hide(p))
1719 {
1720 find_free_variables_process(process::hide(p).operand(),free_variables_in_p);
1721 return;
1722 }
1723
1724 if (is_rename(p))
1725 {
1726 find_free_variables_process(process::rename(p).operand(),free_variables_in_p);
1727 return;
1728 }
1729
1730 throw mcrl2::runtime_error("Internal error: expect a pCRL process (1) " + process::pp(p));
1731 }
1732
1734 {
1735 std::set<variable> free_variables_in_p;
1736 find_free_variables_process(p,free_variables_in_p);
1737 return free_variables_in_p;
1738 }
1739
1740 /* Remove assignments that do not appear in the parameter list. */
1742 {
1743 assignment_vector result;
1744 for(const assignment& a: assignments)
1745 {
1746 if (std::find(parameters.begin(),parameters.end(),a.lhs())!=parameters.end()) // found.
1747 {
1748 result.push_back(a);
1749 }
1750 }
1751 return assignment_list(result.begin(),result.end());
1752 }
1753
1754 /* Check whether the assignments occur in the same order in the list of parameters */
1756 const assignment_list& assignments,
1757 const variable_list& parameters)
1758 {
1759 assignment_list::iterator i=assignments.begin();
1760 for(const variable& v: parameters)
1761 {
1762 if (i!=assignments.end() && v==i->lhs())
1763 {
1764 ++i;
1765 }
1766 }
1767 return i==assignments.end();
1768
1769 }
1770
1771 /******************* substitute *****************************************/
1772
1773
1774 template <class Substitution>
1776 const assignment_list& assignments,
1777 const variable_list& parameters,
1778 const bool replacelhs,
1779 const bool replacerhs,
1780 Substitution& sigma)
1781 {
1782 assert(check_assignment_list(assignments, parameters));
1783 /* precondition: the variables in the assignment occur in
1784 the same sequence as in the parameters, which stands for the
1785 total list of parameters.
1786
1787 This function replaces the variables in vars by the terms in terms
1788 in the right hand side of the assignments if replacerhs holds, and
1789 in the lefthandside of an assignment if replacelhs holds. If for some variable
1790 occuring in the parameterlist no assignment is present, whereas
1791 this variable occurs in vars, an assignment for it is added.
1792
1793 It is not possible to use standard substitution functions to replace substitute_assignmentlis
1794 because they do not take the parameters of processes into account.
1795 For instance consider a process P(b:D)=... of which an instance P() exists.
1796 If the substitution sigma(b)=t is applied to P() the result should be P(b=t).
1797 The standard substitutions do not take this parameterlist into account, as it stands.
1798 */
1799
1800 assert(replacelhs==0 || replacelhs==1);
1801 assert(replacerhs==0 || replacerhs==1);
1802 if (parameters.empty())
1803 {
1804 assert(assignments.empty());
1805 return assignments;
1806 }
1807
1808 const variable& parameter=parameters.front();
1809
1810 if (!assignments.empty())
1811 {
1812 const assignment& ass=assignments.front();
1813 variable lhs=ass.lhs();
1814 if (parameter==lhs)
1815 {
1816 /* The assignment refers to parameter par. Substitute its
1817 left and righthandside */
1818 data_expression rhs=ass.rhs();
1819
1820 if (replacelhs)
1821 {
1822 lhs = atermpp::down_cast<variable>(sigma(lhs));
1823 }
1824 if (replacerhs)
1825 {
1827 }
1828
1829 assignment_list result=
1831 assignments.tail(),
1832 parameters.tail(),
1833 replacelhs,
1834 replacerhs,
1835 sigma);
1836 result.push_front(assignment(lhs,rhs));
1837 return result;
1838 }
1839 }
1840
1841 /* Here the first parameter is not equal to the first
1842 assignment. So, we must find out whether a value
1843 for this variable is substituted, that is different
1844 from the variable, in which case an assignment must
1845 be added. */
1846
1847 variable lhs=parameter;
1848 data_expression rhs=parameter;
1849
1850 if (replacelhs)
1851 {
1852 lhs = atermpp::down_cast<data::variable>(sigma(lhs));
1853 }
1854 if (replacerhs)
1855 {
1857 }
1858
1859 if (lhs==rhs)
1860 {
1862 assignments,
1863 parameters.tail(),
1864 replacelhs,
1865 replacerhs,
1866 sigma);
1867 }
1868 assignment_list result=
1870 assignments,
1871 parameters.tail(),
1872 replacelhs,
1873 replacerhs,
1874 sigma);
1875 result.push_front(assignment(lhs,rhs));
1876 return result;
1877 }
1878
1879 // Sort the assignments, such that they have the same order as the parameters
1881 {
1882 std::map<variable,data_expression>assignment_map;
1883 for(const assignment& a: ass)
1884 {
1885 assignment_map[a.lhs()]=a.rhs();
1886 }
1887
1888 assignment_vector result;
1889 for(const variable& v: parameters)
1890 {
1891 const std::map<variable,data_expression>::const_iterator j=assignment_map.find(v);
1892 if (j!=assignment_map.end()) // found
1893 {
1894 result.push_back(assignment(j->first,j->second));
1895 }
1896 }
1897 return assignment_list(result.begin(),result.end());
1898 }
1899
1901 const process_identifier& id,
1902 const assignment_list& assignments)
1903 {
1904 objectdatatype& object=objectIndex(id);
1905 variable_list parameters=object.parameters;
1906 for(const assignment& a: assignments)
1907 {
1908 // Every assignment must occur in the parameter list, in the right sequence.
1909
1910 variable v;
1911 do
1912 {
1913 if (parameters.empty())
1914 {
1915 return false;
1916 }
1917 v=parameters.front();
1918 parameters.pop_front();
1919 }
1920 while (v!=a.lhs());
1921
1922 }
1923 return true;
1924 }
1925
1926 /* The function below calculates sigma(p) and replaces
1927 all variables that are bound by a sum in p by unique
1928 variables */
1929
1930
1931 /* The function below cannot be replace by replace_variables_capture_avoiding although
1932 * the interfaces are the same. As yet it is unclear why, but the difference shows itself
1933 * for instance when linearising lift3_final.mcrl2 and lift3_init.mcrl2 */
1934 template <class Substitution>
1936 const process_expression& p,
1937 Substitution& sigma)
1938 {
1939 if (is_choice(p))
1940 {
1942 if (left==delta_at_zero())
1943 {
1944 return substitute_pCRLproc(choice(p).right(),sigma);
1945 }
1947 if (right==delta_at_zero())
1948 {
1949 return left;
1950 }
1951 return choice(left,right);
1952 }
1953 if (is_seq(p))
1954 {
1956 if (q==delta_at_zero())
1957 {
1958 return q;
1959 }
1960 return seq(q, substitute_pCRLproc(seq(p).right(),sigma));
1961 }
1962 if (is_sync(p))
1963 {
1964 return process::sync(
1967 }
1968 if (is_if_then(p))
1969 {
1971 if (condition==sort_bool::false_())
1972 {
1973 return delta_at_zero();
1974 }
1975 if (condition==sort_bool::true_())
1976 {
1977 return substitute_pCRLproc(if_then(p).then_case(),sigma);
1978 }
1979 return if_then(condition,substitute_pCRLproc(if_then(p).then_case(),sigma));
1980 }
1981 if (is_if_then_else(p))
1982 {
1984 if (condition==sort_bool::false_())
1985 {
1986 return substitute_pCRLproc(if_then_else(p).else_case(),sigma);
1987 }
1988 if (condition==sort_bool::true_())
1989 {
1990 return substitute_pCRLproc(if_then_else(p).then_case(),sigma);
1991 }
1992 return if_then_else(
1993 condition,
1994 substitute_pCRLproc(if_then_else(p).then_case(),sigma),
1995 substitute_pCRLproc(if_then_else(p).else_case(),sigma));
1996 }
1997
1998 if (is_sum(p))
1999 {
2000 variable_list sumargs=sum(p).variables();
2001 variable_list vars;
2003
2004 for( std::map < variable, data_expression >::const_iterator i=sigma.begin(); i!=sigma.end(); ++i)
2005 {
2006 vars=push_back(vars,i->first);
2007 terms=push_back(terms,i->second);
2008 }
2009
2011 alphaconvert(sumargs,local_sigma,vars,terms);
2012
2013 return sum(sumargs,
2014 substitute_pCRLproc(sum(p).operand(),local_sigma));
2015 }
2016
2018 {
2019 const stochastic_operator& sto=down_cast<const stochastic_operator>(p);
2020 variable_list sumargs=sto.variables();
2021 variable_list vars;
2023
2024 for( std::map < variable, data_expression >::const_iterator i=sigma.begin(); i!=sigma.end(); ++i)
2025 {
2026 vars=push_back(vars,i->first);
2027 terms=push_back(terms,i->second);
2028 }
2029
2031 alphaconvert(sumargs,local_sigma,vars,terms);
2032
2033 return stochastic_operator(
2034 sumargs,
2036 substitute_pCRLproc(sto.operand(),local_sigma));
2037 }
2038
2040 {
2041 const process_instance_assignment q(p);
2043 const variable_list parameters=object.parameters;
2044 const assignment_list new_assignments=substitute_assignmentlist(q.assignments(),parameters,false,true,sigma);
2045 assert(check_valid_process_instance_assignment(q.identifier(),new_assignments));
2046 return process_instance_assignment(q.identifier(),new_assignments);
2047 }
2048
2049 if (is_action(p))
2050 {
2051 return action(action(p).label(),
2052 /* data::*/replace_variables_capture_avoiding_alt(action(p).arguments(), sigma));
2053 }
2054
2055 if (is_at(p))
2056 {
2057 return at(substitute_pCRLproc(at(p).operand(),sigma),
2058 /* data::*/replace_variables_capture_avoiding_alt(at(p).time_stamp(),sigma));
2059 }
2060
2061 if (is_delta(p))
2062 {
2063 return p;
2064 }
2065
2066 if (is_tau(p))
2067 {
2068 return p;
2069 }
2070
2071 if (is_sync(p))
2072 {
2073 return process::sync(
2076 }
2077
2078 throw mcrl2::runtime_error("Internal error: expect a pCRL process (2) " + process::pp(p));
2079 return process_expression();
2080 }
2081
2082
2083 // The function below transforms a ProcessAssignment to a Process, provided
2084 // that the process is defined in objectnames.
2085
2087 const process_instance& procId,
2088 const std::set<variable>& bound_variables=std::set<variable>())
2089 {
2090 objectdatatype& object=objectIndex(procId.identifier());
2091 const variable_list process_parameters=object.parameters;
2092 const data_expression_list& rhss=procId.actual_parameters();
2093
2094 assignment_vector new_assignments;
2096 for(variable_list::const_iterator i=process_parameters.begin(); i!=process_parameters.end(); ++i, ++j)
2097 {
2098 assert(j!=rhss.end());
2099 if (*i==*j)
2100 {
2101 if (bound_variables.count(*i)>0) // Now *j is a different variable than *i
2102 {
2103 new_assignments.push_back(assignment(*i,*j));
2104 }
2105 }
2106 else
2107 {
2108 new_assignments.push_back(assignment(*i,*j));
2109 }
2110 }
2111 assert(j==rhss.end());
2112
2113 assert(check_valid_process_instance_assignment(procId.identifier(),assignment_list(new_assignments.begin(),new_assignments.end())));
2114 process_instance_assignment p(procId.identifier(), assignment_list(new_assignments.begin(),new_assignments.end()));
2115 return p;
2116 }
2117
2118 /********************************************************************/
2119 /* */
2120 /* BELOW THE PROCEDURES ARE GIVEN TO TRANSFORM PROCESSES TO */
2121 /* LINEAR PROCESSES. */
2122 /* */
2123 /* */
2124 /* */
2125 /********************************************************************/
2126
2127 typedef enum { first, later } variableposition;
2128
2129 /**************** tovarheadGNF *********************************/
2130
2132 const variable_list& parameters,
2133 const process_expression& body)
2134 {
2135 variable_vector result;
2136 for(const variable& p: parameters)
2137 {
2138 if (occursinpCRLterm(p,body,false))
2139 {
2140 result.push_back(p);
2141 }
2142 }
2143 return variable_list(result.begin(),result.end());
2144 }
2145
2146 // The variable below is used to count the number of new processes that
2147 // are made. If this number is very high, it is likely that the regular
2148 // flag is used, and an unbounded number of new processes are generated.
2149 // In such a case a warning is printed suggesting to use regular2.
2150
2152 const variable_list& parameters,
2153 const process_expression& body,
2154 const processstatustype ps,
2155 const bool canterminate,
2156 const bool containstime)
2157 {
2158 assert(canterminatebody(body)==canterminate);
2159 assert(containstimebody(body)==containstime);
2160
2162 if (searchProcDeclaration(parameters,body,ps, canterminate,containstime,p1))
2163 {
2164 return p1; // The process did already exist. No need to make a new one.
2165 }
2166
2167 static std::size_t numberOfNewProcesses=0, warningNumber=25;
2168 numberOfNewProcesses++;
2169 if (numberOfNewProcesses == warningNumber)
2170 {
2171 mCRL2log(mcrl2::log::warning) << "Generated " << numberOfNewProcesses << " new internal processes.";
2172
2174 {
2175 mCRL2log(mcrl2::log::warning) << " A possible unbounded loop can be avoided by using `regular2' or `stack' as linearisation method." << std::endl;
2176 }
2177 else if (options.lin_method==lmRegular2)
2178 {
2179 mCRL2log(mcrl2::log::warning) << " A possible unbounded loop can be avoided by using `stack' as the linearisation method." << std::endl;
2180 }
2181 else
2182 {
2183 mCRL2log(mcrl2::log::warning) << std::endl;
2184 }
2185 warningNumber=warningNumber*5;
2186 }
2187 const variable_list parameters1=parameters_that_occur_in_body(parameters, body);
2189 const process_identifier p(s, parameters1);
2190 assert(std::string(p.name()).size()>0);
2192 p,
2193 parameters1,
2194 body,
2195 ps,
2196 canterminate,
2197 containstime);
2198 return p;
2199 }
2200
2201
2203 const process_expression& body,
2204 const data_expression& time,
2205 const variable_list& freevars)
2206 {
2207 if (is_choice(body))
2208 {
2209 return choice(
2210 wraptime(choice(body).left(),time,freevars),
2211 wraptime(choice(body).right(),time,freevars));
2212 }
2213
2214 if (is_sum(body))
2215 {
2216 variable_list sumvars=sum(body).variables();
2217 process_expression body1=sum(body).operand();
2218
2220 alphaconvert(sumvars,sigma,freevars,data_expression_list());
2221 body1=substitute_pCRLproc(body1, sigma);
2223 const data_expression time1=/*data::*/replace_variables_capture_avoiding_alt(time, sigma_aux);
2224 body1=wraptime(body1,time1,sumvars+freevars);
2225 return sum(sumvars,body1);
2226 }
2227
2228 if (is_stochastic_operator(body))
2229 {
2230 const stochastic_operator& sto=down_cast<const stochastic_operator>(body);
2231 variable_list sumvars=sto.variables();
2232 process_expression body1=sto.operand();
2233
2235 alphaconvert(sumvars,sigma,freevars,data_expression_list());
2236 body1=substitute_pCRLproc(body1, sigma);
2239 const data_expression time1=replace_variables_capture_avoiding_alt(time, sigma_aux);
2240 body1=wraptime(body1,time1,sumvars+freevars);
2241 return stochastic_operator(sumvars,new_distribution,body1);
2242 }
2243
2244 if (is_if_then(body))
2245 {
2246 return if_then(if_then(body).condition(),wraptime(if_then(body).then_case(),time,freevars));
2247 }
2248
2249 if (is_seq(body))
2250 {
2251 return seq(wraptime(seq(body).left(),time,freevars),seq(body).right());
2252 }
2253
2254 if (is_at(body))
2255 {
2256 /* make a new process */
2257 const process_identifier newproc=newprocess(freevars,body,pCRL,
2261 newproc,
2262 assignment_list()), //data::data_expression_list(objectdata[objectIndex(newproc)].parameters)),
2263 time);
2264 }
2265
2266 if (// (is_process_instance(body))||
2268 (is_sync(body))||
2269 (is_action(body))||
2270 (is_tau(body))||
2271 (is_delta(body)))
2272 {
2273 return at(body,time);
2274 }
2275
2276 throw mcrl2::runtime_error("Internal error: expect a pCRL process in wraptime " + process::pp(body));
2277 return process_expression();
2278 }
2279
2281
2282 variable get_fresh_variable(const std::string& s, const sort_expression& sort, const int reuse_index=-1)
2283 {
2284 /* If reuse_index is smaller than 0 (-1 is the default value), an unused variable name is returned,
2285 based on the string s with sort `sort'. If reuse_index is larger or equal to
2286 0 the reuse_index+1 generated variable is returned. If for a particular reuse_index
2287 this function is called for the first time, it is guaranteed that the returned
2288 variable is unique, and not used as variable elsewhere. Upon subsequent calls
2289 get_fresh_variable will return the same variable for the same s,sort and reuse_triple.
2290 This feature is added to make it possible to avoid generating too many different variables. */
2291
2292
2293 if (reuse_index<0)
2294 {
2296 insertvariable(v,true);
2297 return v;
2298 }
2299 else
2300 {
2301 static std::map < int , std::map < variable,variable > > generated_variables;
2302 variable table_index_term(s,sort);
2303 variable old_variable;
2304 if (generated_variables[reuse_index].count(table_index_term)>0)
2305 {
2306 old_variable=generated_variables[reuse_index][table_index_term];
2307 }
2308 else
2309 {
2310 /* A new variable must be generated */
2311 old_variable=get_fresh_variable(s,sort);
2312 generated_variables[reuse_index][table_index_term]=old_variable;
2313 }
2314 return old_variable;
2315 }
2316 }
2317
2319 {
2320 /* this function returns a list of variables,
2321 corresponding to the sorts in sortlist */
2322
2323 if (sortlist.empty())
2324 {
2325 return variable_list();
2326 }
2327
2328 const sort_expression& sort=sortlist.front();
2329
2330 variable_list result=make_pars(sortlist.tail());
2331 result.push_front(get_fresh_variable("a",sort));
2332 return result;
2333 }
2334
2336 const process_expression& act, // This is a multi-action, actually.
2337 const data_expression& condition,
2338 const process_expression& restterm,
2339 const variable_list& freevars,
2340 const std::set<variable>& variables_bound_in_sum)
2341 {
2342 if (is_if_then(restterm))
2343 {
2344 /* Here we check whether the process body has the form
2345 a (c -> x). For state space generation it turns out
2346 to be beneficial to rewrite this to c-> a x + !c -> a.delta@0, as in
2347 certain cases this leads to a reduction of the number
2348 of states. In this code, we recursively check whether
2349 the action must be distributed over x. This optimisation
2350 was observed by Yaroslav Usenko, May 2006. Implemented by JFG.
2351 On industrial examples, it appears to reduce the state space
2352 with a factor up to 2.
2353 Before october 2008 this code was wrong, as it transformed
2354 an expression to c-> a x, ommitting the a.delta@0. */
2355
2356 const data_expression& c=down_cast<if_then>(restterm).condition();
2357 const process_expression r=choice(
2359 act,
2360 lazy::and_(condition,c),
2361 if_then(restterm).then_case(),
2362 freevars,variables_bound_in_sum),
2364 act,
2365 lazy::and_(condition,lazy::not_(c)),
2366 delta_at_zero(),
2367 freevars,variables_bound_in_sum));
2368 return r;
2369 }
2370 if (is_if_then_else(restterm))
2371 {
2372 /* Here we check whether the process body has the form
2373 a (c -> x <> y). For state space generation it turns out
2374 to be beneficial to rewrite this to c-> a x + !c -> a y, as in
2375 certain cases this leads to a reduction of the number
2376 of states, despite the duplication of the a action. In this code,
2377 we recursively check whether the action must be distributed over
2378 x and y. This optimisation
2379 was observed by Yaroslav Usenko, May 2006. Implemented by JFG.
2380 On industrial examples, it appears to reduce the state space
2381 with a factor up to 2. */
2382
2383 const data_expression& c=down_cast<if_then_else>(restterm).condition();
2384 const process_expression r=choice(
2386 act,
2387 lazy::and_(condition,c),
2388 if_then_else(restterm).then_case(),
2389 freevars,variables_bound_in_sum),
2391 act,
2392 lazy::and_(condition,lazy::not_(c)),
2393 if_then_else(restterm).else_case(),
2394 freevars,variables_bound_in_sum));
2395 return r;
2396 }
2397 const process_expression restterm1=bodytovarheadGNF(restterm,seq_state,freevars,later,variables_bound_in_sum);
2398 return if_then(condition,seq(act,restterm1));
2399 }
2400
2401
2402 assignment_list parameters_to_assignment_list(const variable_list& parameters, const std::set<variable>& variables_bound_in_sum)
2403 {
2404 assignment_vector result;
2405 for(const variable& v: parameters)
2406 {
2407 if (variables_bound_in_sum.count(v)>0)
2408 {
2409 result.push_back(assignment(v,v)); // rhs is another variable than the lhs!!
2410 }
2411 }
2412 return assignment_list(result.begin(),result.end());
2413 }
2414
2415
2417 const process_expression& body,
2418 const state s,
2419 const variable_list& freevars,
2420 const variableposition v,
2421 const std::set<variable>& variables_bound_in_sum)
2422 {
2423 /* it is assumed that we only receive processes with
2424 operators alt, seq, sum_state, cond, name, delta, tau, sync, at and stochastic operator in it */
2425
2426 if (is_choice(body))
2427 {
2428 if (alt_state>=s)
2429 {
2430 const process_expression body1=bodytovarheadGNF(choice(body).left(),alt_state,freevars,first,variables_bound_in_sum);
2431 const process_expression body2=bodytovarheadGNF(choice(body).right(),alt_state,freevars,first,variables_bound_in_sum);
2432 if (isDeltaAtZero(body1))
2433 {
2434 return body2;
2435 }
2436 if (isDeltaAtZero(body2))
2437 {
2438 return body1;
2439 }
2440 return choice(body1,body2);
2441 }
2442 const process_expression body1=bodytovarheadGNF(body,alt_state,freevars,first,variables_bound_in_sum);
2443 const process_identifier newproc=newprocess(freevars,body1,pCRL,
2444 canterminatebody(body1),
2445 containstimebody(body1));
2446 assert(check_valid_process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum)));
2447 return process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum));
2448 }
2449
2450 if (is_sum(body))
2451 {
2452 if (sum_state>=s)
2453 {
2454 variable_list sumvars=sum(body).variables();
2455 process_expression body1=sum(body).operand();
2456
2458 alphaconvert(sumvars,sigma,freevars,data_expression_list());
2459 body1=substitute_pCRLproc(body1,sigma);
2460 std::set<variable> variables_bound_in_sum1=variables_bound_in_sum;
2461 variables_bound_in_sum1.insert(sumvars.begin(),sumvars.end());
2462 body1=bodytovarheadGNF(body1,sum_state,sumvars+freevars,first,variables_bound_in_sum1);
2463 /* Due to the optimisation below, suggested by Yaroslav Usenko, bodytovarheadGNF(...,sum_state,...)
2464 can deliver a process of the form c -> x + !c -> y. In this case, the
2465 sumvars must be distributed over both summands. */
2466 if (is_choice(body1))
2467 {
2468 return choice(sum(sumvars,choice(body1).left()),
2469 sum(sumvars,choice(body1).right()));
2470 }
2471 return sum(sumvars,body1);
2472 }
2473 const process_expression body1=bodytovarheadGNF(body,alt_state,freevars,first,variables_bound_in_sum);
2474 const process_identifier newproc=newprocess(freevars,body1,pCRL,
2475 canterminatebody(body1),
2476 containstimebody(body1));
2477 assert(check_valid_process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum)));
2478 return process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum));
2479 }
2480
2481 if (is_stochastic_operator(body))
2482 {
2483 if (seq_state>=s)
2484 {
2485 const stochastic_operator& sto=down_cast<const stochastic_operator>(body);
2486 variable_list sumvars=sto.variables();
2487 data_expression distribution=sto.distribution();
2488 process_expression body1=sto.operand();
2489
2491 alphaconvert(sumvars,sigma,freevars,data_expression_list());
2492 distribution=/* data::*/replace_variables_capture_avoiding_alt(distribution,sigma);
2493 body1=substitute_pCRLproc(body1,sigma);
2494 std::set<variable> variables_bound_in_sum1=variables_bound_in_sum;
2495 variables_bound_in_sum1.insert(sumvars.begin(),sumvars.end());
2496 body1=bodytovarheadGNF(body1,name_state,sumvars+freevars,v,variables_bound_in_sum1);
2497 /* Due to the optimisation below, suggested by Yaroslav Usenko, bodytovarheadGNF(...,sum_state,...)
2498 can deliver a process of the form c -> x + !c -> y. In this case, the
2499 sumvars must be distributed over both summands. */
2500 return stochastic_operator(sumvars,distribution,body1);
2501 }
2502 const process_expression body_=bodytovarheadGNF(body,seq_state,freevars,first,variables_bound_in_sum);
2503 const process_identifier newproc=newprocess(freevars,body_,pCRL,
2504 canterminatebody(body_),
2505 containstimebody(body_));
2506 assert(check_valid_process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum)));
2507 return process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum));
2508 }
2509
2510 if (is_if_then(body))
2511 {
2512 const data_expression& condition=down_cast<if_then>(body).condition();
2513 const process_expression& body1=down_cast<if_then>(body).then_case();
2514
2515 if (s<=sum_state)
2516 {
2517 return if_then(
2518 condition,
2519 bodytovarheadGNF(body1,seq_state,freevars,first,variables_bound_in_sum));
2520 }
2521 const process_expression body2=bodytovarheadGNF(body,alt_state,freevars,first,variables_bound_in_sum);
2522 const process_identifier newproc=newprocess(freevars,body2,pCRL,
2523 canterminatebody(body2),
2524 containstimebody(body2));
2525 assert(check_valid_process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum)));
2526 return process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum));
2527
2528 }
2529
2530 if (is_if_then_else(body))
2531 {
2532 const data_expression& condition=down_cast<if_then_else>(body).condition();
2533 const process_expression& body1=down_cast<if_then_else>(body).then_case();
2534 const process_expression& body2=down_cast<if_then_else>(body).else_case();
2535
2536 if (isDeltaAtZero(body1) && isDeltaAtZero(body2))
2537 {
2538 return body1;
2539 }
2540
2541 if ((s<=sum_state) && ((isDeltaAtZero(body1))||(isDeltaAtZero(body2))))
2542 {
2543 if (isDeltaAtZero(body2))
2544 {
2545 return if_then(
2546 condition,
2547 bodytovarheadGNF(body1,seq_state,freevars,first,variables_bound_in_sum));
2548 }
2549 /* body1=="Delta@0" */
2550 {
2551 return if_then(
2552 lazy::not_(condition),
2553 bodytovarheadGNF(body2,seq_state,freevars,first,variables_bound_in_sum));
2554 }
2555 }
2556 if (alt_state==s) /* body1!=Delta@0 and body2!=Delta@0 */
2557 {
2558 return
2559 choice(
2560 if_then(
2561 condition,
2562 bodytovarheadGNF(body1,seq_state,freevars,first,variables_bound_in_sum)),
2563 if_then(
2564 lazy::not_(condition),
2565 bodytovarheadGNF(body2,seq_state,freevars,first,variables_bound_in_sum)));
2566 }
2567 const process_expression body3=bodytovarheadGNF(body,alt_state,freevars,first,variables_bound_in_sum);
2568 const process_identifier newproc=newprocess(freevars,body3,pCRL,
2569 canterminatebody(body3),
2570 containstimebody(body3));
2571 assert(check_valid_process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum)));
2572 return process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum));
2573
2574 }
2575
2576 if (is_seq(body))
2577 {
2578 process_expression body1=seq(body).left();
2579 process_expression body2=seq(body).right();
2580
2581 if (s<=seq_state)
2582 {
2583 body1=bodytovarheadGNF(body1,name_state,freevars,v,variables_bound_in_sum);
2584 if (!canterminatebody(body1))
2585 {
2586 /* In this case there is no need to investigate body2, as it cannot be reached. */
2587 return body1;
2588 }
2589 if ((is_if_then(body2)) && (s<=sum_state))
2590 {
2591 /* Here we check whether the process body has the form
2592 a (c -> x) + !c -> delta@0. For state space generation it turns out
2593 to be beneficial to rewrite this to c-> a x, as in
2594 certain cases this leads to a reduction of the number
2595 of states. An extra change (24/12/2006) is that the
2596 conditions are distributed recursively over
2597 all conditions. The optimisation
2598 was observed by Yaroslav Usenko, May 2006. Implemented by JFG.
2599 On industrial examples, it appears to reduce the state space
2600 with a factor up to 2. Until 1/11/2008 this code was incorrect,
2601 because the summand a (!c -> delta@0) was not forgotten.*/
2602
2603 const data_expression& c=down_cast<if_then>(body2).condition();
2604
2605 const process_expression r= choice(
2606 distributeActionOverConditions(body1,c,if_then(body2).then_case(),freevars,variables_bound_in_sum),
2607 distributeActionOverConditions(body1,lazy::not_(c),delta_at_zero(),freevars,variables_bound_in_sum));
2608 return r;
2609 }
2610 if ((is_if_then_else(body2)) && (s<=sum_state))
2611 {
2612
2613 /* Here we check whether the process body has the form
2614 a (c -> x <> y). For state space generation it turns out
2615 to be beneficial to rewrite this to c-> a x + !c -> a y, as in
2616 certain cases this leads to a reduction of the number
2617 of states, despite the duplication of the a action. An extra
2618 change (24/12/2006) is that the conditions are distributed recursively over
2619 all conditions. The optimisation
2620 was observed by Yaroslav Usenko, May 2006. Implemented by JFG.
2621 On industrial examples, it appears to reduce the state space
2622 with a factor up to 2. */
2623
2624
2625 const data_expression& c=down_cast<if_then_else>(body2).condition();
2626 const process_expression r= choice(
2627 distributeActionOverConditions(body1,c,if_then_else(body2).then_case(),freevars,variables_bound_in_sum),
2628 distributeActionOverConditions(body1,lazy::not_(c),if_then_else(body2).else_case(),freevars,variables_bound_in_sum));
2629 return r;
2630 }
2631 body2=bodytovarheadGNF(body2,seq_state,freevars,later,variables_bound_in_sum);
2632 return seq(body1,body2);
2633 }
2634 body1=bodytovarheadGNF(body,alt_state,freevars,first,variables_bound_in_sum);
2635 const process_identifier newproc=newprocess(freevars,body1,pCRL,canterminatebody(body1),
2636 containstimebody(body1));
2637
2638 assert(check_valid_process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum)));
2639 return process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum));
2640 }
2641
2642 if (is_action(body))
2643 {
2644 if ((s==multiaction_state)||(v==first))
2645 {
2646 return body;
2647 }
2648
2649 bool isnew=false;
2650 objectdatatype& object=addMultiAction(action(body),isnew);
2651
2652 if (object.process_representing_action==process_identifier())
2653 {
2654 /* this action does not yet have a corresponding process, which
2655 must be constructed. The resulting process is stored in
2656 the variable process_representing_action in objectdata. Tempvar below is
2657 needed as objectdata may be realloced as a side effect
2658 of newprocess */
2659 const process_identifier tempvar=newprocess(
2660 object.parameters,
2661 object.processbody,
2662 GNF,true,false);
2663 object.process_representing_action=tempvar;
2664 }
2666 process_instance(object.process_representing_action,
2667 action(body).arguments()));
2668 }
2669
2670 if (is_sync(body))
2671 {
2672 bool isnew=false;
2673 const process_expression& body1=down_cast<process::sync>(body).left();
2674 const process_expression& body2=down_cast<process::sync>(body).right();
2676 bodytovarheadGNF(body1,multiaction_state,freevars,v,variables_bound_in_sum),
2677 bodytovarheadGNF(body2,multiaction_state,freevars,v,variables_bound_in_sum));
2678
2680 if ((s==multiaction_state)||(v==first))
2681 {
2682 return mp;
2683 }
2684
2685 objectdatatype& object=addMultiAction(mp,isnew);
2686
2687 if (object.process_representing_action==process_identifier())
2688 {
2689 /* this action does not yet have a corresponding process, which
2690 must be constructed. The resulting process is stored in
2691 the variable process_representing_action in objectdata. Tempvar below is needed
2692 as objectdata may be realloced as a side effect of newprocess */
2694 object.parameters,
2695 object.processbody,
2696 GNF,true,false);
2697 object.process_representing_action=tempvar;
2698 }
2701 process_identifier(object.process_representing_action),
2702 getarguments(ma)));
2703 }
2704
2705 if (is_at(body))
2706 {
2708 at(body).operand(),
2709 s,
2710 freevars,
2711 first,variables_bound_in_sum);
2712 data_expression time=data_expression(at(body).time_stamp());
2713 /* put the time operator around the first action or process */
2714 body1=wraptime(body1,time,freevars);
2715 if (v==first)
2716 {
2717 return body1;
2718 }
2719
2720 /* make a new process, containing this process */
2721 const process_identifier newproc=newprocess(freevars,body1,pCRL,
2722 canterminatebody(body1),
2723 containstimebody(body1));
2724 assert(check_valid_process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum)));
2725 return process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum));
2726 }
2727
2729 {
2730 // return transform_process_assignment_to_process(body);
2731 return body;
2732 }
2733
2734
2735 if (is_tau(body))
2736 {
2737 if (v==first)
2738 {
2739 return tau();
2740 }
2743 }
2744
2745 if (is_delta(body))
2746 {
2747 if (v==first)
2748 {
2749 return body;
2750 }
2753 }
2754
2755 throw mcrl2::runtime_error("unexpected process format in bodytovarheadGNF " + process::pp(body) +".");
2756 return process_expression();
2757 }
2758
2759 void procstovarheadGNF(const std::vector < process_identifier>& procs)
2760 {
2761 /* transform the processes in procs into pre-Greibach Normal Form */
2762 for (const process_identifier& i: procs)
2763 {
2764 objectdatatype& object=objectIndex(i);
2765
2766 // The intermediate variable result is needed here
2767 // because objectdata can be reallocated as a side
2768 // effect of bodytovarheadGNF.
2769
2770 std::set<variable> variables_bound_in_sum;
2771 const process_expression result=
2773 object.processbody,
2774 alt_state,
2775 object.parameters,
2776 first,
2777 variables_bound_in_sum);
2778 object.processbody=result;
2779 }
2780 }
2781
2782 /**************** towards real GREIBACH normal form **************/
2783
2785
2787 {
2788 if (is_choice(body1))
2789 {
2790 return choice(
2791 putbehind(choice(body1).left(),body2),
2792 putbehind(choice(body1).right(),body2));
2793 }
2794
2795 if (is_seq(body1))
2796 {
2797 return seq(seq(body1).left(), putbehind(seq(body1).right(),body2));
2798 }
2799
2800 if (is_if_then(body1))
2801 {
2802 return if_then(if_then(body1).condition(),putbehind(if_then(body1).then_case(),body2));
2803 }
2804
2805 if (is_sum(body1))
2806 {
2807 /* we must take care that no variables in body2 are
2808 inadvertently bound */
2809 variable_list sumvars=sum(body1).variables();
2810
2812 alphaconvertprocess(sumvars,sigma,body2);
2813 return sum(sumvars,
2814 putbehind(substitute_pCRLproc(sum(body1).operand(), sigma),
2815 body2));
2816 }
2817
2818 if (is_stochastic_operator(body1))
2819 {
2820 const stochastic_operator& sto=atermpp::down_cast<stochastic_operator>(body1);
2821 variable_list stochvars=sto.variables();
2822
2823 // See explanation at sum operator above.
2825 alphaconvertprocess(stochvars,sigma,body2);
2826 return stochastic_operator(
2827 stochvars,
2828 sto.distribution(),
2829 putbehind(
2831 body2));
2832 }
2833
2834
2835 if (is_action(body1))
2836 {
2837 return seq(body1,body2);
2838 }
2839
2840 if (is_sync(body1))
2841 {
2842 return seq(body1,body2);
2843 }
2844
2846 {
2847 return seq(body1,body2);
2848 }
2849
2850 if (is_delta(body1))
2851 {
2852 return body1;
2853 }
2854
2855 if (is_tau(body1))
2856 {
2857 return seq(body1,body2);
2858 // throw mcrl2::runtime_error("Expect only multiactions, not a tau.");
2859 }
2860
2861 if (is_at(body1))
2862 {
2863 return seq(body1,body2);
2864 }
2865
2866 throw mcrl2::runtime_error("Internal error. Unexpected process format in putbehind " + process::pp(body1) +".");
2867 return process_expression();
2868 }
2869
2870
2872 const process_expression& body1,
2873 const data_expression& condition)
2874 {
2875 if (is_choice(body1))
2876 {
2877 return choice(
2878 distribute_condition(choice(body1).left(),condition),
2879 distribute_condition(choice(body1).right(),condition));
2880 }
2881
2882 if (is_seq(body1))
2883 {
2884 return if_then(condition,body1);
2885 }
2886
2887 if (is_if_then(body1))
2888 {
2889 return if_then(
2890 lazy::and_(if_then(body1).condition(),condition),
2891 if_then(body1).then_case());
2892 }
2893
2894 if (is_sum(body1))
2895 {
2896 /* we must take care that no variables in condition are
2897 inadvertently bound */
2898 variable_list sumvars=sum(body1).variables();
2900 alphaconvert(sumvars,sigma,variable_list(), data_expression_list({ condition }));
2901 return sum(
2902 sumvars,
2904 substitute_pCRLproc(sum(body1).operand(),sigma),
2905 condition));
2906 }
2907
2908 if (is_stochastic_operator(body1))
2909 {
2910 /* we must take care that no variables in condition are
2911 inadvertently bound */
2912 const stochastic_operator& sto=atermpp::down_cast<stochastic_operator>(body1);
2913 variable_list stochvars=sto.variables();
2915 alphaconvert(stochvars,sigma,variable_list(), data_expression_list({ condition }));
2916
2917 return stochastic_operator(
2918 stochvars,
2919 if_(condition,
2921 sto.distribution(),
2922 sigma),
2926 condition));
2927 }
2928
2929 if (is_at(body1)||
2930 is_action(body1)||
2931 is_sync(body1)||
2933 is_delta(body1)||
2934 is_tau(body1))
2935 {
2936 return if_then(condition,body1);
2937 }
2938
2939 throw mcrl2::runtime_error("Internal error. Unexpected process format in distribute condition " + process::pp(body1) +".");
2940 }
2941
2942 /* This process calculates the equivalent of sum sumvars dist stochvars[distribution] body
2943 where the distribution occurs in front. This can only be done under limited circumstances.
2944 Assume we can enumerate the values of sumvars by e1,...,en. Introduce n copies of stochvars,
2945 i.e., stochvars1,...,stochvarsn. The new process becomes
2946
2947 dist stochvars1,stochvars2,...,stochvarsn
2948 [distribution(e1,stochvars1)*distribution(e2,stochvars2)*...*distribution(en,stochvarsn)].
2949
2950 body(e1,stochvars1)+
2951 body(e2,stochvars2)+
2952 ...
2953 body(en,stochvarsn)
2954 */
2955
2957 const variable_list& sumvars,
2958 const variable_list& stochvars,
2959 const data_expression& distribution,
2960 const process_expression& body)
2961 {
2962 if (options.norewrite)
2963 {
2964 throw mcrl2::runtime_error("The use of the rewriter must be allowed to distribute a sum operator over a distribution.");
2965 }
2966
2967
2968 std::vector < data_expression_vector > data_vector(1,data_expression_vector());
2969 for(const variable& v:sumvars)
2970 {
2971 std::vector < data_expression_vector > new_data_vector;
2972
2973 if (!data.is_certainly_finite(v.sort()))
2974 {
2975 throw mcrl2::runtime_error("Cannot distribute a sum variable of non finite sort " + pp(v.sort()) + " over a distribution, which is required for linearisation.");
2976 }
2977
2978 for(const data_expression& d: enumerate_expressions(v.sort(),data,rewr))
2979 {
2980 for(const data_expression_vector& dv: data_vector)
2981 {
2982 data_expression_vector new_dv=dv;
2983 new_dv.push_back(d);
2984 new_data_vector.push_back(new_dv);
2985 }
2986
2987 }
2988 data_vector.swap(new_data_vector);
2989
2990 }
2991 assert(!data_vector.empty());
2992
2993 process_expression resulting_body;
2994 data_expression resulting_distribution;
2995 variable_list resulting_stochastic_variables;
2996 bool result_defined=false;
2997 for(const data_expression_vector& d: data_vector)
2998 {
3000 variable_list vl = stochvars;
3001 alphaconvert(vl,sigma,stochvars,data_expression_list());
3002 data_expression_vector::const_iterator i=d.begin();
3003 for(const variable& v: sumvars)
3004 {
3005 sigma[v] = *i;
3006 ++i;
3007 }
3009 const data_expression new_distribution=replace_variables_capture_avoiding_alt(distribution, sigma);
3010
3011 if (result_defined)
3012 {
3013 resulting_body=choice(resulting_body, d1);
3014 resulting_distribution=sort_real::times(resulting_distribution,new_distribution);
3015 resulting_stochastic_variables=resulting_stochastic_variables + vl;
3016 }
3017 else
3018 {
3019 resulting_body=d1;
3020 resulting_distribution=new_distribution;
3021 resulting_stochastic_variables=vl;
3022 result_defined=true;
3023 }
3024 }
3025 /* Put the distribution in front. */
3026
3027 return stochastic_operator(resulting_stochastic_variables, resulting_distribution, resulting_body);
3028 }
3029
3031 const variable_list& sumvars,
3032 const variable_list& stochastic_variables,
3033 const data_expression& distribution,
3034 const process_expression& body)
3035 {
3036 if (is_sum(body)||
3037 is_choice(body)||
3038 is_seq(body)||
3039 is_if_then(body)||
3040 is_sync(body)||
3041 is_action(body)||
3042 is_tau(body)||
3043 is_at(body)||
3045 isDeltaAtZero(body))
3046 {
3047 return enumerate_distribution_and_sums(sumvars,stochastic_variables,distribution,body);
3048 }
3049
3050 if (is_delta(body)||
3051 is_tau(body))
3052 {
3053 return body;
3054 }
3055
3056 if (is_stochastic_operator(body))
3057 {
3058 const stochastic_operator& sto=atermpp::down_cast<stochastic_operator>(body);
3060 variable_list inner_stoch_vars=sto.variables();
3061 alphaconvert(inner_stoch_vars,sigma,sumvars,data_expression_list());
3062 const process_expression new_body=substitute_pCRLproc(sto.operand(), sigma);
3065 stochastic_variables + inner_stoch_vars,
3066 sort_real::times(distribution,new_distribution), new_body);
3067 }
3068
3069 throw mcrl2::runtime_error("Internal error. Unexpected process format in distribute_sum " + process::pp(body) +".");
3070 return process_expression();
3071 }
3072
3074 const variable_list& sumvars,
3075 const process_expression& body1)
3076 {
3077 if (is_choice(body1))
3078 {
3079 return choice(
3080 distribute_sum(sumvars,choice(body1).left()),
3081 distribute_sum(sumvars,choice(body1).right()));
3082 }
3083
3084 if (is_seq(body1)||
3085 is_if_then(body1)||
3086 is_sync(body1)||
3087 is_action(body1)||
3088 is_tau(body1)||
3089 is_at(body1)||
3091 isDeltaAtZero(body1))
3092 {
3093 return sum(sumvars,body1);
3094 }
3095
3096 if (is_sum(body1))
3097 {
3099 variable_list inner_sumvars=sum(body1).variables();
3100 alphaconvert(inner_sumvars,sigma,sumvars,data_expression_list());
3101 const process_expression new_body1=substitute_pCRLproc(sum(body1).operand(), sigma);
3102 return sum(sumvars+inner_sumvars,new_body1);
3103 }
3104
3105 if (is_stochastic_operator(body1))
3106 {
3107 const stochastic_operator& sto=atermpp::down_cast<stochastic_operator>(body1);
3109 variable_list inner_stoch_vars=sto.variables();
3110 alphaconvert(inner_stoch_vars,sigma,sumvars,data_expression_list());
3111 const process_expression new_body1=substitute_pCRLproc(sto.operand(), sigma);
3113 return distribute_sum_over_a_stochastic_operator(sumvars, inner_stoch_vars, new_distribution, new_body1);
3114 }
3115
3116 if (is_delta(body1)||
3117 is_tau(body1))
3118 {
3119 return body1;
3120 }
3121
3122 throw mcrl2::runtime_error("Internal error. Unexpected process format in distribute_sum " + process::pp(body1) +".");
3123 return process_expression();
3124 }
3125
3127 const std::vector < process_instance_assignment >& s1,
3128 const std::vector < process_instance_assignment >& s2,
3129 const bool regular2)
3130 {
3131 /* s1 and s2 are sequences of typed variables of
3132 the form Process(ProcVarId("P2",[SortId("Bit"),
3133 SortId("Bit")]),[OpId("b1",SortId("Bit")),OpId("b1",SortId("Bit"))]).
3134 This function yields true if the names and types of
3135 the processes in s1 and s2 match. */
3136
3137 std::vector < process_instance_assignment >::const_iterator i2=s2.begin();
3138 for (std::vector < process_instance_assignment >::const_iterator i1=s1.begin();
3139 i1!=s1.end(); ++i1,++i2)
3140 {
3141 if (i2==s2.end())
3142 {
3143 return false;
3144 }
3145 if (regular2)
3146 {
3147 if (i1->identifier()!=i2->identifier())
3148 {
3149 return false;
3150 }
3151 }
3152 else
3153 {
3154 if (*i1!=*i2)
3155 {
3156 return false;
3157 }
3158 }
3159 }
3160 if (i2!=s2.end())
3161 {
3162 return false;
3163 }
3164 return true;
3165 }
3166
3168 const std::vector < process_instance_assignment >& process_names,
3169 process_identifier& result)
3170 {
3171 std::vector < std::vector < process_instance_assignment > >::const_iterator rwalker=representedprocesses.begin();
3172 for (std::vector < process_identifier >::const_iterator walker=seq_varnames.begin();
3173 walker!=seq_varnames.end(); ++walker,++rwalker)
3174 {
3175 assert(rwalker!=representedprocesses.end());
3176 const process_identifier process=*walker;
3177 if (match_sequence(process_names,*rwalker,options.lin_method==lmRegular2))
3178 {
3179 result=process;
3180 return true;
3181 }
3182 }
3183 assert(rwalker==representedprocesses.end());
3184 return false;
3185 }
3186
3188 const process_expression& sequence,
3189 std::vector < process_instance_assignment >& result)
3190 {
3191 if (is_action(sequence)||is_process_instance_assignment(sequence))
3192 {
3193 result.push_back(atermpp::down_cast<process_instance_assignment>(sequence));
3194 return;
3195 }
3196
3197 if (is_stochastic_operator(sequence))
3198 {
3199 const stochastic_operator& sto=atermpp::down_cast<stochastic_operator>(sequence);
3200 extract_names(sto.operand(),result);
3201 return;
3202 }
3203
3204 if (is_seq(sequence))
3205 {
3206 const process_expression& first=down_cast<seq>(sequence).left();
3208 {
3209 result.push_back(atermpp::down_cast<process_instance_assignment>(first));
3210 objectdatatype& object=objectIndex(atermpp::down_cast<process_instance_assignment>(first).identifier());
3211 if (object.canterminate)
3212 {
3213 extract_names(seq(sequence).right(),result);
3214 }
3215 return;
3216 }
3217 }
3218
3219 throw mcrl2::runtime_error("Internal error. Expected sequence of process names (1) " + process::pp(sequence) + ".");
3220 }
3221
3223 {
3224 /* we expect that oldbody is a sequence of process instances */
3225
3226 if (is_process_instance_assignment(oldbody))
3227 {
3229 const variable_list parameters=objectIndex(procId).parameters;
3232 return parameters;
3233 }
3234
3235 if (is_seq(oldbody))
3236 {
3237 const process_expression& first=down_cast<seq>(oldbody).left();
3239 {
3241 if (object.canterminate)
3242 {
3244 const variable_list pars=parscollect(seq(oldbody).right(),newbody);
3245 variable_list pars1, pars2;
3246 const variable_list new_pars=construct_renaming(pars,objectIndex(procId).parameters,pars1,pars2,false);
3247 assignment_vector new_assignment;
3248 for(variable_list::const_iterator i=pars2.begin(), j=new_pars.begin(); i!=pars2.end(); ++i,++j)
3249 {
3250 assert(j!=new_pars.end());
3251 new_assignment.push_back(assignment(*i,*j));
3252 }
3253 assert(check_valid_process_instance_assignment(procId,assignment_list(new_assignment.begin(),new_assignment.end())));
3254 newbody=seq(process_instance_assignment(procId,assignment_list(new_assignment.begin(),new_assignment.end())),newbody);
3255 const variable_list result=pars1+pars;
3256 assert(std::set<variable>(result.begin(),result.end()).size()==result.size()); // all elements in the result are unique.
3257 return result;
3258 }
3259 else
3260 {
3261 return parscollect(first,newbody);
3262 }
3263 }
3264 }
3265
3266 throw mcrl2::runtime_error("Internal error. Expected a sequence of process names (2) " + process::pp(oldbody) +".");
3267 return variable_list();
3268 }
3269
3271 const process_expression& t,
3272 const variable_list& vl,
3273 const std::set<variable>& variables_bound_in_sum)
3274 {
3275 assignment_vector result;
3276 for(const variable& v: vl)
3277 {
3278 if (variables_bound_in_sum.count(v)>0 && occursinpCRLterm(v,t,false))
3279 {
3280 result.push_back(assignment(v,v)); // Here an identity assignment is used, as it is possible
3281 // that the *i at the lhs is not the same variable as *i at the rhs.
3282 }
3283 }
3284 return assignment_list(result.begin(),result.end());
3285 }
3286
3288 {
3290 {
3291 const process_instance_assignment p(t);
3293
3294 const variable_list pars=object.parameters; // These are the old parameters of the process.
3295 assert(pars.size()<=vl.size());
3296
3297 std::map<variable,data_expression>sigma;
3298 for(const assignment& a: p.assignments())
3299 {
3300 sigma[a.lhs()]=a.rhs();
3301 }
3302
3303 assignment_list result;
3304 for(variable_list::const_iterator i=pars.begin(); i!=pars.end(); ++i, vl.pop_front())
3305 {
3306 assert(!vl.empty());
3307 const data_expression new_rhs=make_map_substitution(sigma)(*i);
3308 result.push_front(assignment(vl.front(),new_rhs)); // Identity assignments are stored as lhs and rhs may
3309 // refer to different variables.
3310 }
3311 return reverse(result);
3312 }
3313
3314 if (is_seq(t))
3315 {
3316 const process_instance_assignment firstproc=atermpp::down_cast<process_instance_assignment>(seq(t).left());
3317 objectdatatype& object=objectIndex(firstproc.identifier());
3318 const assignment_list first_assignment=argscollect_regular2(firstproc,vl);
3319 if (object.canterminate)
3320 {
3321 return first_assignment + argscollect_regular2(seq(t).right(),vl);
3322 }
3323 return first_assignment;
3324 }
3325
3326 throw mcrl2::runtime_error("Internal error. Expected a sequence of process names (3) " + process::pp(t) +".");
3327 }
3328
3330 {
3332 {
3333 return t;
3334 }
3335
3337 {
3338 const stochastic_operator& sto=atermpp::down_cast<const stochastic_operator>(t);
3340 }
3341
3342 if (is_seq(t))
3343 {
3344 const process_expression& firstproc=down_cast<seq>(t).left();
3345 objectdatatype& object=objectIndex(process_instance_assignment(firstproc).identifier());
3346 if (object.canterminate)
3347 {
3348 return seq(firstproc,cut_off_unreachable_tail(seq(t).right()));
3349 }
3350 return firstproc;
3351 }
3352
3353 throw mcrl2::runtime_error("Internal error. Expected a sequence of process names (4) " + process::pp(t) +".");
3354 return process_expression();
3355 }
3356
3358 process_expression sequence,
3359 std::vector <process_identifier>& todo,
3360 const variable_list& freevars,
3361 const std::set<variable>& variables_bound_in_sum)
3362 {
3363 process_identifier new_process;
3364
3365 /* Sequence consists of a sequence of process references,
3366 concatenated with the sequential composition operator, which may be preceded by
3367 a stochastic operator. */
3368 if (is_stochastic_operator(sequence))
3369 {
3370 /* Leave the stochastic operators in place */
3371 const stochastic_operator& sto=atermpp::down_cast<const stochastic_operator>(sequence);
3372 std::set<variable> variables_bound_in_sum_new=variables_bound_in_sum;
3373 variables_bound_in_sum_new.insert(sto.variables().begin(),sto.variables().end());
3374 return stochastic_operator(sto.variables(),
3375 sto.distribution(),
3376 create_regular_invocation(sto.operand(),todo,freevars+sto.variables(),variables_bound_in_sum_new));
3377 }
3378
3379 sequence=cut_off_unreachable_tail(sequence);
3380 sequence=pCRLrewrite(sequence);
3381 std::vector < process_instance_assignment > process_names;
3382 extract_names(sequence,process_names);
3383 assert(!process_names.empty());
3384
3385 if (process_names.size()==1)
3386 {
3387 /* length of list equals 1 */
3388 if (is_process_instance_assignment(sequence))
3389 {
3390 return sequence;
3391 }
3392 if (is_seq(sequence))
3393 {
3394 return seq(sequence).left();
3395 }
3396 throw mcrl2::runtime_error("Internal error. Expected a sequence of process names " + process::pp(sequence) +".");
3397 }
3398 /* There is more than one process name in the sequence,
3399 so, we must replace them by a single name */
3400
3401 /* We first start out by searching whether
3402 there is already a variable with a matching sequence
3403 of variables */
3404 if (!exists_variable_for_sequence(process_names,new_process))
3405 {
3406 /* There does not exist an appropriate variable,
3407 so, make it and return its index in n */
3408 process_expression newbody;
3410 {
3411 variable_list pars=parscollect(sequence,newbody);
3412 new_process=newprocess(pars,newbody,pCRL,
3413 canterminatebody(newbody),
3414 containstimebody(newbody));
3415 representedprocesses.push_back(process_names);
3416 }
3417 else
3418 {
3419 new_process=newprocess(freevars,sequence,pCRL,
3420 canterminatebody(sequence),containstimebody(sequence));
3421 representedprocesses.push_back(process_names);
3422 }
3423 seq_varnames.push_back(new_process);
3424 todo.push_back(new_process);
3425 }
3426 /* now we must construct arguments */
3427 variable_list parameters=objectIndex(new_process).parameters;
3429 {
3430 const assignment_list args=argscollect_regular2(sequence,parameters);
3431 assert(check_valid_process_instance_assignment(new_process,args));
3432 const process_expression p=process_instance_assignment(new_process,args);
3433 return p;
3434 }
3435 else
3436 {
3437 assert(check_valid_process_instance_assignment(new_process,argscollect_regular(sequence,parameters,variables_bound_in_sum)));
3438 return process_instance_assignment(new_process,argscollect_regular(sequence,parameters,variables_bound_in_sum));
3439 }
3440 }
3441
3443 const process_expression& t,
3444 std::vector <process_identifier>& todo,
3445 const variable_list& freevars,
3446 const std::set<variable>& variables_bound_in_sum)
3447 /* t has the form of the sum, and condition over actions
3448 each followed by a sequence of variables. We replace
3449 this variable by a single one, putting the new variable
3450 on the todo list, to be transformed to regular form also. */
3451 {
3452 if (is_choice(t))
3453 {
3454 const process_expression t1=to_regular_form(choice(t).left(),todo,freevars,variables_bound_in_sum);
3455 const process_expression t2=to_regular_form(choice(t).right(),todo,freevars,variables_bound_in_sum);
3456 return choice(t1,t2);
3457 }
3458
3459 if (is_seq(t))
3460 {
3461 const process_expression& firstact=down_cast<seq>(t).left();
3462 assert(is_at(firstact)||is_tau(firstact)||is_action(firstact)||is_sync(firstact));
3463 /* the sequence of variables in
3464 the second argument must be replaced */
3465 return seq(firstact,create_regular_invocation(seq(t).right(),todo,freevars,variables_bound_in_sum));
3466 }
3467
3468 if (is_if_then(t))
3469 {
3470 return if_then(if_then(t).condition(),to_regular_form(if_then(t).then_case(),todo,freevars,variables_bound_in_sum));
3471 }
3472
3473 if (is_sum(t))
3474 {
3475 variable_list sumvars=sum(t).variables();
3476
3478 alphaconvert(sumvars,sigma,freevars,data_expression_list());
3479 const process_expression body=substitute_pCRLproc(sum(t).operand(), sigma);
3480
3481 std::set<variable> variables_bound_in_sum1=variables_bound_in_sum;
3482 variables_bound_in_sum1.insert(sumvars.begin(),sumvars.end());
3483 return sum(sumvars,
3485 body,
3486 todo,
3487 sumvars+freevars,
3488 variables_bound_in_sum1));
3489 }
3490
3492 {
3493 const stochastic_operator& sto=down_cast<const stochastic_operator>(t);
3494 variable_list sumvars=sto.variables();
3495
3497 alphaconvert(sumvars,sigma,freevars,data_expression_list());
3499 sto.distribution(),
3500 sigma);
3502
3503 std::set<variable> variables_bound_in_sum1=variables_bound_in_sum;
3504 variables_bound_in_sum1.insert(sumvars.begin(),sumvars.end());
3505 return stochastic_operator(
3506 sumvars,
3507 distribution,
3509 body,
3510 todo,
3511 sumvars+freevars,
3512 variables_bound_in_sum1));
3513 }
3514
3515 if (is_sync(t)||is_action(t)||is_delta(t)||is_tau(t)||is_at(t))
3516 {
3517 return t;
3518 }
3519
3520 throw mcrl2::runtime_error("to regular form expects GNF " + process::pp(t) +".");
3521 return process_expression();
3522 }
3523
3525 const process_expression& body,
3526 const data_expression& time,
3527 const variable_list& freevars,
3528 data_expression& timecondition)
3529 {
3530 if (is_choice(body))
3531 {
3532 return choice(
3533 distributeTime(choice(body).left(),time,freevars,timecondition),
3534 distributeTime(choice(body).right(),time,freevars,timecondition));
3535 }
3536
3537 if (is_sum(body))
3538 {
3539 variable_list sumvars=sum(body).variables();
3540 process_expression body1=sum(body).operand();
3542 alphaconvert(sumvars,sigma,freevars,data_expression_list());
3543 body1=substitute_pCRLproc(body1, sigma);
3545 const data_expression time1=replace_variables_capture_avoiding_alt(time,sigma_aux);
3546 body1=distributeTime(body1,time1,sumvars+freevars,timecondition);
3547 return sum(sumvars,body1);
3548 }
3549
3550 if (is_stochastic_operator(body))
3551 {
3552 const stochastic_operator& sto=down_cast<const stochastic_operator>(body);
3553 variable_list sumvars=sto.variables();
3555 alphaconvert(sumvars,sigma,freevars,data_expression_list());
3559 const data_expression time1=/*data::*/replace_variables_capture_avoiding_alt(time,sigma_aux);
3560 new_body=distributeTime(new_body,time1,sumvars+freevars,timecondition);
3561 return stochastic_operator(sumvars,new_distribution,new_body);
3562 }
3563
3564 if (is_if_then(body))
3565 {
3566 data_expression timecondition=sort_bool::true_();
3568 if_then(body).then_case(),
3569 time,
3570 freevars,
3571 timecondition);
3572
3573 return if_then(
3574 lazy::and_(data_expression(if_then(body).condition()),timecondition),
3575 body1);
3576 }
3577
3578 if (is_seq(body))
3579 {
3580 return seq(
3581 distributeTime(seq(body).left(), time,freevars,timecondition),
3582 seq(body).right());
3583 }
3584
3585 if (is_at(body))
3586 {
3587 /* make a new process */
3588 timecondition=equal_to(time,data_expression(at(body).time_stamp()));
3589 return body;
3590 }
3591
3592 if ((is_sync(body))||
3593 (is_action(body))||
3594 (is_tau(body))||
3595 (is_delta(body)))
3596 {
3597 return at(body,time);
3598 }
3599
3600 throw mcrl2::runtime_error("Internal error: expect a pCRL process in distributeTime " + process::pp(body) +".");
3601 return process_expression();
3602 }
3603
3605 const process_expression& body,
3607 std::vector <process_identifier>& todo,
3608 const bool regular,
3609 processstatustype mode,
3610 const variable_list& freevars,
3611 const std::set <variable>& variables_bound_in_sum)
3612 /* This process delivers the transformation of body
3613 to GNF with actions as a head symbol, or it
3614 delivers NULL if body is not a pCRL process.
3615 If regular=1, then an attempt is made to obtain a
3616 GNF where one action is always followed by a
3617 variable. */
3618 {
3619 if (is_at(body))
3620 {
3621 data_expression timecondition=sort_bool::true_();
3623 at(body).operand(),
3624 first,
3625 todo,
3626 regular,
3627 mode,
3628 freevars,
3629 variables_bound_in_sum);
3630 return distributeTime(
3631 body1,
3632 at(body).time_stamp(),
3633 freevars,
3634 timecondition);
3635 }
3636
3637 if (is_choice(body))
3638 {
3639 const process_expression body1=procstorealGNFbody(choice(body).left(),first,todo,
3640 regular,mode,freevars,variables_bound_in_sum);
3641 const process_expression body2=procstorealGNFbody(choice(body).right(),first,todo,
3642 regular,mode,freevars,variables_bound_in_sum);
3643 return choice(body1,body2);
3644 }
3645
3646 if (is_seq(body))
3647 {
3648 const process_expression body1=procstorealGNFbody(seq(body).left(),v,
3649 todo,regular,mode,freevars,variables_bound_in_sum);
3650 const process_expression body2=procstorealGNFbody(seq(body).right(),later,
3651 todo,regular,mode,freevars,variables_bound_in_sum);
3652 process_expression t3=putbehind(body1,body2);
3653 if ((regular) && (v==first))
3654 {
3655 /* We must transform t3 to regular form */
3656 t3=to_regular_form(t3,todo,freevars,variables_bound_in_sum);
3657 }
3658 return t3;
3659 }
3660
3661 if (is_if_then(body))
3662 {
3664 procstorealGNFbody(if_then(body).then_case(),first,
3665 todo,regular,mode,freevars,variables_bound_in_sum),
3666 if_then(body).condition());
3667 return r;
3668 }
3669
3670 if (is_sum(body))
3671 {
3672 const variable_list sumvars=sum(body).variables();
3673 std::set<variable> variables_bound_in_sum1=variables_bound_in_sum;
3674 variables_bound_in_sum1.insert(sumvars.begin(),sumvars.end());
3675 return distribute_sum(sumvars,
3676 procstorealGNFbody(sum(body).operand(),first,
3677 todo,regular,mode,sumvars+freevars,variables_bound_in_sum1));
3678 }
3679
3680 if (is_stochastic_operator(body))
3681 {
3682 const stochastic_operator& sto=down_cast<const stochastic_operator>(body);
3683 const variable_list& sumvars=sto.variables();
3684 std::set<variable> variables_bound_in_sum1=variables_bound_in_sum;
3685 variables_bound_in_sum1.insert(sumvars.begin(),sumvars.end());
3686 return stochastic_operator(
3687 sumvars,
3688 sto.distribution(),
3690 todo,regular,mode,sumvars+freevars,variables_bound_in_sum1));
3691 }
3692
3693 if (is_action(body))
3694 {
3695 return body;
3696 }
3697
3698 if (is_sync(body))
3699 {
3700 return body;
3701 }
3702
3704 {
3706
3707 if (v==later)
3708 {
3709 if (regular)
3710 {
3711 mode=mCRL;
3712 }
3713 todo.push_back(t);
3714 /* if ((!regular)||(mode=mCRL))
3715 todo.push_back(t);
3716 / * single = in `mode=mCRL' is important, otherwise crash
3717 I do not understand the reason for this at this moment
3718 JFG (9/5/2000) */
3719 return body;
3720 }
3721
3722 objectdatatype& object=objectIndex(t);
3723 if (object.processstatus==mCRL)
3724 {
3725 todo.push_back(t);
3726 return process_expression();
3727 }
3728 /* The variable is a pCRL process and v==first, so,
3729 we must now substitute */
3730 procstorealGNFrec(t,first,todo,regular);
3731
3733 for(const assignment& a: static_cast<const process_instance_assignment&>(body).assignments())
3734 {
3735 sigma[a.lhs()]=a.rhs();
3736 }
3737 process_expression t3=substitute_pCRLproc(object.processbody,sigma);
3738 if (regular)
3739 {
3740 t3=to_regular_form(t3,todo,freevars,variables_bound_in_sum);
3741 }
3742
3743 return t3;
3744 }
3745
3746 if (is_delta(body))
3747 {
3748 return body;
3749 }
3750
3751 if (is_tau(body))
3752 {
3753 return body;
3754 }
3755
3756 if (is_merge(body))
3757 {
3759 todo,regular,mode,freevars,variables_bound_in_sum);
3761 todo,regular,mode,freevars,variables_bound_in_sum);
3762 return process_expression();
3763 }
3764
3765 if (is_hide(body))
3766 {
3767 procstorealGNFbody(hide(body).operand(),later,todo,regular,mode,freevars,variables_bound_in_sum);
3768 return process_expression();
3769 }
3770
3771 if (is_rename(body))
3772 {
3773 procstorealGNFbody(process::rename(body).operand(),later,todo,regular,mode,freevars,variables_bound_in_sum);
3774 return process_expression();
3775 }
3776
3777 if (is_allow(body))
3778 {
3779 procstorealGNFbody(allow(body).operand(),later,todo,regular,mode,freevars,variables_bound_in_sum);
3780 return process_expression();
3781 }
3782
3783 if (is_block(body))
3784 {
3785 procstorealGNFbody(block(body).operand(),later,todo,regular,mode,freevars,variables_bound_in_sum);
3786 return process_expression();
3787 }
3788
3789 if (is_comm(body))
3790 {
3791 procstorealGNFbody(comm(body).operand(),later,todo,regular,mode,freevars,variables_bound_in_sum);
3792 return process_expression();
3793 }
3794
3795 // The exception below can especially trigger on a process_instance as these ought to have
3796 // been removed earlier in favour of process_instance_assignments.
3797 throw mcrl2::runtime_error("unexpected process format in procstorealGNF " + process::pp(body) +".");
3798 return process_expression();
3799 }
3800
3801
3803 const process_identifier& procIdDecl,
3804 const variableposition v,
3805 std::vector <process_identifier>& todo,
3806 const bool regular)
3807
3808 /* Do a depth first search on process variables and substitute
3809 for the headvariable of a pCRL process, in case it is a process,
3810 such that we obtain a Greibach Normal Form. All pCRL processes will
3811 be labelled with GNF to indicate that they are in
3812 Greibach Normal Form. */
3813
3814 {
3815 objectdatatype& object=objectIndex(procIdDecl);
3816 if (object.processstatus==pCRL)
3817 {
3818 object.processstatus=GNFbusy;
3819 std::set<variable> variables_bound_in_sum;
3820 const process_expression t=procstorealGNFbody(object.processbody,first,
3821 todo,regular,pCRL,object.parameters,variables_bound_in_sum);
3822 if (object.processstatus!=GNFbusy)
3823 {
3824 throw mcrl2::runtime_error("There is something wrong with recursion.");
3825 }
3826
3827 object.processbody=t;
3828 object.processstatus=GNF;
3829 return;
3830 }
3831
3832 if (object.processstatus==mCRL)
3833 {
3834 object.processstatus=mCRLbusy;
3835 std::set<variable> variables_bound_in_sum;
3836 const process_expression t=procstorealGNFbody(object.processbody,first,todo,
3837 regular,mCRL,object.parameters,variables_bound_in_sum);
3838 /* if the last result is not equal to NULL,
3839 the body of this process is itself a processidentifier */
3840
3841 object.processstatus=mCRLdone;
3842 return;
3843 }
3844
3845 if ((object.processstatus==GNFbusy) && (v==first))
3846 {
3847 throw mcrl2::runtime_error("Unguarded recursion in process " + process::pp(procIdDecl) +".");
3848 }
3849
3850 if ((object.processstatus==GNFbusy)||
3851 (object.processstatus==GNF)||
3852 (object.processstatus==mCRLdone)||
3853 (object.processstatus==multiAction))
3854 {
3855 return;
3856 }
3857
3858 if (object.processstatus==mCRLbusy)
3859 {
3860 throw mcrl2::runtime_error("Unguarded recursion in process " + process::pp(procIdDecl) +".");
3861 }
3862
3863 throw mcrl2::runtime_error("strange process type: " + std::to_string(object.processstatus));
3864 }
3865
3866 void procstorealGNF(const process_identifier& procsIdDecl,
3867 const bool regular)
3868 {
3869 std::vector <process_identifier> todo;
3870 todo.push_back(procsIdDecl);
3871 for (; !todo.empty() ;)
3872 {
3873 const process_identifier pi=todo.back();
3874 todo.pop_back();
3875 procstorealGNFrec(pi,first,todo,regular);
3876 }
3877 }
3878
3879
3880 /**************** GENERaTE LPE **********************************/
3881 /* */
3882 /* */
3883 /* */
3884 /* */
3885 /* */
3886 /* */
3887 /****************************************************************/
3888
3889
3890 /**************** Make pCRL procs ******************************/
3891
3892 /* make_pCRL_procs searches for all process identifiers reachable from id in pCRL equations. */
3893
3895 std::set<process_identifier>& reachable_process_identifiers)
3896 {
3897 if (reachable_process_identifiers.count(id)==0) // not found
3898 {
3899 reachable_process_identifiers.insert(id);
3900 make_pCRL_procs(objectIndex(id).processbody,reachable_process_identifiers);
3901 }
3902 return;
3903 }
3904
3906 std::set<process_identifier>& reachable_process_identifiers)
3907 {
3908 if (is_choice(t))
3909 {
3910 make_pCRL_procs(choice(t).left(),reachable_process_identifiers);
3911 make_pCRL_procs(choice(t).right(),reachable_process_identifiers);
3912 return;
3913 }
3914
3915 if (is_seq(t))
3916 {
3917 make_pCRL_procs(seq(t).left(),reachable_process_identifiers);
3918 make_pCRL_procs(seq(t).right(),reachable_process_identifiers);
3919 return;
3920 }
3921
3922 if (is_if_then(t))
3923 {
3924 make_pCRL_procs(if_then(t).then_case(),reachable_process_identifiers);
3925 return;
3926 }
3927
3928 if (is_sum(t))
3929 {
3930 make_pCRL_procs(sum(t).operand(),reachable_process_identifiers);
3931 return;
3932 }
3933
3935 {
3936 make_pCRL_procs(stochastic_operator(t).operand(),reachable_process_identifiers);
3937 return;
3938 }
3939
3941 {
3942 make_pCRL_procs(atermpp::down_cast<process_instance_assignment>(t).identifier(), reachable_process_identifiers);
3943 return;
3944 }
3945
3946 if (is_sync(t)||is_action(t)||is_tau(t)||is_delta(t)||is_at(t))
3947 {
3948 return;
3949 }
3950
3951 throw mcrl2::runtime_error("unexpected process format " + process::pp(t) + " in make_pCRL_procs");
3952 }
3953
3954 /**************** minimize_set_of_reachable_process_identifiers ******************************/
3955
3956 /* The process identifiers in reachable_process_identifiers have bodies and parameters lists
3957 * that can be the same. If that is the case, they are identified. The right hand sides are substituted to
3958 * reflect the identified processes. Example: P1 = a.P2, P3=a.P4, P2=b.Q, P4=b.Q. This reduces to P1 = a.P2, P2 = b.Q.
3959 * The result is a map from remaining process identifiers to their adapted process bodies.
3960 * The initial process was part of the reachable_process_identifiers and must be part of the map.
3961 */
3962
3963 /* set_proc_identifier_map is an auxiliary procedure that effectively sets identfier_identifier_map[id1]:=... or
3964 identifier_identifier_map[id1]:=... in such a way that they map to the same element. In this way identifier_identifier_map
3965 acts as a representation of the equivalence set induced by equations id1==id2. A constraint is that
3966 initial_process may never occur as a left hand side. An invariant of identifier_identifier_map is
3967 that no id1 that occurs at the right of the map can occur at the left.
3968 */
3969
3970 process_identifier get_last(const process_identifier& id, const std::map< process_identifier, process_identifier >& identifier_identifier_map)
3971 {
3972 process_identifier target_id=id;
3973 bool ready=false;
3974 do
3975 {
3976 const std::map< process_identifier, process_identifier >::const_iterator i=identifier_identifier_map.find(target_id);
3977 if (i==identifier_identifier_map.end())
3978 {
3979 ready=true;
3980 }
3981 else
3982 {
3983 target_id=i->second;
3984 }
3985 }
3986 while (!ready);
3987 return target_id;
3988 }
3989
3990
3992 std::map< process_identifier, process_identifier >& identifier_identifier_map,
3993 const process_identifier& id1_,
3994 const process_identifier& id2_,
3995 const process_identifier& initial_process)
3996 {
3997 assert(id1_!=id2_);
3998
3999 /* Take care that id1 is the last identifier or that id2 is arger than id1. This guarantees
4000 that there will be no loops in the mapping of identifiers. */
4001 // make_substitution sigma(identifier_identifier_map);
4002 process_identifier id1= get_last(id1_,identifier_identifier_map);
4003 process_identifier id2= get_last(id2_,identifier_identifier_map);
4004 if (id1==initial_process)
4005 {
4006 id1.swap(id2);
4007 }
4008 if (id1!=id2)
4009 {
4010 identifier_identifier_map[id1]=id2;
4011 }
4012 }
4013
4014 void complete_proc_identifier_map(std::map< process_identifier, process_identifier >& identifier_identifier_map)
4015 {
4016 std::map< process_identifier, process_identifier > new_identifier_identifier_map;
4017 for(const std::pair<const process_identifier, process_identifier >& p: identifier_identifier_map)
4018 {
4019 new_identifier_identifier_map[p.first]=get_last(p.second,identifier_identifier_map);
4020 }
4021 identifier_identifier_map.swap(new_identifier_identifier_map);
4022#ifndef NDEBUG
4023 /* In the result no right hand side occurs as the left hand side of identifier_identifier_map */
4024 typedef std::pair< const process_identifier, process_identifier > identifier_identifier_pair;
4025 for(const identifier_identifier_pair& p: identifier_identifier_map)
4026 {
4027 assert(identifier_identifier_map.count(p.second)==0);
4028 }
4029#endif
4030 }
4031
4033 {
4036 const std::map< process_identifier, process_identifier >& m_map;
4037
4038 make_substitution(const std::map< process_identifier, process_identifier >& map)
4039 : m_map(map)
4040 {}
4041
4043 {
4044 const std::map< process_identifier, process_identifier >::const_iterator i=m_map.find(id);
4045 if (i==m_map.end()) // Not found
4046 {
4047 return id;
4048 }
4049 return i->second;
4050 }
4051 };
4052
4053 std::set< process_identifier >
4054 minimize_set_of_reachable_process_identifiers(const std::set<process_identifier>& reachable_process_identifiers,
4055 const process_identifier& initial_process)
4056 {
4057 assert(reachable_process_identifiers.count(initial_process)>0);
4058 typedef std::pair< variable_list, process_expression > parameters_process_pair;
4059 typedef std::map< std::pair< variable_list, process_expression >, process_identifier > mapping_type;
4060 typedef std::pair<const std::pair< variable_list, process_expression >, process_identifier > mapping_type_pair;
4061
4062 /* First put the identifiers in reachable_process_identifiers in the process mapping */
4063 mapping_type process_mapping;
4064 std::map< process_identifier, process_identifier > identifier_identifier_map;
4065 for(const process_identifier& id: reachable_process_identifiers)
4066 {
4067 objectdatatype& object=objectIndex(id);
4068 const parameters_process_pair p(object.parameters,object.processbody);
4069 mapping_type::const_iterator i=process_mapping.find(p);
4070 if (i==process_mapping.end()) // Not found.
4071 {
4072 process_mapping[p]=id;
4073 }
4074 else
4075 {
4076 set_proc_identifier_map(identifier_identifier_map,id,i->second,initial_process);
4077 }
4078 }
4079 complete_proc_identifier_map(identifier_identifier_map);
4080
4081 while (!identifier_identifier_map.empty())
4082 {
4083 /* Move the elements of process_mapping into new_process_mapping, under application
4084 * of the identifier_identifier_map. If this yields new process_identifiers to be identified store
4085 * these in new_proc_identifier_map. Do this until no process_identifiers are mapped onto
4086 * each other anymore. */
4087 std::map< process_identifier, process_identifier > new_identifier_identifier_map;
4088 mapping_type new_process_mapping;
4089 for(const mapping_type_pair& p: process_mapping)
4090 {
4091 const make_substitution sigma(identifier_identifier_map);
4092 const parameters_process_pair p_new(p.first.first, replace_process_identifiers( p.first.second, sigma));
4093 mapping_type::const_iterator i=new_process_mapping.find(p_new);
4094 if (i==new_process_mapping.end()) // Not found.
4095 {
4096 new_process_mapping[p_new]=sigma(p.second);
4097 }
4098 else
4099 {
4100 set_proc_identifier_map(new_identifier_identifier_map,sigma(p.second),sigma(i->second),initial_process);
4101 }
4102 }
4103 complete_proc_identifier_map(new_identifier_identifier_map);
4104 identifier_identifier_map.swap(new_identifier_identifier_map);
4105 process_mapping.swap(new_process_mapping);
4106 }
4107
4108 /* Store the pairs from process mapping into result */
4109 /* std::map< process_identifier, process_expression > result;
4110 for(const mapping_type_pair& p: process_mapping)
4111 {
4112 result[p.second]=p.first.second;
4113 }
4114 assert(result.count(initial_process)>0);
4115 return result; */
4116 std::set< process_identifier > result;
4117 for(const mapping_type_pair& p: process_mapping)
4118 {
4119 result.insert(p.second);
4120 objectdatatype& object=objectIndex(p.second);
4121 object.processbody=p.first.second;
4122 }
4123 assert(result.count(initial_process)>0);
4124 return result;
4125 }
4126
4127 /**************** remove_stochastic_operators_from_front **********************/
4128
4130 {
4131 protected:
4134
4135 public:
4138 {}
4139
4140 process_pid_pair(const process_pid_pair& other) = default;
4144
4146 {
4147 return m_process_body;
4148 }
4149
4151 {
4152 return m_pid;
4153 }
4154 }; // end process_pid_pair
4155
4156 /* Take the process expressions from process_equations and transform these such that
4157 no process equation has an initial distribution. The changed equations are reported
4158 back, including adapted parameter lists because sometimes the variables bound in the
4159 stochastic operators need to be added.
4160 The initial distribution of the initial process is reported back in
4161 initial_stochastic_distribution, including if necessary adapted sets of arguments. */
4162
4163 std::set< process_identifier >
4165 const std::set< process_identifier >& reachable_process_identifiers,
4166 process_identifier& initial_process_id, // If parameters change, another initial_process_id will be substituted.
4167 stochastic_distribution& initial_stochastic_distribution)
4168 {
4169 /* First obtain the stochastic distribution for each process variable. */
4170 std::map< process_identifier, process_pid_pair > processes_with_stochastic_distribution_first;
4171 std::set< process_identifier > result;
4172 for(const process_identifier& p: reachable_process_identifiers)
4173 {
4174 objectdatatype& object = objectIndex(p);
4175 process_expression proc_=obtain_initial_distribution_term(object.processbody);
4176 if (!is_stochastic_operator(proc_))
4177 {
4178 processes_with_stochastic_distribution_first.insert(std::pair< process_identifier, process_pid_pair >(p, process_pid_pair(proc_,p)));
4179 result.insert(p);
4180 }
4181 else
4182 {
4183 const stochastic_operator& proc=down_cast<const stochastic_operator>(proc_);
4184 assert(!is_process_instance_assignment(proc.operand()));
4185 objectdatatype& object=objectIndex(p);
4187 variable_list vars=proc.variables();
4188 alphaconvert(vars,local_sigma, vars + object.parameters, data_expression_list());
4189
4190 const process_identifier newproc=newprocess(
4191 vars + object.parameters,
4192 process::replace_variables_capture_avoiding_with_an_identifier_generator(proc.operand(),
4193 local_sigma,
4195 pCRL,
4196 canterminatebody(proc.operand()),
4197 containstimebody(proc.operand()));
4198 // calculate the substitution to be applied on proc.distribution() which is moved outside the
4199 // body the process.
4200 processes_with_stochastic_distribution_first.insert(
4201 std::pair< process_identifier, process_pid_pair >
4202 (p,
4204 vars,
4205 replace_variables_capture_avoiding_alt(proc.distribution(), local_sigma),
4206 proc.operand()),
4207 newproc)));
4208 result.insert(newproc);
4209 }
4210 }
4211
4212 for(const process_identifier& p: reachable_process_identifiers)
4213 {
4214 objectdatatype& object=objectIndex(processes_with_stochastic_distribution_first.at(p).process_id());
4215 assert(!is_stochastic_operator(object.processbody));
4216 object.processbody=transform_initial_distribution_term(object.processbody,processes_with_stochastic_distribution_first);
4217 assert(!is_stochastic_operator(object.processbody));
4218 }
4219
4220 // Adapt the initial process
4221 process_expression initial_distribution=processes_with_stochastic_distribution_first.at(initial_process_id).process_body();
4223 {
4224 const stochastic_operator sto=atermpp::down_cast<stochastic_operator>(initial_distribution);
4225 initial_stochastic_distribution = stochastic_distribution(sto.variables(), sto.distribution());
4226 initial_process_id=processes_with_stochastic_distribution_first.at(initial_process_id).process_id();
4227 }
4228 else
4229 {
4230 initial_stochastic_distribution = stochastic_distribution(variable_list(), sort_real::real_one());
4231 }
4232
4233 return result;
4234 }
4235
4236
4237 /**************** Collectparameterlist ******************************/
4238
4240 {
4241 /* Note: variables can be different, although they have the
4242 same string, due to different types. If they have the
4243 same string, but different types, the conflict must
4244 be resolved by renaming the name of the variable */
4245
4246 if (vl.empty())
4247 {
4248 return false;
4249 }
4250 const variable& var1=vl.front();
4251 assert(is_variable(var1));
4252
4253 /* The variable with correct type is present: */
4254 if (var==var1)
4255 {
4256 return true;
4257 }
4258
4259 assert(var.name()!=var1.name());
4260
4261 /* otherwise it can be present in vl */
4262 return alreadypresent(var,vl.tail());
4263 }
4264
4266 const variable_list& par2)
4267 {
4268 if (par2.empty())
4269 {
4270 return par1;
4271 }
4272
4273 variable var2=par2.front();
4274 assert(is_variable(var2));
4275
4276 variable_list result=joinparameters(par1,par2.tail());
4277 if (alreadypresent(var2,par1))
4278 {
4279 return result;
4280 }
4281
4282 result.push_front(var2);
4283 return result;
4284 }
4285
4286 variable_list collectparameterlist(const std::set< process_identifier >& pCRLprocs)
4287 {
4288 variable_list parameters;
4289 for (const process_identifier& p: pCRLprocs)
4290 {
4291 const objectdatatype& object=objectIndex(p);
4292 parameters=joinparameters(parameters,object.parameters);
4293 }
4294 return parameters;
4295 }
4296
4297 /**************** Declare local datatypes ******************************/
4298
4300 const std::set < process_identifier >& pCRLprocs)
4301 {
4302 create_enumeratedtype(pCRLprocs.size());
4303 }
4304
4306 {
4307 public:
4318
4319 // Stack operations are not supposed to be copied.
4322
4325 {
4326 parameter_list=pl;
4328 spec.stack_operations_list=this;
4329
4330 //create structured sort
4331 // Stack = struct emptystack?is_empty
4332 // | push(getstate: Pos, getx1: S1, ..., getxn: Sn, pop: Stack)
4333 // ;
4334
4335 basic_sort stack_sort_alias(spec.fresh_identifier_generator("Stack"));
4337 for (const variable& v: pl)
4338 {
4339 sp_push_arguments.push_back(structured_sort_constructor_argument(spec.fresh_identifier_generator("get" + std::string(v.name())), v.sort()));
4340 sorts.push_front(v.sort());
4341 }
4342 sp_push_arguments.push_back(structured_sort_constructor_argument(spec.fresh_identifier_generator("pop"), stack_sort_alias));
4344 structured_sort_constructor sc_push(spec.fresh_identifier_generator("push"), sp_push_arguments);
4345 structured_sort_constructor sc_emptystack(spec.fresh_identifier_generator("emptystack"),spec.fresh_identifier_generator("isempty"));
4346
4347 structured_sort_constructor_vector constructors(1,sc_push);
4348 constructors.push_back(sc_emptystack);
4349 //add data declarations for structured sort
4350 spec.data.add_alias(alias(stack_sort_alias,structured_sort(constructors)));
4351 stacksort=data::normalize_sorts(stack_sort_alias,spec.data);
4352 push=sc_push.constructor_function(stack_sort_alias);
4353 emptystack=sc_emptystack.constructor_function(stack_sort_alias);
4354 empty=sc_emptystack.recogniser_function(stack_sort_alias);
4355 const std::vector< data::function_symbol > projection_functions =
4356 sc_push.projection_functions(stack_sort_alias);
4357 pop=projection_functions.back();
4358 getstate=projection_functions.front();
4359 get=function_symbol_list(projection_functions.begin()+1,projection_functions.end()-1);
4360 }
4361
4363 {
4364 }
4365 };
4366
4368 {
4369 public:
4373 std::size_t no_of_states;
4374 /* the boolean state variables occur in reverse
4375 order, i.e. the least significant first, whereas
4376 in parameter lists, the order is reversed. */
4378
4379
4380 /* All datatypes for different stacks that are being generated
4381 are stored in the following list, such that it can be investigated
4382 whether a suitable stacktype already exist, before generating a new
4383 one */
4384
4387
4388
4392 {
4393 if (stack_operations_list==nullptr)
4394 {
4395 return nullptr;
4396 }
4398 {
4399 return stack_operations_list;
4400 }
4402 }
4403
4407 const bool regular,
4408 const std::set < process_identifier >& pCRLprocs,
4409 const bool singlecontrolstate)
4410 {
4411 assert(pCRLprocs.size()>0);
4412 parameters=parlist;
4413
4414 no_of_states=pCRLprocs.size();
4415 process_identifier last= *pCRLprocs.begin();
4416 const std::string s3((spec.options.statenames)?std::string(last.name()):std::string("s"));
4417 if ((spec.options.binary) && (spec.options.newstate))
4418 {
4419 std::size_t i=spec.upperpowerof2(no_of_states);
4420 for (; i>0 ; i--)
4421 {
4423 spec.insertvariable(name,true);
4425 }
4426 }
4427
4428 if (regular)
4429 {
4430 opns=nullptr;
4431 if (spec.options.newstate)
4432 {
4433 if (!spec.options.binary)
4434 {
4435 if (!singlecontrolstate)
4436 {
4437 const std::size_t e=spec.create_enumeratedtype(no_of_states);
4439 }
4440 else
4441 {
4442 /* Generate a stackvariable that is never used */
4444 }
4445 }
4446 else
4447 {
4449 }
4450 }
4451 else
4452 {
4454 }
4455 spec.insertvariable(stackvar,true);
4456 }
4457 else
4458 {
4459 if (spec.options.newstate)
4460 {
4461 throw mcrl2::runtime_error("cannot combine stacks with " +
4462 (spec.options.binary?std::string("binary"):std::string("an enumerated type")));
4463 }
4465
4466 if (opns!=nullptr)
4467 {
4469 spec.insertvariable(stackvar,true);
4470 }
4471 else
4472 {
4473 variable_list temp=parlist;
4474 temp.push_front(variable("state",sort_pos::pos()));
4475 opns=(stackoperations*) new stackoperations(temp,spec);
4477 spec.insertvariable(stackvar,true);
4478 }
4479 }
4480 }
4481
4483 {
4484 }
4485
4486 };
4487
4489 {
4490 return is_variable(d) && global_variables.count(atermpp::down_cast<variable>(d)) > 0;
4491 }
4492
4494 const stacklisttype& stack)
4495 {
4496 /* first search whether the variable is a free process variable */
4497 if (global_variables.count(var)>0)
4498 {
4499 return var;
4500 }
4501
4502 /* otherwise find out whether the variable matches a parameter */
4503 function_symbol_list::const_iterator getmappings=stack.opns->get.begin();
4504 for (variable_list::const_iterator walker=stack.parameters.begin() ;
4505 walker!=stack.parameters.end() ; ++walker,++getmappings)
4506 {
4507 if (*walker==var)
4508 {
4509 return application(*getmappings,stack.stackvar);
4510 }
4511 assert(getmappings!=stack.opns->get.end());
4512 }
4513 assert(0); /* We cannot end up here, because that means that we
4514 are looking for in non-existing variable */
4515 return var;
4516 }
4517
4519 std::size_t i,
4520 const assignment_list& t1,
4521 const stacklisttype& stack)
4522 {
4523 assert(i>0);
4524 assignment_list t(t1);
4525 if (!options.newstate)
4526 {
4527 assignment_list result=t;
4528 result.push_front(assignment(stack.stackvar,sort_pos::pos(i)));
4529 return result;
4530 }
4531
4532 i=i-1; /* below we count from 0 instead from 1 as done in the
4533 first version of the prover */
4534
4535 if (!options.binary)
4536 {
4537 const std::size_t e=create_enumeratedtype(stack.no_of_states);
4538 data_expression_list l=enumeratedtypes[e].elementnames;
4539 for (; i>0 ; i--)
4540 {
4541 assert(l.size()>0);
4542 l.pop_front();
4543 }
4544 assignment_list result=t;
4545 assert(l.size()>0);
4546 result.push_front(assignment(stack.stackvar,l.front()));
4547 return result;
4548 }
4549 /* else a sequence of boolean values needs to be generated,
4550 representing the value i, when there are l->n elements */
4551 {
4552 std::size_t k=upperpowerof2(stack.no_of_states);
4553 variable_list::const_iterator boolean_state_variables=stack.booleanStateVariables.begin();
4554 for (; k>0 ; k--, ++boolean_state_variables)
4555 {
4556 if ((i % 2)==0)
4557 {
4558 t.push_front(assignment(*boolean_state_variables,sort_bool::false_()));
4559 i=i/2;
4560 }
4561 else
4562 {
4563 t.push_front(assignment(*boolean_state_variables,sort_bool::true_()));
4564 i=(i-1)/2;
4565 }
4566 }
4567 return t;
4568 }
4569 }
4570
4572 std::size_t i,
4573 const data_expression_list& t1,
4574 const stacklisttype& stack)
4575 {
4577 if (!options.newstate)
4578 {
4579 data_expression_list result=t;
4580 result.push_front(sort_pos::pos(i));
4581 return result;
4582 }
4583
4584 i=i-1; /* below we count from 0 instead from 1 as done in the
4585 first version of the linearizer */
4586
4587 if (!options.binary)
4588 {
4589 const std::size_t e=create_enumeratedtype(stack.no_of_states);
4590 data_expression_list l(enumeratedtypes[e].elementnames);
4591 for (; i>0 ; i--)
4592 {
4593 l.pop_front();
4594 }
4595 data_expression_list result=t;
4596 result.push_front(l.front());
4597 return result;
4598 }
4599 /* else a sequence of boolean values needs to be generated,
4600 representing the value i, when there are l->n elements */
4601 {
4602 std::size_t k=upperpowerof2(stack.no_of_states);
4603 variable_list::const_iterator boolean_state_variables=stack.booleanStateVariables.begin();
4604 for (; k>0 ; k--, ++boolean_state_variables)
4605 {
4606 if ((i % 2)==0)
4607 {
4609 i=i/2;
4610 }
4611 else
4612 {
4614 i=(i-1)/2;
4615 }
4616 }
4617 return t;
4618 }
4619 }
4620
4622 const process_identifier& procId,
4623 const std::set < process_identifier >& pCRLproc,
4624 const stacklisttype& stack,
4625 int regular)
4626 {
4627 std::size_t i=1;
4628 for (const process_identifier& p: pCRLproc)
4629 {
4630 if (p==procId)
4631 {
4632 break;
4633 }
4634 ++i;
4635 }
4636 /* i is the index of the current process */
4637
4638 if (!options.newstate)
4639 {
4640 if (regular)
4641 {
4642 return equal_to(stack.stackvar, processencoding(i,assignment_list(),stack).front().rhs());
4643 }
4644 return equal_to(
4645 application(stack.opns->getstate,stack.stackvar),
4646 processencoding(i,assignment_list(),stack).front().rhs());
4647 }
4648
4649 if (!options.binary) /* Here a state encoding using enumerated types
4650 must be declared */
4651 {
4652 create_enumeratedtype(stack.no_of_states);
4653 if (regular)
4654 {
4655 return equal_to(stack.stackvar,
4656 processencoding(i,assignment_list(),stack).front().rhs());
4657 }
4658 return equal_to(
4659 application(stack.opns->getstate, stack.stackvar),
4660 processencoding(i,assignment_list(),stack).front().rhs());
4661 }
4662
4663 /* in this case we must encode the condition using
4664 boolean variables */
4665
4666 const variable_list vars=stack.booleanStateVariables;
4667
4668 i=i-1; /* start counting from 0, instead from 1 */
4670 for (const variable& v: vars)
4671 {
4672 if ((i % 2)==0)
4673 {
4674 t3=lazy::and_(lazy::not_(v),t3);
4675 i=i/2;
4676 }
4677 else
4678 {
4679 t3=lazy::and_(v,t3);
4680 i=(i-1)/2;
4681 }
4682
4683 }
4684 assert(i==0);
4685 return t3;
4686 }
4687
4689 const data_expression& t,
4690 const stacklisttype& stack,
4691 const variable_list& vars,
4692 const variable_list& stochastic_variables)
4693 {
4694 if (is_function_symbol(t))
4695 {
4696 return t;
4697 }
4698
4699 if (is_machine_number(t))
4700 {
4701 return t;
4702 }
4703
4704 if (is_variable(t))
4705 {
4706 if (std::find(vars.begin(),vars.end(),t)!=vars.end())
4707 {
4708 /* t occurs in vars, so, t does not have to be reconstructed
4709 from the stack */
4710 return t;
4711 }
4712 else
4713 if (std::find(stochastic_variables.begin(),stochastic_variables.end(),t)!=stochastic_variables.end())
4714 {
4715 /* t occurs in stochastic_variables, so, t does not have to be reconstructed
4716 from the stack */
4717 return t;
4718 }
4719 else
4720 {
4721 return getvar(atermpp::down_cast<variable>(t), stack);
4722 }
4723 }
4724
4725 if (is_application(t))
4726 {
4727 const application&a=atermpp::down_cast<application>(t);
4728 return application(
4729 adapt_term_to_stack(a.head(),stack,vars,stochastic_variables),
4730 adapt_termlist_to_stack(a.begin(),a.end(),stack,vars,stochastic_variables));
4731 }
4732
4733 if (is_abstraction(t))
4734 {
4735 const abstraction& abs_t=down_cast<abstraction>(t);
4736 return abstraction(
4737 abs_t.binding_operator(),
4738 abs_t.variables(),
4739 adapt_term_to_stack(abs_t.body(),stack,abs_t.variables() + vars,stochastic_variables));
4740 }
4741
4742 if (is_where_clause(t))
4743 {
4744 const where_clause where_t(t);
4745 const assignment_list old_assignments=reverse(where_t.assignments());
4746 variable_list new_vars=vars;
4747 assignment_list new_assignments;
4748 for (const assignment& a: old_assignments)
4749 {
4750 new_vars.push_front(a.lhs());
4751 new_assignments.push_front(
4752 assignment(
4753 a.lhs(),
4754 adapt_term_to_stack(a.rhs(),stack,vars,stochastic_variables)));
4755
4756 }
4757 return where_clause(
4758 adapt_term_to_stack(where_t,stack,new_vars,stochastic_variables),
4759 new_assignments);
4760
4761 }
4762
4763 assert(0); // expected a term;
4764 return t; // in case of non-debug mode, try to return something as decent as possible.
4765 }
4766
4767 template <typename Iterator>
4769 Iterator begin,
4770 const Iterator& end,
4771 const stacklisttype& stack,
4772 const variable_list& vars,
4773 const variable_list& stochastic_variables)
4774 {
4776 for (; begin != end; ++begin)
4777 {
4778 result.push_back(adapt_term_to_stack(*begin,stack, vars,stochastic_variables));
4779 }
4780 return result;
4781 }
4782
4783
4785 const action_list& multiAction,
4786 const stacklisttype& stack,
4787 const variable_list& vars)
4788 {
4789 if (multiAction.empty())
4790 {
4791 return multiAction;
4792 }
4793
4794 const action act=action(multiAction.front());
4795
4797
4799 act.arguments().begin(),
4800 act.arguments().end(),
4801 stack,
4802 vars,
4803 variable_list()));
4804 result.push_front(action(act.label(),data_expression_list(vec.begin(),vec.end())));
4805 return result;
4806 }
4807
4809 const action_list& multiAction,
4810 const stacklisttype& stack,
4811 const variable_list& vars)
4812 {
4814 }
4815
4816 data_expression representative_generator_internal(const sort_expression& s, const bool allow_dont_care_var=true)
4817 {
4818 if ((!options.noglobalvars) && allow_dont_care_var)
4819 {
4820 const variable newVariable(fresh_identifier_generator("dc"),s);
4821 insertvariable(newVariable,true);
4822 global_variables.insert(newVariable);
4823 return newVariable;
4824 }
4825 return representative_generator(data)(s);
4826 }
4827
4829 const variable& s,
4830 const assignment_list& args,
4831 const stacklisttype& stack,
4832 const variable_list& vars,
4833 const std::set<variable>& free_variables_in_body,
4834 const variable_list& stochastic_variables)
4835 {
4836 /* We generate the value for variable s in the list of
4837 the parameters of the process. If s is equal to some
4838 variable in pars, it is an argument of the current
4839 process, and it must be replaced by the corresponding
4840 argument in args.
4841 If s does not occur in pars, it must be replaced
4842 by a dummy value.
4843 */
4844 for (const assignment& a: args)
4845 {
4846 if (s==a.lhs())
4847 {
4848 return adapt_term_to_stack(a.rhs(),stack,vars,stochastic_variables);
4849 }
4850 }
4851
4852 if (free_variables_in_body.find(s)==free_variables_in_body.end())
4853 {
4855 return adapt_term_to_stack(result,stack,vars,stochastic_variables);
4856 }
4857 return adapt_term_to_stack(s,stack,vars,stochastic_variables);
4858 }
4859
4860