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//
9/// \file linearise.cpp
10/// \brief This file contains code to transform an mCRL2 process
11/// into a linear process.
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// The following define allows collecting and printing statistics about the operations performed in the linearizer.
29// Currently, we collect and print information about:
30// the operations performed on processes,
31// the number of summands and the total number of actions in the multications in these summands.
32// Note that all logs are written to std::cout, so if this flag is enabled, tool output can not be streamed through std::cout
33//#define MCRL2_LOG_LPS_LINEARISE_STATISTICS 1
34
35//mCRL2 data
36#include <ranges>
37
38#include "mcrl2/atermpp/aterm.h"
39#include "mcrl2/data/substitutions/maintain_variables_in_rhs.h"
40#include "mcrl2/data/fourier_motzkin.h"
41#include "mcrl2/data/enumerator.h"
42#include "mcrl2/data/real_utilities.h"
43
44// linear process libraries.
45#include "mcrl2/lps/constelm.h"
46#include "mcrl2/lps/linearise.h"
47#include "mcrl2/lps/linearise_allow_block.h"
48#include "mcrl2/lps/linearise_communication.h"
49#include "mcrl2/lps/linearise_hide.h"
50#include "mcrl2/lps/linearise_rename.h"
51#include "mcrl2/lps/linearise_utility.h"
52#include "mcrl2/lps/replace_capture_avoiding_with_an_identifier_generator.h"
53#include "mcrl2/lps/sumelm.h"
54
55#include "mcrl2/lps/detail/ultimate_delay.h"
56
57// Process libraries.
58#include "mcrl2/process/alphabet_reduce.h"
59#include "mcrl2/process/balance_nesting_depth.h"
60#include "mcrl2/process/process_expression.h"
61
62
63// For Aterm library extension functions
64using namespace atermpp;
65using namespace mcrl2;
66using namespace mcrl2::core;
67using namespace mcrl2::core::detail;
68using namespace mcrl2::data;
69using namespace mcrl2::data::detail;
70using namespace mcrl2::lps;
71using namespace mcrl2::process;
72
73/* Preamble */
74
75using processstatustype = enum
76{
77 unknown,
78 mCRL,
79 mCRLdone,
80 mCRLbusy,
81 mCRLlin,
82 pCRL,
83 multiAction,
84 GNF,
85 GNFalpha,
86 GNFbusy,
87 error
88};
89
90/**************** Definitions of object class ***********************/
91
92using objecttype = enum
93{
94 none,
95 _map,
96 func,
97 act,
98 proc,
99 variable_,
100 sorttype,
101 multiact
102};
103
105{
106 public:
107 identifier_string objectname;
108 variable_list parameters;
109 process::action_label_list multi_action_names;
110 bool constructor=false;
112 process_identifier process_representing_action; /* for actions target sort is used to
113 indicate the process representing this action. */
115 processstatustype processstatus=unknown;
116 objecttype object=none;
117 bool canterminate=false;
118 bool containstime=false;
119
121 {
122 return process::find_free_variables(processbody);
123 }
124 objectdatatype()=default;
127 ~objectdatatype()=default;
128};
129
130
132{
133 public:
134 process::action_label_list acts; /* storage place for actions */
135 std::set < variable > global_variables; /* storage place for free variables occurring
136 in processes ranging over data */
137 variable_list initdatavars; /* storage place for free variables in
138 init clause */
139 data_specification data; /* contains the data specification for the current process. */
140
143
144 private:
145 class stackoperations;
146 class stacklisttype;
147 class enumtype;
148 class enumeratedtype;
149
151 /* storage place for processes,
152 uses alt, seq, par, lmer, cond,sum,
153 com, bound, at, name, delta,
154 tau, hide, rename, encap */
155 mcrl2::data::rewriter rewr; /* The rewriter used while linearising */
156 action terminationAction; /* A list of length one with the action that denotes termination */
157 process_identifier terminatedProcId; /* A process identifier of which the body consists of the termination
158 action */
161 std::vector < process_identifier > seq_varnames; /* Contains names of processes which represent a sequence
162 of process variables */
163 std::vector < std::vector < process_instance_assignment > > representedprocesses; /* contains the sequences of process
164 instances that are represented by the variables in seq_varnames */
166 bool timeIsBeingUsed = false;
169 std::map < aterm, objectdatatype > objectdata; // It is important to guarantee that the objects will not
170 // be moved to another place when the object data structure grows. This
171 // is because objects in this datatype are passed around by reference.
172
176
177 public:
178 specification_basic_type(const process::action_label_list& as,
179 const std::vector< process_equation >& ps,
180 const variable_list& idvs,
181 const data_specification& ds,
182 const std::set < data::variable >& glob_vars,
183 const t_lin_options& opt,
184 const process_specification& procspec):
185 acts(),
187 data(ds),
189 options(opt)
190 {
191 // find_identifiers does not find the identifiers in the enclosed data specification.
192 fresh_identifier_generator.add_identifiers(process::find_identifiers(procspec));
193 // So, the identifiers in the data type must be added explicitly.
194 fresh_identifier_generator.add_identifiers(data::find_identifiers(ds.equations()));
195 fresh_identifier_generator.add_identifiers(data::find_identifiers(ds.user_defined_aliases()));
196 fresh_identifier_generator.add_identifiers(data::find_identifiers(ds.sorts()));
197 fresh_identifier_generator.add_identifiers(data::find_identifiers(ds.constructors()));
198 fresh_identifier_generator.add_identifiers(data::find_identifiers(ds.mappings()));
199
200 stack_operations_list=nullptr;
201 acts=as;
202 storeact(acts);
203 procs=ps;
204 storeprocs(procs);
205 initdatavars=idvs;
206 // The terminationAction and the terminatedProcId must be defined after initialisation of
207 // data as otherwise fresh name does not work properly.
208 terminationAction=action(action_label(fresh_identifier_generator("Terminate"),sort_expression_list()),data_expression_list());
209 terminatedProcId=process_identifier(fresh_identifier_generator("Terminated**"), variable_list());
210// /* Changed delta() to DeltaAtZero on 24/12/2006. Moved back in spring 2007, as this introduces unwanted time constraints. */
213 variable_list(),
215 pCRL,false,false);
216 delta_process=newprocess(variable_list(),delta(),pCRL,false,false);
217 tau_process=newprocess(variable_list(),tau(),pCRL,true,false);
218 }
219
221 {
222 while (stack_operations_list!=nullptr)
223 {
227 }
228
229 }
230
231 private:
232
233 static
235 {
237 {
239 }
240 if (r1==sort_real::real_one())
241 {
242 return r2;
243 }
244 if (r2==sort_real::real_one())
245 {
246 return r1;
247 }
248 return sort_real::times(r1,r2);
249 }
250
251 static
253 {
254 return at(delta(), sort_real::real_(0));
255 }
256
258 {
259 if (!is_at(t))
260 {
261 return false;
262 }
264 {
265 return false;
266 }
268 }
269
270 /***************** temporary helper function to compare substitutions ******************/
271
272 template <class Expression, IsSubstitution Substitution>
273 Expression replace_variables_capture_avoiding_alt(const Expression& e, Substitution& sigma)
274 {
275 return process::replace_variables_capture_avoiding_with_an_identifier_generator(e, sigma, fresh_identifier_generator);
276 }
277
278
279 /***************** retrieve basic objects ******************/
280
282 {
283 if (objectdata.count(o)==0)
284 {
285 throw mcrl2::runtime_error("Fail to recognize " + process::pp(o) +
286 ". Most likely due to unguarded recursion in a process equation. ");
287 }
288 }
289
291 {
293 assert(objectdata.find(o)->second.objectname==o.name());
294 assert(objectdata.find(o)->second.parameters==o.variables());
295 objectdatatype& result=objectdata.find(o)->second;
296#ifndef NDEBUG
297 for(const variable& v: result.get_free_variables())
298 {
299 assert(std::find(result.parameters.begin(), result.parameters.end(), v)!=result.parameters.end()||
300 std::find(global_variables.begin(), global_variables.end(), v)!=global_variables.end());
301 }
302#endif
303 return result;
304 }
305
307 {
309 assert(objectdata.find(o)->second.objectname==o.name());
310 assert(objectdata.find(o)->second.parameters==o.variables());
311 return objectdata.find(o)->second;
312 }
313
314 void addString(const identifier_string& str)
315 {
316 fresh_identifier_generator.add_identifier(str);
317 }
318
319 static
321 {
322 if (ma.size()==0)
323 {
324 return tau();
325 }
326 if (ma.size()==1)
327 {
328 return ma.front();
329 }
330 return process::sync(ma.front(),action_list_to_process(ma.tail()));
331 }
332
333 /// Convert the process expression to an action list.
334 /// Note: The resulting actions are not sorted.
335 static
336 action_list to_action_list(const process_expression& p)
337 {
338 if (is_tau(p))
339 {
340 return action_list();
341 }
342
343 if (is_action(p))
344 {
345 return action_list({ action(p) });
346 }
347
348 if (is_sync(p))
349 {
350 return to_action_list(process::sync(p).left())+to_action_list(process::sync(p).right());
351 }
352 assert(0);
353 return action_list();
354 }
355
356 /// Convert the process expression to a sorted action list.
357 static
359 {
360 return sort_actions(to_action_list(p));
361 }
362
363 static
364 action_label_list getnames(const process_expression& multiAction)
365 {
366 if (is_action(multiAction))
367 {
368 return action_label_list({ action(multiAction).label() });
369 }
370 assert(is_sync(multiAction));
371 return getnames(process::sync(multiAction).left())+getnames(process::sync(multiAction).right());
372 }
373
374 // Returns a list of variables with the same sort as the expressions in the list.
375 // If the expression is a variable not occurring in the occurs_set that variable
376 // is used.
377 variable_list make_parameters_rec(const data_expression_list& l,
378 std::set < variable>& occurs_set)
379 {
380 variable_list result;
381 for (const data_expression& e: l)
382 {
383 /* if the current argument of the multi-action is a variable that does
384 not occur in result, use this variable. This is advantageous, when joining
385 processes to one linear process where variable names are joined. If this
386 is not being done (as happened before 4/1/2008) very long lists of parameters
387 can occur when linearising using regular2 */
388 if (is_variable(e) && std::find(occurs_set.begin(),occurs_set.end(),e)==occurs_set.end())
389 {
390 const variable& v = atermpp::down_cast<variable>(e);
391 result.push_front(v);
392 occurs_set.insert(v);
393 }
394 else
395 {
396 result.push_front(variable(get_fresh_variable("a",e.sort())));
397 }
398 }
399 return reverse(result);
400 }
401
402 variable_list getparameters_rec(const process_expression& multiAction,
403 std::set < variable>& occurs_set)
404 {
405 if (is_action(multiAction))
406 {
407 return make_parameters_rec(action(multiAction).arguments(),occurs_set);
408 }
409 assert(is_sync(multiAction));
410 return getparameters_rec(process::sync(multiAction).left(),occurs_set)+
411 getparameters_rec(process::sync(multiAction).right(),occurs_set);
412 }
413
414 variable_list getparameters(const process_expression& multiAction)
415 {
416 std::set < variable > occurs_set;
417 return getparameters_rec(multiAction,occurs_set);
418 }
419
420 static
421 data_expression_list getarguments(const action_list& multiAction)
422 {
423 data_expression_list result;
424 for (const action& a: multiAction)
425 {
426 result=reverse(a.arguments()) + result;
427 }
428 return reverse(result);
429 }
430
431 static
432 action_list makemultiaction(const process::action_label_list& actionIds, const data_expression_list& args)
433 {
434 action_list result;
435 data_expression_list::const_iterator e_walker=args.begin();
436 for (const process::action_label& l: actionIds)
437 {
438 std::size_t arity=l.sorts().size();
439 data_expression_list temp_args;
440 for (std::size_t i=0 ; i< arity; ++i,++e_walker)
441 {
442 assert(e_walker!=args.end());
443 temp_args.push_front(*e_walker);
444 }
445 temp_args=reverse(temp_args);
446 result.push_front(action(l,temp_args));
447 }
448 assert(e_walker==args.end());
449 return reverse(result);
450 }
451
452 objectdatatype& addMultiAction(const process_expression& multiAction, bool& isnew)
453 {
454 const process::action_label_list actionnames=getnames(multiAction);
455
456 isnew=(objectdata.count(actionnames)==0);
457
458 if (isnew)
459 {
460 objectdatatype object;
461
462 // tempvar is needed as objectdata can change during a call
463 // of getparameters.
464 const variable_list templist=getparameters(multiAction);
465 object.parameters=templist;
466 object.object=multiact;
467 // must separate assignment below as
468 // objectdata may change as a side effect of make
469 // multiaction.
470 const action_list tempvar=makemultiaction(actionnames, variable_list_to_data_expression_list(object.parameters));
471 object.processbody=action_list_to_process(tempvar);
472
473 objectdata[actionnames]=object;
474 }
475 return objectdata.find(actionnames)->second;
476 }
477
478 void insertvariable(const variable& var, const bool mustbenew)
479 {
481
482 if (objectdata.count(var.name())>0 && mustbenew)
483 {
484 throw mcrl2::runtime_error("Variable " + data::pp(var) + " already exists. ");
485 }
486
487 objectdatatype object;
488 object.objectname=var.name();
489 object.object=variable_;
490 objectdata[var.name()]=object;
491 }
492
493 void insertvariables(const variable_list& vars, const bool mustbenew)
494 {
495 for (const variable& v: vars)
496 {
497 insertvariable(v,mustbenew);
498 }
499 }
500
501 template <data::IsSubstitution Substitution>
503 {
504 std::set<data::variable> result;
505 for (typename Substitution::const_iterator i = sigma.begin(); i != sigma.end(); ++i)
506 {
507 std::set<data::variable> V = data::find_free_variables(i->second);
508 V.erase(i->first);
509 result.insert(V.begin(), V.end());
510 }
511 return result;
512 }
513
514
515 /************ upperpowerof2 *********************************************/
516
517 static
519 /* function yields n for the smallest value n such that
520 2^n>=i. This constitutes the number of bits necessary
521 to represent a number smaller than i. i is assumed to
522 be at least 1. */
523 {
524 std::size_t n=0;
525 std::size_t powerof2=1;
526 for (; powerof2< i ; n++)
527 {
528 powerof2=2*powerof2;
529 }
530 return n;
531 }
532
534 {
535 if (!options.norewrite)
536 {
538 {
539 rewr=rewriter(data,options.rewrite_strategy);
541 }
542 return rewr(t);
543 }
544 return t;
545 }
546
547 data_expression_list RewriteTermList(const data_expression_list& t)
548 {
549 data_expression_vector v;
550 for(const data_expression& d: t)
551 {
552 v.push_back(RewriteTerm(d));
553 }
554 return data_expression_list(v.begin(),v.end());
555 }
556
557 assignment_list rewrite_assignments(const assignment_list& t)
558 {
559 assignment_vector v;
560 for(const assignment& a: t)
561 {
562 v.emplace_back(a.lhs(), RewriteTerm(a.rhs()));
563 }
564 return assignment_list(v.begin(),v.end());
565 }
566
568 {
569 return action(t.label(),RewriteTermList(t.arguments()));
570 }
571
573 {
574 return process_instance_assignment(t.identifier(),rewrite_assignments(t.assignments()));
575 }
576
578 {
579 if (is_tau(t))
580 {
581 return t;
582 }
583
584 if (is_action(t))
585 {
587 }
588
589 assert(is_sync(t)); // A multi action is a sequence of actions with a sync operator in between.
591 }
592
593
595 {
596 if (options.norewrite)
597 {
598 return t;
599 }
600
601 if (is_if_then(t))
602 {
603 const data_expression new_cond=RewriteTerm(down_cast<if_then>(t).condition());
604 const process_expression new_then_case=pCRLrewrite(down_cast<if_then>(t).then_case());
605 if (new_cond==sort_bool::true_())
606 {
607 return new_then_case;
608 }
609 return if_then(new_cond,new_then_case);
610 }
611
612 if (is_seq(t))
613 {
614 /* only one summand is needed */
615 return seq(
618 }
619
620 if (is_at(t))
621 {
622 const data_expression& atTime=RewriteTerm(down_cast<at>(t).time_stamp());
623 const process_expression t1=pCRLrewrite(down_cast<at>(t).operand());
624 return at(t1,atTime);
625 }
626
628 {
629 const stochastic_operator& sto=atermpp::down_cast<const stochastic_operator>(t);
631 }
632
633 if (is_delta(t) || is_tau(t))
634 {
635 return t;
636 }
637
638 if (is_action(t))
639 {
640 return RewriteAction(atermpp::down_cast<process::action>(t));
641 }
642
644 {
646 }
647
648 if (is_sync(t))
649 {
650 return RewriteMultAct(t);
651 }
652
653 assert(0); // Expected a term in pCRL format, using only basic process operators.
654 return t;
655 }
656
657 /************ storeact ****************************************************/
658
660 {
661 if (objectdata.count(actionId)>0)
662 {
663 throw mcrl2::runtime_error("Action " + process::pp(actionId) + " is added twice. This is an internal error in the lineariser. Please report. ");
664 }
665
666 const identifier_string& str=actionId.name();
667 addString(str);
668
669 objectdatatype object;
670 object.objectname=str;
671 object.object=act;
673
674 objectdata[actionId]=object;
675 return objectdata.find(actionId)->second;
676 }
677
678 void storeact(const process::action_label_list& acts)
679 {
680 for (const process::action_label& l: acts)
681 {
682 insertAction(l);
683 }
684 }
685
686 /************ storeprocs *************************************************/
687
689 const process_identifier& procId,
690 const variable_list& parameters,
691 const process_expression& body,
692 processstatustype s,
693 const bool canterminate,
694 const bool containstime)
695 {
696 assert(procId.variables().size()==parameters.size());
697 const std::string str=procId.name();
698 addString(str);
699
700 if (objectdata.count(procId)>0)
701 {
702 throw mcrl2::runtime_error("Process " + process::pp(procId) + " is added twice. This is an internal error in the lineariser. Please report. ");
703 }
704
705 objectdatatype object;
706 object.objectname=procId.name();
707 object.object=proc;
708 object.processbody=body;
709 object.canterminate=canterminate;
710 object.containstime=containstime;
711 object.processstatus=s;
712 object.parameters=parameters;
713 insertvariables(parameters,false);
714 objectdata[procId]=object;
715 return objectdata.find(procId)->second;
716 }
717
718 void storeprocs(const std::vector< process_equation >& procs)
719 {
720 for (const process_equation& e: procs)
721 {
722 insert_process_declaration(
723 e.identifier(),
724 e.formal_parameters(),
725 e.expression(),
726 unknown,false,false);
727 }
728 }
729
731 const variable_list& parameters,
732 const process_expression& body,
733 const processstatustype s,
734 const bool canterminate,
735 const bool containstime,
736 process_identifier& p) const
737 {
738 for(const std::pair<const aterm,objectdatatype>& d: objectdata)
739 {
740 if (d.second.object==proc &&
741 d.second.parameters==parameters &&
742 d.second.processbody==body &&
743 d.second.canterminate==canterminate &&
744 d.second.containstime==containstime &&
745 d.second.processstatus==s)
746 {
747 p=process_identifier(d.second.objectname,d.second.parameters);
748 return true;
749 }
750 }
751 return false;
752 }
753
754
755 /************ storeinit *************************************************/
756
757 public:
759 {
760 /* init is used as the name of the initial process,
761 because it cannot occur as a string in the input */
762
763 process_identifier initprocess(std::string("init"), variable_list());
764 insert_process_declaration(initprocess,variable_list(),init,unknown,false,false);
765 return initprocess;
766 }
767
768 private:
769
770 /********** various functions on action and multi actions ***************/
771
772
773
774 action_list linMergeMultiActionList(const action_list& ma1, const action_list& ma2)
775 {
776 action_list result=ma2;
777 for (const action& a: ma1)
778 {
779 result = atermpp::insert_sorted(a, result);
780 }
781 return result;
782 }
783
784
786 {
787 return linMergeMultiActionList(to_action_list(ma1),to_action_list(ma2));
788 }
789
790 /************** determine_process_status ********************************/
791
793 const process_expression& body, // intentionally not a reference.
794 const processstatustype status)
795 {
796 /* In this procedure it is determined whether a process
797 is of type mCRL, pCRL or a multiAction. pCRL processes
798 occur strictly within mCRL processes, and multiActions
799 occur strictly within pCRL processes. Processes that pass
800 this procedure can be linearised. Bounded initialisation,
801 the leftmerge and synchronization merge on the highest
802 level are filtered out. */
803 if (is_choice(body))
804 {
805 if (status==multiAction)
806 {
807 throw mcrl2::runtime_error("Choice operator occurs in a multi-action in " + process::pp(body) + ". The lineariser cannot handle such a pattern. ");
808 }
809 const processstatustype s1=determine_process_statusterm(choice(body).left(),pCRL);
810 const processstatustype s2=determine_process_statusterm(choice(body).right(),pCRL);
811 if ((s1==mCRL)||(s2==mCRL))
812 {
813 throw mcrl2::runtime_error("An operator ||, allow, hide, rename, or comm occurs within the scope of a choice operator in " + process::pp(body) +
814 ". The lineariser cannot handle such processes. ");
815 }
816 return pCRL;
817 }
818
819 if (is_seq(body))
820 {
821 if (status==multiAction)
822 {
823 throw mcrl2::runtime_error("Sequential operator occurs in a multi-action in " + process::pp(body) +". The lineariser cannot handle such a pattern. ");
824 }
825 const processstatustype s1=determine_process_statusterm(seq(body).left(),pCRL);
826 const processstatustype s2=determine_process_statusterm(seq(body).right(),pCRL);
827 if ((s1==mCRL)||(s2==mCRL))
828 {
829 throw mcrl2::runtime_error("An operator ||, allow, hide, rename, or comm occurs in the scope of a sequential operator in " + process::pp(body) +". "
830 + "The lineariser cannot handle such processes. ");
831 }
832 return pCRL;
833 }
834
835 if (is_merge(body))
836 {
837 if (status!=mCRL)
838 {
839 throw mcrl2::runtime_error("The parallel operator occurs in the scope of recursion, or the condition, sequential or choice operator in " +
840 process::pp(body) + ". The lineariser cannot handle such processes. ");
841 }
844 return mCRL;
845 }
846
847 if (is_left_merge(body))
848 {
849 throw mcrl2::runtime_error("Cannot linearise because the specification contains a leftmerge. ");
850 }
851
852 if (is_if_then(body))
853 {
854 if (status==multiAction)
855 {
856 throw mcrl2::runtime_error("If-then occurs in a multi-action in " + process::pp(body) + ". The lineariser cannot linearise this. ");
857 }
858 const processstatustype s1=determine_process_statusterm(if_then(body).then_case(),pCRL);
859 if (s1==mCRL)
860 {
861 throw mcrl2::runtime_error("An operator ||, allow, hide, rename, or comm occurs in the scope of the if-then operator in " + process::pp(body) + ". "
862 + "The lineariser cannot handle such processes. ");
863 }
864 return pCRL;
865 }
866
867 if (is_if_then_else(body))
868 {
869 if (status==multiAction)
870 {
871 throw mcrl2::runtime_error("If-then-else occurs in a multi-action in " + process::pp(body) +".");
872 }
873 const processstatustype s1=determine_process_statusterm(if_then_else(body).then_case(),pCRL);
874 const processstatustype s2=determine_process_statusterm(if_then_else(body).else_case(),pCRL);
875 if ((s1==mCRL)||(s2==mCRL))
876 {
877 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) +
878 ". " + "The lineariser cannot handle such processes. ");
879 }
880 return pCRL;
881 }
882
883 if (is_sum(body))
884 {
885 /* insert the variable names of variables, to avoid
886 that this variable name will be reused later on */
888 if (status==multiAction)
889 {
890 throw mcrl2::runtime_error("The sum operator occurs within a multi-action in " + process::pp(body) + ". "
891 + "The lineariser cannot handle such processes. ");
892 }
893 const processstatustype s1=determine_process_statusterm(sum(body).operand(),pCRL);
894 if (s1==mCRL)
895 {
896 throw mcrl2::runtime_error("An operator ||, allow, hide, rename, or comm occurs in the scope of the sum operator in " + process::pp(body) + ". "
897 "The lineariser cannot handle such processes. ");
898 }
899 return pCRL;
900 }
901
903 {
905 /* insert the variable names of variables, to avoid
906 that this variable name will be reused later on */
907 const stochastic_operator& sto=down_cast<const stochastic_operator>(body);
909 if (status==multiAction)
910 {
911 throw mcrl2::runtime_error("Stochastic operator occurs within a multi-action in " + process::pp(body) +".");
912 }
913 const processstatustype s1=determine_process_statusterm(sto.operand(),pCRL);
914 if (s1==mCRL)
915 {
916 throw mcrl2::runtime_error("An operator ||, allow, hide, rename, or comm occurs in the scope of the stochastic operator in " + process::pp(body) + ". "
917 + "The lineariser cannot handle such processes. ");
918 }
919 return s1;
920 }
921
922 if (is_comm(body))
923 {
924 if (status!=mCRL)
925 {
926 throw mcrl2::runtime_error("The communication operator occurs in the scope of recursion, the condition, the sequential operation or the choice in "
927 + process::pp(body) + ". The lineariser cannot linearise such processes. ");
928 }
930 return mCRL;
931 }
932
933 if (is_bounded_init(body))
934 {
935 throw mcrl2::runtime_error("Cannot linearise a specification with the bounded initialization operator.");
936 }
937
938 if (is_at(body))
939 {
940 timeIsBeingUsed = true;
941 if (status==multiAction)
942 {
943 throw mcrl2::runtime_error("Time operator occurs in a multi-action in " + process::pp(body) +".");
944 }
945 const processstatustype s1=determine_process_statusterm(at(body).operand(),pCRL);
946 if (s1==mCRL)
947 {
948 throw mcrl2::runtime_error("An operator ||, allow, hide, rename, or comm occurs in the scope of a time operator in " + process::pp(body) + "."
949 + "The lineariser cannot handle such processes. ");
950 }
951 return pCRL;
952 }
953
954 if (is_sync(body))
955 {
956 const processstatustype s1=determine_process_statusterm(process::sync(body).left(),pCRL);
957 const processstatustype s2=determine_process_statusterm(process::sync(body).right(),pCRL);
958 if ((s1!=multiAction)||(s2!=multiAction))
959 {
960 throw mcrl2::runtime_error("Other objects than multi-actions occur in the scope of a synch operator in " + process::pp(body) +".");
961 }
962 return multiAction;
963 }
964
965 if (is_action(body))
966 {
967 return multiAction;
968 }
969
971 {
973 return status;
974 }
975
976 if (is_delta(body))
977 {
978 return pCRL;
979 }
980
981 if (is_tau(body))
982 {
983 return multiAction;
984 }
985
986 if (is_hide(body))
987 {
988 if (status!=mCRL)
989 {
990 throw mcrl2::runtime_error("A hide operator occurs in the scope of recursion, or a condition, choice or sequential operator in " + process::pp(body) +".");
991 }
993 return mCRL;
994 }
995
996 if (is_rename(body))
997 {
998 if (status!=mCRL)
999 {
1000 throw mcrl2::runtime_error("A rename operator occurs in the scope of recursion, or a condition, choice or sequential operator in " + process::pp(body) +".");
1001 }
1003 return mCRL;
1004 }
1005
1006 if (is_allow(body))
1007 {
1008 if (status!=mCRL)
1009 {
1010 throw mcrl2::runtime_error("An allow operator occurs in the scope of recursion, or a condition, choice or sequential operator in " + process::pp(body) +".");
1011 }
1013 return mCRL;
1014 }
1015
1016 if (is_block(body))
1017 {
1018 if (status!=mCRL)
1019 {
1020 throw mcrl2::runtime_error("A block operator occurs in the scope of recursion, or a condition, choice or sequential operator in " + process::pp(body) +".");
1021 }
1023 return mCRL;
1024 }
1025
1026 throw mcrl2::runtime_error("Process has unexpected format (2) " + process::pp(body) +".");
1027 return error;
1028 }
1029
1030
1032 const process_identifier& procDecl,
1033 const processstatustype status)
1034 {
1035 objectdatatype& object=objectIndex(procDecl);
1036 processstatustype s=object.processstatus;
1037
1038 if (s==unknown)
1039 {
1040 object.processstatus=status;
1041 if (status==pCRL)
1042 {
1044 return;
1045 }
1046 /* status==mCRL */
1048 if (s!=status)
1049 {
1050 /* s==pCRL and status==mCRL */
1051 object.processstatus=s;
1053 }
1054 }
1055 if (s==mCRL)
1056 {
1057 if (status==pCRL)
1058 {
1059 object.processstatus=pCRL;
1061 }
1062 }
1063 }
1064
1065 /*********** collect pcrlprocessen **********************************/
1066
1067 void collectPcrlProcesses_term(const process_expression& body, // Intentionally not a reference.
1068 std::vector <process_identifier>& pcrlprocesses,
1069 std::set <process_identifier>& visited)
1070 {
1071 if (is_if_then(body))
1072 {
1073 collectPcrlProcesses_term(if_then(body).then_case(),pcrlprocesses,visited);
1074 return;
1075 }
1076
1077 if (is_if_then_else(body))
1078 {
1079 collectPcrlProcesses_term(if_then_else(body).then_case(),pcrlprocesses,visited);
1080 collectPcrlProcesses_term(if_then_else(body).else_case(),pcrlprocesses,visited);
1081 return;
1082 }
1083
1084 if (is_choice(body))
1085 {
1086 collectPcrlProcesses_term(choice(body).left(),pcrlprocesses,visited);
1087 collectPcrlProcesses_term(choice(body).right(),pcrlprocesses,visited);
1088 return ;
1089 }
1090
1091 if (is_seq(body))
1092 {
1093 collectPcrlProcesses_term(seq(body).left(),pcrlprocesses,visited);
1094 collectPcrlProcesses_term(seq(body).right(),pcrlprocesses,visited);
1095 return ;
1096 }
1097
1098 if (is_merge(body))
1099 {
1100 collectPcrlProcesses_term(process::merge(body).left(),pcrlprocesses,visited);
1101 collectPcrlProcesses_term(process::merge(body).right(),pcrlprocesses,visited);
1102 return ;
1103 }
1104
1105 if (is_sync(body))
1106 {
1107 collectPcrlProcesses_term(process::sync(body).left(),pcrlprocesses,visited);
1108 collectPcrlProcesses_term(process::sync(body).right(),pcrlprocesses,visited);
1109 return ;
1110 }
1111
1112 if (is_sum(body))
1113 {
1114 collectPcrlProcesses_term(sum(body).operand(),pcrlprocesses,visited);
1115 return;
1116 }
1117
1119 {
1120 collectPcrlProcesses_term(stochastic_operator(body).operand(),pcrlprocesses,visited);
1121 return;
1122 }
1123
1124 if (is_at(body))
1125 {
1126 collectPcrlProcesses_term(at(body).operand(),pcrlprocesses,visited);
1127 return;
1128 }
1129
1131 {
1132 collectPcrlProcesses(process_instance_assignment(body).identifier(),pcrlprocesses,visited);
1133 return;
1134 }
1135
1136 if (is_hide(body))
1137 {
1138 collectPcrlProcesses_term(hide(body).operand(),pcrlprocesses,visited);
1139 return;
1140 }
1141
1142 if (is_rename(body))
1143 {
1144 collectPcrlProcesses_term(process::rename(body).operand(),pcrlprocesses,visited);
1145 return;
1146 }
1147
1148 if (is_allow(body))
1149 {
1150 collectPcrlProcesses_term(allow(body).operand(),pcrlprocesses,visited);
1151 return;
1152 }
1153
1154 if (is_block(body))
1155 {
1156 collectPcrlProcesses_term(block(body).operand(),pcrlprocesses,visited);
1157 return;
1158 }
1159
1160 if (is_comm(body))
1161 {
1162 collectPcrlProcesses_term(comm(body).operand(),pcrlprocesses,visited);
1163 return;
1164 }
1165
1166 if ((is_delta(body))||
1167 (is_tau(body))||
1168 (is_action(body)))
1169 {
1170 return;
1171 }
1172
1173 throw mcrl2::runtime_error("process has unexpected format (1) " + process::pp(body) +".");
1174 }
1175
1177 const process_identifier& procDecl,
1178 std::vector <process_identifier>& pcrlprocesses,
1179 std::set <process_identifier>& visited)
1180 {
1181 if (visited.count(procDecl)==0)
1182 {
1183 visited.insert(procDecl);
1184 objectdatatype& object=objectIndex(procDecl);
1185 if (object.processstatus==pCRL)
1186 {
1187 pcrlprocesses.push_back(procDecl);
1188 }
1189 collectPcrlProcesses_term(object.processbody,pcrlprocesses,visited);
1190 }
1191 }
1192
1194 const process_identifier& procDecl,
1195 std::vector <process_identifier>& pcrlprocesses)
1196 {
1197 std::set <process_identifier> visited;
1198 collectPcrlProcesses(procDecl, pcrlprocesses, visited);
1199 }
1200
1201 /**************** occursinterm *** occursintermlist ***********/
1202
1204 const data_expression& t,
1205 const std::set < variable >& vars_set,
1206 std::set < variable >& vars_result_set)
1207 {
1208 if (is_variable(t))
1209 {
1210 const variable& v = atermpp::down_cast<variable>(t);
1211 if (vars_set.find(v)!=vars_set.end())
1212 {
1213 vars_result_set.insert(v);
1214 }
1215 return;
1216 }
1217
1219 {
1220 return;
1221 }
1222
1224 {
1225 return;
1226 }
1227
1228 if (is_abstraction(t))
1229 {
1230 return;
1231 }
1232
1233 if (is_where_clause(t))
1234 {
1235 return;
1236 }
1237
1238 if (!is_application(t))
1239 {
1240 mCRL2log(mcrl2::log::log_level_t::error) << "term of unexpected type " << t << std::endl;
1241 }
1242
1243 assert(is_application(t));
1244
1245 const application& a=atermpp::down_cast<const application>(t);
1246 filter_vars_by_term(a.head(),vars_set,vars_result_set);
1247 filter_vars_by_termlist(a.begin(),a.end(),vars_set,vars_result_set);
1248 }
1249
1250 static
1251 bool occursintermlist(const variable& var, const data_expression_list& r)
1252 {
1253 for (const data_expression& d: r)
1254 {
1255 if (occursinterm(d, var))
1256 {
1257 return true;
1258 }
1259 }
1260 return false;
1261 }
1262
1263 bool occursintermlist(const variable& var, const assignment_list& r, const process_identifier& proc_name) const
1264 {
1265 std::set<variable> assigned_variables;
1266 for (const assignment& l: r)
1267 {
1268 if (occursinterm(l.rhs(), var))
1269 {
1270 return true;
1271 }
1272 assigned_variables.insert(l.lhs());
1273 }
1274 // Check whether x does not occur in the assignment list. Then variable x is assigned to
1275 // itself, and it occurs in the process.
1276 for (const variable& v: objectIndex(proc_name).parameters)
1277 {
1278 if (var==v)
1279 {
1280 if (assigned_variables.count(var)==0) // This variable is not assigned, so it does occur!
1281 {
1282 return true;
1283 }
1284 }
1285 }
1286 return false;
1287 }
1288
1289 template <typename Iterator>
1291 Iterator begin,
1292 const Iterator& end,
1293 const std::set < variable >& vars_set,
1294 std::set < variable >& vars_result_set)
1295 {
1296 for (; begin != end; ++begin)
1297 {
1298 filter_vars_by_term(*begin,vars_set,vars_result_set);
1299 }
1300 }
1301
1303 const action_list& multiaction,
1304 const std::set < variable >& vars_set,
1305 std::set < variable >& vars_result_set)
1306 {
1307 for (const action& ma: multiaction)
1308 {
1309 filter_vars_by_termlist(ma.arguments().begin(), ma.arguments().end(),vars_set,vars_result_set);
1310 }
1311 return;
1312 }
1313
1315 const assignment_list& assignments,
1316 const variable_list& parameters,
1317 const std::set < variable >& vars_set,
1318 std::set < variable >& vars_result_set)
1319 {
1320 const auto& l= parameters | std::views::transform([](const variable& v) { return atermpp::down_cast<data_expression>(v); });
1321 filter_vars_by_termlist(l.begin(),l.end(),vars_set,vars_result_set);
1322 for (const assignment& a: assignments)
1323 {
1324 filter_vars_by_term(a.rhs(),vars_set,vars_result_set);
1325 }
1326 }
1327
1329 const process_expression& p,
1330 const bool strict)
1331 {
1332 if (is_choice(p))
1333 {
1334 return occursinpCRLterm(var,choice(p).left(),strict)||
1336 }
1337 if (is_seq(p))
1338 {
1339 return occursinpCRLterm(var,seq(p).left(),strict)||
1341 }
1342 if (is_if_then(p))
1343 {
1346 }
1347
1348 if (is_sum(p))
1349 {
1350 if (strict)
1351 {
1354 /* below appears better? , but leads
1355 to errors. Should be investigated. */
1356 }
1357 else
1358 {
1361 }
1362 }
1364 {
1365 const stochastic_operator& sto=atermpp::down_cast<const stochastic_operator>(p);
1366 if (strict)
1367 {
1371 }
1372 else
1373 {
1376 occursinpCRLterm(var,sto.operand(),strict));
1377 }
1378 }
1380 {
1382 }
1383 if (is_action(p))
1384 {
1386 }
1387 if (is_sync(p))
1388 {
1389 return occursinpCRLterm(var,process::sync(p).left(),strict)||
1391 }
1392 if (is_at(p))
1393 {
1396 }
1397 if (is_delta(p))
1398 {
1399 return false;
1400 }
1401 if (is_tau(p))
1402 {
1403 return false;
1404 }
1405 throw mcrl2::runtime_error("unexpected process format in occursinCRLterm " + process::pp(p));
1406 return false;
1407 }
1408
1409 template <IsSubstitution MutableSubstitution>
1411 variable_list& sumvars,
1412 MutableSubstitution& sigma,
1413 const process_expression& p)
1414 {
1415 /* This function replaces the variables in sumvars
1416 by unique ones if these variables occur in the process expression p.
1417 The substitution sigma contains the renaming. */
1418 variable_vector newsumvars;
1419
1420 for (const variable& var: sumvars)
1421 {
1422 if (occursinpCRLterm(var,p,true))
1423 {
1424 const variable newvar=get_fresh_variable(var.name(),var.sort());
1425 newsumvars.push_back(newvar);
1426 sigma[var]=newvar;
1427 }
1428 else
1429 {
1430 newsumvars.push_back(var);
1431 }
1432 }
1433 sumvars=variable_list(newsumvars.begin(), newsumvars.end());
1434 }
1435
1436 template <IsSubstitution MutableSubstitution>
1438 variable_list& sumvars,
1439 MutableSubstitution& sigma,
1440 const variable_list& occurvars,
1441 const data_expression_list& occurterms)
1442 {
1443 /* This function replaces the variables in sumvars
1444 by unique ones if these variables occur in occurvars
1445 or occurterms. It extends rename_vars and rename
1446 terms to rename the replaced variables to new ones. */
1447 variable_list newsumvars;
1448
1449 for (const variable& var: sumvars)
1450 {
1451 if (occursintermlist(var,variable_list_to_data_expression_list(occurvars)) ||
1452 occursintermlist(var,occurterms))
1453 {
1454 const variable newvar=get_fresh_variable(var.name(),var.sort());
1455 newsumvars.push_front(newvar);
1456 sigma[var]=newvar;
1457 }
1458 else
1459 {
1460 newsumvars.push_front(var);
1461 }
1462 }
1463 sumvars=reverse(newsumvars);
1464 }
1465
1467 {
1468 std::set<variable> free_variables_in_p_new;
1469 free_variables_in_p_new=process::find_free_variables(p);
1470 return free_variables_in_p_new;
1471 }
1472
1473 /* Remove assignments that do not appear in the parameter list. */
1474 static
1475 assignment_list filter_assignments(const assignment_list& assignments, const variable_list& parameters)
1476 {
1477 assignment_vector result;
1478 for(const assignment& a: assignments)
1479 {
1480 if (std::find(parameters.begin(),parameters.end(),a.lhs())!=parameters.end()) // found.
1481 {
1482 result.push_back(a);
1483 }
1484 }
1485 return assignment_list(result.begin(),result.end());
1486 }
1487
1488 /* Check whether the assignments occur in the same order in the list of parameters */
1490 const assignment_list& assignments,
1491 const variable_list& parameters)
1492 {
1493 assignment_list::iterator i=assignments.begin();
1494 for(const variable& v: parameters)
1495 {
1496 if (i!=assignments.end() && v==i->lhs())
1497 {
1498 ++i;
1499 }
1500 }
1501 return i==assignments.end();
1502
1503 }
1504
1505 /******************* substitute *****************************************/
1506
1507
1508 template <IsSubstitution Substitution>
1510 const assignment_list& assignments,
1511 const variable_list& parameters,
1512 const bool replacelhs,
1513 const bool replacerhs,
1514 Substitution& sigma)
1515 {
1516 assert(check_assignment_list(assignments, parameters));
1517 /* precondition: the variables in the assignment occur in
1518 the same sequence as in the parameters, which stands for the
1519 total list of parameters.
1520
1521 This function replaces the variables in vars by the terms in terms
1522 in the right hand side of the assignments if replacerhs holds, and
1523 in the lefthandside of an assignment if replacelhs holds. If for some variable
1524 occuring in the parameterlist no assignment is present, whereas
1525 this variable occurs in vars, an assignment for it is added.
1526
1527 It is not possible to use standard substitution functions to replace substitute_assignmentlis
1528 because they do not take the parameters of processes into account.
1529 For instance consider a process P(b:D)=... of which an instance P() exists.
1530 If the substitution sigma(b)=t is applied to P() the result should be P(b=t).
1531 The standard substitutions do not take this parameterlist into account, as it stands.
1532 */
1533
1534 assert(replacelhs || replacerhs);
1535 if (parameters.empty())
1536 {
1537 assert(assignments.empty());
1538 return assignments;
1539 }
1540
1541 const variable& parameter=parameters.front();
1542
1543 if (!assignments.empty())
1544 {
1545 const assignment& ass=assignments.front();
1546 variable lhs=ass.lhs();
1547 if (parameter==lhs)
1548 {
1549 /* The assignment refers to parameter par. Substitute its
1550 left and righthandside */
1551 data_expression rhs=ass.rhs();
1552
1553 if (replacelhs)
1554 {
1555 lhs = atermpp::down_cast<variable>(sigma(lhs));
1556 }
1557 if (replacerhs)
1558 {
1559 rhs=replace_variables_capture_avoiding_alt(rhs,sigma);
1560 }
1561
1562 assignment_list result=
1563 substitute_assignmentlist(
1564 assignments.tail(),
1565 parameters.tail(),
1566 replacelhs,
1567 replacerhs,
1568 sigma);
1569 result.push_front(assignment(lhs,rhs));
1570 return result;
1571 }
1572 }
1573
1574 /* Here the first parameter is not equal to the first
1575 assignment. So, we must find out whether a value
1576 for this variable is substituted, that is different
1577 from the variable, in which case an assignment must
1578 be added. */
1579
1580 variable lhs=parameter;
1581 data_expression rhs=parameter;
1582
1583 if (replacelhs)
1584 {
1585 lhs = atermpp::down_cast<data::variable>(sigma(lhs));
1586 }
1587 if (replacerhs)
1588 {
1589 rhs=replace_variables_capture_avoiding_alt(rhs,sigma);
1590 }
1591
1592 if (lhs==rhs)
1593 {
1594 return substitute_assignmentlist(
1595 assignments,
1596 parameters.tail(),
1597 replacelhs,
1598 replacerhs,
1599 sigma);
1600 }
1601 assignment_list result=
1602 substitute_assignmentlist(
1603 assignments,
1604 parameters.tail(),
1605 replacelhs,
1606 replacerhs,
1607 sigma);
1608 result.push_front(assignment(lhs,rhs));
1609 return result;
1610 }
1611
1612 // Sort the assignments, such that they have the same order as the parameters
1613 static
1614 assignment_list sort_assignments(const assignment_list& ass, const variable_list& parameters)
1615 {
1616 std::map<variable,data_expression>assignment_map;
1617 for(const assignment& a: ass)
1618 {
1619 assignment_map[a.lhs()]=a.rhs();
1620 }
1621
1622 assignment_vector result;
1623 for(const variable& v: parameters)
1624 {
1625 const std::map<variable,data_expression>::const_iterator j=assignment_map.find(v);
1626 if (j!=assignment_map.end()) // found
1627 {
1628 result.emplace_back(j->first, j->second);
1629 }
1630 }
1631 return assignment_list(result.begin(),result.end());
1632 }
1633
1635 const process_identifier& id,
1636 const assignment_list& assignments)
1637 {
1638 objectdatatype& object=objectIndex(id);
1639 variable_list parameters=object.parameters;
1640 for(const assignment& a: assignments)
1641 {
1642 // Every assignment must occur in the parameter list, in the right sequence.
1643
1644 variable v;
1645 do
1646 {
1647 if (parameters.empty())
1648 {
1649 return false;
1650 }
1651 v=parameters.front();
1652 parameters.pop_front();
1653 }
1654 while (v!=a.lhs());
1655
1656 }
1657 return true;
1658 }
1659
1660 /* The function below calculates sigma(p) and replaces
1661 all variables that are bound by a sum in p by unique
1662 variables */
1663
1664
1665 /* The function below cannot be replace by replace_variables_capture_avoiding although
1666 * the interfaces are the same. As yet it is unclear why, but the difference shows itself
1667 * for instance when linearising lift3-final.mcrl2 and lift3_init.mcrl2 */
1668 template <IsSubstitution Substitution>
1670 const process_expression& p,
1671 Substitution& sigma)
1672 {
1673 if (is_choice(p))
1674 {
1675 process_expression left=substitute_pCRLproc(choice(p).left(),sigma);
1676 if (left==delta_at_zero())
1677 {
1678 return substitute_pCRLproc(choice(p).right(),sigma);
1679 }
1680 process_expression right=substitute_pCRLproc(choice(p).right(),sigma);
1681 if (right==delta_at_zero())
1682 {
1683 return left;
1684 }
1685 return choice(left,right);
1686 }
1687 if (is_seq(p))
1688 {
1689 process_expression q=substitute_pCRLproc(seq(p).left(),sigma);
1690 if (q==delta_at_zero())
1691 {
1692 return q;
1693 }
1694 return seq(q, substitute_pCRLproc(seq(p).right(),sigma));
1695 }
1696 if (is_if_then(p))
1697 {
1698 data_expression condition=RewriteTerm(replace_variables_capture_avoiding_alt(if_then(p).condition(), sigma));
1699 if (condition==sort_bool::false_())
1700 {
1701 return delta_at_zero();
1702 }
1703 if (condition==sort_bool::true_())
1704 {
1705 return substitute_pCRLproc(if_then(p).then_case(),sigma);
1706 }
1707 return if_then(condition,substitute_pCRLproc(if_then(p).then_case(),sigma));
1708 }
1709 if (is_if_then_else(p))
1710 {
1711 data_expression condition=RewriteTerm(replace_variables_capture_avoiding_alt(if_then_else(p).condition(), sigma));
1712 if (condition==sort_bool::false_())
1713 {
1714 return substitute_pCRLproc(if_then_else(p).else_case(),sigma);
1715 }
1716 if (condition==sort_bool::true_())
1717 {
1718 return substitute_pCRLproc(if_then_else(p).then_case(),sigma);
1719 }
1720 return if_then_else(
1721 condition,
1722 substitute_pCRLproc(if_then_else(p).then_case(),sigma),
1723 substitute_pCRLproc(if_then_else(p).else_case(),sigma));
1724 }
1725
1726 if (is_sum(p))
1727 {
1728 variable_list sumargs=sum(p).variables();
1729 variable_list vars;
1730 data_expression_list terms;
1731
1732 for( std::map < variable, data_expression >::const_iterator i=sigma.begin(); i!=sigma.end(); ++i)
1733 {
1734 vars=push_back(vars,i->first);
1735 terms=push_back(terms,i->second);
1736 }
1737
1738 maintain_variables_in_rhs< mutable_map_substitution<> > local_sigma=sigma;
1739 alphaconvert(sumargs,local_sigma,vars,terms);
1740
1741 return sum(sumargs,
1742 substitute_pCRLproc(sum(p).operand(),local_sigma));
1743 }
1744
1746 {
1747 const stochastic_operator& sto=down_cast<const stochastic_operator>(p);
1748 variable_list sumargs=sto.variables();
1749 variable_list vars;
1750 data_expression_list terms;
1751
1752 for( std::map < variable, data_expression >::const_iterator i=sigma.begin(); i!=sigma.end(); ++i)
1753 {
1754 vars=push_back(vars,i->first);
1755 terms=push_back(terms,i->second);
1756 }
1757
1758 maintain_variables_in_rhs< mutable_map_substitution<> > local_sigma=sigma;
1759 alphaconvert(sumargs,local_sigma,vars,terms);
1760
1761 return stochastic_operator(
1762 sumargs,
1763 replace_variables_capture_avoiding_alt(sto.distribution(),local_sigma),
1764 substitute_pCRLproc(sto.operand(),local_sigma));
1765 }
1766
1768 {
1771 const variable_list parameters=object.parameters;
1772 const assignment_list new_assignments=substitute_assignmentlist(q.assignments(),parameters,false,true,sigma);
1773 assert(check_valid_process_instance_assignment(q.identifier(),new_assignments));
1774 return process_instance_assignment(q.identifier(),new_assignments);
1775 }
1776
1777 if (is_action(p))
1778 {
1779 return action(action(p).label(),
1780 replace_variables_capture_avoiding_alt(action(p).arguments(), sigma));
1781 }
1782
1783 if (is_at(p))
1784 {
1785 return at(substitute_pCRLproc(at(p).operand(),sigma),
1786 replace_variables_capture_avoiding_alt(at(p).time_stamp(),sigma));
1787 }
1788
1789 if (is_delta(p))
1790 {
1791 return p;
1792 }
1793
1794 if (is_tau(p))
1795 {
1796 return p;
1797 }
1798
1799 if (is_sync(p))
1800 {
1801 return process::sync(
1802 substitute_pCRLproc(process::sync(p).left(),sigma),
1803 substitute_pCRLproc(process::sync(p).right(),sigma));
1804 }
1805
1806 throw mcrl2::runtime_error("Internal error: expect a pCRL process (2) " + process::pp(p));
1807 return process_expression();
1808 }
1809
1810
1811 // The function below transforms a ProcessAssignment to a Process, provided
1812 // that the process is defined in objectnames.
1813
1815 const process_instance& procId,
1816 const std::set<variable>& bound_variables=std::set<variable>())
1817 {
1819 const variable_list process_parameters=object.parameters;
1820 const data_expression_list& rhss=procId.actual_parameters();
1821
1822 assignment_vector new_assignments;
1823 data_expression_list::const_iterator j=rhss.begin();
1824 for(variable_list::const_iterator i=process_parameters.begin(); i!=process_parameters.end(); ++i, ++j)
1825 {
1826 assert(j!=rhss.end());
1827 if (*i==*j)
1828 {
1829 if (bound_variables.count(*i)>0) // Now *j is a different variable than *i
1830 {
1831 new_assignments.emplace_back(*i, *j);
1832 }
1833 }
1834 else
1835 {
1836 new_assignments.emplace_back(*i, *j);
1837 }
1838 }
1839 assert(j==rhss.end());
1840
1841 assert(check_valid_process_instance_assignment(procId.identifier(),assignment_list(new_assignments.begin(),new_assignments.end())));
1842 process_instance_assignment p(procId.identifier(), assignment_list(new_assignments.begin(),new_assignments.end()));
1843 return p;
1844 }
1845
1846 /********************************************************************/
1847 /* */
1848 /* BELOW THE PROCEDURES ARE GIVEN TO TRANSFORM PROCESSES TO */
1849 /* LINEAR PROCESSES. */
1850 /* */
1851 /* */
1852 /* */
1853 /********************************************************************/
1854
1855 using variableposition = enum
1856 {
1857 first,
1858 later
1859 };
1860
1861 /**************** tovarheadGNF *********************************/
1862
1864 const variable_list& parameters,
1865 const process_expression& body)
1866 {
1867 variable_vector result;
1868 for(const variable& p: parameters)
1869 {
1870 if (occursinpCRLterm(p,body,false))
1871 {
1872 result.push_back(p);
1873 }
1874 }
1875 return variable_list(result.begin(),result.end());
1876 }
1877
1878 // The variable below is used to count the number of new processes that
1879 // are made. If this number is very high, it is likely that the regular
1880 // flag is used, and an unbounded number of new processes are generated.
1881 // In such a case a warning is printed suggesting to use regular2.
1882
1884 const variable_list& parameters,
1885 const process_expression& body,
1886 const processstatustype ps,
1887 const bool canterminate,
1888 const bool containstime)
1889 {
1890 assert(canterminatebody(body)==canterminate);
1891 assert(containstimebody(body)==containstime);
1892
1894 if (searchProcDeclaration(parameters,body,ps, canterminate,containstime,p1))
1895 {
1896 return p1; // The process did already exist. No need to make a new one.
1897 }
1898
1899 static std::size_t numberOfNewProcesses = 0;
1900 static std::size_t warningNumber = 25;
1901 numberOfNewProcesses++;
1902 if (numberOfNewProcesses == warningNumber)
1903 {
1904 mCRL2log(mcrl2::log::log_level_t::warning) << "Generated " << numberOfNewProcesses << " new internal processes.";
1905
1906 if (options.lin_method==t_lin_method::lmRegular)
1907 {
1908 mCRL2log(mcrl2::log::log_level_t::warning) << " If linearisation does not terminate, the use of the flag `--lin-method=regular2' or `--lin-method=stack' in mcrl22lps can help." << std::endl;
1909 }
1910 else if (options.lin_method==t_lin_method::lmRegular2)
1911 {
1912 mCRL2log(mcrl2::log::log_level_t::warning) << " If linearisation does not terminate, using the flag `--lin-method=stack' in mcrl22lps can help.\n" << std::endl;
1913 }
1914 else
1915 {
1916 mCRL2log(mcrl2::log::log_level_t::warning) << std::endl;
1917 }
1918 warningNumber=warningNumber*5;
1919 }
1920 const variable_list parameters1=parameters_that_occur_in_body(parameters, body);
1921 const core::identifier_string s=fresh_identifier_generator("P");
1922 const process_identifier p(s, parameters1);
1923 assert(std::string(p.name()).size()>0);
1924 insert_process_declaration(
1925 p,
1926 parameters1,
1927 body,
1928 ps,
1929 canterminate,
1930 containstime);
1931 return p;
1932 }
1933
1934
1936 const process_expression& body,
1937 const data_expression& time,
1938 const variable_list& freevars)
1939 {
1940 if (is_choice(body))
1941 {
1942 return choice(
1943 wraptime(choice(body).left(),time,freevars),
1944 wraptime(choice(body).right(),time,freevars));
1945 }
1946
1947 if (is_sum(body))
1948 {
1949 variable_list sumvars=sum(body).variables();
1951
1952 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
1953 alphaconvert(sumvars,sigma,freevars,data_expression_list());
1954 body1=substitute_pCRLproc(body1, sigma);
1955 maintain_variables_in_rhs< mutable_map_substitution<> > sigma_aux(sigma);
1956 const data_expression time1=/*data::*/replace_variables_capture_avoiding_alt(time, sigma_aux);
1957 body1=wraptime(body1,time1,sumvars+freevars);
1958 return sum(sumvars,body1);
1959 }
1960
1962 {
1963 const stochastic_operator& sto=down_cast<const stochastic_operator>(body);
1964 variable_list sumvars=sto.variables();
1966
1967 maintain_variables_in_rhs<mutable_map_substitution<> > sigma;
1968 alphaconvert(sumvars,sigma,freevars,data_expression_list());
1969 body1=substitute_pCRLproc(body1, sigma);
1970 const data_expression new_distribution=replace_variables_capture_avoiding_alt(sto.distribution(), sigma);
1971 maintain_variables_in_rhs< mutable_map_substitution<> > sigma_aux(sigma);
1972 const data_expression time1=replace_variables_capture_avoiding_alt(time, sigma_aux);
1973 body1=wraptime(body1,time1,sumvars+freevars);
1974 return stochastic_operator(sumvars,new_distribution,body1);
1975 }
1976
1977 if (is_if_then(body))
1978 {
1980 }
1981
1982 if (is_seq(body))
1983 {
1984 return seq(wraptime(seq(body).left(),time,freevars),seq(body).right());
1985 }
1986
1987 if (is_at(body))
1988 {
1989 /* make a new process */
1990 const process_identifier newproc=newprocess(freevars,body,pCRL,
1992 assert(check_valid_process_instance_assignment(newproc,assignment_list()));
1994 newproc,
1995 assignment_list()), //data::data_expression_list(objectdata[objectIndex(newproc)].parameters)),
1996 time);
1997 }
1998
1999 if (// (is_process_instance(body))||
2001 (is_sync(body))||
2002 (is_action(body))||
2003 (is_tau(body))||
2004 (is_delta(body)))
2005 {
2006 return at(body,time);
2007 }
2008
2009 throw mcrl2::runtime_error("Internal error: expect a pCRL process in wraptime " + process::pp(body));
2010 return process_expression();
2011 }
2012
2013 using state = enum
2014 {
2015 alt_state,
2016 sum_state, /* cond,*/
2017 seq_state,
2018 name_state,
2019 multiaction_state
2020 };
2021
2022 variable get_fresh_variable(const std::string& s, const sort_expression& sort, const int reuse_index=-1)
2023 {
2024 /* If reuse_index is smaller than 0 (-1 is the default value), an unused variable name is returned,
2025 based on the string s with sort `sort'. If reuse_index is larger or equal to
2026 0 the reuse_index+1 generated variable is returned. If for a particular reuse_index
2027 this function is called for the first time, it is guaranteed that the returned
2028 variable is unique, and not used as variable elsewhere. Upon subsequent calls
2029 get_fresh_variable will return the same variable for the same s,sort and reuse_triple.
2030 This feature is added to make it possible to avoid generating too many different variables. */
2031
2032
2033 if (reuse_index<0)
2034 {
2035 variable v(fresh_identifier_generator(s),sort);
2036 insertvariable(v,true);
2037 return v;
2038 }
2039 else
2040 {
2041 static std::map < int , std::map < variable,variable > > generated_variables;
2042 variable table_index_term(s,sort);
2043 variable old_variable;
2044 if (generated_variables[reuse_index].count(table_index_term)>0)
2045 {
2046 old_variable=generated_variables[reuse_index][table_index_term];
2047 }
2048 else
2049 {
2050 /* A new variable must be generated */
2051 old_variable=get_fresh_variable(s,sort);
2052 generated_variables[reuse_index][table_index_term]=old_variable;
2053 }
2054 return old_variable;
2055 }
2056 }
2057
2058 variable_list make_pars(const sort_expression_list& sortlist)
2059 {
2060 /* this function returns a list of variables,
2061 corresponding to the sorts in sortlist */
2062
2063 if (sortlist.empty())
2064 {
2065 return variable_list();
2066 }
2067
2068 const sort_expression& sort=sortlist.front();
2069
2070 variable_list result=make_pars(sortlist.tail());
2071 result.push_front(get_fresh_variable("a",sort));
2072 return result;
2073 }
2074
2076 const process_expression& act, // This is a multi-action, actually.
2077 const data_expression& condition,
2078 const process_expression& restterm,
2079 const variable_list& freevars,
2080 const std::set<variable>& variables_bound_in_sum)
2081 {
2082 if (is_if_then(restterm))
2083 {
2084 /* Here we check whether the process body has the form
2085 a (c -> x). For state space generation it turns out
2086 to be beneficial to rewrite this to c-> a x + !c -> a.delta@0, as in
2087 certain cases this leads to a reduction of the number
2088 of states. In this code, we recursively check whether
2089 the action must be distributed over x. This optimisation
2090 was observed by Yaroslav Usenko, May 2006. Implemented by JFG.
2091 On industrial examples, it appears to reduce the state space
2092 with a factor up to 2.
2093 Before october 2008 this code was wrong, as it transformed
2094 an expression to c-> a x, ommitting the a.delta@0. */
2095
2096 const data_expression& c=down_cast<if_then>(restterm).condition();
2097 const process_expression r=choice(
2098 distributeActionOverConditions(
2099 act,
2100 lazy::and_(condition,c),
2101 if_then(restterm).then_case(),
2102 freevars,variables_bound_in_sum),
2103 distributeActionOverConditions(
2104 act,
2105 lazy::and_(condition,lazy::not_(c)),
2106 delta_at_zero(),
2107 freevars,variables_bound_in_sum));
2108 return r;
2109 }
2110 if (is_if_then_else(restterm))
2111 {
2112 /* Here we check whether the process body has the form
2113 a (c -> x <> y). For state space generation it turns out
2114 to be beneficial to rewrite this to c-> a x + !c -> a y, as in
2115 certain cases this leads to a reduction of the number
2116 of states, despite the duplication of the a action. In this code,
2117 we recursively check whether the action must be distributed over
2118 x and y. This optimisation
2119 was observed by Yaroslav Usenko, May 2006. Implemented by JFG.
2120 On industrial examples, it appears to reduce the state space
2121 with a factor up to 2. */
2122
2123 const data_expression& c=down_cast<if_then_else>(restterm).condition();
2124 const process_expression r=choice(
2125 distributeActionOverConditions(
2126 act,
2127 lazy::and_(condition,c),
2128 if_then_else(restterm).then_case(),
2129 freevars,variables_bound_in_sum),
2130 distributeActionOverConditions(
2131 act,
2132 lazy::and_(condition,lazy::not_(c)),
2133 if_then_else(restterm).else_case(),
2134 freevars,variables_bound_in_sum));
2135 return r;
2136 }
2137 const process_expression restterm1=bodytovarheadGNF(restterm,seq_state,freevars,later,variables_bound_in_sum);
2138 return if_then(condition,seq(act,restterm1));
2139 }
2140
2141
2142 static
2143 assignment_list parameters_to_assignment_list(const variable_list& parameters, const std::set<variable>& variables_bound_in_sum)
2144 {
2145 assignment_vector result;
2146 for(const variable& v: parameters)
2147 {
2148 if (variables_bound_in_sum.count(v)>0)
2149 {
2150 result.emplace_back(v, v); // rhs is another variable than the lhs!!
2151 }
2152 }
2153 return assignment_list(result.begin(),result.end());
2154 }
2155
2156
2158 const process_expression& body,
2159 const state s,
2160 const variable_list& freevars,
2161 const variableposition v,
2162 const std::set<variable>& variables_bound_in_sum)
2163 {
2164 /* it is assumed that we only receive processes with
2165 operators alt, seq, sum_state, cond, name, delta, tau, sync, at and stochastic operator in it */
2166
2167 if (is_choice(body))
2168 {
2169 if (alt_state>=s)
2170 {
2171 const process_expression body1=bodytovarheadGNF(choice(body).left(),alt_state,freevars,first,variables_bound_in_sum);
2172 const process_expression body2=bodytovarheadGNF(choice(body).right(),alt_state,freevars,first,variables_bound_in_sum);
2173 if (isDeltaAtZero(body1))
2174 {
2175 return body2;
2176 }
2177 if (isDeltaAtZero(body2))
2178 {
2179 return body1;
2180 }
2181 return choice(body1,body2);
2182 }
2183 const process_expression body1=bodytovarheadGNF(body,alt_state,freevars,first,variables_bound_in_sum);
2184 const process_identifier newproc=newprocess(freevars,body1,pCRL,
2187 assert(check_valid_process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum)));
2188 return process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum));
2189 }
2190
2191 if (is_sum(body))
2192 {
2193 if (sum_state>=s)
2194 {
2195 variable_list sumvars=sum(body).variables();
2197
2198 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
2199 alphaconvert(sumvars,sigma,freevars,data_expression_list());
2200 body1=substitute_pCRLproc(body1,sigma);
2201 std::set<variable> variables_bound_in_sum1=variables_bound_in_sum;
2202 variables_bound_in_sum1.insert(sumvars.begin(),sumvars.end());
2203 body1=bodytovarheadGNF(body1,sum_state,sumvars+freevars,first,variables_bound_in_sum1);
2204 /* Due to the optimisation below, suggested by Yaroslav Usenko, bodytovarheadGNF(...,sum_state,...)
2205 can deliver a process of the form c -> x + !c -> y. In this case, the
2206 sumvars must be distributed over both summands. */
2207 if (is_choice(body1))
2208 {
2209 return choice(sum(sumvars,choice(body1).left()),
2210 sum(sumvars,choice(body1).right()));
2211 }
2212 return sum(sumvars,body1);
2213 }
2214 const process_expression body1=bodytovarheadGNF(body,alt_state,freevars,first,variables_bound_in_sum);
2215 const process_identifier newproc=newprocess(freevars,body1,pCRL,
2218 assert(check_valid_process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum)));
2219 return process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum));
2220 }
2221
2223 {
2224 if (seq_state>=s)
2225 {
2226 const stochastic_operator& sto=down_cast<const stochastic_operator>(body);
2227 variable_list sumvars=sto.variables();
2228 data_expression distribution=sto.distribution();
2230
2231 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
2232 alphaconvert(sumvars,sigma,freevars,data_expression_list());
2233 distribution=/* data::*/replace_variables_capture_avoiding_alt(distribution,sigma);
2234 body1=substitute_pCRLproc(body1,sigma);
2235 std::set<variable> variables_bound_in_sum1=variables_bound_in_sum;
2236 variables_bound_in_sum1.insert(sumvars.begin(),sumvars.end());
2237 body1=bodytovarheadGNF(body1,name_state,sumvars+freevars,v,variables_bound_in_sum1);
2238 /* Due to the optimisation below, suggested by Yaroslav Usenko, bodytovarheadGNF(...,sum_state,...)
2239 can deliver a process of the form c -> x + !c -> y. In this case, the
2240 sumvars must be distributed over both summands. */
2241 return stochastic_operator(sumvars,distribution,body1);
2242 }
2243 const process_expression body_=bodytovarheadGNF(body,seq_state,freevars,first,variables_bound_in_sum);
2244 const process_identifier newproc=newprocess(freevars,body_,pCRL,
2247 assert(check_valid_process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum)));
2248 return process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum));
2249 }
2250
2251 if (is_if_then(body))
2252 {
2253 const data_expression& condition=down_cast<if_then>(body).condition();
2254 const process_expression& body1=down_cast<if_then>(body).then_case();
2255
2256 if (s<=sum_state)
2257 {
2258 return if_then(
2259 condition,
2260 bodytovarheadGNF(body1,seq_state,freevars,first,variables_bound_in_sum));
2261 }
2262 const process_expression body2=bodytovarheadGNF(body,alt_state,freevars,first,variables_bound_in_sum);
2263 const process_identifier newproc=newprocess(freevars,body2,pCRL,
2266 assert(check_valid_process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum)));
2267 return process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum));
2268
2269 }
2270
2271 if (is_if_then_else(body))
2272 {
2273 const data_expression& condition=down_cast<if_then_else>(body).condition();
2274 const process_expression& body1=down_cast<if_then_else>(body).then_case();
2275 const process_expression& body2=down_cast<if_then_else>(body).else_case();
2276
2277 if (isDeltaAtZero(body1) && isDeltaAtZero(body2))
2278 {
2279 return body1;
2280 }
2281
2282 if ((s<=sum_state) && ((isDeltaAtZero(body1))||(isDeltaAtZero(body2))))
2283 {
2284 if (isDeltaAtZero(body2))
2285 {
2286 return if_then(
2287 condition,
2288 bodytovarheadGNF(body1,seq_state,freevars,first,variables_bound_in_sum));
2289 }
2290 /* body1=="Delta@0" */
2291 {
2292 return if_then(
2293 lazy::not_(condition),
2294 bodytovarheadGNF(body2,seq_state,freevars,first,variables_bound_in_sum));
2295 }
2296 }
2297 if (alt_state==s) /* body1!=Delta@0 and body2!=Delta@0 */
2298 {
2299 return
2300 choice(
2301 if_then(
2302 condition,
2303 bodytovarheadGNF(body1,seq_state,freevars,first,variables_bound_in_sum)),
2304 if_then(
2305 lazy::not_(condition),
2306 bodytovarheadGNF(body2,seq_state,freevars,first,variables_bound_in_sum)));
2307 }
2308 const process_expression body3=bodytovarheadGNF(body,alt_state,freevars,first,variables_bound_in_sum);
2309 const process_identifier newproc=newprocess(freevars,body3,pCRL,
2312 assert(check_valid_process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum)));
2313 return process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum));
2314
2315 }
2316
2317 if (is_seq(body))
2318 {
2321
2322 if (s<=seq_state)
2323 {
2324 body1=bodytovarheadGNF(body1,name_state,freevars,v,variables_bound_in_sum);
2325 if (!canterminatebody(body1))
2326 {
2327 /* In this case there is no need to investigate body2, as it cannot be reached. */
2328 return body1;
2329 }
2330 if ((is_if_then(body2)) && (s<=sum_state))
2331 {
2332 /* Here we check whether the process body has the form
2333 a (c -> x) + !c -> delta@0. For state space generation it turns out
2334 to be beneficial to rewrite this to c-> a x, as in
2335 certain cases this leads to a reduction of the number
2336 of states. An extra change (24/12/2006) is that the
2337 conditions are distributed recursively over
2338 all conditions. The optimisation
2339 was observed by Yaroslav Usenko, May 2006. Implemented by JFG.
2340 On industrial examples, it appears to reduce the state space
2341 with a factor up to 2. Until 1/11/2008 this code was incorrect,
2342 because the summand a (!c -> delta@0) was not forgotten.*/
2343
2344 const data_expression& c=down_cast<if_then>(body2).condition();
2345
2346 const process_expression r= choice(
2347 distributeActionOverConditions(body1,c,if_then(body2).then_case(),freevars,variables_bound_in_sum),
2348 distributeActionOverConditions(body1,lazy::not_(c),delta_at_zero(),freevars,variables_bound_in_sum));
2349 return r;
2350 }
2351 if ((is_if_then_else(body2)) && (s<=sum_state))
2352 {
2353
2354 /* Here we check whether the process body has the form
2355 a (c -> x <> y). For state space generation it turns out
2356 to be beneficial to rewrite this to c-> a x + !c -> a y, as in
2357 certain cases this leads to a reduction of the number
2358 of states, despite the duplication of the a action. An extra
2359 change (24/12/2006) is that the conditions are distributed recursively over
2360 all conditions. The optimisation
2361 was observed by Yaroslav Usenko, May 2006. Implemented by JFG.
2362 On industrial examples, it appears to reduce the state space
2363 with a factor up to 2. */
2364
2365
2366 const data_expression& c=down_cast<if_then_else>(body2).condition();
2367 const process_expression r= choice(
2368 distributeActionOverConditions(body1,c,if_then_else(body2).then_case(),freevars,variables_bound_in_sum),
2369 distributeActionOverConditions(body1,lazy::not_(c),if_then_else(body2).else_case(),freevars,variables_bound_in_sum));
2370 return r;
2371 }
2372 body2=bodytovarheadGNF(body2,seq_state,freevars,later,variables_bound_in_sum);
2373 return seq(body1,body2);
2374 }
2375 body1=bodytovarheadGNF(body,alt_state,freevars,first,variables_bound_in_sum);
2376 const process_identifier newproc=newprocess(freevars,body1,pCRL,canterminatebody(body1),
2378
2379 assert(check_valid_process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum)));
2380 return process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum));
2381 }
2382
2383 if (is_action(body))
2384 {
2385 if ((s==multiaction_state)||(v==first))
2386 {
2387 return body;
2388 }
2389
2390 bool isnew=false;
2392
2394 {
2395 /* this action does not yet have a corresponding process, which
2396 must be constructed. The resulting process is stored in
2397 the variable process_representing_action in objectdata. Tempvar below is
2398 needed as objectdata may be realloced as a side effect
2399 of newprocess */
2400 const process_identifier tempvar=newprocess(
2401 object.parameters,
2402 object.processbody,
2403 GNF,true,false);
2404 object.process_representing_action=tempvar;
2405 }
2406 return transform_process_instance_to_process_instance_assignment(
2407 process_instance(object.process_representing_action,
2408 action(body).arguments()));
2409 }
2410
2411 if (is_sync(body))
2412 {
2413 bool isnew=false;
2414 const process_expression& body1=down_cast<process::sync>(body).left();
2415 const process_expression& body2=down_cast<process::sync>(body).right();
2416 const action_list ma=linMergeMultiActionListProcess(
2417 bodytovarheadGNF(body1,multiaction_state,freevars,v,variables_bound_in_sum),
2418 bodytovarheadGNF(body2,multiaction_state,freevars,v,variables_bound_in_sum));
2419
2420 const process_expression mp=action_list_to_process(ma);
2421 if ((s==multiaction_state)||(v==first))
2422 {
2423 return mp;
2424 }
2425
2426 objectdatatype& object=addMultiAction(mp,isnew);
2427
2429 {
2430 /* this action does not yet have a corresponding process, which
2431 must be constructed. The resulting process is stored in
2432 the variable process_representing_action in objectdata. Tempvar below is needed
2433 as objectdata may be realloced as a side effect of newprocess */
2435 object.parameters,
2436 object.processbody,
2437 GNF,true,false);
2438 object.process_representing_action=tempvar;
2439 }
2440 return transform_process_instance_to_process_instance_assignment(
2441 process_instance(
2442 process_identifier(object.process_representing_action),
2443 getarguments(ma)));
2444 }
2445
2446 if (is_at(body))
2447 {
2448 process_expression body1=bodytovarheadGNF(
2449 at(body).operand(),
2450 s,
2451 freevars,
2452 first,variables_bound_in_sum);
2454 /* put the time operator around the first action or process */
2455 body1=wraptime(body1,time,freevars);
2456 if (v==first)
2457 {
2458 return body1;
2459 }
2460
2461 /* make a new process, containing this process */
2462 const process_identifier newproc=newprocess(freevars,body1,pCRL,
2465 assert(check_valid_process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum)));
2466 return process_instance_assignment(newproc,parameters_to_assignment_list(objectIndex(newproc).parameters,variables_bound_in_sum));
2467 }
2468
2470 {
2471 return body;
2472 }
2473
2474
2475 if (is_tau(body))
2476 {
2477 if (v==first)
2478 {
2479 return tau();
2480 }
2482 return process_instance_assignment(tau_process,assignment_list());
2483 }
2484
2485 if (is_delta(body))
2486 {
2487 if (v==first)
2488 {
2489 return body;
2490 }
2492 return process_instance_assignment(delta_process,assignment_list());
2493 }
2494
2495 throw mcrl2::runtime_error("unexpected process format in bodytovarheadGNF " + process::pp(body) +".");
2496 return process_expression();
2497 }
2498
2499 void procstovarheadGNF(const std::vector < process_identifier>& procs)
2500 {
2501 /* transform the processes in procs into pre-Greibach Normal Form */
2502 for (const process_identifier& i: procs)
2503 {
2504 objectdatatype& object=objectIndex(i);
2505
2506 // The intermediate variable result is needed here
2507 // because objectdata can be reallocated as a side
2508 // effect of bodytovarheadGNF.
2509
2510 std::set<variable> variables_bound_in_sum;
2511 const process_expression result=
2512 bodytovarheadGNF(
2513 object.processbody,
2514 alt_state,
2515 object.parameters,
2516 first,
2517 variables_bound_in_sum);
2518 object.processbody=result;
2519 }
2520 }
2521
2522 /**************** towards real GREIBACH normal form **************/
2523
2525 {
2526 terminating,
2527 infinite
2528 };
2529
2531 {
2532 if (is_choice(body1))
2533 {
2534 return choice(
2537 }
2538
2539 if (is_seq(body1))
2540 {
2541 return seq(seq(body1).left(), putbehind(seq(body1).right(),body2));
2542 }
2543
2544 if (is_if_then(body1))
2545 {
2547 }
2548
2549 if (is_sum(body1))
2550 {
2551 /* we must take care that no variables in body2 are
2552 inadvertently bound */
2553 variable_list sumvars=sum(body1).variables();
2554
2555 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
2556 alphaconvertprocess(sumvars,sigma,body2);
2557 return sum(sumvars,
2558 putbehind(substitute_pCRLproc(sum(body1).operand(), sigma),
2559 body2));
2560 }
2561
2562 if (is_stochastic_operator(body1))
2563 {
2564 const stochastic_operator& sto=atermpp::down_cast<stochastic_operator>(body1);
2565 variable_list stochvars=sto.variables();
2566
2567 // See explanation at sum operator above.
2568 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
2569 alphaconvertprocess(stochvars,sigma,body2);
2570 return stochastic_operator(
2571 stochvars,
2572 sto.distribution(),
2573 putbehind(
2574 substitute_pCRLproc(sto.operand(),sigma),
2575 body2));
2576 }
2577
2578
2579 if (is_action(body1))
2580 {
2581 return seq(body1,body2);
2582 }
2583
2584 if (is_sync(body1))
2585 {
2586 return seq(body1,body2);
2587 }
2588
2590 {
2591 return seq(body1,body2);
2592 }
2593
2594 if (is_delta(body1))
2595 {
2596 return body1;
2597 }
2598
2599 if (is_tau(body1))
2600 {
2601 return seq(body1,body2);
2602 }
2603
2604 if (is_at(body1))
2605 {
2606 return seq(body1,body2);
2607 }
2608
2609 throw mcrl2::runtime_error("Internal error. Unexpected process format in putbehind " + process::pp(body1) +".");
2610 return process_expression();
2611 }
2612
2613
2615 const process_expression& body1,
2616 const data_expression& condition)
2617 {
2618 if (is_choice(body1))
2619 {
2620 return choice(
2623 }
2624
2625 if (is_seq(body1))
2626 {
2627 return if_then(condition,body1);
2628 }
2629
2630 if (is_if_then(body1))
2631 {
2632 return if_then(
2635 }
2636
2637 if (is_sum(body1))
2638 {
2639 /* we must take care that no variables in condition are
2640 inadvertently bound */
2641 variable_list sumvars=sum(body1).variables();
2642 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
2643 alphaconvert(sumvars,sigma,variable_list(), data_expression_list({ condition }));
2644 return sum(
2645 sumvars,
2646 distribute_condition(
2647 substitute_pCRLproc(sum(body1).operand(),sigma),
2648 condition));
2649 }
2650
2651 if (is_stochastic_operator(body1))
2652 {
2653 /* we must take care that no variables in condition are
2654 inadvertently bound */
2655 const stochastic_operator& sto=atermpp::down_cast<stochastic_operator>(body1);
2656 variable_list stochvars=sto.variables();
2657 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
2658 alphaconvert(stochvars,sigma,variable_list(), data_expression_list({ condition }));
2659
2660 return stochastic_operator(
2661 stochvars,
2662 if_(condition,
2663 replace_variables_capture_avoiding_alt(
2664 sto.distribution(),
2665 sigma),
2666 if_(variables_are_equal_to_default_values(stochvars),real_one(),real_zero())),
2667 distribute_condition(
2668 substitute_pCRLproc(sto.operand(),sigma),
2669 condition));
2670 }
2671
2672 if (is_at(body1)||
2673 is_action(body1)||
2674 is_sync(body1)||
2676 is_delta(body1)||
2677 is_tau(body1))
2678 {
2679 return if_then(condition,body1);
2680 }
2681
2682 throw mcrl2::runtime_error("Internal error. Unexpected process format in distribute condition " + process::pp(body1) +".");
2683 }
2684
2685 /* This process calculates the equivalent of sum sumvars dist stochvars[distribution] body
2686 where the distribution occurs in front. This can only be done under limited circumstances.
2687 Assume we can enumerate the values of sumvars by e1,...,en. Introduce n copies of stochvars,
2688 i.e., stochvars1,...,stochvarsn. The new process becomes
2689
2690 dist stochvars1,stochvars2,...,stochvarsn
2691 [distribution(e1,stochvars1)*distribution(e2,stochvars2)*...*distribution(en,stochvarsn)].
2692
2693 body(e1,stochvars1)+
2694 body(e2,stochvars2)+
2695 ...
2696 body(en,stochvarsn)
2697 */
2698
2700 const variable_list& sumvars,
2701 const variable_list& stochvars,
2702 const data_expression& distribution,
2703 const process_expression& body)
2704 {
2705 if (options.norewrite)
2706 {
2707 throw mcrl2::runtime_error("The use of the rewriter must be allowed to distribute a sum operator over a distribution.");
2708 }
2709
2710
2711 std::vector < data_expression_vector > data_vector(1,data_expression_vector());
2712 for(const variable& v:sumvars)
2713 {
2714 std::vector < data_expression_vector > new_data_vector;
2715
2716 if (!data.is_certainly_finite(v.sort()))
2717 {
2718 throw mcrl2::runtime_error("Cannot distribute a sum variable of non finite sort " + pp(v.sort()) + " over a distribution, which is required for linearisation.");
2719 }
2720
2721 for(const data_expression& d: enumerate_expressions(v.sort(),data,rewr))
2722 {
2723 for(const data_expression_vector& dv: data_vector)
2724 {
2725 data_expression_vector new_dv=dv;
2726 new_dv.push_back(d);
2727 new_data_vector.push_back(new_dv);
2728 }
2729
2730 }
2731 data_vector.swap(new_data_vector);
2732
2733 }
2734 assert(!data_vector.empty());
2735
2736 process_expression resulting_body;
2737 data_expression resulting_distribution;
2738 variable_list resulting_stochastic_variables;
2739 bool result_defined=false;
2740 for(const data_expression_vector& d: data_vector)
2741 {
2742 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
2743 variable_list vl = stochvars;
2744 alphaconvert(vl,sigma,stochvars,data_expression_list());
2745 data_expression_vector::const_iterator i=d.begin();
2746 for(const variable& v: sumvars)
2747 {
2748 sigma[v] = *i;
2749 ++i;
2750 }
2751 const process_expression d1=substitute_pCRLproc(body,sigma);
2752 const data_expression new_distribution=replace_variables_capture_avoiding_alt(distribution, sigma);
2753
2754 if (result_defined)
2755 {
2756 resulting_body=choice(resulting_body, d1);
2757 resulting_distribution=sort_real::times(resulting_distribution,new_distribution);
2758 resulting_stochastic_variables=resulting_stochastic_variables + vl;
2759 }
2760 else
2761 {
2762 resulting_body=d1;
2763 resulting_distribution=new_distribution;
2764 resulting_stochastic_variables=vl;
2765 result_defined=true;
2766 }
2767 }
2768 /* Put the distribution in front. */
2769
2770 return stochastic_operator(resulting_stochastic_variables, resulting_distribution, resulting_body);
2771 }
2772
2774 const variable_list& sumvars,
2775 const variable_list& stochastic_variables,
2776 const data_expression& distribution,
2777 const process_expression& body)
2778 {
2779 if (is_sum(body)||
2780 is_choice(body)||
2781 is_seq(body)||
2782 is_if_then(body)||
2783 is_sync(body)||
2784 is_action(body)||
2785 is_tau(body)||
2786 is_at(body)||
2788 isDeltaAtZero(body))
2789 {
2790 return enumerate_distribution_and_sums(sumvars,stochastic_variables,distribution,body);
2791 }
2792
2793 if (is_delta(body)||
2794 is_tau(body))
2795 {
2796 return body;
2797 }
2798
2800 {
2801 const stochastic_operator& sto=atermpp::down_cast<stochastic_operator>(body);
2802 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
2803 variable_list inner_stoch_vars=sto.variables();
2804 alphaconvert(inner_stoch_vars,sigma,sumvars,data_expression_list());
2805 const process_expression new_body=substitute_pCRLproc(sto.operand(), sigma);
2806 const data_expression new_distribution=replace_variables_capture_avoiding_alt(sto.distribution(), sigma);
2807 return distribute_sum_over_a_stochastic_operator(sumvars,
2808 stochastic_variables + inner_stoch_vars,
2809 sort_real::times(distribution,new_distribution), new_body);
2810 }
2811
2812 throw mcrl2::runtime_error("Internal error. Unexpected process format in distribute_sum " + process::pp(body) +".");
2813 return process_expression();
2814 }
2815
2817 const variable_list& sumvars,
2818 const process_expression& body1)
2819 {
2820 if (is_choice(body1))
2821 {
2822 return choice(
2825 }
2826
2827 if (is_seq(body1)||
2828 is_if_then(body1)||
2829 is_sync(body1)||
2830 is_action(body1)||
2831 is_tau(body1)||
2832 is_at(body1)||
2834 isDeltaAtZero(body1))
2835 {
2836 return sum(sumvars,body1);
2837 }
2838
2839 if (is_sum(body1))
2840 {
2841 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
2842 variable_list inner_sumvars=sum(body1).variables();
2843 alphaconvert(inner_sumvars,sigma,sumvars,data_expression_list());
2844 const process_expression new_body1=substitute_pCRLproc(sum(body1).operand(), sigma);
2845 return sum(sumvars+inner_sumvars,new_body1);
2846 }
2847
2848 if (is_stochastic_operator(body1))
2849 {
2850 const stochastic_operator& sto=atermpp::down_cast<stochastic_operator>(body1);
2851 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
2852 variable_list inner_stoch_vars=sto.variables();
2853 alphaconvert(inner_stoch_vars,sigma,sumvars,data_expression_list());
2854 const process_expression new_body1=substitute_pCRLproc(sto.operand(), sigma);
2855 const data_expression new_distribution=replace_variables_capture_avoiding_alt(sto.distribution(), sigma);
2856 return distribute_sum_over_a_stochastic_operator(sumvars, inner_stoch_vars, new_distribution, new_body1);
2857 }
2858
2859 if (is_delta(body1)||
2860 is_tau(body1))
2861 {
2862 return body1;
2863 }
2864
2865 throw mcrl2::runtime_error("Internal error. Unexpected process format in distribute_sum " + process::pp(body1) +".");
2866 return process_expression();
2867 }
2868
2869 static
2871 const std::vector < process_instance_assignment >& s1,
2872 const std::vector < process_instance_assignment >& s2,
2873 const bool regular2)
2874 {
2875 /* s1 and s2 are sequences of typed variables of
2876 the form Process(ProcVarId("P2",[SortId("Bit"),
2877 SortId("Bit")]),[OpId("b1",SortId("Bit")),OpId("b1",SortId("Bit"))]).
2878 This function yields true if the names and types of
2879 the processes in s1 and s2 match. */
2880
2881 std::vector < process_instance_assignment >::const_iterator i2=s2.begin();
2882 for (std::vector < process_instance_assignment >::const_iterator i1=s1.begin();
2883 i1!=s1.end(); ++i1,++i2)
2884 {
2885 if (i2==s2.end())
2886 {
2887 return false;
2888 }
2889 if (regular2)
2890 {
2891 if (i1->identifier()!=i2->identifier())
2892 {
2893 return false;
2894 }
2895 }
2896 else
2897 {
2898 if (*i1!=*i2)
2899 {
2900 return false;
2901 }
2902 }
2903 }
2904 if (i2!=s2.end())
2905 {
2906 return false;
2907 }
2908 return true;
2909 }
2910
2912 const std::vector < process_instance_assignment >& process_names,
2913 process_identifier& result)
2914 {
2915 std::vector < std::vector < process_instance_assignment > >::const_iterator rwalker=representedprocesses.begin();
2916 for (std::vector < process_identifier >::const_iterator walker=seq_varnames.begin();
2917 walker!=seq_varnames.end(); ++walker,++rwalker)
2918 {
2919 assert(rwalker!=representedprocesses.end());
2920 const process_identifier process=*walker;
2921 if (match_sequence(process_names,*rwalker,options.lin_method==t_lin_method::lmRegular2))
2922 {
2923 result=process;
2924 return true;
2925 }
2926 }
2927 assert(rwalker==representedprocesses.end());
2928 return false;
2929 }
2930
2932 const process_expression& sequence,
2933 std::vector < process_instance_assignment >& result)
2934 {
2935 if (is_action(sequence)||is_process_instance_assignment(sequence))
2936 {
2937 result.push_back(atermpp::down_cast<process_instance_assignment>(sequence));
2938 return;
2939 }
2940
2941 if (is_stochastic_operator(sequence))
2942 {
2943 const stochastic_operator& sto=atermpp::down_cast<stochastic_operator>(sequence);
2944 extract_names(sto.operand(),result);
2945 return;
2946 }
2947
2948 if (is_seq(sequence))
2949 {
2950 const process_expression& first=down_cast<seq>(sequence).left();
2952 {
2953 result.push_back(atermpp::down_cast<process_instance_assignment>(first));
2954 objectdatatype& object=objectIndex(atermpp::down_cast<process_instance_assignment>(first).identifier());
2955 if (object.canterminate)
2956 {
2957 extract_names(seq(sequence).right(),result);
2958 }
2959 return;
2960 }
2961 }
2962
2963 throw mcrl2::runtime_error("Internal error. Expected sequence of process names (1) " + process::pp(sequence) + ".");
2964 }
2965
2966 variable_list parscollect(const process_expression& oldbody, process_expression& newbody)
2967 {
2968 /* we expect that oldbody is a sequence of process instances */
2969
2971 {
2973 const variable_list parameters=objectIndex(procId).parameters;
2974 assert(check_valid_process_instance_assignment(procId,data::assignment_list()));
2975 newbody=process_instance_assignment(procId,data::assignment_list());
2976 return parameters;
2977 }
2978
2979 if (is_seq(oldbody))
2980 {
2981 const process_expression& first=down_cast<seq>(oldbody).left();
2983 {
2985 if (object.canterminate)
2986 {
2988 const variable_list pars=parscollect(seq(oldbody).right(),newbody);
2989 variable_list pars1;
2990 variable_list pars2;
2991 const variable_list new_pars=construct_renaming(pars,objectIndex(procId).parameters,pars1,pars2,false);
2992 assignment_vector new_assignment;
2993 for(variable_list::const_iterator i=pars2.begin(), j=new_pars.begin(); i!=pars2.end(); ++i,++j)
2994 {
2995 assert(j!=new_pars.end());
2996 new_assignment.emplace_back(*i, *j);
2997 }
2998 assert(check_valid_process_instance_assignment(procId,assignment_list(new_assignment.begin(),new_assignment.end())));
2999 newbody=seq(process_instance_assignment(procId,assignment_list(new_assignment.begin(),new_assignment.end())),newbody);
3000 const variable_list result=pars1+pars;
3001 assert(std::set<variable>(result.begin(),result.end()).size()==result.size()); // all elements in the result are unique.
3002 return result;
3003 }
3004 else
3005 {
3006 return parscollect(first,newbody);
3007 }
3008 }
3009 }
3010
3011 throw mcrl2::runtime_error("Internal error. Expected a sequence of process names (2) " + process::pp(oldbody) +".");
3012 return variable_list();
3013 }
3014
3015 assignment_list argscollect_regular(
3016 const process_expression& t,
3017 const variable_list& vl,
3018 const std::set<variable>& variables_bound_in_sum)
3019 {
3020 assignment_vector result;
3021 for(const variable& v: vl)
3022 {
3023 if (variables_bound_in_sum.count(v)>0 && occursinpCRLterm(v,t,false))
3024 {
3025 result.emplace_back(v, v); // Here an identity assignment is used, as it is possible
3026 // that the *i at the lhs is not the same variable as *i at the rhs.
3027 }
3028 }
3029 return assignment_list(result.begin(),result.end());
3030 }
3031
3032 assignment_list argscollect_regular2(const process_expression& t, variable_list& vl)
3033 {
3035 {
3038
3039 const variable_list pars=object.parameters; // These are the old parameters of the process.
3040 assert(pars.size()<=vl.size());
3041
3042 std::map<variable,data_expression>sigma;
3043 for(const assignment& a: p.assignments())
3044 {
3045 sigma[a.lhs()]=a.rhs();
3046 }
3047
3048 assignment_list result;
3049 for(variable_list::const_iterator i=pars.begin(); i!=pars.end(); ++i, vl.pop_front())
3050 {
3051 assert(!vl.empty());
3052 const data_expression new_rhs=make_map_substitution(sigma)(*i);
3053 result.push_front(assignment(vl.front(),new_rhs)); // Identity assignments are stored as lhs and rhs may
3054 // refer to different variables.
3055 }
3056 return reverse(result);
3057 }
3058
3059 if (is_seq(t))
3060 {
3061 const process_instance_assignment firstproc=atermpp::down_cast<process_instance_assignment>(seq(t).left());
3063 const assignment_list first_assignment=argscollect_regular2(firstproc,vl);
3064 if (object.canterminate)
3065 {
3066 return first_assignment + argscollect_regular2(seq(t).right(),vl);
3067 }
3068 return first_assignment;
3069 }
3070
3071 throw mcrl2::runtime_error("Internal error. Expected a sequence of process names (3) " + process::pp(t) +".");
3072 }
3073
3075 {
3077 {
3078 return t;
3079 }
3080
3082 {
3083 const stochastic_operator& sto=atermpp::down_cast<const stochastic_operator>(t);
3085 }
3086
3087 if (is_seq(t))
3088 {
3089 const process_expression& firstproc=down_cast<seq>(t).left();
3091 if (object.canterminate)
3092 {
3094 }
3095 return firstproc;
3096 }
3097
3098 throw mcrl2::runtime_error("Internal error. Expected a sequence of process names (4) " + process::pp(t) +".");
3099 return process_expression();
3100 }
3101
3103 process_expression sequence,
3104 std::vector <process_identifier>& todo,
3105 const variable_list& freevars,
3106 const std::set<variable>& variables_bound_in_sum)
3107 {
3108 process_identifier new_process;
3109
3110 /* Sequence consists of a sequence of process references,
3111 concatenated with the sequential composition operator, which may be preceded by
3112 a stochastic operator. */
3113 if (is_stochastic_operator(sequence))
3114 {
3115 /* Leave the stochastic operators in place */
3116 const stochastic_operator& sto=atermpp::down_cast<const stochastic_operator>(sequence);
3117 std::set<variable> variables_bound_in_sum_new=variables_bound_in_sum;
3118 variables_bound_in_sum_new.insert(sto.variables().begin(),sto.variables().end());
3119 return stochastic_operator(sto.variables(),
3120 sto.distribution(),
3121 create_regular_invocation(sto.operand(),todo,freevars+sto.variables(),variables_bound_in_sum_new));
3122 }
3123
3124 sequence=cut_off_unreachable_tail(sequence);
3125 sequence=pCRLrewrite(sequence);
3126 std::vector < process_instance_assignment > process_names;
3127 extract_names(sequence,process_names);
3128 assert(!process_names.empty());
3129
3130 if (process_names.size()==1)
3131 {
3132 /* length of list equals 1 */
3134 {
3135 return sequence;
3136 }
3137 if (is_seq(sequence))
3138 {
3139 return seq(sequence).left();
3140 }
3141 throw mcrl2::runtime_error("Internal error. Expected a sequence of process names " + process::pp(sequence) +".");
3142 }
3143 /* There is more than one process name in the sequence,
3144 so, we must replace them by a single name */
3145
3146 /* We first start out by searching whether
3147 there is already a variable with a matching sequence
3148 of variables */
3149 if (!exists_variable_for_sequence(process_names,new_process))
3150 {
3151 /* There does not exist an appropriate variable,
3152 so, make it and return its index in n */
3153 process_expression newbody;
3154 if (options.lin_method==t_lin_method::lmRegular2)
3155 {
3156 variable_list pars=parscollect(sequence,newbody);
3157 new_process=newprocess(pars,newbody,pCRL,
3158 canterminatebody(newbody),
3159 containstimebody(newbody));
3160 representedprocesses.push_back(process_names);
3161 }
3162 else
3163 {
3164 new_process=newprocess(freevars,sequence,pCRL,
3166 representedprocesses.push_back(process_names);
3167 }
3168 seq_varnames.push_back(new_process);
3169 todo.push_back(new_process);
3170 }
3171 /* now we must construct arguments */
3172 variable_list parameters=objectIndex(new_process).parameters;
3173 if (options.lin_method==t_lin_method::lmRegular2)
3174 {
3175 const assignment_list args=argscollect_regular2(sequence,parameters);
3176 assert(check_valid_process_instance_assignment(new_process,args));
3177 const process_expression p=process_instance_assignment(new_process,args);
3178 return p;
3179 }
3180 else
3181 {
3182 assert(check_valid_process_instance_assignment(new_process,argscollect_regular(sequence,parameters,variables_bound_in_sum)));
3183 return process_instance_assignment(new_process,argscollect_regular(sequence,parameters,variables_bound_in_sum));
3184 }
3185 }
3186
3188 const process_expression& t,
3189 std::vector <process_identifier>& todo,
3190 const variable_list& freevars,
3191 const std::set<variable>& variables_bound_in_sum)
3192 /* t has the form of the sum, and condition over actions
3193 each followed by a sequence of variables. We replace
3194 this variable by a single one, putting the new variable
3195 on the todo list, to be transformed to regular form also. */
3196 {
3197 if (is_choice(t))
3198 {
3199 const process_expression t1=to_regular_form(choice(t).left(),todo,freevars,variables_bound_in_sum);
3200 const process_expression t2=to_regular_form(choice(t).right(),todo,freevars,variables_bound_in_sum);
3201 return choice(t1,t2);
3202 }
3203
3204 if (is_seq(t))
3205 {
3206 const process_expression& firstact=down_cast<seq>(t).left();
3207 assert(is_at(firstact)||is_tau(firstact)||is_action(firstact)||is_sync(firstact));
3208 /* the sequence of variables in
3209 the second argument must be replaced */
3210 return seq(firstact,create_regular_invocation(seq(t).right(),todo,freevars,variables_bound_in_sum));
3211 }
3212
3213 if (is_if_then(t))
3214 {
3215 return if_then(if_then(t).condition(),to_regular_form(if_then(t).then_case(),todo,freevars,variables_bound_in_sum));
3216 }
3217
3218 if (is_sum(t))
3219 {
3220 variable_list sumvars=sum(t).variables();
3221
3222 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
3223 alphaconvert(sumvars,sigma,freevars,data_expression_list());
3224 const process_expression body=substitute_pCRLproc(sum(t).operand(), sigma);
3225
3226 std::set<variable> variables_bound_in_sum1=variables_bound_in_sum;
3227 variables_bound_in_sum1.insert(sumvars.begin(),sumvars.end());
3228 return sum(sumvars,
3229 to_regular_form(
3230 body,
3231 todo,
3232 sumvars+freevars,
3233 variables_bound_in_sum1));
3234 }
3235
3237 {
3238 const stochastic_operator& sto=down_cast<const stochastic_operator>(t);
3239 variable_list sumvars=sto.variables();
3240
3241 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
3242 alphaconvert(sumvars,sigma,freevars,data_expression_list());
3243 const data_expression distribution=replace_variables_capture_avoiding_alt(
3244 sto.distribution(),
3245 sigma);
3246 const process_expression body=substitute_pCRLproc(sto.operand(),sigma);
3247
3248 std::set<variable> variables_bound_in_sum1=variables_bound_in_sum;
3249 variables_bound_in_sum1.insert(sumvars.begin(),sumvars.end());
3250 return stochastic_operator(
3251 sumvars,
3252 distribution,
3253 to_regular_form(
3254 body,
3255 todo,
3256 sumvars+freevars,
3257 variables_bound_in_sum1));
3258 }
3259
3261 {
3262 return t;
3263 }
3264
3265 throw mcrl2::runtime_error("to regular form expects GNF " + process::pp(t) +".");
3266 return process_expression();
3267 }
3268
3270 const process_expression& body,
3271 const data_expression& time,
3272 const variable_list& freevars,
3273 data_expression& timecondition)
3274 {
3275 if (is_choice(body))
3276 {
3277 return choice(
3278 distributeTime(choice(body).left(),time,freevars,timecondition),
3279 distributeTime(choice(body).right(),time,freevars,timecondition));
3280 }
3281
3282 if (is_sum(body))
3283 {
3284 variable_list sumvars=sum(body).variables();
3286 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
3287 alphaconvert(sumvars,sigma,freevars,data_expression_list());
3288 body1=substitute_pCRLproc(body1, sigma);
3289 maintain_variables_in_rhs< mutable_map_substitution<> > sigma_aux(sigma);
3290 const data_expression time1=replace_variables_capture_avoiding_alt(time,sigma_aux);
3291 body1=distributeTime(body1,time1,sumvars+freevars,timecondition);
3292 return sum(sumvars,body1);
3293 }
3294
3296 {
3297 const stochastic_operator& sto=down_cast<const stochastic_operator>(body);
3298 variable_list sumvars=sto.variables();
3299 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
3300 alphaconvert(sumvars,sigma,freevars,data_expression_list());
3301 process_expression new_body=substitute_pCRLproc(sto.operand(), sigma);
3302 data_expression new_distribution=/*data::*/replace_variables_capture_avoiding_alt(sto.distribution(), sigma);
3303 maintain_variables_in_rhs< mutable_map_substitution<> > sigma_aux(sigma);
3304 const data_expression time1=/*data::*/replace_variables_capture_avoiding_alt(time,sigma_aux);
3305 new_body=distributeTime(new_body,time1,sumvars+freevars,timecondition);
3306 return stochastic_operator(sumvars,new_distribution,new_body);
3307 }
3308
3309 if (is_if_then(body))
3310 {
3311 data_expression timecondition=sort_bool::true_();
3314 time,
3315 freevars,
3316 timecondition);
3317
3318 return if_then(
3320 body1);
3321 }
3322
3323 if (is_seq(body))
3324 {
3325 return seq(
3326 distributeTime(seq(body).left(), time,freevars,timecondition),
3327 seq(body).right());
3328 }
3329
3330 if (is_at(body))
3331 {
3332 /* make a new process */
3333 timecondition=equal_to(time,data_expression(at(body).time_stamp()));
3334 return body;
3335 }
3336
3337 if ((is_sync(body))||
3338 (is_action(body))||
3339 (is_tau(body))||
3340 (is_delta(body)))
3341 {
3342 return at(body,time);
3343 }
3344
3345 throw mcrl2::runtime_error("Internal error: expect a pCRL process in distributeTime " + process::pp(body) +".");
3346 return process_expression();
3347 }
3348
3350 const process_expression& body,
3351 variableposition v,
3352 std::vector <process_identifier>& todo,
3353 const bool regular,
3354 processstatustype mode,
3355 const variable_list& freevars,
3356 const std::set <variable>& variables_bound_in_sum)
3357 /* This process delivers the transformation of body
3358 to GNF with actions as a head symbol, or it
3359 delivers NULL if body is not a pCRL process.
3360 If regular=1, then an attempt is made to obtain a
3361 GNF where one action is always followed by a
3362 variable. */
3363 {
3364 if (is_at(body))
3365 {
3366 data_expression timecondition=sort_bool::true_();
3367 process_expression body1=procstorealGNFbody(
3368 at(body).operand(),
3369 first,
3370 todo,
3371 regular,
3372 mode,
3373 freevars,
3374 variables_bound_in_sum);
3375 return distributeTime(
3376 body1,
3378 freevars,
3379 timecondition);
3380 }
3381
3382 if (is_choice(body))
3383 {
3384 const process_expression body1=procstorealGNFbody(choice(body).left(),first,todo,
3385 regular,mode,freevars,variables_bound_in_sum);
3386 const process_expression body2=procstorealGNFbody(choice(body).right(),first,todo,
3387 regular,mode,freevars,variables_bound_in_sum);
3388 return choice(body1,body2);
3389 }
3390
3391 if (is_seq(body))
3392 {
3393 const process_expression body1=procstorealGNFbody(seq(body).left(),v,
3394 todo,regular,mode,freevars,variables_bound_in_sum);
3395 const process_expression body2=procstorealGNFbody(seq(body).right(),later,
3396 todo,regular,mode,freevars,variables_bound_in_sum);
3397 process_expression t3=putbehind(body1,body2);
3398 if ((regular) && (v==first))
3399 {
3400 /* We must transform t3 to regular form */
3401 t3=to_regular_form(t3,todo,freevars,variables_bound_in_sum);
3402 }
3403 return t3;
3404 }
3405
3406 if (is_if_then(body))
3407 {
3408 const process_expression r=distribute_condition(
3409 procstorealGNFbody(if_then(body).then_case(),first,
3410 todo,regular,mode,freevars,variables_bound_in_sum),
3411 if_then(body).condition());
3412 return r;
3413 }
3414
3415 if (is_sum(body))
3416 {
3417 const variable_list sumvars=sum(body).variables();
3418 std::set<variable> variables_bound_in_sum1=variables_bound_in_sum;
3419 variables_bound_in_sum1.insert(sumvars.begin(),sumvars.end());
3420 return distribute_sum(sumvars,
3421 procstorealGNFbody(sum(body).operand(),first,
3422 todo,regular,mode,sumvars+freevars,variables_bound_in_sum1));
3423 }
3424
3426 {
3427 const stochastic_operator& sto=down_cast<const stochastic_operator>(body);
3428 const variable_list& sumvars=sto.variables();
3429 std::set<variable> variables_bound_in_sum1=variables_bound_in_sum;
3430 variables_bound_in_sum1.insert(sumvars.begin(),sumvars.end());
3431 return stochastic_operator(
3432 sumvars,
3433 sto.distribution(),
3434 procstorealGNFbody(sto.operand(),v,
3435 todo,regular,mode,sumvars+freevars,variables_bound_in_sum1));
3436 }
3437
3438 if (is_action(body))
3439 {
3440 return body;
3441 }
3442
3443 if (is_sync(body))
3444 {
3445 return body;
3446 }
3447
3449 {
3451
3452 if (v==later)
3453 {
3454 if (regular)
3455 {
3456 mode=mCRL;
3457 }
3458 todo.push_back(t);
3459 /* if ((!regular)||(mode=mCRL))
3460 todo.push_back(t);
3461 / * single = in `mode=mCRL' is important, otherwise crash
3462 I do not understand the reason for this at this moment
3463 JFG (9/5/2000) */
3464 return body;
3465 }
3466
3468 if (object.processstatus==mCRL)
3469 {
3470 todo.push_back(t);
3471 return process_expression();
3472 }
3473 /* The variable is a pCRL process and v==first, so,
3474 we must now substitute */
3475 procstorealGNFrec(t,first,todo,regular);
3476
3477 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
3478 for(const assignment& a: static_cast<const process_instance_assignment&>(body).assignments())
3479 {
3480 sigma[a.lhs()]=a.rhs();
3481 }
3482 process_expression t3=substitute_pCRLproc(object.processbody,sigma);
3483 if (regular)
3484 {
3485 t3=to_regular_form(t3,todo,freevars,variables_bound_in_sum);
3486 }
3487
3488 return t3;
3489 }
3490
3491 if (is_delta(body))
3492 {
3493 return body;
3494 }
3495
3496 if (is_tau(body))
3497 {
3498 return body;
3499 }
3500
3501 if (is_merge(body))
3502 {
3503 procstorealGNFbody(process::merge(body).left(),later,
3504 todo,regular,mode,freevars,variables_bound_in_sum);
3505 procstorealGNFbody(process::merge(body).right(),later,
3506 todo,regular,mode,freevars,variables_bound_in_sum);
3507 return process_expression();
3508 }
3509
3510 if (is_hide(body))
3511 {
3512 procstorealGNFbody(hide(body).operand(),later,todo,regular,mode,freevars,variables_bound_in_sum);
3513 return process_expression();
3514 }
3515
3516 if (is_rename(body))
3517 {
3518 procstorealGNFbody(process::rename(body).operand(),later,todo,regular,mode,freevars,variables_bound_in_sum);
3519 return process_expression();
3520 }
3521
3522 if (is_allow(body))
3523 {
3524 procstorealGNFbody(allow(body).operand(),later,todo,regular,mode,freevars,variables_bound_in_sum);
3525 return process_expression();
3526 }
3527
3528 if (is_block(body))
3529 {
3530 procstorealGNFbody(block(body).operand(),later,todo,regular,mode,freevars,variables_bound_in_sum);
3531 return process_expression();
3532 }
3533
3534 if (is_comm(body))
3535 {
3536 procstorealGNFbody(comm(body).operand(),later,todo,regular,mode,freevars,variables_bound_in_sum);
3537 return process_expression();
3538 }
3539
3540 // The exception below can especially trigger on a process_instance as these ought to have
3541 // been removed earlier in favour of process_instance_assignments.
3542 throw mcrl2::runtime_error("unexpected process format in procstorealGNF " + process::pp(body) +".");
3543 return process_expression();
3544 }
3545
3546
3548 const process_identifier& procIdDecl,
3549 const variableposition v,
3550 std::vector <process_identifier>& todo,
3551 const bool regular)
3552
3553 /* Do a depth first search on process variables and substitute
3554 for the headvariable of a pCRL process, in case it is a process,
3555 such that we obtain a Greibach Normal Form. All pCRL processes will
3556 be labelled with GNF to indicate that they are in
3557 Greibach Normal Form. */
3558
3559 {
3560 objectdatatype& object=objectIndex(procIdDecl);
3561 if (object.processstatus==pCRL)
3562 {
3563 object.processstatus=GNFbusy;
3564 std::set<variable> variables_bound_in_sum;
3565 const process_expression t=procstorealGNFbody(object.processbody,first,
3566 todo,regular,pCRL,object.parameters,variables_bound_in_sum);
3567 if (object.processstatus!=GNFbusy)
3568 {
3569 throw mcrl2::runtime_error("There is something wrong with recursion.");
3570 }
3571
3572 object.processbody=t;
3573 object.processstatus=GNF;
3574 return;
3575 }
3576
3577 if (object.processstatus==mCRL)
3578 {
3579 object.processstatus=mCRLbusy;
3580 std::set<variable> variables_bound_in_sum;
3581 const process_expression t=procstorealGNFbody(object.processbody,first,todo,
3582 regular,mCRL,object.parameters,variables_bound_in_sum);
3583 /* if the last result is not equal to NULL,
3584 the body of this process is itself a processidentifier */
3585
3586 object.processstatus=mCRLdone;
3587 return;
3588 }
3589
3590 if ((object.processstatus==GNFbusy) && (v==first))
3591 {
3592 throw mcrl2::runtime_error("Unguarded recursion in process " + process::pp(procIdDecl) +".");
3593 }
3594
3595 if ((object.processstatus==GNFbusy)||
3596 (object.processstatus==GNF)||
3597 (object.processstatus==mCRLdone)||
3598 (object.processstatus==multiAction))
3599 {
3600 return;
3601 }
3602
3603 if (object.processstatus==mCRLbusy)
3604 {
3605 throw mcrl2::runtime_error("Unguarded recursion in process " + process::pp(procIdDecl) +".");
3606 }
3607
3608 throw mcrl2::runtime_error("strange process type: " + std::to_string(object.processstatus));
3609 }
3610
3611 void procstorealGNF(const process_identifier& procsIdDecl,
3612 const bool regular)
3613 {
3614 std::vector <process_identifier> todo;
3615 todo.push_back(procsIdDecl);
3616 while (!todo.empty())
3617 {
3618 const process_identifier pi=todo.back();
3619 todo.pop_back();
3620 procstorealGNFrec(pi,first,todo,regular);
3621 }
3622 }
3623
3624
3625 /**************** GENERaTE LPE **********************************/
3626 /* */
3627 /* */
3628 /* */
3629 /* */
3630 /* */
3631 /* */
3632 /****************************************************************/
3633
3634
3635 /**************** Make pCRL procs ******************************/
3636
3637 /* make_pCRL_procs searches for all process identifiers reachable from id in pCRL equations. */
3638
3640 std::set<process_identifier>& reachable_process_identifiers)
3641 {
3642 if (reachable_process_identifiers.count(id)==0) // not found
3643 {
3644 reachable_process_identifiers.insert(id);
3645 make_pCRL_procs(objectIndex(id).processbody,reachable_process_identifiers);
3646 }
3647 return;
3648 }
3649
3651 std::set<process_identifier>& reachable_process_identifiers)
3652 {
3653 if (is_choice(t))
3654 {
3655 make_pCRL_procs(choice(t).left(),reachable_process_identifiers);
3656 make_pCRL_procs(choice(t).right(),reachable_process_identifiers);
3657 return;
3658 }
3659
3660 if (is_seq(t))
3661 {
3662 make_pCRL_procs(seq(t).left(),reachable_process_identifiers);
3663 make_pCRL_procs(seq(t).right(),reachable_process_identifiers);
3664 return;
3665 }
3666
3667 if (is_if_then(t))
3668 {
3669 make_pCRL_procs(if_then(t).then_case(),reachable_process_identifiers);
3670 return;
3671 }
3672
3673 if (is_sum(t))
3674 {
3675 make_pCRL_procs(sum(t).operand(),reachable_process_identifiers);
3676 return;
3677 }
3678
3680 {
3681 make_pCRL_procs(stochastic_operator(t).operand(),reachable_process_identifiers);
3682 return;
3683 }
3684
3686 {
3687 make_pCRL_procs(atermpp::down_cast<process_instance_assignment>(t).identifier(), reachable_process_identifiers);
3688 return;
3689 }
3690
3692 {
3693 return;
3694 }
3695
3696 throw mcrl2::runtime_error("unexpected process format " + process::pp(t) + " in make_pCRL_procs");
3697 }
3698
3699 /**************** minimize_set_of_reachable_process_identifiers ******************************/
3700
3701 /* The process identifiers in reachable_process_identifiers have bodies and parameters lists
3702 * that can be the same. If that is the case, they are identified. The right hand sides are substituted to
3703 * 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.
3704 * The result is a map from remaining process identifiers to their adapted process bodies.
3705 * The initial process was part of the reachable_process_identifiers and must be part of the map.
3706 */
3707
3708 /* set_proc_identifier_map is an auxiliary procedure that effectively sets identfier_identifier_map[id1]:=... or
3709 identifier_identifier_map[id1]:=... in such a way that they map to the same element. In this way identifier_identifier_map
3710 acts as a representation of the equivalence set induced by equations id1==id2. A constraint is that
3711 initial_process may never occur as a left hand side. An invariant of identifier_identifier_map is
3712 that no id1 that occurs at the right of the map can occur at the left.
3713 */
3714
3715 static
3716 process_identifier get_last(const process_identifier& id, const std::map< process_identifier, process_identifier >& identifier_identifier_map)
3717 {
3718 process_identifier target_id=id;
3719 bool ready=false;
3720 do
3721 {
3722 const std::map< process_identifier, process_identifier >::const_iterator i=identifier_identifier_map.find(target_id);
3723 if (i==identifier_identifier_map.end())
3724 {
3725 ready=true;
3726 }
3727 else
3728 {
3729 target_id=i->second;
3730 }
3731 }
3732 while (!ready);
3733 return target_id;
3734 }
3735
3736
3737 static
3739 std::map< process_identifier, process_identifier >& identifier_identifier_map,
3740 const process_identifier& id1_,
3741 const process_identifier& id2_,
3742 const process_identifier& initial_process)
3743 {
3744 assert(id1_!=id2_);
3745
3746 /* Take care that id1 is the last identifier or that id2 is arger than id1. This guarantees
3747 that there will be no loops in the mapping of identifiers. */
3748 process_identifier id1= get_last(id1_,identifier_identifier_map);
3749 process_identifier id2= get_last(id2_,identifier_identifier_map);
3750 if (id1==initial_process)
3751 {
3752 id1.swap(id2);
3753 }
3754 if (id1!=id2)
3755 {
3756 identifier_identifier_map[id1]=id2;
3757 }
3758 }
3759
3760 static
3761 void complete_proc_identifier_map(std::map< process_identifier, process_identifier >& identifier_identifier_map)
3762 {
3763 std::map< process_identifier, process_identifier > new_identifier_identifier_map;
3764 for(const std::pair<const process_identifier, process_identifier >& p: identifier_identifier_map)
3765 {
3766 new_identifier_identifier_map[p.first]=get_last(p.second,identifier_identifier_map);
3767 }
3768 identifier_identifier_map.swap(new_identifier_identifier_map);
3769#ifndef NDEBUG
3770 /* In the result no right hand side occurs as the left hand side of identifier_identifier_map */
3771 using identifier_identifier_pair = std::pair<const process_identifier, process_identifier>;
3772 for(const identifier_identifier_pair& p: identifier_identifier_map)
3773 {
3774 assert(identifier_identifier_map.count(p.second)==0);
3775 }
3776#endif
3777 }
3778
3780 {
3781 using result_type = process_identifier;
3782 using argument_type = process_identifier;
3784
3785 make_substitution(const std::map< process_identifier, process_identifier >& map)
3786 : m_map(map)
3787 {}
3788
3790 {
3791 const std::map< process_identifier, process_identifier >::const_iterator i=m_map.find(id);
3792 if (i==m_map.end()) // Not found
3793 {
3794 return id;
3795 }
3796 return i->second;
3797 }
3798 };
3799
3803 {
3804 assert(reachable_process_identifiers.count(initial_process)>0);
3805 using parameters_process_pair = std::pair<variable_list, process_expression>;
3806 using mapping_type = std::map<std::pair<variable_list, process_expression>, process_identifier>;
3807 using mapping_type_pair = std::pair<const std::pair<variable_list, process_expression>, process_identifier>;
3808
3809 /* First put the identifiers in reachable_process_identifiers in the process mapping */
3810 mapping_type process_mapping;
3811 std::map< process_identifier, process_identifier > identifier_identifier_map;
3812 for(const process_identifier& id: reachable_process_identifiers)
3813 {
3814 objectdatatype& object=objectIndex(id);
3815 const parameters_process_pair p(object.parameters,object.processbody);
3816 mapping_type::const_iterator i=process_mapping.find(p);
3817 if (i==process_mapping.end()) // Not found.
3818 {
3819 process_mapping[p]=id;
3820 }
3821 else
3822 {
3823 set_proc_identifier_map(identifier_identifier_map,id,i->second,initial_process);
3824 }
3825 }
3826 complete_proc_identifier_map(identifier_identifier_map);
3827
3828 while (!identifier_identifier_map.empty())
3829 {
3830 /* Move the elements of process_mapping into new_process_mapping, under application
3831 * of the identifier_identifier_map. If this yields new process_identifiers to be identified store
3832 * these in new_proc_identifier_map. Do this until no process_identifiers are mapped onto
3833 * each other anymore. */
3834 std::map< process_identifier, process_identifier > new_identifier_identifier_map;
3835 mapping_type new_process_mapping;
3836 for(const mapping_type_pair& p: process_mapping)
3837 {
3838 const make_substitution sigma(identifier_identifier_map);
3839 const parameters_process_pair p_new(p.first.first, replace_process_identifiers( p.first.second, sigma));
3840 mapping_type::const_iterator i=new_process_mapping.find(p_new);
3841 if (i==new_process_mapping.end()) // Not found.
3842 {
3843 new_process_mapping[p_new]=sigma(p.second);
3844 }
3845 else
3846 {
3847 set_proc_identifier_map(new_identifier_identifier_map,sigma(p.second),sigma(i->second),initial_process);
3848 }
3849 }
3850 complete_proc_identifier_map(new_identifier_identifier_map);
3851 identifier_identifier_map.swap(new_identifier_identifier_map);
3852 process_mapping.swap(new_process_mapping);
3853 }
3854
3855 /* Store the pairs from process mapping into result */
3856 std::set< process_identifier > result;
3857 for(const mapping_type_pair& p: process_mapping)
3858 {
3859 result.insert(p.second);
3860 objectdatatype& object=objectIndex(p.second);
3861 object.processbody=p.first.second;
3862 }
3863 assert(result.count(initial_process)>0);
3864 return result;
3865 }
3866
3867 /**************** remove_stochastic_operators_from_front **********************/
3868
3870 {
3871 protected:
3874
3875 public:
3877 : m_process_body(process_body), m_pid(pid)
3878 {}
3879
3880 process_pid_pair(const process_pid_pair& other) = default;
3882 process_pid_pair& operator=(const process_pid_pair& other) = default;
3884
3886 {
3887 return m_process_body;
3888 }
3889
3891 {
3892 return m_pid;
3893 }
3894 }; // end process_pid_pair
3895
3896 /* Take the process expressions from process_equations and transform these such that
3897 no process equation has an initial distribution. The changed equations are reported
3898 back, including adapted parameter lists because sometimes the variables bound in the
3899 stochastic operators need to be added.
3900 The initial distribution of the initial process is reported back in
3901 initial_stochastic_distribution, including if necessary adapted sets of arguments. */
3902
3906 process_identifier& initial_process_id, // If parameters change, another initial_process_id will be substituted.
3908 {
3909 /* First obtain the stochastic distribution for each process variable. */
3910 std::map< process_identifier, process_pid_pair > processes_with_stochastic_distribution_first;
3911 std::set< process_identifier > result;
3912 for(const process_identifier& p: reachable_process_identifiers)
3913 {
3914 objectdatatype& object = objectIndex(p);
3915 process_expression proc_=obtain_initial_distribution_term(object.processbody);
3916 if (!is_stochastic_operator(proc_))
3917 {
3918 processes_with_stochastic_distribution_first.insert(std::pair< process_identifier, process_pid_pair >(p, process_pid_pair(proc_,p)));
3919 result.insert(p);
3920 }
3921 else
3922 {
3923 const stochastic_operator& proc=down_cast<const stochastic_operator>(proc_);
3924 assert(!is_process_instance_assignment(proc.operand()));
3925 objectdatatype& object=objectIndex(p);
3926 maintain_variables_in_rhs< mutable_map_substitution<> > local_sigma;
3927 variable_list vars=proc.variables();
3928 alphaconvert(vars,local_sigma, vars + object.parameters, data_expression_list());
3929
3930 const process_identifier newproc=newprocess(
3931 vars + object.parameters,
3932 process::replace_variables_capture_avoiding_with_an_identifier_generator(proc.operand(),
3933 local_sigma,
3934 fresh_identifier_generator),
3935 pCRL,
3936 canterminatebody(proc.operand()),
3937 containstimebody(proc.operand()));
3938 // calculate the substitution to be applied on proc.distribution() which is moved outside the
3939 // body the process.
3940 processes_with_stochastic_distribution_first.insert(
3941 std::pair< process_identifier, process_pid_pair >
3942 (p,
3943 process_pid_pair(stochastic_operator(
3944 vars,
3945 replace_variables_capture_avoiding_alt(proc.distribution(), local_sigma),
3946 proc.operand()),
3947 newproc)));
3948 result.insert(newproc);
3949 }
3950 }
3951
3952 for(const process_identifier& p: reachable_process_identifiers)
3953 {
3954 objectdatatype& object=objectIndex(processes_with_stochastic_distribution_first.at(p).process_id());
3955 assert(!is_stochastic_operator(object.processbody));
3956 object.processbody=transform_initial_distribution_term(object.processbody,processes_with_stochastic_distribution_first);
3957 assert(!is_stochastic_operator(object.processbody));
3958 }
3959
3960 // Adapt the initial process
3961 process_expression initial_distribution=processes_with_stochastic_distribution_first.at(initial_process_id).process_body();
3962 if (is_stochastic_operator(initial_distribution))
3963 {
3964 const stochastic_operator sto=atermpp::down_cast<stochastic_operator>(initial_distribution);
3965 initial_stochastic_distribution = stochastic_distribution(sto.variables(), sto.distribution());
3966 initial_process_id=processes_with_stochastic_distribution_first.at(initial_process_id).process_id();
3967 }
3968 else
3969 {
3970 initial_stochastic_distribution = stochastic_distribution(variable_list(), sort_real::real_one());
3971 }
3972
3973 return result;
3974 }
3975
3976
3977 /**************** Collectparameterlist ******************************/
3978
3980 const variable_list& vl,
3981 mutable_indexed_substitution<>& parameter_renaming)
3982 {
3983 /* Note: variables can be different, although they have the
3984 same string, due to different types. If they have the
3985 same string, but different types, the conflict must
3986 be resolved by renaming the name of the variable.
3987 Note that var is changed to the new variable name. */
3988
3989 for(const variable& v: vl)
3990 {
3991 if (v.name()==var.name())
3992 {
3993 if (v.sort()==var.sort())
3994 {
3995 return true; // The variable is present.
3996 }
3997 else
3998 {
3999 if (parameter_renaming(v)==v)
4000 {
4001 const variable new_var(fresh_identifier_generator(var.name()),var.sort());
4002 parameter_renaming[var]=new_var;
4003 var=new_var;
4004 return false; // variable var is renamed, as name clashed with a variable with a different sort. Not present.
4005 }
4006 else
4007 {
4008 // The variable var is renamed, and the renaming was already present.
4009 var=atermpp::down_cast<variable>(parameter_renaming(v));
4010 return true;
4011 }
4012 }
4013 }
4014 }
4015 return false; // The variable var is not present.
4016 }
4017
4018 variable_list joinparameters(const variable_list& par1,
4019 const variable_list& par2,
4020 mutable_indexed_substitution<>& parameter_renaming)
4021 {
4022 if (par2.empty())
4023 {
4024 return par1;
4025 }
4026
4027 variable var2=par2.front();
4028 assert(is_variable(var2));
4029
4030 variable_list result=joinparameters(par1,par2.tail(), parameter_renaming);
4031 if (alreadypresent(var2,par1, parameter_renaming))
4032 {
4033 return result;
4034 }
4035
4036 result.push_front(var2);
4037 return result;
4038 }
4039
4040 // While collecting the parameter list the process parameters that are part of the process identifiers may change.
4041 // This means the list pCRLprocs may change.
4042 variable_list collectparameterlist(std::set<process_identifier>& pCRLprocs)
4043 // mutable_indexed_substitution<>& parameter_renaming)
4044 {
4045 mutable_indexed_substitution<> parameter_renaming; // Used to rename variables with the same name but different sorts.
4046 variable_list parameters;
4047 for (const process_identifier& p: pCRLprocs)
4048 {
4049 const objectdatatype& object=objectIndex(p);
4050 parameters=joinparameters(parameters,object.parameters, parameter_renaming);
4051 }
4052 // Apply the parameter renaming.
4053 std::set<process_identifier> new_pCRLprocs;
4054 for (const process_identifier& p: pCRLprocs)
4055 {
4056 objectdatatype object=objectIndex(p);
4057 object.processbody=process::replace_all_variables(object.processbody,parameter_renaming);
4058 object.parameters=process::replace_all_variables(object.parameters,parameter_renaming);
4059 process_identifier p_new=process::replace_all_variables(p,parameter_renaming);
4060 objectdata[p_new]=object;
4061 new_pCRLprocs.insert(p_new);
4062 }
4063 pCRLprocs=new_pCRLprocs;
4064 return parameters;
4065 }
4066
4067 /**************** Declare local datatypes ******************************/
4068
4070 const std::set < process_identifier >& pCRLprocs)
4071 {
4072 create_enumeratedtype(pCRLprocs.size());
4073 }
4074
4076 {
4077 public:
4078 variable_list parameter_list;
4080 sort_expression_list sorts;
4081 function_symbol_list get;
4088
4089 // Stack operations are not supposed to be copied.
4092
4093 stackoperations(const variable_list& pl,
4095 {
4096 parameter_list=pl;
4098 spec.stack_operations_list=this;
4099
4100 //create structured sort
4101 // Stack = struct emptystack?is_empty
4102 // | push(getstate: Pos, getx1: S1, ..., getxn: Sn, pop: Stack)
4103 // ;
4104
4105 basic_sort stack_sort_alias(spec.fresh_identifier_generator("Stack"));
4106 structured_sort_constructor_argument_vector sp_push_arguments;
4107 for (const variable& v: pl)
4108 {
4109 sp_push_arguments.emplace_back(spec.fresh_identifier_generator("get" + std::string(v.name())), v.sort());
4110 sorts.push_front(v.sort());
4111 }
4112 sp_push_arguments.emplace_back(spec.fresh_identifier_generator("pop"), stack_sort_alias);
4113 sorts=reverse(sorts);
4114 structured_sort_constructor sc_push(spec.fresh_identifier_generator("push"), sp_push_arguments);
4115 structured_sort_constructor sc_emptystack(spec.fresh_identifier_generator("emptystack"),spec.fresh_identifier_generator("isempty"));
4116
4117 structured_sort_constructor_vector constructors(1,sc_push);
4118 constructors.push_back(sc_emptystack);
4119 //add data declarations for structured sort
4120 spec.data.add_alias(alias(stack_sort_alias,structured_sort(constructors)));
4121 stacksort=data::normalize_sorts(stack_sort_alias,spec.data);
4122 push=sc_push.constructor_function(stack_sort_alias);
4123 emptystack=sc_emptystack.constructor_function(stack_sort_alias);
4124 empty=sc_emptystack.recogniser_function(stack_sort_alias);
4125 const std::vector< data::function_symbol > projection_functions =
4126 sc_push.projection_functions(stack_sort_alias);
4127 pop=projection_functions.back();
4128 getstate=projection_functions.front();
4129 get=function_symbol_list(projection_functions.begin()+1,projection_functions.end()-1);
4130 }
4131
4132 ~stackoperations() = default;
4133 };
4134
4136 {
4137 public:
4139 variable_list parameters;
4142 /* the boolean state variables occur in reverse
4143 order, i.e. the least significant first, whereas
4144 in parameter lists, the order is reversed. */
4146
4147
4148 /* All datatypes for different stacks that are being generated
4149 are stored in the following list, such that it can be investigated
4150 whether a suitable stacktype already exist, before generating a new
4151 one */
4152
4155
4156 static
4158 const variable_list& parameters,
4159 stackoperations* stack_operations_list)
4160 {
4161 if (stack_operations_list==nullptr)
4162 {
4163 return nullptr;
4164 }
4165 if (parameters==stack_operations_list->parameter_list)
4166 {
4167 return stack_operations_list;
4168 }
4169 return find_suitable_stack_operations(parameters,stack_operations_list->next);
4170 }
4171
4172 /// \brief Constructor
4173 stacklisttype(const variable_list& parlist,
4175 const bool regular,
4176 const std::set < process_identifier >& pCRLprocs,
4177 const bool singlecontrolstate)
4178 {
4179 assert(pCRLprocs.size()>0);
4180 parameters=parlist;
4181
4182 no_of_states=pCRLprocs.size();
4183 process_identifier last= *pCRLprocs.begin();
4184 const std::string s3((spec.options.statenames)?std::string(last.name()):std::string("s"));
4185 if ((spec.options.binary) && (spec.options.newstate))
4186 {
4187 std::size_t i=spec.upperpowerof2(no_of_states);
4188 for (; i>0 ; i--)
4189 {
4190 variable name(spec.fresh_identifier_generator("bst"),sort_bool::bool_());
4191 spec.insertvariable(name,true);
4192 booleanStateVariables.push_front(name);
4193 }
4194 }
4195
4196 if (regular)
4197 {
4198 opns=nullptr;
4199 if (spec.options.newstate)
4200 {
4201 if (!spec.options.binary)
4202 {
4203 if (!singlecontrolstate)
4204 {
4205 const std::size_t e=spec.create_enumeratedtype(no_of_states);
4206 stackvar=variable(spec.fresh_identifier_generator(s3), spec.enumeratedtypes[e].sortId);
4207 }
4208 else
4209 {
4210 /* Generate a stackvariable that is never used */
4211 stackvar=variable(spec.fresh_identifier_generator("Never_used"), sort_bool::bool_());
4212 }
4213 }
4214 else
4215 {
4216 stackvar=variable(spec.fresh_identifier_generator(s3),sort_bool::bool_());
4217 }
4218 }
4219 else
4220 {
4221 stackvar=variable(spec.fresh_identifier_generator(s3), sort_pos::pos());
4222 }
4224 }
4225 else
4226 {
4227 if (spec.options.newstate)
4228 {
4229 throw mcrl2::runtime_error("cannot combine stacks with " +
4230 (spec.options.binary?std::string("binary"):std::string("an enumerated type")));
4231 }
4233
4234 if (opns!=nullptr)
4235 {
4236 stackvar=variable(spec.fresh_identifier_generator(s3),opns->stacksort);
4238 }
4239 else
4240 {
4241 variable_list temp=parlist;
4242 temp.push_front(variable("state",sort_pos::pos()));
4243 opns=(stackoperations*) new stackoperations(temp,spec);
4244 stackvar = variable(spec.fresh_identifier_generator(s3), opns->stacksort);
4246 }
4247 }
4248 }
4249
4250 ~stacklisttype() = default;
4251 };
4252
4254 {
4255 return is_variable(d) && global_variables.count(atermpp::down_cast<variable>(d)) > 0;
4256 }
4257
4259 const stacklisttype& stack) const
4260 {
4261 /* first search whether the variable is a free process variable */
4262 if (global_variables.count(var)>0)
4263 {
4264 return var;
4265 }
4266
4267 /* otherwise find out whether the variable matches a parameter */
4268 function_symbol_list::const_iterator getmappings=stack.opns->get.begin();
4269 for (variable_list::const_iterator walker=stack.parameters.begin() ;
4270 walker!=stack.parameters.end() ; ++walker,++getmappings)
4271 {
4272 if (*walker==var)
4273 {
4274 return application(*getmappings,stack.stackvar);
4275 }
4276 assert(getmappings!=stack.opns->get.end());
4277 }
4278 assert(0); /* We cannot end up here, because that means that we
4279 are looking for in non-existing variable */
4280 return var;
4281 }
4282
4283 assignment_list processencoding(
4284 std::size_t i,
4285 const assignment_list& t1,
4286 const stacklisttype& stack)
4287 {
4288 assert(i>0);
4289 assignment_list t(t1);
4290 if (!options.newstate)
4291 {
4292 assignment_list result=t;
4293 result.push_front(assignment(stack.stackvar,sort_pos::pos(i)));
4294 return result;
4295 }
4296
4297 i=i-1; /* below we count from 0 instead from 1 as done in the
4298 first version of the prover */
4299
4300 if (!options.binary)
4301 {
4302 const std::size_t e=create_enumeratedtype(stack.no_of_states);
4303 data_expression_list l=enumeratedtypes[e].elementnames;
4304 for (; i>0 ; i--)
4305 {
4306 assert(l.size()>0);
4307 l.pop_front();
4308 }
4309 assignment_list result=t;
4310 assert(l.size()>0);
4311 result.push_front(assignment(stack.stackvar,l.front()));
4312 return result;
4313 }
4314 /* else a sequence of boolean values needs to be generated,
4315 representing the value i, when there are l->n elements */
4316 {
4317 std::size_t k=upperpowerof2(stack.no_of_states);
4318 variable_list::const_iterator boolean_state_variables=stack.booleanStateVariables.begin();
4319 for (; k>0 ; k--, ++boolean_state_variables)
4320 {
4321 if ((i % 2)==0)
4322 {
4323 t.push_front(assignment(*boolean_state_variables,sort_bool::false_()));
4324 i=i/2;
4325 }
4326 else
4327 {
4328 t.push_front(assignment(*boolean_state_variables,sort_bool::true_()));
4329 i=(i-1)/2;
4330 }
4331 }
4332 return t;
4333 }
4334 }
4335
4336 data_expression_list processencoding(
4337 std::size_t i,
4338 const data_expression_list& t1,
4339 const stacklisttype& stack)
4340 {
4341 data_expression_list t(t1);
4342 if (!options.newstate)
4343 {
4344 data_expression_list result=t;
4345 result.push_front(sort_pos::pos(i));
4346 return result;
4347 }
4348
4349 i=i-1; /* below we count from 0 instead from 1 as done in the
4350 first version of the linearizer */
4351
4352 if (!options.binary)
4353 {
4354 const std::size_t e=create_enumeratedtype(stack.no_of_states);
4355 data_expression_list l(enumeratedtypes[e].elementnames);
4356 for (; i>0 ; i--)
4357 {
4358 l.pop_front();
4359 }
4360 data_expression_list result=t;
4361 result.push_front(l.front());
4362 return result;
4363 }
4364 /* else a sequence of boolean values needs to be generated,
4365 representing the value i, when there are l->n elements */
4366 {
4367 std::size_t k=upperpowerof2(stack.no_of_states);
4368 variable_list::const_iterator boolean_state_variables=stack.booleanStateVariables.begin();
4369 for (; k>0 ; k--, ++boolean_state_variables)
4370 {
4371 if ((i % 2)==0)
4372 {
4373 t.push_front(sort_bool::false_());
4374 i=i/2;
4375 }
4376 else
4377 {
4378 t.push_front(sort_bool::true_());
4379 i=(i-1)/2;
4380 }
4381 }
4382 return t;
4383 }
4384 }
4385
4387 const process_identifier& procId,
4388 const std::set < process_identifier >& pCRLproc,
4389 const stacklisttype& stack,
4390 int regular)
4391 {
4392 std::size_t i=1;
4393 for (const process_identifier& p: pCRLproc)
4394 {
4395 if (p==procId)
4396 {
4397 break;
4398 }
4399 ++i;
4400 }
4401 /* i is the index of the current process */
4402
4403 if (!options.newstate)
4404 {
4405 if (regular)
4406 {
4407 return equal_to(stack.stackvar, processencoding(i,assignment_list(),stack).front().rhs());
4408 }
4409 return equal_to(
4410 application(stack.opns->getstate,stack.stackvar),
4411 processencoding(i,assignment_list(),stack).front().rhs());
4412 }
4413
4414 if (!options.binary) /* Here a state encoding using enumerated types
4415 must be declared */
4416 {
4417 create_enumeratedtype(stack.no_of_states);
4418 if (regular)
4419 {
4420 return equal_to(stack.stackvar,
4421 processencoding(i,assignment_list(),stack).front().rhs());
4422 }
4423 return equal_to(
4424 application(stack.opns->getstate, stack.stackvar),
4425 processencoding(i,assignment_list(),stack).front().rhs());
4426 }
4427
4428 /* in this case we must encode the condition using
4429 boolean variables */
4430
4431 const variable_list vars=stack.booleanStateVariables;
4432
4433 i=i-1; /* start counting from 0, instead from 1 */
4435 for (const variable& v: vars)
4436 {
4437 if ((i % 2)==0)
4438 {
4439 t3=lazy::and_(lazy::not_(v),t3);
4440 i=i/2;
4441 }
4442 else
4443 {
4444 t3=lazy::and_(v,t3);
4445 i=(i-1)/2;
4446 }
4447
4448 }
4449 assert(i==0);
4450 return t3;
4451 }
4452
4454 const data_expression& t,
4455 const stacklisttype& stack,
4456 const variable_list& vars,
4457 const variable_list& stochastic_variables)
4458 {
4460 {
4461 return t;
4462 }
4463
4465 {
4466 return t;
4467 }
4468
4469 if (is_variable(t))
4470 {
4471 if (std::find(vars.begin(),vars.end(),t)!=vars.end())
4472 {
4473 /* t occurs in vars, so, t does not have to be reconstructed
4474 from the stack */
4475 return t;
4476 }
4477 else
4478 if (std::find(stochastic_variables.begin(),stochastic_variables.end(),t)!=stochastic_variables.end())
4479 {
4480 /* t occurs in stochastic_variables, so, t does not have to be reconstructed
4481 from the stack */
4482 return t;
4483 }
4484 else
4485 {
4486 return getvar(atermpp::down_cast<variable>(t), stack);
4487 }
4488 }
4489
4490 if (is_application(t))
4491 {
4492 const application&a=atermpp::down_cast<application>(t);
4493 return application(
4494 adapt_term_to_stack(a.head(),stack,vars,stochastic_variables),
4495 adapt_termlist_to_stack(a.begin(),a.end(),stack,vars,stochastic_variables));
4496 }
4497
4498 if (is_abstraction(t))
4499 {
4500 const abstraction& abs_t=down_cast<abstraction>(t);
4501 return abstraction(
4503 abs_t.variables(),
4504 adapt_term_to_stack(abs_t.body(),stack,abs_t.variables() + vars,stochastic_variables));
4505 }
4506
4507 if (is_where_clause(t))
4508 {
4509 const where_clause where_t(t);
4510 const assignment_list old_assignments=reverse(where_t.assignments());
4511 variable_list new_vars=vars;
4512 assignment_list new_assignments;
4513 for (const assignment& a: old_assignments)
4514 {
4515 new_vars.push_front(a.lhs());
4516 new_assignments.push_front(
4517 assignment(
4518 a.lhs(),
4519 adapt_term_to_stack(a.rhs(),stack,vars,stochastic_variables)));
4520
4521 }
4522 return where_clause(
4523 adapt_term_to_stack(where_t,stack,new_vars,stochastic_variables),
4524 new_assignments);
4525
4526 }
4527
4528 assert(0); // expected a term;
4529 return t; // in case of non-debug mode, try to return something as decent as possible.
4530 }
4531
4532 template <typename Iterator>
4535 const Iterator& end,
4536 const stacklisttype& stack,
4537 const variable_list& vars,
4539 {
4540 data_expression_vector result;
4541 for (; begin != end; ++begin)
4542 {
4543 result.push_back(adapt_term_to_stack(*begin,stack, vars,stochastic_variables));
4544 }
4545 return result;
4546 }
4547
4548 /// Adapt multiactions to stack by traversing the list.
4549 /// Note: the result is sorted w.r.t. operator< on action.
4550 action_list
4551 adapt_multiaction_to_stack(const action_list& multiAction, const stacklisttype& stack, const variable_list& vars)
4552 {
4553 action_vector result;
4554
4555 for (const action& act : multiAction)
4556 {
4557 const data_expression_vector vec(
4558 adapt_termlist_to_stack(act.arguments().begin(), act.arguments().end(), stack, vars, variable_list()));
4559 result.emplace_back(act.label(), data_expression_list(vec.begin(), vec.end()));
4560 }
4561
4562 // As the arguments change, sort order w.r.t. action_label is preserved, but order w.r.t. arguments is changed.
4563 // resort the list.
4564 std::sort(result.begin(), result.end());
4565
4566 return action_list(result.begin(), result.end());
4567 }
4568
4569 data_expression representative_generator_internal(const sort_expression& s, const bool allow_dont_care_var=true)
4570 {
4571 if ((!options.noglobalvars) && allow_dont_care_var)
4572 {
4573 const variable newVariable(fresh_identifier_generator("dc"),s);
4574 insertvariable(newVariable,true);
4575 global_variables.insert(newVariable);
4576 return newVariable;
4577 }
4578 return representative_generator(data)(s);
4579 }
4580
4582 const variable& s,
4583 const assignment_list& args,
4584 const stacklisttype& stack,
4585 const variable_list& vars,
4586 const std::set<variable>& free_variables_in_body,
4587 const variable_list& stochastic_variables)
4588 {
4589 /* We generate the value for variable s in the list of
4590 the parameters of the process. If s is equal to some
4591 variable in pars, it is an argument of the current
4592 process, and it must be replaced by the corresponding
4593 argument in args.
4594 If s does not occur in pars, it must be replaced
4595 by a dummy value.
4596 */
4597 for (const assignment& a: args)
4598 {
4599 if (s==a.lhs())
4600 {
4601 return adapt_term_to_stack(a.rhs(),stack,vars,stochastic_variables);
4602 }
4603 }
4604
4605 if (free_variables_in_body.find(s)==free_variables_in_body.end())
4606 {
4608 return adapt_term_to_stack(result,stack,vars,stochastic_variables);
4609 }
4610 return adapt_term_to_stack(s,stack,vars,stochastic_variables);
4611 }
4612
4613
4614 data_expression_list findarguments(
4615 const variable_list& pars,
4616 const variable_list& parlist,
4617 const assignment_list& args,
4618 const data_expression_list& t2,
4619 const stacklisttype& stack,
4620 const variable_list& vars,
4621 const std::set<variable>& free_variables_in_body,
4622 const variable_list& stochastic_variables)
4623 {
4624 if (parlist.empty())
4625 {
4626 return t2;
4627 }
4628 data_expression_list result=findarguments(pars,parlist.tail(),args,t2,stack,vars,free_variables_in_body,stochastic_variables);
4629 data_expression rhs=find_(parlist.front(),args,stack,vars,free_variables_in_body,stochastic_variables);
4630
4631 result.push_front(rhs);
4632 return result;
4633 }
4634
4635 assignment_list find_dummy_arguments(
4636 const variable_list& parlist, // The list of all parameters.
4637 const assignment_list& args,
4638 const std::set<variable>& free_variables_in_body,
4639 const variable_list& stochastic_variables)
4640 {
4641 std::map<variable,data_expression> assignment_map;
4642 for(const assignment& a: args)
4643 {
4644 assignment_map[a.lhs()]=a.rhs();
4645 }
4646
4647 assignment_vector result;
4648 for(const variable& v: parlist)
4649 {
4650 if (std::find(stochastic_variables.begin(),stochastic_variables.end(),v)!=stochastic_variables.end())
4651 {
4652 // v is a stochastic variable. Insert the identity assignment.
4653 result.emplace_back(v, v);
4654 }
4655 else if (free_variables_in_body.find(v)==free_variables_in_body.end())
4656 {
4657 // The variable *i must get a default value.
4658 const data_expression rhs=representative_generator_internal(v.sort());
4659 result.emplace_back(v, rhs);
4660 }
4661 else
4662 {
4663 const std::map<variable,data_expression>::iterator k=assignment_map.find(v);
4664 if (k!=assignment_map.end()) // There is assignment for v. Use it.
4665 {
4666 result.emplace_back(k->first, k->second);
4667 assignment_map.erase(k);
4668 }
4669 }
4670
4671 }
4672 return assignment_list(result.begin(), result.end());
4673 }
4674
4675
4676
4677 assignment_list push_regular(
4678 const process_identifier& procId,
4679 const assignment_list& args,
4680 const stacklisttype& stack,
4681 const std::set < process_identifier >& pCRLprocs,
4682 bool singlestate,
4683 const variable_list& stochastic_variables)
4684 {
4685 objectdatatype& object=objectIndex(procId);
4686 const assignment_list t=find_dummy_arguments(stack.parameters,args,object.get_free_variables(),stochastic_variables);
4687
4688 if (singlestate)
4689 {
4690 return t;
4691 }
4692
4693 std::size_t i=1;
4694 for (const process_identifier& p: pCRLprocs)
4695 {
4696 if (p==procId)
4697 {
4698 break;
4699 }
4700 ++i;
4701 }
4702 return processencoding(i,t,stack);
4703 }
4704
4705
4706 assignment_list make_procargs_regular(
4707 const process_expression& t,
4708 const stacklisttype& stack,
4709 const std::set < process_identifier >& pcrlprcs,
4710 const bool singlestate,
4711 const variable_list& stochastic_variables)
4712 {
4713 /* t is a sequential composition of process variables */
4714
4715 if (is_seq(t))
4716 {
4717 throw mcrl2::runtime_error("Process is not regular, as it has stacking vars: " + process::pp(t) + ".");
4718 }
4719
4721 {
4722 const process_identifier& procId=atermpp::down_cast<process_instance_assignment>(t).identifier();
4723 const assignment_list& t1=atermpp::down_cast<process_instance_assignment>(t).assignments();
4724 return push_regular(procId,
4725 t1,
4726 stack,
4727 pcrlprcs,
4728 singlestate,
4729 stochastic_variables);
4730 }
4731
4732 throw mcrl2::runtime_error("Expect seq or name constructing a recursive invocation: " + process::pp(t) + ".");
4733 }
4734
4736 const process_identifier& procId,
4737 const assignment_list& args,
4738 const data_expression_list& t2,
4739 const stacklisttype& stack,
4740 const std::set < process_identifier >& pCRLprocs,
4741 const variable_list& vars,
4742 const variable_list& stochastic_variables)
4743 {
4744 objectdatatype& object=objectIndex(procId);
4745 const data_expression_list t=findarguments(object.parameters,
4746 stack.parameters,
4747 args,t2,stack,vars,
4748 object.get_free_variables(),
4749 stochastic_variables);
4750
4751 std::size_t i=1;
4752 for (const process_identifier& p: pCRLprocs)
4753 {
4754 if (p==procId)
4755 {
4756 break;
4757 }
4758 ++i;
4759 }
4760 const data_expression_list l=processencoding(i,t,stack);
4761 assert(l.size()==function_sort(stack.opns->push.sort()).domain().size());
4762 return application(stack.opns->push,l);
4763 }
4764
4766 const process_expression& t,
4767 const stacklisttype& stack,
4768 const std::set < process_identifier >& pcrlprcs,
4769 const variable_list& vars,
4770 const variable_list& stochastic_variables)
4771 {
4772 /* t is a sequential composition of process variables */
4773
4774 if (is_seq(t))
4775 {
4776 const process_instance_assignment process=atermpp::down_cast<process_instance_assignment>(seq(t).left());
4777 const process_expression& process2=down_cast<seq>(t).right();
4778 const process_identifier& procId=process.identifier();
4779 const assignment_list& t1=process.assignments();
4780
4782 {
4783 const data_expression stackframe=make_procargs_stack(process2,stack,pcrlprcs, vars,stochastic_variables);
4784 return push_stack(procId,t1, data_expression_list({ stackframe }),stack,pcrlprcs,vars,stochastic_variables);
4785 }
4786
4787 return push_stack(procId,t1, data_expression_list({ data_expression(stack.opns->emptystack) }),
4788 stack,pcrlprcs,vars,stochastic_variables);
4789 }
4790
4792 {
4794 const assignment_list t1=process_instance_assignment(t).assignments();
4795
4797 {
4798 return push_stack(procId,
4799 t1,
4800 data_expression_list({ data_expression(application(stack.opns->pop,stack.stackvar)) }),
4801 stack,
4802 pcrlprcs,
4803 vars,
4804 stochastic_variables);
4805 }
4806 return push_stack(procId,
4807 t1,
4808 data_expression_list({ data_expression(stack.opns->emptystack) }),
4809 stack,
4810 pcrlprcs,
4811 vars,
4812 stochastic_variables);
4813 }
4814
4815 throw mcrl2::runtime_error("Expect seq or name putting processes on a stack: " + process::pp(t) +".");
4816 }
4817
4818 assignment_list make_procargs(
4819 const process_expression& t,
4820 const stacklisttype& stack,
4821 const std::set < process_identifier >& pcrlprcs,
4822 const variable_list& vars,
4823 const bool regular,
4824 const bool singlestate,
4825 const variable_list& stochastic_variables)
4826 {
4827 if (regular)
4828 {
4829 return make_procargs_regular(t,stack,pcrlprcs,singlestate,stochastic_variables);
4830 }
4831 /* return a stackframe */
4832 data_expression sf=make_procargs_stack(t,stack,pcrlprcs,vars,stochastic_variables);
4833 return assignment_list({ assignment(stack.stackvar,sf) });
4834 }
4835
4836 static
4837 bool occursin(const variable& name,
4838 const variable_list& pars)
4839 {
4840 assert(is_variable(name));
4841 for (const variable& v: pars)
4842 {
4843 if (name.name()==v.name())
4844 {
4845 return true;
4846 }
4847 }
4848 return false;
4849 }
4850
4851
4853 const variable_list& pars,
4854 const stacklisttype& stack)
4855 {
4856 /* stack.parameters is the total list of parameters of the
4857 aggregated pCRL process. The variable pars contains
4858 the list of all variables occuring in the initial
4859 process. */
4860
4861 data_expression_vector result;
4862 for(const variable& par: stack.parameters)
4863 {
4864 /* // Check whether it is a stochastic variable.
4865 if (std::find(stochastic_variables.begin(),stochastic_variables.end(),par)!=pars.end())
4866 {
4867 result.push_back(assignment(par,par));
4868 }
4869 // Otherwise, check that is is an ordinary parameter.
4870 else */
4871 if (std::find(pars.begin(),pars.end(),par)!=pars.end())
4872 {
4873 result.push_back(par);
4874 }
4875 /* otherwise the value of this argument is irrelevant, so
4876 make it a don't care variable. */
4877 else
4878 {
4879 result.push_back(representative_generator_internal(par.sort()));
4880 }
4881 }
4882 return data_expression_list(result.begin(), result.end());
4883 }
4884
4885 assignment_list pushdummy_regular(
4886 const variable_list& pars,
4887 const stacklisttype& stack,
4888 const variable_list& stochastic_variables)
4889 {
4890 /* stack.parameters is the total list of parameters of the
4891 aggregated pCRL process. The variable pars contains
4892 the list of all variables occuring in the initial
4893 process. */
4894
4895 assignment_vector result;
4896 for(const variable& par: stack.parameters)
4897 {
4898 // Check whether it is a stochastic variable.
4899 if (std::find(stochastic_variables.begin(),stochastic_variables.end(),par)!=pars.end())
4900 {
4901 result.emplace_back(par, par);
4902 }
4903 // Otherwise, check that is is an ordinary parameter.
4904 else if (std::find(pars.begin(),pars.end(),par)!=pars.end())
4905 {
4906 }
4907 /* otherwise the value of this argument is irrelevant, so
4908 make it a don't care variable. */
4909 else
4910 {
4911 result.emplace_back(par, representative_generator_internal(par.sort()));
4912 }
4913 }
4914 return assignment_list(result.begin(), result.end());
4915 }
4916
4917 data_expression_list pushdummyrec_stack(
4918 const variable_list& totalpars,
4919 const variable_list& pars,
4920 const stacklisttype& stack,
4921 const variable_list& stochastic_variables)
4922 {
4923 /* totalpars is the total list of parameters of the
4924 aggregated pCRL process. The variable pars contains
4925 the list of all variables occuring in the initial
4926 process. */
4927
4928 if (totalpars.empty())
4929 {
4930 return data_expression_list({ data_expression(stack.opns->emptystack) });
4931 }
4932
4933 const variable& par=totalpars.front();
4934 if (std::find(pars.begin(),pars.end(),par)!=pars.end())
4935 {
4936 data_expression_list result=pushdummyrec_stack(totalpars.tail(),pars,stack,stochastic_variables);
4937 result.push_front(par);
4938 return result;
4939 }
4940 /* Check whether the parameter par refers to a stochastic variables */
4941 if (std::find(stochastic_variables.begin(),stochastic_variables.end(),par)!=pars.end())
4942 {
4943 data_expression_list result=pushdummyrec_stack(totalpars.tail(),pars,stack,stochastic_variables);
4944 result.push_front(par);
4945 return result;
4946 }
4947 /* otherwise the value of this argument is irrelevant, so
4948 make it Nil, if a regular translation is made. If a translation
4949 with stacks is made, then yield a default `unique' term. */
4950 data_expression_list result=pushdummyrec_stack(totalpars.tail(),pars,stack,stochastic_variables);
4951 result.push_front(representative_generator_internal(par.sort()));
4952 return result;
4953 }
4954
4955 data_expression_list pushdummy_stack(
4956 const variable_list& parameters,
4957 const stacklisttype& stack,
4958 const variable_list& stochastic_variables)
4959 {
4960 return pushdummyrec_stack(stack.parameters,parameters,stack,stochastic_variables);
4961 }
4962
4963 data_expression_list make_initialstate(
4964 const process_identifier& initialProcId,
4965 const stacklisttype& stack,
4966 const std::set < process_identifier >& pcrlprcs,
4967 const bool regular,
4968 const bool singlecontrolstate,
4969 const stochastic_distribution& initial_stochastic_distribution)
4970 {
4971 std::size_t i=1;
4972 for (const process_identifier& p: pcrlprcs)
4973 {
4974 if (p==initialProcId)
4975 {
4976 break;
4977 }
4978 ++i;
4979 }
4980 /* i is the index of the initial state */
4981
4982 if (regular)
4983 {
4984 data_expression_list result=
4985 pushdummy_regular_data_expressions(objectIndex(initialProcId).parameters,
4986 stack);
4987 if (!singlecontrolstate)
4988 {
4989 return processencoding(i,result,stack);
4990 }
4991 return result;
4992 }
4993 else
4994 {
4995 data_expression_list result=
4996 pushdummy_stack(objectIndex(initialProcId).parameters,
4997 stack,
4998 initial_stochastic_distribution.variables());
4999 const data_expression_list l=processencoding(i,result,stack);
5000 assert(l.size()==function_sort(stack.opns->push.sort()).domain().size());
5001 return data_expression_list({ application(stack.opns->push,l) });
5002 }
5003 }
5004
5005 /************************* Routines for summands **************************/
5006
5007 assignment_list dummyparameterlist(const stacklisttype& stack,
5008 const bool singlestate)
5009 {
5010 if (singlestate)
5011 {
5012 return assignment_list();
5013 }
5014
5015 return processencoding(1,assignment_list(),stack); /* Take 1 as dummy indicator */
5016 }
5017
5018
5020 stochastic_action_summand_vector& action_summands,
5021 deadlock_summand_vector& deadlock_summands,
5022 const variable_list& sumvars,
5023 const data_expression& condition,
5024 const action_list& multiAction,
5025 const data_expression& actTime,
5026 const stochastic_distribution& distribution,
5027 const assignment_list& procargs,
5028 const bool has_time,
5029 const bool is_deadlock_summand)
5030 {
5031 assert(distribution != stochastic_distribution());
5032 assert(std::is_sorted(multiAction.begin(), multiAction.end()));
5033
5034 const data_expression rewritten_condition=RewriteTerm(condition);
5035 if (rewritten_condition==sort_bool::false_())
5036 {
5037 return;
5038 }
5039
5040 if (is_deadlock_summand)
5041 {
5042 insert_timed_delta_summand(action_summands,
5043 deadlock_summands,
5044 deadlock_summand(sumvars,
5045 rewritten_condition,
5046 has_time?deadlock(actTime):deadlock()),options.ignore_time);
5047 }
5048 else
5049 {
5050 action_summands.emplace_back(sumvars,
5051 rewritten_condition,
5052 has_time ? multi_action(multiAction, actTime) : multi_action(multiAction),
5053 procargs,
5054 stochastic_distribution(distribution.variables(), RewriteTerm(distribution.distribution())));
5055 }
5056 }
5057
5058
5060 const process_identifier& procId,
5061 stochastic_action_summand_vector& action_summands,
5062 deadlock_summand_vector& deadlock_summands,
5063 process_expression summandterm,
5064 const std::set < process_identifier >& pCRLprocs,
5065 const stacklisttype& stack,
5066 const bool regular,
5067 const bool singlestate,
5068 const variable_list& process_parameters)
5069
5070 {
5071 data_expression atTime;
5072 action_list multiAction;
5073 bool is_delta_summand=false;
5074 bool has_time=false;
5075
5076 if (isDeltaAtZero(summandterm))
5077 {
5078 // delta@0 does not need to be added.
5079 return;
5080 }
5081
5082 /* remove the sum operators; collect the sum variables in the
5083 list sumvars */
5084
5085 variable_list sumvars;
5086 while (is_sum(summandterm))
5087 {
5088 sumvars=sum(summandterm).variables() + sumvars;
5089 summandterm=sum(summandterm).operand();
5090 }
5091
5092 // Check whether the variables in sumvars clash with the parameter list,
5093 // and rename the summandterm accordingly.
5094 maintain_variables_in_rhs< mutable_map_substitution<> > local_sigma;
5095 alphaconvert(sumvars,local_sigma,process_parameters,data_expression_list());
5096 summandterm=substitute_pCRLproc(summandterm, local_sigma);
5097
5098
5099 /* translate the condition */
5100
5101 data_expression condition1;
5102 if (regular && singlestate)
5103 {
5104 condition1=sort_bool::true_();
5105 }
5106 else
5107 {
5108 condition1=correctstatecond(procId,pCRLprocs,stack,regular);
5109 }
5110
5111 stochastic_distribution cumulative_distribution(variable_list(),sort_real::real_one());
5112
5113 /* The conditions are collected for use. The stochastic operators before the action are ignored */
5114 while ((is_if_then(summandterm)||is_stochastic_operator(summandterm)))
5115 {
5116 if (is_if_then(summandterm))
5117 {
5118 const data_expression& localcondition=down_cast<if_then>(summandterm).condition();
5119 if (!(regular && singlestate))
5120 {
5121 condition1=lazy::and_(
5122 condition1,
5123 ((regular)?localcondition:
5124 adapt_term_to_stack(
5125 localcondition,
5126 stack,
5127 sumvars,
5128 cumulative_distribution.variables())));
5129 }
5130 else
5131 {
5132 /* regular and singlestate */
5133 condition1=lazy::and_(localcondition,condition1);
5134 }
5135 summandterm=if_then(summandterm).then_case();
5136 }
5137 else
5138 {
5139 const stochastic_operator& sto=atermpp::down_cast<const stochastic_operator>(summandterm);
5140 cumulative_distribution=stochastic_distribution(
5141 cumulative_distribution.variables()+sto.variables(),
5142 real_times_optimized(cumulative_distribution.distribution(),
5143 sto.distribution()));
5144 summandterm=sto.operand();
5145 }
5146 }
5147
5148 if (!cumulative_distribution.variables().empty() && !sumvars.empty())
5149 {
5150 throw mcrl2::runtime_error("Cannot permute sum operator and stochastic distribution in " + data::pp(summandterm));
5151 }
5152
5153 if (is_seq(summandterm))
5154 {
5155 /* only one summand is needed */
5156 process_expression t1=seq(summandterm).left();
5157 process_expression t2=seq(summandterm).right();
5158 if (is_at(t1))
5159 {
5160 has_time=true;
5161 atTime=at(t1).time_stamp();
5162 t1=at(t1).operand();
5163 }
5164
5165 if (t1==delta())
5166 {
5167 is_delta_summand=true;
5168 }
5169 else
5170 {
5171 assert(is_tau(t1)||is_action(t1)||is_sync(t1));
5172 multiAction=to_sorted_action_list(t1);
5173 }
5174
5175 stochastic_distribution distribution(variable_list(),sort_real::real_one());
5176 process_expression t2_new=t2;
5178 {
5179 const stochastic_operator& sto=atermpp::down_cast<const stochastic_operator>(t2);
5181 t2_new=sto.operand();
5182 }
5183 const assignment_list procargs=make_procargs(t2_new,stack,pCRLprocs,sumvars,regular,singlestate,distribution.variables());
5184
5185 if (!regular)
5186 {
5187 if (!is_delta_summand)
5188 {
5189 multiAction=adapt_multiaction_to_stack(
5190 multiAction,stack,sumvars);
5191 }
5192 if (has_time)
5193 {
5194 atTime=adapt_term_to_stack(
5195 atTime,stack,sumvars,variable_list());
5196 }
5197 distribution=stochastic_distribution(
5198 distribution.variables(),
5199 adapt_term_to_stack(distribution.distribution(),stack,sumvars,distribution.variables()));
5200 }
5201 insert_summand(action_summands,deadlock_summands,
5202 sumvars,condition1,multiAction,
5203 atTime,distribution,procargs,
5204 has_time,is_delta_summand);
5205 return;
5206 }
5207
5208 /* There is a single initial multiaction or deadlock, possibly timed*/
5209 if (is_at(summandterm))
5210 {
5211 atTime=at(summandterm).time_stamp();
5212 summandterm=at(summandterm).operand();
5213 has_time=true;
5214 }
5215 else
5216 {
5217 // do nothing
5218 }
5219
5220 if (is_delta(summandterm))
5221 {
5222 is_delta_summand=true;
5223 }
5224 else if (is_tau(summandterm))
5225 {
5226 // multiAction is already empty.
5227 }
5228 else if (is_action(summandterm))
5229 {
5230 assert(multiAction.empty()); // so the result must be sorted.
5231 multiAction.push_front(action(summandterm));
5232 }
5233 else if (is_sync(summandterm))
5234 {
5235 multiAction=to_sorted_action_list(summandterm);
5236 }
5237 else
5238 {
5239 throw mcrl2::runtime_error("expected multiaction " + process::pp(summandterm) +".");
5240 }
5241
5242 if (regular)
5243 {
5244 if (!is_delta_summand)
5245 /* As termination has been replaced by an explicit action terminated, followed
5246 * by delta, a single terminating action cannot exist for regular processes. */
5247 {
5248 throw mcrl2::runtime_error("terminating processes should not exist when using the regular flag");
5249 }
5250 insert_summand(action_summands,
5251 deadlock_summands,
5252 sumvars,
5253 condition1,
5254 multiAction,
5255 atTime,
5256 stochastic_distribution(variable_list(),sort_real::real_one()),
5257 dummyparameterlist(stack,singlestate),
5258 has_time,
5259 is_delta_summand);
5260 return;
5261 }
5262
5263 multiAction=adapt_multiaction_to_stack(multiAction,stack,sumvars);
5264 assignment_list procargs ({ assignment(stack.stackvar,application(stack.opns->pop,stack.stackvar)) });
5265
5266 insert_summand(
5267 action_summands,
5268 deadlock_summands,
5269 sumvars,
5270 condition1,
5271 multiAction,
5272 atTime,
5273 stochastic_distribution(variable_list(),sort_real::real_one()), // TODO: UNLIKELY THAT THIS IS CORRECT.
5274 procargs,
5275 has_time,
5276 is_delta_summand);
5277
5278 return;
5279 }
5280
5281
5283 const process_identifier& procId,
5284 stochastic_action_summand_vector& action_summands,
5285 deadlock_summand_vector& deadlock_summands,
5286 const process_expression& body,
5287 const variable_list& pars,
5288 const stacklisttype& stack,
5289 const bool regular,
5290 const bool singlestate,
5291 const std::set < process_identifier >& pCRLprocs)
5292 {
5293 if (is_choice(body))
5294 {
5295 const process_expression& t1=down_cast<choice>(body).left();
5296 const process_expression& t2=down_cast<choice>(body).right();
5297
5298 collectsumlistterm(procId,action_summands,deadlock_summands,t1,pars,stack,
5299 regular,singlestate,pCRLprocs);
5300 collectsumlistterm(procId,action_summands,deadlock_summands,t2,pars,stack,
5301 regular,singlestate,pCRLprocs);
5302 return;
5303 }
5305 {
5306 /* Remove leading stochastic operators. They will not become
5307 part of this summand */
5308 const stochastic_operator& sto=atermpp::down_cast<const stochastic_operator>(body);
5309 collectsumlistterm(procId,action_summands,deadlock_summands,sto.operand(),pars,stack,
5310 regular,singlestate,pCRLprocs);
5311 }
5312 else
5313 {
5314 add_summands(procId,
5315 action_summands,
5316 deadlock_summands,
5317 body,
5318 pCRLprocs,
5319 stack,
5320 regular,
5321 singlestate,
5322 pars);
5323 }
5324 }
5325
5327 stochastic_action_summand_vector& action_summands,
5328 deadlock_summand_vector& deadlock_summands,
5329 const std::set < process_identifier >& pCRLprocs,
5330 const variable_list& pars,
5331 const stacklisttype& stack,
5332 bool regular,
5333 bool singlestate)
5334 {
5335 for (const process_identifier& p: pCRLprocs)
5336 {
5337 objectdatatype& object=objectIndex(p);
5338
5339 collectsumlistterm(
5340 p,
5341 action_summands,
5342 deadlock_summands,
5343 object.processbody,
5344 pars,
5345 stack,
5346 regular,
5347 singlestate,
5348 pCRLprocs);
5349 }
5350 }
5351
5352
5353 /**************** Enumtype and enumeratedtype **********************************/
5354
5356 {
5357 public:
5360 data_expression_list elementnames;
5361 function_symbol_list functions;
5362
5363 enumeratedtype(const std::size_t n,
5365 {
5366 size=n;
5367 if (n==2)
5368 {
5370 elementnames = data_expression_list({ sort_bool::false_(), sort_bool::true_()});
5371 }
5372 else
5373 {
5374 //create new sort identifier
5375 basic_sort sort_id(spec.fresh_identifier_generator("Enum" + std::to_string(n)));
5376 sortId=sort_id;
5377 //create structured sort
5378 // Enumi = struct en_i | ... | e0_i;
5379 structured_sort_constructor_list struct_conss;
5380 for (std::size_t j=0 ; (j<n) ; j++)
5381 {
5382 //create constructor declaration of the structured sort
5383 const identifier_string s=spec.fresh_identifier_generator("e" + std::to_string(j) + "_" + std::to_string(n));
5384 const structured_sort_constructor struct_cons(s,"");
5385
5386 struct_conss.push_front(struct_cons);
5387 }
5388 structured_sort sort_struct(struct_conss);
5389
5390 //add declaration of standard functions
5391 spec.data.add_alias(alias(sort_id, sort_struct));
5392
5393 //store new declarations in return value w
5394 sortId = sort_id;
5395 const function_symbol_vector& constructors=spec.data.constructors(sort_id);
5396 assert(constructors.size()==n);
5397 elementnames = data::data_expression_list(constructors.begin(),constructors.end());
5398 }
5399 }
5400
5402 {
5403 size=e.size;
5405 elementnames=e.elementnames;
5406 functions=e.functions;
5407 }
5408
5410 = default;
5411
5412 ~enumeratedtype() = default;
5413 };
5414
5416 {
5417 std::size_t w;
5418
5419 for (w=0; ((w<enumeratedtypes.size()) && (enumeratedtypes[w].size!=n)); ++w) {};
5420
5421 if (w==enumeratedtypes.size()) // There is no enumeratedtype of arity n.
5422 {
5423 enumeratedtypes.emplace_back(n, *this);
5424 }
5425 return w;
5426 }
5427
5428 data::function_symbol find_case_function(std::size_t index, const sort_expression& sort) const
5429 {
5430 for (const data::function_symbol& f: enumeratedtypes[index].functions)
5431 {
5432 sort_expression_list domain = function_sort(f.sort()).domain();
5433 assert(domain.size() >= 2);
5434 if (*(++domain.begin())==sort)
5435 {
5436 return f;
5437 }
5438 };
5439
5440 throw mcrl2::runtime_error("searching for a nonexisting case function on sort " + data::pp(sort) +".");
5442 }
5443
5445 const std::size_t index,
5446 const data::function_symbol& functionname,
5447 const sort_expression& sort)
5448 {
5449 variable_list vars;
5450 data_expression_list args;
5451 data_expression_list xxxterm;
5452
5453 const variable v1=get_fresh_variable("x",sort);
5454 const std::size_t n=enumeratedtypes[index].size;
5455 for (std::size_t j=0; (j<n); j++)
5456 {
5457 const variable v=get_fresh_variable("y",sort);
5458 vars.push_front(v);
5459 args.push_front(v);
5460 xxxterm.push_front(v1);
5461 }
5462
5463 /* I generate here an equation of the form
5464 C(e,x,x,x,...x)=x for a variable x. */
5465 const sort_expression& s=enumeratedtypes[index].sortId;
5466 const variable v=get_fresh_variable("e",s);
5467
5468 // we add e in front of xxxterm; note that xxxterm is not used afterwards
5469 // anymore, so we don't need to create a temporary copy for it here.
5470 xxxterm.push_front(data_expression(v));
5471 data.add_equation(
5472 data_equation(
5473 variable_list({ v1, v }),
5474 application(functionname,xxxterm),
5475 v1));
5477
5478 variable_list auxvars=vars;
5479
5480 const data_expression_list& elementnames=enumeratedtypes[index].elementnames;
5481 for (const data_expression& w: elementnames)
5482 {
5483 assert(auxvars.size()>0);
5484 args.push_front(w);
5485 data.add_equation(data_equation(
5486 vars,
5487 application(functionname,args),
5488 auxvars.front()));
5489 //fresh_equation_added=true; already set before the loop, omitting here
5490 args.pop_front();
5491 auxvars.pop_front();
5492 }
5493 }
5494
5496 const sort_expression& sort,
5497 const std::size_t enumeratedtype_index)
5498 {
5499 assert(enumeratedtype_index<enumeratedtypes.size());
5500 /* first find out whether the function exists already, in which
5501 case nothing needs to be done */
5502
5503 const function_symbol_list& functions=enumeratedtypes[enumeratedtype_index].functions;
5504 const function_symbol_list::const_iterator i = std::find_if(functions.begin(), functions.end(), [&sort](const data::function_symbol& w){
5505 const function_sort& w1sort(down_cast<function_sort>(w.sort()));
5506 assert(function_sort(w1sort).domain().size()>1);
5507 // w matches if the second sort of the case function equals sort
5508 return *(++(w1sort.domain().begin())) == sort;
5509 });
5510 if(i != functions.end()) {
5511 return;
5512 }
5513 /* The function does not exist;
5514 Create a new function of enumeratedtype e, on sort */
5515
5516 if (enumeratedtypes[enumeratedtype_index].sortId==sort_bool::bool_())
5517 {
5518 /* take the if function on sort 'sort' */
5519 enumeratedtypes[enumeratedtype_index].functions.push_front(if_(sort));
5520 return;
5521 }
5522 // else
5523 sort_expression_list newsortlist;
5524 const std::size_t n=enumeratedtypes[enumeratedtype_index].size;
5525 for (std::size_t j=0; j<n ; ++j)
5526 {
5527 newsortlist.push_front(sort);
5528 }
5529 const sort_expression& sid=enumeratedtypes[enumeratedtype_index].sortId;
5530 newsortlist.push_front(sid);
5531
5532 const function_sort newsort(newsortlist,sort);
5533 const data::function_symbol casefunction(
5534 fresh_identifier_generator("C" + std::to_string(n) + "_" +
5535 (!is_basic_sort(newsort)?"":std::string(basic_sort(sort).name()))), newsort);
5536 data.add_mapping(casefunction);
5537 enumeratedtypes[enumeratedtype_index].functions.push_front(casefunction);
5538
5539 define_equations_for_case_function(enumeratedtype_index,casefunction,sort);
5540 return;
5541 }
5542
5544 {
5545 public:
5546 // enumtypes are not supposed to be copied.
5547 enumtype(const enumtype&)=delete;
5548 enumtype& operator =(const enumtype& )=delete;
5549
5552
5553 enumtype(std::size_t n,
5554 const sort_expression_list& fsorts,
5555 const sort_expression_list& gsorts,
5557 {
5558 enumeratedtype_index=spec.create_enumeratedtype(n);
5559
5560 var=variable(spec.fresh_identifier_generator("e"),spec.enumeratedtypes[enumeratedtype_index].sortId);
5561 spec.insertvariable(var,true);
5562
5563 for (const sort_expression& f: fsorts)
5564 {
5565 spec.create_case_function_on_enumeratedtype(f,enumeratedtype_index);
5566 }
5567
5568 for (const sort_expression& f: gsorts)
5569 {
5570 spec.create_case_function_on_enumeratedtype(f,enumeratedtype_index);
5571 }
5572
5573 spec.create_case_function_on_enumeratedtype(sort_bool::bool_(),enumeratedtype_index);
5574
5576 {
5577 spec.create_case_function_on_enumeratedtype(sort_real::real_(),enumeratedtype_index);
5578 }
5579 }
5580
5581 ~enumtype() = default;
5582 };
5583 /************** Merge summands using enumerated type ***********************/
5584
5585 /* The function below returns true if the variable var could be mapped
5586 on an existing variable v' in matchinglist. The pars and args form pair
5587 form a substitution that will be extended with the pair [var,v']. i
5588 It returns false if the variable is new.
5589
5590 If var is added (and not mapped on some other variable in the matchinglist/aka v)
5591 it is checked whether var occurs in v or in the process_parameters,
5592 in which case var is renamed to a fresh variable. The renaming is added
5593 to the substitution encoded in pars/args.
5594 */
5595
5596
5598 variable& var,
5599 const variable_list& v,
5600 variable_list& matchinglist,
5601 variable_list& pars,
5602 data_expression_list& args,
5603 const variable_list& process_parameters)
5604 {
5605 variable_list auxmatchinglist;
5606
5607 /* First find out whether var:sort can be matched to a
5608 term in the matching list */
5609
5610 /* first find out whether the variable occurs in the matching
5611 list, so, they can be joined */
5612
5613 for (variable_list::const_iterator i=matchinglist.begin();
5614 i!=matchinglist.end(); ++i)
5615 {
5616 variable var1=*i;
5617 if (var.sort()==var1.sort())
5618 {
5619 /* sorts match, so, we join the variables */
5620 if (var!=var1)
5621 {
5622 pars.push_front(var);
5623 args.push_front(data_expression(var1));
5624 }
5625 /* copy remainder of matching list */
5626 for (++i ; i!=matchinglist.end(); ++i)
5627 {
5628 auxmatchinglist.push_front(*i);
5629 }
5630 matchinglist=reverse(auxmatchinglist);
5631 return true;
5632 }
5633 else
5634 {
5635 auxmatchinglist.push_front(var1);
5636 }
5637 }
5638
5639 /* turn auxmatchinglist back in normal order, and put the result
5640 in *matchinglist */
5641
5642 matchinglist=reverse(auxmatchinglist);
5643
5644 /* in this case no matching argument has been found.
5645 So, we must find out whether *var is an allowed variable, not
5646 occuring in the variablelist v.
5647 But if so, we must replace it by a new one. */
5648 for (const variable& var1 : v)
5649 {
5650 if (var.name() == var1.name())
5651 {
5652 pars.push_front(var);
5653 var=get_fresh_variable(var.name(),var.sort());
5654 args.push_front(data_expression(var));
5655 return false;
5656 }
5657 }
5658
5659 /* Check whether the variable occurs in the prcoess parameter list, in which case
5660 it also needs to be renamed */
5661 for (const variable& var1: process_parameters)
5662 {
5663 if (var.name()==var1.name())
5664 {
5665 pars.push_front(var);
5666 var=get_fresh_variable(var.name(),var.sort());
5667 args.push_front(data_expression(var));
5668 return false;
5669 }
5670 }
5671
5672 return false;
5673 }
5674
5675 static
5676 data_expression_list extend(const data_expression& c, const data_expression_list& cl)
5677 {
5678 return data_expression_list(cl.begin(),
5679 cl.end(),
5680 [&c](const data_expression& e)->data_expression { return lazy::and_(c,e);} );
5681
5682 }
5683
5685 {
5687 for(const variable& v: vl)
5688 {
5689 const data_expression unique=representative_generator_internal(v.sort(),false);
5690 result=lazy::and_(result,equal_to(v,unique));
5691 }
5692 return result;
5693 }
5694
5695
5696 data_expression_list extend_conditions(
5697 const variable& var,
5698 const data_expression_list& conditionlist)
5699 {
5700 try
5701 {
5703 const data_expression newcondition=equal_to(var,unique);
5704 return extend(newcondition,conditionlist);
5705 }
5706 catch (mcrl2::runtime_error&)
5707 {
5708 // The representative generator failed to find a term of var.sort().
5709 // No condition is generated, meaning that var will be unrestrained. This
5710 // is correct, and as the var.sort() has no concrete value, this will most
5711 // likely not effect matters as state space generation.
5712 return conditionlist;
5713 }
5714 }
5715
5716
5717 data_expression transform_matching_list(const variable_list& matchinglist)
5718 {
5720 for(const variable& v: matchinglist)
5721 {
5722 try
5723 {
5724 data_expression unique=representative_generator_internal(v.sort(),false);
5725 result=lazy::and_(result, equal_to(v,unique));
5726 }
5727 catch (mcrl2::runtime_error&) // NOLINT(bugprone-empty-catch)
5728 {
5729 // No representant for sort v.sort() could be found. No condition is added.
5730 }
5731 }
5732 return result;
5733 }
5734
5735
5736 data_expression_list addcondition(
5737 const variable_list& matchinglist,
5738 const data_expression_list& conditionlist)
5739 {
5740 data_expression_list result=conditionlist;
5741 result.push_front(transform_matching_list(matchinglist));
5742 return result;
5743 }
5744
5745/* Join the variables of v1 to v2 and rename the variables in v1
5746 * if needed. The conditionlist gives conditions to restrain variables
5747 * that did not occur in the other list. renaming pars and args give
5748 * renamings to be applied if variables in v1 had to be renamed. It
5749 * is not allowed to rename to names already occurring in the parameter
5750 * list. */
5751
5752 variable_list merge_var(
5753 const variable_list& v1,
5754 const variable_list& v2,
5755 std::vector < variable_list>& renamings_pars,
5756 std::vector < data_expression_list>& renamings_args,
5757 data_expression_list& conditionlist,
5758 const variable_list& process_parameters)
5759 {
5760 data_expression_list renamingargs;
5761 variable_list renamingpars;
5762 variable_list matchinglist=v2;
5763
5764 /* If the sequence of sum variables is reversed,
5765 * the variables are merged in the same sequence for all
5766 * summands (due to a suggestion of Muck van Weerdenburg) */
5767
5768 variable_list v1h=(v2.empty()?reverse(v1):v1);
5769
5770 variable_list result=v2;
5771 for (const variable& v_: v1h)
5772 {
5773 variable v=v_;
5774 if (!mergeoccursin(v,v2,
5775 matchinglist,renamingpars,renamingargs,process_parameters))
5776 {
5777 result.push_front(v);
5778 conditionlist=extend_conditions(v,conditionlist);
5779 }
5780 }
5781 conditionlist=addcondition(matchinglist,conditionlist);
5782 renamings_pars.push_back(renamingpars);
5783 renamings_args.push_back(renamingargs);
5784 return result;
5785 }
5786
5787 variable_list make_binary_sums(
5788 std::size_t n,
5789 const sort_expression& enumtypename,
5790 data_expression& condition,
5791 const variable_list& tail)
5792 {
5793 variable_list result;
5794 assert(n>1);
5795 condition=sort_bool::true_();
5796
5797 n=n-1;
5798 for (result=tail ; (n>0) ; n=n/2)
5799 {
5800 variable sumvar=get_fresh_variable("e",enumtypename);
5801 result.push_front(sumvar);
5802 if ((n % 2)==0)
5803 {
5804 condition=lazy::and_(sumvar,condition);
5805 }
5806 else
5807 {
5808 condition=lazy::or_(sumvar,condition);
5809 }
5810 }
5811 return result;
5812 }
5813
5815 std::size_t n,
5816 const variable_list& sums,
5817 data_expression_list& terms,
5818 const sort_expression& termsort,
5819 const enumtype& e)
5820 {
5821 assert(!terms.empty());
5822
5823 if (n<=0)
5824 {
5825 assert(!terms.empty());
5826 const data_expression t=terms.front();
5827 terms.pop_front();
5828 return t;
5829 }
5830
5831 assert(!sums.empty());
5832 const variable& casevar=sums.front();
5833
5834 const data_expression t=construct_binary_case_tree_rec(n / 2,sums.tail(),terms,termsort,e);
5835
5836 if (terms.empty())
5837 {
5838 return t;
5839 }
5840
5841 const data_expression t1=construct_binary_case_tree_rec(n / 2,sums.tail(),terms,termsort,e);
5842
5843 if (t==t1)
5844 {
5845 return t;
5846 }
5847 return application(find_case_function(e.enumeratedtype_index, termsort), casevar, t, t1);
5848 }
5849
5850 template <class T>
5851 bool all_equal(const atermpp::term_list<T>& l)
5852 {
5853 if (l.empty())
5854 {
5855 return true;
5856 }
5857 typename atermpp::term_list<T>::const_iterator i=l.begin();
5858 const T& first=*i;
5859 for(++i ; i!=l.end(); ++i)
5860 {
5861 if (*i!=first)
5862 {
5863 return false;
5864 }
5865 }
5866 return true;
5867 }
5868
5870 std::size_t n,
5871 const variable_list& sums,
5872 data_expression_list terms,
5873 const sort_expression& termsort,
5874 const enumtype& e)
5875 {
5876 return construct_binary_case_tree_rec(n-1,sums,terms,termsort,e);
5877 }
5878
5879 static
5881 const stochastic_action_summand& summand1,
5882 const stochastic_action_summand& summand2)
5883 {
5884 if (summand1.has_time()!= summand2.has_time())
5885 {
5886 return false;
5887 }
5888
5889 /* Here the multiactions are proper multi actions,
5890 both with or without a time indication */
5891
5892 /* The actions can be clustered if they contain
5893 the same actions, with the same sorts for the
5894 actions. We assume that the multiactions are
5895 ordered.
5896 */
5897
5898 const action_list multiactionlist1=summand1.multi_action().actions();
5899 const action_list multiactionlist2=summand2.multi_action().actions();
5900 action_list::const_iterator i2=multiactionlist2.begin();
5901 for (action_list::const_iterator i1=multiactionlist1.begin(); i1!=multiactionlist1.end(); ++i1,++i2)
5902 {
5903 if (i2==multiactionlist2.end())
5904 {
5905 return false;
5906 }
5907 if (i1->label()!=i2->label())
5908 {
5909 return false;
5910 }
5911 }
5912 return i2 == multiactionlist2.end();
5913 }
5914
5915 static
5916 data_expression getRHSassignment(const variable& var, const assignment_list& as)
5917 {
5918 for (const assignment& a: as)
5919 {
5920 if (a.lhs()==var)
5921 {
5922 return a.rhs();
5923 }
5924 }
5925 return var;
5926 }
5927
5928 // This function makes an assignment_list where the terms in resultnextstate are
5929 // assigned to the variables in the variable parameters, where no assignment is added
5930 // if the righ and left hand side are identical, except when the parameter equals
5931 // the expression in resultnextstate and this expression is a variable in sumvars
5932 // or stochvars.
5933 assignment_list make_optimised_assignment_list(const variable_list& parameters,
5934 const data_expression_list& resultnextstate,
5935 const variable_list& sum_vars,
5936 const variable_list& stoch_vars)
5937 {
5938 assert(parameters.size()==resultnextstate.size());
5939
5940 data_expression_list::const_iterator i=resultnextstate.begin();
5941 return assignment_list(parameters.begin(),
5942 parameters.end(),
5943 [&i](const variable& p){ return assignment(p, *i++); }, // assignment to be inserted.
5944 [&sum_vars, &stoch_vars](const assignment& p) // filter. If true, assignment is added.
5945 { return p.lhs()!=p.rhs() ||
5946 std::find(sum_vars.begin(),sum_vars.end(),p.rhs())!=sum_vars.end() ||
5947 std::find(stoch_vars.begin(),stoch_vars.end(),p.rhs())!=stoch_vars.end(); });
5948
5949 }
5950
5951
5953 const enumtype& e,
5954 const stochastic_action_summand_vector& action_summands,
5955 const variable_list& parameters)
5956 {
5957 /* This function gets a list of summands, with
5958 the same multiaction and time
5959 status. It yields a single clustered summand
5960 by introducing an auxiliary sum operator, with
5961 a variable of enumtype. In case binary is used,
5962 a sequence of variables are introduced of sort Bool */
5963
5964 std::size_t n=action_summands.size();
5965 variable_list resultsum;
5966 data_expression resultcondition;
5967 action_list resultmultiaction;
5968 data_expression resulttime;
5969
5970 std::vector < variable_list > rename_list_pars;
5971 std::vector < data_expression_list > rename_list_args;
5972 /* rename list is a list of pairs of variable and term lists */
5973 data_expression_list conditionlist;
5974 data_expression binarysumcondition;
5975 int equaluptillnow=1;
5976
5977 std::set<variable> all_sum_variables;
5978 for (const stochastic_action_summand& smmnd: action_summands)
5979 {
5980 const variable_list sumvars=smmnd.summation_variables();
5981 resultsum=merge_var(sumvars,resultsum,rename_list_pars,rename_list_args,conditionlist,parameters);
5982 all_sum_variables.insert(sumvars.begin(),sumvars.end());
5983 }
5984
5985 if (options.binary)
5986 {
5987 resultsum=make_binary_sums(
5988 n,
5989 enumeratedtypes[e.enumeratedtype_index].sortId,
5990 binarysumcondition,
5991 resultsum);
5992 }
5993 else
5994 {
5995 resultsum.push_front(e.var);
5996 }
5997
5998 /* we construct the resulting condition */
5999 data_expression_list auxresult;
6000 std::vector < variable_list >::const_iterator auxrename_list_pars=rename_list_pars.begin();
6001 std::vector < data_expression_list >::const_iterator auxrename_list_args=rename_list_args.begin();
6002
6003 data_expression equalterm;
6004 equaluptillnow=1;
6005 for (const stochastic_action_summand& smmnd: action_summands)
6006 {
6007 const data_expression& condition=smmnd.condition();
6008 assert(auxrename_list_pars!=rename_list_pars.end());
6009 assert(auxrename_list_args!=rename_list_args.end());
6010 const variable_list auxpars= *auxrename_list_pars;
6011 ++auxrename_list_pars;
6012 const data_expression_list auxargs= *auxrename_list_args;
6013 ++auxrename_list_args;
6014 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
6015 data_expression_list::const_iterator j=auxargs.begin();
6016 for (variable_list::const_iterator i=auxpars.begin();
6017 i!=auxpars.end(); ++i, ++j)
6018 {
6019 /* Substitutions are carried out from left to right. The first applicable substitution counts */
6020 if (sigma(*i)==*i) // sigma *i is undefined.
6021 {
6022 sigma[*i]=*j;
6023 }
6024 }
6025 maintain_variables_in_rhs< mutable_map_substitution<> > mutable_sigma(sigma);
6026
6027 const data_expression auxresult1=replace_variables_capture_avoiding_alt(condition,mutable_sigma);
6028 if (equalterm==data_expression()||is_global_variable(equalterm))
6029 {
6030 equalterm=auxresult1;
6031 }
6032 else
6033 {
6034 if (equaluptillnow)
6035 {
6036 equaluptillnow=((auxresult1==equalterm)||is_global_variable(auxresult1));
6037 }
6038 }
6039 auxresult.push_front(auxresult1);
6040 }
6041 if (options.binary)
6042 {
6043 resultcondition=construct_binary_case_tree(n,
6044 resultsum,auxresult,sort_bool::bool_(),e);
6045 resultcondition=lazy::and_(binarysumcondition,resultcondition);
6046 resultcondition=lazy::and_(
6047 construct_binary_case_tree(n,
6048 resultsum,conditionlist,sort_bool::bool_(),e),
6049 resultcondition);
6050 }
6051 else
6052 {
6053 if (equaluptillnow)
6054 {
6055 if (all_equal(conditionlist))
6056 {
6057 resultcondition=lazy::and_(conditionlist.front(), equalterm);
6058 }
6059 else
6060 {
6061 data_expression_list tempconditionlist=conditionlist;
6062 tempconditionlist.push_front(data_expression(e.var));
6063 resultcondition=lazy::and_(
6064 application(
6065 find_case_function(e.enumeratedtype_index,sort_bool::bool_()),
6066 tempconditionlist),
6067 equalterm);
6068 }
6069 }
6070 else
6071 {
6072 data_expression_list tempauxresult=auxresult;
6073 tempauxresult.push_front(data_expression(e.var));
6074 resultcondition=application(
6075 find_case_function(e.enumeratedtype_index,sort_bool::bool_()),
6076 tempauxresult);
6077 if (all_equal(conditionlist))
6078 {
6079 resultcondition=lazy::and_(conditionlist.front(),resultcondition);
6080 }
6081 else
6082 {
6083 data_expression_list tempconditionlist=conditionlist;
6084 tempconditionlist.push_front(data_expression(e.var));
6085 resultcondition=lazy::and_(
6086 application(
6087 find_case_function(e.enumeratedtype_index,sort_bool::bool_()),
6088 tempconditionlist),
6089 resultcondition);
6090 }
6091 }
6092 }
6093
6094 /* now we construct the arguments of the action */
6095 /* First we collect all multi-actions in a separate vector
6096 of multiactions */
6097 std::vector < action_list > multiActionList;
6098
6099 for (const stochastic_action_summand& smmnd: action_summands)
6100 {
6101 multiActionList.push_back(smmnd.multi_action().actions());
6102 }
6103
6104 action_list resultmultiactionlist;
6105 std::size_t multiactioncount= multiActionList[0].size(); // The number of multi actions.
6106 for (; multiactioncount>0 ; multiactioncount--)
6107 {
6108 data_expression_list resultf;
6109 // fcnt is the arity of the action with index multiactioncount-1;
6110 action_list::const_iterator a=multiActionList[0].begin();
6111 for (std::size_t i=1 ; i<multiactioncount ; ++i,++a) {}
6112 std::size_t fcnt=(a->arguments()).size();
6113 data_expression f;
6114
6115 for (; fcnt>0 ; fcnt--)
6116 {
6117 data_expression_list auxresult;
6118 data_expression equalterm;
6119 bool equaluptillnow=true;
6120 std::vector < variable_list > ::const_iterator auxrename_list_pars=rename_list_pars.begin();
6121 std::vector < data_expression_list >::const_iterator auxrename_list_args=rename_list_args.begin();
6122 std::vector<action_list>::const_iterator multiactionwalker=multiActionList.begin();
6123 for (stochastic_action_summand_vector::const_iterator walker=action_summands.begin(); walker!=action_summands.end();
6124 ++walker,++multiactionwalker)
6125 {
6126 assert(auxrename_list_pars!=rename_list_pars.end());
6127 assert(auxrename_list_args!=rename_list_args.end());
6128 const variable_list auxpars= *auxrename_list_pars;
6129 ++auxrename_list_pars;
6130 const data_expression_list auxargs= *auxrename_list_args;
6131 ++auxrename_list_args;
6132 // f is the fcnt-th argument of the multiactioncount-th action in the list
6133 action_list::const_iterator a1=multiactionwalker->begin();
6134 for (std::size_t i=1; i<multiactioncount; ++i, ++a1) {};
6135 data_expression_list::const_iterator d1=(a1->arguments()).begin();
6136 for (std::size_t i=1; i<fcnt; ++i, ++d1) {};
6137 f= *d1;
6138 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
6139 data_expression_list::const_iterator j=auxargs.begin();
6140 for (variable_list::const_iterator i=auxpars.begin();
6141 i!=auxpars.end(); ++i, ++j)
6142 {
6143 /* Substitutions are carried out from left to right. The first applicable substitution counts */
6144 if (sigma(*i)==*i) // *i is not assigned in sigma.
6145 {
6146 sigma[*i]=*j;
6147 }
6148 }
6149
6150 maintain_variables_in_rhs< mutable_map_substitution<> > mutable_sigma(sigma);
6151 const data_expression auxresult1=replace_variables_capture_avoiding_alt(f,mutable_sigma);
6152
6153 if (equalterm==data_expression()||is_global_variable(equalterm))
6154 {
6155 equalterm=auxresult1;
6156 }
6157 else
6158 {
6159 if (equaluptillnow)
6160 {
6161 equaluptillnow=((equalterm==auxresult1)||is_global_variable(auxresult1));
6162 }
6163 }
6164 auxresult.push_front(auxresult1);
6165 }
6166 if (equaluptillnow)
6167 {
6168 resultf.push_front(equalterm);
6169 }
6170 else
6171 {
6172 if (!options.binary)
6173 {
6174 data_expression_list tempauxresult=auxresult;
6175 tempauxresult.push_front(data_expression(e.var));
6176 resultf.push_front(data_expression(application(
6177 find_case_function(e.enumeratedtype_index,f.sort()),
6178 tempauxresult)));
6179 }
6180 else
6181 {
6182 data_expression temp=construct_binary_case_tree(
6183 n,
6184 resultsum,
6185 auxresult,
6186 f.sort(),
6187 e);
6188 resultf.push_front(temp);
6189 }
6190 }
6191 }
6192 a=multiActionList[0].begin();
6193 for (std::size_t i=1 ; i<multiactioncount ; ++i,++a) {}
6194 resultmultiactionlist.push_front(action(a->label(),resultf));
6195 }
6196
6197 /* Construct resulttime, the time of the action ... */
6198
6199 equaluptillnow=true;
6200 equalterm=data_expression();
6201 bool some_summand_has_time=false;
6202 bool all_summands_have_time=true;
6203
6204 // first find out whether there is a summand with explicit time.
6205 for (const stochastic_action_summand& smmnd: action_summands)
6206 {
6207 if (smmnd.has_time())
6208 {
6209 some_summand_has_time=true;
6210 }
6211 else
6212 {
6213 all_summands_have_time=false;
6214 }
6215 }
6216
6217 if (some_summand_has_time)
6218 {
6219 variable dummy_time_variable;
6220 if (!all_summands_have_time)
6221 {
6222 // Generate a fresh dummy variable, and add it to the summand variables
6223 dummy_time_variable=get_fresh_variable("dt",sort_real::real_());
6224 resultsum.push_front(dummy_time_variable);
6225 }
6226 std::vector < variable_list >::const_iterator auxrename_list_pars=rename_list_pars.begin();
6227 std::vector < data_expression_list >::const_iterator auxrename_list_args=rename_list_args.begin();
6228 data_expression_list auxresult;
6229 for (const stochastic_action_summand& smmnd: action_summands)
6230 {
6231 if (smmnd.has_time())
6232 {
6233 const data_expression& actiontime=smmnd.multi_action().time();
6234
6235 assert(auxrename_list_pars!=rename_list_pars.end());
6236 assert(auxrename_list_args!=rename_list_args.end());
6237 const variable_list auxpars= *auxrename_list_pars;
6238 ++auxrename_list_pars;
6239 const data_expression_list auxargs= *auxrename_list_args;
6240 ++auxrename_list_args;
6241
6242 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
6243 data_expression_list::const_iterator j=auxargs.begin();
6244 for (variable_list::const_iterator i=auxpars.begin();
6245 i!=auxpars.end(); ++i, ++j)
6246 {
6247 /* Substitutions are carried out from left to right. The first applicable substitution counts */
6248 if (sigma(*i)==*i) // sigma is not defined for *i.
6249 {
6250 sigma[*i]=*j;
6251 }
6252 }
6253
6254 maintain_variables_in_rhs< mutable_map_substitution<> > mutable_sigma(sigma);
6255 const data_expression auxresult1=replace_variables_capture_avoiding_alt(actiontime, mutable_sigma);
6256 if (equalterm==data_expression()||is_global_variable(equalterm))
6257 {
6258 equalterm=auxresult1;
6259 }
6260 else
6261 {
6262 if (equaluptillnow)
6263 {
6264 equaluptillnow=((auxresult1==equalterm)||is_global_variable(auxresult1));
6265 }
6266 }
6267 auxresult.push_front(auxresult1);
6268 }
6269 else
6270 {
6271 // this summand does not have time. But some summands have.
6272 auxresult.push_front(data_expression(dummy_time_variable));
6273 equaluptillnow=false;
6274 }
6275 }
6276 if (options.binary==1)
6277 {
6278 resulttime=construct_binary_case_tree(n,
6279 resultsum,auxresult,sort_real::real_(),e);
6280 }
6281 else
6282 {
6283 if (equaluptillnow)
6284 {
6285 resulttime=equalterm;
6286 }
6287 else
6288 {
6289 data_expression_list tempauxresult=auxresult;
6290 tempauxresult.push_front(data_expression(e.var));
6291 resulttime=application(
6292 find_case_function(e.enumeratedtype_index,sort_real::real_()),
6293 tempauxresult);
6294 }
6295 }
6296 }
6297
6298 /* we construct the resulting distribution --------------------------------------------------------- */
6299
6300
6301 variable_list resulting_stochastic_variables;
6302 std::vector < variable_list > stochastic_rename_list_pars;
6303 std::vector < data_expression_list > stochastic_rename_list_args;
6304 data_expression resulting_distribution=sort_real::real_one();
6305 {
6306 data_expression_list stochastic_conditionlist;
6307 for (const stochastic_action_summand& smmnd: action_summands)
6308 {
6309 const variable_list stochastic_vars=smmnd.distribution().variables();
6310 resulting_stochastic_variables=merge_var(stochastic_vars,resulting_stochastic_variables,
6311 stochastic_rename_list_pars,stochastic_rename_list_args,stochastic_conditionlist,
6312 parameters + variable_list(all_sum_variables.begin(),all_sum_variables.end()));
6313 }
6314
6315 std::vector < variable_list >::const_iterator auxrename_list_pars=rename_list_pars.begin();
6316 std::vector < data_expression_list >::const_iterator auxrename_list_args=rename_list_args.begin();
6317
6318 std::vector < variable_list >::const_iterator stochastic_auxrename_list_pars=stochastic_rename_list_pars.begin();
6319 std::vector < data_expression_list >::const_iterator stochastic_auxrename_list_args=stochastic_rename_list_args.begin();
6320
6321 data_expression_list aux_result;
6322 data_expression equalterm;
6323 equaluptillnow=1;
6324 for (const stochastic_action_summand& smmnd: action_summands)
6325 {
6326 const data_expression& dist=smmnd.distribution().distribution();
6327 const variable_list stochastic_variables=smmnd.distribution().variables();
6328 assert(auxrename_list_pars!=rename_list_pars.end());
6329 assert(auxrename_list_args!=rename_list_args.end());
6330 assert(stochastic_auxrename_list_pars!=stochastic_rename_list_pars.end());
6331 assert(stochastic_auxrename_list_args!=stochastic_rename_list_args.end());
6332
6333 const variable_list auxpars= *auxrename_list_pars;
6334 ++auxrename_list_pars;
6335 const data_expression_list auxargs= *auxrename_list_args;
6336 ++auxrename_list_args;
6337 const variable_list stochastic_auxpars= *stochastic_auxrename_list_pars;
6338 ++stochastic_auxrename_list_pars;
6339 const data_expression_list stochastic_auxargs= *stochastic_auxrename_list_args;
6340 ++stochastic_auxrename_list_args;
6341 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
6342 data_expression_list::const_iterator j=stochastic_auxargs.begin();
6343 for (variable_list::const_iterator i=stochastic_auxpars.begin();
6344 i!=stochastic_auxpars.end(); ++i, ++j)
6345 {
6346 /* Substitutions are carried out from left to right. The first applicable substitution counts */
6347 if (sigma(*i)==*i) // sigma is not defined for *i.
6348 {
6349 sigma[*i]=*j;
6350 }
6351 }
6352 j=auxargs.begin();
6353 for (variable_list::const_iterator i=auxpars.begin();
6354 i!=auxpars.end(); ++i, ++j)
6355 {
6356 /* Substitutions are carried out from left to right. The first applicable substitution counts */
6357 if (sigma(*i)==*i) // sigma is not defined for *i.
6358 {
6359 sigma[*i]=*j;
6360 }
6361 }
6362 maintain_variables_in_rhs< mutable_map_substitution<> > mutable_sigma(sigma);
6363
6364 const data_expression auxresult1=replace_variables_capture_avoiding_alt(dist,mutable_sigma);
6365 if (equalterm==data_expression()||is_global_variable(equalterm))
6366 {
6367 equalterm=auxresult1;
6368 }
6369 else
6370 {
6371 if (equaluptillnow)
6372 {
6373 equaluptillnow=((auxresult1==equalterm)||is_global_variable(auxresult1));
6374 }
6375 }
6376 aux_result.push_front(auxresult1);
6377 }
6378 if (options.binary)
6379 {
6380 resulting_distribution=construct_binary_case_tree(n,
6381 resultsum,aux_result,sort_real::real_(),e);
6382 resulting_distribution=lazy::and_(
6383 construct_binary_case_tree(n,
6384 resultsum,stochastic_conditionlist,sort_bool::bool_(),e),
6385 resulting_distribution);
6386 }
6387 else
6388 {
6389 if (equaluptillnow)
6390 {
6391 if (all_equal(stochastic_conditionlist))
6392 {
6393 if (stochastic_conditionlist.front()==sort_bool::true_())
6394 {
6395 resulting_distribution=equalterm;
6396 }
6397 else
6398 {
6399 resulting_distribution=real_times_optimized(
6400 if_(stochastic_conditionlist.front(),sort_real::real_one(),sort_real::real_zero()),
6401 equalterm);
6402 }
6403 }
6404 else
6405 {
6406 data_expression_list temp_stochastic_conditionlist=stochastic_conditionlist;
6407 temp_stochastic_conditionlist.push_front(data_expression(e.var));
6408 resulting_distribution=real_times_optimized(
6409 if_(application(
6410 find_case_function(e.enumeratedtype_index,sort_bool::bool_()),
6411 temp_stochastic_conditionlist),
6412 sort_real::real_one(),
6413 sort_real::real_zero()),
6414 equalterm);
6415 }
6416 }
6417 else
6418 {
6419 data_expression_list tempauxresult=aux_result;
6420 tempauxresult.push_front(data_expression(e.var));
6421 resulting_distribution=application(
6422 find_case_function(e.enumeratedtype_index,sort_real::real_()),
6423 tempauxresult);
6424 if (all_equal(stochastic_conditionlist))
6425 {
6426 if (stochastic_conditionlist.front()==sort_bool::true_())
6427 {
6428 resulting_distribution=real_times_optimized(
6429 if_(stochastic_conditionlist.front(),sort_real::real_one(),sort_real::real_zero()),
6430 resulting_distribution);
6431 }
6432 else
6433 {
6434 resulting_distribution=real_times_optimized(
6435 if_(stochastic_conditionlist.front(),sort_real::real_one(),sort_real::real_zero()),
6436 resulting_distribution);
6437 }
6438 }
6439 else
6440 {
6441 data_expression_list temp_stochastic_conditionlist=stochastic_conditionlist;
6442 temp_stochastic_conditionlist.push_front(data_expression(e.var));
6443 resulting_distribution=real_times_optimized(
6444 if_(application(
6445 find_case_function(e.enumeratedtype_index,sort_bool::bool_()),
6446 temp_stochastic_conditionlist),
6447 sort_real::real_one(),
6448 sort_real::real_zero()),
6449 resulting_distribution);
6450 }
6451 }
6452 }
6453 }
6454 /* now we construct the arguments of the invoked -----------------------------------------------------
6455 process, i.e. the new function g */
6456 data_expression_list resultnextstate;
6457
6458 for (const variable& var: parameters)
6459 {
6460 equalterm=data_expression();
6461 equaluptillnow=1;
6462 std::vector < variable_list >::const_iterator auxrename_list_pars=rename_list_pars.begin();
6463 std::vector < data_expression_list >::const_iterator auxrename_list_args=rename_list_args.begin();
6464 std::vector < variable_list >::const_iterator stochastic_auxrename_list_pars=stochastic_rename_list_pars.begin();
6465 std::vector < data_expression_list >::const_iterator stochastic_auxrename_list_args=stochastic_rename_list_args.begin();
6466 data_expression_list auxresult;
6467 for (const stochastic_action_summand& smmnd: action_summands)
6468 {
6469 const assignment_list nextstate=smmnd.assignments();
6470 assert(auxrename_list_pars!=rename_list_pars.end());
6471 assert(auxrename_list_args!=rename_list_args.end());
6472 assert(stochastic_auxrename_list_pars!=stochastic_rename_list_pars.end());
6473 assert(stochastic_auxrename_list_args!=stochastic_rename_list_args.end());
6474 const variable_list auxpars= *auxrename_list_pars;
6475 ++auxrename_list_pars;
6476 const data_expression_list auxargs= *auxrename_list_args;
6477 ++auxrename_list_args;
6478 const variable_list stochastic_auxpars= *stochastic_auxrename_list_pars;
6479 ++stochastic_auxrename_list_pars;
6480 const data_expression_list stochastic_auxargs= *stochastic_auxrename_list_args;
6481 ++stochastic_auxrename_list_args;
6482
6483 data_expression nextstateparameter;
6484 nextstateparameter=getRHSassignment(var,nextstate);
6485
6486 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
6487 data_expression_list::const_iterator j=stochastic_auxargs.begin();
6488 for (variable_list::const_iterator i=stochastic_auxpars.begin();
6489 i!=stochastic_auxpars.end(); ++i, ++j)
6490 {
6491 /* Substitutions are carried out from left to right. The first applicable substitution counts */
6492 if (sigma(*i)==*i) // sigma is not defined for *i.
6493 {
6494 sigma[*i]=*j;
6495 }
6496 }
6497 j=auxargs.begin();
6498 for (variable_list::const_iterator i=auxpars.begin();
6499 i!=auxpars.end(); ++i, ++j)
6500 {
6501 /* Substitutions are carried out from left to right. The first applicable substitution counts */
6502 if (sigma(*i)==*i) // sigma is not defined for *i.
6503 {
6504 sigma[*i]=*j;
6505 }
6506 }
6507
6508 maintain_variables_in_rhs< mutable_map_substitution<> > mutable_sigma(sigma);
6509 data_expression auxresult1=replace_variables_capture_avoiding_alt(nextstateparameter, mutable_sigma);
6510 if (equalterm==data_expression()) // ||is_global_variable(equalterm)) Adding this last part leads to smaller case functions,
6511 // but bigger and less structured state spaces, as parameters are less often put to default
6512 // values. Constant elimination can less often be applied as constant eliminiation does not
6513 // have subtle knowledge of case functions.
6514 {
6515 equalterm=auxresult1;
6516 }
6517 else if (equaluptillnow)
6518 {
6519 // set equaluptillnow if the arguments of this case function are all equal,
6520 // or are all equal to global variables. Setting a case function
6521 // C(e,v,dc1) to the value v, where dc1 is a global variable can result in
6522 // the growth of the state space, as dc1 is not set to a default value, but
6523 // keeps the value v.
6524 equaluptillnow=((equalterm==auxresult1)||
6525 ((equalterm==data_expression()||is_global_variable(equalterm)) &&
6526 is_global_variable(auxresult1)));
6527 }
6528
6529 auxresult.push_front(auxresult1);
6530 }
6531 if (equaluptillnow)
6532 {
6533 resultnextstate.push_front(equalterm);
6534 }
6535 else
6536 {
6537 if (!options.binary)
6538 {
6539 data_expression_list tempauxresult=auxresult;
6540 tempauxresult.push_front(data_expression(e.var));
6541 resultnextstate.push_front(
6542 data_expression(application(
6543 find_case_function(
6544 e.enumeratedtype_index,
6545 var.sort()),
6546 tempauxresult)));
6547 }
6548 else
6549 {
6550 data_expression temp=construct_binary_case_tree(
6551 n,
6552 resultsum,
6553 auxresult,
6554 var.sort(),
6555 e);
6556 resultnextstate.push_front(temp);
6557 }
6558 }
6559 }
6560 /* Now turn *resultg around */
6561 resultnextstate=reverse(resultnextstate);
6562 /* The list of arguments in nextstate are now in a sequential form, and
6563 must be transformed back to a list of assignments */
6564 const assignment_list final_resultnextstate=make_optimised_assignment_list(parameters,resultnextstate,resultsum,resulting_stochastic_variables);
6565 return stochastic_action_summand(
6566 resultsum,
6567 resultcondition,
6568 some_summand_has_time?multi_action(resultmultiactionlist,resulttime):multi_action(resultmultiactionlist),
6569 final_resultnextstate,
6570 stochastic_distribution(resulting_stochastic_variables,resulting_distribution));
6571 }
6572
6574 const enumtype& e,
6575 const deadlock_summand_vector& deadlock_summands,
6576 const variable_list& parameters)
6577 {
6578 /* This function gets a list of summands, with
6579 the same multiaction and time
6580 status. It yields a single clustered summand
6581 by introducing an auxiliary sum operator, with
6582 a variable of enumtype. In case binary is used,
6583 a sequence of variables are introduced of sort Bool */
6584
6585 std::size_t n=deadlock_summands.size();
6586 variable_list resultsum;
6587 data_expression resultcondition;
6588 action_list resultmultiaction;
6589 data_expression resulttime;
6590
6591 std::vector < variable_list > rename_list_pars;
6592 std::vector < data_expression_list > rename_list_args;
6593 /* rename list is a list of pairs of variable and term lists */
6594 data_expression_list conditionlist;
6595 data_expression binarysumcondition;
6596 int equaluptillnow=1;
6597
6598 for (const deadlock_summand& smmnd: deadlock_summands)
6599 {
6600 const variable_list sumvars=smmnd.summation_variables();
6601 resultsum=merge_var(sumvars,resultsum,rename_list_pars,rename_list_args,conditionlist,parameters);
6602 }
6603
6604 if (options.binary)
6605 {
6606 resultsum=make_binary_sums(
6607 n,
6608 enumeratedtypes[e.enumeratedtype_index].sortId,
6609 binarysumcondition,
6610 resultsum);
6611 }
6612 else
6613 {
6614 resultsum.push_front(e.var);
6615 }
6616
6617 /* we construct the resulting condition */
6618 data_expression_list auxresult;
6619 std::vector < variable_list >::const_iterator auxrename_list_pars=rename_list_pars.begin();
6620 std::vector < data_expression_list >::const_iterator auxrename_list_args=rename_list_args.begin();
6621
6622 data_expression equalterm;
6623 equaluptillnow=1;
6624 for (const deadlock_summand& smmnd: deadlock_summands)
6625 {
6626 const data_expression& condition=smmnd.condition();
6627 assert(auxrename_list_pars!=rename_list_pars.end());
6628 assert(auxrename_list_args!=rename_list_args.end());
6629 const variable_list auxpars= *auxrename_list_pars;
6630 ++auxrename_list_pars;
6631 const data_expression_list auxargs= *auxrename_list_args;
6632 ++auxrename_list_args;
6633 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
6634 data_expression_list::const_iterator j=auxargs.begin();
6635 for (variable_list::const_iterator i=auxpars.begin();
6636 i!=auxpars.end(); ++i, ++j)
6637 {
6638 /* Substitutions are carried out from left to right. The first applicable substitution counts */
6639 if (sigma(*i)== *i) // sigma is not defined for *i.
6640 {
6641 sigma[*i]=*j;
6642 }
6643 }
6644
6645 maintain_variables_in_rhs< mutable_map_substitution<> > mutable_sigma(sigma);
6646 const data_expression auxresult1=replace_variables_capture_avoiding_alt(condition, mutable_sigma);
6647 if (equalterm==data_expression()||is_global_variable(equalterm))
6648 {
6649 equalterm=auxresult1;
6650 }
6651 else
6652 {
6653 if (equaluptillnow)
6654 {
6655 equaluptillnow=((auxresult1==equalterm)||is_global_variable(auxresult1));
6656 }
6657 }
6658 auxresult.push_front(auxresult1);
6659 }
6660 if (options.binary)
6661 {
6662 resultcondition=construct_binary_case_tree(n,
6663 resultsum,auxresult,sort_bool::bool_(),e);
6664 resultcondition=lazy::and_(binarysumcondition,resultcondition);
6665 resultcondition=lazy::and_(
6666 construct_binary_case_tree(n,
6667 resultsum,conditionlist,sort_bool::bool_(),e),
6668 resultcondition);
6669 }
6670 else
6671 {
6672 if (equaluptillnow)
6673 {
6674 if (all_equal(conditionlist))
6675 {
6676 resultcondition=lazy::and_(conditionlist.front(),equalterm);
6677 }
6678 else
6679 {
6680 data_expression_list tempconditionlist=conditionlist;
6681 tempconditionlist.push_front(data_expression(e.var));
6682 resultcondition=lazy::and_(
6683 application(
6684 find_case_function(e.enumeratedtype_index,sort_bool::bool_()),
6685 tempconditionlist),
6686 equalterm);
6687 }
6688 }
6689 else
6690 {
6691 data_expression_list tempauxresult=auxresult;
6692 tempauxresult.push_front(data_expression(e.var));
6693 resultcondition=application(
6694 find_case_function(e.enumeratedtype_index,sort_bool::bool_()),
6695 tempauxresult);
6696 if (all_equal(conditionlist))
6697 {
6698 resultcondition=lazy::and_(conditionlist.front(),resultcondition);
6699 }
6700 else
6701 {
6702 data_expression_list tempconditionlist=conditionlist;
6703 tempconditionlist.push_front(data_expression(e.var));
6704 resultcondition=lazy::and_(
6705 application(
6706 find_case_function(e.enumeratedtype_index,sort_bool::bool_()),
6707 tempconditionlist),
6708 resultcondition);
6709 }
6710 }
6711 }
6712
6713 /* now we construct the arguments of the action */
6714 /* First we collect all multi-actions in a separate vector
6715 of multiactions */
6716 std::vector < action_list > multiActionList;
6717
6718 action_list resultmultiactionlist;
6719
6720 /* Construct resulttime, the time of the action ... */
6721
6722 equaluptillnow=true;
6723 equalterm=data_expression();
6724 bool some_summand_has_time=false;
6725 bool all_summands_have_time=true;
6726
6727 // first find out whether there is a summand with explicit time.
6728 for (const deadlock_summand& smmnd: deadlock_summands)
6729 {
6730 if (smmnd.deadlock().has_time())
6731 {
6732 some_summand_has_time=true;
6733 }
6734 else
6735 {
6736 all_summands_have_time=false;
6737 }
6738 }
6739
6740 if ((some_summand_has_time))
6741 {
6742 variable dummy_time_variable;
6743 if (!all_summands_have_time)
6744 {
6745 // Generate a fresh dummy variable, and add it to the summand variables
6746 dummy_time_variable=get_fresh_variable("dt",sort_real::real_());
6747 resultsum.push_front(dummy_time_variable);
6748 }
6749 std::vector < variable_list >::const_iterator auxrename_list_pars=rename_list_pars.begin();
6750 std::vector < data_expression_list >::const_iterator auxrename_list_args=rename_list_args.begin();
6751 data_expression_list auxresult;
6752 for (const deadlock_summand& smmnd: deadlock_summands)
6753 {
6754 if (smmnd.deadlock().has_time())
6755 {
6756 const data_expression& actiontime=smmnd.deadlock().time();
6757
6758 assert(auxrename_list_pars!=rename_list_pars.end());
6759 assert(auxrename_list_args!=rename_list_args.end());
6760 const variable_list auxpars= *auxrename_list_pars;
6761 ++auxrename_list_pars;
6762 const data_expression_list auxargs= *auxrename_list_args;
6763 ++auxrename_list_args;
6764
6765 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
6766 data_expression_list::const_iterator j=auxargs.begin();
6767 for (variable_list::const_iterator i=auxpars.begin();
6768 i!=auxpars.end(); ++i, ++j)
6769 {
6770 /* Substitutions are carried out from left to right. The first applicable substitution counts */
6771 if (sigma(*i)==*i) // sigma is not defined for *i.
6772 {
6773 sigma[*i]=*j;
6774 }
6775 }
6776
6777 maintain_variables_in_rhs< mutable_map_substitution<> > mutable_sigma(sigma);
6778 const data_expression auxresult1=replace_variables_capture_avoiding_alt(actiontime, mutable_sigma);
6779 if (equalterm==data_expression()||is_global_variable(equalterm))
6780 {
6781 equalterm=auxresult1;
6782 }
6783 else
6784 {
6785 if (equaluptillnow)
6786 {
6787 equaluptillnow=((auxresult1==equalterm)||is_global_variable(auxresult1));
6788 }
6789 }
6790 auxresult.push_front(auxresult1);
6791 }
6792 else
6793 {
6794 // this summand does not have time. But some summands have.
6795 auxresult.push_front(data_expression(dummy_time_variable));
6796 equaluptillnow=false;
6797 }
6798 }
6799 if (options.binary==1)
6800 {
6801 resulttime=construct_binary_case_tree(n,
6802 resultsum,auxresult,sort_real::real_(),e);
6803 }
6804 else
6805 {
6806 if (equaluptillnow)
6807 {
6808 resulttime=equalterm;
6809 }
6810 else
6811 {
6812 data_expression_list tempauxresult=auxresult;
6813 tempauxresult.push_front(data_expression(e.var));
6814 resulttime=application(
6815 find_case_function(e.enumeratedtype_index,sort_real::real_()),
6816 tempauxresult);
6817 }
6818 }
6819 }
6820
6821 return deadlock_summand(resultsum,
6822 resultcondition,
6823 some_summand_has_time?deadlock(resulttime):deadlock());
6824 }
6825
6826 static
6827 sort_expression_list getActionSorts(const action_list& actionlist)
6828 {
6829 sort_expression_list resultsorts;
6830
6831 for (const action& act: actionlist)
6832 {
6833 resultsorts=act.label().sorts()+resultsorts;
6834 }
6835 return resultsorts;
6836 }
6837
6839 stochastic_action_summand_vector& action_summands,
6840 deadlock_summand_vector& deadlock_summands,
6841 const variable_list& pars)
6842 {
6843 {
6844 /* We cluster first the action summands with the action
6845 occurring in the first summand of sums.
6846 These summands are first stored in w1. */
6847
6848 stochastic_action_summand_vector result;
6849 stochastic_action_summand_vector reducible_sumlist=action_summands;
6850
6851 for (const stochastic_action_summand& summand1: action_summands)
6852 {
6853 stochastic_action_summand_vector w1;
6854 stochastic_action_summand_vector w2;
6855 for (const stochastic_action_summand& summand2: reducible_sumlist)
6856 {
6857 if (summandsCanBeClustered(summand1,summand2))
6858 {
6859 w1.push_back(summand2);
6860 }
6861 else
6862 {
6863 w2.push_back(summand2);
6864 }
6865 }
6866 reducible_sumlist.swap(w2);
6867
6868 /* In w1 we now find all the summands labelled with
6869 similar multiactions, actiontime and terminationstatus.
6870 We must now construct its clustered form. */
6871 std::size_t n=w1.size();
6872
6873 if (n>0)
6874 {
6875 if (n>1)
6876 {
6877 const action_list multiaction=w1.front().multi_action().actions();
6878 sort_expression_list actionsorts;
6879 actionsorts=getActionSorts(multiaction);
6880
6881 const enumtype enumeratedtype_(options.binary?2:n,actionsorts,get_sorts(pars),*this);
6882
6883 result.push_back(collect_sum_arg_arg_cond(enumeratedtype_,w1,pars));
6884 }
6885 else
6886 {
6887 for(const stochastic_action_summand& summand: result)
6888 {
6889 w1.push_back(summand);
6890 }
6891 w1.swap(result);
6892 }
6893 }
6894 }
6895 action_summands=result;
6896 }
6897
6898 // Now the delta summands are clustered.
6899 deadlock_summand_vector result;
6900 deadlock_summand_vector reducible_sumlist=deadlock_summands;
6901
6902 for (const deadlock_summand& summand1: deadlock_summands)
6903 {
6904 deadlock_summand_vector w1;
6905 deadlock_summand_vector w2;
6906 for (const deadlock_summand& summand2: reducible_sumlist)
6907 {
6908 if (summand1.deadlock().has_time()==summand2.deadlock().has_time())
6909 {
6910 w1.push_back(summand2);
6911 }
6912 else
6913 {
6914 w2.push_back(summand2);
6915 }
6916 }
6917 reducible_sumlist.swap(w2);
6918
6919 /* In w1 we now find all the summands labelled with
6920 either no or an action time.
6921 We must now construct its clustered form. */
6922 const std::size_t n=w1.size();
6923
6924 if (n>0)
6925 {
6926 if (n>1)
6927 {
6928 sort_expression_list actionsorts;
6929
6930 const enumtype enumeratedtype_(options.binary?2:n,actionsorts,get_sorts(pars),*this);
6931
6932 result.push_back(collect_sum_arg_arg_cond(enumeratedtype_,w1,pars));
6933 }
6934 else
6935 {
6936 assert(w1.size()==1);
6937 result.push_back(w1.front());
6938 }
6939 }
6940 }
6941 assert(reducible_sumlist.empty());
6942 deadlock_summands.swap(result);
6943 }
6944
6945
6946 /**************** GENERaTE LPEpCRL **********************************/
6947
6948
6949 /* The variable regular indicates that we are interested in generating
6950 a LPE assuming the pCRL term under consideration is regular */
6951
6952 void generateLPEpCRL(stochastic_action_summand_vector& action_summands,
6953 deadlock_summand_vector& deadlock_summands,
6954 const process_identifier& procId,
6955 const bool containstime,
6956 const bool regular,
6957 variable_list& parameters,
6958 data_expression_list& init,
6959 stochastic_distribution& initial_stochastic_distribution)
6960 /* A pair of initial state and linear process must be extracted
6961 from the underlying GNF */
6962 {
6963 // We use action_summands and deadlock_summands as an output.
6964 assert(action_summands.empty());
6965 assert(deadlock_summands.empty());
6966
6967 bool singlecontrolstate=false;
6968 std::set< process_identifier > reachable_process_identifiers;
6969 make_pCRL_procs(procId, reachable_process_identifiers);
6970
6971 /* now reachable process identifiers contains a list of all process id's in this pCRL process.
6972 Some of these processes have equal parameter lists and right hand sides. A typical example is
6973 P1 = a.P2, P3=a.P4, P2=b.Q, P4=b.Q. This reduces to P1 = a.P2, P2 = b.Q.
6974 We reduce the set of process_identifiers in reachable_process_identifiers and perform the
6975 appropriate substitution in the right hand side.
6976 The result is a map from process_identifiers to adapted process bodies.
6977 */
6978 const std::set< process_identifier > reduced_set_of_process_identifiers =
6979 minimize_set_of_reachable_process_identifiers(reachable_process_identifiers,procId);
6980
6981 /* The equations can still contain stochastic operators that occur before the first action.
6982 We translate the equations such that the stochastic operators do not occur in front anymore.
6983 Moving the stochastic operators to the front means that the number of parameters of each
6984 process can increase. In this case we introduce a new process identifiers. The resulting
6985 ids of processes occur in stochastic_normalized_process_identifiers. */
6986
6987 process_identifier initial_proc_id=procId; // the initial process id may be renamed.
6988 std::set< process_identifier > stochastic_normalized_process_identifiers =
6989 remove_stochastic_operators_from_front(reduced_set_of_process_identifiers, initial_proc_id, initial_stochastic_distribution);
6990
6991 /* collect the parameters, but assume that variables
6992 have a unique sort */
6993 if (stochastic_normalized_process_identifiers.size()==1)
6994 {
6995 singlecontrolstate=true;
6996 }
6997 parameters=collectparameterlist(stochastic_normalized_process_identifiers);
6998
6999 if ((!regular)||((!singlecontrolstate) && (options.newstate) && (!options.binary)))
7000 {
7001 declare_control_state(stochastic_normalized_process_identifiers);
7002 }
7003 stacklisttype stack(parameters,*this,regular,stochastic_normalized_process_identifiers,singlecontrolstate);
7004
7005 if (regular)
7006 {
7007 if ((options.binary) && (options.newstate))
7008 {
7009 parameters=stack.parameters;
7010 if (!singlecontrolstate)
7011 {
7012 parameters=reverse(stack.booleanStateVariables) + parameters;
7013 }
7014 }
7015 else /* !binary or oldstate */
7016 {
7017 variable_list tempparameters=stack.parameters;
7018 tempparameters.push_front(stack.stackvar);
7019 parameters=
7020 ((!singlecontrolstate)?tempparameters:stack.parameters);
7021 }
7022 }
7023 else /* not regular, use a stack */
7024 {
7025 parameters = variable_list({ stack.stackvar });
7026 }
7027 init=make_initialstate(initial_proc_id,stack,stochastic_normalized_process_identifiers,regular,singlecontrolstate,initial_stochastic_distribution);
7028 assert(init.size()==parameters.size());
7029 collectsumlist(action_summands,deadlock_summands,stochastic_normalized_process_identifiers,parameters,stack,regular,singlecontrolstate);
7030
7031 if (!options.no_intermediate_cluster)
7032 {
7033 cluster_actions(action_summands,deadlock_summands,parameters);
7034 }
7035
7036 if ((!containstime) || options.ignore_time)
7037 {
7038 /* We add a delta summand to each process, if the flag
7039 ignore_time is set, or if the process does not contain any
7040 explicit reference to time. This affects the behaviour of each
7041 process in the sense that each process can idle
7042 indefinitely. It has the advantage that large numbers
7043 numbers of timed delta summands are subsumed by
7044 this delta. As the removal of timed delta's
7045 is time consuming in the linearisation, the use
7046 of this flag, can speed up linearisation considerably */
7047 deadlock_summands.emplace_back(variable_list(), sort_bool::true_(), deadlock());
7048 }
7049 }
7050
7051
7052
7053 /**************** equalargs ****************************************/
7054
7055 static
7056 bool occursinvarandremove(const variable& var, variable_list& vl)
7057 {
7058 bool result=false;
7059
7060 if (vl.empty())
7061 {
7062 return false;
7063 }
7064 vl.pop_front();
7065 const variable var1=vl.front();
7066 if (var==var1)
7067 {
7068 return true;
7069 }
7070
7071 // Names of variables cannot be the same, even if they have different types.
7072 if (var.name()==var1.name())
7073 {
7074 throw mcrl2::runtime_error("variable conflict " + data::pp(var) + ":" + data::pp(var.sort()) + " versus " +
7075 data::pp(var1) + ":" + data::pp(var1.sort()) + ".");
7076 }
7077
7078 result=occursinvarandremove(var,vl);
7079 vl.push_front(var1);
7080 return result;
7081 }
7082
7083 /********************** construct renaming **************************/
7084
7085 variable_list construct_renaming(
7086 const variable_list& pars1,
7087 const variable_list& pars2,
7088 variable_list& pars3,
7089 variable_list& pars4,
7090 const bool unique=true)
7091 {
7092 /* check whether the variables in pars2 are unique,
7093 wrt to those in pars1, and deliver:
7094 - in pars3 a list of renamed parameters pars2, such that
7095 pars3 is unique with respect to pars1;
7096 - in pars4 a list of parameters that need to be renamed;
7097 - as a return result, new values for the parameters in pars4.
7098 This allows using substitute_data(list) to rename
7099 action and process arguments and conditions to adapt
7100 to the new parameter names.
7101 The variable unique indicates whether the generated variables
7102 are unique, and not occurring elsewhere. If unique is false,
7103 it is attempted to reuse previously generated variable names,
7104 as long as they do not occur in pars1. The default value for
7105 unique is true.
7106 */
7107
7108 variable_list t;
7109 variable_list t1;
7110 variable_list t2;
7111
7112 if (pars2.empty())
7113 {
7114 pars3=variable_list();
7115 pars4=variable_list();
7116 }
7117 else
7118 {
7119 const variable& var2=pars2.front();
7120 variable var3=var2;
7121 for (std::size_t i=0 ; occursin(var3,pars1)||occursin(var3,pars2) ; ++i)
7122 {
7123 var3=get_fresh_variable(var2.name(),var2.sort(),(unique?-1:static_cast<int>(i)));
7124 }
7125 if (var3!=var2)
7126 {
7127 t1=construct_renaming(pars1,pars2.tail(),t,t2,unique);
7128 t1.push_front(var3);
7129
7130 pars4=t2;
7131 pars4.push_front(var2);
7132 pars3=t;
7133 pars3.push_front(var3);
7134 }
7135 else
7136 {
7137 t1=construct_renaming(pars1,pars2.tail(),t,pars4,unique);
7138 pars3=t;
7139 pars3.push_front(var2);
7140 }
7141
7142 }
7143 return t1;
7144 }
7145
7146 /**************** communication operator composition ****************/
7147
7148 template <typename List>
7149 static sort_expression_list get_sorts(const List& l)
7150 {
7151 return sort_expression_list(l.begin(), l.end(), [](const typename List::value_type& d) -> sort_expression {return d.sort();});
7152 }
7153
7154 static
7156 const variable_list& sumvars,
7157 const data_expression& actiontime,
7158 const data_expression& condition)
7159 {
7160 /* Check whether actiontime is an expression
7161 of the form t1 +...+ tn, where one of the
7162 ti is a variable in sumvars that does not occur in condition */
7163
7164 if (is_variable(actiontime))
7165 {
7166 const variable& t = atermpp::down_cast<variable>(actiontime);
7168 {
7169 return true;
7170 }
7171 }
7172
7174 {
7175 return (check_real_variable_occurrence(sumvars,data::binary_left(application(actiontime)),condition) ||
7176 check_real_variable_occurrence(sumvars,data::binary_right(application(actiontime)),condition));
7177 }
7178
7179 return false;
7180 }
7181
7183 const variable_list& sumvars,
7184 const variable_list& freevars,
7185 const data_expression& condition,
7186 const bool has_time,
7187 const variable& timevariable,
7188 const data_expression& actiontime,
7189 variable_list& used_sumvars)
7190 {
7191 /* Generate a condition of the form:
7192
7193 exists sumvars. condition && timevariable<actiontime
7194
7195 where the sumvars are added to the existentially quantified
7196 variables, and the resulting expression is
7197
7198 condition && timevariable<actiontime
7199
7200 The comments below refer to old code, where an explicit
7201 existential quantor was generated.
7202
7203 OLD:
7204 Currently, the existential quantifier must use an equation,
7205 which represents a higher order function. The existential
7206 quantifier is namely of type exists:sorts1->Bool, where sorts1
7207 are the sorts of the quantified variables.
7208
7209 If the sum variables do not occur in the expression, they
7210 are not quantified.
7211
7212 If the actiontime is of the form t1+t2+...+tn where one
7213 of the ti is a quantified real variable in sumvars, and this
7214 variable does not occur in the condition, then the expression
7215 of the form timevariable < actiontime is omitted.
7216 */
7217
7218 assert(used_sumvars.empty());
7219 data_expression result;
7220 variable_list variables;
7221 if (!has_time || (check_real_variable_occurrence(sumvars,actiontime,condition)))
7222 {
7223 result=condition;
7224 }
7225 else
7226 {
7227 result=RewriteTerm(
7229 condition,
7230 data::less(timevariable,actiontime)));
7231 variables.push_front(timevariable);
7232 }
7233 for (const variable& v: freevars)
7234 {
7235 if (occursinterm(result, v))
7236 {
7237 variables.push_front(v);
7238 }
7239 }
7240
7241 for (const variable& v: global_variables)
7242 {
7243 if (occursinterm(result, v))
7244 {
7245 variables.push_front(v);
7246 }
7247 }
7248
7249 for (const variable& v: sumvars)
7250 {
7251 if (occursinterm(result, v))
7252 {
7253 used_sumvars.push_front(v);
7254 }
7255 }
7256 used_sumvars = reverse(used_sumvars);
7257
7258 return result;
7259 }
7260
7262 const stochastic_action_summand_vector& action_summands,
7263 const deadlock_summand_vector& deadlock_summands,
7264 const variable_list& freevars)
7265 {
7266 /* First walk through the summands to see whether
7267 a summand with condition true that does not refer
7268 to time exists. In that case the ultimate delay
7269 condition is true */
7270
7271 variable time_variable=get_fresh_variable("timevar",sort_real::real_());
7272 for (const deadlock_summand& summand: deadlock_summands)
7273 {
7274 if ((!summand.deadlock().has_time()) && (summand.condition()==sort_bool::true_()))
7275 {
7276 return lps::detail::ultimate_delay(time_variable);
7277 }
7278 }
7279
7280 for (const stochastic_action_summand& summand: action_summands)
7281 {
7282 if ((!summand.multi_action().has_time()) && (summand.condition()==sort_bool::true_()))
7283 {
7284 return lps::detail::ultimate_delay(time_variable);
7285 }
7286 }
7287
7288 /* Unfortunately, no ultimate delay condition true can
7289 be generated. So, we must now traverse all conditions
7290 to generate a non trivial ultimate delay condition */
7291
7292 data_expression_list results;
7293 data_expression_list condition_list;
7294 std::vector < variable_list> renamings_pars;
7295 std::vector < data_expression_list> renamings_args;
7296 variable_list existentially_quantified_variables;
7297 for (const deadlock_summand& s: deadlock_summands)
7298 {
7299 variable_list new_existentially_quantified_variables;
7300 const data_expression ult_del_condition=
7301 makesingleultimatedelaycondition(
7302 s.summation_variables(),
7303 freevars,
7304 s.condition(),
7305 s.deadlock().has_time(),
7306 time_variable,
7307 s.deadlock().time(),
7308 new_existentially_quantified_variables);
7309 existentially_quantified_variables=merge_var(
7310 new_existentially_quantified_variables,
7311 existentially_quantified_variables,
7312 renamings_pars,
7313 renamings_args,
7314 condition_list,
7315 variable_list());
7316 results.push_front(ult_del_condition);
7317 }
7318
7319 for (const stochastic_action_summand& s: action_summands)
7320 {
7321 variable_list new_existentially_quantified_variables;
7322 const data_expression ult_del_condition=
7323 makesingleultimatedelaycondition(
7324 s.summation_variables(),
7325 freevars,
7326 s.condition(),
7327 s.multi_action().has_time(),
7328 time_variable,
7329 s.multi_action().time(),
7330 new_existentially_quantified_variables);
7331 existentially_quantified_variables=merge_var(
7332 new_existentially_quantified_variables,
7333 existentially_quantified_variables,
7334 renamings_pars,
7335 renamings_args,
7336 condition_list,
7337 variable_list());
7338 results.push_front(ult_del_condition);
7339 }
7340
7342
7343 assert(results.size()==condition_list.size());
7344 assert(results.size()==renamings_pars.size());
7345 assert(results.size()==renamings_args.size());
7346
7347 std::vector < variable_list>::const_iterator renamings_par=renamings_pars.begin();
7348 std::vector < data_expression_list>::const_iterator renamings_arg=renamings_args.begin();
7349 condition_list=reverse(condition_list);
7350 results=reverse(results);
7351 data_expression_list::const_iterator j=condition_list.begin();
7352 for(data_expression_list::const_iterator i=results.begin();
7353 i!=results.end(); ++i,++j,++renamings_par,++renamings_arg)
7354 {
7355 const variable_list& auxpars=*renamings_par;
7356 const data_expression_list& auxargs=*renamings_arg;
7357
7358 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
7359 data_expression_list::const_iterator j1=auxargs.begin();
7360 for (variable_list::const_iterator i1=auxpars.begin();
7361 i1!=auxpars.end(); ++i1, ++j1)
7362 {
7363 /* Substitutions are carried out from left to right. The first applicable substitution counts */
7364 if (sigma(*i1)==*i1)
7365 {
7366 sigma[*i1]=*j1;
7367
7368 }
7369 }
7370
7371 maintain_variables_in_rhs< mutable_map_substitution<> > mutable_sigma(sigma);
7372 result=lazy::or_(result,replace_variables_capture_avoiding_alt(lazy::and_(*i,*j), mutable_sigma));
7373 }
7374 return lps::detail::ultimate_delay(time_variable, existentially_quantified_variables, RewriteTerm(result));
7375 }
7376
7377
7378 /******** make_unique_variables **********************/
7379
7381 const variable_list& var_list,
7382 const std::string& hint)
7383 {
7384 /* This function generates a list of variables with the same sorts
7385 as in variable_list, where all names are unique */
7386
7387 data::maintain_variables_in_rhs< data::mutable_map_substitution<> > sigma;
7388
7389 for(const variable& var: var_list)
7390 {
7391 const data::variable v =
7392 get_fresh_variable(std::string(var.name()) + ((hint.empty())?"":"_") + hint, var.sort());
7393 sigma[var] = v;
7394 }
7395 return sigma;
7396 }
7397
7398 /******** make_parameters_and_variables_unique **********************/
7399
7401 stochastic_action_summand_vector& action_summands,
7402 deadlock_summand_vector& deadlock_summands,
7403 variable_list& pars,
7404 lps::detail::ultimate_delay& ultimate_delay_condition,
7405 const std::string& hint="")
7406 {
7407 stochastic_action_summand_vector result_action_summands;
7408
7409 data::maintain_variables_in_rhs<data::mutable_map_substitution<> > sigma=make_unique_variables(pars, hint);
7410 const variable_list unique_pars=data::replace_variables(pars, sigma);
7411
7412 if (!options.ignore_time)
7413 {
7414 // Remove variables locally bound in the ultimate_delay_condition
7415 maintain_variables_in_rhs< data::mutable_map_substitution<> > local_sigma=sigma;
7416 for(const variable& v: ultimate_delay_condition.variables())
7417 {
7418 local_sigma[v]=v;
7419 }
7420 ultimate_delay_condition.constraint()=replace_variables_capture_avoiding_alt(
7421 ultimate_delay_condition.constraint(),
7422 local_sigma); // Only substitute the variables in the lhs.
7423 }
7424 for (const stochastic_action_summand& smmnd: action_summands)
7425 {
7426 const variable_list& sumvars=smmnd.summation_variables();
7427 data::maintain_variables_in_rhs<data::mutable_map_substitution<> > sigma_sumvars=make_unique_variables(sumvars,hint);
7428 const variable_list unique_sumvars=data::replace_variables(sumvars, sigma_sumvars);
7429
7430 stochastic_distribution distribution=smmnd.distribution();
7431 const variable_list stochastic_vars=distribution.variables();
7432 data::maintain_variables_in_rhs<data::mutable_map_substitution<> > sigma_stochastic_vars=
7433 make_unique_variables(stochastic_vars,hint);
7434 const variable_list unique_stochastic_vars=data::replace_variables(stochastic_vars, sigma_stochastic_vars);
7435
7436 data_expression condition=smmnd.condition();
7437 action_list multiaction=smmnd.multi_action().actions();
7438 data_expression actiontime=smmnd.multi_action().time();
7439 assignment_list nextstate=smmnd.assignments();
7440
7441 condition=replace_variables_capture_avoiding_alt(condition, sigma_sumvars);
7442 condition=replace_variables_capture_avoiding_alt(condition, sigma);
7443
7444 actiontime=replace_variables_capture_avoiding_alt(actiontime, sigma_sumvars);
7445 actiontime=replace_variables_capture_avoiding_alt(actiontime, sigma);
7446 multiaction=lps::replace_variables_capture_avoiding_with_an_identifier_generator(multiaction, sigma_sumvars, fresh_identifier_generator);
7447 multiaction=lps::replace_variables_capture_avoiding_with_an_identifier_generator(multiaction, sigma, fresh_identifier_generator);
7448
7449 distribution=stochastic_distribution(
7450 unique_stochastic_vars,
7451 replace_variables_capture_avoiding_alt(distribution.distribution(), sigma_sumvars));
7452 distribution=stochastic_distribution(
7453 unique_stochastic_vars,
7454 replace_variables_capture_avoiding_alt(distribution.distribution(), sigma_stochastic_vars));
7455 distribution=stochastic_distribution(
7456 distribution.variables(),
7457 replace_variables_capture_avoiding_alt(distribution.distribution(), sigma));
7458
7459 nextstate=substitute_assignmentlist(nextstate,pars,false,true,sigma_sumvars);
7460 nextstate=substitute_assignmentlist(nextstate,pars,false,true,sigma_stochastic_vars);
7461 nextstate=substitute_assignmentlist(nextstate,pars,true,true,sigma);
7462
7463 result_action_summands.emplace_back(unique_sumvars,
7464 condition,
7465 smmnd.multi_action().has_time() ? multi_action(multiaction, actiontime) : multi_action(multiaction),
7466 nextstate,
7467 distribution);
7468 }
7469 pars=unique_pars;
7470 action_summands.swap(result_action_summands);
7471
7472 deadlock_summand_vector result_deadlock_summands;
7473
7474 assert(unique_pars.size()==pars.size());
7475
7476 for (const deadlock_summand& smmnd: deadlock_summands)
7477 {
7478 const variable_list& sumvars=smmnd.summation_variables();
7479 maintain_variables_in_rhs<data::mutable_map_substitution<> > sigma_sumvars=make_unique_variables(sumvars,hint);
7480 const variable_list unique_sumvars=data::replace_variables(sumvars, sigma_sumvars);
7481
7482 assert(unique_sumvars.size()==sumvars.size());
7483 data_expression condition=smmnd.condition();
7484 data_expression actiontime=smmnd.deadlock().time();
7485
7486 condition=replace_variables_capture_avoiding_alt(condition, sigma_sumvars);
7487 condition=replace_variables_capture_avoiding_alt(condition, sigma);
7488
7489 actiontime=replace_variables_capture_avoiding_alt(actiontime, sigma_sumvars);
7490 actiontime=replace_variables_capture_avoiding_alt(actiontime, sigma);
7491
7492 result_deadlock_summands.emplace_back(unique_sumvars,
7493 condition,
7494 smmnd.deadlock().has_time() ? deadlock(actiontime) : deadlock());
7495 }
7496 pars=unique_pars;
7497 result_deadlock_summands.swap(deadlock_summands);
7498 }
7499
7500
7501
7502
7503
7504 /**************** parallel composition ******************************/
7505
7506
7507
7508 /// \brief Returns the conjunction of the two delay conditions and the join of the variables, where
7509 /// the variables in delay2 are renamed to avoid conflict with those in delay1.
7511 const lps::detail::ultimate_delay& delay1,
7512 const lps::detail::ultimate_delay& delay2)
7513 {
7514 // Make the bound variables of the second ultimate delay different from those in the first.
7515 variable_list renameable_variables=delay2.variables();
7516 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
7517 alphaconvert(renameable_variables, sigma, delay1.variables(), data_expression_list());
7518 // Additionally map the time variable of the second ultimate delay to that of the first.
7519 sigma[delay2.time_var()]=delay1.time_var();
7520 data_expression new_constraint;
7521 make_optimized_and(new_constraint, delay1.constraint(), replace_variables_capture_avoiding_alt(delay2.constraint(),sigma));
7522 variable_list new_existential_variables = delay1.variables()+renameable_variables;
7523
7524 // TODO: The new constraint can be simplified, as two conditions sharing the timed variable have been merged.
7525 return lps::detail::ultimate_delay(
7526 delay1.time_var(),
7527 new_existential_variables,
7528 new_constraint);
7529 }
7530
7532 const lps::detail::ultimate_delay& ultimate_delay_condition,
7533 const stochastic_action_summand_vector& action_summands1,
7534 const action_name_multiset_list& allowlist, // This is a list of list of identifierstring.
7535 const bool is_allow, // If is_allow or is_block is set, perform inline allow/block filtering.
7536 const bool is_block,
7537 stochastic_action_summand_vector& action_summands)
7538 {
7539 lps::detail::allow_list_cache allow_cache;
7540 if(is_allow)
7541 {
7542 allow_cache = lps::detail::make_allow_list_cache(allowlist);
7543 }
7544 for (const stochastic_action_summand& summand1: action_summands1)
7545 {
7546 variable_list sumvars=ultimate_delay_condition.variables();
7547 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
7548 alphaconvert(sumvars,sigma,summand1.summation_variables(),data_expression_list());
7549
7550 variable_list sumvars1 = summand1.summation_variables() + sumvars;
7551 const action_list& multiaction1 = summand1.multi_action().actions();
7552 assert(std::is_sorted(multiaction1.begin(), multiaction1.end()));
7553 data_expression actiontime1 = summand1.multi_action().time();
7554 data_expression condition1 = summand1.condition();
7555 const assignment_list& nextstate1 = summand1.assignments();
7556 const stochastic_distribution& distribution1=summand1.distribution();
7557 bool has_time = summand1.has_time();
7558
7559 if (multiaction1 != action_list({ terminationAction }))
7560 {
7561 if (is_allow && !allow_(allow_cache,multiaction1,terminationAction))
7562 {
7563 continue;
7564 }
7565 if (is_block && encap(allowlist,multiaction1))
7566 {
7567 continue;
7568 }
7569
7570 if (!options.ignore_time)
7571 {
7572 if (!has_time)
7573 {
7574 if (ultimate_delay_condition.constraint()!=sort_bool::true_())
7575 {
7576 actiontime1=ultimate_delay_condition.time_var();
7577 sumvars1.push_front(ultimate_delay_condition.time_var());
7578 condition1=lazy::and_(condition1,
7579 replace_variables_capture_avoiding_alt(ultimate_delay_condition.constraint(), sigma));
7580 has_time=true;
7581 }
7582 }
7583 else
7584 {
7585 /* Summand1 has time. Substitute the time expression for
7586 timevar in ultimate_delay_condition, and extend the condition */
7587 const std::set<variable> variables_in_actiontime1=find_free_variables(actiontime1);
7588 sigma[ultimate_delay_condition.time_var()]=actiontime1;
7589 const data_expression intermediateultimatedelaycondition=
7590 replace_variables_capture_avoiding_alt(ultimate_delay_condition.constraint(),sigma);
7591 make_optimized_and(condition1, condition1, intermediateultimatedelaycondition);
7592 }
7593
7594 condition1=RewriteTerm(condition1);
7595 }
7596
7597 if (condition1!=sort_bool::false_())
7598 {
7599 action_summands.emplace_back(sumvars1,
7600 condition1,
7601 has_time ? multi_action(multiaction1, actiontime1) : multi_action(multiaction1),
7602 nextstate1,
7603 distribution1);
7604 }
7605 }
7606 }
7607 }
7608
7610 const lps::detail::ultimate_delay& ultimate_delay_condition,
7611 const deadlock_summand_vector& deadlock_summands1,
7612 const bool is_allow, // If is_allow or is_block is set, perform inline allow/block filtering.
7613 const bool is_block,
7614 const stochastic_action_summand_vector& action_summands,
7615 deadlock_summand_vector& deadlock_summands)
7616 {
7617 bool inline_allow = is_allow || is_block;
7618
7619 if (!inline_allow)
7620 {
7621 for (const deadlock_summand& summand1: deadlock_summands1)
7622 {
7623 variable_list sumvars=ultimate_delay_condition.variables();
7624 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
7625 alphaconvert(sumvars,sigma,summand1.summation_variables(),data_expression_list());
7626
7627 variable_list sumvars1=summand1.summation_variables() + sumvars;
7628 data_expression actiontime1=summand1.deadlock().time();
7629 data_expression condition1=summand1.condition();
7630 bool has_time=summand1.deadlock().has_time();
7631
7632 if (!options.ignore_time)
7633 {
7634 if (!has_time)
7635 {
7636 if (ultimate_delay_condition.constraint()!=sort_bool::true_())
7637 {
7638 actiontime1=ultimate_delay_condition.time_var();
7639 sumvars1.push_front(ultimate_delay_condition.time_var());
7640 make_optimized_and(condition1, condition1,
7641 replace_variables_capture_avoiding_alt(ultimate_delay_condition.constraint(),
7642 sigma));
7643 has_time=true;
7644 }
7645 }
7646 else
7647 {
7648 /* Summand1 has time. Substitute the time expression for
7649 timevar in ultimate_delay_condition, and extend the condition */
7650 const std::set<variable> variables_in_actiontime1=find_free_variables(actiontime1);
7651 sigma[ultimate_delay_condition.time_var()]=actiontime1;
7652 const data_expression intermediateultimatedelaycondition=
7653 replace_variables_capture_avoiding_alt(ultimate_delay_condition.constraint(),sigma);
7654 make_optimized_and(condition1, condition1, intermediateultimatedelaycondition);
7655 }
7656
7657 condition1=RewriteTerm(condition1);
7658 }
7659
7660 if (condition1!=sort_bool::false_() && !options.ignore_time)
7661 {
7662 insert_timed_delta_summand(action_summands,
7663 deadlock_summands,
7664 deadlock_summand(sumvars1,condition1, has_time?deadlock(actiontime1):deadlock()),
7665 options.ignore_time);
7666 }
7667 }
7668 }
7669 }
7670
7671#ifdef MCRL2_LOG_LPS_LINEARISE_STATISTICS
7675 const bool is_allow,
7676 const bool is_block,
7678 size_t indent = 0)
7679 {
7680 std::string indent_str(indent, ' ');
7682
7683 os << indent_str << "- operator: leftmerge" << std::endl;
7684
7685 indent += 2;
7686 indent_str = std::string(indent, ' ');
7687 os << indent_str << "is_allow: " << std::boolalpha << is_allow << std::endl;
7688 os << indent_str << "is_block: " << std::boolalpha << is_block << std::endl;
7689 if (is_allow)
7690 {
7691 os << indent_str << "number of allow expressions: " << num_allow_block_actions << std::endl;
7692 }
7693 if (is_block)
7694 {
7695 os << indent_str << "number of block expressions: " << num_allow_block_actions << std::endl;
7696 }
7697
7698 os << indent_str << "LPS before:" << std::endl
7700 << indent_str << "result before:" << std::endl
7702 << indent_str << "result after:" << std::endl
7704
7705 return os.str();
7706 }
7707#endif
7708
7710 const stochastic_action_summand_vector& action_summands1,
7711 const deadlock_summand_vector& deadlock_summands1,
7712 const lps::detail::ultimate_delay& ultimate_delay_condition2,
7713 const action_name_multiset_list& allowlist, // This is a list of list of identifierstring.
7714 const bool is_allow, // If is_allow or is_block is set, perform inline allow/block filtering.
7715 const bool is_block,
7716 stochastic_action_summand_vector& action_summands,
7717 deadlock_summand_vector& deadlock_summands)
7718 {
7719#ifdef MCRL2_LOG_LPS_LINEARISE_STATISTICS
7720 lps_statistics_t lps_statistics_before = get_statistics(action_summands1, deadlock_summands1);
7721 lps_statistics_t lps_statistics_result_before = get_statistics(action_summands, deadlock_summands);
7722#endif
7723
7724 calculate_left_merge_deadlock(ultimate_delay_condition2, deadlock_summands1,
7725 is_allow, is_block, action_summands, deadlock_summands);
7726 calculate_left_merge_action(ultimate_delay_condition2, action_summands1,
7727 allowlist, is_allow, is_block, action_summands);
7728
7729#ifdef MCRL2_LOG_LPS_LINEARISE_STATISTICS
7730 lps_statistics_t lps_statistics_result_after = get_statistics(action_summands, deadlock_summands);
7731
7732 std::cout << log_leftmerge_application(lps_statistics_before,
7733 lps_statistics_result_before,
7734 lps_statistics_result_after,
7735 is_allow,
7736 is_block,
7737 (is_block ? allowlist.front().size() : allowlist.size()), 4);
7738#endif
7739 }
7740
7741
7743 const stochastic_action_summand_vector& action_summands1,
7744 const stochastic_action_summand_vector& action_summands2,
7745 const action_name_multiset_list& allowlist, // This is a list of list of identifierstring.
7746 const bool is_allow, // If is_allow or is_block is set, perform inline allow/block filtering.
7747 const bool is_block,
7748 stochastic_action_summand_vector& action_summands)
7749 {
7750 lps::detail::allow_list_cache allow_cache;
7751 if(is_allow)
7752 {
7753 allow_cache = lps::detail::make_allow_list_cache(allowlist);
7754 }
7755
7756 // First combine the action summands.
7757 for (const stochastic_action_summand& summand1: action_summands1)
7758 {
7759 const variable_list& sumvars1=summand1.summation_variables();
7760 const action_list multiaction1=summand1.multi_action().actions();
7761 const data_expression& actiontime1=summand1.multi_action().time();
7762 const data_expression& condition1=summand1.condition();
7763 const assignment_list& nextstate1=summand1.assignments();
7764 const stochastic_distribution& distribution1=summand1.distribution();
7765
7766 for (const stochastic_action_summand& summand2: action_summands2)
7767 {
7768 const variable_list& sumvars2=summand2.summation_variables();
7769 const action_list multiaction2=summand2.multi_action().actions();
7770 const data_expression& actiontime2=summand2.multi_action().time();
7771 const data_expression& condition2=summand2.condition();
7772 const assignment_list& nextstate2=summand2.assignments();
7773 const stochastic_distribution& distribution2=summand2.distribution();
7774
7775 if ((multiaction1 == action_list({ terminationAction })) == (multiaction2 == action_list({ terminationAction })))
7776 {
7777 action_list multiaction3;
7778 if ((multiaction1 == action_list({ terminationAction })) && (multiaction2 == action_list({ terminationAction })))
7779 {
7780 multiaction3.push_front(terminationAction);
7781 }
7782 else
7783 {
7784 multiaction3=linMergeMultiActionList(multiaction1,multiaction2);
7785 }
7786
7787 if (is_allow && !allow_(allow_cache,multiaction3,terminationAction))
7788 {
7789 continue;
7790 }
7791 if (is_block && encap(allowlist,multiaction3))
7792 {
7793 continue;
7794 }
7795
7796 const variable_list allsums=sumvars1+sumvars2;
7797 data_expression condition3= lazy::and_(condition1,condition2);
7798 data_expression action_time3;
7799 bool has_time3=summand1.has_time()||summand2.has_time();
7800
7801 if (!summand1.has_time())
7802 {
7803 if (summand2.has_time())
7804 {
7805 /* summand 2 has time*/
7806 action_time3=actiontime2;
7807 }
7808 }
7809 else
7810 {
7811 /* summand 1 has time */
7812 if (!summand2.has_time())
7813 {
7814 action_time3=actiontime1;
7815 }
7816 else
7817 {
7818 /* both summand 1 and 2 have time */
7819 action_time3=actiontime1;
7820 condition3=lazy::and_(
7821 condition3,
7822 equal_to(actiontime1,actiontime2));
7823 }
7824 }
7825
7826 const assignment_list nextstate3=nextstate1+nextstate2;
7827 const stochastic_distribution distribution3(
7828 distribution1.variables()+distribution2.variables(),
7829 real_times_optimized(distribution1.distribution(),distribution2.distribution()));
7830
7831 condition3=RewriteTerm(condition3);
7832 if (condition3!=sort_bool::false_())
7833 {
7834 assert(std::is_sorted(multiaction3.begin(), multiaction3.end()));
7835 action_summands.emplace_back(allsums,
7836 condition3,
7837 has_time3 ? multi_action(multiaction3, action_time3) : multi_action(multiaction3),
7838 nextstate3,
7839 distribution3);
7840 }
7841 }
7842 }
7843 }
7844 }
7845
7847 const stochastic_action_summand_vector& action_summands1,
7848 const deadlock_summand_vector& deadlock_summands1,
7849 const stochastic_action_summand_vector& action_summands,
7850 deadlock_summand_vector& deadlock_summands)
7851 {
7852 for (const stochastic_action_summand& summand1: action_summands1)
7853 {
7854 const variable_list& sumvars1=summand1.summation_variables();
7855 const data_expression& actiontime1=summand1.multi_action().time();
7856 const data_expression& condition1=summand1.condition();
7857
7858 for (const deadlock_summand& summand2: deadlock_summands1)
7859 {
7860 const variable_list& sumvars2=summand2.summation_variables();
7861 const data_expression& actiontime2=summand2.deadlock().time();
7862 const data_expression& condition2=summand2.condition();
7863
7864 const variable_list allsums=sumvars1+sumvars2;
7865 const data_expression condition3= lazy::and_(condition1,condition2);
7866 data_expression action_time3;
7867 bool has_time3=summand1.has_time()||summand2.has_time();
7868
7869 if (!summand1.has_time())
7870 {
7871 if (summand2.has_time())
7872 {
7873 /* summand 2 has time*/
7874 action_time3=actiontime2;
7875 }
7876 }
7877 else
7878 {
7879 /* summand 1 has time */
7880 if (!summand2.has_time())
7881 {
7882 action_time3=actiontime1;
7883 }
7884 else
7885 {
7886 /* both summand 1 and 2 have time */
7887 action_time3=RewriteTerm(sort_real::minimum(actiontime1,actiontime2));
7888 }
7889 }
7890
7891 if (condition3!=sort_bool::false_() && !options.ignore_time)
7892 {
7893 insert_timed_delta_summand(action_summands,
7894 deadlock_summands,
7895 deadlock_summand(allsums,
7896 condition3,
7897 has_time3?deadlock(action_time3):deadlock()),
7898 options.ignore_time);
7899 }
7900
7901 }
7902 }
7903 }
7904
7906 const deadlock_summand_vector& deadlock_summands1,
7907 const deadlock_summand_vector& deadlock_summands2,
7908 const stochastic_action_summand_vector& action_summands,
7909 deadlock_summand_vector& deadlock_summands)
7910 {
7911 for (const deadlock_summand& summand1: deadlock_summands1)
7912 {
7913 const variable_list& sumvars1=summand1.summation_variables();
7914 const data_expression& actiontime1=summand1.deadlock().time();
7915 const data_expression& condition1=summand1.condition();
7916
7917 for (const deadlock_summand& summand2: deadlock_summands2)
7918 {
7919 const variable_list& sumvars2=summand2.summation_variables();
7920 const data_expression& actiontime2=summand2.deadlock().time();
7921 const data_expression& condition2=summand2.condition();
7922
7923 const variable_list allsums=sumvars1+sumvars2;
7924 const data_expression condition3= lazy::and_(condition1,condition2);
7925 data_expression action_time3;
7926 bool has_time3=summand1.has_time()||summand2.has_time();
7927
7928 if (!summand1.has_time())
7929 {
7930 if (summand2.has_time())
7931 {
7932 /* summand 2 has time*/
7933 action_time3=actiontime2;
7934 }
7935 }
7936 else
7937 {
7938 /* summand 1 has time */
7939 if (!summand2.has_time())
7940 {
7941 action_time3=actiontime1;
7942 }
7943 else
7944 {
7945 /* both summand 1 and 2 have time */
7946 action_time3=RewriteTerm(sort_real::minimum(actiontime1,actiontime2));
7947 }
7948 }
7949
7950 if (condition3!=sort_bool::false_() && !options.ignore_time)
7951 {
7952 insert_timed_delta_summand(action_summands,
7953 deadlock_summands,
7954 deadlock_summand(allsums,
7955 condition3,
7956 has_time3?deadlock(action_time3):deadlock()),
7957 options.ignore_time);
7958 }
7959
7960 }
7961 }
7962 }
7963
7964#ifdef MCRL2_LOG_LPS_LINEARISE_STATISTICS
7969 const bool is_allow,
7970 const bool is_block,
7972 size_t indent = 0)
7973 {
7974 std::string indent_str(indent, ' ');
7976
7977 os << indent_str << "- operator: communicationmerge" << std::endl;
7978
7979 indent += 2;
7980 indent_str = std::string(indent, ' ');
7981 os << indent_str << "is_allow: " << std::boolalpha << is_allow << std::endl;
7982 os << indent_str << "is_block: " << std::boolalpha << is_block << std::endl;
7983 if (is_allow)
7984 {
7985 os << indent_str << "number of allow expressions: " << num_allow_block_actions << std::endl;
7986 }
7987 if (is_block)
7988 {
7989 os << indent_str << "number of block expressions: " << num_allow_block_actions << std::endl;
7990 }
7991
7992 os << indent_str << "first LPS before:" << std::endl << print(lps1_statistics_before, indent + 2)
7993 << indent_str << "second LPS before:" << std::endl << print(lps2_statistics_before, indent + 2)
7994 << indent_str << "result LPS before:" << std::endl << print(lps_result_statistics_before, indent + 2)
7995 << indent_str << "result LPS after:" << std::endl << print(lps_statistics_after, indent + 2);
7996
7997 return os.str();
7998 }
7999#endif
8000
8002 const stochastic_action_summand_vector& action_summands1,
8003 const deadlock_summand_vector& deadlock_summands1,
8004 const stochastic_action_summand_vector& action_summands2,
8005 const deadlock_summand_vector& deadlock_summands2,
8006 const action_name_multiset_list& allowlist, // This is a list of list of identifierstring.
8007 const bool is_allow, // If is_allow or is_block is set, perform inline allow/block filtering.
8008 const bool is_block,
8009 stochastic_action_summand_vector& action_summands,
8010 deadlock_summand_vector& deadlock_summands)
8011 {
8012#ifdef MCRL2_LOG_LPS_LINEARISE_STATISTICS
8013 lps_statistics_t lps1_statistics_before = get_statistics(action_summands1, deadlock_summands1);
8014 lps_statistics_t lps2_statistics_before = get_statistics(action_summands2, deadlock_summands2);
8015 lps_statistics_t lps_result_statistics_before = get_statistics(action_summands, deadlock_summands);
8016#endif
8017
8018 calculate_communication_merge_action_summands(action_summands1, action_summands2, allowlist, is_allow, is_block, action_summands);
8019 calculate_communication_merge_action_deadlock_summands(action_summands1, deadlock_summands2, action_summands, deadlock_summands);
8020 calculate_communication_merge_action_deadlock_summands(action_summands2, deadlock_summands1, action_summands, deadlock_summands);
8021 calculate_communication_merge_deadlock_summands(deadlock_summands1, deadlock_summands2, action_summands, deadlock_summands);
8022
8023#ifdef MCRL2_LOG_LPS_LINEARISE_STATISTICS
8024 lps_statistics_t lps_statistics_after = get_statistics(action_summands, deadlock_summands);
8025
8026 std::cout << log_communicationmerge_application(lps1_statistics_before, lps2_statistics_before, lps_result_statistics_before,
8027 lps_statistics_after, is_allow, is_block, (is_block ? allowlist.front().size() : allowlist.size()), 4);
8028#endif
8029 }
8030
8031
8033 const stochastic_action_summand_vector& action_summands1,
8034 const deadlock_summand_vector& deadlock_summands1,
8035 const lps::detail::ultimate_delay& ultimate_delay_condition1,
8036 const stochastic_action_summand_vector& action_summands2,
8037 const deadlock_summand_vector& deadlock_summands2,
8038 const lps::detail::ultimate_delay& ultimate_delay_condition2,
8039 const variable_list& par1,
8040 const variable_list& par3,
8041 const action_name_multiset_list& allowlist1, // This is a list of list of identifierstring.
8042 const bool is_allow, // If is_allow or is_block is set, perform inline allow/block filtering.
8043 const bool is_block,
8044 stochastic_action_summand_vector& action_summands,
8045 deadlock_summand_vector& deadlock_summands)
8046 {
8047 assert(action_summands.size()==0);
8048 assert(deadlock_summands.size()==0);
8049
8050 variable_list allpars = par1 + par3;
8051
8052 if (is_allow || is_block)
8053 {
8054 // Inline allow is only supported for ignore_time,
8055 // for in other cases generation of delta summands cannot be inlined in any simple way.
8056 assert(!options.nodeltaelimination && options.ignore_time);
8057 deadlock_summands.emplace_back(variable_list(), sort_bool::true_(), deadlock());
8058 }
8059
8060 /* first we enumerate the summands of t1 */
8061
8062 action_name_multiset_list allowlist(allowlist1);
8063 calculate_left_merge(action_summands1, deadlock_summands1,
8064 ultimate_delay_condition2, allowlist, is_allow, is_block,
8065 action_summands, deadlock_summands);
8066
8067 /* second we enumerate the summands of sumlist2 */
8068 calculate_left_merge(action_summands2, deadlock_summands2,
8069 ultimate_delay_condition1, allowlist, is_allow, is_block,
8070 action_summands, deadlock_summands);
8071
8072 /* thirdly we enumerate all multi actions*/
8073
8074 calculate_communication_merge(action_summands1, deadlock_summands1, action_summands2, deadlock_summands2,
8075 allowlist, is_allow, is_block, action_summands, deadlock_summands);
8076 }
8077
8078
8079#ifdef MCRL2_LOG_LPS_LINEARISE_STATISTICS
8080 static
8084 const bool is_allow,
8085 const bool is_block,
8087 size_t indent = 0)
8088 {
8089 std::string indent_str(indent, ' ');
8091
8092 os << indent_str << "- operator: parallel" << std::endl;
8093
8094 indent += 2;
8095 indent_str = std::string(indent, ' ');
8096 os << indent_str << "is_allow: " << std::boolalpha << is_allow << std::endl;
8097 os << indent_str << "is_block: " << std::boolalpha << is_block << std::endl;
8098 if (is_allow)
8099 {
8100 os << indent_str << "number of allow expressions: " << num_allow_block_actions << std::endl;
8101 }
8102 if (is_block)
8103 {
8104 os << indent_str << "number of block expressions: " << num_allow_block_actions << std::endl;
8105 }
8106
8107 os << indent_str << "first LPS before:" << std::endl << print(lps1_statistics_before, indent+2)
8108 << indent_str << "second LPS before:" << std::endl << print(lps2_statistics_before, indent+2);
8109
8110 os << indent_str << "subcalls:" << std::endl;
8111
8112 return os.str();
8113 }
8114
8115 static
8118 size_t indent = 0)
8119 {
8120 indent += 2;
8121 std::string indent_str(indent, ' ');
8123
8124 os << indent_str << "after:" << std::endl << print(lps_statistics_after, indent+2);
8125
8126 return os.str();
8127 }
8128#endif
8129
8131 const stochastic_action_summand_vector& action_summands1,
8132 const deadlock_summand_vector& deadlock_summands1,
8133 const variable_list& pars1,
8134 const data_expression_list& init1,
8135 const stochastic_distribution& initial_stochastic_distribution1,
8136 const lps::detail::ultimate_delay& ultimate_delay_condition1,
8137 const stochastic_action_summand_vector& action_summands2,
8138 const deadlock_summand_vector& deadlock_summands2,
8139 const variable_list& pars2,
8140 const data_expression_list& init2,
8141 const stochastic_distribution& initial_stochastic_distribution2,
8142 const lps::detail::ultimate_delay& ultimate_delay_condition2,
8143 const action_name_multiset_list& allowlist1, // This is a list of list of identifierstring.
8144 const bool is_allow, // If is_allow or is_block is set, perform inline allow/block filtering.
8145 const bool is_block,
8146 stochastic_action_summand_vector& action_summands,
8147 deadlock_summand_vector& deadlock_summands,
8148 variable_list& pars_result,
8149 data_expression_list& init_result,
8150 stochastic_distribution& initial_stochastic_distribution,
8151 lps::detail::ultimate_delay& ultimate_delay_condition)
8152 {
8153 mCRL2log(mcrl2::log::log_level_t::verbose) <<
8154 (is_allow ? "- calculating the parallel composition modulo the allow operator: " :
8155 is_block ? "- calculating the parallel composition modulo the block operator: " :
8156 "- calculating the parallel composition: ") <<
8157 action_summands1.size() <<
8158 " action summands + " << deadlock_summands1.size() <<
8159 " deadlock summands || " << action_summands2.size() <<
8160 " action summands + " << deadlock_summands2.size() << " deadlock summands = ";
8161
8162 // At this point the parameters of pars1 and pars2 are unique, except for
8163 // those that are constant in both processes.
8164
8165#ifdef MCRL2_LOG_LPS_LINEARISE_STATISTICS
8166 lps_statistics_t lps1_statistics_before = get_statistics(action_summands1, deadlock_summands1);
8167 lps_statistics_t lps2_statistics_before = get_statistics(action_summands2, deadlock_summands2);
8168
8169 std::cout << log_parallelcomposition_application_start(lps1_statistics_before, lps2_statistics_before, is_allow, is_block, (is_block?allowlist1.front().size():allowlist1.size()));
8170#endif
8171
8172 variable_list pars3;
8173 for (const variable& v: pars2)
8174 {
8175 if (std::find(pars1.begin(),pars1.end(),v)==pars1.end())
8176 {
8177 // *i does not occur in pars1.
8178 pars3.push_front(v);
8179 }
8180 }
8181
8182 pars3=reverse(pars3);
8183 assert(action_summands.size()==0);
8184 assert(deadlock_summands.size()==0);
8185 combine_summand_lists(action_summands1,deadlock_summands1,ultimate_delay_condition1,
8186 action_summands2,deadlock_summands2,ultimate_delay_condition2,
8187 pars1,pars3,allowlist1,is_allow,is_block,action_summands,deadlock_summands);
8188
8189 mCRL2log(mcrl2::log::log_level_t::verbose) << action_summands.size() << " actions and " << deadlock_summands.size() << " delta summands.\n";
8190 pars_result=pars1+pars3;
8191 init_result=init1 + init2;
8192 initial_stochastic_distribution=stochastic_distribution(
8193 initial_stochastic_distribution1.variables()+initial_stochastic_distribution2.variables(),
8194 sort_real::times(initial_stochastic_distribution1.distribution(),
8195 initial_stochastic_distribution2.distribution()));
8196 if (!options.ignore_time)
8197 {
8198 ultimate_delay_condition=combine_ultimate_delays(ultimate_delay_condition1, ultimate_delay_condition2);
8199 }
8200
8201#ifdef MCRL2_LOG_LPS_LINEARISE_STATISTICS
8202 lps_statistics_t lps_statistics_after = get_statistics(action_summands, deadlock_summands);
8203
8204 std::cout << log_parallelcomposition_application_end(lps_statistics_after);
8205#endif
8206 }
8207
8208 /**************** GENERaTE LPEmCRL **********************************/
8209
8210
8211 /// \brief Linearise a process indicated by procIdDecl.
8212 /// \details Returns actions_summands, deadlock_summands, the process parameters
8213 /// and the initial assignment list.
8214
8216 stochastic_action_summand_vector& action_summands,
8217 deadlock_summand_vector& deadlock_summands,
8218 const process_expression& t,
8219 const bool regular,
8220 const bool rename_variables,
8221 variable_list& pars,
8222 data_expression_list& init,
8223 stochastic_distribution& initial_stochastic_distribution,
8224 lps::detail::ultimate_delay& ultimate_delay_condition)
8225 {
8227 {
8228 generateLPEmCRL(action_summands,deadlock_summands,process_instance_assignment(t).identifier(),
8229 regular,pars,init,initial_stochastic_distribution,ultimate_delay_condition);
8230
8232
8233 // Now apply the assignment in this process to the obtained initial process and the distribution.
8234 maintain_variables_in_rhs<mutable_map_substitution<> > sigma;
8235 for (const assignment& a: process_instance_assignment(t).assignments())
8236 {
8237 sigma[a.lhs()]=a.rhs();
8238 }
8239
8240 init=replace_variables_capture_avoiding_alt(init,sigma);
8241 initial_stochastic_distribution =
8242 stochastic_distribution(
8243 initial_stochastic_distribution.variables(),
8244 replace_variables_capture_avoiding_alt(initial_stochastic_distribution.distribution(), sigma));
8245
8246 // Make the bound variables and parameters in this process unique.
8247
8248 if ((object.processstatus==GNF)||
8249 (object.processstatus==pCRL)||
8250 (object.processstatus==GNFalpha))
8251 {
8252 make_parameters_and_sum_variables_unique(action_summands,deadlock_summands,pars,ultimate_delay_condition,std::string(object.objectname));
8253 }
8254 else
8255 {
8256 if (rename_variables)
8257 {
8258 make_parameters_and_sum_variables_unique(action_summands,deadlock_summands,pars,ultimate_delay_condition);
8259 }
8260 }
8261
8262 if (regular && !options.do_not_apply_constelm)
8263 {
8264 // We apply constant elimination on the obtained linear process.
8265 // In order to do so, we have to create a complete process specification first, as
8266 // this is what the interface of constelm requires.
8267 // Note that this is only useful, in regular mode. This does not make sense if
8268 // stacks are being used.
8269
8270 stochastic_linear_process lps(pars,deadlock_summands,action_summands);
8271 stochastic_process_initializer initializer(init,initial_stochastic_distribution);
8272
8273 stochastic_specification temporary_spec(data,acts,global_variables,lps,initializer);
8274 constelm_algorithm < rewriter, stochastic_specification > alg(temporary_spec,rewr);
8275
8276 // Remove constants from the specification, where global variables are
8277 // also instantiated if they exist.
8278 data::mutable_map_substitution<> sigma = alg.compute_constant_parameters(true); // Instantiate global variables; Take conditions into account.
8279 alg.remove_parameters(sigma);
8280 alg.remove_trivial_summands();
8281
8282 if (!options.ignore_time)
8283 {
8284 ultimate_delay_condition.constraint()=data::replace_variables_capture_avoiding(ultimate_delay_condition.constraint(),sigma);
8285 }
8286
8287 // Reconstruct the variables from the temporary specification
8288 init=temporary_spec.initial_process().expressions();
8289 pars=temporary_spec.process().process_parameters();
8290 assert(init.size()==pars.size());
8291 // Add all free variables in object.parameters that are not already in the parameter list
8292 // and are not global variables to pars. This can occur when a parameter of the process is replaced
8293 // by a constant, which by itself is a parameter.
8294
8295 std::set <variable> variable_list = lps::find_free_variables(temporary_spec.process().action_summands());
8296 const std::set <variable> variable_list1 = lps::find_free_variables(temporary_spec.process().deadlock_summands());
8297 variable_list.insert(variable_list1.begin(),variable_list1.end());
8298 for (const variable& v: variable_list)
8299 {
8300 if (std::find(pars.begin(),pars.end(),v)==pars.end() && // The free variable is not in pars,
8301 global_variables.find(v)==global_variables.end() // it is neither a global variable
8302 // (lps::search_free_variable(temporary_spec.process().action_summands(),v) || lps::search_free_variable(temporary_spec.process().deadlock_summands(),v))
8303 ) // and it occurs in the summands.
8304 {
8305 pars.push_front(v);
8306 }
8307 }
8308
8309 action_summands=temporary_spec.process().action_summands();
8310 deadlock_summands=temporary_spec.process().deadlock_summands();
8311 }
8312 // Now constelm has been applied.
8313 return;
8314 } // End process assignment.
8315
8316 if (is_merge(t))
8317 {
8318 variable_list pars1;
8319 variable_list pars2;
8320 data_expression_list init1;
8321 data_expression_list init2;
8322 stochastic_distribution initial_stochastic_distribution1;
8323 stochastic_distribution initial_stochastic_distribution2;
8324 stochastic_action_summand_vector action_summands1;
8325 stochastic_action_summand_vector action_summands2;
8326 deadlock_summand_vector deadlock_summands1;
8327 deadlock_summand_vector deadlock_summands2;
8328 lps::detail::ultimate_delay ultimate_delay_condition1;
8329 lps::detail::ultimate_delay ultimate_delay_condition2;
8330 generateLPEmCRLterm(action_summands1,deadlock_summands1,process::merge(t).left(),
8331 regular,rename_variables,pars1,init1,initial_stochastic_distribution1,ultimate_delay_condition1);
8332 generateLPEmCRLterm(action_summands2,deadlock_summands2,process::merge(t).right(),
8333 regular,true,pars2,init2,initial_stochastic_distribution2,ultimate_delay_condition2);
8334 parallelcomposition(action_summands1,deadlock_summands1,pars1,init1,initial_stochastic_distribution1,ultimate_delay_condition1,
8335 action_summands2,deadlock_summands2,pars2,init2,initial_stochastic_distribution2,ultimate_delay_condition2,
8336 action_name_multiset_list(),false,false,
8337 action_summands,deadlock_summands,pars,init,initial_stochastic_distribution,ultimate_delay_condition);
8338 return;
8339 }
8340
8341 if (is_hide(t))
8342 {
8343 generateLPEmCRLterm(action_summands,deadlock_summands,hide(t).operand(),
8344 regular,rename_variables,pars,init,initial_stochastic_distribution,ultimate_delay_condition);
8345 hidecomposition(hide(t).hide_set(),action_summands);
8346 return;
8347 }
8348
8349 if (is_allow(t))
8350 {
8352 if (!options.nodeltaelimination && options.ignore_time && is_merge(par))
8353 {
8354 // Perform parallel composition with inline allow.
8355 variable_list pars1;
8356 variable_list pars2;
8357 data_expression_list init1;
8358 data_expression_list init2;
8359 stochastic_distribution initial_stochastic_distribution1;
8360 stochastic_distribution initial_stochastic_distribution2;
8361 stochastic_action_summand_vector action_summands1;
8362 stochastic_action_summand_vector action_summands2;
8363 deadlock_summand_vector deadlock_summands1;
8364 deadlock_summand_vector deadlock_summands2;
8365 lps::detail::ultimate_delay ultimate_delay_condition1;
8366 lps::detail::ultimate_delay ultimate_delay_condition2;
8367 generateLPEmCRLterm(action_summands1,deadlock_summands1,process::merge(par).left(),
8368 regular,rename_variables,pars1,init1,initial_stochastic_distribution1,ultimate_delay_condition1);
8369 generateLPEmCRLterm(action_summands2,deadlock_summands2,process::merge(par).right(),
8370 regular,true,pars2,init2,initial_stochastic_distribution2,ultimate_delay_condition2);
8371 parallelcomposition(action_summands1,deadlock_summands1,pars1,init1,initial_stochastic_distribution1,ultimate_delay_condition1,
8372 action_summands2,deadlock_summands2,pars2,init2,initial_stochastic_distribution2,ultimate_delay_condition2,
8373 allow(t).allow_set(),true,false,
8374 action_summands,deadlock_summands,pars,init,initial_stochastic_distribution,ultimate_delay_condition);
8375 return;
8376 }
8377 else if (!options.nodeltaelimination && options.ignore_time && is_comm(par))
8378 {
8379 generateLPEmCRLterm(action_summands,deadlock_summands,comm(par).operand(),
8380 regular,rename_variables,pars,init,initial_stochastic_distribution,ultimate_delay_condition);
8381 communicationcomposition(comm(par).comm_set(),allow(t).allow_set(),true,false,action_summands,deadlock_summands,terminationAction,options.nosumelm,options.nodeltaelimination,options.ignore_time,[this](const data::data_expression& e) { return RewriteTerm(e); });
8382 return;
8383 }
8384
8385 generateLPEmCRLterm(action_summands,deadlock_summands,par,regular,rename_variables,pars,init,initial_stochastic_distribution,ultimate_delay_condition);
8386 allowblockcomposition(allow(t).allow_set(),true,action_summands,deadlock_summands,terminationAction,options.ignore_time,options.nodeltaelimination);
8387 return;
8388 }
8389
8390 if (is_block(t))
8391 {
8393 if (!options.nodeltaelimination && options.ignore_time && is_merge(par))
8394 {
8395 // Perform parallel composition with inline block.
8396 variable_list pars1;
8397 variable_list pars2;
8398 data_expression_list init1;
8399 data_expression_list init2;
8400 stochastic_distribution initial_stochastic_distribution1;
8401 stochastic_distribution initial_stochastic_distribution2;
8402 stochastic_action_summand_vector action_summands1;
8403 stochastic_action_summand_vector action_summands2;
8404 deadlock_summand_vector deadlock_summands1;
8405 deadlock_summand_vector deadlock_summands2;
8406 lps::detail::ultimate_delay ultimate_delay_condition1;
8407 lps::detail::ultimate_delay ultimate_delay_condition2;
8408 generateLPEmCRLterm(action_summands1,deadlock_summands1,process::merge(par).left(),
8409 regular,rename_variables,pars1,init1,initial_stochastic_distribution1,ultimate_delay_condition1);
8410 generateLPEmCRLterm(action_summands2,deadlock_summands2,process::merge(par).right(),
8411 regular,true,pars2,init2,initial_stochastic_distribution2,ultimate_delay_condition2);
8412 // Encode the actions of the block list in one multi action.
8413 parallelcomposition(action_summands1,deadlock_summands1,pars1,init1,initial_stochastic_distribution1,ultimate_delay_condition1,
8414 action_summands2,deadlock_summands2,pars2,init2,initial_stochastic_distribution2,ultimate_delay_condition2,
8415 action_name_multiset_list({action_name_multiset(block(t).block_set())}),false,true,
8416 action_summands,deadlock_summands,pars,init,initial_stochastic_distribution,ultimate_delay_condition);
8417 return;
8418 }
8419 else if (!options.nodeltaelimination && options.ignore_time && is_comm(par))
8420 {
8421 generateLPEmCRLterm(action_summands,deadlock_summands,comm(par).operand(),
8422 regular,rename_variables,pars,init,initial_stochastic_distribution,ultimate_delay_condition);
8423 // Encode the actions of the block list in one multi action.
8424 communicationcomposition(comm(par).comm_set(),action_name_multiset_list( { action_name_multiset(block(t).block_set())} ),
8425 false,true,action_summands,deadlock_summands,terminationAction,options.nosumelm,options.nodeltaelimination,options.ignore_time,[this](const data::data_expression& e) { return RewriteTerm(e); });
8426 return;
8427 }
8428
8429 generateLPEmCRLterm(action_summands,deadlock_summands,par,regular,rename_variables,pars,init,initial_stochastic_distribution,ultimate_delay_condition);
8430 // Encode the actions of the block list in one multi action.
8431 allowblockcomposition(action_name_multiset_list({action_name_multiset(block(t).block_set())}),false,action_summands,deadlock_summands,terminationAction,options.ignore_time,options.nodeltaelimination);
8432 return;
8433 }
8434
8435 if (is_rename(t))
8436 {
8437 generateLPEmCRLterm(action_summands,deadlock_summands,process::rename(t).operand(),
8438 regular,rename_variables,pars,init,initial_stochastic_distribution,ultimate_delay_condition);
8439 lps::rename(process::rename(t).rename_set(),action_summands);
8440 return;
8441 }
8442
8443 if (is_comm(t))
8444 {
8445 generateLPEmCRLterm(action_summands,deadlock_summands,comm(t).operand(),
8446 regular,rename_variables,pars,init,initial_stochastic_distribution,ultimate_delay_condition);
8447 communicationcomposition(comm(t).comm_set(),action_name_multiset_list(),false,false,action_summands,deadlock_summands,terminationAction,options.nosumelm,options.nodeltaelimination,options.ignore_time,[this](const data::data_expression& e) { return RewriteTerm(e); });
8448 return;
8449 }
8450
8452 {
8453 const stochastic_operator& sto=atermpp::down_cast<stochastic_operator>(t);
8454 generateLPEmCRLterm(action_summands,deadlock_summands,sto.operand(),
8455 regular,rename_variables,pars,init,initial_stochastic_distribution,ultimate_delay_condition);
8456 variable_list stochvars=sto.variables();
8457 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
8458 alphaconvert(stochvars,sigma,pars + initial_stochastic_distribution.variables(), data_expression_list());
8459 initial_stochastic_distribution=stochastic_distribution(
8460 stochvars+initial_stochastic_distribution.variables(),
8461 sort_real::times(replace_variables_capture_avoiding_alt(
8462 sto.distribution(), sigma),
8463 initial_stochastic_distribution.distribution()));
8464 /* Reset the bound variables in the initial_stochastic_distribution, to avoid erroneous renaming in the body of the process */
8465 for(const variable& v: initial_stochastic_distribution.variables())
8466 {
8467 sigma[v]=v;
8468 }
8469 init=replace_variables_capture_avoiding_alt(init,sigma);
8470 return;
8471 }
8472
8473 throw mcrl2::runtime_error("Internal error. Expect an mCRL term " + process::pp(t) +".");
8474 }
8475
8476 /**************** GENERaTE LPEmCRL **********************************/
8477
8478 /* The result are a list of action summands, deadlock summand, the parameters of this
8479 linear process and its initial values. A initial stochastic distribution that must
8480 precede the initial linear process and the ultimate delay condition of this
8481 linear process that can be used or be ignored.
8482
8483 */
8484
8486 stochastic_action_summand_vector& action_summands,
8487 deadlock_summand_vector& deadlock_summands,
8488 const process_identifier& procIdDecl,
8489 const bool regular,
8490 variable_list& pars,
8491 data_expression_list& init,
8492 stochastic_distribution& initial_stochastic_distribution,
8493 lps::detail::ultimate_delay& ultimate_delay_condition)
8494 {
8495 /* If regular=1, then a regular version of the pCRL processes
8496 must be generated */
8497
8498 objectdatatype& object=objectIndex(procIdDecl);
8499
8500 if ((object.processstatus==GNF)||
8501 (object.processstatus==pCRL)||
8502 (object.processstatus==GNFalpha)||
8503 (object.processstatus==multiAction))
8504 {
8505 generateLPEpCRL(action_summands,deadlock_summands,procIdDecl,
8506 object.containstime,regular,pars,init,initial_stochastic_distribution);
8507 if (options.ignore_time)
8508 {
8509 ultimate_delay_condition=lps::detail::ultimate_delay();
8510 }
8511 else
8512 {
8513 ultimate_delay_condition=getUltimateDelayCondition(action_summands,deadlock_summands,pars);
8514
8515 try
8516 {
8517 // The ultimate_delay_condition can be complex. Try to simplify it with a fourier_motzkin reduction.
8518 data_expression simplified_ultimate_delay_condition;
8519 variable_list reduced_sumvars;
8520
8521 fourier_motzkin(ultimate_delay_condition.constraint(),
8522 ultimate_delay_condition.variables(),
8523 simplified_ultimate_delay_condition,
8524 reduced_sumvars,
8525 rewr);
8526 std::swap(ultimate_delay_condition.constraint(), simplified_ultimate_delay_condition);
8527 std::swap(ultimate_delay_condition.variables(), reduced_sumvars);
8528 }
8529 catch (mcrl2::runtime_error& e)
8530 {
8531 // Applying Fourier Motzkin failed. Continue working with the old ultimate delay condition.
8532 mCRL2log(mcrl2::log::log_level_t::debug) << "Simplifying a condition using Fourier-Motzkin reduction failed (I). \n" << e.what() << std::endl;
8533 }
8534 }
8535
8536 assert(init.size() == pars.size());
8537 return;
8538 }
8539 /* process is a mCRLdone */
8540 if ((object.processstatus==mCRLdone)||
8541 (object.processstatus==mCRLlin)||
8542 (object.processstatus==mCRL))
8543 {
8544 object.processstatus=mCRLlin;
8545 generateLPEmCRLterm(action_summands, deadlock_summands, object.processbody,
8546 regular, false, pars, init, initial_stochastic_distribution, ultimate_delay_condition);
8547 assert(init.size() == pars.size());
8548 return;
8549 }
8550
8551 throw mcrl2::runtime_error("laststatus: " + std::to_string(object.processstatus));
8552 }
8553
8554 /**************** alphaconversion ********************************/
8555
8557 const process_expression& t,
8558 const variable_list& parameters,
8559 maintain_variables_in_rhs<mutable_map_substitution<> > sigma)
8560 {
8561 if (is_choice(t))
8562 {
8563 return choice(
8564 alphaconversionterm(choice(t).left(),parameters,sigma),
8565 alphaconversionterm(choice(t).right(),parameters,sigma));
8566 }
8567
8568 if (is_seq(t))
8569 {
8570 return seq(
8571 alphaconversionterm(seq(t).left(),parameters,sigma),
8572 alphaconversionterm(seq(t).right(),parameters,sigma));
8573 }
8574
8575 if (is_sync(t))
8576 {
8577 return process::sync(
8578 alphaconversionterm(process::sync(t).left(),parameters,sigma),
8579 alphaconversionterm(process::sync(t).right(),parameters,sigma));
8580 }
8581
8582 if (is_bounded_init(t))
8583 {
8584 return bounded_init(
8585 alphaconversionterm(bounded_init(t).left(),parameters,sigma),
8586 alphaconversionterm(bounded_init(t).right(),parameters,sigma));
8587 }
8588
8589 if (is_merge(t))
8590 {
8591 return process::merge(
8592 alphaconversionterm(process::merge(t).left(),parameters,sigma),
8593 alphaconversionterm(process::merge(t).right(),parameters,sigma));
8594 }
8595
8596 if (is_left_merge(t))
8597 {
8598 return left_merge(
8599 alphaconversionterm(left_merge(t).left(),parameters,sigma),
8600 alphaconversionterm(left_merge(t).right(),parameters,sigma));
8601 }
8602
8603 if (is_at(t))
8604 {
8605 return at(alphaconversionterm(at(t).operand(),parameters,sigma),
8606 replace_variables_capture_avoiding_alt(at(t).time_stamp(),sigma));
8607 }
8608
8609 if (is_if_then(t))
8610 {
8611 return if_then(
8612 replace_variables_capture_avoiding_alt(if_then(t).condition(), sigma),
8613 alphaconversionterm(if_then(t).then_case(),parameters,sigma));
8614 }
8615
8616 if (is_sum(t))
8617 {
8618 variable_list sumvars=sum(t).variables();
8619 maintain_variables_in_rhs<mutable_map_substitution<> > local_sigma=sigma;
8620
8621 alphaconvert(sumvars,local_sigma,variable_list(),variable_list_to_data_expression_list(parameters));
8622 return sum(sumvars,alphaconversionterm(sum(t).operand(), sumvars+parameters, local_sigma));
8623 }
8624
8626 {
8627 const stochastic_operator& sto=down_cast<const stochastic_operator>(t);
8628 variable_list sumvars=sto.variables();
8629 maintain_variables_in_rhs<mutable_map_substitution<> > local_sigma=sigma;
8630
8631 alphaconvert(sumvars,local_sigma,variable_list(),variable_list_to_data_expression_list(parameters));
8632 return stochastic_operator(
8633 sumvars,
8634 replace_variables_capture_avoiding_alt(sto.distribution(), sigma),
8635 alphaconversionterm(sto.operand(), sumvars+parameters, local_sigma));
8636 }
8637
8639 {
8641 objectdatatype& object=objectIndex(procId);
8642
8643 const variable_list instance_parameters=object.parameters;
8644 alphaconversion(procId,instance_parameters);
8645
8646 const process_instance_assignment result(procId,
8647 substitute_assignmentlist(process_instance_assignment(t).assignments(),
8648 instance_parameters,false, true,sigma));
8650 return result;
8651 }
8652
8653 if (is_action(t))
8654 {
8655 return action(action(t).label(), replace_variables_capture_avoiding_alt(action(t).arguments(), sigma));
8656 }
8657
8658 if (is_delta(t)||
8659 is_tau(t))
8660 {
8661 return t;
8662 }
8663
8664 if (is_hide(t))
8665 {
8666 return alphaconversionterm(hide(t).operand(),parameters,sigma);
8667 }
8668
8669 if (is_rename(t))
8670 {
8671 return alphaconversionterm(process::rename(t).operand(),parameters,sigma);
8672 }
8673
8674 if (is_comm(t))
8675 {
8676 return alphaconversionterm(comm(t).operand(),parameters,sigma);
8677 }
8678
8679 if (is_allow(t))
8680 {
8681 return alphaconversionterm(allow(t).operand(),parameters,sigma);
8682 }
8683
8684 if (is_block(t))
8685 {
8686 return alphaconversionterm(block(t).operand(),parameters,sigma);
8687 }
8688
8689 throw mcrl2::runtime_error("unexpected process format in alphaconversionterm " + process::pp(t) +".");
8690 return process_expression();
8691 }
8692
8693 void alphaconversion(const process_identifier& procId, const variable_list& parameters)
8694 {
8695 objectdatatype& object=objectIndex(procId);
8696
8697 if ((object.processstatus==GNF)||
8698 (object.processstatus==multiAction))
8699 {
8700 object.processstatus=GNFalpha;
8701 // tempvar below is needed as objectdata may be reallocated
8702 // during a call to alphaconversionterm.
8703 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
8704 const process_expression tempvar=alphaconversionterm(object.processbody,parameters,sigma);
8705 object.processbody=tempvar;
8706 }
8707 else if (object.processstatus==mCRLdone)
8708 {
8709 maintain_variables_in_rhs< mutable_map_substitution<> > sigma;
8710 alphaconversionterm(object.processbody,parameters,sigma);
8711
8712 }
8713 else if (object.processstatus==GNFalpha)
8714 {
8715 return;
8716 }
8717 else
8718 {
8719 throw mcrl2::runtime_error("unknown type " + std::to_string(object.processstatus) +
8720 " in alphaconversion of " + process::pp(procId) +".");
8721 }
8722 return;
8723 }
8724
8725 /***** determinewhetherprocessescontaintime; **********/
8726
8728 const process_expression& t,
8729 bool* stable,
8730 std::set < process_identifier >& visited,
8731 bool allowrecursion,
8732 bool& contains_if_then)
8733 {
8734 if (is_merge(t))
8735 {
8736 /* the construction below is needed to guarantee that
8737 both subterms are recursively investigated */
8738 bool r1=containstimebody(process::merge(t).left(),stable,visited,allowrecursion,contains_if_then);
8739 bool r2=containstimebody(process::merge(t).right(),stable,visited,allowrecursion,contains_if_then);
8740 return r1||r2;
8741 }
8742
8744 {
8745 if (allowrecursion)
8746 {
8747 return (containstime_rec(process_instance_assignment(t).identifier(),stable,visited,contains_if_then));
8748 }
8750 }
8751
8752 if (is_hide(t))
8753 {
8754 return containstimebody(hide(t).operand(),stable,visited,allowrecursion,contains_if_then);
8755 }
8756
8757 if (is_rename(t))
8758 {
8759 return containstimebody(process::rename(t).operand(),stable,visited,allowrecursion,contains_if_then);
8760 }
8761
8762 if (is_allow(t))
8763 {
8764 return containstimebody(allow(t).operand(),stable,visited,allowrecursion,contains_if_then);
8765 }
8766
8767 if (is_block(t))
8768 {
8769 return containstimebody(block(t).operand(),stable,visited,allowrecursion,contains_if_then);
8770 }
8771
8772 if (is_comm(t))
8773 {
8774 return containstimebody(comm(t).operand(),stable,visited,allowrecursion,contains_if_then);
8775 }
8776
8777 if (is_choice(t))
8778 {
8779 bool r1=containstimebody(choice(t).left(),stable,visited,allowrecursion,contains_if_then);
8780 bool r2=containstimebody(choice(t).right(),stable,visited,allowrecursion,contains_if_then);
8781 return r1||r2;
8782 }
8783
8784 if (is_seq(t))
8785 {
8786 bool r1=containstimebody(seq(t).left(),stable,visited,allowrecursion,contains_if_then);
8787 bool r2=containstimebody(seq(t).right(),stable,visited,allowrecursion,contains_if_then);
8788 return r1||r2;
8789 }
8790
8791 if (is_if_then(t))
8792 {
8793 // If delta is added, c->p is translated into c->p<>delta,
8794 // otherwise into c->p<>delta@0. In this last case the process
8795 // contains time.
8796 contains_if_then=true;
8797 if (options.ignore_time)
8798 {
8799 return containstimebody(if_then(t).then_case(),stable,visited,allowrecursion,contains_if_then);
8800 }
8801 else
8802 {
8803 return true;
8804 }
8805 }
8806
8807 if (is_if_then_else(t))
8808 {
8809 bool r1=containstimebody(if_then_else(t).then_case(),stable,visited,allowrecursion,contains_if_then);
8810 bool r2=containstimebody(if_then_else(t).else_case(),stable,visited,allowrecursion,contains_if_then);
8811 return r1||r2;
8812 }
8813
8814 if (is_sum(t))
8815 {
8816 return containstimebody(sum(t).operand(),stable,visited,allowrecursion,contains_if_then);
8817 }
8818
8820 {
8821 return containstimebody(stochastic_operator(t).operand(),stable,visited,allowrecursion,contains_if_then);
8822 }
8823
8824 if (is_action(t)||
8825 is_delta(t)||
8826 is_tau(t))
8827 {
8828 return false;
8829 }
8830
8831 if (is_at(t))
8832 {
8833 return true;
8834 }
8835
8836 if (is_sync(t))
8837 {
8838 bool r1=containstimebody(process::sync(t).left(),stable,visited,allowrecursion,contains_if_then);
8839 bool r2=containstimebody(process::sync(t).right(),stable,visited,allowrecursion,contains_if_then);
8840 return r1||r2;
8841 }
8842
8843 throw mcrl2::runtime_error("unexpected process format in containstime " + process::pp(t) +".");
8844 return false;
8845 }
8846
8848 const process_identifier& procId,
8849 bool* stable,
8850 std::set < process_identifier >& visited,
8851 bool& contains_if_then)
8852 {
8853 objectdatatype& object=objectIndex(procId);
8854
8855 if (visited.count(procId)==0)
8856 {
8857 visited.insert(procId);
8858 const bool ct=containstimebody(object.processbody,stable,visited,true,contains_if_then);
8859 static bool show_only_once=true;
8860 if (ct && options.ignore_time && show_only_once)
8861 {
8862 mCRL2log(mcrl2::log::log_level_t::warning) << "process " << procId.name() <<
8863 " contains time, which is now not preserved. \n" <<
8864 "Use --timed or -T, or untick `add deadlocks' for a correct timed linearisation...\n";
8865 show_only_once=false;
8866 }
8867 if (object.containstime!=ct)
8868 {
8869 object.containstime=ct;
8870 if (stable!=nullptr)
8871 {
8872 *stable=false;
8873 }
8874 }
8875 }
8876 return (object.containstime);
8877 }
8878
8880 {
8881 std::set < process_identifier > visited;
8882 bool stable;
8883 bool contains_if_then;
8884 return containstimebody(t,&stable,visited,false,contains_if_then);
8885 }
8886
8888
8889 {
8890 /* This function sets for all reachable processes in the array objectdata
8891 whether they contain time in the field containstime. In verbose mode
8892 it prints the process variables that contain time. Furtermore, it returns
8893 whether there are processes that contain an if-then that will be translated
8894 to an if-then-else with an delta@0 in the else branch, introducing time */
8895 bool stable=false;
8896 bool contains_if_then=false;
8897
8898 while (!stable)
8899 {
8900 std::set < process_identifier > visited;
8901 stable=true;
8902 containstime_rec(procId,&stable,visited,contains_if_then);
8903 }
8904 return contains_if_then;
8905 }
8906 /***** transform_initial_distribution_term **********/
8907
8909 const process_expression& t,
8910 const std::map < process_identifier, process_pid_pair >& processes_with_initial_distribution)
8911 {
8913 {
8914 const process_instance_assignment u=atermpp::down_cast<process_instance_assignment>(t);
8915 const process_expression new_process=processes_with_initial_distribution.at(u.identifier()).process_body();
8916 if (is_stochastic_operator(new_process))
8917 {
8918 // Add the initial stochastic_distribution.
8919 const process_identifier& new_identifier=processes_with_initial_distribution.at(u.identifier()).process_id();
8920 objectdatatype& object=objectIndex(new_identifier);
8921 const variable_list new_parameters=object.parameters;
8922 const stochastic_operator& sto=down_cast<stochastic_operator>(new_process);
8923 assignment_list new_assignments;
8924
8925 const variable_list relevant_stochastic_variables=parameters_that_occur_in_body(sto.variables(),object.processbody);
8926 assert(relevant_stochastic_variables.size()<=new_parameters.size());
8927 // The variables in sto.variables() may clash with local variables u and therefore need to be rename.
8928 variable_list renamed_sto_variables=sto.variables();
8929 mutable_indexed_substitution<> local_sigma1;
8930 alphaconvertprocess(renamed_sto_variables, local_sigma1, t);
8931
8932
8933 variable_list::const_iterator i=new_parameters.begin();
8934 for(const variable& v: relevant_stochastic_variables)
8935 {
8936 new_assignments=push_back(new_assignments,assignment(*i,local_sigma1(v)));
8937 ++i;
8938 }
8939 data_expression new_distribution = data::replace_variables_capture_avoiding(sto.distribution(),local_sigma1);
8940
8941 // Some of the variables may occur only in the distribution, which is now moved out.
8942 // Therefore, the assignments must be filtered.
8943 new_assignments=filter_assignments(new_assignments + u.assignments(),object.parameters);
8944
8945 // The variables bound in the distribution may conflict
8946
8947 // Furthermore, the old assignment must be applied to the distribution, when it is moved
8948 // outside of the process body.
8949 mutable_indexed_substitution<> local_sigma;
8950 for(const assignment& a:u.assignments())
8951 {
8952 local_sigma[a.lhs()]=a.rhs();
8953 }
8954 return stochastic_operator(renamed_sto_variables,
8955 data::replace_variables_capture_avoiding(new_distribution, local_sigma),
8956 process_instance_assignment(new_identifier,new_assignments));
8957 }
8958 return t;
8959
8960 }
8961
8962 if (is_choice(t))
8963 {
8964 const process_expression r1_=transform_initial_distribution_term(choice(t).left(),processes_with_initial_distribution);
8965 const process_expression r2_=transform_initial_distribution_term(choice(t).right(),processes_with_initial_distribution);
8967 {
8968 const stochastic_operator& r1=down_cast<const stochastic_operator>(r1_);
8970 {
8971 /* both r1_ and r2_ are stochastic */
8972 const stochastic_operator& r2=down_cast<const stochastic_operator>(r2_);
8973 const process_expression& new_body1=r1.operand();
8974 process_expression new_body2=r2.operand();
8975 variable_list stochvars=r2.variables();
8976 maintain_variables_in_rhs< mutable_map_substitution<> > local_sigma;
8977 alphaconvert(stochvars,local_sigma, r1.variables(),data_expression_list());
8978 new_body2=substitute_pCRLproc(new_body2, local_sigma);
8979 const data_expression new_distribution=
8980 process::replace_variables_capture_avoiding_with_an_identifier_generator(r2.distribution(),local_sigma,fresh_identifier_generator);
8981
8982 return stochastic_operator(r1.variables() + stochvars,
8983 real_times_optimized(r1.distribution(),new_distribution),
8984 choice(new_body1,new_body2));
8985 }
8986 /* r1 is and r2_ is not a stochastic operator */
8988 }
8990 {
8991 /* r1_ is not and r2_ is a stochastic operator */
8992 const stochastic_operator& r2=down_cast<const stochastic_operator>(r2_);
8994 }
8995 /* neither r1_ nor r2_ is stochastic */
8996 return choice(r1_,r2_);
8997 }
8998
8999 if (is_seq(t))
9000 {
9001 const process_expression r1=transform_initial_distribution_term(seq(t).left(),processes_with_initial_distribution);
9002 const process_expression r2=transform_initial_distribution_term(seq(t).right(),processes_with_initial_distribution);
9004 {
9005 const stochastic_operator& r=down_cast<const stochastic_operator>(r1);
9007 }
9008 return seq(r1,r2);
9009 }
9010
9011 if (is_if_then(t))
9012 {
9013 const if_then& t_=atermpp::down_cast<if_then>(t);
9014 const process_expression r=transform_initial_distribution_term(t_.then_case(),processes_with_initial_distribution);
9016 {
9017 const stochastic_operator& r_=down_cast<const stochastic_operator>(r);
9021 }
9022 return if_then(t_.condition(),r);
9023 }
9024
9025 if (is_if_then_else(t))
9026 {
9027 const if_then_else& t_=atermpp::down_cast<if_then_else>(t);
9028 const process_expression r1_=transform_initial_distribution_term(t_.then_case(),processes_with_initial_distribution);
9029 const process_expression r2_=transform_initial_distribution_term(t_.else_case(),processes_with_initial_distribution);
9031 {
9032 const stochastic_operator& r1=down_cast<const stochastic_operator>(r1_);
9034 {
9035 /* both r1_ and r2_ are stochastic */
9036
9037 const stochastic_operator& r2=down_cast<const stochastic_operator>(r2_);
9038 const process_expression& new_body1=r1.operand();
9039 process_expression new_body2=r2.operand();
9040 variable_list stochvars=r2.variables();
9041 maintain_variables_in_rhs< mutable_map_substitution<> > local_sigma;
9042 alphaconvert(stochvars,local_sigma, r1.variables(),data_expression_list());
9043 new_body2=substitute_pCRLproc(new_body2, local_sigma);
9044 const data_expression new_distribution=
9045 process::replace_variables_capture_avoiding_with_an_identifier_generator(r2.distribution(),local_sigma,fresh_identifier_generator);
9046
9047 return stochastic_operator(r1.variables() + stochvars,
9048 if_(t_.condition(),
9049 lazy::and_(variables_are_equal_to_default_values(stochvars),r1.distribution()),
9050 lazy::and_(variables_are_equal_to_default_values(r1.variables()),new_distribution)),
9051 if_then_else(t_.condition(),new_body1,new_body2));
9052 }
9053 /* r1 is and r2_ is not a stochastic operator */
9058 }
9060 {
9061 /* r1_ is not and r2_ is a stochastic operator */
9062 const stochastic_operator& r2=down_cast<const stochastic_operator>(r2_);
9068 }
9069 /* neither r1_ nor r2_ is stochastic */
9070 return if_then_else(t_.condition(),r1_,r2_);
9071
9072 }
9073
9074 if (is_sum(t))
9075 {
9076 const sum& s=down_cast<const sum>(t);
9077 const process_expression r_= transform_initial_distribution_term(s.operand(),processes_with_initial_distribution);
9079 {
9080 const stochastic_operator& r=down_cast<const stochastic_operator>(r_);
9081 std::set <variable> variables_in_distribution=find_all_variables(r.distribution());
9082 for(const variable& v: s.variables())
9083 {
9084 if (variables_in_distribution.count(v)>0)
9085 {
9086 throw mcrl2::runtime_error("Cannot commute a sum operator over a stochastic operator in " +
9087 process::pp(t) + ".\n" +
9088 "The problematic variable is " + pp(v) + ":" + pp(v.sort()) + ".");
9089 }
9090 }
9094 }
9095 return sum(s.variables(),r_);
9096 }
9097
9099 {
9100 const stochastic_operator& sto=down_cast<const stochastic_operator>(t);
9101 const process_expression r_= transform_initial_distribution_term(sto.operand(),processes_with_initial_distribution);
9102
9104 {
9105 const stochastic_operator& r=down_cast<const stochastic_operator>(r_);
9108 r.operand());
9109 }
9111 }
9112
9113 if (is_action(t))
9114 {
9115 return t;
9116 }
9117
9118 if (is_delta(t))
9119 {
9120 return t;
9121 }
9122
9123 if (is_tau(t))
9124 {
9125 return t;
9126 }
9127
9128 if (is_at(t))
9129 {
9130 const process_expression r_=transform_initial_distribution_term(at(t).operand(),processes_with_initial_distribution);
9132 {
9133 const stochastic_operator& r=down_cast<const stochastic_operator>(r_);
9135 }
9136 return t;
9137 }
9138
9139 if (is_sync(t))
9140 {
9141 return t;
9142 }
9143
9144 throw mcrl2::runtime_error("unexpected process format in transform_initial_distribution_term " + process::pp(t) +".");
9145 }
9146
9147 /***** obtain_initial_distribution **********/
9148
9150 {
9151 /* This function obtains the initial distribution of a pCRL term that is in Greibach normal form. */
9153 {
9154 assert(0); // This is not possible.
9155 return t;
9156 }
9157
9158 if (is_choice(t))
9159 {
9163 {
9164 const stochastic_operator& r1=down_cast<const stochastic_operator>(r1_);
9166 {
9167 /* both r1_ and r2_ are stochastic */
9168 const stochastic_operator& r2=down_cast<const stochastic_operator>(r2_);
9169 const process_expression& new_body1=r1.operand();
9170 process_expression new_body2=r2.operand();
9171 variable_list stochvars=r2.variables();
9172 maintain_variables_in_rhs< mutable_map_substitution<> > local_sigma;
9173 alphaconvert(stochvars,local_sigma, r1.variables(),data_expression_list());
9174 new_body2=substitute_pCRLproc(new_body2, local_sigma);
9175 const data_expression new_distribution=
9176 process::replace_variables_capture_avoiding_with_an_identifier_generator(r2.distribution(),local_sigma,fresh_identifier_generator);
9177
9178 return stochastic_operator(r1.variables() + stochvars,
9179 real_times_optimized(r1.distribution(),new_distribution),
9180 choice(new_body1,new_body2));
9181 }
9182 /* r1 is and r2_ is not a stochastic operator */
9184 }
9186 {
9187 /* r1_ is not and r2_ is a stochastic operator */
9188 const stochastic_operator& r2=down_cast<const stochastic_operator>(r2_);
9190 }
9191 /* neither r1_ nor r2_ is stochastic */
9192 return t;
9193 }
9194
9195 if (is_seq(t))
9196 {
9197 const process_expression r_=obtain_initial_distribution_term(down_cast<seq>(t).left());
9199 {
9200 const stochastic_operator& r=down_cast<const stochastic_operator>(r_);
9202 }
9203 return t;
9204 }
9205
9206 if (is_if_then(t))
9207 {
9208 const process_expression r_=obtain_initial_distribution_term(down_cast<if_then>(t).then_case());
9210 {
9211 const if_then& t_if_then=atermpp::down_cast<if_then>(t);
9212 const stochastic_operator& r=down_cast<const stochastic_operator>(r_);
9216 }
9217 return t;
9218 }
9219
9220 if (is_if_then_else(t))
9221 {
9222 const process_expression r1_=obtain_initial_distribution_term(down_cast<if_then_else>(t).then_case());
9223 const process_expression r2_=obtain_initial_distribution_term(down_cast<if_then_else>(t).else_case());
9225 {
9226 const stochastic_operator& r1=down_cast<const stochastic_operator>(r1_);
9228 {
9229 /* both r1_ and r2_ are stochastic */
9230
9231 const stochastic_operator& r2=down_cast<const stochastic_operator>(r2_);
9232 const process_expression& new_body1=r1.operand();
9233 process_expression new_body2=r2.operand();
9234 variable_list stochvars=r2.variables();
9235 maintain_variables_in_rhs< mutable_map_substitution<> > local_sigma;
9236 alphaconvert(stochvars,local_sigma, r1.variables(),data_expression_list());
9237 new_body2=substitute_pCRLproc(new_body2, local_sigma);
9238 const data_expression new_distribution=
9239 process::replace_variables_capture_avoiding_with_an_identifier_generator(r2.distribution(),local_sigma,fresh_identifier_generator);
9240
9241 return stochastic_operator(r1.variables() + stochvars,
9242 if_(if_then_else(t).condition(),
9243 lazy::and_(variables_are_equal_to_default_values(stochvars),r1.distribution()),
9244 lazy::and_(variables_are_equal_to_default_values(r1.variables()),new_distribution)),
9245 if_then_else(if_then_else(t).condition(),new_body1,new_body2));
9246 }
9247 /* r1 is and r2_ is not a stochastic operator */
9252 }
9254 {
9255 /* r1_ is not and r2_ is a stochastic operator */
9256 const stochastic_operator& r2=down_cast<const stochastic_operator>(r2_);
9262 }
9263 /* neither r1_ nor r2_ is stochastic */
9264 return t;
9265
9266 }
9267
9268 if (is_sum(t))
9269 {
9270 const sum& s=down_cast<const sum>(t);
9273 {
9274 const stochastic_operator& r=down_cast<const stochastic_operator>(r_);
9275 std::set <variable> variables_in_distribution=find_all_variables(r.distribution());
9276 for(const variable& v: s.variables())
9277 {
9278 if (variables_in_distribution.count(v)>0)
9279 {
9280 throw mcrl2::runtime_error("Cannot commute a sum operator over a stochastic operator in " +
9281 process::pp(t) + ".\n" +
9282 "The problematic variable is " + pp(v) + ":" + pp(v.sort()) + ".");
9283 }
9284 }
9288 }
9289 return t;
9290 }
9291
9293 {
9294 const stochastic_operator& sto=down_cast<const stochastic_operator>(t);
9296
9298 {
9299 const stochastic_operator& r=down_cast<const stochastic_operator>(r_);
9302 r.operand());
9303 }
9305 }
9306
9307 if (is_action(t))
9308 {
9309 return t;
9310 }
9311
9312 if (is_delta(t))
9313 {
9314 return t;
9315 }
9316
9317 if (is_tau(t))
9318 {
9319 return t;
9320 }
9321
9322 if (is_at(t))
9323 {
9326 {
9327 const stochastic_operator& r=down_cast<const stochastic_operator>(r_);
9329 }
9330 return t;
9331 }
9332
9333 if (is_sync(t))
9334 {
9335 return t;
9336 }
9337
9338 throw mcrl2::runtime_error("unexpected process format in canterminate " + process::pp(t) +".");
9339 }
9340
9341
9343 {
9344 objectdatatype& object=objectIndex(procId);
9346 if (!is_stochastic_operator(initial_distribution_))
9347 {
9348 return process_instance_assignment(procId,assignment_list());
9349 }
9350 const stochastic_operator& initial_distribution=down_cast<const stochastic_operator>(initial_distribution_);
9351 if (!is_process_instance_assignment(initial_distribution.operand()))
9352 {
9353 const process_identifier new_procId=
9354 newprocess(object.parameters + initial_distribution.variables(),
9355 initial_distribution.operand(),
9356 pCRL, canterminatebody(initial_distribution.operand()), containstimebody(initial_distribution.operand()));
9357
9358 return stochastic_operator(initial_distribution.variables(),
9359 initial_distribution.distribution(),
9360 process_instance_assignment(new_procId,assignment_list())); // TODO add correct assignment here.
9361 }
9362 return initial_distribution;
9363 }
9364
9365 /***** determinewhetherprocessescanterminate(init); **********/
9366
9368 const process_expression& t,
9369 bool& stable,
9370 std::set < process_identifier >& visited,
9371 const bool allowrecursion)
9372 {
9373 if (is_merge(t))
9374 {
9375 /* the construction below is needed to guarantee that
9376 both subterms are recursively investigated */
9377 const bool r1=canterminatebody(process::merge(t).left(),stable,visited,allowrecursion);
9378 const bool r2=canterminatebody(process::merge(t).right(),stable,visited,allowrecursion);
9379 return r1&&r2;
9380 }
9381
9383 {
9385 if (allowrecursion)
9386 {
9387 return (canterminate_rec(u.identifier(),stable,visited));
9388 }
9390 }
9391
9392 if (is_hide(t))
9393 {
9394 return (canterminatebody(hide(t).operand(),stable,visited,allowrecursion));
9395 }
9396
9397 if (is_rename(t))
9398 {
9399 return (canterminatebody(process::rename(t).operand(),stable,visited,allowrecursion));
9400 }
9401
9402 if (is_allow(t))
9403 {
9404 return (canterminatebody(allow(t).operand(),stable,visited,allowrecursion));
9405 }
9406
9407 if (is_block(t))
9408 {
9409 return (canterminatebody(block(t).operand(),stable,visited,allowrecursion));
9410 }
9411
9412 if (is_comm(t))
9413 {
9414 return (canterminatebody(comm(t).operand(),stable,visited,allowrecursion));
9415 }
9416
9417 if (is_choice(t))
9418 {
9419 const bool r1=canterminatebody(choice(t).left(),stable,visited,allowrecursion);
9420 const bool r2=canterminatebody(choice(t).right(),stable,visited,allowrecursion);
9421 return r1||r2;
9422 }
9423
9424 if (is_seq(t))
9425 {
9426 const bool r1=canterminatebody(seq(t).left(),stable,visited,allowrecursion);
9427 const bool r2=canterminatebody(seq(t).right(),stable,visited,allowrecursion);
9428 return r1&&r2;
9429 }
9430
9431 if (is_if_then(t))
9432 {
9433 return canterminatebody(if_then(t).then_case(),stable,visited,allowrecursion);
9434 }
9435
9436 if (is_if_then_else(t))
9437 {
9438 const bool r1=canterminatebody(if_then_else(t).then_case(),stable,visited,allowrecursion);
9439 const bool r2=canterminatebody(if_then_else(t).else_case(),stable,visited,allowrecursion);
9440 return r1||r2;
9441 }
9442
9443 if (is_sum(t))
9444 {
9445 return (canterminatebody(sum(t).operand(),stable,visited,allowrecursion));
9446 }
9447
9449 {
9450 return (canterminatebody(stochastic_operator(t).operand(),stable,visited,allowrecursion));
9451 }
9452
9453 if (is_action(t))
9454 {
9455 return true;
9456 }
9457
9458 if (is_delta(t))
9459 {
9460 return false;
9461 }
9462
9463 if (is_tau(t))
9464 {
9465 return true;
9466 }
9467
9468 if (is_at(t))
9469 {
9470 return canterminatebody(at(t).operand(),stable,visited,allowrecursion);
9471 }
9472
9473 if (is_sync(t))
9474 {
9475 const bool r1=canterminatebody(process::sync(t).left(),stable,visited,allowrecursion);
9476 const bool r2=canterminatebody(process::sync(t).right(),stable,visited,allowrecursion);
9477 return r1&&r2;
9478 }
9479
9480 throw mcrl2::runtime_error("unexpected process format in canterminate " + process::pp(t) +".");
9481 return false;
9482 }
9483
9485 const process_identifier& procId,
9486 bool& stable,
9487 std::set < process_identifier >& visited)
9488 {
9489 objectdatatype& object=objectIndex(procId);
9490
9491 if (visited.count(procId)==0)
9492 {
9493 visited.insert(procId);
9494 const bool ct=canterminatebody(object.processbody,stable,visited,true);
9495 if (object.canterminate!=ct)
9496 {
9497 object.canterminate=ct;
9498 stable=false;
9499 }
9500 }
9501 return (object.canterminate);
9502 }
9503
9505 {
9506 std::set < process_identifier > visited;
9507 bool stable=false;
9508 return canterminatebody(t,stable,visited,false);
9509 }
9510
9512 {
9513 bool stable=false;
9514 while (!stable)
9515 {
9516 std::set < process_identifier > visited;
9517 stable=true;
9518 canterminate_rec(procId,stable,visited);
9519 }
9520 }
9521
9522 /***** distinguishmCRLandpCRLprocsAndAddTerminatedAction ******/
9523
9525 std::map < process_identifier,process_identifier >& visited_id,
9526 std::map < process_expression,process_expression >& visited_proc)
9527 {
9528 if (visited_id.count(procId)>0)
9529 {
9530 return visited_id[procId];
9531 }
9532
9533 objectdatatype& object=objectIndex(procId);
9534
9535 if ((object.processstatus!=mCRL) &&
9536 (object.canterminate==0))
9537 {
9538 /* no new process needs to be constructed */
9539 return procId;
9540 }
9541
9542 if (object.processstatus==mCRL)
9543 {
9544 visited_id[procId]=procId;
9545 object.processbody = split_body(object.processbody,
9546 visited_id,visited_proc,
9547 object.parameters);
9548 return procId;
9549 }
9550
9551 const process_identifier newProcId(
9552 fresh_identifier_generator(procId.name()),
9553 object.parameters);
9554 visited_id[procId]=newProcId;
9555
9556 if (object.canterminate)
9557 {
9560 newProcId,
9561 object.parameters,
9565 return newProcId;
9566 }
9567 return procId;
9568 }
9569
9570/* expand_process_instance_assignment takes a process instance assignment X(...) and
9571 * replaces it by Y if X=Y. This process is repeated until the right hand side of
9572 * an equation is not a process instance anymore */
9573
9576 std::set<process_identifier>& visited_processes)
9577 {
9578 if (visited_processes.count(t.identifier())>0)
9579 {
9580 throw mcrl2::runtime_error("Process " + pp(t.identifier()) + " is unguardedly defined in itself");
9581 };
9582
9583 visited_processes.insert(t.identifier());
9586 {
9587 const process_instance_assignment q=expand_process_instance_assignment(
9588 down_cast<process_instance_assignment>(object.processbody),
9589 visited_processes);
9590
9591 maintain_variables_in_rhs<mutable_map_substitution<> > sigma;
9593 for (const assignment& a: assign.assignments())
9594 {
9595 sigma[a.lhs()]=a.rhs();
9596 }
9597
9598 return down_cast<process_instance_assignment>(substitute_pCRLproc(q,sigma));
9599 }
9600 return t;
9601 }
9602
9604 {
9605 std::set<process_identifier> visited_processes;
9606 return expand_process_instance_assignment(t,visited_processes);
9607 }
9608
9609/* Transform process_arguments
9610 * This function replaces process_instances by process_instance_assignments.
9611 * All assignments in a process_instance_assignment are ordered in the same
9612 * sequence as the parameters belonging to that assignment.
9613 * All assignments in a process_instance_assignment of the form x=x where
9614 * x is not a bound variable are removed.
9615 * Furthermore, process occurrences X where X is defined as X=X1, X1=X2...Xn-1=Xn
9616 * are replaced by Xn, with the necessary substitutions for data parameters.
9617 * The reason for this is that only Xn will become a process in the linear
9618 * process, with its own state. Otherwise there is a risk that all process
9619 * variables X, X1, X2, etc. have separate states, and worse, for all of them
9620 * a copy of the summands will be added.
9621*/
9622
9623
9624 /* This function replaces all process instances by a process instance assignment,
9625 furthermore, if a process consists of only a process instantionation, i.e., X=Y, it is removed.
9626 Nested stochastic operators are merged into one. */
9628 const process_identifier& procId,
9629 std::set<process_identifier>& visited_processes)
9630 {
9631 if (visited_processes.count(procId)==0)
9632 {
9633 visited_processes.insert(procId);
9634 objectdatatype& object=objectIndex(procId);
9635 const std::set<variable> bound_variables;
9636 object.processbody=transform_process_arguments_body(
9637 object.processbody,
9638 bound_variables,
9639 visited_processes);
9640 }
9641 }
9642
9643 /* This function replaces all process instances by a process instance assignment and
9644 merges nested stochastic operators into one. */
9646 {
9647 std::set<process_identifier> visited_processes;
9648 transform_process_arguments(procId,visited_processes);
9649 }
9650
9651 /* This function replaces all process instances by a process instance assignment
9652 and merges nested stochastic operators into one. */
9654 const process_expression& t,
9655 const std::set<variable>& bound_variables,
9656 std::set<process_identifier>& visited_processes)
9657 {
9659 {
9660 transform_process_arguments(process_instance(t).identifier(),visited_processes);
9661 process_instance_assignment t1=transform_process_instance_to_process_instance_assignment(atermpp::down_cast<process_instance>(t),bound_variables);
9663 }
9665 {
9666 transform_process_arguments(process_instance_assignment(t).identifier(),visited_processes);
9669 assert(check_valid_process_instance_assignment(u.identifier(),
9670 sort_assignments(u.assignments(),object.parameters)));
9671 process_instance_assignment t1(u.identifier(),
9672 sort_assignments(u.assignments(),object.parameters));
9674 }
9675 if (is_hide(t))
9676 {
9677 return hide(hide(t).hide_set(),
9678 transform_process_arguments_body(hide(t).operand(),bound_variables,visited_processes));
9679 }
9680 if (is_rename(t))
9681 {
9682 return process::rename(
9683 process::rename(t).rename_set(),
9684 transform_process_arguments_body(process::rename(t).operand(),bound_variables,visited_processes));
9685 }
9686 if (is_allow(t))
9687 {
9688 return allow(allow(t).allow_set(),
9689 transform_process_arguments_body(allow(t).operand(),bound_variables,visited_processes));
9690 }
9691 if (is_block(t))
9692 {
9693 return block(block(t).block_set(),
9694 transform_process_arguments_body(block(t).operand(),bound_variables,visited_processes));
9695 }
9696 if (is_comm(t))
9697 {
9698 return comm(comm(t).comm_set(),
9699 transform_process_arguments_body(comm(t).operand(),bound_variables,visited_processes));
9700 }
9701 if (is_merge(t))
9702 {
9703 return merge(
9704 transform_process_arguments_body(merge(t).left(),bound_variables,visited_processes),
9705 transform_process_arguments_body(merge(t).right(),bound_variables,visited_processes));
9706 }
9707 if (is_choice(t))
9708 {
9709 return choice(
9710 transform_process_arguments_body(choice(t).left(),bound_variables,visited_processes),
9711 transform_process_arguments_body(choice(t).right(),bound_variables,visited_processes));
9712 }
9713 if (is_seq(t))
9714 {
9715 return seq(
9716 transform_process_arguments_body(seq(t).left(),bound_variables,visited_processes),
9717 transform_process_arguments_body(seq(t).right(),bound_variables,visited_processes));
9718 }
9719 if (is_if_then_else(t))
9720 {
9721 return if_then_else(
9722 if_then_else(t).condition(),
9723 transform_process_arguments_body(if_then_else(t).then_case(),bound_variables,visited_processes),
9724 transform_process_arguments_body(if_then_else(t).else_case(),bound_variables,visited_processes));
9725 }
9726 if (is_if_then(t))
9727 {
9728 return if_then(
9729 if_then(t).condition(),
9730 transform_process_arguments_body(if_then(t).then_case(),bound_variables,visited_processes));
9731 }
9732 if (is_sum(t))
9733 {
9734 std::set<variable> bound_variables1=bound_variables;
9735 const variable_list sum_vars=sum(t).variables();
9736 bound_variables1.insert(sum_vars.begin(),sum_vars.end());
9737 return sum(
9738 sum_vars,
9739 transform_process_arguments_body(sum(t).operand(),bound_variables1,visited_processes));
9740 }
9741 if (is_action(t))
9742 {
9743 return t;
9744 }
9745 if (is_delta(t))
9746 {
9747 return t;
9748 }
9749 if (is_tau(t))
9750 {
9751 return t;
9752 }
9753 if (is_at(t))
9754 {
9755 return at(
9756 transform_process_arguments_body(at(t).operand(),bound_variables,visited_processes),
9757 at(t).time_stamp());
9758 }
9759 if (is_sync(t))
9760 {
9761 return process::sync(
9762 transform_process_arguments_body(process::sync(t).left(),bound_variables,visited_processes),
9763 transform_process_arguments_body(process::sync(t).right(),bound_variables,visited_processes));
9764 }
9766 {
9767 const stochastic_operator& tso=down_cast<const stochastic_operator>(t);
9768 // Join nested stochastic operators into one.
9769 variable_list vars = tso.variables();
9772 while (is_stochastic_operator(body))
9773 {
9774 const stochastic_operator& tso1 = down_cast<const stochastic_operator>(body);
9775 vars = vars + tso1.variables();
9777 body = tso1.operand();
9778 }
9779 return stochastic_operator(
9780 vars,
9781 dist,
9782 transform_process_arguments_body(body,bound_variables,visited_processes));
9783 }
9784 throw mcrl2::runtime_error("unexpected process format in transform_process_arguments_body " + process::pp(t) +".");
9785 }
9786
9787/* ----------------------------- split body --------------------------- */
9788
9790 const process_expression& t,
9791 std::map < process_identifier,process_identifier >& visited_id,
9792 std::map < process_expression,process_expression>& visited_proc,
9793 const variable_list& parameters)
9794 {
9795 /* Replace pCRL process terms that occur in the scope of mCRL processes
9796 by a process identifier. E.g. (a+b)||c is replaced by X||c and
9797 a new process equation X=a+b is added. Furthermore, if the replaced
9798 process can terminate a termination action is put behind it.
9799 In the example X=(a+b).terminate.delta@0.
9800
9801 Besides this each ProcessAssignment is transformed into a Process. */
9802
9803 process_expression result;
9804
9805 if (visited_proc.count(t)>0)
9806 {
9807 return visited_proc[t];
9808 }
9809
9810 if (is_merge(t))
9811 {
9812 result=process::merge(
9813 split_body(process::merge(t).left(),visited_id,visited_proc,parameters),
9814 split_body(process::merge(t).right(),visited_id,visited_proc,parameters));
9815 }
9817 {
9820 assert(check_valid_process_instance_assignment(split_process(u.identifier(),visited_id,visited_proc),
9821 sort_assignments(u.assignments(),object.parameters)));
9822 result=process_instance_assignment(
9823 split_process(u.identifier(),visited_id,visited_proc),
9824 sort_assignments(u.assignments(),object.parameters));
9825 }
9826 else if (is_hide(t))
9827 {
9828 result=hide(hide(t).hide_set(),
9829 split_body(hide(t).operand(),visited_id,visited_proc,parameters));
9830 }
9831 else if (is_rename(t))
9832 {
9833 result=process::rename(
9834 process::rename(t).rename_set(),
9835 split_body(process::rename(t).operand(),visited_id,visited_proc,parameters));
9836 }
9837 else if (is_allow(t))
9838 {
9839 result=allow(allow(t).allow_set(),
9840 split_body(allow(t).operand(),visited_id,visited_proc,parameters));
9841 }
9842 else if (is_block(t))
9843 {
9844 result=block(block(t).block_set(),
9845 split_body(block(t).operand(),visited_id,visited_proc,parameters));
9846 }
9847 else if (is_comm(t))
9848 {
9849 result=comm(comm(t).comm_set(),
9850 split_body(comm(t).operand(),visited_id,visited_proc,parameters));
9851 }
9852 else if (is_stochastic_operator(t))
9853 {
9854 const stochastic_operator& t1 = atermpp::down_cast<stochastic_operator>(t);
9855 result=stochastic_operator(t1.variables(),
9856 t1.distribution(),
9857 split_body(t1.operand(),visited_id,visited_proc,parameters));
9858 }
9859 else if (is_choice(t)||
9860 is_seq(t)||
9862 is_if_then(t)||
9863 is_sum(t)||
9864 is_action(t)||
9865 is_delta(t)||
9866 is_tau(t)||
9867 is_at(t)||
9868 is_sync(t))
9869 {
9870 if (canterminatebody(t))
9871 {
9873 const process_identifier p=newprocess(parameters,
9875 pCRL,
9876 false,
9878 assert(check_valid_process_instance_assignment(p,assignment_list()));
9879 result=process_instance_assignment(p,assignment_list());
9880 visited_proc[t]=result;
9881 }
9882 else
9883 {
9884 const process_identifier p=newprocess(parameters,t,pCRL,false,containstimebody(t));
9885 assert(check_valid_process_instance_assignment(p,assignment_list()));
9886 result=process_instance_assignment(p,assignment_list());
9887 visited_proc[t]=result;
9888 }
9889 }
9890 else
9891 {
9892 throw mcrl2::runtime_error("unexpected process format in split process " + process::pp(t) +".");
9893 }
9894
9895 return result;
9896 }
9897
9899 const process_identifier& procId)
9900 {
9901 std::map < process_identifier,process_identifier> visited_id;
9902 std::map < process_expression,process_expression> visited_proc;
9903 return split_process(procId,visited_id,visited_proc);
9904 }
9905
9906 /**************** AddTerminationActionIfNecessary ****************/
9907
9908 void AddTerminationActionIfNecessary(const stochastic_action_summand_vector& summands)
9909 {
9910 for (const stochastic_action_summand& smd: summands)
9911 {
9912 const action_list multiaction=smd.multi_action().actions();
9913 if (multiaction == action_list({ terminationAction }))
9914 {
9915 acts.push_front(terminationAction.label());
9916 mCRL2log(mcrl2::log::log_level_t::warning) << "The action " << process::pp(terminationAction) <<
9917 " followed by a deadlock is added to signal termination of the linear process. \n";
9918 return;
9919 }
9920 }
9921 }
9922
9923 public:
9924 /********************** SieveProcDataVars ***********************/
9925
9927 const std::set <variable>& vars,
9928 const stochastic_action_summand_vector& action_summands,
9929 const deadlock_summand_vector& deadlock_summands,
9930 const variable_list& parameters)
9931 {
9932 /* In this routine it is checked which free variables
9933 in vars occur in the summands. Those variables
9934 that occur in the summands are returned. The
9935 parameters are needed to check occurrences of vars
9936 in the assignment list */
9937
9938 std::set < variable > vars_set(vars.begin(),vars.end());
9939 std::set < variable > vars_result_set;
9940
9941 for (const deadlock_summand& smd: deadlock_summands)
9942 {
9943 if (smd.deadlock().has_time())
9944 {
9945 filter_vars_by_term(smd.deadlock().time(),vars_set,vars_result_set);
9946 }
9947 filter_vars_by_term(smd.condition(),vars_set,vars_result_set);
9948 }
9949 for (const stochastic_action_summand& smd: action_summands)
9950 {
9951 filter_vars_by_multiaction(smd.multi_action().actions(),vars_set,vars_result_set);
9952 filter_vars_by_assignmentlist(smd.assignments(),parameters,vars_set,vars_result_set);
9953
9954 if (smd.multi_action().has_time())
9955 {
9956 filter_vars_by_term(smd.multi_action().time(),vars_set,vars_result_set);
9957 }
9958 filter_vars_by_term(smd.condition(),vars_set,vars_result_set);
9959 filter_vars_by_term(smd.distribution().distribution(),vars_set,vars_result_set);
9960 }
9961 variable_list result;
9962 for (const mcrl2::data::variable& i: std::ranges::reverse_view(vars_result_set))
9963 {
9964 result.push_front(i);
9965 }
9966
9967 return result;
9968 }
9969
9970 public:
9972 const std::set <variable>& vars,
9973 const data_expression_list& initial_state_expressions)
9974 {
9975 const std::set < variable > vars_set(vars.begin(),vars.end());
9976 std::set < variable > vars_result_set;
9977
9978
9979 filter_vars_by_termlist(initial_state_expressions.begin(),
9980 initial_state_expressions.end(),
9981 vars_set,
9982 vars_result_set);
9983
9984 variable_list result;
9985 for (const mcrl2::data::variable& i: std::ranges::reverse_view(vars_result_set))
9986 {
9987 result.push_front(i);
9988 }
9989
9990 return result;
9991 }
9992
9993 /**************** transform **************************************/
9994 public:
9996 const process_identifier& init,
9997 stochastic_action_summand_vector& action_summands,
9998 deadlock_summand_vector& deadlock_summands,
9999 variable_list& parameters,
10000 data_expression_list& initial_state,
10001 stochastic_distribution& initial_stochastic_distribution)
10002 {
10003 /* Then select the BPA processes, and check that the others
10004 are proper parallel processes */
10005 transform_process_arguments(init); // Also merges nested stochastic operators.
10010
10011 std::vector <process_identifier> pcrlprocesslist;
10012 collectPcrlProcesses(init_,pcrlprocesslist);
10013
10014 if (pcrlprocesslist.size()==0)
10015 {
10016 throw mcrl2::runtime_error("There are no processes to be linearised. This is most likely due to the use of unguarded recursion in process equations");
10017 // Note that this can occur with a specification
10018 // proc P(x:Int) = P(x); init P(1);
10019 }
10020
10021 /* Second, transform into GNF with possibly variables as a head,
10022 but no actions in the tail */
10023 procstovarheadGNF(pcrlprocesslist);
10024
10025 /* Third, transform to GNF by subsitution, such that the
10026 first variable in a sequence is always an actionvariable */
10027 procstorealGNF(init_,options.lin_method!=t_lin_method::lmStack);
10028
10029 lps::detail::ultimate_delay dummy_ultimate_delay_condition;
10030 generateLPEmCRL(action_summands,
10031 deadlock_summands,
10032 init_,
10033 options.lin_method!=t_lin_method::lmStack,
10034 parameters,
10035 initial_state,
10036 initial_stochastic_distribution,
10037 dummy_ultimate_delay_condition);
10038 allowblockcomposition(action_name_multiset_list({action_name_multiset()}),false,action_summands,deadlock_summands,terminationAction,options.ignore_time,options.nodeltaelimination); // This removes superfluous delta summands.
10039 if (options.final_cluster)
10040 {
10041 cluster_actions(action_summands,deadlock_summands,parameters);
10042 }
10043
10044 AddTerminationActionIfNecessary(action_summands);
10045 }
10046
10047}; // End of the class specification basictype
10048
10049/**************** linearise **************************************/
10050
10052 const mcrl2::process::process_specification& type_checked_spec,
10053 const mcrl2::lps::t_lin_options& lin_options)
10054{
10055 mCRL2log(mcrl2::log::log_level_t::verbose) << "linearising the process specification using the '" << lin_options.lin_method << "' method.\n";
10056 mcrl2::process::process_specification input_process=type_checked_spec;
10057 data_specification data_spec=input_process.data();
10058
10059 if (lin_options.balance_summands) // Make a balanced tree of long expressions of the shape p1 + p2 + p3 + ... + p4.
10060 // By default the parser provides a skewed tree, and for very long sequences of summands this overflows the
10061 // stack.
10062 {
10063 balance_summands(input_process);
10064 }
10065
10066 if (lin_options.apply_alphabet_axioms) // Apply alphabet reduction if requested.
10067 {
10068 alphabet_reduce(input_process, 1000ul);
10069 }
10070
10071 std::set<data::sort_expression> s;
10072 process::find_sort_expressions(input_process.action_labels(), std::inserter(s, s.end()));
10073 process::find_sort_expressions(input_process.equations(), std::inserter(s, s.end()));
10074 process::find_sort_expressions(input_process.init(), std::inserter(s, s.end()));
10075 s.insert(sort_real::real_());
10076 data_spec.add_context_sorts(s);
10077
10079 input_process.equations(),
10080 data::variable_list(input_process.global_variables().begin(),input_process.global_variables().end()),
10081 data_spec,
10082 input_process.global_variables(),
10083 lin_options,
10084 input_process);
10085 process_identifier init=spec.storeinit(input_process.init());
10086
10087 //linearise spec
10088 variable_list parameters;
10089 data_expression_list initial_state;
10090 stochastic_action_summand_vector action_summands;
10091 deadlock_summand_vector deadlock_summands;
10092 stochastic_distribution initial_distribution(
10093 variable_list(),
10095 spec.transform(init,action_summands,deadlock_summands,parameters,initial_state,initial_distribution);
10096
10097 // compute global variables
10098 data::variable_list globals1 = spec.SieveProcDataVarsSummands(spec.global_variables,action_summands,deadlock_summands,parameters);
10099 data::variable_list globals2 = spec.SieveProcDataVarsAssignments(spec.global_variables,initial_state);
10100 std::set<data::variable> global_variables;
10101 global_variables.insert(globals1.begin(), globals1.end());
10102 global_variables.insert(globals2.begin(), globals2.end());
10103 stochastic_linear_process lps(parameters,
10104 deadlock_summands,
10105 action_summands);
10107 spec.data,
10108 spec.acts,
10109 global_variables,
10110 lps,
10111 stochastic_process_initializer(initial_state,initial_distribution));
10112
10113 // add missing sorts to the data specification
10115 return spec1;
10116}
aterm_string & operator=(const aterm_string &t) noexcept=default
A list of aterm objects.
Definition aterm_list.h:26
A unordered_map class in which aterms can be stored.
An abstraction expression.
Definition abstraction.h:23
const variable_list & variables() const
Definition abstraction.h:60
const data_expression & body() const
Definition abstraction.h:65
const binder_type & binding_operator() const
Definition abstraction.h:55
\brief A sort alias
Definition alias.h:23
alias(const basic_sort &name, const sort_expression &reference)
\brief Constructor Z12.
Definition alias.h:39
\brief Assignment of a data expression to a variable
Definition assignment.h:88
const data_expression & rhs() const
Definition assignment.h:119
const variable & lhs() const
Definition assignment.h:114
\brief A basic sort
Definition basic_sort.h:25
data_expression & operator=(const data_expression &) noexcept=default
data_expression()
\brief Default constructor X3.
data_expression & operator=(data_expression &&) noexcept=default
data_expression(const data_expression &) noexcept=default
Move semantics.
\brief A function sort
\brief A function symbol
function_symbol & operator=(function_symbol &&) noexcept=default
function_symbol()
Default constructor.
Rewriter that operates on data expressions.
Definition rewriter.h:84
Identifier generator that stores the identifiers of the context in a set. Using the operator()() and ...
\brief A sort expression
sort_expression & operator=(const sort_expression &) noexcept=default
\brief A constructor for a structured sort
function_symbol constructor_function(const sort_expression &s) const
Returns the constructor function for this constructor, assuming it is internally represented with sor...
function_symbol recogniser_function(const sort_expression &s) const
Returns the function corresponding to the recogniser of this constructor, such that it is usable in t...
\brief A data variable
Definition variable.h:25
const core::identifier_string & name() const
Definition variable.h:35
variable & operator=(variable &&) noexcept=default
const sort_expression & sort() const
Definition variable.h:40
\brief A where expression
where_clause(const atermpp::aterm &term)
bool has_time() const
Returns true if time is available.
Algorithm class for elimination of constant parameters.
Definition constelm.h:23
LPS summand containing a deadlock.
data_expression & constraint()
Obtain a reference to the constraint.
LPS summand containing a multi-action.
\brief A stochastic distribution
stochastic_distribution & operator=(stochastic_distribution &&) noexcept=default
stochastic_distribution()
\brief Default constructor X3.
const data::variable_list & variables() const
stochastic_distribution(const data::variable_list &variables, const data::data_expression &distribution)
\brief Constructor Z12.
const data::data_expression & distribution() const
stochastic_process_initializer(const data::data_expression_list &expressions, const stochastic_distribution &distribution)
Constructor.
\brief An action label
const core::identifier_string & name() const
action(const atermpp::aterm &term)
const data::data_expression_list & arguments() const
const action_label & label() const
\brief The allow operator
allow(const atermpp::aterm &term)
const process_expression & operand() const
\brief The at operator
at(const atermpp::aterm &term)
at(const process_expression &operand, const data::data_expression &time_stamp)
\brief Constructor Z14.
const data::data_expression & time_stamp() const
const process_expression & operand() const
\brief The block operator
const process_expression & operand() const
block(const atermpp::aterm &term)
\brief The choice operator
choice(const atermpp::aterm &term)
const process_expression & left() const
choice(const process_expression &left, const process_expression &right)
\brief Constructor Z14.
const process_expression & right() const
\brief The communication operator
comm(const atermpp::aterm &term)
const process_expression & operand() const
\brief The value delta
delta()
\brief Default constructor X3.
\brief The hide operator
hide(const atermpp::aterm &term)
const process_expression & operand() const
\brief The if-then-else operator
const process_expression & else_case() const
const process_expression & then_case() const
if_then_else(const atermpp::aterm &term)
const data::data_expression & condition() const
if_then_else(const data::data_expression &condition, const process_expression &then_case, const process_expression &else_case)
\brief Constructor Z14.
\brief The if-then operator
const process_expression & then_case() const
if_then(const data::data_expression &condition, const process_expression &then_case)
\brief Constructor Z14.
if_then(const atermpp::aterm &term)
const data::data_expression & condition() const
\brief The merge operator
merge(const atermpp::aterm &term)
const process_expression & right() const
const process_expression & left() const
\brief A process expression
process_expression & operator=(const process_expression &) noexcept=default
process_expression(const process_expression &) noexcept=default
Move semantics.
process_expression()
\brief Default constructor X3.
process_expression & operator=(process_expression &&) noexcept=default
\brief A process identifier
process_identifier(const process_identifier &) noexcept=default
Move semantics.
process_identifier & operator=(const process_identifier &) noexcept=default
const core::identifier_string & name() const
process_identifier & operator=(process_identifier &&) noexcept=default
process_instance_assignment(const process_instance_assignment &) noexcept=default
Move semantics.
process_instance_assignment(const process_identifier &identifier, const data::assignment_list &assignments)
\brief Constructor Z14.
const data::assignment_list & assignments() const
process_instance_assignment(const atermpp::aterm &term)
const process_identifier & identifier() const
const data::data_expression_list & actual_parameters() const
const process_identifier & identifier() const
Process specification consisting of a data specification, action labels, a sequence of process equati...
process_expression & init()
Returns the initialization of the process specification.
process::action_label_list & action_labels()
Returns the action label specification.
\brief The rename operator
rename(const atermpp::aterm &term)
const process_expression & operand() const
\brief The sequential composition
const process_expression & right() const
seq(const atermpp::aterm &term)
const process_expression & left() const
seq(const process_expression &left, const process_expression &right)
\brief Constructor Z14.
\brief The distribution operator
const data::variable_list & variables() const
const data::data_expression & distribution() const
const process_expression & operand() const
stochastic_operator(const data::variable_list &variables, const data::data_expression &distribution, const process_expression &operand)
\brief Constructor Z14.
\brief The sum operator
const process_expression & operand() const
sum(const data::variable_list &variables, const process_expression &operand)
\brief Constructor Z14.
sum(const atermpp::aterm &term)
const data::variable_list & variables() const
\brief The synchronization operator
const process_expression & left() const
sync(const process_expression &left, const process_expression &right)
\brief Constructor Z14.
const process_expression & right() const
sync(const atermpp::aterm &term)
\brief The value tau
tau()
\brief Default constructor X3.
process_expression processbody
objectdatatype(const objectdatatype &o)=default
process_expression representedprocess
~objectdatatype()=default
process::action_label_list multi_action_names
processstatustype processstatus
identifier_string objectname
std::set< variable > get_free_variables() const
objectdatatype()=default
objectdatatype & operator=(const objectdatatype &o)=default
objecttype object
process_identifier process_representing_action
variable_list parameters
enumeratedtype(const enumeratedtype &e)
enumeratedtype(const std::size_t n, specification_basic_type &spec)
enumeratedtype & operator=(const enumeratedtype &e)=default
enumtype(const enumtype &)=delete
enumtype & operator=(const enumtype &)=delete
enumtype(std::size_t n, const sort_expression_list &fsorts, const sort_expression_list &gsorts, specification_basic_type &spec)
process_pid_pair & operator=(const process_pid_pair &other)=default
const process_expression & process_body() const
const process_identifier & process_id() const
process_pid_pair(const process_pid_pair &other)=default
process_pid_pair & operator=(process_pid_pair &&other)=default
process_pid_pair(const process_expression &process_body, const process_identifier &pid)
process_pid_pair(process_pid_pair &&other)=default
static stackoperations * find_suitable_stack_operations(const variable_list &parameters, stackoperations *stack_operations_list)
stacklisttype & operator=(const stacklisttype &)=delete
stacklisttype(const variable_list &parlist, specification_basic_type &spec, const bool regular, const std::set< process_identifier > &pCRLprocs, const bool singlecontrolstate)
Constructor.
stacklisttype(const stacklisttype &)=delete
stackoperations & operator=(const stackoperations &)=delete
stackoperations(const stackoperations &)=delete
stackoperations(const variable_list &pl, specification_basic_type &spec)
process_expression procstorealGNFbody(const process_expression &body, variableposition v, std::vector< process_identifier > &todo, const bool regular, processstatustype mode, const variable_list &freevars, const std::set< variable > &variables_bound_in_sum)
data_expression construct_binary_case_tree(std::size_t n, const variable_list &sums, data_expression_list terms, const sort_expression &termsort, const enumtype &e)
data::maintain_variables_in_rhs< data::mutable_map_substitution<> > make_unique_variables(const variable_list &var_list, const std::string &hint)
variable_list parscollect(const process_expression &oldbody, process_expression &newbody)
stochastic_action_summand collect_sum_arg_arg_cond(const enumtype &e, const stochastic_action_summand_vector &action_summands, const variable_list &parameters)
action_list linMergeMultiActionList(const action_list &ma1, const action_list &ma2)
void generateLPEpCRL(stochastic_action_summand_vector &action_summands, deadlock_summand_vector &deadlock_summands, const process_identifier &procId, const bool containstime, const bool regular, variable_list &parameters, data_expression_list &init, stochastic_distribution &initial_stochastic_distribution)
variable get_fresh_variable(const std::string &s, const sort_expression &sort, const int reuse_index=-1)
process_expression distributeActionOverConditions(const process_expression &act, const data_expression &condition, const process_expression &restterm, const variable_list &freevars, const std::set< variable > &variables_bound_in_sum)
data_expression construct_binary_case_tree_rec(std::size_t n, const variable_list &sums, data_expression_list &terms, const sort_expression &termsort, const enumtype &e)
static void complete_proc_identifier_map(std::map< process_identifier, process_identifier > &identifier_identifier_map)
process_expression to_regular_form(const process_expression &t, std::vector< process_identifier > &todo, const variable_list &freevars, const std::set< variable > &variables_bound_in_sum)
void collectsumlistterm(const process_identifier &procId, stochastic_action_summand_vector &action_summands, deadlock_summand_vector &deadlock_summands, const process_expression &body, const variable_list &pars, const stacklisttype &stack, const bool regular, const bool singlestate, const std::set< process_identifier > &pCRLprocs)
data_expression_list findarguments(const variable_list &pars, const variable_list &parlist, const assignment_list &args, const data_expression_list &t2, const stacklisttype &stack, const variable_list &vars, const std::set< variable > &free_variables_in_body, const variable_list &stochastic_variables)
void calculate_communication_merge(const stochastic_action_summand_vector &action_summands1, const deadlock_summand_vector &deadlock_summands1, const stochastic_action_summand_vector &action_summands2, const deadlock_summand_vector &deadlock_summands2, const action_name_multiset_list &allowlist, const bool is_allow, const bool is_block, stochastic_action_summand_vector &action_summands, deadlock_summand_vector &deadlock_summands)
void insertvariable(const variable &var, const bool mustbenew)
process_identifier storeinit(const process_expression &init)
static action_list to_sorted_action_list(const process_expression &p)
Convert the process expression to a sorted action list.
process_expression pCRLrewrite(const process_expression &t)
data_expression_list pushdummy_regular_data_expressions(const variable_list &pars, const stacklisttype &stack)
void define_equations_for_case_function(const std::size_t index, const data::function_symbol &functionname, const sort_expression &sort)
void alphaconvert(variable_list &sumvars, MutableSubstitution &sigma, const variable_list &occurvars, const data_expression_list &occurterms)
bool canterminatebody(const process_expression &t)
data_expression transform_matching_list(const variable_list &matchinglist)
void add_summands(const process_identifier &procId, stochastic_action_summand_vector &action_summands, deadlock_summand_vector &deadlock_summands, process_expression summandterm, const std::set< process_identifier > &pCRLprocs, const stacklisttype &stack, const bool regular, const bool singlestate, const variable_list &process_parameters)
bool isDeltaAtZero(const process_expression &t)
data::function_symbol find_case_function(std::size_t index, const sort_expression &sort) const
data_expression_list make_initialstate(const process_identifier &initialProcId, const stacklisttype &stack, const std::set< process_identifier > &pcrlprcs, const bool regular, const bool singlecontrolstate, const stochastic_distribution &initial_stochastic_distribution)
static bool summandsCanBeClustered(const stochastic_action_summand &summand1, const stochastic_action_summand &summand2)
static sort_expression_list getActionSorts(const action_list &actionlist)
void filter_vars_by_multiaction(const action_list &multiaction, const std::set< variable > &vars_set, std::set< variable > &vars_result_set)
static process_identifier get_last(const process_identifier &id, const std::map< process_identifier, process_identifier > &identifier_identifier_map)
static bool check_real_variable_occurrence(const variable_list &sumvars, const data_expression &actiontime, const data_expression &condition)
process_identifier newprocess(const variable_list &parameters, const process_expression &body, const processstatustype ps, const bool canterminate, const bool containstime)
void make_pCRL_procs(const process_identifier &id, std::set< process_identifier > &reachable_process_identifiers)
void filter_vars_by_term(const data_expression &t, const std::set< variable > &vars_set, std::set< variable > &vars_result_set)
data_expression_list pushdummy_stack(const variable_list &parameters, const stacklisttype &stack, const variable_list &stochastic_variables)
void procstorealGNFrec(const process_identifier &procIdDecl, const variableposition v, std::vector< process_identifier > &todo, const bool regular)
static action_label_list getnames(const process_expression &multiAction)
variable_list getparameters_rec(const process_expression &multiAction, std::set< variable > &occurs_set)
static int match_sequence(const std::vector< process_instance_assignment > &s1, const std::vector< process_instance_assignment > &s2, const bool regular2)
assignment_list argscollect_regular2(const process_expression &t, variable_list &vl)
assignment_list make_optimised_assignment_list(const variable_list &parameters, const data_expression_list &resultnextstate, const variable_list &sum_vars, const variable_list &stoch_vars)
void calculate_left_merge_action(const lps::detail::ultimate_delay &ultimate_delay_condition, const stochastic_action_summand_vector &action_summands1, const action_name_multiset_list &allowlist, const bool is_allow, const bool is_block, stochastic_action_summand_vector &action_summands)
process_expression split_body(const process_expression &t, std::map< process_identifier, process_identifier > &visited_id, std::map< process_expression, process_expression > &visited_proc, const variable_list &parameters)
void parallelcomposition(const stochastic_action_summand_vector &action_summands1, const deadlock_summand_vector &deadlock_summands1, const variable_list &pars1, const data_expression_list &init1, const stochastic_distribution &initial_stochastic_distribution1, const lps::detail::ultimate_delay &ultimate_delay_condition1, const stochastic_action_summand_vector &action_summands2, const deadlock_summand_vector &deadlock_summands2, const variable_list &pars2, const data_expression_list &init2, const stochastic_distribution &initial_stochastic_distribution2, const lps::detail::ultimate_delay &ultimate_delay_condition2, const action_name_multiset_list &allowlist1, const bool is_allow, const bool is_block, stochastic_action_summand_vector &action_summands, deadlock_summand_vector &deadlock_summands, variable_list &pars_result, data_expression_list &init_result, stochastic_distribution &initial_stochastic_distribution, lps::detail::ultimate_delay &ultimate_delay_condition)
bool occursintermlist(const variable &var, const assignment_list &r, const process_identifier &proc_name) const
void transform_process_arguments(const process_identifier &procId)
objectdatatype & insert_process_declaration(const process_identifier &procId, const variable_list &parameters, const process_expression &body, processstatustype s, const bool canterminate, const bool containstime)
static void set_proc_identifier_map(std::map< process_identifier, process_identifier > &identifier_identifier_map, const process_identifier &id1_, const process_identifier &id2_, const process_identifier &initial_process)
bool canterminate_rec(const process_identifier &procId, bool &stable, std::set< process_identifier > &visited)
set_identifier_generator fresh_identifier_generator
std::set< process_identifier > remove_stochastic_operators_from_front(const std::set< process_identifier > &reachable_process_identifiers, process_identifier &initial_process_id, stochastic_distribution &initial_stochastic_distribution)
void filter_vars_by_termlist(Iterator begin, const Iterator &end, const std::set< variable > &vars_set, std::set< variable > &vars_result_set)
bool searchProcDeclaration(const variable_list &parameters, const process_expression &body, const processstatustype s, const bool canterminate, const bool containstime, process_identifier &p) const
process_identifier splitmCRLandpCRLprocsAndAddTerminatedAction(const process_identifier &procId)
action_list linMergeMultiActionListProcess(const process_expression &ma1, const process_expression &ma2)
std::vector< process_equation > procs
action_list adapt_multiaction_to_stack(const action_list &multiAction, const stacklisttype &stack, const variable_list &vars)
void determinewhetherprocessescanterminate(const process_identifier &procId)
process_expression distribute_condition(const process_expression &body1, const data_expression &condition)
bool containstime_rec(const process_identifier &procId, bool *stable, std::set< process_identifier > &visited, bool &contains_if_then)
void collectsumlist(stochastic_action_summand_vector &action_summands, deadlock_summand_vector &deadlock_summands, const std::set< process_identifier > &pCRLprocs, const variable_list &pars, const stacklisttype &stack, bool regular, bool singlestate)
data_expression correctstatecond(const process_identifier &procId, const std::set< process_identifier > &pCRLproc, const stacklisttype &stack, int regular)
void calculate_communication_merge_action_summands(const stochastic_action_summand_vector &action_summands1, const stochastic_action_summand_vector &action_summands2, const action_name_multiset_list &allowlist, const bool is_allow, const bool is_block, stochastic_action_summand_vector &action_summands)
processstatustype determine_process_statusterm(const process_expression &body, const processstatustype status)
void calculate_communication_merge_action_deadlock_summands(const stochastic_action_summand_vector &action_summands1, const deadlock_summand_vector &deadlock_summands1, const stochastic_action_summand_vector &action_summands, deadlock_summand_vector &deadlock_summands)
data_expression getvar(const variable &var, const stacklisttype &stack) const
variable_list initdatavars
lps::detail::ultimate_delay combine_ultimate_delays(const lps::detail::ultimate_delay &delay1, const lps::detail::ultimate_delay &delay2)
Returns the conjunction of the two delay conditions and the join of the variables,...
process_expression distributeTime(const process_expression &body, const data_expression &time, const variable_list &freevars, data_expression &timecondition)
data_expression_list pushdummyrec_stack(const variable_list &totalpars, const variable_list &pars, const stacklisttype &stack, const variable_list &stochastic_variables)
static action_list to_action_list(const process_expression &p)
std::set< data::variable > sigma_variables(const Substitution &sigma)
void combine_summand_lists(const stochastic_action_summand_vector &action_summands1, const deadlock_summand_vector &deadlock_summands1, const lps::detail::ultimate_delay &ultimate_delay_condition1, const stochastic_action_summand_vector &action_summands2, const deadlock_summand_vector &deadlock_summands2, const lps::detail::ultimate_delay &ultimate_delay_condition2, const variable_list &par1, const variable_list &par3, const action_name_multiset_list &allowlist1, const bool is_allow, const bool is_block, stochastic_action_summand_vector &action_summands, deadlock_summand_vector &deadlock_summands)
process_expression cut_off_unreachable_tail(const process_expression &t)
std::vector< enumeratedtype > enumeratedtypes
data_expression find_(const variable &s, const assignment_list &args, const stacklisttype &stack, const variable_list &vars, const std::set< variable > &free_variables_in_body, const variable_list &stochastic_variables)
process::action_label_list acts
assignment_list make_procargs_regular(const process_expression &t, const stacklisttype &stack, const std::set< process_identifier > &pcrlprcs, const bool singlestate, const variable_list &stochastic_variables)
std::size_t create_enumeratedtype(const std::size_t n)
void generateLPEmCRL(stochastic_action_summand_vector &action_summands, deadlock_summand_vector &deadlock_summands, const process_identifier &procIdDecl, const bool regular, variable_list &pars, data_expression_list &init, stochastic_distribution &initial_stochastic_distribution, lps::detail::ultimate_delay &ultimate_delay_condition)
process_instance_assignment expand_process_instance_assignment(const process_instance_assignment &t)
static process_expression delta_at_zero()
assignment_list rewrite_assignments(const assignment_list &t)
assignment_list push_regular(const process_identifier &procId, const assignment_list &args, const stacklisttype &stack, const std::set< process_identifier > &pCRLprocs, bool singlestate, const variable_list &stochastic_variables)
void transform_process_arguments(const process_identifier &procId, std::set< process_identifier > &visited_processes)
std::set< process_identifier > minimize_set_of_reachable_process_identifiers(const std::set< process_identifier > &reachable_process_identifiers, const process_identifier &initial_process)
void collectPcrlProcesses(const process_identifier &procDecl, std::vector< process_identifier > &pcrlprocesses, std::set< process_identifier > &visited)
variable_list make_binary_sums(std::size_t n, const sort_expression &enumtypename, data_expression &condition, const variable_list &tail)
variable_list SieveProcDataVarsAssignments(const std::set< variable > &vars, const data_expression_list &initial_state_expressions)
data_expression_list extend_conditions(const variable &var, const data_expression_list &conditionlist)
bool check_valid_process_instance_assignment(const process_identifier &id, const assignment_list &assignments)
void calculate_left_merge_deadlock(const lps::detail::ultimate_delay &ultimate_delay_condition, const deadlock_summand_vector &deadlock_summands1, const bool is_allow, const bool is_block, const stochastic_action_summand_vector &action_summands, deadlock_summand_vector &deadlock_summands)
void addString(const identifier_string &str)
void procstovarheadGNF(const std::vector< process_identifier > &procs)
static process_expression action_list_to_process(const action_list &ma)
process_expression distribute_sum_over_a_stochastic_operator(const variable_list &sumvars, const variable_list &stochastic_variables, const data_expression &distribution, const process_expression &body)
void alphaconvertprocess(variable_list &sumvars, MutableSubstitution &sigma, const process_expression &p)
process_expression obtain_initial_distribution_term(const process_expression &t)
data_expression push_stack(const process_identifier &procId, const assignment_list &args, const data_expression_list &t2, const stacklisttype &stack, const std::set< process_identifier > &pCRLprocs, const variable_list &vars, const variable_list &stochastic_variables)
void calculate_left_merge(const stochastic_action_summand_vector &action_summands1, const deadlock_summand_vector &deadlock_summands1, const lps::detail::ultimate_delay &ultimate_delay_condition2, const action_name_multiset_list &allowlist, const bool is_allow, const bool is_block, stochastic_action_summand_vector &action_summands, deadlock_summand_vector &deadlock_summands)
void declare_control_state(const std::set< process_identifier > &pCRLprocs)
void create_case_function_on_enumeratedtype(const sort_expression &sort, const std::size_t enumeratedtype_index)
variable_list SieveProcDataVarsSummands(const std::set< variable > &vars, const stochastic_action_summand_vector &action_summands, const deadlock_summand_vector &deadlock_summands, const variable_list &parameters)
static data_expression_list extend(const data_expression &c, const data_expression_list &cl)
specification_basic_type & operator=(const specification_basic_type &)=delete
static bool occursinvarandremove(const variable &var, variable_list &vl)
process_expression bodytovarheadGNF(const process_expression &body, const state s, const variable_list &freevars, const variableposition v, const std::set< variable > &variables_bound_in_sum)
process_identifier terminatedProcId
process_expression distribute_sum(const variable_list &sumvars, const process_expression &body1)
data_expression variables_are_equal_to_default_values(const variable_list &vl)
void procstorealGNF(const process_identifier &procsIdDecl, const bool regular)
static bool check_assignment_list(const assignment_list &assignments, const variable_list &parameters)
data_expression RewriteTerm(const data_expression &t)
void alphaconversion(const process_identifier &procId, const variable_list &parameters)
bool all_equal(const atermpp::term_list< T > &l)
void cluster_actions(stochastic_action_summand_vector &action_summands, deadlock_summand_vector &deadlock_summands, const variable_list &pars)
process_expression transform_initial_distribution_term(const process_expression &t, const std::map< process_identifier, process_pid_pair > &processes_with_initial_distribution)
process_expression create_regular_invocation(process_expression sequence, std::vector< process_identifier > &todo, const variable_list &freevars, const std::set< variable > &variables_bound_in_sum)
process_expression transform_process_arguments_body(const process_expression &t, const std::set< variable > &bound_variables, std::set< process_identifier > &visited_processes)
static assignment_list parameters_to_assignment_list(const variable_list &parameters, const std::set< variable > &variables_bound_in_sum)
assignment_list dummyparameterlist(const stacklisttype &stack, const bool singlestate)
mcrl2::data::rewriter rewr
void make_pCRL_procs(const process_expression &t, std::set< process_identifier > &reachable_process_identifiers)
stackoperations * stack_operations_list
process_expression wraptime(const process_expression &body, const data_expression &time, const variable_list &freevars)
objectdatatype & objectIndex(const process_identifier &o)
process_identifier split_process(const process_identifier &procId, std::map< process_identifier, process_identifier > &visited_id, std::map< process_expression, process_expression > &visited_proc)
bool mergeoccursin(variable &var, const variable_list &v, variable_list &matchinglist, variable_list &pars, data_expression_list &args, const variable_list &process_parameters)
static bool occursintermlist(const variable &var, const data_expression_list &r)
bool exists_variable_for_sequence(const std::vector< process_instance_assignment > &process_names, process_identifier &result)
std::set< variable > find_free_variables_process(const process_expression &p)
process_expression alphaconversionterm(const process_expression &t, const variable_list &parameters, maintain_variables_in_rhs< mutable_map_substitution<> > sigma)
process_expression putbehind(const process_expression &body1, const process_expression &body2)
assignment_list substitute_assignmentlist(const assignment_list &assignments, const variable_list &parameters, const bool replacelhs, const bool replacerhs, Substitution &sigma)
process_instance_assignment transform_process_instance_to_process_instance_assignment(const process_instance &procId, const std::set< variable > &bound_variables=std::set< variable >())
process_instance_assignment RewriteProcess(const process_instance_assignment &t)
process_identifier delta_process
const objectdatatype & objectIndex(const process_identifier &o) const
void insert_summand(stochastic_action_summand_vector &action_summands, deadlock_summand_vector &deadlock_summands, const variable_list &sumvars, const data_expression &condition, const action_list &multiAction, const data_expression &actTime, const stochastic_distribution &distribution, const assignment_list &procargs, const bool has_time, const bool is_deadlock_summand)
bool alreadypresent(variable &var, const variable_list &vl, mutable_indexed_substitution<> &parameter_renaming)
variable_list parameters_that_occur_in_body(const variable_list &parameters, const process_expression &body)
process_expression enumerate_distribution_and_sums(const variable_list &sumvars, const variable_list &stochvars, const data_expression &distribution, const process_expression &body)
bool containstimebody(const process_expression &t)
assignment_list make_procargs(const process_expression &t, const stacklisttype &stack, const std::set< process_identifier > &pcrlprcs, const variable_list &vars, const bool regular, const bool singlestate, const variable_list &stochastic_variables)
data_expression adapt_term_to_stack(const data_expression &t, const stacklisttype &stack, const variable_list &vars, const variable_list &stochastic_variables)
data_expression_list processencoding(std::size_t i, const data_expression_list &t1, const stacklisttype &stack)
process_expression RewriteMultAct(const process_expression &t)
void generateLPEmCRLterm(stochastic_action_summand_vector &action_summands, deadlock_summand_vector &deadlock_summands, const process_expression &t, const bool regular, const bool rename_variables, variable_list &pars, data_expression_list &init, stochastic_distribution &initial_stochastic_distribution, lps::detail::ultimate_delay &ultimate_delay_condition)
Linearise a process indicated by procIdDecl.
variable_list make_pars(const sort_expression_list &sortlist)
specification_basic_type(const specification_basic_type &)=delete
static data_expression real_times_optimized(const data_expression &r1, const data_expression &r2)
bool occursinpCRLterm(const variable &var, const process_expression &p, const bool strict)
void collectPcrlProcesses(const process_identifier &procDecl, std::vector< process_identifier > &pcrlprocesses)
static std::size_t upperpowerof2(std::size_t i)
void extract_names(const process_expression &sequence, std::vector< process_instance_assignment > &result)
std::map< aterm, objectdatatype > objectdata
action RewriteAction(const action &t)
data_expression_vector adapt_termlist_to_stack(Iterator begin, const Iterator &end, const stacklisttype &stack, const variable_list &vars, const variable_list &stochastic_variables)
data_expression make_procargs_stack(const process_expression &t, const stacklisttype &stack, const std::set< process_identifier > &pcrlprcs, const variable_list &vars, const variable_list &stochastic_variables)
specification_basic_type(const process::action_label_list &as, const std::vector< process_equation > &ps, const variable_list &idvs, const data_specification &ds, const std::set< data::variable > &glob_vars, const t_lin_options &opt, const process_specification &procspec)
variable_list getparameters(const process_expression &multiAction)
data_expression makesingleultimatedelaycondition(const variable_list &sumvars, const variable_list &freevars, const data_expression &condition, const bool has_time, const variable &timevariable, const data_expression &actiontime, variable_list &used_sumvars)
void collectPcrlProcesses_term(const process_expression &body, std::vector< process_identifier > &pcrlprocesses, std::set< process_identifier > &visited)
data_expression representative_generator_internal(const sort_expression &s, const bool allow_dont_care_var=true)
assignment_list processencoding(std::size_t i, const assignment_list &t1, const stacklisttype &stack)
objectdatatype & addMultiAction(const process_expression &multiAction, bool &isnew)
void storeprocs(const std::vector< process_equation > &procs)
process_expression substitute_pCRLproc(const process_expression &p, Substitution &sigma)
void make_parameters_and_sum_variables_unique(stochastic_action_summand_vector &action_summands, deadlock_summand_vector &deadlock_summands, variable_list &pars, lps::detail::ultimate_delay &ultimate_delay_condition, const std::string &hint="")
std::set< variable > global_variables
static data_expression getRHSassignment(const variable &var, const assignment_list &as)
variable_list construct_renaming(const variable_list &pars1, const variable_list &pars2, variable_list &pars3, variable_list &pars4, const bool unique=true)
void determine_process_status(const process_identifier &procDecl, const processstatustype status)
static action_list makemultiaction(const process::action_label_list &actionIds, const data_expression_list &args)
void insertvariables(const variable_list &vars, const bool mustbenew)
variable_list collectparameterlist(std::set< process_identifier > &pCRLprocs)
data_expression_list RewriteTermList(const data_expression_list &t)
data_specification data
variable_list make_parameters_rec(const data_expression_list &l, std::set< variable > &occurs_set)
void detail_check_objectdata(const process_identifier &o) const
static assignment_list filter_assignments(const assignment_list &assignments, const variable_list &parameters)
variable_list merge_var(const variable_list &v1, const variable_list &v2, std::vector< variable_list > &renamings_pars, std::vector< data_expression_list > &renamings_args, data_expression_list &conditionlist, const variable_list &process_parameters)
bool containstimebody(const process_expression &t, bool *stable, std::set< process_identifier > &visited, bool allowrecursion, bool &contains_if_then)
static assignment_list sort_assignments(const assignment_list &ass, const variable_list &parameters)
assignment_list find_dummy_arguments(const variable_list &parlist, const assignment_list &args, const std::set< variable > &free_variables_in_body, const variable_list &stochastic_variables)
process_identifier tau_process
static sort_expression_list get_sorts(const List &l)
std::vector< process_identifier > seq_varnames
void storeact(const process::action_label_list &acts)
bool is_global_variable(const data_expression &d) const
variable_list joinparameters(const variable_list &par1, const variable_list &par2, mutable_indexed_substitution<> &parameter_renaming)
static bool occursin(const variable &name, const variable_list &pars)
bool canterminatebody(const process_expression &t, bool &stable, std::set< process_identifier > &visited, const bool allowrecursion)
void AddTerminationActionIfNecessary(const stochastic_action_summand_vector &summands)
bool determinewhetherprocessescontaintime(const process_identifier &procId)
void filter_vars_by_assignmentlist(const assignment_list &assignments, const variable_list &parameters, const std::set< variable > &vars_set, std::set< variable > &vars_result_set)
data_expression_list addcondition(const variable_list &matchinglist, const data_expression_list &conditionlist)
void calculate_communication_merge_deadlock_summands(const deadlock_summand_vector &deadlock_summands1, const deadlock_summand_vector &deadlock_summands2, const stochastic_action_summand_vector &action_summands, deadlock_summand_vector &deadlock_summands)
void transform(const process_identifier &init, stochastic_action_summand_vector &action_summands, deadlock_summand_vector &deadlock_summands, variable_list &parameters, data_expression_list &initial_state, stochastic_distribution &initial_stochastic_distribution)
process_expression obtain_initial_distribution(const process_identifier &procId)
objectdatatype & insertAction(const action_label &actionId)
Expression replace_variables_capture_avoiding_alt(const Expression &e, Substitution &sigma)
process_instance_assignment expand_process_instance_assignment(const process_instance_assignment &t, std::set< process_identifier > &visited_processes)
assignment_list pushdummy_regular(const variable_list &pars, const stacklisttype &stack, const variable_list &stochastic_variables)
assignment_list argscollect_regular(const process_expression &t, const variable_list &vl, const std::set< variable > &variables_bound_in_sum)
static data_expression_list getarguments(const action_list &multiAction)
lps::detail::ultimate_delay getUltimateDelayCondition(const stochastic_action_summand_vector &action_summands, const deadlock_summand_vector &deadlock_summands, const variable_list &freevars)
#define mCRL2log(LEVEL)
mCRL2log(LEVEL) provides the stream used to log.
Definition logger.h:435
A collection of utilities for lazy expression construction.
data_expression and_(data_expression const &p, data_expression const &q)
Returns an expression equivalent to p or q.
Namespace for system defined sort bool_.
Definition bool.h:29
const basic_sort & bool_()
Constructor for sort expression Bool.
Definition bool.h:41
const function_symbol & false_()
Constructor for function symbol false.
Definition bool.h:106
const function_symbol & true_()
Constructor for function symbol true.
Definition bool.h:74
Namespace for system defined sort real_.
data_expression & real_one()
bool is_plus_application(const atermpp::aterm &e)
Recogniser for application of +.
Definition real1.h:1133
data_expression & real_zero()
application times(const data_expression &arg0, const data_expression &arg1)
Application of function symbol *.
Definition real1.h:1282
bool is_application(const data_expression &t)
Returns true if the term t is an application.
bool is_where_clause(const atermpp::aterm &x)
Returns true if the term t is a where clause.
application less(const data_expression &arg0, const data_expression &arg1)
Application of function symbol <.
Definition standard.h:263
bool is_abstraction(const atermpp::aterm &x)
Returns true if the term t is an abstraction.
application if_(const data_expression &arg0, const data_expression &arg1, const data_expression &arg2)
Application of function symbol if.
Definition standard.h:224
bool is_function_symbol(const atermpp::aterm &x)
Returns true if the term t is a function symbol.
bool is_machine_number(const atermpp::aterm &x)
Returns true if the term t is a machine_number.
const data_expression_list & variable_list_to_data_expression_list(const variable_list &l)
Transform a variable_list into a data_expression_list.
bool is_variable(const atermpp::aterm &x)
Returns true if the term t is a variable.
A class that takes a linear process specification and checks all tau-summands of that LPS for conflue...
The main namespace for the LPS library.
Definition constelm.h:18
void complete_data_specification(stochastic_specification &spec)
Adds all sorts that appear in the process of l to the data specification of l.
bool occursinterm(const data::data_expression &t, const data::variable &var)
mcrl2::lps::stochastic_specification linearise(const mcrl2::process::process_specification &type_checked_spec, const mcrl2::lps::t_lin_options &lin_options=t_lin_options())
Linearises a process specification.
The main namespace for the Process library.
bool is_at(const atermpp::aterm &x)
bool is_process_instance(const atermpp::aterm &x)
bool is_process_instance_assignment(const atermpp::aterm &x)
bool is_tau(const atermpp::aterm &x)
bool is_seq(const atermpp::aterm &x)
bool is_merge(const atermpp::aterm &x)
bool is_allow(const atermpp::aterm &x)
bool is_bounded_init(const atermpp::aterm &x)
bool is_delta(const atermpp::aterm &x)
bool is_sum(const atermpp::aterm &x)
bool is_block(const atermpp::aterm &x)
bool is_if_then_else(const atermpp::aterm &x)
bool is_comm(const atermpp::aterm &x)
void alphabet_reduce(process_specification &procspec, std::size_t duplicate_equation_limit=(std::numeric_limits< size_t >::max)())
Applies alphabet reduction to a process specification.
Definition process.cpp:82
bool is_action(const atermpp::aterm &x)
bool is_left_merge(const atermpp::aterm &x)
bool is_hide(const atermpp::aterm &x)
bool is_if_then(const atermpp::aterm &x)
bool is_choice(const atermpp::aterm &x)
bool is_stochastic_operator(const atermpp::aterm &x)
bool is_rename(const atermpp::aterm &x)
bool is_sync(const atermpp::aterm &x)
void swap(atermpp::aterm &t1, atermpp::aterm &t2) noexcept
Swaps two term_applss.
Definition aterm.h:364
Options for linearisation.
Definition linearise.h:25
make_substitution(const std::map< process_identifier, process_identifier > &map)
process_identifier operator()(const process_identifier &id) const
const std::map< process_identifier, process_identifier > & m_map