From 4bb22ef6116f822e9a464c28976b51112e4d5f17 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Thu, 2 Jan 2025 15:38:31 +0100 Subject: [PATCH 01/54] Document comm_table, extract method. --- libraries/lps/source/linearise.cpp | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index 051efb0793..5d3b19b7fd 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -7806,9 +7806,7 @@ class specification_basic_type } - // Type and variables for a somewhat more efficient storage of the - // communication function - + /// Data structure to store the communication function more efficiently. class comm_entry { public: @@ -7816,30 +7814,41 @@ class specification_basic_type comm_entry(const comm_entry& )=delete; comm_entry& operator=(const comm_entry& )=delete; + /// Left-hand sides of communication expressions std::vector lhs; + + /// Right-hand sides of communication expressions std::vector rhs; + + /// Temporary data using in determining whether communication is allowed. + /// See usages of the data structure below. std::vector tmp; std::vector< bool > match_failed; comm_entry(const communication_expression_list& communications) + : tmp(communications.size(), identifier_string_list()), + match_failed(communications.size(), false) { for (const communication_expression& l: communications) { lhs.push_back(l.action_name().names()); rhs.push_back(l.name()); - tmp.push_back(identifier_string_list()); - match_failed.push_back(false); } } - ~comm_entry() - {} + ~comm_entry() = default; std::size_t size() const { assert(lhs.size()==rhs.size() && rhs.size()==tmp.size() && tmp.size()==match_failed.size()); return lhs.size(); } + + void reset_temporary_data() + { + tmp = lhs; + match_failed = std::vector(size(), false); + } }; process::action_label can_communicate(const action_list& m, comm_entry& comm_table) @@ -7849,11 +7858,7 @@ class specification_basic_type a communication can take place. If not action_label() is delivered, otherwise the resulting action is the result. */ // first copy the left-hand sides of communications for use - for (std::size_t i=0; i Date: Thu, 2 Jan 2025 16:05:59 +0100 Subject: [PATCH 02/54] Eliminate rest_is_null. This seems a remnant of ancient history, when rest was stored as a pointer. To eliminate, we use the invariant rest[i].empty() == rest_is_null[i]. --- libraries/lps/source/linearise.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index 5d3b19b7fd..f2ebab99a3 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -7851,6 +7851,8 @@ class specification_basic_type } }; + /// Determine if there exists a communication expression a1|...|an -> b in comm_table + /// such that a1|...|an \subseteq m', where m' is the multiset of actionnames for multiaction m. process::action_label can_communicate(const action_list& m, comm_entry& comm_table) { /* this function indicates whether the actions in m @@ -7969,9 +7971,7 @@ class specification_basic_type // the rest of actions of lhs that are not in m should be in n // rest[i] contains the part of n in which lhs i has to find matching actions - // rest_is_null[i] contains indications whether rest[i] is NULL. std::vector < action_list > rest(comm_table.size(),n); - std::vector < bool > rest_is_null(comm_table.size(),false); // check every lhs for (std::size_t i=0; i Date: Thu, 2 Jan 2025 16:18:11 +0100 Subject: [PATCH 03/54] Improve readability of code while (a != b = c) { S } is rather ugly and hard to understand. Replace this with the equivalent: b = c; while (a != b) { S ; b = c } --- libraries/lps/source/linearise.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index f2ebab99a3..42e0723976 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -7948,14 +7948,15 @@ class specification_basic_type continue; } - identifier_string commname; - while (actionname != (commname = comm_table.tmp[i].front())) + identifier_string commname = comm_table.tmp[i].front(); + while (actionname != commname) { // action is not in m, so it should be in n // but all actions in m come before n comm_table.match_failed[i]=true; comm_table.tmp[i]=identifier_string_list(); break; + commname = comm_table.tmp[i].front(); } if (actionname==commname) // actionname found { @@ -7990,15 +7991,16 @@ class specification_basic_type } // get first action in lhs i const identifier_string commname = comm_table.tmp[i].front(); - identifier_string restname; + identifier_string restname = rest[i].front().label().name(); // find it in rest[i] - while (commname!=(restname = rest[i].front().label().name())) + while (commname!=restname) { rest[i].pop_front(); if (rest[i].empty()) // no more { break; } + restname = rest[i].front().label().name(); } if (commname!=restname) // action was not found { From 4d959800ab42d4d4aaae6935b865ce40903d8479 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Thu, 2 Jan 2025 16:25:01 +0100 Subject: [PATCH 04/54] Remove now useless while loop. The previous refactoring showed that the body of the loop was executed at most once, and can be replaced by a simple if-then-else --- libraries/lps/source/linearise.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index 42e0723976..2ba55164d6 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -7948,18 +7948,16 @@ class specification_basic_type continue; } - identifier_string commname = comm_table.tmp[i].front(); - while (actionname != commname) + if (actionname != comm_table.tmp[i].front()) { // action is not in m, so it should be in n // but all actions in m come before n comm_table.match_failed[i]=true; comm_table.tmp[i]=identifier_string_list(); - break; - commname = comm_table.tmp[i].front(); } - if (actionname==commname) // actionname found + else { + // actionname found; on to the next action comm_table.tmp[i].pop_front(); comm_ok = true; } From 893e167b51c22b151974a22a08164bf9098f28b9 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Thu, 2 Jan 2025 16:27:19 +0100 Subject: [PATCH 05/54] Remove superfluous assignment if match_failed[i] == true, tmp[i] is never read anymore. --- libraries/lps/source/linearise.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index 2ba55164d6..342b5f25dc 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -7953,7 +7953,6 @@ class specification_basic_type // action is not in m, so it should be in n // but all actions in m come before n comm_table.match_failed[i]=true; - comm_table.tmp[i]=identifier_string_list(); } else { From 9efd488b76dc9fca29edf70a3d95d28af564fa3c Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Thu, 2 Jan 2025 16:49:56 +0100 Subject: [PATCH 06/54] Extract method to reduce code duplication After refactoring, this code can be shared between can_communicate and might_communicate. --- libraries/lps/source/linearise.cpp | 120 ++++++++++++----------------- 1 file changed, 50 insertions(+), 70 deletions(-) diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index 342b5f25dc..4554bce7ff 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -7849,10 +7849,55 @@ class specification_basic_type tmp = lhs; match_failed = std::vector(size(), false); } + + /// Check if m is contained in a lhs in the communication entry. + /// Returns true if this is the case, false otherwise. + /// Precondition: temporary data has been reset. + /// Postcondition: for every i such that m is not contained in lhs[i], match_failed[i] is true. + bool match_multiaction(const action_list& m) { + // m must match a lhs; check every action + for (const action& a: m) + { + const identifier_string& actionname=a.label().name(); + + // check every lhs for actionname + bool comm_ok = false; + for (std::size_t i=0; i < size(); ++i) + { + if (match_failed[i]) // lhs i does not match + { + continue; + } + if (tmp[i].empty()) // lhs cannot match actionname + { + match_failed[i]=true; + continue; + } + if (actionname != tmp[i].front()) + { + // no match + match_failed[i] = true; + } + else + { + // possible match; on to next action + tmp[i].pop_front(); + comm_ok = true; + } + } + if (!comm_ok) // no (possibly) matching lhs + { + return false; + } + } + + // There must be an lhs that contains m. + return true; + } }; /// Determine if there exists a communication expression a1|...|an -> b in comm_table - /// such that a1|...|an \subseteq m', where m' is the multiset of actionnames for multiaction m. + /// such that m' \subseteq a1|...|an , where m' is the multiset of actionnames for multiaction m. process::action_label can_communicate(const action_list& m, comm_entry& comm_table) { /* this function indicates whether the actions in m @@ -7862,40 +7907,8 @@ class specification_basic_type // first copy the left-hand sides of communications for use comm_table.reset_temporary_data(); - // m must match a lhs; check every action - for (const action& a: m) - { - identifier_string actionname=a.label().name(); - - // check every lhs for actionname - bool comm_ok = false; - for (std::size_t i=0; i Date: Thu, 2 Jan 2025 17:04:34 +0100 Subject: [PATCH 07/54] Refactor functions into method Due to the use of temporary date, can_communicate and might_communicate are tightly couple to comm_entry. This changeset makes this explicit, by pushing the functions into the comm_entry class as methods. This also nicely encapsulates the date contained in comm_entry. --- libraries/lps/source/linearise.cpp | 224 +++++++++++++++-------------- 1 file changed, 113 insertions(+), 111 deletions(-) diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index 4554bce7ff..381372dbb9 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -7809,11 +7809,7 @@ class specification_basic_type /// Data structure to store the communication function more efficiently. class comm_entry { - public: - // comm_entries are not copyable. - comm_entry(const comm_entry& )=delete; - comm_entry& operator=(const comm_entry& )=delete; - + protected: /// Left-hand sides of communication expressions std::vector lhs; @@ -7825,25 +7821,6 @@ class specification_basic_type std::vector tmp; std::vector< bool > match_failed; - comm_entry(const communication_expression_list& communications) - : tmp(communications.size(), identifier_string_list()), - match_failed(communications.size(), false) - { - for (const communication_expression& l: communications) - { - lhs.push_back(l.action_name().names()); - rhs.push_back(l.name()); - } - } - - ~comm_entry() = default; - - std::size_t size() const - { - assert(lhs.size()==rhs.size() && rhs.size()==tmp.size() && tmp.size()==match_failed.size()); - return lhs.size(); - } - void reset_temporary_data() { tmp = lhs; @@ -7894,109 +7871,134 @@ class specification_basic_type // There must be an lhs that contains m. return true; } - }; - /// Determine if there exists a communication expression a1|...|an -> b in comm_table - /// such that m' \subseteq a1|...|an , where m' is the multiset of actionnames for multiaction m. - process::action_label can_communicate(const action_list& m, comm_entry& comm_table) - { - /* this function indicates whether the actions in m - consisting of actions and data occur in C, such that - a communication can take place. If not action_label() is delivered, - otherwise the resulting action is the result. */ - // first copy the left-hand sides of communications for use - comm_table.reset_temporary_data(); - if(!comm_table.match_multiaction(m)) { - return action_label(); - } + public: + // comm_entries are not copyable. + comm_entry(const comm_entry& )=delete; + comm_entry& operator=(const comm_entry& )=delete; - // there is a lhs containing m; find it - for (std::size_t i=0; i rest(comm_table.size(),n); + ~comm_entry() = default; - // check every lhs - for (std::size_t i=0; i b in comm_table + /// such that m' \subseteq a1|...|an , where m' is the multiset of actionnames for multiaction m. + process::action_label can_communicate(const action_list& m) { - // .. find them in rest[i] - if (rest[i].empty()) // no luck - { - break; + /* this function indicates whether the actions in m + consisting of actions and data occur in C, such that + a communication can take place. If not action_label() is delivered, + otherwise the resulting action is the result. */ + // first copy the left-hand sides of communications for use + reset_temporary_data(); + + if(!match_multiaction(m)) { + return action_label(); } - // get first action in lhs i - const identifier_string commname = comm_table.tmp[i].front(); - identifier_string restname = rest[i].front().label().name(); - // find it in rest[i] - while (commname!=restname) + + // there is a lhs containing m; find it + for (std::size_t i=0; i rest(size(),n); + + // check every lhs + for (std::size_t i=0; i Date: Fri, 3 Jan 2025 07:58:48 +0100 Subject: [PATCH 08/54] Fix regression introduced in 16a30aedabd852d1f126706c364c90d0805b9274 rest[i].empty() iff rest_is_null[i] is not an invariant (we only know rest_is_null[i] implies rest[i].empty()) --- libraries/lps/source/linearise.cpp | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index 381372dbb9..aacea19cae 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -7949,7 +7949,11 @@ class specification_basic_type // the rest of actions of lhs that are not in m should be in n // rest[i] contains the part of n in which lhs i has to find matching actions - std::vector < action_list > rest(size(),n); + // if rest[i] cannot match the left hand side tmp[i], i.e., it becomes empty + // before matching all actions in the lhs, we set it to std::nullopt. + // N.B. when rest[i] becomes empty after matching all actions in the lhs, + // rest[i].empty() is a meaningful result: we have a successful match. + std::vector < std::optional > rest(size(),n); // check every lhs for (std::size_t i=0; iempty()) // no luck { + rest[i] = std::nullopt; break; } // get first action in lhs i - const identifier_string commname = tmp[i].front(); - identifier_string restname = rest[i].front().label().name(); + const identifier_string& commname = tmp[i].front(); + identifier_string restname = rest[i]->front().label().name(); // find it in rest[i] while (commname!=restname) { - rest[i].pop_front(); - if (rest[i].empty()) // no more + rest[i]->pop_front(); + if (rest[i]->empty()) // no more { + rest[i] = std::nullopt; break; } - restname = rest[i].front().label().name(); + restname = rest[i]->front().label().name(); } if (commname!=restname) // action was not found { break; } // action found; try next - rest[i].pop_front(); + rest[i]->pop_front(); tmp[i].pop_front(); } - if (!rest[i].empty()) // lhs was found in rest[i] + if (rest[i] != std::nullopt) // lhs was found in rest[i] { return true; } From 5f431612c0bbc5c106a96e10d2cb5d7d6209f7ab Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Fri, 3 Jan 2025 08:36:41 +0100 Subject: [PATCH 09/54] Small performance improvements - Make lhs, rhs in comm_entry constant; this was already the case, but the intent is now also clear in the code. - Use references instead of copies in some places where appropriate. --- libraries/lps/source/linearise.cpp | 60 +++++++++++++++++++----------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index aacea19cae..a1ebd33d4f 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -7811,10 +7811,10 @@ class specification_basic_type { protected: /// Left-hand sides of communication expressions - std::vector lhs; + const std::vector lhs; /// Right-hand sides of communication expressions - std::vector rhs; + const std::vector rhs; /// Temporary data using in determining whether communication is allowed. /// See usages of the data structure below. @@ -7829,9 +7829,11 @@ class specification_basic_type /// Check if m is contained in a lhs in the communication entry. /// Returns true if this is the case, false otherwise. - /// Precondition: temporary data has been reset. /// Postcondition: for every i such that m is not contained in lhs[i], match_failed[i] is true. + /// NB: resets temporary data before performing computations. bool match_multiaction(const action_list& m) { + reset_temporary_data(); + // m must match a lhs; check every action for (const action& a: m) { @@ -7872,6 +7874,29 @@ class specification_basic_type return true; } + // Initialization of lhs, defined as static function so it can be used in the constructor. + // Allows lhs to be defined as const. + static std::vector init_lhs(const communication_expression_list& communications) + { + std::vector result; + for (const communication_expression& l: communications) + { + result.push_back(l.action_name().names()); + } + return result; + } + + // Initialization of rhs, defined as static function so it can be used in the constructor. + // Allows rhs to be defined as const. + static std::vector init_rhs(const communication_expression_list& communications) + { + std::vector result; + for (const communication_expression& l: communications) + { + result.push_back(l.name()); + } + return result; + } public: // comm_entries are not copyable. @@ -7879,15 +7904,11 @@ class specification_basic_type comm_entry& operator=(const comm_entry& )=delete; comm_entry(const communication_expression_list& communications) - : tmp(communications.size(), identifier_string_list()), + : lhs(init_lhs(communications)), + rhs(init_rhs(communications)), + tmp(communications.size(), identifier_string_list()), match_failed(communications.size(), false) - { - for (const communication_expression& l: communications) - { - lhs.push_back(l.action_name().names()); - rhs.push_back(l.name()); - } - } + {} ~comm_entry() = default; @@ -7906,8 +7927,6 @@ class specification_basic_type a communication can take place. If not action_label() is delivered, otherwise the resulting action is the result. */ // first copy the left-hand sides of communications for use - reset_temporary_data(); - if(!match_multiaction(m)) { return action_label(); } @@ -7941,8 +7960,6 @@ class specification_basic_type that are not in m should be in n (i.e. there must be a subbag o of n such that m+o can communicate. */ - reset_temporary_data(); - if(!match_multiaction(m)) { return false; } @@ -7962,6 +7979,7 @@ class specification_basic_type { continue; } + // as long as there are still unmatched actions in lhs i... while (!tmp[i].empty()) { @@ -7990,6 +8008,7 @@ class specification_basic_type { break; } + // action found; try next rest[i]->pop_front(); tmp[i].pop_front(); @@ -8183,15 +8202,14 @@ class specification_basic_type "- calculating the communication operator on ") << action_summands.size() << " action summands"; /* first we sort the multiactions in communications */ - communication_expression_list resultingCommunications; + communication_expression_list sorted_communications; for (const communication_expression& comm: communications) { const action_name_multiset& source=comm.action_name(); const identifier_string& target=comm.name(); - resultingCommunications.push_front(communication_expression(sort_action_labels(source),target)); + sorted_communications.push_front(communication_expression(sort_action_labels(source),target)); } - communication_expression_list communications1=resultingCommunications; stochastic_action_summand_vector resultsumlist; deadlock_summand_vector resultingDeltaSummands; @@ -8210,7 +8228,7 @@ class specification_basic_type for (const stochastic_action_summand& smmnd: action_summands) { const variable_list& sumvars=smmnd.summation_variables(); - const action_list multiaction=smmnd.multi_action().actions(); + const action_list& multiaction=smmnd.multi_action().actions(); const data_expression& condition=smmnd.condition(); const assignment_list& nextstate=smmnd.assignments(); const stochastic_distribution& dist=smmnd.distribution(); @@ -8256,13 +8274,13 @@ class specification_basic_type const tuple_list multiactionconditionlist= makeMultiActionConditionList( multiaction, - communications1); + sorted_communications); assert(multiactionconditionlist.actions.size()== multiactionconditionlist.conditions.size()); for (std::size_t i=0 ; i Date: Fri, 3 Jan 2025 08:42:44 +0100 Subject: [PATCH 10/54] Get rid of repeated allocation and deallocation of terms --- libraries/lps/source/linearise.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index a1ebd33d4f..3ea5c721c2 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -8121,14 +8121,18 @@ class specification_basic_type { action_list alpha=reverse(alpha_in); data_expression cond = sort_bool::false_(); + + action a; // a and beta used in the loop; avoid repeated allocation and deallocation + action_list beta; + action_list actl; // used in inner loop. while (!alpha.empty()) { - const action a = alpha.front(); - action_list beta = alpha.tail(); + a = alpha.front(); + beta = alpha.tail(); while (!beta.empty()) { - const action_list actl({ a, beta.front() }); + actl = action_list({a, beta.front()}); if (comm_table.might_communicate(actl,beta.tail()) && xi(actl,beta.tail(),comm_table)) { // sort and remove duplicates?? From 72f78d7bdbd2721fa93e1bd44f340508f069d36b Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Fri, 3 Jan 2025 09:15:02 +0100 Subject: [PATCH 11/54] Eliminate parameter r_is_null r_is_null is true iff r.empty() --- libraries/lps/source/linearise.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index 3ea5c721c2..9697f3d931 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -8031,7 +8031,6 @@ class specification_basic_type const action_list& w, const action_list& n, const action_list& r, - const bool r_is_null, comm_entry& comm_table) { /* phi is a function that yields a list of pairs @@ -8054,7 +8053,7 @@ class specification_basic_type is possible */ if (c!=action_label()) { - const tuple_list T=makeMultiActionConditionList_aux(w,comm_table,r,r_is_null); + const tuple_list T=makeMultiActionConditionList_aux(w,comm_table,r); return addActionCondition( (c==action_label()?action():action(c,d)), //Check. Nil kan niet geleverd worden. sort_bool::true_(), @@ -8072,20 +8071,20 @@ class specification_basic_type { action_list tempw=w; tempw=push_back(tempw,firstaction); - return phi(m,d,tempw,o,r,r_is_null,comm_table); + return phi(m,d,tempw,o,r,comm_table); } else { action_list tempm=m; tempm=push_back(tempm,firstaction); - const tuple_list T=phi(tempm,d,w,o,r,r_is_null,comm_table); + const tuple_list T=phi(tempm,d,w,o,r,comm_table); action_list tempw=w; tempw=push_back(tempw,firstaction); return addActionCondition( action(), condition, T, - phi(m,d,tempw,o,r,r_is_null,comm_table)); + phi(m,d,tempw,o,r,comm_table)); } } @@ -8150,8 +8149,7 @@ class specification_basic_type tuple_list makeMultiActionConditionList_aux( const action_list& multiaction, comm_entry& comm_table, - const action_list& r, - const bool r_is_null) + const action_list& r) { /* This is the function gamma(m,C,r) provided by Muck van Weerdenburg in Calculation of @@ -8159,7 +8157,7 @@ class specification_basic_type if (multiaction.empty()) { tuple_list t; - t.conditions.push_back((r_is_null)?static_cast(sort_bool::true_()):psi(r,comm_table)); + t.conditions.push_back((r.empty())?static_cast(sort_bool::true_()):psi(r,comm_table)); t.actions.push_back(action_list()); return t; } @@ -8171,12 +8169,12 @@ class specification_basic_type firstaction.arguments(), action_list(), remainingmultiaction, - r,r_is_null,comm_table); + r,comm_table); action_list tempr=r; tempr.push_front(firstaction); const tuple_list T=makeMultiActionConditionList_aux( remainingmultiaction,comm_table, - (r_is_null) ? action_list({ firstaction }) : tempr, false); + tempr); return addActionCondition(firstaction,sort_bool::true_(),T,S); } @@ -8185,7 +8183,7 @@ class specification_basic_type const communication_expression_list& communications) { comm_entry comm_table(communications); - return makeMultiActionConditionList_aux(multiaction,comm_table,action_list(),true); + return makeMultiActionConditionList_aux(multiaction,comm_table,action_list()); } void communicationcomposition( From 3e862c1409f55fe3ba2d52c68de2fe32aa047146 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Fri, 3 Jan 2025 10:43:00 +0100 Subject: [PATCH 12/54] Cache can/might_communicate --- libraries/lps/source/linearise.cpp | 148 +++++++++++++++++------------ 1 file changed, 87 insertions(+), 61 deletions(-) diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index 9697f3d931..5447ecc5dc 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -7816,6 +7816,10 @@ class specification_basic_type /// Right-hand sides of communication expressions const std::vector rhs; + /// Caches. + std::unordered_map m_can_communicate_cache; + std::unordered_map m_might_communicate_cache; + /// Temporary data using in determining whether communication is allowed. /// See usages of the data structure below. std::vector tmp; @@ -7864,6 +7868,7 @@ class specification_basic_type comm_ok = true; } } + if (!comm_ok) // no (possibly) matching lhs { return false; @@ -7923,30 +7928,40 @@ class specification_basic_type process::action_label can_communicate(const action_list& m) { /* this function indicates whether the actions in m - consisting of actions and data occur in C, such that - a communication can take place. If not action_label() is delivered, - otherwise the resulting action is the result. */ - // first copy the left-hand sides of communications for use - if(!match_multiaction(m)) { - return action_label(); + consisting of actions and data occur in C, such that + a communication can take place. If not action_label() is delivered, + otherwise the resulting action is the result. */ + + // Check the cache first. + auto it = m_can_communicate_cache.find(m); + if(it != m_can_communicate_cache.end()) + { + return it->second; } - // there is a lhs containing m; find it - for (std::size_t i=0; isecond; } - // the rest of actions of lhs that are not in m should be in n - // rest[i] contains the part of n in which lhs i has to find matching actions - // if rest[i] cannot match the left hand side tmp[i], i.e., it becomes empty - // before matching all actions in the lhs, we set it to std::nullopt. - // N.B. when rest[i] becomes empty after matching all actions in the lhs, - // rest[i].empty() is a meaningful result: we have a successful match. - std::vector < std::optional > rest(size(),n); + bool result = false; - // check every lhs - for (std::size_t i=0; i> rest(size(), n); - // as long as there are still unmatched actions in lhs i... - while (!tmp[i].empty()) + // check every lhs + for (std::size_t i = 0; i < size(); ++i) { - assert(rest[i] != std::nullopt); - // .. find them in rest[i] - if (rest[i]->empty()) // no luck + if (match_failed[i]) // lhs i did not contain m { - rest[i] = std::nullopt; - break; + continue; } - // get first action in lhs i - const identifier_string& commname = tmp[i].front(); - identifier_string restname = rest[i]->front().label().name(); - // find it in rest[i] - while (commname!=restname) + + // as long as there are still unmatched actions in lhs i... + while (!tmp[i].empty()) { - rest[i]->pop_front(); - if (rest[i]->empty()) // no more + assert(rest[i] != std::nullopt); + // .. find them in rest[i] + if (rest[i]->empty()) // no luck { rest[i] = std::nullopt; break; } - restname = rest[i]->front().label().name(); + // get first action in lhs i + const identifier_string& commname = tmp[i].front(); + identifier_string restname = rest[i]->front().label().name(); + // find it in rest[i] + while (commname != restname) + { + rest[i]->pop_front(); + if (rest[i]->empty()) // no more + { + rest[i] = std::nullopt; + break; + } + restname = rest[i]->front().label().name(); + } + if (commname != restname) // action was not found + { + break; + } + + // action found; try next + rest[i]->pop_front(); + tmp[i].pop_front(); } - if (commname!=restname) // action was not found + + if (rest[i] != std::nullopt) // lhs was found in rest[i] { + result = true; break; } - - // action found; try next - rest[i]->pop_front(); - tmp[i].pop_front(); - } - - if (rest[i] != std::nullopt) // lhs was found in rest[i] - { - return true; } } - // no lhs completely matches - return false; + // cache the result + m_might_communicate_cache.insert({m, result}); + return result; } - }; tuple_list phi(const action_list& m, @@ -8098,7 +8122,7 @@ class specification_basic_type { const action& a = beta.front(); action_list l=alpha; - l=push_back(l,a); + l=push_back(l,a); // this is expensive! const action_list& beta_next = beta.tail(); if (comm_table.can_communicate(l)!=action_label()) @@ -8131,7 +8155,9 @@ class specification_basic_type while (!beta.empty()) { - actl = action_list({a, beta.front()}); + actl = action_list(); + actl.emplace_front(beta.front()); + actl.emplace_front(a); if (comm_table.might_communicate(actl,beta.tail()) && xi(actl,beta.tail(),comm_table)) { // sort and remove duplicates?? From 3ff549a8691e5b492392dc5d1c1bd93b50e72088 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Fri, 3 Jan 2025 12:32:35 +0100 Subject: [PATCH 13/54] Perform some obvious code cleanup. --- .idea/editor.xml | 580 ++++++++++++++++++++ libraries/lps/include/mcrl2/lps/linearise.h | 4 +- libraries/lps/source/linearise.cpp | 178 +++--- 3 files changed, 685 insertions(+), 77 deletions(-) create mode 100644 .idea/editor.xml diff --git a/.idea/editor.xml b/.idea/editor.xml new file mode 100644 index 0000000000..184ac5e44d --- /dev/null +++ b/.idea/editor.xml @@ -0,0 +1,580 @@ + + + + + \ No newline at end of file diff --git a/libraries/lps/include/mcrl2/lps/linearise.h b/libraries/lps/include/mcrl2/lps/linearise.h index 0f2d3eae4f..0ee87990e9 100644 --- a/libraries/lps/include/mcrl2/lps/linearise.h +++ b/libraries/lps/include/mcrl2/lps/linearise.h @@ -69,7 +69,7 @@ struct t_lin_options /// \exception mcrl2::runtime_error Linearisation failed mcrl2::lps::stochastic_specification linearise( const mcrl2::process::process_specification& type_checked_spec, - mcrl2::lps::t_lin_options lin_options = t_lin_options()); + const mcrl2::lps::t_lin_options& lin_options = t_lin_options()); /// \brief Linearises a process specification from a textual specification /// \param[in] text A string containing a process specification @@ -78,7 +78,7 @@ mcrl2::lps::stochastic_specification linearise( /// \exception mcrl2::runtime_error Linearisation failed inline mcrl2::lps::stochastic_specification linearise( const std::string& text, - mcrl2::lps::t_lin_options lin_options = t_lin_options()) + const mcrl2::lps::t_lin_options& lin_options = t_lin_options()) { mcrl2::process::process_specification spec = mcrl2::process::parse_process_specification(text); diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index 5447ecc5dc..6bc6d3cb57 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -106,7 +106,7 @@ class objectdatatype constructor=false; processstatus=unknown; object=none; - canterminate=0; + canterminate=false; containstime=false; } @@ -238,14 +238,14 @@ class specification_basic_type terminatedProcId, variable_list(), seq(terminationAction,delta()), - pCRL,0,false); + pCRL,false,false); delta_process=newprocess(variable_list(),delta(),pCRL,false,false); tau_process=newprocess(variable_list(),tau(),pCRL,true,false); } ~specification_basic_type() { - for (; stack_operations_list!=nullptr;) + while (stack_operations_list!=nullptr) { stackoperations* temp=stack_operations_list->next; delete stack_operations_list; @@ -255,18 +255,8 @@ class specification_basic_type } private: - /* data_expression real_zero() - { - static data_expression zero=sort_real::creal(sort_int::cint(sort_nat::c0()),sort_pos::c1()); - return zero; - } - - data_expression real_one() - { - static data_expression one=sort_real::creal(sort_int::cint(sort_nat::cnat(sort_pos::c1())),sort_pos::c1()); - return one; - } */ + static data_expression real_times_optimized(const data_expression& r1, const data_expression& r2) { if (r1==sort_real::real_zero() || r2==sort_real::real_zero()) @@ -284,7 +274,8 @@ class specification_basic_type return sort_real::times(r1,r2); } - process_expression delta_at_zero(void) + static + process_expression delta_at_zero() { return at(delta(), sort_real::real_(0)); } @@ -344,6 +335,7 @@ class specification_basic_type fresh_identifier_generator.add_identifier(str); } + static process_expression action_list_to_process(const action_list& ma) { if (ma.size()==0) @@ -357,6 +349,7 @@ class specification_basic_type return process::sync(ma.front(),action_list_to_process(ma.tail())); } + static action_list to_action_list(const process_expression& p) { if (is_tau(p)) @@ -377,7 +370,8 @@ class specification_basic_type return action_list(); } - process::action_label_list getnames(const process_expression& multiAction) + static + action_label_list getnames(const process_expression& multiAction) { if (is_action(multiAction)) { @@ -433,6 +427,7 @@ class specification_basic_type return getparameters_rec(multiAction,occurs_set); } + static data_expression_list getarguments(const action_list& multiAction) { data_expression_list result; @@ -443,6 +438,7 @@ class specification_basic_type return reverse(result); } + static action_list makemultiaction(const process::action_label_list& actionIds, const data_expression_list& args) { action_list result; @@ -540,6 +536,7 @@ class specification_basic_type /************ upperpowerof2 *********************************************/ + static std::size_t upperpowerof2(std::size_t i) /* function yields n for the smallest value n such that 2^n>=i. This constitutes the number of bits necessary @@ -749,7 +746,7 @@ class specification_basic_type e.identifier(), e.formal_parameters(), e.expression(), - unknown,0,false); + unknown,false,false); } } @@ -759,7 +756,7 @@ class specification_basic_type const processstatustype s, const bool canterminate, const bool containstime, - process_identifier& p) + process_identifier& p) const { for(const std::pair& d: objectdata) { @@ -787,13 +784,14 @@ class specification_basic_type because it cannot occur as a string in the input */ process_identifier initprocess(std::string("init"), variable_list()); - insert_process_declaration(initprocess,variable_list(),init,unknown,0,false); + insert_process_declaration(initprocess,variable_list(),init,unknown,false,false); return initprocess; } private: /********** various functions on action and multi actions ***************/ + static bool actioncompare(const action_label& a1, const action_label& a2) { /* first compare the strings in the actions */ @@ -812,6 +810,7 @@ class specification_basic_type return false; } + static action_list linInsertActionInMultiActionList( const action& act, action_list multiAction) @@ -1103,9 +1102,8 @@ class specification_basic_type const process_identifier& procDecl, const processstatustype status) { - processstatustype s; objectdatatype& object=objectIndex(procDecl); - s=object.processstatus; + processstatustype s=object.processstatus; if (s==unknown) { @@ -1272,7 +1270,8 @@ class specification_basic_type /**************** occursinterm *** occursintermlist ***********/ - bool occursinterm(const variable& var, const data_expression& t) const + static + bool occursinterm(const variable& var, const data_expression& t) { return data::search_free_variable(t, var); } @@ -1326,7 +1325,8 @@ class specification_basic_type filter_vars_by_termlist(a.begin(),a.end(),vars_set,vars_result_set); } - bool occursintermlist(const variable& var, const data_expression_list& r) const + static + bool occursintermlist(const variable& var, const data_expression_list& r) { for (const data_expression& d: r) { @@ -1738,6 +1738,7 @@ class specification_basic_type } /* Remove assignments that do not appear in the parameter list. */ + static assignment_list filter_assignments(const assignment_list& assignments, const variable_list& parameters) { assignment_vector result; @@ -1877,6 +1878,7 @@ class specification_basic_type } // Sort the assignments, such that they have the same order as the parameters + static assignment_list sort_assignments(const assignment_list& ass, const variable_list& parameters) { std::mapassignment_map; @@ -2399,6 +2401,7 @@ class specification_basic_type } + static assignment_list parameters_to_assignment_list(const variable_list& parameters, const std::set& variables_bound_in_sum) { assignment_vector result; @@ -3123,6 +3126,7 @@ class specification_basic_type return process_expression(); } + static int match_sequence( const std::vector < process_instance_assignment >& s1, const std::vector < process_instance_assignment >& s2, @@ -3868,7 +3872,7 @@ class specification_basic_type { std::vector todo; todo.push_back(procsIdDecl); - for (; !todo.empty() ;) + while (!todo.empty()) { const process_identifier pi=todo.back(); todo.pop_back(); @@ -3967,6 +3971,7 @@ class specification_basic_type that no id1 that occurs at the right of the map can occur at the left. */ + static process_identifier get_last(const process_identifier& id, const std::map< process_identifier, process_identifier >& identifier_identifier_map) { process_identifier target_id=id; @@ -3988,6 +3993,7 @@ class specification_basic_type } + static void set_proc_identifier_map( std::map< process_identifier, process_identifier >& identifier_identifier_map, const process_identifier& id1_, @@ -4011,6 +4017,7 @@ class specification_basic_type } } + static void complete_proc_identifier_map(std::map< process_identifier, process_identifier >& identifier_identifier_map) { std::map< process_identifier, process_identifier > new_identifier_identifier_map; @@ -4236,6 +4243,7 @@ class specification_basic_type /**************** Collectparameterlist ******************************/ + static bool alreadypresent(variable& var,const variable_list& vl) { /* Note: variables can be different, although they have the @@ -4262,6 +4270,7 @@ class specification_basic_type return alreadypresent(var,vl.tail()); } + static variable_list joinparameters(const variable_list& par1, const variable_list& par2) { @@ -4385,7 +4394,7 @@ class specification_basic_type stacklisttype(const stacklisttype& )=delete; stacklisttype& operator=(const stacklisttype& )=delete; - + static stackoperations* find_suitable_stack_operations( const variable_list& parameters, stackoperations* stack_operations_list) @@ -4491,7 +4500,7 @@ class specification_basic_type } data_expression getvar(const variable& var, - const stacklisttype& stack) + const stacklisttype& stack) const { /* first search whether the variable is a free process variable */ if (global_variables.count(var)>0) @@ -5082,6 +5091,7 @@ class specification_basic_type return assignment_list({ assignment(stack.stackvar,sf) }); } + static bool occursin(const variable& name, const variable_list& pars) { @@ -5330,7 +5340,7 @@ class specification_basic_type list sumvars */ variable_list sumvars; - for (; is_sum(summandterm) ;) + while (is_sum(summandterm)) { sumvars=sum(summandterm).variables() + sumvars; summandterm=sum(summandterm).operand(); @@ -5358,7 +5368,7 @@ class specification_basic_type stochastic_distribution cumulative_distribution(variable_list(),sort_real::real_one()); /* The conditions are collected for use. The stochastic operators before the action are ignored */ - for (; (is_if_then(summandterm)||is_stochastic_operator(summandterm)) ;) + while ((is_if_then(summandterm)||is_stochastic_operator(summandterm))) { if (is_if_then(summandterm)) { @@ -5671,7 +5681,7 @@ class specification_basic_type return w; } - data::function_symbol find_case_function(std::size_t index, const sort_expression& sort) + data::function_symbol find_case_function(std::size_t index, const sort_expression& sort) const { for (const data::function_symbol& f: enumeratedtypes[index].functions) { @@ -5921,6 +5931,7 @@ class specification_basic_type return false; } + static data_expression_list extend(const data_expression& c, const data_expression_list& cl) { return data_expression_list(cl.begin(), @@ -6124,6 +6135,7 @@ class specification_basic_type return construct_binary_case_tree_rec(n-1,sums,terms,termsort,e); } + static bool summandsCanBeClustered( const stochastic_action_summand& summand1, const stochastic_action_summand& summand2) @@ -6159,6 +6171,7 @@ class specification_basic_type return i2 == multiactionlist2.end(); } + static data_expression getRHSassignment(const variable& var, const assignment_list& as) { for (const assignment& a: as) @@ -7047,6 +7060,7 @@ class specification_basic_type some_summand_has_time?deadlock(resulttime):deadlock()); } + static sort_expression_list getActionSorts(const action_list& actionlist) { sort_expression_list resultsorts; @@ -7275,6 +7289,7 @@ class specification_basic_type /**************** hiding *****************************************/ + static action_list hide_(const identifier_string_list& hidelist, const action_list& multiaction) { action_list resultactionlist; @@ -7291,6 +7306,7 @@ class specification_basic_type return reverse(resultactionlist); } + static void hidecomposition(const identifier_string_list& hidelist, stochastic_action_summand_vector& action_summands) { for (stochastic_action_summand_vector::iterator i=action_summands.begin(); i!=action_summands.end() ; ++i) @@ -7306,6 +7322,7 @@ class specification_basic_type /**************** allow/block *************************************/ + static bool implies_condition(const data_expression& c1, const data_expression& c2) { if (c2==sort_bool::true_()) @@ -7368,7 +7385,7 @@ class specification_basic_type void insert_timed_delta_summand( const stochastic_action_summand_vector& action_summands, deadlock_summand_vector& deadlock_summands, - const deadlock_summand& s) + const deadlock_summand& s) const { deadlock_summand_vector result; @@ -7435,6 +7452,7 @@ class specification_basic_type // in which case true is delivered. If multiaction is the action Terminate, // then true is also returned. + static bool allowsingleaction(const action_name_multiset& allowaction, const action_list& multiaction) { @@ -7486,6 +7504,7 @@ class specification_basic_type return false; } + static bool encap(const action_name_multiset_list& encaplist, const action_list& multiaction) { for (const action& a: multiaction) @@ -7605,6 +7624,7 @@ class specification_basic_type /**************** renaming ******************************************/ + static action rename_action(const rename_expression_list& renamings, const action& act) { const action_label& actionId=act.label(); @@ -7653,13 +7673,14 @@ class specification_basic_type /**************** equalargs ****************************************/ + static bool occursinvarandremove(const variable& var, variable_list& vl) { bool result=false; if (vl.empty()) { - return 0; + return false; } vl.pop_front(); const variable var1=vl.front(); @@ -7752,7 +7773,7 @@ class specification_basic_type } template - sort_expression_list get_sorts(const List& l) + static sort_expression_list get_sorts(const List& l) { return sort_expression_list(l.begin(), l.end(), [](const typename List::value_type& d) -> sort_expression {return d.sort();}); } @@ -7810,11 +7831,14 @@ class specification_basic_type class comm_entry { protected: + /// Type used to store the names of the actions in a multiaction + using multi_action_name_t = std::vector; + /// Left-hand sides of communication expressions - const std::vector lhs; + const std::vector m_lhs; /// Right-hand sides of communication expressions - const std::vector rhs; + const std::vector m_rhs; /// Caches. std::unordered_map m_can_communicate_cache; @@ -7822,13 +7846,16 @@ class specification_basic_type /// Temporary data using in determining whether communication is allowed. /// See usages of the data structure below. - std::vector tmp; - std::vector< bool > match_failed; + std::vector m_lhs_iters; // offset into lhs + std::vector m_match_failed; void reset_temporary_data() { - tmp = lhs; - match_failed = std::vector(size(), false); + for (std::size_t i = 0; i < size(); ++i) + { + m_lhs_iters[i] = m_lhs[i].begin(); + m_match_failed[i] = false; + } } /// Check if m is contained in a lhs in the communication entry. @@ -7847,24 +7874,24 @@ class specification_basic_type bool comm_ok = false; for (std::size_t i=0; i < size(); ++i) { - if (match_failed[i]) // lhs i does not match + if (m_match_failed[i]) // lhs i does not match { continue; } - if (tmp[i].empty()) // lhs cannot match actionname + if (m_lhs_iters[i] == m_lhs[i].end()) // lhs cannot match actionname { - match_failed[i]=true; + m_match_failed[i]=true; continue; } - if (actionname != tmp[i].front()) + if (actionname != *m_lhs_iters[i]) { // no match - match_failed[i] = true; + m_match_failed[i] = true; } else { // possible match; on to next action - tmp[i].pop_front(); + ++m_lhs_iters[i]; comm_ok = true; } } @@ -7881,12 +7908,13 @@ class specification_basic_type // Initialization of lhs, defined as static function so it can be used in the constructor. // Allows lhs to be defined as const. - static std::vector init_lhs(const communication_expression_list& communications) + static std::vector < multi_action_name_t > init_lhs(const communication_expression_list& communications) { - std::vector result; + std::vector result; for (const communication_expression& l: communications) { - result.push_back(l.action_name().names()); + const identifier_string_list& names = l.action_name().names(); + result.emplace_back(names.begin(), names.end()); } return result; } @@ -7909,18 +7937,18 @@ class specification_basic_type comm_entry& operator=(const comm_entry& )=delete; comm_entry(const communication_expression_list& communications) - : lhs(init_lhs(communications)), - rhs(init_rhs(communications)), - tmp(communications.size(), identifier_string_list()), - match_failed(communications.size(), false) + : m_lhs(init_lhs(communications)), + m_rhs(init_rhs(communications)), + m_lhs_iters(communications.size()), + m_match_failed(communications.size()) {} ~comm_entry() = default; std::size_t size() const { - assert(lhs.size()==rhs.size() && rhs.size()==tmp.size() && tmp.size()==match_failed.size()); - return lhs.size(); + assert(lhs.size()==rhs.size() && rhs.size()==m_lhs_iters.size() && m_lhs_iters.size()==match_failed.size()); + return m_lhs.size(); } /// Determine if there exists a communication expression a1|...|an -> b in comm_table @@ -7947,13 +7975,13 @@ class specification_basic_type for (std::size_t i = 0; i < size(); ++i) { // lhs i matches only if comm_table[i] is empty - if ((!match_failed[i]) && tmp[i].empty()) + if ((!m_match_failed[i]) && m_lhs_iters[i] == m_lhs[i].end()) { - if (rhs[i] == tau()) + if (m_rhs[i] == tau()) { throw mcrl2::runtime_error("Cannot linearise a process with a communication operator, containing a communication that results in tau or that has an empty right hand side"); } - result = action_label(rhs[i], m.front().label().sorts()); + result = action_label(m_rhs[i], m.front().label().sorts()); break; } } @@ -7976,8 +8004,7 @@ class specification_basic_type subbag o of n such that m+o can communicate. */ // Check the cache first. - auto it = m_might_communicate_cache.find(m); - if(it != m_might_communicate_cache.end()) + if(auto it = m_might_communicate_cache.find(m); it != m_might_communicate_cache.end()) { return it->second; } @@ -7988,7 +8015,7 @@ class specification_basic_type { // the rest of actions of lhs that are not in m should be in n // rest[i] contains the part of n in which lhs i has to find matching actions - // if rest[i] cannot match the left hand side tmp[i], i.e., it becomes empty + // if rest[i] cannot match the left hand side m_lhs_iters[i], i.e., it becomes empty // before matching all actions in the lhs, we set it to std::nullopt. // N.B. when rest[i] becomes empty after matching all actions in the lhs, // rest[i].empty() is a meaningful result: we have a successful match. @@ -7997,13 +8024,13 @@ class specification_basic_type // check every lhs for (std::size_t i = 0; i < size(); ++i) { - if (match_failed[i]) // lhs i did not contain m + if (m_match_failed[i]) // lhs i did not contain m { continue; } // as long as there are still unmatched actions in lhs i... - while (!tmp[i].empty()) + while (m_lhs_iters[i] != m_lhs[i].end()) { assert(rest[i] != std::nullopt); // .. find them in rest[i] @@ -8013,7 +8040,7 @@ class specification_basic_type break; } // get first action in lhs i - const identifier_string& commname = tmp[i].front(); + const identifier_string& commname = *m_lhs_iters[i]; identifier_string restname = rest[i]->front().label().name(); // find it in rest[i] while (commname != restname) @@ -8033,7 +8060,7 @@ class specification_basic_type // action found; try next rest[i]->pop_front(); - tmp[i].pop_front(); + ++m_lhs_iters[i]; } if (rest[i] != std::nullopt) // lhs was found in rest[i] @@ -8073,7 +8100,7 @@ class specification_basic_type } if (n.empty()) { - process::action_label c = comm_table.can_communicate(m); /* returns action_label() if no communication + action_label c = comm_table.can_communicate(m); /* returns action_label() if no communication is possible */ if (c!=action_label()) { @@ -8112,6 +8139,7 @@ class specification_basic_type } } + static bool xi(const action_list& alpha, const action_list& beta, comm_entry& comm_table) { if (beta.empty()) @@ -8360,6 +8388,7 @@ class specification_basic_type mCRL2log(mcrl2::log::verbose) << " resulting in " << action_summands.size() << " action summands and " << deadlock_summands.size() << " delta summands\n"; } + static bool check_real_variable_occurrence( const variable_list& sumvars, const data_expression& actiontime, @@ -9139,11 +9168,9 @@ class specification_basic_type assert(action_summands.size()==0); assert(deadlock_summands.size()==0); - variable_list allpars; - allpars=par1 + par3; + variable_list allpars = par1 + par3; - bool inline_allow = is_allow || is_block; - if (inline_allow) + if (is_allow || is_block) { // Inline allow is only supported for ignore_time, // for in other cases generation of delta summands cannot be inlined in any simple way. @@ -9524,11 +9551,12 @@ class specification_basic_type { ultimate_delay_condition=getUltimateDelayCondition(action_summands,deadlock_summands,pars); - // The ultimate_delay_condition can be complex. Try to simplify it with a fourier_motzkin reduction. - data_expression simplified_ultimate_delay_condition; - variable_list reduced_sumvars; try { + // The ultimate_delay_condition can be complex. Try to simplify it with a fourier_motzkin reduction. + data_expression simplified_ultimate_delay_condition; + variable_list reduced_sumvars; + fourier_motzkin(ultimate_delay_condition.constraint(), ultimate_delay_condition.variables(), simplified_ultimate_delay_condition, @@ -9903,13 +9931,13 @@ class specification_basic_type it prints the process variables that contain time. Furtermore, it returns whether there are processes that contain an if-then that will be translated to an if-then-else with an delta@0 in the else branch, introducing time */ - bool stable=0; + bool stable=false; bool contains_if_then=false; while (!stable) { std::set < process_identifier > visited; - stable=1; + stable=true; containstime_rec(procId,&stable,visited,contains_if_then); } return contains_if_then; @@ -9945,7 +9973,7 @@ class specification_basic_type for(const variable& v: relevant_stochastic_variables) { new_assignments=push_back(new_assignments,assignment(*i,local_sigma1(v))); - i++; + ++i; } data_expression new_distribution = data::replace_variables_capture_avoiding(sto.distribution(),local_sigma1); @@ -11293,7 +11321,7 @@ class specification_basic_type mcrl2::lps::stochastic_specification mcrl2::lps::linearise( const mcrl2::process::process_specification& type_checked_spec, - mcrl2::lps::t_lin_options lin_options) + const mcrl2::lps::t_lin_options& lin_options) { mCRL2log(mcrl2::log::verbose) << "linearising the process specification using the '" << lin_options.lin_method << "' method.\n"; mcrl2::process::process_specification input_process=type_checked_spec; From ce31d19dfbce0aaf5b57e1f541b51925650aee79 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Fri, 3 Jan 2025 13:41:26 +0100 Subject: [PATCH 14/54] Use iterators as argument First step in refactoring this code. Using iterators should avoid a lot of copying and (re)allocation. --- libraries/lps/source/linearise.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index 6bc6d3cb57..f0a3adfd6e 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -8104,7 +8104,7 @@ class specification_basic_type is possible */ if (c!=action_label()) { - const tuple_list T=makeMultiActionConditionList_aux(w,comm_table,r); + const tuple_list T=makeMultiActionConditionList_aux(w.begin(), w.end(),comm_table,r); return addActionCondition( (c==action_label()?action():action(c,d)), //Check. Nil kan niet geleverd worden. sort_bool::true_(), @@ -8150,7 +8150,7 @@ class specification_basic_type { const action& a = beta.front(); action_list l=alpha; - l=push_back(l,a); // this is expensive! + l=push_back(l,a); const action_list& beta_next = beta.tail(); if (comm_table.can_communicate(l)!=action_label()) @@ -8201,14 +8201,15 @@ class specification_basic_type // returns a list of tuples. tuple_list makeMultiActionConditionList_aux( - const action_list& multiaction, + action_list::const_iterator multiaction_first, + action_list::const_iterator multiaction_last, comm_entry& comm_table, const action_list& r) { /* This is the function gamma(m,C,r) provided by Muck van Weerdenburg in Calculation of Communication with open terms [1]. */ - if (multiaction.empty()) + if (multiaction_first == multiaction_last) { tuple_list t; t.conditions.push_back((r.empty())?static_cast(sort_bool::true_()):psi(r,comm_table)); @@ -8216,8 +8217,9 @@ class specification_basic_type return t; } - const action& firstaction=multiaction.front(); - const action_list& remainingmultiaction=multiaction.tail(); /* This is m in [1] */ + const action& firstaction=*multiaction_first; + + const action_list remainingmultiaction(std::next(multiaction_first), multiaction_last); /* This is m in [1] */ const tuple_list S=phi(action_list({ firstaction }), firstaction.arguments(), @@ -8226,8 +8228,7 @@ class specification_basic_type r,comm_table); action_list tempr=r; tempr.push_front(firstaction); - const tuple_list T=makeMultiActionConditionList_aux( - remainingmultiaction,comm_table, + const tuple_list T=makeMultiActionConditionList_aux(std::next(multiaction_first), multiaction_last,comm_table, tempr); return addActionCondition(firstaction,sort_bool::true_(),T,S); } @@ -8237,7 +8238,7 @@ class specification_basic_type const communication_expression_list& communications) { comm_entry comm_table(communications); - return makeMultiActionConditionList_aux(multiaction,comm_table,action_list()); + return makeMultiActionConditionList_aux(multiaction.begin(), multiaction.end(),comm_table,action_list()); } void communicationcomposition( @@ -8271,7 +8272,7 @@ class specification_basic_type deadlock_summand_vector resultingDeltaSummands; deadlock_summands.swap(resultingDeltaSummands); - bool inline_allow = is_allow || is_block; + const bool inline_allow = is_allow || is_block; if (inline_allow) { // Inline allow is only supported for ignore_time, From d8811b4afb94d799af4bf85a6a1650a85bf4b1d6 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Fri, 3 Jan 2025 13:47:19 +0100 Subject: [PATCH 15/54] Pass iterators instead of lists. --- libraries/lps/source/linearise.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index f0a3adfd6e..e8a5537ee1 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -8080,7 +8080,8 @@ class specification_basic_type tuple_list phi(const action_list& m, const data_expression_list& d, const action_list& w, - const action_list& n, + action_list::const_iterator n_first, + action_list::const_iterator n_last, const action_list& r, comm_entry& comm_table) { @@ -8094,11 +8095,11 @@ class specification_basic_type and C contains a list of multiaction action pairs indicating possible communications */ - if (!comm_table.might_communicate(m,n)) + if (!comm_table.might_communicate(m,action_list(n_first, n_last))) { return tuple_list(); } - if (n.empty()) + if (n_first == n_last) { action_label c = comm_table.can_communicate(m); /* returns action_label() if no communication is possible */ @@ -8115,27 +8116,27 @@ class specification_basic_type return tuple_list(); } /* if n=[a(f)] \oplus o */ - const action& firstaction=n.front(); - const action_list& o=n.tail(); + const action& firstaction=*n_first; + const data_expression condition=pairwiseMatch(d,firstaction.arguments()); if (condition==sort_bool::false_()) { action_list tempw=w; tempw=push_back(tempw,firstaction); - return phi(m,d,tempw,o,r,comm_table); + return phi(m,d,tempw,std::next(n_first), n_last,r,comm_table); } else { action_list tempm=m; tempm=push_back(tempm,firstaction); - const tuple_list T=phi(tempm,d,w,o,r,comm_table); + const tuple_list T=phi(tempm,d,w,std::next(n_first), n_last,r,comm_table); action_list tempw=w; tempw=push_back(tempw,firstaction); return addActionCondition( action(), condition, T, - phi(m,d,tempw,o,r,comm_table)); + phi(m,d,tempw,std::next(n_first), n_last,r,comm_table)); } } @@ -8219,12 +8220,10 @@ class specification_basic_type const action& firstaction=*multiaction_first; - const action_list remainingmultiaction(std::next(multiaction_first), multiaction_last); /* This is m in [1] */ - const tuple_list S=phi(action_list({ firstaction }), firstaction.arguments(), action_list(), - remainingmultiaction, + std::next(multiaction_first), multiaction_last, r,comm_table); action_list tempr=r; tempr.push_front(firstaction); From 6983c21d18eef53d7ff373e96cd4a61b2969267d Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Fri, 3 Jan 2025 13:55:31 +0100 Subject: [PATCH 16/54] Pass iterators into might_communicate --- libraries/lps/source/linearise.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index e8a5537ee1..d1960cfacc 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -7993,7 +7993,8 @@ class specification_basic_type } bool might_communicate(const action_list& m, - const action_list& n) + action_list::const_iterator n_first, + action_list::const_iterator n_last) { /* this function indicates whether the actions in m consisting of actions and data occur in C, such that @@ -8019,7 +8020,7 @@ class specification_basic_type // before matching all actions in the lhs, we set it to std::nullopt. // N.B. when rest[i] becomes empty after matching all actions in the lhs, // rest[i].empty() is a meaningful result: we have a successful match. - std::vector> rest(size(), n); + std::vector> rest(size(), action_list(n_first, n_last)); // check every lhs for (std::size_t i = 0; i < size(); ++i) @@ -8080,8 +8081,8 @@ class specification_basic_type tuple_list phi(const action_list& m, const data_expression_list& d, const action_list& w, - action_list::const_iterator n_first, - action_list::const_iterator n_last, + const action_list::const_iterator& n_first, + const action_list::const_iterator& n_last, const action_list& r, comm_entry& comm_table) { @@ -8095,7 +8096,7 @@ class specification_basic_type and C contains a list of multiaction action pairs indicating possible communications */ - if (!comm_table.might_communicate(m,action_list(n_first, n_last))) + if (!comm_table.might_communicate(m, n_first, n_last)) { return tuple_list(); } @@ -8158,7 +8159,7 @@ class specification_basic_type { return true; } - else if (comm_table.might_communicate(l,beta_next)) + else if (comm_table.might_communicate(l,beta_next.begin(), beta_next.end())) { return xi(l,beta_next,comm_table) || xi(alpha,beta_next,comm_table); } @@ -8187,7 +8188,8 @@ class specification_basic_type actl = action_list(); actl.emplace_front(beta.front()); actl.emplace_front(a); - if (comm_table.might_communicate(actl,beta.tail()) && xi(actl,beta.tail(),comm_table)) + const action_list& beta_tail = beta.tail(); + if (comm_table.might_communicate(actl,beta_tail.begin(), beta_tail.end()) && xi(actl,beta.tail(),comm_table)) { // sort and remove duplicates?? cond = lazy::or_(cond,pairwiseMatch(a.arguments(),beta.front().arguments())); From 9993d64568cb066a66aead61ef54fe172f9551f3 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Fri, 3 Jan 2025 14:03:53 +0100 Subject: [PATCH 17/54] Store iterator pairs instead of action_list in might_communicate --- libraries/lps/source/linearise.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index d1960cfacc..a73002cd85 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -8016,11 +8016,12 @@ class specification_basic_type { // the rest of actions of lhs that are not in m should be in n // rest[i] contains the part of n in which lhs i has to find matching actions - // if rest[i] cannot match the left hand side m_lhs_iters[i], i.e., it becomes empty + // if rest[i] cannot match the next remaining action in the left hand side, stored in m_lhs_iters[i], i.e., rest[i] becomes empty // before matching all actions in the lhs, we set it to std::nullopt. // N.B. when rest[i] becomes empty after matching all actions in the lhs, // rest[i].empty() is a meaningful result: we have a successful match. - std::vector> rest(size(), action_list(n_first, n_last)); + std::vector>> + rest(size(), std::make_pair(n_first, n_last)); // pairs of iterator into n; the second element of the pair indicates the end of the range in n. // check every lhs for (std::size_t i = 0; i < size(); ++i) @@ -8035,24 +8036,24 @@ class specification_basic_type { assert(rest[i] != std::nullopt); // .. find them in rest[i] - if (rest[i]->empty()) // no luck + if (rest[i]->first == rest[i]->second) // no luck { rest[i] = std::nullopt; break; } // get first action in lhs i const identifier_string& commname = *m_lhs_iters[i]; - identifier_string restname = rest[i]->front().label().name(); + identifier_string restname = rest[i]->first->label().name(); // find it in rest[i] while (commname != restname) { - rest[i]->pop_front(); - if (rest[i]->empty()) // no more + ++(rest[i]->first); + if (rest[i]->first == rest[i]->second) // no more { rest[i] = std::nullopt; break; } - restname = rest[i]->front().label().name(); + restname = rest[i]->first->label().name(); } if (commname != restname) // action was not found { @@ -8060,7 +8061,7 @@ class specification_basic_type } // action found; try next - rest[i]->pop_front(); + ++(rest[i]->first); ++m_lhs_iters[i]; } From 05b2a3b681bc6b60cc8124eb7aaec1fc7cffc14b Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Fri, 3 Jan 2025 17:18:54 +0100 Subject: [PATCH 18/54] Extract renaming from linearise.cpp --- .../lps/include/mcrl2/lps/linearise_rename.h | 86 +++++++++++++++++++ .../lps/include/mcrl2/lps/linearise_utility.h | 57 ++++++++++++ libraries/lps/source/linearise.cpp | 71 +-------------- 3 files changed, 147 insertions(+), 67 deletions(-) create mode 100644 libraries/lps/include/mcrl2/lps/linearise_rename.h create mode 100644 libraries/lps/include/mcrl2/lps/linearise_utility.h diff --git a/libraries/lps/include/mcrl2/lps/linearise_rename.h b/libraries/lps/include/mcrl2/lps/linearise_rename.h new file mode 100644 index 0000000000..2655ef07ca --- /dev/null +++ b/libraries/lps/include/mcrl2/lps/linearise_rename.h @@ -0,0 +1,86 @@ +// Author(s): Jan Friso Groote, Jeroen Keiren +// Copyright: see the accompanying file COPYING or copy at +// https://github.com/mCRL2org/mCRL2/blob/master/COPYING +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +/// \file mcrl2/lps/linearise_rename.h +/// \brief Apply the rename operator to action summands. + +#ifndef MCRL2_LPS_LINEARISE_RENAME_H +#define MCRL2_LPS_LINEARISE_RENAME_H + +#include "mcrl2/atermpp/aterm_list.h" +#include "mcrl2/lps/stochastic_action_summand.h" +#include "mcrl2/lps/linearise_utility.h" +#include "mcrl2/process/process_expression.h" + + +namespace mcrl2 +{ + +namespace lps +{ + + /// Apply renamings to a single action + inline + process::action rename(const process::rename_expression_list& renamings, const process::action& action) + { + const process::action_label& action_label = action.label(); + const core::identifier_string& action_name = action_label.name(); + for (const process::rename_expression& renaming: renamings) + { + if (action_name == renaming.source()) + { + return process::action(process::action_label(renaming.target(), action_label.sorts()), action.arguments()); + } + } + return action; + } + + inline + process::action_list rename(const process::rename_expression_list& renamings, + const process::action_list& actions) + { + process::action_list result; + + for (const process::action& a: actions) + { + result.push_front(rename(renamings, a)); + } + + result = atermpp::sort_list(result, std::function(action_compare)); + return result; + } + + inline + multi_action rename(const process::rename_expression_list& renamings, + const multi_action& multi_action) + { + return lps::multi_action(rename(renamings, multi_action.actions()), multi_action.time()); + } + + inline + void rename( + const process::rename_expression_list& renamings, + lps::stochastic_action_summand_vector& action_summands) + { + for (lps::stochastic_action_summand_vector::iterator i=action_summands.begin(); i!=action_summands.end(); ++i) + { + *i = lps::stochastic_action_summand(i->summation_variables(), + i->condition(), + rename(renamings, i->multi_action()), + i->assignments(), + i->distribution()); + } + } + +} // namespace lps + +} // namespace mcrl2 + + + +#endif // MCRL2_LPS_LINEARISE_RENAME_H diff --git a/libraries/lps/include/mcrl2/lps/linearise_utility.h b/libraries/lps/include/mcrl2/lps/linearise_utility.h new file mode 100644 index 0000000000..e919adc015 --- /dev/null +++ b/libraries/lps/include/mcrl2/lps/linearise_utility.h @@ -0,0 +1,57 @@ +// Author(s): Jan Friso Groote, Jeroen Keiren +// Copyright: see the accompanying file COPYING or copy at +// https://github.com/mCRL2org/mCRL2/blob/master/COPYING +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +/// \file mcrl2/lps/linearise_utilities.h +/// \brief Utilities used in linearisation. Mainly for actions and multiactions. + +#ifndef MCRL2_LPS_LINEARISE_UTILITY_H +#define MCRL2_LPS_LINEARISE_UTILITY_H + +#include "mcrl2/process/process_expression.h" + + +namespace mcrl2 +{ + +namespace lps +{ + +/// Determine if a1 < a2; the key requirement is that orderings of action labels and the actions in multiactions are +/// consistent. +bool action_label_compare(const process::action_label& a1, const process::action_label& a2) +{ + /* first compare the strings in the actions */ + if (std::string(a1.name())multi_action().actions()); - - *i= stochastic_action_summand(i->summation_variables(), - i->condition(), - i->multi_action().has_time()?multi_action(actions,i->multi_action().time()):multi_action(actions), - i->assignments(), - i->distribution()); - - } - } /**************** equalargs ****************************************/ @@ -9477,7 +9414,7 @@ class specification_basic_type { generateLPEmCRLterm(action_summands,deadlock_summands,process::rename(t).operand(), regular,rename_variables,pars,init,initial_stochastic_distribution,ultimate_delay_condition); - renamecomposition(process::rename(t).rename_set(),action_summands); + lps::rename(process::rename(t).rename_set(),action_summands); return; } From d7f2ae6a1ffdd56f45676c277a765ec96394ec5f Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Sun, 5 Jan 2025 16:55:06 +0100 Subject: [PATCH 19/54] Rename an individual summand --- .../lps/include/mcrl2/lps/linearise_rename.h | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_rename.h b/libraries/lps/include/mcrl2/lps/linearise_rename.h index 2655ef07ca..424095dc19 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_rename.h +++ b/libraries/lps/include/mcrl2/lps/linearise_rename.h @@ -62,19 +62,27 @@ namespace lps return lps::multi_action(rename(renamings, multi_action.actions()), multi_action.time()); } + inline + stochastic_action_summand rename(const process::rename_expression_list& renamings, + const stochastic_action_summand& summand) + { + return stochastic_action_summand(summand.summation_variables(), + summand.condition(), + rename(renamings, summand.multi_action()), + summand.assignments(), + summand.distribution()); + } + + inline void rename( const process::rename_expression_list& renamings, lps::stochastic_action_summand_vector& action_summands) { - for (lps::stochastic_action_summand_vector::iterator i=action_summands.begin(); i!=action_summands.end(); ++i) - { - *i = lps::stochastic_action_summand(i->summation_variables(), - i->condition(), - rename(renamings, i->multi_action()), - i->assignments(), - i->distribution()); - } + for (auto& action_summand: action_summands) + { + action_summand = rename(renamings, action_summand); + } } } // namespace lps From 9f5d3554254d52c24e78cd943ec7e3638361b5cf Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Sun, 5 Jan 2025 16:56:06 +0100 Subject: [PATCH 20/54] Move allow/block to separate file --- .../include/mcrl2/lps/linearise_allow_block.h | 215 ++++++++++ .../lps/include/mcrl2/lps/linearise_utility.h | 165 ++++++++ libraries/lps/source/linearise.cpp | 371 +----------------- 3 files changed, 397 insertions(+), 354 deletions(-) create mode 100644 libraries/lps/include/mcrl2/lps/linearise_allow_block.h diff --git a/libraries/lps/include/mcrl2/lps/linearise_allow_block.h b/libraries/lps/include/mcrl2/lps/linearise_allow_block.h new file mode 100644 index 0000000000..08184904af --- /dev/null +++ b/libraries/lps/include/mcrl2/lps/linearise_allow_block.h @@ -0,0 +1,215 @@ +// Author(s): Jan Friso Groote, Jeroen Keiren +// Copyright: see the accompanying file COPYING or copy at +// https://github.com/mCRL2org/mCRL2/blob/master/COPYING +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +/// \file mcrl2/lps/linearise_allow_block.h +/// \brief Apply the allow and block operators to summands. + +#ifndef MCRL2_LPS_LINEARISE_ALLLOW_BLOCK_H +#define MCRL2_LPS_LINEARISE_ALLLOW_BLOCK_H + +#include "mcrl2/atermpp/aterm_list.h" +#include "mcrl2/lps/deadlock_summand.h" +#include "mcrl2/lps/stochastic_action_summand.h" +#include "mcrl2/lps/linearise_utility.h" +#include "mcrl2/process/process_expression.h" + + +namespace mcrl2 +{ + +namespace lps +{ + +/**************** allow/block *************************************/ + +inline +bool allowsingleaction(const process::action_name_multiset& allowaction, + const process::action_list& multiaction) +{ + /* The special cases where multiaction==tau and multiaction=={ Terminated } must have been + dealt with separately. */ + assert(multiaction.size()!=0 && multiaction != action_list({ terminationAction })); + + const core::identifier_string_list& names=allowaction.names(); + core::identifier_string_list::const_iterator i=names.begin(); + + for (process::action_list::const_iterator walker=multiaction.begin(); + walker!=multiaction.end(); ++walker,++i) + { + if (i==names.end()) + { + return false; + } + if (*i!=walker->label().name()) + { + return false; + } + } + return i==names.end(); +} + +/// \brief determine whether the multiaction has the same labels as the allow action, +// in which case true is delivered. If multiaction is the action Terminate, +// then true is also returned. +inline +bool allow_(const process::action_name_multiset_list& allowlist, + const process::action_list& multiaction, + const process::action termination_action) +{ + /* The empty multiaction, i.e. tau, is never blocked by allow */ + if (multiaction.empty()) + { + return true; + } + + /* The multiaction is equal to the special Terminate action. This action cannot be blocked. */ + if (multiaction == process::action_list({ termination_action })) + { + return true; + } + + for (process::action_name_multiset_list::const_iterator i=allowlist.begin(); + i!=allowlist.end(); ++i) + { + if (allowsingleaction(*i,multiaction)) + { + return true; + } + } + return false; +} + +inline +bool encap(const process::action_name_multiset_list& encaplist, const process::action_list& multiaction) +{ + for (const process::action& a: multiaction) + { + assert(encaplist.size()==1); + for (const core::identifier_string& s1: encaplist.front().names()) + { + const core::identifier_string s2=a.label().name(); + if (s1==s2) + { + return true; + } + } + } + return false; +} + + +void allowblockcomposition( + const process::action_name_multiset_list& allowlist1, // This is a list of list of identifierstring. + const bool is_allow, + stochastic_action_summand_vector& action_summands, + deadlock_summand_vector& deadlock_summands, + const process::action& termination_action, + bool ignore_time, + bool nodeltaelimination) + { + /* This function calculates the allow or the block operator, + depending on whether is_allow is true */ + + stochastic_action_summand_vector sourcesumlist; + action_summands.swap(sourcesumlist); + + deadlock_summand_vector resultdeltasumlist; + deadlock_summand_vector resultsimpledeltasumlist; + deadlock_summands.swap(resultdeltasumlist); + + process::action_name_multiset_list allowlist((is_allow)?sort_multi_action_labels(allowlist1):allowlist1); + + std::size_t sourcesumlist_length=sourcesumlist.size(); + if (sourcesumlist_length>2 || is_allow) // This condition prevents this message to be printed + // when performing data elimination. In this case the + // term delta is linearised, to determine which data + // is essential for all processes. In these cases a + // message about the block operator is very confusing. + { + mCRL2log(mcrl2::log::verbose) << "- calculating the " << (is_allow?"allow":"block") << + " operator on " << sourcesumlist.size() << " action summands and " << resultdeltasumlist.size() << " delta summands"; + } + + /* First add the resulting sums in two separate lists + one for actions, and one for delta's. The delta's + are added at the end to the actions, where for + each delta summand it is determined whether it ought + to be added, or is superseded by an action or another + delta summand */ + for (const stochastic_action_summand& smmnd: sourcesumlist) + { + const data::variable_list& sumvars=smmnd.summation_variables(); + const process::action_list multiaction=smmnd.multi_action().actions(); + const data::data_expression& actiontime=smmnd.multi_action().time(); + const data::data_expression& condition=smmnd.condition(); + + // Explicitly allow the termination action in any allow. + if ((is_allow && allow_(allowlist,multiaction,termination_action)) || + (!is_allow && !encap(allowlist,multiaction))) + { + action_summands.push_back(smmnd); + } + else + { + if (smmnd.has_time()) + { + resultdeltasumlist.push_back(deadlock_summand(sumvars, condition, deadlock(actiontime))); + } + else + { + // summand has no time. + if (condition==data::sort_bool::true_()) + { + resultsimpledeltasumlist.push_back(deadlock_summand(sumvars, condition, deadlock())); + } + else + { + resultdeltasumlist.push_back(deadlock_summand(sumvars, condition, deadlock())); + } + } + } + } + + if (nodeltaelimination) + { + deadlock_summands.swap(resultsimpledeltasumlist); + copy(resultdeltasumlist.begin(),resultdeltasumlist.end(),back_inserter(deadlock_summands)); + } + else + { + if (!ignore_time) /* if a delta summand is added, conditional, timed + delta's are subsumed and do not need to be added */ + { + for (const deadlock_summand& summand: resultsimpledeltasumlist) + { + insert_timed_delta_summand(action_summands,deadlock_summands,summand,ignore_time); + } + for (const deadlock_summand& summand: resultdeltasumlist) + { + insert_timed_delta_summand(action_summands,deadlock_summands,summand,ignore_time); + } + } + else + { + // Add a true -> delta + insert_timed_delta_summand(action_summands,deadlock_summands,deadlock_summand(data::variable_list(),data::sort_bool::true_(),deadlock()), ignore_time); + } + } + if (mCRL2logEnabled(mcrl2::log::verbose) && (sourcesumlist_length>2 || is_allow)) + { + mCRL2log(mcrl2::log::verbose) << ", resulting in " << action_summands.size() << " action summands and " << deadlock_summands.size() << " delta summands\n"; + } + } + +} // namespace lps + +} // namespace mcrl2 + + + +#endif // MCRL2_LPS_LINEARISE_ALLLOW_BLOCK_H diff --git a/libraries/lps/include/mcrl2/lps/linearise_utility.h b/libraries/lps/include/mcrl2/lps/linearise_utility.h index e919adc015..fba2ed8bf9 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_utility.h +++ b/libraries/lps/include/mcrl2/lps/linearise_utility.h @@ -48,6 +48,171 @@ bool action_compare(const process::action& a1, const process::action& a2) return action_label_compare(a1.label(), a2.label()); } +/// Insert action into an action_list, keeping the action list sorted w.r.t. cmp. +/// Complexity: O(n) for an action_list of length n. +process::action_list insert( + const process::action& act, + process::action_list l, + const std::function& cmp + = [](const process::action& t1, const process::action& t2){ return t1& cmp + = [](const core::identifier_string& t1, const core::identifier_string& t2){ return std::string(t1)(c2))) && + implies_condition(c1,data::binary_right(down_cast(c2))); + } + + if (data::sort_bool::is_or_application(c1)) + { + return implies_condition(data::binary_left(down_cast(c1)),c2) && + implies_condition(data::binary_right(down_cast(c1)),c2); + } + + if (data::sort_bool::is_and_application(c1)) + { + return implies_condition(data::binary_left(down_cast(c1)),c2) || + implies_condition(data::binary_right(down_cast(c1)),c2); + } + + if (data::sort_bool::is_or_application(c2)) + { + return implies_condition(c1,data::binary_left(down_cast(c2))) || + implies_condition(c1,data::binary_right(down_cast(c2))); + } + + return false; +} + +inline +void insert_timed_delta_summand( + const stochastic_action_summand_vector& action_summands, + deadlock_summand_vector& deadlock_summands, + const deadlock_summand& s, + bool ignore_time) + { + deadlock_summand_vector result; + + // const variable_list sumvars=s.summation_variables(); + const data::data_expression& cond=s.condition(); + const data::data_expression& actiontime=s.deadlock().time(); + + // First check whether the delta summand is subsumed by an action summands. + if (!ignore_time) + { + for (const stochastic_action_summand& as: action_summands) + { + const data::data_expression& cond1=as.condition(); + if (((actiontime==as.multi_action().time()) || (!as.multi_action().has_time())) && + (implies_condition(cond,cond1))) + { + /* De delta summand is subsumed by action summand as. So, it does not + have to be added. */ + + return; + } + } + } + + for (deadlock_summand_vector::iterator i=deadlock_summands.begin(); i!=deadlock_summands.end(); ++i) + { + const deadlock_summand& smmnd=*i; + const data::data_expression& cond1=i->condition(); + if ((!ignore_time) && + ((actiontime==i->deadlock().time()) || (!i->deadlock().has_time())) && + (implies_condition(cond,cond1))) + { + /* put the summand that was effective in removing + this delta summand to the front, such that it + is encountered early later on, removing a next + delta summand */ + + copy(i,deadlock_summands.end(),back_inserter(result)); + deadlock_summands.swap(result); + return; + } + if (((ignore_time)|| + (((actiontime==smmnd.deadlock().time())|| (!s.deadlock().has_time())) && + (implies_condition(cond1,cond))))) + { + /* do not add summand to result, as it is superseded by s */ + } + else + { + result.push_back(smmnd); + } + } + + result.push_back(s); + deadlock_summands.swap(result); + } + } // namespace lps } // namespace mcrl2 diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index 4c757a4699..bad8c4044f 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -37,6 +37,7 @@ #include "mcrl2/lps/sumelm.h" #include "mcrl2/lps/constelm.h" #include "mcrl2/lps/replace_capture_avoiding_with_an_identifier_generator.h" +#include "mcrl2/lps/linearise_allow_block.h" #include "mcrl2/lps/linearise_utility.h" #include "mcrl2/lps/linearise_rename.h" @@ -794,43 +795,14 @@ class specification_basic_type /********** various functions on action and multi actions ***************/ - static - action_list linInsertActionInMultiActionList( - const action& act, - action_list multiAction) - { - /* store the action in the multiAction, alphabetically - sorted on the actionname in the actionId. Note that - the empty multiAction represents tau. */ - - if (multiAction.empty()) - { - return action_list({ act }); - } - const action firstAction=multiAction.front(); - /* Actions are compared on the basis of their position - in memory, to order them. As the aterm library maintains - pointers to objects that are not garbage collected, this - is a safe way to do this. */ - if (action_label_compare(act.label(),firstAction.label())) - { - multiAction.push_front(act); - return multiAction; - } - action_list result= linInsertActionInMultiActionList( - act, - multiAction.tail()); - result.push_front(firstAction); - return result; - } action_list linMergeMultiActionList(const action_list& ma1, const action_list& ma2) { action_list result=ma2; for (const action& a: ma1) { - result=linInsertActionInMultiActionList(a,result); + result=insert(a,result,action_compare); } return result; } @@ -5283,7 +5255,7 @@ class specification_basic_type deadlock_summands, deadlock_summand(sumvars, rewritten_condition, - has_time?deadlock(actTime):deadlock())); + has_time?deadlock(actTime):deadlock()),options.ignore_time); } else { @@ -7304,310 +7276,6 @@ class specification_basic_type } } - /**************** allow/block *************************************/ - - static - bool implies_condition(const data_expression& c1, const data_expression& c2) - { - if (c2==sort_bool::true_()) - { - return true; - } - - if (c1==sort_bool::false_()) - { - return true; - } - - if (c1==sort_bool::true_()) - { - return false; - } - - if (c2==sort_bool::false_()) - { - return false; - } - - if (c1==c2) - { - return true; - } - - /* Dealing with the conjunctions (&&) first and then the disjunctions (||) - yields a 10-fold speed increase compared to the case where first the - || occur, and then the &&. This result was measured on the alternating - bit protocol, with --regular. */ - - if (sort_bool::is_and_application(c2)) - { - return implies_condition(c1,data::binary_left(down_cast(c2))) && - implies_condition(c1,data::binary_right(down_cast(c2))); - } - - if (sort_bool::is_or_application(c1)) - { - return implies_condition(data::binary_left(down_cast(c1)),c2) && - implies_condition(data::binary_right(down_cast(c1)),c2); - } - - if (sort_bool::is_and_application(c1)) - { - return implies_condition(data::binary_left(down_cast(c1)),c2) || - implies_condition(data::binary_right(down_cast(c1)),c2); - } - - if (sort_bool::is_or_application(c2)) - { - return implies_condition(c1,data::binary_left(down_cast(c2))) || - implies_condition(c1,data::binary_right(down_cast(c2))); - } - - return false; - } - - void insert_timed_delta_summand( - const stochastic_action_summand_vector& action_summands, - deadlock_summand_vector& deadlock_summands, - const deadlock_summand& s) const - { - deadlock_summand_vector result; - - // const variable_list sumvars=s.summation_variables(); - const data_expression& cond=s.condition(); - const data_expression& actiontime=s.deadlock().time(); - - // First check whether the delta summand is subsumed by an action summands. - if (!options.ignore_time) - { - for (const stochastic_action_summand& as: action_summands) - { - const data_expression& cond1=as.condition(); - if (((actiontime==as.multi_action().time()) || (!as.multi_action().has_time())) && - (implies_condition(cond,cond1))) - { - /* De delta summand is subsumed by action summand as. So, it does not - have to be added. */ - - return; - } - } - } - - for (deadlock_summand_vector::iterator i=deadlock_summands.begin(); i!=deadlock_summands.end(); ++i) - { - const deadlock_summand& smmnd=*i; - const data_expression& cond1=i->condition(); - if ((!options.ignore_time) && - ((actiontime==i->deadlock().time()) || (!i->deadlock().has_time())) && - (implies_condition(cond,cond1))) - { - /* put the summand that was effective in removing - this delta summand to the front, such that it - is encountered early later on, removing a next - delta summand */ - - copy(i,deadlock_summands.end(),back_inserter(result)); - deadlock_summands.swap(result); - return; - } - if (((options.ignore_time)|| - (((actiontime==smmnd.deadlock().time())|| (!s.deadlock().has_time())) && - (implies_condition(cond1,cond))))) - { - /* do not add summand to result, as it is superseded by s */ - } - else - { - result.push_back(smmnd); - } - } - - result.push_back(s); - deadlock_summands.swap(result); - } - - static action_name_multiset_list sort_multi_action_labels(const action_name_multiset_list& l) - { - return action_name_multiset_list(l.begin(),l.end(),[](const action_name_multiset& al){ return sort_action_labels(al); }); - } - - /// \brief determine whether the multiaction has the same labels as the allow action, - // in which case true is delivered. If multiaction is the action Terminate, - // then true is also returned. - - static - bool allowsingleaction(const action_name_multiset& allowaction, - const action_list& multiaction) - { - /* The special cases where multiaction==tau and multiaction=={ Terminated } must have been - dealt with separately. */ - assert(multiaction.size()!=0 && multiaction != action_list({ terminationAction })); - - const identifier_string_list& names=allowaction.names(); - identifier_string_list::const_iterator i=names.begin(); - - for (action_list::const_iterator walker=multiaction.begin(); - walker!=multiaction.end(); ++walker,++i) - { - if (i==names.end()) - { - return false; - } - if (*i!=walker->label().name()) - { - return false; - } - } - return i==names.end(); - } - - bool allow_(const action_name_multiset_list& allowlist, - const action_list& multiaction) - { - /* The empty multiaction, i.e. tau, is never blocked by allow */ - if (multiaction.empty()) - { - return true; - } - - /* The multiaction is equal to the special Terminate action. This action cannot be blocked. */ - if (multiaction == action_list({ terminationAction })) - { - return true; - } - - for (action_name_multiset_list::const_iterator i=allowlist.begin(); - i!=allowlist.end(); ++i) - { - if (allowsingleaction(*i,multiaction)) - { - return true; - } - } - return false; - } - - static - bool encap(const action_name_multiset_list& encaplist, const action_list& multiaction) - { - for (const action& a: multiaction) - { - assert(encaplist.size()==1); - for (const identifier_string& s1: encaplist.front().names()) - { - const identifier_string s2=a.label().name(); - if (s1==s2) - { - return true; - } - } - } - return false; - } - - void allowblockcomposition( - const action_name_multiset_list& allowlist1, // This is a list of list of identifierstring. - const bool is_allow, - stochastic_action_summand_vector& action_summands, - deadlock_summand_vector& deadlock_summands) - { - /* This function calculates the allow or the block operator, - depending on whether is_allow is true */ - - stochastic_action_summand_vector sourcesumlist; - action_summands.swap(sourcesumlist); - - deadlock_summand_vector resultdeltasumlist; - deadlock_summand_vector resultsimpledeltasumlist; - deadlock_summands.swap(resultdeltasumlist); - - action_name_multiset_list allowlist((is_allow)?sort_multi_action_labels(allowlist1):allowlist1); - - std::size_t sourcesumlist_length=sourcesumlist.size(); - if (sourcesumlist_length>2 || is_allow) // This condition prevents this message to be printed - // when performing data elimination. In this case the - // term delta is linearised, to determine which data - // is essential for all processes. In these cases a - // message about the block operator is very confusing. - { - mCRL2log(mcrl2::log::verbose) << "- calculating the " << (is_allow?"allow":"block") << - " operator on " << sourcesumlist.size() << " action summands and " << resultdeltasumlist.size() << " delta summands"; - } - - /* First add the resulting sums in two separate lists - one for actions, and one for delta's. The delta's - are added at the end to the actions, where for - each delta summand it is determined whether it ought - to be added, or is superseded by an action or another - delta summand */ - for (const stochastic_action_summand& smmnd: sourcesumlist) - { - const variable_list& sumvars=smmnd.summation_variables(); - const action_list multiaction=smmnd.multi_action().actions(); - const data_expression& actiontime=smmnd.multi_action().time(); - const data_expression& condition=smmnd.condition(); - - // Explicitly allow the termination action in any allow. - if ((is_allow && allow_(allowlist,multiaction)) || - (!is_allow && !encap(allowlist,multiaction))) - { - action_summands.push_back(smmnd); - } - else - { - if (smmnd.has_time()) - { - resultdeltasumlist.push_back(deadlock_summand(sumvars, condition, deadlock(actiontime))); - } - else - { - // summand has no time. - if (condition==sort_bool::true_()) - { - resultsimpledeltasumlist.push_back(deadlock_summand(sumvars, condition, deadlock())); - } - else - { - resultdeltasumlist.push_back(deadlock_summand(sumvars, condition, deadlock())); - } - } - } - } - - if (options.nodeltaelimination) - { - deadlock_summands.swap(resultsimpledeltasumlist); - copy(resultdeltasumlist.begin(),resultdeltasumlist.end(),back_inserter(deadlock_summands)); - } - else - { - if (!options.ignore_time) /* if a delta summand is added, conditional, timed - delta's are subsumed and do not need to be added */ - { - for (const deadlock_summand& summand: resultsimpledeltasumlist) - { - insert_timed_delta_summand(action_summands,deadlock_summands,summand); - } - for (const deadlock_summand& summand: resultdeltasumlist) - { - insert_timed_delta_summand(action_summands,deadlock_summands,summand); - } - } - else - { - // Add a true -> delta - insert_timed_delta_summand(action_summands,deadlock_summands,deadlock_summand(variable_list(),sort_bool::true_(),deadlock())); - } - } - if (mCRL2logEnabled(mcrl2::log::verbose) && (sourcesumlist_length>2 || is_allow)) - { - mCRL2log(mcrl2::log::verbose) << ", resulting in " << action_summands.size() << " action summands and " << deadlock_summands.size() << " delta summands\n"; - } - } - - - /**************** equalargs ****************************************/ static @@ -7701,14 +7369,6 @@ class specification_basic_type /**************** communication operator composition ****************/ - static action_name_multiset sort_action_labels(const action_name_multiset& actionlabels) - { - return action_name_multiset(atermpp::sort_list( - actionlabels.names(), - [](const identifier_string& a1, const identifier_string& a2) - { return std::string(a1) static sort_expression_list get_sorts(const List& l) { @@ -7756,7 +7416,7 @@ class specification_basic_type for (std::size_t i=0; i Date: Sun, 5 Jan 2025 19:19:45 +0100 Subject: [PATCH 21/54] Add missing namespaces --- .../lps/include/mcrl2/lps/linearise_utility.h | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_utility.h b/libraries/lps/include/mcrl2/lps/linearise_utility.h index fba2ed8bf9..d9886056cd 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_utility.h +++ b/libraries/lps/include/mcrl2/lps/linearise_utility.h @@ -6,13 +6,16 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // -/// \file mcrl2/lps/linearise_utilities.h +/// \file mcrl2/lps/linearise_utility.h /// \brief Utilities used in linearisation. Mainly for actions and multiactions. #ifndef MCRL2_LPS_LINEARISE_UTILITY_H #define MCRL2_LPS_LINEARISE_UTILITY_H +#include "mcrl2/data/data_expression.h" #include "mcrl2/process/process_expression.h" +#include "mcrl2/lps/deadlock_summand.h" +#include "mcrl2/lps/stochastic_action_summand.h" namespace mcrl2 @@ -23,6 +26,7 @@ namespace lps /// Determine if a1 < a2; the key requirement is that orderings of action labels and the actions in multiactions are /// consistent. +inline bool action_label_compare(const process::action_label& a1, const process::action_label& a2) { /* first compare the strings in the actions */ @@ -43,6 +47,7 @@ bool action_label_compare(const process::action_label& a1, const process::action /// Determine if a1 < a2; the key requirement is that orderings of action labels and the actions in multiactions are /// consistent. +inline bool action_compare(const process::action& a1, const process::action& a2) { return action_label_compare(a1.label(), a2.label()); @@ -50,6 +55,7 @@ bool action_compare(const process::action& a1, const process::action& a2) /// Insert action into an action_list, keeping the action list sorted w.r.t. cmp. /// Complexity: O(n) for an action_list of length n. +inline process::action_list insert( const process::action& act, process::action_list l, @@ -125,26 +131,26 @@ bool implies_condition(const data::data_expression& c1, const data::data_express if (data::sort_bool::is_and_application(c2)) { - return implies_condition(c1,data::binary_left(down_cast(c2))) && - implies_condition(c1,data::binary_right(down_cast(c2))); + return implies_condition(c1,data::binary_left(atermpp::down_cast(c2))) && + implies_condition(c1,data::binary_right(atermpp::down_cast(c2))); } if (data::sort_bool::is_or_application(c1)) { - return implies_condition(data::binary_left(down_cast(c1)),c2) && - implies_condition(data::binary_right(down_cast(c1)),c2); + return implies_condition(data::binary_left(atermpp::down_cast(c1)),c2) && + implies_condition(data::binary_right(atermpp::down_cast(c1)),c2); } if (data::sort_bool::is_and_application(c1)) { - return implies_condition(data::binary_left(down_cast(c1)),c2) || - implies_condition(data::binary_right(down_cast(c1)),c2); + return implies_condition(data::binary_left(atermpp::down_cast(c1)),c2) || + implies_condition(data::binary_right(atermpp::down_cast(c1)),c2); } if (data::sort_bool::is_or_application(c2)) { - return implies_condition(c1,data::binary_left(down_cast(c2))) || - implies_condition(c1,data::binary_right(down_cast(c2))); + return implies_condition(c1,data::binary_left(atermpp::down_cast(c2))) || + implies_condition(c1,data::binary_right(atermpp::down_cast(c2))); } return false; From d27b4ce77d4a34341d151e892129454cf64c4426 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Sun, 5 Jan 2025 19:57:34 +0100 Subject: [PATCH 22/54] Simplify insert and fix regression test --- .../lps/include/mcrl2/lps/linearise_utility.h | 23 ++--- libraries/lps/source/linearise.cpp | 4 +- tests/regression/tickets/1114/1.aut | 85 +++++++++++++++++++ tests/regression/tickets/1114/2.aut | 3 + 4 files changed, 99 insertions(+), 16 deletions(-) create mode 100644 tests/regression/tickets/1114/1.aut create mode 100644 tests/regression/tickets/1114/2.aut diff --git a/libraries/lps/include/mcrl2/lps/linearise_utility.h b/libraries/lps/include/mcrl2/lps/linearise_utility.h index d9886056cd..92ad994e4d 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_utility.h +++ b/libraries/lps/include/mcrl2/lps/linearise_utility.h @@ -53,33 +53,28 @@ bool action_compare(const process::action& a1, const process::action& a2) return action_label_compare(a1.label(), a2.label()); } -/// Insert action into an action_list, keeping the action list sorted w.r.t. cmp. +/// Insert action into an action_list, keeping the action list sorted w.r.t. action_compare. /// Complexity: O(n) for an action_list of length n. inline process::action_list insert( - const process::action& act, - process::action_list l, - const std::function& cmp - = [](const process::action& t1, const process::action& t2){ return t1 Date: Mon, 6 Jan 2025 10:18:24 +0100 Subject: [PATCH 23/54] Extract calculation of the communication operator. --- .../mcrl2/lps/linearise_communication.h | 672 ++++++++++++++++++ .../lps/include/mcrl2/lps/linearise_utility.h | 106 +-- libraries/lps/source/linearise.cpp | 635 +---------------- 3 files changed, 737 insertions(+), 676 deletions(-) create mode 100644 libraries/lps/include/mcrl2/lps/linearise_communication.h diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h new file mode 100644 index 0000000000..2bd79adf7b --- /dev/null +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -0,0 +1,672 @@ +// Author(s): Jan Friso Groote, Jeroen Keiren +// Copyright: see the accompanying file COPYING or copy at +// https://github.com/mCRL2org/mCRL2/blob/master/COPYING +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +/// \file mcrl2/lps/linearise_communication.h +/// \brief Apply the rename operator to action summands. + +#ifndef MCRL2_LPS_LINEARISE_COMMUNICATION_H +#define MCRL2_LPS_LINEARISE_COMMUNICATION_H + +#include "mcrl2/atermpp/aterm_list.h" +#include "mcrl2/lps/linearise_allow_block.h" +#include "mcrl2/lps/linearise_utility.h" +#include "mcrl2/lps/sumelm.h" +#include "mcrl2/lps/stochastic_action_summand.h" +#include "mcrl2/process/process_expression.h" + + +namespace mcrl2 +{ + +namespace lps +{ + +// Check that the sorts of both termlists match. +inline +data::data_expression pairwiseMatch(const data::data_expression_list& l1, const data::data_expression_list& l2, const std::function& RewriteTerm) +{ + if (l1.size()!=l2.size()) + { + return data::sort_bool::false_(); + } + data::data_expression_list::const_iterator i2=l2.begin(); + data::data_expression result=data::sort_bool::true_(); + for(const data::data_expression& t1: l1) + { + if (t1.sort()!=i2->sort()) + { + return data::sort_bool::false_(); + } + result=data::lazy::and_(result,RewriteTerm(equal_to(t1,*i2))); + ++i2; + } + return result; +} + +// a tuple_list contains pairs of actions (multi-action) and the condition under which this action +// can occur. +struct tuple_list +{ + std::vector < process::action_list > actions; + std::vector < data::data_expression > conditions; +}; + +inline +tuple_list addActionCondition( + const process::action& firstaction, + const data::data_expression& condition, + const tuple_list& L, + tuple_list S) +{ + /* if firstaction==action(), it should not be added */ + assert(condition!=sort_bool::false_()); // It makes no sense to add an action with condition false, + // as it cannot happen anyhow. + for (std::size_t i=0; i; + + /// Left-hand sides of communication expressions + const std::vector m_lhs; + + /// Right-hand sides of communication expressions + const std::vector m_rhs; + + /// Caches. + std::unordered_map m_can_communicate_cache; + std::unordered_map m_might_communicate_cache; + + /// Temporary data using in determining whether communication is allowed. + /// See usages of the data structure below. + std::vector m_lhs_iters; // offset into lhs + std::vector m_match_failed; + + void reset_temporary_data() + { + for (std::size_t i = 0; i < size(); ++i) + { + m_lhs_iters[i] = m_lhs[i].begin(); + m_match_failed[i] = false; + } + } + + /// Check if m is contained in a lhs in the communication entry. + /// Returns true if this is the case, false otherwise. + /// Postcondition: for every i such that m is not contained in lhs[i], match_failed[i] is true. + /// NB: resets temporary data before performing computations. + bool match_multiaction(const process::action_list& m) { + reset_temporary_data(); + + // m must match a lhs; check every action + for (const process::action& a: m) + { + const core::identifier_string& actionname=a.label().name(); + + // check every lhs for actionname + bool comm_ok = false; + for (std::size_t i=0; i < size(); ++i) + { + if (m_match_failed[i]) // lhs i does not match + { + continue; + } + if (m_lhs_iters[i] == m_lhs[i].end()) // lhs cannot match actionname + { + m_match_failed[i]=true; + continue; + } + if (actionname != *m_lhs_iters[i]) + { + // no match + m_match_failed[i] = true; + } + else + { + // possible match; on to next action + ++m_lhs_iters[i]; + comm_ok = true; + } + } + + if (!comm_ok) // no (possibly) matching lhs + { + return false; + } + } + + // There must be an lhs that contains m. + return true; + } + + // Initialization of lhs, defined as static function so it can be used in the constructor. + // Allows lhs to be defined as const. + static std::vector < multi_action_name_t > init_lhs(const process::communication_expression_list& communications) + { + std::vector result; + for (const process::communication_expression& l: communications) + { + const core::identifier_string_list& names = l.action_name().names(); + result.emplace_back(names.begin(), names.end()); + } + return result; + } + + // Initialization of rhs, defined as static function so it can be used in the constructor. + // Allows rhs to be defined as const. + static std::vector init_rhs(const process::communication_expression_list& communications) + { + std::vector result; + for (const process::communication_expression& l: communications) + { + result.push_back(l.name()); + } + return result; + } + + public: + // comm_entries are not copyable. + comm_entry(const comm_entry& )=delete; + comm_entry& operator=(const comm_entry& )=delete; + + comm_entry(const process::communication_expression_list& communications) + : m_lhs(init_lhs(communications)), + m_rhs(init_rhs(communications)), + m_lhs_iters(communications.size()), + m_match_failed(communications.size()) + {} + + ~comm_entry() = default; + + std::size_t size() const + { + assert(lhs.size()==rhs.size() && rhs.size()==m_lhs_iters.size() && m_lhs_iters.size()==match_failed.size()); + return m_lhs.size(); + } + + /// Determine if there exists a communication expression a1|...|an -> b in comm_table + /// such that m' \subseteq a1|...|an , where m' is the multiset of actionnames for multiaction m. + process::action_label can_communicate(const process::action_list& m) + { + /* this function indicates whether the actions in m + consisting of actions and data occur in C, such that + a communication can take place. If not process::action_label() is delivered, + otherwise the resulting action is the result. */ + + // Check the cache first. + auto it = m_can_communicate_cache.find(m); + if(it != m_can_communicate_cache.end()) + { + return it->second; + } + + process::action_label result; // if no match fount, return process::action_label() + + if(match_multiaction(m)) + { + // there is a lhs containing m; find it + for (std::size_t i = 0; i < size(); ++i) + { + // lhs i matches only if comm_table[i] is empty + if ((!m_match_failed[i]) && m_lhs_iters[i] == m_lhs[i].end()) + { + if (m_rhs[i] == process::tau()) + { + throw mcrl2::runtime_error("Cannot linearise a process with a communication operator, containing a communication that results in tau or that has an empty right hand side"); + } + result = process::action_label(m_rhs[i], m.front().label().sorts()); + break; + } + } + } + + // cache the result + m_can_communicate_cache.insert({m, result}); + return result; + } + + bool might_communicate(const process::action_list& m, + process::action_list::const_iterator n_first, + process::action_list::const_iterator n_last) + { + /* this function indicates whether the actions in m + consisting of actions and data occur in C, such that + a communication might take place (i.e. m is a subbag + of the lhs of a communication in C). + if n is not empty, then all actions of a matching communication + that are not in m should be in n (i.e. there must be a + subbag o of n such that m+o can communicate. */ + + // Check the cache first. + if(auto it = m_might_communicate_cache.find(m); it != m_might_communicate_cache.end()) + { + return it->second; + } + + bool result = false; + + if(match_multiaction(m)) + { + // the rest of actions of lhs that are not in m should be in n + // rest[i] contains the part of n in which lhs i has to find matching actions + // if rest[i] cannot match the next remaining action in the left hand side, stored in m_lhs_iters[i], i.e., rest[i] becomes empty + // before matching all actions in the lhs, we set it to std::nullopt. + // N.B. when rest[i] becomes empty after matching all actions in the lhs, + // rest[i].empty() is a meaningful result: we have a successful match. + std::vector>> + rest(size(), std::make_pair(n_first, n_last)); // pairs of iterator into n; the second element of the pair indicates the end of the range in n. + + // check every lhs + for (std::size_t i = 0; i < size(); ++i) + { + if (m_match_failed[i]) // lhs i did not contain m + { + continue; + } + + // as long as there are still unmatched actions in lhs i... + while (m_lhs_iters[i] != m_lhs[i].end()) + { + assert(rest[i] != std::nullopt); + // .. find them in rest[i] + if (rest[i]->first == rest[i]->second) // no luck + { + rest[i] = std::nullopt; + break; + } + // get first action in lhs i + const core::identifier_string& commname = *m_lhs_iters[i]; + core::identifier_string restname = rest[i]->first->label().name(); + // find it in rest[i] + while (commname != restname) + { + ++(rest[i]->first); + if (rest[i]->first == rest[i]->second) // no more + { + rest[i] = std::nullopt; + break; + } + restname = rest[i]->first->label().name(); + } + if (commname != restname) // action was not found + { + break; + } + + // action found; try next + ++(rest[i]->first); + ++m_lhs_iters[i]; + } + + if (rest[i] != std::nullopt) // lhs was found in rest[i] + { + result = true; + break; + } + } + } + + // cache the result + m_might_communicate_cache.insert({m, result}); + return result; + } +}; + +/// \prototype +inline +tuple_list makeMultiActionConditionList_aux( + process::action_list::const_iterator multiaction_first, + process::action_list::const_iterator multiaction_last, + comm_entry& comm_table, + const process::action_list& r, + const std::function& RewriteTerm); + +inline +tuple_list phi(const process::action_list& m, + const data::data_expression_list& d, + const process::action_list& w, + const process::action_list::const_iterator& n_first, + const process::action_list::const_iterator& n_last, + const process::action_list& r, + comm_entry& comm_table, + const std::function& RewriteTerm) +{ + /* phi is a function that yields a list of pairs + indicating how the actions in m|w|n can communicate. + The pairs contain the resulting multi action and + a condition on data indicating when communication + can take place. In the communication all actions of + m, none of w and a subset of n can take part in the + communication. d is the data parameter of the communication + and C contains a list of multiaction action pairs indicating + possible communications */ + + if (!comm_table.might_communicate(m, n_first, n_last)) + { + return tuple_list(); + } + if (n_first == n_last) + { + process::action_label c = comm_table.can_communicate(m); /* returns process::action_label() if no communication + is possible */ + if (c!=process::action_label()) + { + const tuple_list T=makeMultiActionConditionList_aux(w.begin(), w.end(),comm_table,r,RewriteTerm); + return addActionCondition( + (c==process::action_label()?process::action():process::action(c,d)), //Check. Nil kan niet geleverd worden. + data::sort_bool::true_(), + T, + tuple_list()); + } + /* c==NULL, actions in m cannot communicate */ + return tuple_list(); + } + /* if n=[a(f)] \oplus o */ + const process::action& firstaction=*n_first; + + const data::data_expression condition=pairwiseMatch(d,firstaction.arguments(),RewriteTerm); + if (condition==data::sort_bool::false_()) + { + process::action_list tempw=w; + tempw=push_back(tempw,firstaction); + return phi(m,d,tempw,std::next(n_first), n_last,r,comm_table,RewriteTerm); + } + else + { + process::action_list tempm=m; + tempm=push_back(tempm,firstaction); + const tuple_list T=phi(tempm,d,w,std::next(n_first), n_last,r,comm_table,RewriteTerm); + process::action_list tempw=w; + tempw=push_back(tempw,firstaction); + return addActionCondition( + process::action(), + condition, + T, + phi(m,d,tempw,std::next(n_first), n_last,r,comm_table,RewriteTerm)); + } +} + +inline +bool xi(const process::action_list& alpha, const process::action_list& beta, comm_entry& comm_table) +{ + if (beta.empty()) + { + return comm_table.can_communicate(alpha)!=process::action_label(); + } + else + { + const process::action& a = beta.front(); + process::action_list l=alpha; + l=push_back(l,a); + const process::action_list& beta_next = beta.tail(); + + if (comm_table.can_communicate(l)!=process::action_label()) + { + return true; + } + else if (comm_table.might_communicate(l,beta_next.begin(), beta_next.end())) + { + return xi(l,beta_next,comm_table) || xi(alpha,beta_next,comm_table); + } + else + { + return xi(alpha,beta_next,comm_table); + } + } +} + +inline +data::data_expression psi(const process::action_list& alpha_in, comm_entry& comm_table, const std::function& RewriteTerm) +{ + process::action_list alpha=reverse(alpha_in); + data::data_expression cond = data::sort_bool::false_(); + + process::action a; // a and beta used in the loop; avoid repeated allocation and deallocation + process::action_list beta; + process::action_list actl; // used in inner loop. + while (!alpha.empty()) + { + a = alpha.front(); + beta = alpha.tail(); + + while (!beta.empty()) + { + actl = process::action_list(); + actl.emplace_front(beta.front()); + actl.emplace_front(a); + const process::action_list& beta_tail = beta.tail(); + if (comm_table.might_communicate(actl,beta_tail.begin(), beta_tail.end()) && xi(actl,beta.tail(),comm_table)) + { + // sort and remove duplicates?? + cond = data::lazy::or_(cond,pairwiseMatch(a.arguments(),beta.front().arguments(),RewriteTerm)); + } + beta.pop_front(); + } + + alpha.pop_front(); + } + return data::lazy::not_(cond); +} + +// returns a list of tuples. +inline +tuple_list makeMultiActionConditionList_aux( + process::action_list::const_iterator multiaction_first, + process::action_list::const_iterator multiaction_last, + comm_entry& comm_table, + const process::action_list& r, + const std::function& RewriteTerm) +{ + /* This is the function gamma(m,C,r) provided + by Muck van Weerdenburg in Calculation of + Communication with open terms [1]. */ + if (multiaction_first == multiaction_last) + { + tuple_list t; + t.conditions.push_back((r.empty())?static_cast(data::sort_bool::true_()):psi(r,comm_table,RewriteTerm)); + t.actions.push_back(process::action_list()); + return t; + } + + const process::action& firstaction=*multiaction_first; + + const tuple_list S=phi(process::action_list({ firstaction }), + firstaction.arguments(), + process::action_list(), + std::next(multiaction_first), multiaction_last, + r,comm_table, RewriteTerm); + process::action_list tempr=r; + tempr.push_front(firstaction); + const tuple_list T=makeMultiActionConditionList_aux(std::next(multiaction_first), multiaction_last,comm_table, + tempr,RewriteTerm); + return addActionCondition(firstaction,data::sort_bool::true_(),T,S); +} + +inline +tuple_list makeMultiActionConditionList( + const process::action_list& multiaction, + const process::communication_expression_list& communications, + const std::function& RewriteTerm) +{ + comm_entry comm_table(communications); + return makeMultiActionConditionList_aux(multiaction.begin(), multiaction.end(),comm_table,process::action_list(),RewriteTerm); +} + +inline +void communicationcomposition( + const process::communication_expression_list& communications, + const process::action_name_multiset_list& allowlist1, // This is a list of list of identifierstring. + const bool is_allow, // If is_allow or is_block is set, perform inline allow/block filtering. + const bool is_block, + stochastic_action_summand_vector& action_summands, + deadlock_summand_vector& deadlock_summands, + const process::action& terminationAction, + const bool nosumelm, + const bool nodeltaelimination, + const bool ignore_time, + const std::function& RewriteTerm) + +{ + /* We follow the implementation of Muck van Weerdenburg, described in + a note: Calculation of communication with open terms. */ + + mCRL2log(mcrl2::log::verbose) << + (is_allow ? "- calculating the communication operator modulo the allow operator on " : + is_block ? "- calculating the communication operator modulo the block operator on " : + "- calculating the communication operator on ") << action_summands.size() << " action summands"; + + /* first we sort the multiactions in communications */ + process::communication_expression_list sorted_communications; + + for (const process::communication_expression& comm: communications) + { + const process::action_name_multiset& source=comm.action_name(); + const core::identifier_string& target=comm.name(); + sorted_communications.push_front(process::communication_expression(sort_action_labels(source),target)); + } + + stochastic_action_summand_vector resultsumlist; + deadlock_summand_vector resultingDeltaSummands; + deadlock_summands.swap(resultingDeltaSummands); + + const bool inline_allow = is_allow || is_block; + if (inline_allow) + { + // Inline allow is only supported for ignore_time, + // for in other cases generation of delta summands cannot be inlined in any simple way. + assert(!options.nodeltaelimination && options.ignore_time); + deadlock_summands.push_back(deadlock_summand(data::variable_list(), data::sort_bool::true_(),deadlock())); + } + process::action_name_multiset_list allowlist((is_allow)?sort_multi_action_labels(allowlist1):allowlist1); + + for (const stochastic_action_summand& smmnd: action_summands) + { + const data::variable_list& sumvars=smmnd.summation_variables(); + const process::action_list& multiaction=smmnd.multi_action().actions(); + const data::data_expression& condition=smmnd.condition(); + const data::assignment_list& nextstate=smmnd.assignments(); + const stochastic_distribution& dist=smmnd.distribution(); + + if (!inline_allow) + { + /* Recall a delta summand for every non delta summand. + * The reason for this is that with communication, the + * conditions for summands can become much more complex. + * Many of the actions in these summands are replaced by + * delta's later on. Due to the more complex conditions it + * will be hard to remove them. By adding a default delta + * with a simple condition, makes this job much easier + * later on, and will in general reduce the number of delta + * summands in the whole system */ + + /* But first remove free variables from sumvars */ + + data::variable_vector newsumvars_; + for (const data::variable& sumvar: sumvars) + { + if (occursinterm(sumvar,condition) || + (smmnd.has_time() && occursinterm(sumvar,smmnd.multi_action().time()))) + { + newsumvars_.push_back(sumvar); + } + } + data::variable_list newsumvars=data::variable_list(newsumvars_.begin(), newsumvars_.end()); + + resultingDeltaSummands.push_back(deadlock_summand(newsumvars, + condition, + smmnd.multi_action().has_time()?deadlock(smmnd.multi_action().time()):deadlock())); + } + + /* the multiactionconditionlist is a list containing + tuples, with a multiaction and the condition, + expressing whether the multiaction can happen. All + conditions exclude each other. Furthermore, the list + is not empty. If no communications can take place, + the original multiaction is delivered, with condition + true. */ + + const tuple_list multiactionconditionlist= + makeMultiActionConditionList( + multiaction, + sorted_communications, + RewriteTerm); + + assert(multiactionconditionlist.actions.size()== + multiactionconditionlist.conditions.size()); + for (std::size_t i=0 ; icondition(); - if ((!ignore_time) && - ((actiontime==i->deadlock().time()) || (!i->deadlock().has_time())) && - (implies_condition(cond,cond1))) - { - /* put the summand that was effective in removing - this delta summand to the front, such that it - is encountered early later on, removing a next - delta summand */ - - copy(i,deadlock_summands.end(),back_inserter(result)); - deadlock_summands.swap(result); - return; - } - if (((ignore_time)|| - (((actiontime==smmnd.deadlock().time())|| (!s.deadlock().has_time())) && - (implies_condition(cond1,cond))))) - { - /* do not add summand to result, as it is superseded by s */ - } - else - { - result.push_back(smmnd); - } + return; } + } + } + + for (deadlock_summand_vector::iterator i=deadlock_summands.begin(); i!=deadlock_summands.end(); ++i) + { + const deadlock_summand& smmnd=*i; + const data::data_expression& cond1=i->condition(); + if ((!ignore_time) && + ((actiontime==i->deadlock().time()) || (!i->deadlock().has_time())) && + (implies_condition(cond,cond1))) + { + /* put the summand that was effective in removing + this delta summand to the front, such that it + is encountered early later on, removing a next + delta summand */ - result.push_back(s); + copy(i,deadlock_summands.end(),back_inserter(result)); deadlock_summands.swap(result); + return; } + if (((ignore_time)|| + (((actiontime==smmnd.deadlock().time())|| (!s.deadlock().has_time())) && + (implies_condition(cond1,cond))))) + { + /* do not add summand to result, as it is superseded by s */ + } + else + { + result.push_back(smmnd); + } + } + + result.push_back(s); + deadlock_summands.swap(result); +} + +inline +bool occursinterm(const data::variable& var, const data::data_expression& t) +{ + return data::search_free_variable(t, var); +} } // namespace lps diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index 1f5f66b6d1..8ec2204894 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -32,14 +32,16 @@ #include "mcrl2/data/real_utilities.h" // linear process libraries. -#include "mcrl2/lps/detail/ultimate_delay.h" -#include "mcrl2/lps/linearise.h" -#include "mcrl2/lps/sumelm.h" #include "mcrl2/lps/constelm.h" -#include "mcrl2/lps/replace_capture_avoiding_with_an_identifier_generator.h" +#include "mcrl2/lps/linearise.h" #include "mcrl2/lps/linearise_allow_block.h" #include "mcrl2/lps/linearise_utility.h" #include "mcrl2/lps/linearise_rename.h" +#include "mcrl2/lps/linearise_communication.h" +#include "mcrl2/lps/replace_capture_avoiding_with_an_identifier_generator.h" +#include "mcrl2/lps/sumelm.h" + +#include "mcrl2/lps/detail/ultimate_delay.h" // Process libraries. #include "mcrl2/process/alphabet_reduce.h" @@ -1226,12 +1228,6 @@ class specification_basic_type /**************** occursinterm *** occursintermlist ***********/ - static - bool occursinterm(const variable& var, const data_expression& t) - { - return data::search_free_variable(t, var); - } - void filter_vars_by_term( const data_expression& t, const std::set < variable >& vars_set, @@ -7375,619 +7371,6 @@ class specification_basic_type return sort_expression_list(l.begin(), l.end(), [](const typename List::value_type& d) -> sort_expression {return d.sort();}); } - // Check that the sorts of both termlists match. - data_expression pairwiseMatch(const data_expression_list& l1, const data_expression_list& l2) - { - if (l1.size()!=l2.size()) - { - return sort_bool::false_(); - } - data_expression_list::const_iterator i2=l2.begin(); - data_expression result=sort_bool::true_(); - for(const data_expression& t1: l1) - { - if (t1.sort()!=i2->sort()) - { - return sort_bool::false_(); - } - result=lazy::and_(result,RewriteTerm(equal_to(t1,*i2))); - ++i2; - } - return result; - } - - // a tuple_list contains pairs of actions (multi-action) and the condition under which this action - // can occur. - struct tuple_list - { - std::vector < action_list > actions; - std::vector < data_expression > conditions; - }; - - tuple_list addActionCondition( - const action& firstaction, - const data_expression& condition, - const tuple_list& L, - tuple_list S) - { - /* if firstaction==action(), it should not be added */ - assert(condition!=sort_bool::false_()); // It makes no sense to add an action with condition false, - // as it cannot happen anyhow. - for (std::size_t i=0; i; - - /// Left-hand sides of communication expressions - const std::vector m_lhs; - - /// Right-hand sides of communication expressions - const std::vector m_rhs; - - /// Caches. - std::unordered_map m_can_communicate_cache; - std::unordered_map m_might_communicate_cache; - - /// Temporary data using in determining whether communication is allowed. - /// See usages of the data structure below. - std::vector m_lhs_iters; // offset into lhs - std::vector m_match_failed; - - void reset_temporary_data() - { - for (std::size_t i = 0; i < size(); ++i) - { - m_lhs_iters[i] = m_lhs[i].begin(); - m_match_failed[i] = false; - } - } - - /// Check if m is contained in a lhs in the communication entry. - /// Returns true if this is the case, false otherwise. - /// Postcondition: for every i such that m is not contained in lhs[i], match_failed[i] is true. - /// NB: resets temporary data before performing computations. - bool match_multiaction(const action_list& m) { - reset_temporary_data(); - - // m must match a lhs; check every action - for (const action& a: m) - { - const identifier_string& actionname=a.label().name(); - - // check every lhs for actionname - bool comm_ok = false; - for (std::size_t i=0; i < size(); ++i) - { - if (m_match_failed[i]) // lhs i does not match - { - continue; - } - if (m_lhs_iters[i] == m_lhs[i].end()) // lhs cannot match actionname - { - m_match_failed[i]=true; - continue; - } - if (actionname != *m_lhs_iters[i]) - { - // no match - m_match_failed[i] = true; - } - else - { - // possible match; on to next action - ++m_lhs_iters[i]; - comm_ok = true; - } - } - - if (!comm_ok) // no (possibly) matching lhs - { - return false; - } - } - - // There must be an lhs that contains m. - return true; - } - - // Initialization of lhs, defined as static function so it can be used in the constructor. - // Allows lhs to be defined as const. - static std::vector < multi_action_name_t > init_lhs(const communication_expression_list& communications) - { - std::vector result; - for (const communication_expression& l: communications) - { - const identifier_string_list& names = l.action_name().names(); - result.emplace_back(names.begin(), names.end()); - } - return result; - } - - // Initialization of rhs, defined as static function so it can be used in the constructor. - // Allows rhs to be defined as const. - static std::vector init_rhs(const communication_expression_list& communications) - { - std::vector result; - for (const communication_expression& l: communications) - { - result.push_back(l.name()); - } - return result; - } - - public: - // comm_entries are not copyable. - comm_entry(const comm_entry& )=delete; - comm_entry& operator=(const comm_entry& )=delete; - - comm_entry(const communication_expression_list& communications) - : m_lhs(init_lhs(communications)), - m_rhs(init_rhs(communications)), - m_lhs_iters(communications.size()), - m_match_failed(communications.size()) - {} - - ~comm_entry() = default; - - std::size_t size() const - { - assert(lhs.size()==rhs.size() && rhs.size()==m_lhs_iters.size() && m_lhs_iters.size()==match_failed.size()); - return m_lhs.size(); - } - - /// Determine if there exists a communication expression a1|...|an -> b in comm_table - /// such that m' \subseteq a1|...|an , where m' is the multiset of actionnames for multiaction m. - process::action_label can_communicate(const action_list& m) - { - /* this function indicates whether the actions in m - consisting of actions and data occur in C, such that - a communication can take place. If not action_label() is delivered, - otherwise the resulting action is the result. */ - - // Check the cache first. - auto it = m_can_communicate_cache.find(m); - if(it != m_can_communicate_cache.end()) - { - return it->second; - } - - action_label result; // if no match fount, return action_label() - - if(match_multiaction(m)) - { - // there is a lhs containing m; find it - for (std::size_t i = 0; i < size(); ++i) - { - // lhs i matches only if comm_table[i] is empty - if ((!m_match_failed[i]) && m_lhs_iters[i] == m_lhs[i].end()) - { - if (m_rhs[i] == tau()) - { - throw mcrl2::runtime_error("Cannot linearise a process with a communication operator, containing a communication that results in tau or that has an empty right hand side"); - } - result = action_label(m_rhs[i], m.front().label().sorts()); - break; - } - } - } - - // cache the result - m_can_communicate_cache.insert({m, result}); - return result; - } - - bool might_communicate(const action_list& m, - action_list::const_iterator n_first, - action_list::const_iterator n_last) - { - /* this function indicates whether the actions in m - consisting of actions and data occur in C, such that - a communication might take place (i.e. m is a subbag - of the lhs of a communication in C). - if n is not empty, then all actions of a matching communication - that are not in m should be in n (i.e. there must be a - subbag o of n such that m+o can communicate. */ - - // Check the cache first. - if(auto it = m_might_communicate_cache.find(m); it != m_might_communicate_cache.end()) - { - return it->second; - } - - bool result = false; - - if(match_multiaction(m)) - { - // the rest of actions of lhs that are not in m should be in n - // rest[i] contains the part of n in which lhs i has to find matching actions - // if rest[i] cannot match the next remaining action in the left hand side, stored in m_lhs_iters[i], i.e., rest[i] becomes empty - // before matching all actions in the lhs, we set it to std::nullopt. - // N.B. when rest[i] becomes empty after matching all actions in the lhs, - // rest[i].empty() is a meaningful result: we have a successful match. - std::vector>> - rest(size(), std::make_pair(n_first, n_last)); // pairs of iterator into n; the second element of the pair indicates the end of the range in n. - - // check every lhs - for (std::size_t i = 0; i < size(); ++i) - { - if (m_match_failed[i]) // lhs i did not contain m - { - continue; - } - - // as long as there are still unmatched actions in lhs i... - while (m_lhs_iters[i] != m_lhs[i].end()) - { - assert(rest[i] != std::nullopt); - // .. find them in rest[i] - if (rest[i]->first == rest[i]->second) // no luck - { - rest[i] = std::nullopt; - break; - } - // get first action in lhs i - const identifier_string& commname = *m_lhs_iters[i]; - identifier_string restname = rest[i]->first->label().name(); - // find it in rest[i] - while (commname != restname) - { - ++(rest[i]->first); - if (rest[i]->first == rest[i]->second) // no more - { - rest[i] = std::nullopt; - break; - } - restname = rest[i]->first->label().name(); - } - if (commname != restname) // action was not found - { - break; - } - - // action found; try next - ++(rest[i]->first); - ++m_lhs_iters[i]; - } - - if (rest[i] != std::nullopt) // lhs was found in rest[i] - { - result = true; - break; - } - } - } - - // cache the result - m_might_communicate_cache.insert({m, result}); - return result; - } - }; - - tuple_list phi(const action_list& m, - const data_expression_list& d, - const action_list& w, - const action_list::const_iterator& n_first, - const action_list::const_iterator& n_last, - const action_list& r, - comm_entry& comm_table) - { - /* phi is a function that yields a list of pairs - indicating how the actions in m|w|n can communicate. - The pairs contain the resulting multi action and - a condition on data indicating when communication - can take place. In the communication all actions of - m, none of w and a subset of n can take part in the - communication. d is the data parameter of the communication - and C contains a list of multiaction action pairs indicating - possible communications */ - - if (!comm_table.might_communicate(m, n_first, n_last)) - { - return tuple_list(); - } - if (n_first == n_last) - { - action_label c = comm_table.can_communicate(m); /* returns action_label() if no communication - is possible */ - if (c!=action_label()) - { - const tuple_list T=makeMultiActionConditionList_aux(w.begin(), w.end(),comm_table,r); - return addActionCondition( - (c==action_label()?action():action(c,d)), //Check. Nil kan niet geleverd worden. - sort_bool::true_(), - T, - tuple_list()); - } - /* c==NULL, actions in m cannot communicate */ - return tuple_list(); - } - /* if n=[a(f)] \oplus o */ - const action& firstaction=*n_first; - - const data_expression condition=pairwiseMatch(d,firstaction.arguments()); - if (condition==sort_bool::false_()) - { - action_list tempw=w; - tempw=push_back(tempw,firstaction); - return phi(m,d,tempw,std::next(n_first), n_last,r,comm_table); - } - else - { - action_list tempm=m; - tempm=push_back(tempm,firstaction); - const tuple_list T=phi(tempm,d,w,std::next(n_first), n_last,r,comm_table); - action_list tempw=w; - tempw=push_back(tempw,firstaction); - return addActionCondition( - action(), - condition, - T, - phi(m,d,tempw,std::next(n_first), n_last,r,comm_table)); - } - } - - static - bool xi(const action_list& alpha, const action_list& beta, comm_entry& comm_table) - { - if (beta.empty()) - { - return comm_table.can_communicate(alpha)!=action_label(); - } - else - { - const action& a = beta.front(); - action_list l=alpha; - l=push_back(l,a); - const action_list& beta_next = beta.tail(); - - if (comm_table.can_communicate(l)!=action_label()) - { - return true; - } - else if (comm_table.might_communicate(l,beta_next.begin(), beta_next.end())) - { - return xi(l,beta_next,comm_table) || xi(alpha,beta_next,comm_table); - } - else - { - return xi(alpha,beta_next,comm_table); - } - } - } - - data_expression psi(const action_list& alpha_in, comm_entry& comm_table) - { - action_list alpha=reverse(alpha_in); - data_expression cond = sort_bool::false_(); - - action a; // a and beta used in the loop; avoid repeated allocation and deallocation - action_list beta; - action_list actl; // used in inner loop. - while (!alpha.empty()) - { - a = alpha.front(); - beta = alpha.tail(); - - while (!beta.empty()) - { - actl = action_list(); - actl.emplace_front(beta.front()); - actl.emplace_front(a); - const action_list& beta_tail = beta.tail(); - if (comm_table.might_communicate(actl,beta_tail.begin(), beta_tail.end()) && xi(actl,beta.tail(),comm_table)) - { - // sort and remove duplicates?? - cond = lazy::or_(cond,pairwiseMatch(a.arguments(),beta.front().arguments())); - } - beta.pop_front(); - } - - alpha.pop_front(); - } - return lazy::not_(cond); - } - - // returns a list of tuples. - tuple_list makeMultiActionConditionList_aux( - action_list::const_iterator multiaction_first, - action_list::const_iterator multiaction_last, - comm_entry& comm_table, - const action_list& r) - { - /* This is the function gamma(m,C,r) provided - by Muck van Weerdenburg in Calculation of - Communication with open terms [1]. */ - if (multiaction_first == multiaction_last) - { - tuple_list t; - t.conditions.push_back((r.empty())?static_cast(sort_bool::true_()):psi(r,comm_table)); - t.actions.push_back(action_list()); - return t; - } - - const action& firstaction=*multiaction_first; - - const tuple_list S=phi(action_list({ firstaction }), - firstaction.arguments(), - action_list(), - std::next(multiaction_first), multiaction_last, - r,comm_table); - action_list tempr=r; - tempr.push_front(firstaction); - const tuple_list T=makeMultiActionConditionList_aux(std::next(multiaction_first), multiaction_last,comm_table, - tempr); - return addActionCondition(firstaction,sort_bool::true_(),T,S); - } - - tuple_list makeMultiActionConditionList( - const action_list& multiaction, - const communication_expression_list& communications) - { - comm_entry comm_table(communications); - return makeMultiActionConditionList_aux(multiaction.begin(), multiaction.end(),comm_table,action_list()); - } - - void communicationcomposition( - const communication_expression_list& communications, - const action_name_multiset_list& allowlist1, // This is a list of list of identifierstring. - const bool is_allow, // If is_allow or is_block is set, perform inline allow/block filtering. - const bool is_block, - stochastic_action_summand_vector& action_summands, - deadlock_summand_vector& deadlock_summands) - - { - /* We follow the implementation of Muck van Weerdenburg, described in - a note: Calculation of communication with open terms. */ - - mCRL2log(mcrl2::log::verbose) << - (is_allow ? "- calculating the communication operator modulo the allow operator on " : - is_block ? "- calculating the communication operator modulo the block operator on " : - "- calculating the communication operator on ") << action_summands.size() << " action summands"; - - /* first we sort the multiactions in communications */ - communication_expression_list sorted_communications; - - for (const communication_expression& comm: communications) - { - const action_name_multiset& source=comm.action_name(); - const identifier_string& target=comm.name(); - sorted_communications.push_front(communication_expression(sort_action_labels(source),target)); - } - - stochastic_action_summand_vector resultsumlist; - deadlock_summand_vector resultingDeltaSummands; - deadlock_summands.swap(resultingDeltaSummands); - - const bool inline_allow = is_allow || is_block; - if (inline_allow) - { - // Inline allow is only supported for ignore_time, - // for in other cases generation of delta summands cannot be inlined in any simple way. - assert(!options.nodeltaelimination && options.ignore_time); - deadlock_summands.push_back(deadlock_summand(variable_list(),sort_bool::true_(),deadlock())); - } - action_name_multiset_list allowlist((is_allow)?sort_multi_action_labels(allowlist1):allowlist1); - - for (const stochastic_action_summand& smmnd: action_summands) - { - const variable_list& sumvars=smmnd.summation_variables(); - const action_list& multiaction=smmnd.multi_action().actions(); - const data_expression& condition=smmnd.condition(); - const assignment_list& nextstate=smmnd.assignments(); - const stochastic_distribution& dist=smmnd.distribution(); - - if (!inline_allow) - { - /* Recall a delta summand for every non delta summand. - * The reason for this is that with communication, the - * conditions for summands can become much more complex. - * Many of the actions in these summands are replaced by - * delta's later on. Due to the more complex conditions it - * will be hard to remove them. By adding a default delta - * with a simple condition, makes this job much easier - * later on, and will in general reduce the number of delta - * summands in the whole system */ - - /* But first remove free variables from sumvars */ - - variable_vector newsumvars_; - for (const variable& sumvar: sumvars) - { - if (occursinterm(sumvar,condition) || - (smmnd.has_time() && occursinterm(sumvar,smmnd.multi_action().time()))) - { - newsumvars_.push_back(sumvar); - } - } - variable_list newsumvars=variable_list(newsumvars_.begin(), newsumvars_.end()); - - resultingDeltaSummands.push_back(deadlock_summand(newsumvars, - condition, - smmnd.multi_action().has_time()?deadlock(smmnd.multi_action().time()):deadlock())); - } - - /* the multiactionconditionlist is a list containing - tuples, with a multiaction and the condition, - expressing whether the multiaction can happen. All - conditions exclude each other. Furthermore, the list - is not empty. If no communications can take place, - the original multiaction is delivered, with condition - true. */ - - const tuple_list multiactionconditionlist= - makeMultiActionConditionList( - multiaction, - sorted_communications); - - assert(multiactionconditionlist.actions.size()== - multiactionconditionlist.conditions.size()); - for (std::size_t i=0 ; i Date: Mon, 6 Jan 2025 10:20:16 +0100 Subject: [PATCH 24/54] Remove accidenally committed file --- .idea/editor.xml | 580 ----------------------------------------------- 1 file changed, 580 deletions(-) delete mode 100644 .idea/editor.xml diff --git a/.idea/editor.xml b/.idea/editor.xml deleted file mode 100644 index 184ac5e44d..0000000000 --- a/.idea/editor.xml +++ /dev/null @@ -1,580 +0,0 @@ - - - - - \ No newline at end of file From 42711e7f7da78bb3527e1a31eb8e9b229a23f0a4 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Mon, 6 Jan 2025 10:21:15 +0100 Subject: [PATCH 25/54] Update .gitignore Ignore IDE files for Jetbrains --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 52d8b1c637..a4af938140 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ build/ .cache # Ignore IDE files +.idea .vscode # Ignore the python virtual environment From 91dc910398dbf902905f4de2e9e1cc886597117b Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Mon, 6 Jan 2025 10:32:32 +0100 Subject: [PATCH 26/54] Swap arguments of occursinterm This now allows removing occursinterm in favour of search_free_variable. --- .../mcrl2/lps/linearise_communication.h | 4 ++-- .../lps/include/mcrl2/lps/linearise_utility.h | 2 +- libraries/lps/source/linearise.cpp | 20 +++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h index 2bd79adf7b..bb22a969dd 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_communication.h +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -580,8 +580,8 @@ void communicationcomposition( data::variable_vector newsumvars_; for (const data::variable& sumvar: sumvars) { - if (occursinterm(sumvar,condition) || - (smmnd.has_time() && occursinterm(sumvar,smmnd.multi_action().time()))) + if (occursinterm(condition, sumvar) || + (smmnd.has_time() && occursinterm(smmnd.multi_action().time(), sumvar))) { newsumvars_.push_back(sumvar); } diff --git a/libraries/lps/include/mcrl2/lps/linearise_utility.h b/libraries/lps/include/mcrl2/lps/linearise_utility.h index 96b5b9eb71..ac1d7d9930 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_utility.h +++ b/libraries/lps/include/mcrl2/lps/linearise_utility.h @@ -215,7 +215,7 @@ void insert_timed_delta_summand( } inline -bool occursinterm(const data::variable& var, const data::data_expression& t) +bool occursinterm(const data::data_expression& t, const data::variable& var) { return data::search_free_variable(t, var); } diff --git a/libraries/lps/source/linearise.cpp b/libraries/lps/source/linearise.cpp index 8ec2204894..a32625966d 100644 --- a/libraries/lps/source/linearise.cpp +++ b/libraries/lps/source/linearise.cpp @@ -1282,7 +1282,7 @@ class specification_basic_type { for (const data_expression& d: r) { - if (occursinterm(var,d)) + if (occursinterm(d, var)) { return true; } @@ -1295,7 +1295,7 @@ class specification_basic_type std::set assigned_variables; for (const assignment& l: r) { - if (occursinterm(var,l.rhs())) + if (occursinterm(l.rhs(), var)) { return true; } @@ -1371,7 +1371,7 @@ class specification_basic_type } if (is_if_then(p)) { - return occursinterm(var,if_then(p).condition())|| + return occursinterm(if_then(p).condition(), var) || occursinpCRLterm(var,if_then(p).then_case(),strict); } @@ -1393,13 +1393,13 @@ class specification_basic_type if (strict) { return occursintermlist(var,variable_list_to_data_expression_list(sto.variables())) || - occursinterm(var,sto.distribution()) || + occursinterm(sto.distribution(), var) || occursinpCRLterm(var,sto.operand(),strict); } else { return (!occursintermlist(var,variable_list_to_data_expression_list(sto.variables()))) && - (occursinterm(var,sto.distribution()) || + (occursinterm(sto.distribution(), var) || occursinpCRLterm(var,sto.operand(),strict)); } } @@ -1418,7 +1418,7 @@ class specification_basic_type } if (is_at(p)) { - return occursinterm(var,at(p).time_stamp()) || + return occursinterm(at(p).time_stamp(), var) || occursinpCRLterm(var,at(p).operand(),strict); } if (is_delta(p)) @@ -7384,7 +7384,7 @@ class specification_basic_type if (is_variable(actiontime)) { const variable& t = atermpp::down_cast(actiontime); - if (occursintermlist(t, variable_list_to_data_expression_list(sumvars)) && !occursinterm(t, condition)) + if (occursintermlist(t, variable_list_to_data_expression_list(sumvars)) && !occursinterm(condition, t)) { return true; } @@ -7452,7 +7452,7 @@ class specification_basic_type } for (const variable& v: freevars) { - if (occursinterm(v,result)) + if (occursinterm(result, v)) { variables.push_front(v); } @@ -7460,7 +7460,7 @@ class specification_basic_type for (const variable& v: global_variables) { - if (occursinterm(v,result)) + if (occursinterm(result, v)) { variables.push_front(v); } @@ -7468,7 +7468,7 @@ class specification_basic_type for (const variable& v: sumvars) { - if (occursinterm(v,result)) + if (occursinterm(result, v)) { used_sumvars.push_front(v); } From 51e3b0eb98d923171aaab78b487a9c4a5d40f368 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Mon, 6 Jan 2025 10:36:10 +0100 Subject: [PATCH 27/54] Extract method to improve readability --- .../lps/include/mcrl2/lps/linearise_utility.h | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_utility.h b/libraries/lps/include/mcrl2/lps/linearise_utility.h index ac1d7d9930..8b1b100f94 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_utility.h +++ b/libraries/lps/include/mcrl2/lps/linearise_utility.h @@ -151,6 +151,14 @@ bool implies_condition(const data::data_expression& c1, const data::data_express return false; } +/// Determine if action summand as subsumes delta summand ds. +inline +bool subsumes(const stochastic_action_summand& as, const deadlock_summand& ds) +{ + return (!as.multi_action().has_time() || ds.deadlock().time() == as.multi_action().time()) + && implies_condition(ds.condition(), as.condition()); +} + inline void insert_timed_delta_summand( const stochastic_action_summand_vector& action_summands, @@ -158,29 +166,22 @@ void insert_timed_delta_summand( const deadlock_summand& s, bool ignore_time) { - deadlock_summand_vector result; - - // const variable_list sumvars=s.summation_variables(); - const data::data_expression& cond=s.condition(); - const data::data_expression& actiontime=s.deadlock().time(); - - // First check whether the delta summand is subsumed by an action summands. + // First check whether the delta summand is subsumed by an action summand. if (!ignore_time) { for (const stochastic_action_summand& as: action_summands) { - const data::data_expression& cond1=as.condition(); - if (((actiontime==as.multi_action().time()) || (!as.multi_action().has_time())) && - (implies_condition(cond,cond1))) + if (subsumes(as, s)) { - /* De delta summand is subsumed by action summand as. So, it does not - have to be added. */ - return; } } } + deadlock_summand_vector result; + const data::data_expression& cond = s.condition(); + const data::data_expression& actiontime = s.deadlock().time(); + for (deadlock_summand_vector::iterator i=deadlock_summands.begin(); i!=deadlock_summands.end(); ++i) { const deadlock_summand& smmnd=*i; From 5449f65c1ee2dbfb3c8893a94c207fd9817cbc6e Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Mon, 6 Jan 2025 10:41:02 +0100 Subject: [PATCH 28/54] Document method --- libraries/lps/include/mcrl2/lps/linearise_utility.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_utility.h b/libraries/lps/include/mcrl2/lps/linearise_utility.h index 8b1b100f94..ba28119cac 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_utility.h +++ b/libraries/lps/include/mcrl2/lps/linearise_utility.h @@ -152,7 +152,9 @@ bool implies_condition(const data::data_expression& c1, const data::data_express } /// Determine if action summand as subsumes delta summand ds. -inline +/// +/// The action summand subsumes the deadlock summand if its condition is implied by that of the deadlock summand, +/// and either the action summand is not timed, or the timestamp of the deadlock summand and the action summand coincide. bool subsumes(const stochastic_action_summand& as, const deadlock_summand& ds) { return (!as.multi_action().has_time() || ds.deadlock().time() == as.multi_action().time()) From 22af01978385c037274a2995c325d9182e471da3 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Mon, 6 Jan 2025 10:55:15 +0100 Subject: [PATCH 29/54] Minor refactoring and documentation --- .../mcrl2/lps/linearise_communication.h | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h index bb22a969dd..16d0b653f5 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_communication.h +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -54,8 +54,19 @@ struct tuple_list { std::vector < process::action_list > actions; std::vector < data::data_expression > conditions; + + std::size_t size() const + { + assert(action.size() == conditions.size()); + return actions.size(); + } }; +/// Returns the list S ++ L', +/// where L' is the list L in which firstaction is inserted into every action, and each condition is strengthened with condition. +/// +/// If firstaction == action(), it is not added to the multiactions in L', but the conditions will be strengthened. +/// \pre condition != sort_bool::false_() inline tuple_list addActionCondition( const process::action& firstaction, @@ -63,16 +74,16 @@ tuple_list addActionCondition( const tuple_list& L, tuple_list S) { - /* if firstaction==action(), it should not be added */ - assert(condition!=sort_bool::false_()); // It makes no sense to add an action with condition false, - // as it cannot happen anyhow. - for (std::size_t i=0; i Date: Mon, 6 Jan 2025 13:34:47 +0100 Subject: [PATCH 30/54] Fix typo --- libraries/atermpp/include/mcrl2/atermpp/aterm_list.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/atermpp/include/mcrl2/atermpp/aterm_list.h b/libraries/atermpp/include/mcrl2/atermpp/aterm_list.h index 110ad44223..6bcbf4923a 100644 --- a/libraries/atermpp/include/mcrl2/atermpp/aterm_list.h +++ b/libraries/atermpp/include/mcrl2/atermpp/aterm_list.h @@ -423,7 +423,7 @@ void make_term_list(term_list& target, Iter first, Iter last, const ATermC /// \param target The variable to which the list is assigned. /// \param first The start of a range of elements. /// \param last The end of a range of elements. -/// \param convert_to_aterm A class with a () operation, whic is applied to each element +/// \param convert_to_aterm A class with a () operation, which is applied to each element /// before it is put into the list. /// \param aterm_filter A class with an operator () that is used to determine whether elements can be inserted in the list. template From f4652f3483375804b1949667c025c8ecf231630b Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Mon, 6 Jan 2025 13:36:09 +0100 Subject: [PATCH 31/54] Extend action_compare to take into account arguments --- .../lps/include/mcrl2/lps/linearise_utility.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_utility.h b/libraries/lps/include/mcrl2/lps/linearise_utility.h index ba28119cac..45d4eaeb3b 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_utility.h +++ b/libraries/lps/include/mcrl2/lps/linearise_utility.h @@ -47,10 +47,23 @@ bool action_label_compare(const process::action_label& a1, const process::action /// Determine if a1 < a2; the key requirement is that orderings of action labels and the actions in multiactions are /// consistent. +/// +/// \returns true iff the label of a1 is less than the label of a2 (w.r.t. action_label_compare), or the labels are equal and the arguments of a1 are less than the arguments of a2. +/// for the latter, we use the standard < comparison. inline bool action_compare(const process::action& a1, const process::action& a2) { - return action_label_compare(a1.label(), a2.label()); + if (action_label_compare(a1.label(), a2.label())) + { + return true; + }; + + if (a1.label() == a2.label()) + { + return a1.arguments() < a2.arguments(); + } + + return false; } /// Insert action into an action_list, keeping the action list sorted w.r.t. action_compare. From 59d6c35e9d46e537f5a7f7c68f6f353edcdcbccf Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Mon, 6 Jan 2025 13:37:14 +0100 Subject: [PATCH 32/54] Move sorting of communications to utility --- .../mcrl2/lps/linearise_communication.h | 76 ++++++++----------- .../lps/include/mcrl2/lps/linearise_utility.h | 16 ++++ 2 files changed, 48 insertions(+), 44 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h index 16d0b653f5..634855aade 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_communication.h +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -495,7 +495,7 @@ tuple_list makeMultiActionConditionList_aux( return t; } - const process::action& firstaction=*multiaction_first; + const process::action& firstaction = *multiaction_first; const tuple_list S=phi(process::action_list({ firstaction }), firstaction.arguments(), @@ -516,13 +516,14 @@ tuple_list makeMultiActionConditionList( const std::function& RewriteTerm) { comm_entry comm_table(communications); - return makeMultiActionConditionList_aux(multiaction.begin(), multiaction.end(),comm_table,process::action_list(),RewriteTerm); + return makeMultiActionConditionList_aux(multiaction.begin(), multiaction.end(), comm_table, process::action_list(), RewriteTerm); } +/// Apply the communication composition to a list of action summands. inline void communicationcomposition( - const process::communication_expression_list& communications, - const process::action_name_multiset_list& allowlist1, // This is a list of list of identifierstring. + process::communication_expression_list communications, + process::action_name_multiset_list allowlist, // This is a list of list of identifierstring. const bool is_allow, // If is_allow or is_block is set, perform inline allow/block filtering. const bool is_block, stochastic_action_summand_vector& action_summands, @@ -542,37 +543,35 @@ void communicationcomposition( is_block ? "- calculating the communication operator modulo the block operator on " : "- calculating the communication operator on ") << action_summands.size() << " action summands"; - /* first we sort the multiactions in communications */ - process::communication_expression_list sorted_communications; - - for (const process::communication_expression& comm: communications) + // Ensure communications and allowlist are sorted. We rely on the sort order later. + communications = sort_communications(communications); + if (is_allow) { - const process::action_name_multiset& source=comm.action_name(); - const core::identifier_string& target=comm.name(); - sorted_communications.push_front(process::communication_expression(sort_action_labels(source),target)); + allowlist = sort_multi_action_labels(allowlist); } - stochastic_action_summand_vector resultsumlist; - deadlock_summand_vector resultingDeltaSummands; - deadlock_summands.swap(resultingDeltaSummands); + deadlock_summand_vector resulting_deadlock_summands; + deadlock_summands.swap(resulting_deadlock_summands); const bool inline_allow = is_allow || is_block; if (inline_allow) { // Inline allow is only supported for ignore_time, // for in other cases generation of delta summands cannot be inlined in any simple way. - assert(!options.nodeltaelimination && options.ignore_time); + assert(!nodeltaelimination && ignore_time); deadlock_summands.push_back(deadlock_summand(data::variable_list(), data::sort_bool::true_(),deadlock())); } - process::action_name_multiset_list allowlist((is_allow)?sort_multi_action_labels(allowlist1):allowlist1); + + stochastic_action_summand_vector resulting_action_summands; for (const stochastic_action_summand& smmnd: action_summands) { - const data::variable_list& sumvars=smmnd.summation_variables(); - const process::action_list& multiaction=smmnd.multi_action().actions(); - const data::data_expression& condition=smmnd.condition(); - const data::assignment_list& nextstate=smmnd.assignments(); - const stochastic_distribution& dist=smmnd.distribution(); + const data::variable_list& sumvars = smmnd.summation_variables(); + const process::action_list& multiaction = smmnd.multi_action().actions(); + const data::data_expression& time = smmnd.multi_action().time(); + const data::data_expression& condition = smmnd.condition(); + const data::assignment_list& nextstate = smmnd.assignments(); + const stochastic_distribution& dist = smmnd.distribution(); if (!inline_allow) { @@ -586,22 +585,12 @@ void communicationcomposition( * later on, and will in general reduce the number of delta * summands in the whole system */ - /* But first remove free variables from sumvars */ + // Create new list of summand variables containing only those that occur in the condition or the timestamp. + data::variable_list newsumvars; + atermpp::make_term_list(newsumvars, sumvars.begin(), sumvars.end(), [](const data::variable& v) { return v; }, + [&condition, &time](const data::variable& v) { return occursinterm(condition, v) || occursinterm(time, v); }); - data::variable_vector newsumvars_; - for (const data::variable& sumvar: sumvars) - { - if (occursinterm(condition, sumvar) || - (smmnd.has_time() && occursinterm(smmnd.multi_action().time(), sumvar))) - { - newsumvars_.push_back(sumvar); - } - } - data::variable_list newsumvars=data::variable_list(newsumvars_.begin(), newsumvars_.end()); - - resultingDeltaSummands.push_back(deadlock_summand(newsumvars, - condition, - smmnd.multi_action().has_time()?deadlock(smmnd.multi_action().time()):deadlock())); + resulting_deadlock_summands.emplace_back(newsumvars, condition, deadlock(time)); } /* the multiactionconditionlist is a list containing @@ -615,14 +604,14 @@ void communicationcomposition( const tuple_list multiactionconditionlist= makeMultiActionConditionList( multiaction, - sorted_communications, + communications, RewriteTerm); for (std::size_t i=0 ; i Date: Mon, 6 Jan 2025 13:37:31 +0100 Subject: [PATCH 33/54] Simplify insert_timed_delta_summand --- .../lps/include/mcrl2/lps/linearise_utility.h | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_utility.h b/libraries/lps/include/mcrl2/lps/linearise_utility.h index 9babbab0ea..2196b86518 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_utility.h +++ b/libraries/lps/include/mcrl2/lps/linearise_utility.h @@ -184,42 +184,49 @@ bool implies_condition(const data::data_expression& c1, const data::data_express /// /// The action summand subsumes the deadlock summand if its condition is implied by that of the deadlock summand, /// and either the action summand is not timed, or the timestamp of the deadlock summand and the action summand coincide. +inline bool subsumes(const stochastic_action_summand& as, const deadlock_summand& ds) { return (!as.multi_action().has_time() || ds.deadlock().time() == as.multi_action().time()) && implies_condition(ds.condition(), as.condition()); } +/// Determine if delta summand ds1 subsumes delta summand ds2. +inline +bool subsumes(const deadlock_summand& ds1, const deadlock_summand& ds2) +{ + return (!ds1.deadlock().has_time() || ds2.deadlock().time() == ds1.deadlock().time()) + && implies_condition(ds2.condition(), ds1.condition()); +} + + inline void insert_timed_delta_summand( const stochastic_action_summand_vector& action_summands, deadlock_summand_vector& deadlock_summands, const deadlock_summand& s, - bool ignore_time) + const bool ignore_time) { + if (ignore_time) + { + deadlock_summands.push_back(s); + return; + } + + assert(!ignore_time); + // First check whether the delta summand is subsumed by an action summand. - if (!ignore_time) + if (std::any_of(action_summands.begin(), action_summands.end(), + [&s](const stochastic_action_summand& as) { return subsumes(as, s); })) { - for (const stochastic_action_summand& as: action_summands) - { - if (subsumes(as, s)) - { - return; - } - } + return; } deadlock_summand_vector result; - const data::data_expression& cond = s.condition(); - const data::data_expression& actiontime = s.deadlock().time(); for (deadlock_summand_vector::iterator i=deadlock_summands.begin(); i!=deadlock_summands.end(); ++i) { - const deadlock_summand& smmnd=*i; - const data::data_expression& cond1=i->condition(); - if ((!ignore_time) && - ((actiontime==i->deadlock().time()) || (!i->deadlock().has_time())) && - (implies_condition(cond,cond1))) + if (subsumes(*i, s)) { /* put the summand that was effective in removing this delta summand to the front, such that it @@ -230,15 +237,9 @@ void insert_timed_delta_summand( deadlock_summands.swap(result); return; } - if (((ignore_time)|| - (((actiontime==smmnd.deadlock().time())|| (!s.deadlock().has_time())) && - (implies_condition(cond1,cond))))) - { - /* do not add summand to result, as it is superseded by s */ - } - else + if (!subsumes(s, *i)) { - result.push_back(smmnd); + result.push_back(*i); } } From c7d13b5c6c9bebed726d49fbd805127d37eaf7d3 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Mon, 6 Jan 2025 16:45:14 +0100 Subject: [PATCH 34/54] Introduce type for action(name) multisets Preparation for using std::multiset. --- .../mcrl2/lps/linearise_communication.h | 123 ++++++++++-------- .../lps/include/mcrl2/lps/linearise_utility.h | 43 +++++- 2 files changed, 108 insertions(+), 58 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h index 634855aade..bc488ee538 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_communication.h +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -26,6 +26,22 @@ namespace mcrl2 namespace lps { +using action_name_t = core::identifier_string; +using action_multiset_t = process::action_list; // sorted w.r.t. action_compare +using action_name_multiset_t = core::identifier_string_list; // sorted w.r.t. action_label_compare + +inline +action_multiset_t make_action_multiset(const process::action_list& actions) +{ + return actions; +} + +inline +process::action_list make_multi_action(const action_multiset_t& actions) +{ + return actions; +} + // Check that the sorts of both termlists match. inline data::data_expression pairwiseMatch(const data::data_expression_list& l1, const data::data_expression_list& l2, const std::function& RewriteTerm) @@ -34,6 +50,7 @@ data::data_expression pairwiseMatch(const data::data_expression_list& l1, const { return data::sort_bool::false_(); } + data::data_expression_list::const_iterator i2=l2.begin(); data::data_expression result=data::sort_bool::true_(); for(const data::data_expression& t1: l1) @@ -52,7 +69,7 @@ data::data_expression pairwiseMatch(const data::data_expression_list& l1, const // can occur. struct tuple_list { - std::vector < process::action_list > actions; + std::vector < action_multiset_t > actions; std::vector < data::data_expression > conditions; std::size_t size() const @@ -91,14 +108,11 @@ tuple_list addActionCondition( class comm_entry { protected: - /// Type used to store the names of the actions in a multiaction - using multi_action_name_t = std::vector; - /// Left-hand sides of communication expressions - const std::vector m_lhs; + const std::vector m_lhs; /// Right-hand sides of communication expressions - const std::vector m_rhs; + const std::vector m_rhs; /// Caches. std::unordered_map m_can_communicate_cache; @@ -106,7 +120,7 @@ class comm_entry /// Temporary data using in determining whether communication is allowed. /// See usages of the data structure below. - std::vector m_lhs_iters; // offset into lhs + std::vector m_lhs_iters; // offset into lhs std::vector m_match_failed; void reset_temporary_data() @@ -122,13 +136,13 @@ class comm_entry /// Returns true if this is the case, false otherwise. /// Postcondition: for every i such that m is not contained in lhs[i], match_failed[i] is true. /// NB: resets temporary data before performing computations. - bool match_multiaction(const process::action_list& m) { + bool match_multiaction(const action_multiset_t& m) { reset_temporary_data(); // m must match a lhs; check every action for (const process::action& a: m) { - const core::identifier_string& actionname=a.label().name(); + const action_name_t& actionname=a.label().name(); // check every lhs for actionname bool comm_ok = false; @@ -168,9 +182,9 @@ class comm_entry // Initialization of lhs, defined as static function so it can be used in the constructor. // Allows lhs to be defined as const. - static std::vector < multi_action_name_t > init_lhs(const process::communication_expression_list& communications) + static std::vector < action_name_multiset_t > init_lhs(const process::communication_expression_list& communications) { - std::vector result; + std::vector result; for (const process::communication_expression& l: communications) { const core::identifier_string_list& names = l.action_name().names(); @@ -181,9 +195,9 @@ class comm_entry // Initialization of rhs, defined as static function so it can be used in the constructor. // Allows rhs to be defined as const. - static std::vector init_rhs(const process::communication_expression_list& communications) + static std::vector init_rhs(const process::communication_expression_list& communications) { - std::vector result; + std::vector result; for (const process::communication_expression& l: communications) { result.push_back(l.name()); @@ -213,7 +227,7 @@ class comm_entry /// Determine if there exists a communication expression a1|...|an -> b in comm_table /// such that m' \subseteq a1|...|an , where m' is the multiset of actionnames for multiaction m. - process::action_label can_communicate(const process::action_list& m) + process::action_label can_communicate(const action_multiset_t& m) { /* this function indicates whether the actions in m consisting of actions and data occur in C, such that @@ -252,9 +266,9 @@ class comm_entry return result; } - bool might_communicate(const process::action_list& m, - process::action_list::const_iterator n_first, - process::action_list::const_iterator n_last) + bool might_communicate(const action_multiset_t& m, + action_multiset_t::const_iterator n_first, + action_multiset_t::const_iterator n_last) { /* this function indicates whether the actions in m consisting of actions and data occur in C, such that @@ -280,7 +294,7 @@ class comm_entry // before matching all actions in the lhs, we set it to std::nullopt. // N.B. when rest[i] becomes empty after matching all actions in the lhs, // rest[i].empty() is a meaningful result: we have a successful match. - std::vector>> + std::vector>> rest(size(), std::make_pair(n_first, n_last)); // pairs of iterator into n; the second element of the pair indicates the end of the range in n. // check every lhs @@ -302,8 +316,8 @@ class comm_entry break; } // get first action in lhs i - const core::identifier_string& commname = *m_lhs_iters[i]; - core::identifier_string restname = rest[i]->first->label().name(); + const action_name_t& commname = *m_lhs_iters[i]; + action_name_t restname = rest[i]->first->label().name(); // find it in rest[i] while (commname != restname) { @@ -342,19 +356,19 @@ class comm_entry /// \prototype inline tuple_list makeMultiActionConditionList_aux( - process::action_list::const_iterator multiaction_first, - process::action_list::const_iterator multiaction_last, + action_multiset_t::const_iterator multiaction_first, + action_multiset_t::const_iterator multiaction_last, comm_entry& comm_table, const process::action_list& r, const std::function& RewriteTerm); inline -tuple_list phi(const process::action_list& m, +tuple_list phi(const action_multiset_t& m, const data::data_expression_list& d, - const process::action_list& w, - const process::action_list::const_iterator& n_first, - const process::action_list::const_iterator& n_last, - const process::action_list& r, + const action_multiset_t& w, + const action_multiset_t::const_iterator& n_first, + const action_multiset_t::const_iterator& n_last, + const action_multiset_t& r, comm_entry& comm_table, const std::function& RewriteTerm) { @@ -394,17 +408,18 @@ tuple_list phi(const process::action_list& m, const data::data_expression condition=pairwiseMatch(d,firstaction.arguments(),RewriteTerm); if (condition==data::sort_bool::false_()) { - process::action_list tempw=w; + action_multiset_t tempw=w; + tempw = insert(firstaction, tempw); tempw=push_back(tempw,firstaction); return phi(m,d,tempw,std::next(n_first), n_last,r,comm_table,RewriteTerm); } else { - process::action_list tempm=m; - tempm=push_back(tempm,firstaction); + action_multiset_t tempm=m; + tempm = insert(firstaction, tempm); const tuple_list T=phi(tempm,d,w,std::next(n_first), n_last,r,comm_table,RewriteTerm); - process::action_list tempw=w; - tempw=push_back(tempw,firstaction); + action_multiset_t tempw=w; + tempw = insert(firstaction, tempw); return addActionCondition( process::action(), condition, @@ -414,7 +429,7 @@ tuple_list phi(const process::action_list& m, } inline -bool xi(const process::action_list& alpha, const process::action_list& beta, comm_entry& comm_table) +bool xi(const action_multiset_t& alpha, const action_multiset_t& beta, comm_entry& comm_table) { if (beta.empty()) { @@ -423,8 +438,8 @@ bool xi(const process::action_list& alpha, const process::action_list& beta, com else { const process::action& a = beta.front(); - process::action_list l=alpha; - l=push_back(l,a); + action_multiset_t l=alpha; + l = insert(a, l); const process::action_list& beta_next = beta.tail(); if (comm_table.can_communicate(l)!=process::action_label()) @@ -443,14 +458,14 @@ bool xi(const process::action_list& alpha, const process::action_list& beta, com } inline -data::data_expression psi(const process::action_list& alpha_in, comm_entry& comm_table, const std::function& RewriteTerm) +data::data_expression psi(const action_multiset_t& alpha_in, comm_entry& comm_table, const std::function& RewriteTerm) { - process::action_list alpha=reverse(alpha_in); + action_multiset_t alpha=reverse(alpha_in); data::data_expression cond = data::sort_bool::false_(); process::action a; // a and beta used in the loop; avoid repeated allocation and deallocation - process::action_list beta; - process::action_list actl; // used in inner loop. + action_multiset_t beta; + action_multiset_t actl; // used in inner loop. while (!alpha.empty()) { a = alpha.front(); @@ -458,10 +473,10 @@ data::data_expression psi(const process::action_list& alpha_in, comm_entry& comm while (!beta.empty()) { - actl = process::action_list(); - actl.emplace_front(beta.front()); - actl.emplace_front(a); - const process::action_list& beta_tail = beta.tail(); + actl = action_multiset_t(); + actl = insert(beta.front(), actl); + actl = insert(a, actl); + const action_multiset_t& beta_tail = beta.tail(); if (comm_table.might_communicate(actl,beta_tail.begin(), beta_tail.end()) && xi(actl,beta.tail(),comm_table)) { // sort and remove duplicates?? @@ -478,10 +493,10 @@ data::data_expression psi(const process::action_list& alpha_in, comm_entry& comm // returns a list of tuples. inline tuple_list makeMultiActionConditionList_aux( - process::action_list::const_iterator multiaction_first, - process::action_list::const_iterator multiaction_last, + action_multiset_t::const_iterator multiaction_first, + action_multiset_t::const_iterator multiaction_last, comm_entry& comm_table, - const process::action_list& r, + const action_multiset_t& r, const std::function& RewriteTerm) { /* This is the function gamma(m,C,r) provided @@ -497,13 +512,13 @@ tuple_list makeMultiActionConditionList_aux( const process::action& firstaction = *multiaction_first; - const tuple_list S=phi(process::action_list({ firstaction }), + const tuple_list S=phi(action_multiset_t({ firstaction }), firstaction.arguments(), - process::action_list(), + action_multiset_t(), std::next(multiaction_first), multiaction_last, r,comm_table, RewriteTerm); - process::action_list tempr=r; - tempr.push_front(firstaction); + action_multiset_t tempr=r; + tempr = insert(firstaction, tempr); const tuple_list T=makeMultiActionConditionList_aux(std::next(multiaction_first), multiaction_last,comm_table, tempr,RewriteTerm); return addActionCondition(firstaction,data::sort_bool::true_(),T,S); @@ -511,12 +526,12 @@ tuple_list makeMultiActionConditionList_aux( inline tuple_list makeMultiActionConditionList( - const process::action_list& multiaction, + const action_multiset_t& multiaction, const process::communication_expression_list& communications, const std::function& RewriteTerm) { comm_entry comm_table(communications); - return makeMultiActionConditionList_aux(multiaction.begin(), multiaction.end(), comm_table, process::action_list(), RewriteTerm); + return makeMultiActionConditionList_aux(multiaction.begin(), multiaction.end(), comm_table, action_multiset_t(), RewriteTerm); } /// Apply the communication composition to a list of action summands. @@ -603,13 +618,13 @@ void communicationcomposition( const tuple_list multiactionconditionlist= makeMultiActionConditionList( - multiaction, + make_action_multiset(multiaction), communications, RewriteTerm); for (std::size_t i=0 ; i& cmp From 50314ca411c0a022514a46ab2b8abedd66580e20 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Tue, 7 Jan 2025 09:34:36 +0100 Subject: [PATCH 35/54] Preparation for using multisets --- .../mcrl2/lps/linearise_communication.h | 68 +++++++++++----- .../lps/include/mcrl2/lps/linearise_rename.h | 2 +- .../lps/include/mcrl2/lps/linearise_utility.h | 78 ++++++++++--------- 3 files changed, 93 insertions(+), 55 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h index bc488ee538..27655e8613 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_communication.h +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -26,6 +26,37 @@ namespace mcrl2 namespace lps { +#if false +using action_name_t = core::identifier_string; +using action_multiset_t = std::multiset; //process::action_list; // sorted w.r.t. action_compare +using action_name_multiset_t = std::multiset; //core::identifier_string_list; // sorted w.r.t. action_label_compare + +inline +action_multiset_t make_action_multiset(const process::action_list& actions) +{ + return action_multiset_t(actions.begin(), actions.end()); +} + +inline +process::action_list make_multi_action(const action_multiset_t& actions) +{ + return process::action_list(actions.begin(), actions.end()); +} + +inline +action_multiset_t insert(const process::action& action, action_multiset_t actions) +{ + actions.insert(action); + return actions; +} + +inline +action_name_multiset_t insert(const action_name_t& action_name, action_name_multiset_t action_names) +{ + action_names.insert(action_name); + return action_names; +} +#else using action_name_t = core::identifier_string; using action_multiset_t = process::action_list; // sorted w.r.t. action_compare using action_name_multiset_t = core::identifier_string_list; // sorted w.r.t. action_label_compare @@ -42,6 +73,8 @@ process::action_list make_multi_action(const action_multiset_t& actions) return actions; } +#endif + // Check that the sorts of both termlists match. inline data::data_expression pairwiseMatch(const data::data_expression_list& l1, const data::data_expression_list& l2, const std::function& RewriteTerm) @@ -115,8 +148,8 @@ class comm_entry const std::vector m_rhs; /// Caches. - std::unordered_map m_can_communicate_cache; - std::unordered_map m_might_communicate_cache; + std::unordered_map m_can_communicate_cache; + std::unordered_map m_might_communicate_cache; /// Temporary data using in determining whether communication is allowed. /// See usages of the data structure below. @@ -255,7 +288,7 @@ class comm_entry { throw mcrl2::runtime_error("Cannot linearise a process with a communication operator, containing a communication that results in tau or that has an empty right hand side"); } - result = process::action_label(m_rhs[i], m.front().label().sorts()); + result = process::action_label(m_rhs[i], m.begin()->label().sorts()); break; } } @@ -316,10 +349,10 @@ class comm_entry break; } // get first action in lhs i - const action_name_t& commname = *m_lhs_iters[i]; - action_name_t restname = rest[i]->first->label().name(); + const action_name_t& comm_name = *m_lhs_iters[i]; + action_name_t rest_name = rest[i]->first->label().name(); // find it in rest[i] - while (commname != restname) + while (comm_name != rest_name) { ++(rest[i]->first); if (rest[i]->first == rest[i]->second) // no more @@ -327,9 +360,9 @@ class comm_entry rest[i] = std::nullopt; break; } - restname = rest[i]->first->label().name(); + rest_name = rest[i]->first->label().name(); } - if (commname != restname) // action was not found + if (comm_name != rest_name) // action was not found { break; } @@ -359,7 +392,7 @@ tuple_list makeMultiActionConditionList_aux( action_multiset_t::const_iterator multiaction_first, action_multiset_t::const_iterator multiaction_last, comm_entry& comm_table, - const process::action_list& r, + const action_multiset_t& r, const std::function& RewriteTerm); inline @@ -379,7 +412,7 @@ tuple_list phi(const action_multiset_t& m, can take place. In the communication all actions of m, none of w and a subset of n can take part in the communication. d is the data parameter of the communication - and C contains a list of multiaction action pairs indicating + and comm_table contains a list of multiaction action pairs indicating possible communications */ if (!comm_table.might_communicate(m, n_first, n_last)) @@ -388,16 +421,12 @@ tuple_list phi(const action_multiset_t& m, } if (n_first == n_last) { - process::action_label c = comm_table.can_communicate(m); /* returns process::action_label() if no communication + const process::action_label c = comm_table.can_communicate(m); /* returns process::action_label() if no communication is possible */ if (c!=process::action_label()) { const tuple_list T=makeMultiActionConditionList_aux(w.begin(), w.end(),comm_table,r,RewriteTerm); - return addActionCondition( - (c==process::action_label()?process::action():process::action(c,d)), //Check. Nil kan niet geleverd worden. - data::sort_bool::true_(), - T, - tuple_list()); + return addActionCondition(process::action(c,d), data::sort_bool::true_(), T, tuple_list()); } /* c==NULL, actions in m cannot communicate */ return tuple_list(); @@ -408,9 +437,10 @@ tuple_list phi(const action_multiset_t& m, const data::data_expression condition=pairwiseMatch(d,firstaction.arguments(),RewriteTerm); if (condition==data::sort_bool::false_()) { + // a(f) cannot take part in communication as the arguments do not match. Move to w and continue with next action action_multiset_t tempw=w; - tempw = insert(firstaction, tempw); - tempw=push_back(tempw,firstaction); + //tempw = insert(firstaction, tempw); + tempw = push_back(tempw, firstaction); // todo return phi(m,d,tempw,std::next(n_first), n_last,r,comm_table,RewriteTerm); } else @@ -506,7 +536,7 @@ tuple_list makeMultiActionConditionList_aux( { tuple_list t; t.conditions.push_back((r.empty())?static_cast(data::sort_bool::true_()):psi(r,comm_table,RewriteTerm)); - t.actions.push_back(process::action_list()); + t.actions.push_back(action_multiset_t()); return t; } diff --git a/libraries/lps/include/mcrl2/lps/linearise_rename.h b/libraries/lps/include/mcrl2/lps/linearise_rename.h index 424095dc19..2358661937 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_rename.h +++ b/libraries/lps/include/mcrl2/lps/linearise_rename.h @@ -51,7 +51,7 @@ namespace lps result.push_front(rename(renamings, a)); } - result = atermpp::sort_list(result, std::function(action_compare)); + result = atermpp::sort_list(result, std::function(action_compare())); return result; } diff --git a/libraries/lps/include/mcrl2/lps/linearise_utility.h b/libraries/lps/include/mcrl2/lps/linearise_utility.h index cc085161a4..65e525af7a 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_utility.h +++ b/libraries/lps/include/mcrl2/lps/linearise_utility.h @@ -24,59 +24,67 @@ namespace mcrl2 namespace lps { -inline -bool action_name_compare(const core::identifier_string& s1, const core::identifier_string& s2) +struct action_name_compare { - return std::string(s1) < std::string(s2); -} + bool operator()(const core::identifier_string& s1, const core::identifier_string& s2) const + { + return std::string(s1) < std::string(s2); + } +}; /// Determine if a1 < a2; the key requirement is that orderings of action labels and the actions in multiactions are /// consistent. -inline -bool action_label_compare(const process::action_label& a1, const process::action_label& a2) +struct action_label_compare { - /* first compare the strings in the actions */ - const core::identifier_string a1_name = a1.name(); - const core::identifier_string a2_name = a2.name(); - if (action_name_compare(a1_name, a2_name)) + bool operator()(const process::action_label& a1, const process::action_label& a2) const { - return true; - } + /* first compare the strings in the actions */ + const core::identifier_string a1_name = a1.name(); + const core::identifier_string a2_name = a2.name(); - if (a1_name == a2_name) - { - /* the strings are equal; the sorts are used to - determine the ordering */ - return a1.sorts() < a2.sorts(); + if (action_name_compare()(a1_name, a2_name)) + { + return true; + } + + if (a1_name == a2_name) + { + /* the strings are equal; the sorts are used to + determine the ordering */ + return a1.sorts() < a2.sorts(); + } + + return false; } +}; - return false; -} /// Determine if a1 < a2; the key requirement is that orderings of action labels and the actions in multiactions are /// consistent. /// /// \returns true iff the label of a1 is less than the label of a2 (w.r.t. action_label_compare), or the labels are equal and the arguments of a1 are less than the arguments of a2. /// for the latter, we use the standard < comparison. -inline -bool action_compare(const process::action& a1, const process::action& a2) +struct action_compare { - const process::action_label a1_label = a1.label(); - const process::action_label a2_label = a2.label(); - - if (action_label_compare(a1_label, a2_label)) + bool operator()(const process::action& a1, const process::action& a2) const { - return true; - }; + const process::action_label a1_label = a1.label(); + const process::action_label a2_label = a2.label(); - if (a1_label == a2_label) - { - return a1.arguments() < a2.arguments(); - } + if (action_label_compare()(a1_label, a2_label)) + { + return true; + }; - return false; -} + if (a1_label == a2_label) + { + return a1.arguments() < a2.arguments(); + } + + return false; + } +}; /// Insert action into an action_list, keeping the action list sorted w.r.t. action_compare. /// Complexity: O(n) for an action_list of length n. @@ -91,7 +99,7 @@ process::action_list insert( } const process::action& head = l.front(); - if (action_compare(act, head)) + if (action_compare()(act, head)) { l.push_front(act); return l; @@ -114,7 +122,7 @@ core::identifier_string_list insert( } const core::identifier_string& head = l.front(); - if (action_name_compare(s, head)) + if (action_name_compare()(s, head)) { l.push_front(s); return l; From 546191615c6cab54779da7305a0660b0ee7c5210 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Tue, 7 Jan 2025 09:38:12 +0100 Subject: [PATCH 36/54] Use iterators instead of objects. --- .../mcrl2/lps/linearise_communication.h | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h index 27655e8613..774c6c208c 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_communication.h +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -459,30 +459,33 @@ tuple_list phi(const action_multiset_t& m, } inline -bool xi(const action_multiset_t& alpha, const action_multiset_t& beta, comm_entry& comm_table) +bool xi(const action_multiset_t& alpha, + const action_multiset_t::const_iterator& beta_first, + const action_multiset_t::const_iterator& beta_last, + comm_entry& comm_table) { - if (beta.empty()) + if (beta_first == beta_last) { return comm_table.can_communicate(alpha)!=process::action_label(); } else { - const process::action& a = beta.front(); + const process::action& a = *beta_first; action_multiset_t l=alpha; l = insert(a, l); - const process::action_list& beta_next = beta.tail(); if (comm_table.can_communicate(l)!=process::action_label()) { return true; } - else if (comm_table.might_communicate(l,beta_next.begin(), beta_next.end())) + + if (comm_table.might_communicate(l,std::next(beta_first), beta_last)) { - return xi(l,beta_next,comm_table) || xi(alpha,beta_next,comm_table); + return xi(l,std::next(beta_first), beta_last,comm_table) || xi(alpha,std::next(beta_first), beta_last, comm_table); } else { - return xi(alpha,beta_next,comm_table); + return xi(alpha, std::next(beta_first), beta_last, comm_table); } } } @@ -507,7 +510,7 @@ data::data_expression psi(const action_multiset_t& alpha_in, comm_entry& comm_ta actl = insert(beta.front(), actl); actl = insert(a, actl); const action_multiset_t& beta_tail = beta.tail(); - if (comm_table.might_communicate(actl,beta_tail.begin(), beta_tail.end()) && xi(actl,beta.tail(),comm_table)) + if (comm_table.might_communicate(actl,beta_tail.begin(), beta_tail.end()) && xi(actl,beta_tail.begin(), beta_tail.end(),comm_table)) { // sort and remove duplicates?? cond = data::lazy::or_(cond,pairwiseMatch(a.arguments(),beta.front().arguments(),RewriteTerm)); From 1014e383ac79dbbc85daab7b6772d9c23310757b Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Tue, 7 Jan 2025 09:42:59 +0100 Subject: [PATCH 37/54] Use iterators in psi --- .../mcrl2/lps/linearise_communication.h | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h index 774c6c208c..6047ac8461 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_communication.h +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -438,18 +438,15 @@ tuple_list phi(const action_multiset_t& m, if (condition==data::sort_bool::false_()) { // a(f) cannot take part in communication as the arguments do not match. Move to w and continue with next action - action_multiset_t tempw=w; - //tempw = insert(firstaction, tempw); - tempw = push_back(tempw, firstaction); // todo + const action_multiset_t tempw = insert(firstaction, w); return phi(m,d,tempw,std::next(n_first), n_last,r,comm_table,RewriteTerm); } else { - action_multiset_t tempm=m; - tempm = insert(firstaction, tempm); + const action_multiset_t tempm = insert(firstaction, m); const tuple_list T=phi(tempm,d,w,std::next(n_first), n_last,r,comm_table,RewriteTerm); - action_multiset_t tempw=w; - tempw = insert(firstaction, tempw); + + const action_multiset_t tempw = insert(firstaction, w); return addActionCondition( process::action(), condition, @@ -493,32 +490,33 @@ bool xi(const action_multiset_t& alpha, inline data::data_expression psi(const action_multiset_t& alpha_in, comm_entry& comm_table, const std::function& RewriteTerm) { - action_multiset_t alpha=reverse(alpha_in); + const action_multiset_t alpha=reverse(alpha_in); + //const action_multiset_t alpha = alpha_in; + action_multiset_t::const_iterator alpha_first = alpha.begin(); + const action_multiset_t::const_iterator alpha_last = alpha.end(); + data::data_expression cond = data::sort_bool::false_(); - process::action a; // a and beta used in the loop; avoid repeated allocation and deallocation - action_multiset_t beta; action_multiset_t actl; // used in inner loop. - while (!alpha.empty()) + while (alpha_first != alpha_last) { - a = alpha.front(); - beta = alpha.tail(); + action_multiset_t::const_iterator beta_first = std::next(alpha_first); + const action_multiset_t::const_iterator beta_last = alpha_last; - while (!beta.empty()) + while (beta_first != beta_last) { actl = action_multiset_t(); - actl = insert(beta.front(), actl); - actl = insert(a, actl); - const action_multiset_t& beta_tail = beta.tail(); - if (comm_table.might_communicate(actl,beta_tail.begin(), beta_tail.end()) && xi(actl,beta_tail.begin(), beta_tail.end(),comm_table)) + actl = insert(*beta_first, actl); + actl = insert(*alpha_first, actl); + if (comm_table.might_communicate(actl,std::next(beta_first), beta_last) && xi(actl,std::next(beta_first), beta_last,comm_table)) { // sort and remove duplicates?? - cond = data::lazy::or_(cond,pairwiseMatch(a.arguments(),beta.front().arguments(),RewriteTerm)); + cond = data::lazy::or_(cond,pairwiseMatch(alpha_first->arguments(),beta_first->arguments(),RewriteTerm)); } - beta.pop_front(); + beta_first = std::next(beta_first); } - alpha.pop_front(); + alpha_first = std::next(alpha_first); } return data::lazy::not_(cond); } From 45f1e926d92333fb5a0086a02e57a57ec224076e Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Tue, 7 Jan 2025 14:53:29 +0100 Subject: [PATCH 38/54] Small updates to allow caching using names instead of multiactions --- .../mcrl2/lps/linearise_communication.h | 134 +++++++++++------- .../lps/include/mcrl2/lps/linearise_utility.h | 36 ++--- 2 files changed, 87 insertions(+), 83 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h index 6047ac8461..57a87bc9b9 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_communication.h +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -20,6 +20,8 @@ #include "mcrl2/process/process_expression.h" +#define CACHE_COMMUNICATION + namespace mcrl2 { @@ -28,7 +30,7 @@ namespace lps #if false using action_name_t = core::identifier_string; -using action_multiset_t = std::multiset; //process::action_list; // sorted w.r.t. action_compare +using action_multiset_t = std::multiset; //process::action_list; // sorted w.r.t. action_compare using action_name_multiset_t = std::multiset; //core::identifier_string_list; // sorted w.r.t. action_label_compare inline @@ -56,6 +58,13 @@ action_name_multiset_t insert(const action_name_t& action_name, action_name_mult action_names.insert(action_name); return action_names; } + +inline +action_multiset_t reverse(const action_multiset_t& actions) +{ + return actions; +} + #else using action_name_t = core::identifier_string; using action_multiset_t = process::action_list; // sorted w.r.t. action_compare @@ -75,6 +84,14 @@ process::action_list make_multi_action(const action_multiset_t& actions) #endif +inline +core::identifier_string_list names(const action_multiset_t& actions) +{ + core::identifier_string_list result; + atermpp::make_term_list(result, actions.begin(), actions.end(), [](const process::action& action) { return action.label().name(); }); + return result; +} + // Check that the sorts of both termlists match. inline data::data_expression pairwiseMatch(const data::data_expression_list& l1, const data::data_expression_list& l2, const std::function& RewriteTerm) @@ -147,9 +164,11 @@ class comm_entry /// Right-hand sides of communication expressions const std::vector m_rhs; +#ifdef CACHE_COMMUNICATION /// Caches. - std::unordered_map m_can_communicate_cache; - std::unordered_map m_might_communicate_cache; + std::unordered_map m_can_communicate_cache; + std::unordered_map m_might_communicate_cache; +#endif /// Temporary data using in determining whether communication is allowed. /// See usages of the data structure below. @@ -169,13 +188,12 @@ class comm_entry /// Returns true if this is the case, false otherwise. /// Postcondition: for every i such that m is not contained in lhs[i], match_failed[i] is true. /// NB: resets temporary data before performing computations. - bool match_multiaction(const action_multiset_t& m) { + bool match_multiaction(const core::identifier_string_list& names) { reset_temporary_data(); // m must match a lhs; check every action - for (const process::action& a: m) + for (const action_name_t& actionname: names) { - const action_name_t& actionname=a.label().name(); // check every lhs for actionname bool comm_ok = false; @@ -267,16 +285,18 @@ class comm_entry a communication can take place. If not process::action_label() is delivered, otherwise the resulting action is the result. */ + const core::identifier_string_list m_names = names(m); +#ifdef CACHE_COMMUNICATION // Check the cache first. - auto it = m_can_communicate_cache.find(m); - if(it != m_can_communicate_cache.end()) + if(const auto it = m_can_communicate_cache.find(m_names); it != m_can_communicate_cache.end()) { return it->second; } +#endif process::action_label result; // if no match fount, return process::action_label() - if(match_multiaction(m)) + if(match_multiaction(m_names)) { // there is a lhs containing m; find it for (std::size_t i = 0; i < size(); ++i) @@ -294,14 +314,17 @@ class comm_entry } } +#ifdef CACHE_COMMUNICATION // cache the result - m_can_communicate_cache.insert({m, result}); + m_can_communicate_cache.insert({m_names, result}); +#endif return result; } - bool might_communicate(const action_multiset_t& m, - action_multiset_t::const_iterator n_first, - action_multiset_t::const_iterator n_last) + template + bool might_communicate(const action_multiset_t& m, + ConstIterType n_first, + ConstIterType n_last) { /* this function indicates whether the actions in m consisting of actions and data occur in C, such that @@ -310,16 +333,19 @@ class comm_entry if n is not empty, then all actions of a matching communication that are not in m should be in n (i.e. there must be a subbag o of n such that m+o can communicate. */ + const core::identifier_string_list m_names = names(m); +#ifdef CACHE_COMMUNICATION // Check the cache first. - if(auto it = m_might_communicate_cache.find(m); it != m_might_communicate_cache.end()) + if(const auto it = m_might_communicate_cache.find(m_names); it != m_might_communicate_cache.end()) { return it->second; } +#endif bool result = false; - if(match_multiaction(m)) + if(match_multiaction(m_names)) { // the rest of actions of lhs that are not in m should be in n // rest[i] contains the part of n in which lhs i has to find matching actions @@ -327,7 +353,7 @@ class comm_entry // before matching all actions in the lhs, we set it to std::nullopt. // N.B. when rest[i] becomes empty after matching all actions in the lhs, // rest[i].empty() is a meaningful result: we have a successful match. - std::vector>> + std::vector>> rest(size(), std::make_pair(n_first, n_last)); // pairs of iterator into n; the second element of the pair indicates the end of the range in n. // check every lhs @@ -380,8 +406,10 @@ class comm_entry } } +#ifdef CACHE_COMMUNICATION // cache the result - m_might_communicate_cache.insert({m, result}); + m_might_communicate_cache.insert({m_names, result}); +#endif return result; } }; @@ -438,62 +466,56 @@ tuple_list phi(const action_multiset_t& m, if (condition==data::sort_bool::false_()) { // a(f) cannot take part in communication as the arguments do not match. Move to w and continue with next action - const action_multiset_t tempw = insert(firstaction, w); - return phi(m,d,tempw,std::next(n_first), n_last,r,comm_table,RewriteTerm); + const tuple_list result = phi(m,d,insert(firstaction, w),std::next(n_first), n_last,r,comm_table,RewriteTerm); + return result; } else { - const action_multiset_t tempm = insert(firstaction, m); - const tuple_list T=phi(tempm,d,w,std::next(n_first), n_last,r,comm_table,RewriteTerm); + const tuple_list T=phi(insert(firstaction, m),d,w,std::next(n_first), n_last,r,comm_table,RewriteTerm); - const action_multiset_t tempw = insert(firstaction, w); - return addActionCondition( + const tuple_list result = addActionCondition( process::action(), condition, T, - phi(m,d,tempw,std::next(n_first), n_last,r,comm_table,RewriteTerm)); + phi(m,d,insert(firstaction, w),std::next(n_first), n_last,r,comm_table,RewriteTerm)); + return result; } } -inline +template bool xi(const action_multiset_t& alpha, - const action_multiset_t::const_iterator& beta_first, - const action_multiset_t::const_iterator& beta_last, + const ConstIterType& beta_first, + const ConstIterType& beta_last, comm_entry& comm_table) { if (beta_first == beta_last) { return comm_table.can_communicate(alpha)!=process::action_label(); } - else - { - const process::action& a = *beta_first; - action_multiset_t l=alpha; - l = insert(a, l); - if (comm_table.can_communicate(l)!=process::action_label()) - { - return true; - } + const action_multiset_t alpha_ = insert(*beta_first, alpha); - if (comm_table.might_communicate(l,std::next(beta_first), beta_last)) - { - return xi(l,std::next(beta_first), beta_last,comm_table) || xi(alpha,std::next(beta_first), beta_last, comm_table); - } - else - { - return xi(alpha, std::next(beta_first), beta_last, comm_table); - } + if (comm_table.can_communicate(alpha_)!=process::action_label()) + { + return true; + } + + bool result = false; + if (comm_table.might_communicate(alpha_,std::next(beta_first), beta_last)) + { + result = xi(alpha,std::next(beta_first), beta_last,comm_table); } + + result = result || xi(alpha,std::next(beta_first), beta_last, comm_table); + return result; } inline -data::data_expression psi(const action_multiset_t& alpha_in, comm_entry& comm_table, const std::function& RewriteTerm) +data::data_expression psi(const action_multiset_t& alpha, comm_entry& comm_table, const std::function& RewriteTerm) { - const action_multiset_t alpha=reverse(alpha_in); - //const action_multiset_t alpha = alpha_in; - action_multiset_t::const_iterator alpha_first = alpha.begin(); - const action_multiset_t::const_iterator alpha_last = alpha.end(); + const action_multiset_t alpha_reverse = reverse(alpha); + action_multiset_t::const_iterator alpha_first = alpha_reverse.begin(); + const action_multiset_t::const_iterator alpha_last = alpha_reverse.end(); data::data_expression cond = data::sort_bool::false_(); @@ -543,15 +565,16 @@ tuple_list makeMultiActionConditionList_aux( const process::action& firstaction = *multiaction_first; - const tuple_list S=phi(action_multiset_t({ firstaction }), + action_multiset_t m({firstaction}); + action_multiset_t w; + const tuple_list S=phi(m, firstaction.arguments(), - action_multiset_t(), + w, std::next(multiaction_first), multiaction_last, r,comm_table, RewriteTerm); - action_multiset_t tempr=r; - tempr = insert(firstaction, tempr); + const tuple_list T=makeMultiActionConditionList_aux(std::next(multiaction_first), multiaction_last,comm_table, - tempr,RewriteTerm); + insert(firstaction, r),RewriteTerm); return addActionCondition(firstaction,data::sort_bool::true_(),T,S); } @@ -562,7 +585,8 @@ tuple_list makeMultiActionConditionList( const std::function& RewriteTerm) { comm_entry comm_table(communications); - return makeMultiActionConditionList_aux(multiaction.begin(), multiaction.end(), comm_table, action_multiset_t(), RewriteTerm); + action_multiset_t r; + return makeMultiActionConditionList_aux(multiaction.begin(), multiaction.end(), comm_table, r, RewriteTerm); } /// Apply the communication composition to a list of action summands. diff --git a/libraries/lps/include/mcrl2/lps/linearise_utility.h b/libraries/lps/include/mcrl2/lps/linearise_utility.h index 65e525af7a..a0b249a487 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_utility.h +++ b/libraries/lps/include/mcrl2/lps/linearise_utility.h @@ -40,22 +40,11 @@ struct action_label_compare bool operator()(const process::action_label& a1, const process::action_label& a2) const { /* first compare the strings in the actions */ - const core::identifier_string a1_name = a1.name(); - const core::identifier_string a2_name = a2.name(); + const core::identifier_string& a1_name = a1.name(); + const core::identifier_string& a2_name = a2.name(); - if (action_name_compare()(a1_name, a2_name)) - { - return true; - } - - if (a1_name == a2_name) - { - /* the strings are equal; the sorts are used to - determine the ordering */ - return a1.sorts() < a2.sorts(); - } - - return false; + return action_name_compare()(a1_name, a2_name) || + (a1_name == a2_name && a1.sorts() < a2.sorts()); } }; @@ -69,20 +58,11 @@ struct action_compare { bool operator()(const process::action& a1, const process::action& a2) const { - const process::action_label a1_label = a1.label(); - const process::action_label a2_label = a2.label(); + const process::action_label& a1_label = a1.label(); + const process::action_label& a2_label = a2.label(); - if (action_label_compare()(a1_label, a2_label)) - { - return true; - }; - - if (a1_label == a2_label) - { - return a1.arguments() < a2.arguments(); - } - - return false; + return action_label_compare()(a1_label, a2_label) || + (a1_label == a2_label && a1.arguments() < a2.arguments()); } }; From ce1960b53d86080751378856f3cb1e4ae7e80bf8 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Wed, 8 Jan 2025 15:20:59 +0100 Subject: [PATCH 39/54] Store a single iterator. --- .../include/mcrl2/lps/linearise_communication.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h index 57a87bc9b9..382eb80c5b 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_communication.h +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -353,8 +353,8 @@ class comm_entry // before matching all actions in the lhs, we set it to std::nullopt. // N.B. when rest[i] becomes empty after matching all actions in the lhs, // rest[i].empty() is a meaningful result: we have a successful match. - std::vector>> - rest(size(), std::make_pair(n_first, n_last)); // pairs of iterator into n; the second element of the pair indicates the end of the range in n. + std::vector> + rest(size(), n_first); // pairs of iterator into n; the second element of the pair indicates the end of the range in n. // check every lhs for (std::size_t i = 0; i < size(); ++i) @@ -369,24 +369,24 @@ class comm_entry { assert(rest[i] != std::nullopt); // .. find them in rest[i] - if (rest[i]->first == rest[i]->second) // no luck + if (*rest[i] == n_last) // no luck { rest[i] = std::nullopt; break; } // get first action in lhs i const action_name_t& comm_name = *m_lhs_iters[i]; - action_name_t rest_name = rest[i]->first->label().name(); + action_name_t rest_name = (*rest[i])->label().name(); // find it in rest[i] while (comm_name != rest_name) { - ++(rest[i]->first); - if (rest[i]->first == rest[i]->second) // no more + ++(*rest[i]); + if (*rest[i] == n_last) // no more { rest[i] = std::nullopt; break; } - rest_name = rest[i]->first->label().name(); + rest_name = (*rest[i])->label().name(); } if (comm_name != rest_name) // action was not found { @@ -394,7 +394,7 @@ class comm_entry } // action found; try next - ++(rest[i]->first); + ++(*rest[i]); ++m_lhs_iters[i]; } From 6df44aa44f780ea2a96a55a23f4946abf303410e Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Wed, 8 Jan 2025 15:22:40 +0100 Subject: [PATCH 40/54] Allow move semantics and remove multiset - One vector was copied in combining tuple lists. By using move semantics, we avoid creation and destruction of aterms - Multisets so far do not seem to prove an advantage, so we avoid their use. --- .../mcrl2/lps/linearise_communication.h | 140 +++++++++--------- 1 file changed, 67 insertions(+), 73 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h index 382eb80c5b..1628925502 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_communication.h +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -19,7 +19,6 @@ #include "mcrl2/lps/stochastic_action_summand.h" #include "mcrl2/process/process_expression.h" - #define CACHE_COMMUNICATION namespace mcrl2 @@ -28,61 +27,10 @@ namespace mcrl2 namespace lps { -#if false -using action_name_t = core::identifier_string; -using action_multiset_t = std::multiset; //process::action_list; // sorted w.r.t. action_compare -using action_name_multiset_t = std::multiset; //core::identifier_string_list; // sorted w.r.t. action_label_compare - -inline -action_multiset_t make_action_multiset(const process::action_list& actions) -{ - return action_multiset_t(actions.begin(), actions.end()); -} - -inline -process::action_list make_multi_action(const action_multiset_t& actions) -{ - return process::action_list(actions.begin(), actions.end()); -} - -inline -action_multiset_t insert(const process::action& action, action_multiset_t actions) -{ - actions.insert(action); - return actions; -} - -inline -action_name_multiset_t insert(const action_name_t& action_name, action_name_multiset_t action_names) -{ - action_names.insert(action_name); - return action_names; -} - -inline -action_multiset_t reverse(const action_multiset_t& actions) -{ - return actions; -} - -#else using action_name_t = core::identifier_string; using action_multiset_t = process::action_list; // sorted w.r.t. action_compare using action_name_multiset_t = core::identifier_string_list; // sorted w.r.t. action_label_compare -inline -action_multiset_t make_action_multiset(const process::action_list& actions) -{ - return actions; -} - -inline -process::action_list make_multi_action(const action_multiset_t& actions) -{ - return actions; -} - -#endif inline core::identifier_string_list names(const action_multiset_t& actions) @@ -127,31 +75,73 @@ struct tuple_list assert(action.size() == conditions.size()); return actions.size(); } + + tuple_list() = default; + + tuple_list(const std::vector < action_multiset_t>& actions_, const std::vector< data::data_expression >& conditions_) + : actions(actions_), conditions(conditions_) + {} + + tuple_list(std::vector < action_multiset_t>&& actions_, std::vector< data::data_expression >&& conditions_) + : actions(std::move(actions_)), conditions(std::move(conditions_)) + {} }; -/// Returns the list S ++ L', +/// Extends the list S to S ++ L', /// where L' is the list L in which firstaction is inserted into every action, and each condition is strengthened with condition. /// +/// Note that by using move semantics for L, we force the caller to transfer ownership of L to this function, +/// and make it explicit that L should not be used by the caller afterwards. /// If firstaction == action(), it is not added to the multiactions in L', but the conditions will be strengthened. /// \pre condition != sort_bool::false_() inline -tuple_list addActionCondition( +void addActionCondition( const process::action& firstaction, const data::data_expression& condition, - const tuple_list& L, - tuple_list S) + tuple_list&& L, + tuple_list& S) { assert(condition!=sort_bool::false_()); // It makes no sense to add an action with condition false, as it cannot happen anyhow. - for (std::size_t i = 0; i < L.size(); ++i) + // If S is empty, do not copy the vectors, but simply perform the operation in L and move. + // This is a common special case + if (S.size() == 0) { - S.actions.push_back((firstaction!=process::action())? - insert(firstaction,L.actions[i]): - L.actions[i]); - S.conditions.push_back(data::lazy::and_(L.conditions[i],condition)); + if (firstaction != process::action()) + { + for (action_multiset_t& m: L.actions) + { + m = insert(firstaction, m); + } + } + + // Strengthen the conditions in L with condition and append to S. + for (data::data_expression& x: L.conditions) + { + x = data::lazy::and_(x, condition); + } + S = std::move(L); + return; } - return S; + if (firstaction == process::action()) + { + S.actions.insert(S.actions.end(), std::make_move_iterator(L.actions.begin()), std::make_move_iterator(L.actions.end())); + } + else + { + for (action_multiset_t& m: L.actions) + { + m = insert(firstaction, m); + S.actions.emplace_back(std::move(m)); + } + } + + // Strengthen the conditions in L with condition and append to S. + for (const data::data_expression& x: L.conditions) + { + S.conditions.emplace_back(data::lazy::and_(x, condition)); + } } /// Data structure to store the communication function more efficiently. @@ -453,8 +443,10 @@ tuple_list phi(const action_multiset_t& m, is possible */ if (c!=process::action_label()) { - const tuple_list T=makeMultiActionConditionList_aux(w.begin(), w.end(),comm_table,r,RewriteTerm); - return addActionCondition(process::action(c,d), data::sort_bool::true_(), T, tuple_list()); + tuple_list T=makeMultiActionConditionList_aux(w.begin(), w.end(),comm_table,r,RewriteTerm); + tuple_list result; + addActionCondition(process::action(c,d), data::sort_bool::true_(), std::move(T), result); + return result; } /* c==NULL, actions in m cannot communicate */ return tuple_list(); @@ -471,13 +463,14 @@ tuple_list phi(const action_multiset_t& m, } else { - const tuple_list T=phi(insert(firstaction, m),d,w,std::next(n_first), n_last,r,comm_table,RewriteTerm); + tuple_list T=phi(insert(firstaction, m),d,w,std::next(n_first), n_last,r,comm_table,RewriteTerm); - const tuple_list result = addActionCondition( + tuple_list result = phi(m,d,insert(firstaction, w),std::next(n_first), n_last,r,comm_table,RewriteTerm); + addActionCondition( process::action(), condition, - T, - phi(m,d,insert(firstaction, w),std::next(n_first), n_last,r,comm_table,RewriteTerm)); + std::move(T), + result); return result; } } @@ -567,15 +560,16 @@ tuple_list makeMultiActionConditionList_aux( action_multiset_t m({firstaction}); action_multiset_t w; - const tuple_list S=phi(m, + tuple_list S=phi(m, firstaction.arguments(), w, std::next(multiaction_first), multiaction_last, r,comm_table, RewriteTerm); - const tuple_list T=makeMultiActionConditionList_aux(std::next(multiaction_first), multiaction_last,comm_table, + tuple_list T=makeMultiActionConditionList_aux(std::next(multiaction_first), multiaction_last,comm_table, insert(firstaction, r),RewriteTerm); - return addActionCondition(firstaction,data::sort_bool::true_(),T,S); + addActionCondition(firstaction,data::sort_bool::true_(),std::move(T),S); + return S; } inline @@ -673,13 +667,13 @@ void communicationcomposition( const tuple_list multiactionconditionlist= makeMultiActionConditionList( - make_action_multiset(multiaction), + multiaction, communications, RewriteTerm); for (std::size_t i=0 ; i Date: Fri, 10 Jan 2025 19:21:43 +0100 Subject: [PATCH 41/54] Count operations when calculating communication operator --- .../mcrl2/lps/linearise_communication.h | 68 ++++++++++--------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h index 1628925502..2d5bfed54a 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_communication.h +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -19,7 +19,7 @@ #include "mcrl2/lps/stochastic_action_summand.h" #include "mcrl2/process/process_expression.h" -#define CACHE_COMMUNICATION +#define MCRL2_COUNT_COMMUNICATION_OPERATIONS namespace mcrl2 { @@ -154,12 +154,6 @@ class comm_entry /// Right-hand sides of communication expressions const std::vector m_rhs; -#ifdef CACHE_COMMUNICATION - /// Caches. - std::unordered_map m_can_communicate_cache; - std::unordered_map m_might_communicate_cache; -#endif - /// Temporary data using in determining whether communication is allowed. /// See usages of the data structure below. std::vector m_lhs_iters; // offset into lhs @@ -276,13 +270,6 @@ class comm_entry otherwise the resulting action is the result. */ const core::identifier_string_list m_names = names(m); -#ifdef CACHE_COMMUNICATION - // Check the cache first. - if(const auto it = m_can_communicate_cache.find(m_names); it != m_can_communicate_cache.end()) - { - return it->second; - } -#endif process::action_label result; // if no match fount, return process::action_label() @@ -304,10 +291,6 @@ class comm_entry } } -#ifdef CACHE_COMMUNICATION - // cache the result - m_can_communicate_cache.insert({m_names, result}); -#endif return result; } @@ -325,14 +308,6 @@ class comm_entry subbag o of n such that m+o can communicate. */ const core::identifier_string_list m_names = names(m); -#ifdef CACHE_COMMUNICATION - // Check the cache first. - if(const auto it = m_might_communicate_cache.find(m_names); it != m_might_communicate_cache.end()) - { - return it->second; - } -#endif - bool result = false; if(match_multiaction(m_names)) @@ -396,10 +371,6 @@ class comm_entry } } -#ifdef CACHE_COMMUNICATION - // cache the result - m_might_communicate_cache.insert({m_names, result}); -#endif return result; } }; @@ -607,6 +578,10 @@ void communicationcomposition( is_block ? "- calculating the communication operator modulo the block operator on " : "- calculating the communication operator on ") << action_summands.size() << " action summands"; +#ifdef MCRL2_COUNT_COMMUNICATION_OPERATIONS + mCRL2log(mcrl2::log::info) << "Calculating communication operator using a set of " << communications.size() << " communication expressions." << std::endl; +#endif + // Ensure communications and allowlist are sorted. We rely on the sort order later. communications = sort_communications(communications); if (is_allow) @@ -671,16 +646,31 @@ void communicationcomposition( communications, RewriteTerm); +#ifdef MCRL2_COUNT_COMMUNICATION_OPERATIONS + mCRL2log(mcrl2::log::info) << "Calculating communication on multiaction with " << multiaction.size() << " actions results in " << multiactionconditionlist.size() << " potential summands" << std::endl; + std::size_t disallowed_summands = 0; + std::size_t blocked_summands = 0; + std::size_t false_condition_summands = 0; + std::size_t added_summands = 0; +#endif + for (std::size_t i=0 ; i Date: Mon, 13 Jan 2025 11:07:00 +0100 Subject: [PATCH 42/54] Document; reduce worst case complexity --- .../include/mcrl2/lps/linearise_allow_block.h | 107 +++++++++++------- 1 file changed, 69 insertions(+), 38 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_allow_block.h b/libraries/lps/include/mcrl2/lps/linearise_allow_block.h index 08184904af..3a414a847f 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_allow_block.h +++ b/libraries/lps/include/mcrl2/lps/linearise_allow_block.h @@ -27,56 +27,65 @@ namespace lps /**************** allow/block *************************************/ +/// Determine if allow_action allows multi_action. +/// +/// \param allow_action A sorted action_name_multiset a1|...|an +/// \param multi_action A multiaction b1(d1)|...|bm(dm) +/// \param termination_action The action that encodes successful termination (used only in debug mode) +/// \returns n == m and ai == bi for 1 <= i <= n inline -bool allowsingleaction(const process::action_name_multiset& allowaction, - const process::action_list& multiaction) +bool allow_(const process::action_name_multiset& allow_action, + const process::action_list& multi_action, + const process::action& termination_action) { /* The special cases where multiaction==tau and multiaction=={ Terminated } must have been dealt with separately. */ - assert(multiaction.size()!=0 && multiaction != action_list({ terminationAction })); + assert(!multi_action.empty()); + assert(multi_action != process::action_list({ termination_action })); + assert(std::is_sorted(allow_action.names().begin(), allow_action.names().end(), action_name_compare())); + assert(std::is_sorted(multi_action.begin(), multi_action.end(), action_compare())); - const core::identifier_string_list& names=allowaction.names(); - core::identifier_string_list::const_iterator i=names.begin(); + if (allow_action.size() != multi_action.size()) + { + return false; + } + + const core::identifier_string_list& names=allow_action.names(); + core::identifier_string_list::const_iterator names_it = names.begin(); + process::action_list::const_iterator multiaction_it = multi_action.begin(); - for (process::action_list::const_iterator walker=multiaction.begin(); - walker!=multiaction.end(); ++walker,++i) + while (names_it != names.end()) { - if (i==names.end()) - { - return false; - } - if (*i!=walker->label().name()) + assert(multiaction_it != multi_action.end()); + if (*names_it != multiaction_it->label().name()) { return false; } + ++names_it; + ++multiaction_it; } - return i==names.end(); + + return true; } -/// \brief determine whether the multiaction has the same labels as the allow action, -// in which case true is delivered. If multiaction is the action Terminate, -// then true is also returned. +/// \brief Determine if multi_action is allowed by an allow expression in allow_list +/// +/// Calculates if the names of the action in multi_action match with an expression in allow_list. +/// If multi_action is the termination action, or multi_action is the empty multiaction, the result is also true. inline -bool allow_(const process::action_name_multiset_list& allowlist, - const process::action_list& multiaction, - const process::action termination_action) +bool allow_(const process::action_name_multiset_list& allow_list, + const process::action_list& multi_action, + const process::action& termination_action) { - /* The empty multiaction, i.e. tau, is never blocked by allow */ - if (multiaction.empty()) - { - return true; - } - - /* The multiaction is equal to the special Terminate action. This action cannot be blocked. */ - if (multiaction == process::action_list({ termination_action })) + // The empty multiaction and the termination action can never be blocked by allows. + if (multi_action.empty() || multi_action == process::action_list({ termination_action })) { return true; } - for (process::action_name_multiset_list::const_iterator i=allowlist.begin(); - i!=allowlist.end(); ++i) + for (const process::action_name_multiset& allow_action: allow_list) { - if (allowsingleaction(*i,multiaction)) + if (allow_(allow_action, multi_action, termination_action)) { return true; } @@ -84,21 +93,43 @@ bool allow_(const process::action_name_multiset_list& allowlist, return false; } +/// \brief Calculate if any of the actions in multiaction is blocked by encap_list. +/// +/// \param encap_list is a list of action_name_multisets of size 1. Its single element contains all the blocked actions. +/// \param multi_action contains a multiaction a1(d1)|...|an(dn) +/// \returns true iff inline bool encap(const process::action_name_multiset_list& encaplist, const process::action_list& multiaction) { - for (const process::action& a: multiaction) + assert(encaplist.size() == 1); + assert(std::is_sorted(multiaction.begin(), multiaction.end(), action_compare())); + + const core::identifier_string_list& blocked_actions = encaplist.front().names(); + assert(std::is_sorted(blocked_actions.begin(), blocked_actions.end(), action_name_compare())); + + core::identifier_string_list::const_iterator blocked_actions_it = blocked_actions.begin(); + process::action_list::const_iterator multiaction_it = multiaction.begin(); + + while (blocked_actions_it != blocked_actions.end() && multiaction_it != multiaction.end()) { - assert(encaplist.size()==1); - for (const core::identifier_string& s1: encaplist.front().names()) + if (*blocked_actions_it == multiaction_it->label().name()) { - const core::identifier_string s2=a.label().name(); - if (s1==s2) - { - return true; - } + return true; + } + + if (action_name_compare()(*blocked_actions_it, multiaction_it->label().name())) + { + ++blocked_actions_it; + } + else + { + // The following assertion should hold because blocked_actions_it is not less than or equal to the name + // of multiaction_it + assert(action_name_compare()(multiaction_it->label().name(), *blocked_actions_it)); + ++multiaction_it; } } + return false; } From eb40b994703eb128a8908dd84e9a31d4c4c02cef Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Mon, 13 Jan 2025 14:59:37 +0100 Subject: [PATCH 43/54] Add tests for renaming. --- .../process/test/linearise_rename_test.cpp | 183 ++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 libraries/process/test/linearise_rename_test.cpp diff --git a/libraries/process/test/linearise_rename_test.cpp b/libraries/process/test/linearise_rename_test.cpp new file mode 100644 index 0000000000..c9a587c7a7 --- /dev/null +++ b/libraries/process/test/linearise_rename_test.cpp @@ -0,0 +1,183 @@ +// Author(s): Jeroen Keiren +// Copyright: see the accompanying file COPYING or copy at +// https://github.com/mCRL2org/mCRL2/blob/master/COPYING +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +/// \file linearise_rename_test.cpp +/// \brief Test for applying rename operator + +#define BOOST_TEST_MODULE linearise_rename_test +#include +#include + +#include "mcrl2/process/rename_expression.h" +#include "mcrl2/lps/linearise_rename.h" + +using namespace mcrl2; +using namespace mcrl2::process; +using namespace mcrl2::lps; + +struct LogDebug +{ + LogDebug() + { + log::logger::set_reporting_level(log::debug); + } +}; +BOOST_GLOBAL_FIXTURE(LogDebug); + +inline +process::action make_action(const std::string& name, const data::data_expression_list& arguments = data::data_expression_list()) +{ + data::sort_expression_list sorts; + for(const auto& expression : arguments) + { + sorts.push_front(expression.sort()); + } + sorts = atermpp::reverse(sorts); + + const action_label label(core::identifier_string(name), sorts); + return process::action(label, arguments); +} + +inline +rename_expression_list rename_rule_ab() +{ + rename_expression_list result; + result.push_front(rename_expression("a", "b")); + return result; +} + +inline +rename_expression_list rename_rule_cd() +{ + rename_expression_list result; + result.push_front(rename_expression("a", "b")); + return result; +} + +inline +rename_expression_list rename_rules_ab_cd() +{ + rename_expression_list result; + result.push_front(rename_expression("c", "d")); + result.push_front(rename_expression("a", "b")); + return result; +} + +BOOST_AUTO_TEST_CASE(test_rename_action) +{ + auto a = make_action("a"); + auto b = make_action("b"); + auto c = make_action("c"); + auto d = make_action("d"); + auto e = make_action("e"); + + BOOST_CHECK_EQUAL(lps::rename(rename_rule_ab(), a), b); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_ab(), b), b); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_ab(), c), c); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_ab(), d), d); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_ab(), e), e); + + BOOST_CHECK_EQUAL(lps::rename(rename_rule_cd(), a), a); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_cd(), b), b); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_cd(), c), d); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_cd(), d), d); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_cd(), e), e); + + BOOST_CHECK_EQUAL(lps::rename(rename_rules_ab_cd(), a), b); + BOOST_CHECK_EQUAL(lps::rename(rename_rules_ab_cd(), b), b); + BOOST_CHECK_EQUAL(lps::rename(rename_rules_ab_cd(), c), d); + BOOST_CHECK_EQUAL(lps::rename(rename_rules_ab_cd(), d), d); + BOOST_CHECK_EQUAL(lps::rename(rename_rules_ab_cd(), e), e); +} + +BOOST_AUTO_TEST_CASE(test_rename_action_with_sort) +{ + auto a = make_action("a", {data::sort_bool::true_()}); + auto b = make_action("b", {data::sort_bool::true_()}); + auto c = make_action("c", {data::variable("x", data::basic_sort("D"))}); + auto d = make_action("d", {data::variable("x", data::basic_sort("D"))});); + auto e = make_action("e"); + + BOOST_CHECK_EQUAL(lps::rename(rename_rule_ab(), a), b); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_ab(), b), b); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_ab(), c), c); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_ab(), d), d); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_ab(), e), e); + + BOOST_CHECK_EQUAL(lps::rename(rename_rule_cd(), a), a); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_cd(), b), b); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_cd(), c), d); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_cd(), d), d); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_cd(), e), e); + + BOOST_CHECK_EQUAL(lps::rename(rename_rules_ab_cd(), a), b); + BOOST_CHECK_EQUAL(lps::rename(rename_rules_ab_cd(), b), b); + BOOST_CHECK_EQUAL(lps::rename(rename_rules_ab_cd(), c), d); + BOOST_CHECK_EQUAL(lps::rename(rename_rules_ab_cd(), d), d); + BOOST_CHECK_EQUAL(lps::rename(rename_rules_ab_cd(), e), e); +} + +BOOST_AUTO_TEST_CASE(test_rename_action_list) +{ + auto a = make_action("a"); + auto b = make_action("b"); + auto c = make_action("c"); + auto d = make_action("d"); + auto e = make_action("e"); + + action_list ab; + ab.push_front(b); + ab.push_front(a); + + action_list bb; + bb.push_front(b); + bb.push_front(b); + + action_list bc; + bc.push_front(c); + bc.push_front(b); + + action_list bd; + bc.push_front(d); + bc.push_front(b); + + action_list cd; + cd.push_front(d); + cd.push_front(c); + + action_list dd; + cd.push_front(d); + cd.push_front(d); + + action_list de; + de.push_front(e); + de.push_front(d); + + BOOST_CHECK_EQUAL(lps::rename(rename_rule_ab(), ab), bb); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_ab(), bb), bb); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_ab(), bc), bc); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_ab(), bd), bd); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_ab(), cd), cd); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_ab(), de), de); + + BOOST_CHECK_EQUAL(lps::rename(rename_rule_cd(), ab), ab); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_cd(), bb), bb); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_cd(), bc), bd); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_cd(), bd), bd); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_cd(), cd), dd); + BOOST_CHECK_EQUAL(lps::rename(rename_rule_cd(), de), de); + + BOOST_CHECK_EQUAL(lps::rename(rename_rules_ab_cd(), ab), bb); + BOOST_CHECK_EQUAL(lps::rename(rename_rules_ab_cd(), bb), bb); + BOOST_CHECK_EQUAL(lps::rename(rename_rules_ab_cd(), bc), bd); + BOOST_CHECK_EQUAL(lps::rename(rename_rules_ab_cd(), bd), bd); + BOOST_CHECK_EQUAL(lps::rename(rename_rules_ab_cd(), cd), dd); + BOOST_CHECK_EQUAL(lps::rename(rename_rules_ab_cd(), de), de); +} + +// TODO: extend with tests for renaming multiactions, summands and lps. \ No newline at end of file From 064fd5473e90aab2f455b187f26d6c990cd60160 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Mon, 13 Jan 2025 16:08:44 +0100 Subject: [PATCH 44/54] Add basic test for calculating allow_ Also fix bug that was caught by this test. --- .../include/mcrl2/lps/linearise_allow_block.h | 10 +- .../test/linearise_allow_block_test.cpp | 155 ++++++++++++++++++ 2 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 libraries/process/test/linearise_allow_block_test.cpp diff --git a/libraries/lps/include/mcrl2/lps/linearise_allow_block.h b/libraries/lps/include/mcrl2/lps/linearise_allow_block.h index 3a414a847f..de3a5f4f17 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_allow_block.h +++ b/libraries/lps/include/mcrl2/lps/linearise_allow_block.h @@ -45,18 +45,17 @@ bool allow_(const process::action_name_multiset& allow_action, assert(std::is_sorted(allow_action.names().begin(), allow_action.names().end(), action_name_compare())); assert(std::is_sorted(multi_action.begin(), multi_action.end(), action_compare())); - if (allow_action.size() != multi_action.size()) + const core::identifier_string_list& names = allow_action.names(); + if (names.size() != multi_action.size()) { return false; } - const core::identifier_string_list& names=allow_action.names(); core::identifier_string_list::const_iterator names_it = names.begin(); process::action_list::const_iterator multiaction_it = multi_action.begin(); while (names_it != names.end()) { - assert(multiaction_it != multi_action.end()); if (*names_it != multiaction_it->label().name()) { return false; @@ -65,6 +64,9 @@ bool allow_(const process::action_name_multiset& allow_action, ++multiaction_it; } + assert(names_it == names.end()); + assert(multiaction_it == multi_action.end()); + return true; } @@ -175,7 +177,7 @@ void allowblockcomposition( for (const stochastic_action_summand& smmnd: sourcesumlist) { const data::variable_list& sumvars=smmnd.summation_variables(); - const process::action_list multiaction=smmnd.multi_action().actions(); + const process::action_list& multiaction=smmnd.multi_action().actions(); const data::data_expression& actiontime=smmnd.multi_action().time(); const data::data_expression& condition=smmnd.condition(); diff --git a/libraries/process/test/linearise_allow_block_test.cpp b/libraries/process/test/linearise_allow_block_test.cpp new file mode 100644 index 0000000000..48691ba19e --- /dev/null +++ b/libraries/process/test/linearise_allow_block_test.cpp @@ -0,0 +1,155 @@ +// Author(s): Jeroen Keiren +// Copyright: see the accompanying file COPYING or copy at +// https://github.com/mCRL2org/mCRL2/blob/master/COPYING +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +/// \file linearise_allow_block_test.cpp +/// \brief Test for applying allow and block operator + +#define BOOST_TEST_MODULE linearise_allow_block_test +#include +#include + +#include "mcrl2/lps/linearise_allow_block.h" + +using namespace mcrl2; +using namespace mcrl2::process; +using namespace mcrl2::lps; + +struct LogDebug +{ + LogDebug() + { + log::logger::set_reporting_level(log::debug); + } +}; +BOOST_GLOBAL_FIXTURE(LogDebug); + + +inline +process::action make_action(const std::string& name, const data::data_expression_list& arguments = data::data_expression_list()) +{ + data::sort_expression_list sorts; + for(const auto& expression : arguments) + { + sorts.push_front(expression.sort()); + } + sorts = atermpp::reverse(sorts); + + const action_label label(core::identifier_string(name), sorts); + return process::action(label, arguments); +} + +inline +action_name_multiset allow_ab() +{ + core::identifier_string_list result; + result.push_front(core::identifier_string("b")); + result.push_front(core::identifier_string("a")); + return action_name_multiset(result); +} + +inline +action_name_multiset allow_abb() +{ + core::identifier_string_list result; + result.push_front(core::identifier_string("b")); + result.push_front(core::identifier_string("b")); + result.push_front(core::identifier_string("a")); + return action_name_multiset(result); +} + +inline +action_name_multiset allow_cd() +{ + core::identifier_string_list result; + result.push_front(core::identifier_string("d")); + result.push_front(core::identifier_string("c")); + return action_name_multiset(result); +} + +inline +action_name_multiset_list allow_ab_abb_cd() +{ + action_name_multiset_list result; + result.push_front(allow_cd()); + result.push_front(allow_abb()); + result.push_front(allow_ab()); + return result; +} + +BOOST_AUTO_TEST_CASE(test_single_allow) +{ + auto a = make_action("a"); + auto b = make_action("b"); + auto c = make_action("c"); + auto termination_action = make_action("Terminate"); + + action_list a_; + a_.push_front(a); + + BOOST_ASSERT(!allow_(allow_ab(), a_, termination_action)); + BOOST_ASSERT(!allow_(allow_abb(), a_, termination_action)); + BOOST_ASSERT(!allow_(allow_cd(), a_, termination_action)); + + action_list ab; + ab.push_front(b); + ab.push_front(a); + + BOOST_ASSERT(allow_(allow_ab(), ab, termination_action)); + BOOST_ASSERT(!allow_(allow_abb(), ab, termination_action)); + BOOST_ASSERT(!allow_(allow_cd(), ab, termination_action)); + + action_list abb; + abb.push_front(b); + abb.push_front(b); + abb.push_front(a); + + BOOST_ASSERT(!allow_(allow_ab(), abb, termination_action)); + BOOST_ASSERT(allow_(allow_abb(), abb, termination_action)); + BOOST_ASSERT(!allow_(allow_cd(), abb, termination_action)); + + action_list bb; + bb.push_front(b); + bb.push_front(b); + + BOOST_ASSERT(!allow_(allow_ab(), bb, termination_action)); + BOOST_ASSERT(!allow_(allow_abb(), bb, termination_action)); + BOOST_ASSERT(!allow_(allow_cd(), bb, termination_action)); +} + +BOOST_AUTO_TEST_CASE(test_allow) +{ + auto a = make_action("a"); + auto b = make_action("b"); + auto c = make_action("c"); + auto termination_action = make_action("Terminate"); + + BOOST_ASSERT(allow_(allow_ab_abb_cd(), action_list(), termination_action)); + action_list terminate; + terminate.push_front(termination_action); + BOOST_ASSERT(allow_(allow_ab_abb_cd(), terminate, termination_action)); + + action_list a_; + a_.push_front(a); + BOOST_ASSERT(!allow_(allow_ab_abb_cd(), a_, termination_action)); + + action_list ab; + ab.push_front(b); + ab.push_front(a); + BOOST_ASSERT(allow_(allow_ab_abb_cd(), ab, termination_action)); + + action_list abb; + abb.push_front(b); + abb.push_front(b); + abb.push_front(a); + BOOST_ASSERT(allow_(allow_ab_abb_cd(), abb, termination_action)); + + action_list bb; + bb.push_front(b); + bb.push_front(b); + BOOST_ASSERT(!allow_(allow_ab_abb_cd(), bb, termination_action)); +} From 72f4719ffc38181bebde6ad72a0df8a59758cb64 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Tue, 14 Jan 2025 19:51:10 +0100 Subject: [PATCH 45/54] Fix test for renaming --- .../test/linearise_rename_test.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) rename libraries/{process => lps}/test/linearise_rename_test.cpp (95%) diff --git a/libraries/process/test/linearise_rename_test.cpp b/libraries/lps/test/linearise_rename_test.cpp similarity index 95% rename from libraries/process/test/linearise_rename_test.cpp rename to libraries/lps/test/linearise_rename_test.cpp index c9a587c7a7..08440a4b63 100644 --- a/libraries/process/test/linearise_rename_test.cpp +++ b/libraries/lps/test/linearise_rename_test.cpp @@ -13,8 +13,8 @@ #include #include -#include "mcrl2/process/rename_expression.h" -#include "mcrl2/lps/linearise_rename.h" +#include "../../process/include/mcrl2/process/rename_expression.h" +#include "../include/mcrl2/lps/linearise_rename.h" using namespace mcrl2; using namespace mcrl2::process; @@ -55,7 +55,7 @@ inline rename_expression_list rename_rule_cd() { rename_expression_list result; - result.push_front(rename_expression("a", "b")); + result.push_front(rename_expression("c", "d")); return result; } @@ -100,7 +100,7 @@ BOOST_AUTO_TEST_CASE(test_rename_action_with_sort) auto a = make_action("a", {data::sort_bool::true_()}); auto b = make_action("b", {data::sort_bool::true_()}); auto c = make_action("c", {data::variable("x", data::basic_sort("D"))}); - auto d = make_action("d", {data::variable("x", data::basic_sort("D"))});); + auto d = make_action("d", {data::variable("x", data::basic_sort("D"))}); auto e = make_action("e"); BOOST_CHECK_EQUAL(lps::rename(rename_rule_ab(), a), b); @@ -143,16 +143,16 @@ BOOST_AUTO_TEST_CASE(test_rename_action_list) bc.push_front(b); action_list bd; - bc.push_front(d); - bc.push_front(b); + bd.push_front(d); + bd.push_front(b); action_list cd; cd.push_front(d); cd.push_front(c); action_list dd; - cd.push_front(d); - cd.push_front(d); + dd.push_front(d); + dd.push_front(d); action_list de; de.push_front(e); From 292adeab52eb8e549da77aef5faf199f2314f2e9 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Tue, 14 Jan 2025 19:51:25 +0100 Subject: [PATCH 46/54] Move test to correct library. --- .../{process => lps}/test/linearise_allow_block_test.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename libraries/{process => lps}/test/linearise_allow_block_test.cpp (97%) diff --git a/libraries/process/test/linearise_allow_block_test.cpp b/libraries/lps/test/linearise_allow_block_test.cpp similarity index 97% rename from libraries/process/test/linearise_allow_block_test.cpp rename to libraries/lps/test/linearise_allow_block_test.cpp index 48691ba19e..e9dce5d4c5 100644 --- a/libraries/process/test/linearise_allow_block_test.cpp +++ b/libraries/lps/test/linearise_allow_block_test.cpp @@ -13,7 +13,7 @@ #include #include -#include "mcrl2/lps/linearise_allow_block.h" +#include "../include/mcrl2/lps/linearise_allow_block.h" using namespace mcrl2; using namespace mcrl2::process; @@ -153,3 +153,5 @@ BOOST_AUTO_TEST_CASE(test_allow) bb.push_front(b); BOOST_ASSERT(!allow_(allow_ab_abb_cd(), bb, termination_action)); } + +// TODO: extend with tests for block. \ No newline at end of file From dd7669b051a1bfb448b7bedc63d0f2486fbb2df5 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Tue, 14 Jan 2025 19:51:38 +0100 Subject: [PATCH 47/54] Use utility function. --- libraries/lps/include/mcrl2/lps/linearise_rename.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_rename.h b/libraries/lps/include/mcrl2/lps/linearise_rename.h index 2358661937..0381c695ec 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_rename.h +++ b/libraries/lps/include/mcrl2/lps/linearise_rename.h @@ -51,7 +51,7 @@ namespace lps result.push_front(rename(renamings, a)); } - result = atermpp::sort_list(result, std::function(action_compare())); + result = sort_actions(result); return result; } From 670dbc5e492115238db3dfbd4010fb9187f5dae0 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Tue, 14 Jan 2025 19:56:42 +0100 Subject: [PATCH 48/54] Dramatically improve the performance of communication. In case there were many matches of communication expressions in a single summand, first all possible combinations of matches were explored, and the corresponding condition and summand generated. So, for a single summand that matches n communication expressions, 2^n possible multiactions are generated. In practice, however, many of these multiactions contain actions that are either blocked, or that are not part of any multiaction in an allow set. I added two test cases that were very slow without this change, but that take hardly any time now. These cases are adapted from the mCRL2 translation of a Cordis model, for which without the modification mcrl22lps --no-alph takes 1550s, and with the modification it terminates in 11s. --- .../mcrl2/lps/linearise_communication.h | 466 +++++++++++------- .../lps/include/mcrl2/lps/linearise_utility.h | 17 +- .../lps/test/linearise_communication_test.cpp | 186 +++++++ 3 files changed, 491 insertions(+), 178 deletions(-) create mode 100644 libraries/lps/test/linearise_communication_test.cpp diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h index 2d5bfed54a..61f2fe5e2a 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_communication.h +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -13,10 +13,11 @@ #define MCRL2_LPS_LINEARISE_COMMUNICATION_H #include "mcrl2/atermpp/aterm_list.h" +#include "mcrl2/core/detail/print_utility.h" #include "mcrl2/lps/linearise_allow_block.h" #include "mcrl2/lps/linearise_utility.h" -#include "mcrl2/lps/sumelm.h" #include "mcrl2/lps/stochastic_action_summand.h" +#include "mcrl2/lps/sumelm.h" #include "mcrl2/process/process_expression.h" #define MCRL2_COUNT_COMMUNICATION_OPERATIONS @@ -32,39 +33,40 @@ using action_multiset_t = process::action_list; // sorted w.r.t. action_compare using action_name_multiset_t = core::identifier_string_list; // sorted w.r.t. action_label_compare +/// Calculate data expression for pairwise equality of the elements of l1 and l2. +/// +/// If the lengths of l1 and l2 differ, or for some index i, the sort l1[i] and l2[i] is different, the pairwise +/// match is false, otherwise an expression equivalent to \bigwegde_i (l1[i] == l2[i]) is returned. inline -core::identifier_string_list names(const action_multiset_t& actions) +data::data_expression pairwise_equal_to(const data::data_expression_list& l1, const data::data_expression_list& l2, const std::function& RewriteTerm) { - core::identifier_string_list result; - atermpp::make_term_list(result, actions.begin(), actions.end(), [](const process::action& action) { return action.label().name(); }); - return result; -} + data::data_expression result = data::sort_bool::true_(); -// Check that the sorts of both termlists match. -inline -data::data_expression pairwiseMatch(const data::data_expression_list& l1, const data::data_expression_list& l2, const std::function& RewriteTerm) -{ if (l1.size()!=l2.size()) { - return data::sort_bool::false_(); + result = data::sort_bool::false_(); } - data::data_expression_list::const_iterator i2=l2.begin(); - data::data_expression result=data::sort_bool::true_(); - for(const data::data_expression& t1: l1) + auto i1 = l1.begin(); + auto i2 = l2.begin(); + + while (i1 != l1.end() && i2 != l2.end() && result != data::sort_bool::false_()) { - if (t1.sort()!=i2->sort()) + if (i1->sort() != i2->sort()) { - return data::sort_bool::false_(); + result = data::sort_bool::false_(); + } + else + { + result = data::lazy::and_(result, RewriteTerm(equal_to(*i1++, *i2++))); } - result=data::lazy::and_(result,RewriteTerm(equal_to(t1,*i2))); - ++i2; } + return result; } -// a tuple_list contains pairs of actions (multi-action) and the condition under which this action -// can occur. +/// a tuple_list contains pairs of actions (multi-action) and the condition under which this action +/// can occur. struct tuple_list { std::vector < action_multiset_t > actions; @@ -72,7 +74,7 @@ struct tuple_list std::size_t size() const { - assert(action.size() == conditions.size()); + assert(actions.size() == conditions.size()); return actions.size(); } @@ -87,44 +89,18 @@ struct tuple_list {} }; -/// Extends the list S to S ++ L', -/// where L' is the list L in which firstaction is inserted into every action, and each condition is strengthened with condition. +/// Extends the list S to S ++ [(a \oplus alpha, c \land c') | (alpha, c') \in T] /// /// Note that by using move semantics for L, we force the caller to transfer ownership of L to this function, /// and make it explicit that L should not be used by the caller afterwards. /// If firstaction == action(), it is not added to the multiactions in L', but the conditions will be strengthened. /// \pre condition != sort_bool::false_() inline -void addActionCondition( - const process::action& firstaction, - const data::data_expression& condition, - tuple_list&& L, - tuple_list& S) +void addActionCondition(const process::action& a, const data::data_expression& c, tuple_list&& L, tuple_list& S) { - assert(condition!=sort_bool::false_()); // It makes no sense to add an action with condition false, as it cannot happen anyhow. + assert(c!=data::sort_bool::false_()); // It makes no sense to add an action with condition false, as it cannot happen anyhow. - // If S is empty, do not copy the vectors, but simply perform the operation in L and move. - // This is a common special case - if (S.size() == 0) - { - if (firstaction != process::action()) - { - for (action_multiset_t& m: L.actions) - { - m = insert(firstaction, m); - } - } - - // Strengthen the conditions in L with condition and append to S. - for (data::data_expression& x: L.conditions) - { - x = data::lazy::and_(x, condition); - } - S = std::move(L); - return; - } - - if (firstaction == process::action()) + if (a == process::action()) { S.actions.insert(S.actions.end(), std::make_move_iterator(L.actions.begin()), std::make_move_iterator(L.actions.end())); } @@ -132,15 +108,22 @@ void addActionCondition( { for (action_multiset_t& m: L.actions) { - m = insert(firstaction, m); + m = insert(a, m); S.actions.emplace_back(std::move(m)); } } - // Strengthen the conditions in L with condition and append to S. - for (const data::data_expression& x: L.conditions) + if (c == data::sort_bool::true_()) { - S.conditions.emplace_back(data::lazy::and_(x, condition)); + S.conditions.insert(S.conditions.end(), std::make_move_iterator(L.conditions.begin()), std::make_move_iterator(L.conditions.end())); + } + else + { + // Strengthen the conditions in L with condition and append to S. + for (const data::data_expression& x: L.conditions) + { + S.conditions.emplace_back(data::lazy::and_(x, c)); + } } } @@ -168,17 +151,18 @@ class comm_entry } } - /// Check if m is contained in a lhs in the communication entry. + /// Check if m is contained in a lhs in the communication entry. (in fact, it must be a prefix). /// Returns true if this is the case, false otherwise. /// Postcondition: for every i such that m is not contained in lhs[i], match_failed[i] is true. /// NB: resets temporary data before performing computations. - bool match_multiaction(const core::identifier_string_list& names) { + bool match_multiaction(const action_multiset_t& multi_action) { + assert(std::is_sorted(multi_action.begin(), multi_action.end(), action_compare())); + reset_temporary_data(); // m must match a lhs; check every action - for (const action_name_t& actionname: names) + for (const process::action& action: multi_action) { - // check every lhs for actionname bool comm_ok = false; for (std::size_t i=0; i < size(); ++i) @@ -192,7 +176,7 @@ class comm_entry m_match_failed[i]=true; continue; } - if (actionname != *m_lhs_iters[i]) + if (action.label().name() != *m_lhs_iters[i]) { // no match m_match_failed[i] = true; @@ -223,6 +207,7 @@ class comm_entry for (const process::communication_expression& l: communications) { const core::identifier_string_list& names = l.action_name().names(); + assert(std::is_sorted(names.begin(), names.end(), action_name_compare())); result.emplace_back(names.begin(), names.end()); } return result; @@ -256,26 +241,27 @@ class comm_entry std::size_t size() const { - assert(lhs.size()==rhs.size() && rhs.size()==m_lhs_iters.size() && m_lhs_iters.size()==match_failed.size()); + assert(m_lhs.size() == m_rhs.size()); + assert(m_rhs.size() == m_lhs_iters.size()); + assert(m_lhs_iters.size() == m_match_failed.size()); return m_lhs.size(); } /// Determine if there exists a communication expression a1|...|an -> b in comm_table - /// such that m' \subseteq a1|...|an , where m' is the multiset of actionnames for multiaction m. + /// such that m' = a1|...|an , where m' is the multiset of actionnames for multiaction m. + /// That is, the actions in m consting of actions and data occur in C, such tat a communication + /// can take place. + /// + /// if \exists_{(b,c) \in C} b = \mu(m), return c, otherwise return action_label() process::action_label can_communicate(const action_multiset_t& m) { - /* this function indicates whether the actions in m - consisting of actions and data occur in C, such that - a communication can take place. If not process::action_label() is delivered, - otherwise the resulting action is the result. */ + assert(std::is_sorted(m.begin(), m.end(), action_compare())); - const core::identifier_string_list m_names = names(m); + process::action_label result; // if no match found, return process::action_label() - process::action_label result; // if no match fount, return process::action_label() - - if(match_multiaction(m_names)) + if(match_multiaction(m)) { - // there is a lhs containing m; find it + // There is a lhs that has m as prefix. Find it, and determine if the lhs matches m completely. for (std::size_t i = 0; i < size(); ++i) { // lhs i matches only if comm_table[i] is empty @@ -294,11 +280,19 @@ class comm_entry return result; } + /// Calculate \exists_{o,c} (\mu(m) \oplus o, c) \in C, with o \subseteq n + /// + /// The function calculates whether the actions in m (consisting of actions and data) occur in a left hand side + /// of a communication a1|...|ak -> b in C (that is, the names of m are a subbag of a1|...|ak), and the actions + /// a1|...|ak that are not in m are in n. I.e., there is a subbag o of n such that m+o can communicate. template bool might_communicate(const action_multiset_t& m, ConstIterType n_first, ConstIterType n_last) { + assert(std::is_sorted(m.begin(), m.end(), action_compare())); + assert(std::is_sorted(n_first, n_last, action_compare())); + /* this function indicates whether the actions in m consisting of actions and data occur in C, such that a communication might take place (i.e. m is a subbag @@ -306,11 +300,9 @@ class comm_entry if n is not empty, then all actions of a matching communication that are not in m should be in n (i.e. there must be a subbag o of n such that m+o can communicate. */ - const core::identifier_string_list m_names = names(m); - bool result = false; - if(match_multiaction(m_names)) + if(match_multiaction(m)) { // the rest of actions of lhs that are not in m should be in n // rest[i] contains the part of n in which lhs i has to find matching actions @@ -375,15 +367,37 @@ class comm_entry } }; +/// Determine if the action is allowable, that is, it is part of an allow expression +/// and it is not part of a block expression +inline +bool maybe_allowed(const process::action_label& a, bool filter_unallowed_actions, const std::vector& allowed_actions, const std::vector& blocked_actions) +{ + assert(std::is_sorted(allowed_actions.begin(), allowed_actions.end(), action_name_compare())); + assert(std::is_sorted(blocked_actions.begin(), blocked_actions.end(), action_name_compare())); + return !filter_unallowed_actions || (std::binary_search(allowed_actions.begin(), allowed_actions.end(), a.name(), action_name_compare()) && + !std::binary_search(blocked_actions.begin(), blocked_actions.end(), a.name(), action_name_compare())); +} + /// \prototype inline tuple_list makeMultiActionConditionList_aux( - action_multiset_t::const_iterator multiaction_first, - action_multiset_t::const_iterator multiaction_last, - comm_entry& comm_table, + action_multiset_t::const_iterator m_first, + action_multiset_t::const_iterator m_last, + comm_entry& C, const action_multiset_t& r, + const std::vector& allowed_actions, + const std::vector& blocked_actions, + bool filter_unallowed_actions, const std::function& RewriteTerm); +/// Calculate $\phi(m, d, w, n, C, r)$ as described in M. v. Weerdenburg. Calculation of Communication +/// with Open Terms in GenSpect Process Algebra. +/// +/// phi is a function that yields a list of pairs indicating how the actions in m|w|n can communicate. +/// The pairs contain the resulting multi action and a condition on data indicating when communication can take place. +/// In the communication all actions of m, none of w and a subset of n can take part in the communication. +/// d is the data parameter of the communication and comm_table contains a list of multiaction action pairs +/// indicating possible communications. inline tuple_list phi(const action_multiset_t& m, const data::data_expression_list& d, @@ -392,58 +406,61 @@ tuple_list phi(const action_multiset_t& m, const action_multiset_t::const_iterator& n_last, const action_multiset_t& r, comm_entry& comm_table, + const std::vector& allowed_actions, + const std::vector& blocked_actions, + bool filter_unallowed_actions, const std::function& RewriteTerm) { - /* phi is a function that yields a list of pairs - indicating how the actions in m|w|n can communicate. - The pairs contain the resulting multi action and - a condition on data indicating when communication - can take place. In the communication all actions of - m, none of w and a subset of n can take part in the - communication. d is the data parameter of the communication - and comm_table contains a list of multiaction action pairs indicating - possible communications */ - - if (!comm_table.might_communicate(m, n_first, n_last)) - { - return tuple_list(); - } - if (n_first == n_last) + assert(std::is_sorted(m.begin(), m.end(), action_compare())); + assert(std::is_sorted(w.begin(), w.end(), action_compare())); + assert(std::is_sorted(n_first, n_last, action_compare())); + assert(std::is_sorted(r.begin(), r.end(), action_compare())); + + tuple_list S; + + // \exists_{o,c} (\mu(m) \oplus o, c) \in C, with o \subseteq n + if (comm_table.might_communicate(m, n_first, n_last)) { - const process::action_label c = comm_table.can_communicate(m); /* returns process::action_label() if no communication - is possible */ - if (c!=process::action_label()) + if (n_first == n_last) // b \land n = [] { - tuple_list T=makeMultiActionConditionList_aux(w.begin(), w.end(),comm_table,r,RewriteTerm); - tuple_list result; - addActionCondition(process::action(c,d), data::sort_bool::true_(), std::move(T), result); - return result; + // There is communication expression whose lhs matches m, result is c. + const process::action_label c = comm_table.can_communicate(m); /* returns process::action_label() if no communication + is possible */ + + if (c!=process::action_label() && maybe_allowed(c, filter_unallowed_actions, allowed_actions, blocked_actions)) + { + // \exists_{(b,c) \in C} b = \mu(m) + // the result of the communication is part of an allow, not in the block set. + // Calculate communication for multiaction w. + // T := \overline{\gamma}(w, C) + tuple_list T = makeMultiActionConditionList_aux(w.begin(), w.end(),comm_table, r, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); + + // S := \{ (c(d) \oplus r, e) \mid (r,e) \in T \} + addActionCondition(process::action(c,d), data::sort_bool::true_(), std::move(T), S); + } } - /* c==NULL, actions in m cannot communicate */ - return tuple_list(); - } - /* if n=[a(f)] \oplus o */ - const process::action& firstaction=*n_first; + else + { + // n = [a(f)] \oplus o + const process::action& a = *n_first; - const data::data_expression condition=pairwiseMatch(d,firstaction.arguments(),RewriteTerm); - if (condition==data::sort_bool::false_()) - { - // a(f) cannot take part in communication as the arguments do not match. Move to w and continue with next action - const tuple_list result = phi(m,d,insert(firstaction, w),std::next(n_first), n_last,r,comm_table,RewriteTerm); - return result; - } - else - { - tuple_list T=phi(insert(firstaction, m),d,w,std::next(n_first), n_last,r,comm_table,RewriteTerm); - - tuple_list result = phi(m,d,insert(firstaction, w),std::next(n_first), n_last,r,comm_table,RewriteTerm); - addActionCondition( - process::action(), - condition, - std::move(T), - result); - return result; + const data::data_expression condition=pairwise_equal_to(d,a.arguments(),RewriteTerm); + if (condition==data::sort_bool::false_()) + { + // a(f) cannot take part in communication as the arguments do not match. Move to w and continue with next action + S = phi(m,d,insert(a, w),std::next(n_first), n_last,r,comm_table, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); + } + else + { + tuple_list T=phi(insert(a, m),d,w,std::next(n_first), n_last,r,comm_table, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); + + S = phi(m,d,insert(a, w),std::next(n_first), n_last,r,comm_table,allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); + addActionCondition(process::action(), condition, std::move(T), S); + } + } } + + return S; } template @@ -452,34 +469,39 @@ bool xi(const action_multiset_t& alpha, const ConstIterType& beta_last, comm_entry& comm_table) { + bool result = false; + if (beta_first == beta_last) { - return comm_table.can_communicate(alpha)!=process::action_label(); + result = comm_table.can_communicate(alpha)!=process::action_label(); } - - const action_multiset_t alpha_ = insert(*beta_first, alpha); - - if (comm_table.can_communicate(alpha_)!=process::action_label()) + else { - return true; - } + const action_multiset_t alpha_ = insert(*beta_first, alpha); - bool result = false; - if (comm_table.might_communicate(alpha_,std::next(beta_first), beta_last)) - { - result = xi(alpha,std::next(beta_first), beta_last,comm_table); - } + if (comm_table.can_communicate(alpha_)!=process::action_label()) + { + result = true; + } + else + { + if (comm_table.might_communicate(alpha_,std::next(beta_first), beta_last)) + { + result = xi(alpha,std::next(beta_first), beta_last,comm_table); + } - result = result || xi(alpha,std::next(beta_first), beta_last, comm_table); + result = result || xi(alpha,std::next(beta_first), beta_last, comm_table); + } + } return result; } inline data::data_expression psi(const action_multiset_t& alpha, comm_entry& comm_table, const std::function& RewriteTerm) { - const action_multiset_t alpha_reverse = reverse(alpha); - action_multiset_t::const_iterator alpha_first = alpha_reverse.begin(); - const action_multiset_t::const_iterator alpha_last = alpha_reverse.end(); + assert(std::is_sorted(alpha.begin(), alpha.end(), action_compare())); + action_multiset_t::const_iterator alpha_first = alpha.begin(); + const action_multiset_t::const_iterator alpha_last = alpha.end(); data::data_expression cond = data::sort_bool::false_(); @@ -492,66 +514,134 @@ data::data_expression psi(const action_multiset_t& alpha, comm_entry& comm_table while (beta_first != beta_last) { actl = action_multiset_t(); - actl = insert(*beta_first, actl); actl = insert(*alpha_first, actl); + actl = insert(*beta_first, actl); if (comm_table.might_communicate(actl,std::next(beta_first), beta_last) && xi(actl,std::next(beta_first), beta_last,comm_table)) { // sort and remove duplicates?? - cond = data::lazy::or_(cond,pairwiseMatch(alpha_first->arguments(),beta_first->arguments(),RewriteTerm)); + cond = data::lazy::or_(cond,pairwise_equal_to(alpha_first->arguments(),beta_first->arguments(),RewriteTerm)); } beta_first = std::next(beta_first); } alpha_first = std::next(alpha_first); } - return data::lazy::not_(cond); + cond = data::lazy::not_(cond); + return cond; } -// returns a list of tuples. +/// Calculate the communication operator applied to a multiaction. +/// +/// This is the function $\overline(\gamma, C, r)$ as described in M. v. Weerdenburg. Calculation of Communication +/// with Open Terms in GenSpect Process Algebra. +/// +/// \param m_first Start of a range of multiactions to which the communication operator should be applied +/// \param m_last End of a range of multiactions to which the communication operator should be applied +/// \param C The communication expressions that must be applied to the multiaction +/// \param r +/// \param RewriteTerm Data rewriter for simplifying expressions. inline tuple_list makeMultiActionConditionList_aux( - action_multiset_t::const_iterator multiaction_first, - action_multiset_t::const_iterator multiaction_last, - comm_entry& comm_table, + action_multiset_t::const_iterator m_first, + action_multiset_t::const_iterator m_last, + comm_entry& C, const action_multiset_t& r, + const std::vector& allowed_actions, + const std::vector& blocked_actions, + const bool filter_unallowed_actions, const std::function& RewriteTerm) { - /* This is the function gamma(m,C,r) provided - by Muck van Weerdenburg in Calculation of - Communication with open terms [1]. */ - if (multiaction_first == multiaction_last) + assert(std::is_sorted(m_first, m_last, action_compare())); + assert(std::is_sorted(r.begin(), r.end(), action_compare())); + + tuple_list S; // result + + // if m = [], then S := { (r, \psi(r, C)) } + if (m_first == m_last) { - tuple_list t; - t.conditions.push_back((r.empty())?static_cast(data::sort_bool::true_()):psi(r,comm_table,RewriteTerm)); - t.actions.push_back(action_multiset_t()); - return t; + if (r.empty()) + { + S.conditions.emplace_back(data::sort_bool::true_()); + } + else + { + S.conditions.emplace_back(psi(r,C, RewriteTerm)); + } + + // TODO: Why don't we insert r here, as van Weerdenburg writes? + S.actions.emplace_back(); } + else + { + // m = [a(d)] \oplus n + const process::action& a = *m_first; - const process::action& firstaction = *multiaction_first; + // S = \phi(a(d), d, [], n, C, r) + S = phi({a}, a.arguments(), action_multiset_t(), std::next(m_first), m_last, + r,C, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); - action_multiset_t m({firstaction}); - action_multiset_t w; - tuple_list S=phi(m, - firstaction.arguments(), - w, - std::next(multiaction_first), multiaction_last, - r,comm_table, RewriteTerm); + // addActionCondition adds a to every multiaction in T; so if a is not part of any allowed action, + // we can skip this part. + if (maybe_allowed(a.label(), filter_unallowed_actions, allowed_actions, blocked_actions)) + { + // T = \overline{\gamma}(n, C, [a(d)] \oplus r) + tuple_list T = makeMultiActionConditionList_aux(std::next(m_first), m_last,C, + insert(a, r), allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); - tuple_list T=makeMultiActionConditionList_aux(std::next(multiaction_first), multiaction_last,comm_table, - insert(firstaction, r),RewriteTerm); - addActionCondition(firstaction,data::sort_bool::true_(),std::move(T),S); + // S := S \cup \{ (a,true) \oplus t \mid t \in T \} + // TODO: van Weerdenburg in his note only calculates S := S \cup T. Understand why that is not correct. + addActionCondition(a,data::sort_bool::true_(),std::move(T),S); + } + } return S; } +/// Calculate the communication operator applied to a multiaction. +/// +/// As the actions in the multiaction can be parameterized with open data expressions, for every subset of communication +/// expressions that match the multiaction, the result contains a multiaction (in which the particular subset of +/// communication expressions has been applied), and a corresponding condition that requires that the parameters are +/// equal for the actions in the left-hand-sides of the matching communication expressions. +/// Multiactions whose condition is false may be removed. +/// +/// This is the function $\overline{\gamma}$ in M. v. Weerdenburg. Calculation of Communication with Open Terms in +/// GenSpect Process Algebra. +/// +/// \param m The multiaction to which the communication operator is applied +/// \param C The communication expressions to be applied +/// \param RewriteTerm The rewriter that should be used to simplify the conditions. inline tuple_list makeMultiActionConditionList( - const action_multiset_t& multiaction, - const process::communication_expression_list& communications, + const action_multiset_t& m, + const process::communication_expression_list& C, + const std::vector& allowed_actions, + const std::vector& blocked_actions, + bool filter_unallowed_actions, const std::function& RewriteTerm) { - comm_entry comm_table(communications); - action_multiset_t r; - return makeMultiActionConditionList_aux(multiaction.begin(), multiaction.end(), comm_table, r, RewriteTerm); + assert(std::is_sorted(m.begin(), m.end(), action_compare())); + comm_entry comm_table(C); + const action_multiset_t r; + return makeMultiActionConditionList_aux(m.begin(), m.end(), comm_table, r, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); +} + +/// Get a sorted list of action names that appear in any of the elements of allowlist +/// The result does not contain duplicates. +/// We use this to remove possible communication results that are not relevant because they +/// are guaranteed to be removed from the result after the fact. +inline +std::vector get_actions(const process::action_name_multiset_list& allowlist) +{ + std::vector result; + for (const process::action_name_multiset& l : allowlist) + { + const action_name_multiset_t& names = l.names(); + result.insert(result.end(), names.begin(), names.end()); + std::sort(result.begin(), result.end(), action_name_compare()); + auto it = std::unique(result.begin(), result.end()); + result.erase(it, result.end()); + } + return result; } /// Apply the communication composition to a list of action summands. @@ -570,6 +660,8 @@ void communicationcomposition( const std::function& RewriteTerm) { + assert(!(is_allow && is_block)); + /* We follow the implementation of Muck van Weerdenburg, described in a note: Calculation of communication with open terms. */ @@ -580,6 +672,8 @@ void communicationcomposition( #ifdef MCRL2_COUNT_COMMUNICATION_OPERATIONS mCRL2log(mcrl2::log::info) << "Calculating communication operator using a set of " << communications.size() << " communication expressions." << std::endl; + mCRL2log(mcrl2::log::info) << "Communication expressions: " << std::endl << core::detail::print_set(communications) << std::endl; + mCRL2log(mcrl2::log::info) << "Allow list: " << std::endl << core::detail::print_set(allowlist) << std::endl; #endif // Ensure communications and allowlist are sorted. We rely on the sort order later. @@ -589,6 +683,24 @@ void communicationcomposition( allowlist = sort_multi_action_labels(allowlist); } + // We precompute a vector of blocked actions and a vector of allowed actions. NB the allowed actions are those actions + // that appear in any allowed multiaction. + std::vector blocked_actions; + if (is_block) + { + blocked_actions = get_actions(allowlist); + } + std::vector allowed_actions; + if (is_allow) + { + allowed_actions = get_actions(allowlist); + // Ensure that the termination action is always allowed, even when it is not an explicit part of + // the list of allowed actions. + // We maintains sort order of the vector + std::vector::iterator it = std::upper_bound(allowed_actions.begin(), allowed_actions.end(), terminationAction.label().name(), action_name_compare()); + allowed_actions.insert(it, terminationAction.label().name()); + } + deadlock_summand_vector resulting_deadlock_summands; deadlock_summands.swap(resulting_deadlock_summands); @@ -640,11 +752,17 @@ void communicationcomposition( the original multiaction is delivered, with condition true. */ - const tuple_list multiactionconditionlist= - makeMultiActionConditionList( - multiaction, - communications, - RewriteTerm); + // We calculate the communication operator on the multiaction in this single summand. As the actions in the + // multiaction can be parameterized with open data expressions, for every subset of applicable communication expressions + // this list in principle contains one summand (unless the condition can be rewritten to false, in which case it + // is omitted). + +#ifdef MCRL2_COUNT_COMMUNICATION_OPERATIONS + mCRL2log(mcrl2::log::info) << "Calculating communication on multiaction with " << multiaction.size() << " actions." << std::endl; + mCRL2log(mcrl2::log::info) << " Multiaction: " << process::pp(multiaction) << std::endl; +#endif + + const tuple_list multiactionconditionlist= makeMultiActionConditionList(multiaction, communications, allowed_actions, blocked_actions, is_allow || is_block, RewriteTerm); #ifdef MCRL2_COUNT_COMMUNICATION_OPERATIONS mCRL2log(mcrl2::log::info) << "Calculating communication on multiaction with " << multiaction.size() << " actions results in " << multiactionconditionlist.size() << " potential summands" << std::endl; diff --git a/libraries/lps/include/mcrl2/lps/linearise_utility.h b/libraries/lps/include/mcrl2/lps/linearise_utility.h index a0b249a487..a6cd2b0b0b 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_utility.h +++ b/libraries/lps/include/mcrl2/lps/linearise_utility.h @@ -114,9 +114,9 @@ core::identifier_string_list insert( } inline -process::action_name_multiset sort_action_labels(const process::action_name_multiset& action_labels, +process::action_name_multiset sort_action_names(const process::action_name_multiset& action_labels, const std::function& cmp - = [](const core::identifier_string& t1, const core::identifier_string& t2){ return std::string(t1)& cmp + = [](const process::action& t1, const process::action& t2){ return action_compare()(t1, t2);}) +{ + return process::action_list(atermpp::sort_list(actions, cmp)); +} + + /// Sort the left-hand sides of the communication expressions in communications /// /// Sorting is done using sort_action_labels, so by default, the left-hand sides of the communications are sorted by names of the actions. @@ -137,7 +146,7 @@ process::communication_expression_list sort_communications(const process::commun for (const process::communication_expression& comm: communications) { - result.push_front(process::communication_expression(sort_action_labels(comm.action_name()),comm.name())); + result.push_front(process::communication_expression(sort_action_names(comm.action_name()),comm.name())); } return result; diff --git a/libraries/lps/test/linearise_communication_test.cpp b/libraries/lps/test/linearise_communication_test.cpp new file mode 100644 index 0000000000..5d4c4320a7 --- /dev/null +++ b/libraries/lps/test/linearise_communication_test.cpp @@ -0,0 +1,186 @@ +// Author(s): Jeroen Keiren +// Copyright: see the accompanying file COPYING or copy at +// https://github.com/mCRL2org/mCRL2/blob/master/COPYING +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +/// \file linearise_communication_test.cpp +/// \brief Test for applying communication operator + +#define BOOST_TEST_MODULE linearise_communication_test +#include +#include + +#include "../../process/include/mcrl2/process/detail/alphabet_parse.h" +#include "../include/mcrl2/lps/linearise_communication.h" +#include "../include/mcrl2/lps/parse.h" + +//#define SKIP_LONG_TESTS + +using namespace mcrl2; +using namespace mcrl2::process; +using namespace mcrl2::lps; + +struct LogDebug +{ + LogDebug() + { + log::logger::set_reporting_level(log::debug); + } +}; +BOOST_GLOBAL_FIXTURE(LogDebug); + +// The following data specification and multiactions are for a large testcase that is extracted from a real-life case +// study (translating Cordis models to mCRL2). Calculating communication expressions, especially on the large sets +// used to take close to 10 minutes. +inline +data::data_specification data_spec() +{ + data::data_specification data_spec = data::detail::default_specification(); + data_spec.add_sort(data::basic_sort("D")); + for (std::size_t i = 0; i < 48; ++i) + { + data::sort_expression s = (16 <= i && i <= 25?data::sort_bool::bool_():data::basic_sort("D")); + data_spec.add_mapping(data::function_symbol("d"+ std::to_string(i), s)); + } + return data_spec; +} + +inline +action_label_list action_declarations() +{ + const std::vector bool_indices({22, 23, 24, 25, 26, 27, 28, 29, 30, 31}); + const std::vector d_indices({1, 4, 5, 9, 11, 13, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 56, 57, 64, 83}); + action_label_list result; + for (std::size_t i = 0; i < 6; ++i) + { + result.emplace_front(core::identifier_string("b" + std::to_string(i)), data::sort_expression_list()); + } + + for (std::size_t i = 0; i < 84; ++i) + { + data::sort_expression_list s; + if (std::find(d_indices.begin(), d_indices.end(), i) != d_indices.end()) { s.push_front(data::basic_sort("D")); } + else if (std::find(bool_indices.begin(), bool_indices.end(), i) != bool_indices.end()) { s.push_front(data::sort_bool::bool_()) ;} + + result.emplace_front(core::identifier_string("a" + std::to_string(i)), s); + result.emplace_front(core::identifier_string("a" + std::to_string(i) + "_r"), s); + result.emplace_front(core::identifier_string("a" + std::to_string(i) + "_s"), s); + } + return result; +} + +inline +action_name_multiset_list allow_set() +{ + const std::string allow_string("{ b1, b2, b3, a1 | a4 | a5 | a9 | a10 | a11 | a13 | a56 | a57 | a64, a1 | a4 | a5 | a9 | a11 | a13 | a56 | a57 | a64, a1 | a4 | a5 | a9 | a11 | a13 | a64, a1 | a8 | a64, a1 | a8 | a64 | a63, a1 | a11 | a64, a1 | a12 | a64, a1 | a20 | a64, a1 | a64, a1 | a64 | a63, a1 | a64 | a63 | a65, a2, a3, a4 | a5 | a9 | a20 | a64, a4 | a5 | a9 | a40 | a54 | a64 | a72, a4 | a5 | a13 | a20 | a64, a4 | a5 | a13 | a39 | a52 | a64 | a71, a4 | a9 | a20 | a64, a4 | a9 | a54 | a64 | a70, a4 | a20 | a64, a4 | a20 | a64 | a73, a4 | a39 | a52 | a64, a4 | a39 | a52 | a64 | a71, a4 | a52 | a64, a4 | a52 | a64 | a68, a5 | a13 | a20 | a64, a5 | a13 | a52 | a64 | a69, a5 | a20 | a64, a5 | a20 | a64 | a74, a5 | a40 | a54 | a64, a5 | a40 | a54 | a64 | a72, a5 | a54 | a64, a5 | a54 | a64 | a68, a6, a7, a8 | a20 | a52 | a64 | a63 | a76, a8 | a20 | a54 | a64 | a63 | a76, a8 | a20 | a64 | a63, a8 | a20 | a64 | a63 | a76, a9 | a20 | a64, a9 | a52 | a64, a13 | a20 | a64, a13 | a54 | a64, a14, a15, a16, a17, a18, a19, a20, a20 | a52 | a54 | a64 | a63 | a65, a20 | a52 | a64 | a63 | a65, a20 | a54 | a64 | a63 | a65, a20 | a64, a20 | a64 | a63 | a65, a20 | a64 | a66 | a67, a20 | a64 | a74, a21, a22 | a23 | a24 | a25 | a26 | a27 | a28 | a29 | a30 | a31 | a32 | a33 | a34 | a35 | a36 | a37 | a38 | a39 | a40 | a41, a22 | a32, a23 | a33, a27 | a37, a28 | a38, a29 | a39, a30 | a40, a31 | a41, a39 | a52 | a64, a40 | a54 | a64, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52 | a64, a52 | a64 | a68, a52 | a64 | a71, a53, a54 | a64, a54 | a64 | a68, a54 | a64 | a72, a55, a58, a59, a60, a61, a62, a64, a64 | a63, a64 | a63 | a65, a64 | a66 | a67, a75, a77, a78, a79, a80, a81, a82, a83, b4, b5 }"); + action_name_multiset_list result = process::detail::parse_allow_set(allow_string); + result = sort_multi_action_labels(result); + return result; +} + +inline +communication_expression_list comm_set() +{ + const std::string comm_string("{ a1_r | a1_s -> a1, a2_r | a2_s -> a2, a3_r | a3_s -> a3, a4_r | a4_s -> a4, a5_r | a5_s -> a5," + "a6_r | a6_s -> a6, a7_r | a7_s -> a7, a8_r | a8_s -> a8, a9_r | a9_s -> a9, a10_r | a10_s -> a10, a11_r | a11_s -> a11," + "a12_r | a12_s -> a12, a13_r | a13_s -> a13, a14_r | a14_s -> a14, a15_r | a15_s -> a15, a16_r | a16_s -> a16, a17_r | a17_s -> a17," + "a18_r | a18_s -> a18, a19_r | a19_s -> a19, a20_r | a20_s -> a20, a21_r | a21_s -> a21, a22_r | a22_s -> a22, a23_r | a23_s -> a23," + "a24_r | a24_s -> a24, a25_r | a25_s -> a25, a26_r | a26_s -> a26, a27_r | a27_s -> a27, a28_r | a28_s -> a28, a29_r | a29_s -> a29," + "a30_r | a30_s -> a30, a31_r | a31_s -> a31, a32_r | a32_s -> a32, a33_r | a33_s -> a33, a34_r | a34_s -> a34, a35_r | a35_s -> a35," + "a36_r | a36_s -> a36, a37_r | a37_s -> a37, a38_r | a38_s -> a38, a39_r | a39_s -> a39, a40_r | a40_s -> a40, a41_r | a41_s -> a41," + "a42_r | a42_s -> a42, a43_r | a43_s -> a43, a44_r | a44_s -> a44, a45_r | a45_s -> a45, a46_r | a46_s -> a46, a47_r | a47_s -> a47," + "a48_r | a48_s -> a48, a49_r | a49_s -> a49, a50_r | a50_s -> a50, a51_r | a51_s -> a51, a52_r | a52_s -> a52, a53_r | a53_s -> a53," + "a54_r | a54_s -> a54, a55_r | a55_s -> a55, a56_r | a56_s -> a56, a57_r | a57_s -> a57, a58_r | a58_s -> a58, a59_r | a59_s -> a59," + "a60_r | a60_s -> a60, a61_r | a61_s -> a61, a62_r | a62_s -> a62, a63_r | a63_s -> a63, a64_r | a64_s -> a64, a65_r | a65_s -> a65," + "a66_r | a66_s -> a66, a67_r | a67_s -> a67, a68_r | a68_s -> a68, a69_r | a69_s -> a69, a70_r | a70_s -> a70, a71_r | a71_s -> a71," + "a72_r | a72_s -> a72, a73_r | a73_s -> a73, a74_r | a74_s -> a74, a75_r | a75_s -> a75, a76_r | a76_s -> a76, a77_r | a77_s -> a77," + "a78_r | a78_s -> a78, a79_r | a79_s -> a79, a80_r | a80_s -> a80, a81_r | a81_s -> a81, a82_r | a82_s -> a82, a83_r | a83_s -> a83 }"); + + communication_expression_list result = process::detail::parse_comm_set(comm_string); + result = sort_communications((result)); + return result; +} + +inline +tuple_list filter_allow(const tuple_list& l, const action_name_multiset_list& allowed) +{ + tuple_list result; + for (std::size_t i = 0; i < l.size(); ++i) + { + if (allow_(allowed, l.actions[i], action(action_label("Terminate", data::sort_expression_list()), data::data_expression_list()))) + { + result.conditions.push_back(l.conditions[i]); + result.actions.push_back(l.actions[i]); + } + } + return result; +} + +BOOST_AUTO_TEST_CASE(test_multact_19) +{ + const std::string multiact_19_string("a1_r(d1)|a1_s(d2)|a4_r(d3)|a4_s(d4)|a5_r(d5)|a5_s(d6)|a9_r(d7)|a9_s(d8)|a11_r(d9)|a11_s(d10)|a13_r(d9)|a13_s(d11)|a56_r(d12)|a56_s(d13)|a57_r(d9)|a57_s(d14)|a64_r(d15)|a64_s(d47)|a82_r"); + const multi_action multact_19 = parse_multi_action(multiact_19_string, action_declarations(), data_spec()); + const action_list actions = sort_actions(multact_19.actions()); + assert(std::is_sorted(actions.begin(), actions.end(), action_compare())); + + data::rewriter R(data_spec()); + + action_name_multiset_list allow_set_ = allow_set(); + tuple_list result = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set_), std::vector(), true, R); + BOOST_CHECK_EQUAL(result.size(), 0); // This shows we have a dramatic set size! + + allow_set_ = sort_multi_action_labels(process::detail::parse_allow_set("{ a1|a4|a5|a9|a11|a13|a56|a57|a64|a82_r }")); + result = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set_), std::vector(), true, R); + BOOST_CHECK_EQUAL(result.size(), 1); + result = filter_allow(result, allow_set_); + BOOST_CHECK_EQUAL(result.size(), 1); + + allow_set_ = sort_multi_action_labels(process::detail::parse_allow_set("{ a1|a4|a5|a9|a11|a13|a56|a57|a64|a82_r, a1|a4_r|a4_s|a5|a9|a11|a13|a56|a57|a64|a82_r }")); + result = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set_), std::vector(), true, R); + BOOST_CHECK_EQUAL(result.size(), 2); + result = filter_allow(result, allow_set_); + BOOST_CHECK_EQUAL(result.size(), 2); + + allow_set_ = sort_multi_action_labels(process::detail::parse_allow_set("{ a1|a4|a5|a9|a11|a13|a56|a57|a64|a82 }")); + result = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set_), std::vector(), true, R); + BOOST_CHECK_EQUAL(result.size(), 0); + result = filter_allow(result, allow_set_); + BOOST_CHECK_EQUAL(result.size(), 0); + +} + +#ifndef SKIP_LONG_TESTS +BOOST_AUTO_TEST_CASE(test_multact_41a) +{ + const std::string multiact_41a_string("a22_r(d16)|a22_s(true)|a23_r(d17)|a23_s(true)|a24_r(d18)|a24_s(true)|a25_r(d19)|a25_s(true)|a26_r(d20)|a26_s(true)|a27_r(d21)|a27_s(true)|a28_r(d22)|a28_s(true)|a29_r(d23)|a29_s(true)|a30_r(d24)|a30_s(true)|a31_r(d25)|a31_s(true)|a32_r(d26)|a32_s(d27)|a33_r(d28)|a33_s(d29)|a34_r(d30)|a34_s(d31)|a35_r(d32)|a35_s(d33)|a36_r(d34)|a36_s(d35)|a37_r(d36)|a37_s(d37)|a38_r(d38)|a38_s(d39)|a39_r(d40)|a39_s(d41)|a40_r(d42)|a40_s(d43)|a41_r(d44)|a41_s(d45)|b4"); + const multi_action multact_41a = parse_multi_action(multiact_41a_string, action_declarations(), data_spec()); + + const action_list actions = sort_actions(multact_41a.actions()); + assert(std::is_sorted(actions.begin(), actions.end(), action_compare())); + + data::rewriter R(data_spec()); + + tuple_list S = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set()), std::vector(), true, R); + BOOST_CHECK_EQUAL(S.size(), 1); // This shows we have a dramatic set size for communication result! + tuple_list result = filter_allow(S, allow_set()); + BOOST_CHECK_EQUAL(result.size(), 0); +} + +BOOST_AUTO_TEST_CASE(test_multact_41b) +{ + const std::string multiact_41b_string("a22_r(d16)|a22_s(true)|a23_r(d17)|a23_s(true)|a24_r(d18)|a24_s(true)|a25_r(d19)|a25_s(true)|a26_r(d20)|a26_s(true)|a27_r(d21)|a27_s(true)|a28_r(d22)|a28_s(true)|a29_r(d23)|a29_s(true)|a30_r(d24)|a30_s(true)|a31_r(d25)|a31_s(true)|a32_r(d26)|a32_s(d27)|a33_r(d28)|a33_s(d29)|a34_r(d30)|a34_s(d31)|a35_r(d32)|a35_s(d33)|a36_r(d34)|a36_s(d35)|a37_r(d36)|a37_s(d37)|a38_r(d38)|a38_s(d39)|a39_r(d40)|a39_s(d41)|a40_r(d42)|a40_s(d43)|a41_r(d44)|a41_s(d45)|a83_s(d46)"); + const multi_action multact_41b = parse_multi_action(multiact_41b_string, action_declarations(), data_spec()); + + const action_list actions = sort_actions(multact_41b.actions()); + assert(std::is_sorted(actions.begin(), actions.end(), action_compare())); + + data::rewriter R(data_spec()); + + tuple_list S = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set()), std::vector(), true, R); + BOOST_CHECK_EQUAL(S.size(), 0); // This shows we have a dramatic set size for communication result! +} +#endif + From a18964e479479db755953ea756cdecb5384c4459 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Wed, 15 Jan 2025 08:49:57 +0100 Subject: [PATCH 49/54] Revert the use of iterators in the interfaces. --- .../mcrl2/lps/linearise_communication.h | 92 +++++++++---------- 1 file changed, 42 insertions(+), 50 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h index 61f2fe5e2a..fd755c3ad4 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_communication.h +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -285,13 +285,10 @@ class comm_entry /// The function calculates whether the actions in m (consisting of actions and data) occur in a left hand side /// of a communication a1|...|ak -> b in C (that is, the names of m are a subbag of a1|...|ak), and the actions /// a1|...|ak that are not in m are in n. I.e., there is a subbag o of n such that m+o can communicate. - template - bool might_communicate(const action_multiset_t& m, - ConstIterType n_first, - ConstIterType n_last) + bool might_communicate(const action_multiset_t& m, const action_multiset_t& n) { assert(std::is_sorted(m.begin(), m.end(), action_compare())); - assert(std::is_sorted(n_first, n_last, action_compare())); + assert(std::is_sorted(n.begin(), n.end(), action_compare())); /* this function indicates whether the actions in m consisting of actions and data occur in C, such that @@ -310,8 +307,8 @@ class comm_entry // before matching all actions in the lhs, we set it to std::nullopt. // N.B. when rest[i] becomes empty after matching all actions in the lhs, // rest[i].empty() is a meaningful result: we have a successful match. - std::vector> - rest(size(), n_first); // pairs of iterator into n; the second element of the pair indicates the end of the range in n. + std::vector> + rest(size(), n.begin()); // pairs of iterator into n; the second element of the pair indicates the end of the range in n. // check every lhs for (std::size_t i = 0; i < size(); ++i) @@ -326,7 +323,7 @@ class comm_entry { assert(rest[i] != std::nullopt); // .. find them in rest[i] - if (*rest[i] == n_last) // no luck + if (*rest[i] == n.end()) // no luck { rest[i] = std::nullopt; break; @@ -338,7 +335,7 @@ class comm_entry while (comm_name != rest_name) { ++(*rest[i]); - if (*rest[i] == n_last) // no more + if (*rest[i] == n.end()) // no more { rest[i] = std::nullopt; break; @@ -381,8 +378,7 @@ bool maybe_allowed(const process::action_label& a, bool filter_unallowed_actions /// \prototype inline tuple_list makeMultiActionConditionList_aux( - action_multiset_t::const_iterator m_first, - action_multiset_t::const_iterator m_last, + const action_multiset_t& m, comm_entry& C, const action_multiset_t& r, const std::vector& allowed_actions, @@ -402,8 +398,7 @@ inline tuple_list phi(const action_multiset_t& m, const data::data_expression_list& d, const action_multiset_t& w, - const action_multiset_t::const_iterator& n_first, - const action_multiset_t::const_iterator& n_last, + const action_multiset_t& n, const action_multiset_t& r, comm_entry& comm_table, const std::vector& allowed_actions, @@ -413,15 +408,15 @@ tuple_list phi(const action_multiset_t& m, { assert(std::is_sorted(m.begin(), m.end(), action_compare())); assert(std::is_sorted(w.begin(), w.end(), action_compare())); - assert(std::is_sorted(n_first, n_last, action_compare())); + assert(std::is_sorted(n.begin(), n.end(), action_compare())); assert(std::is_sorted(r.begin(), r.end(), action_compare())); tuple_list S; // \exists_{o,c} (\mu(m) \oplus o, c) \in C, with o \subseteq n - if (comm_table.might_communicate(m, n_first, n_last)) + if (comm_table.might_communicate(m, n)) { - if (n_first == n_last) // b \land n = [] + if (n.empty()) // b \land n = [] { // There is communication expression whose lhs matches m, result is c. const process::action_label c = comm_table.can_communicate(m); /* returns process::action_label() if no communication @@ -433,7 +428,7 @@ tuple_list phi(const action_multiset_t& m, // the result of the communication is part of an allow, not in the block set. // Calculate communication for multiaction w. // T := \overline{\gamma}(w, C) - tuple_list T = makeMultiActionConditionList_aux(w.begin(), w.end(),comm_table, r, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); + tuple_list T = makeMultiActionConditionList_aux(w,comm_table, r, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); // S := \{ (c(d) \oplus r, e) \mid (r,e) \in T \} addActionCondition(process::action(c,d), data::sort_bool::true_(), std::move(T), S); @@ -442,19 +437,20 @@ tuple_list phi(const action_multiset_t& m, else { // n = [a(f)] \oplus o - const process::action& a = *n_first; + const process::action& a = n.front(); + const action_multiset_t& n_tail = n.tail(); const data::data_expression condition=pairwise_equal_to(d,a.arguments(),RewriteTerm); if (condition==data::sort_bool::false_()) { // a(f) cannot take part in communication as the arguments do not match. Move to w and continue with next action - S = phi(m,d,insert(a, w),std::next(n_first), n_last,r,comm_table, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); + S = phi(m,d,insert(a, w), n_tail, r,comm_table, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); } else { - tuple_list T=phi(insert(a, m),d,w,std::next(n_first), n_last,r,comm_table, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); + tuple_list T=phi(insert(a, m),d,w, n_tail,r,comm_table, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); - S = phi(m,d,insert(a, w),std::next(n_first), n_last,r,comm_table,allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); + S = phi(m,d,insert(a, w), n_tail,r,comm_table,allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); addActionCondition(process::action(), condition, std::move(T), S); } } @@ -463,21 +459,19 @@ tuple_list phi(const action_multiset_t& m, return S; } -template bool xi(const action_multiset_t& alpha, - const ConstIterType& beta_first, - const ConstIterType& beta_last, + const action_multiset_t& beta, comm_entry& comm_table) { bool result = false; - if (beta_first == beta_last) + if (beta.empty()) { result = comm_table.can_communicate(alpha)!=process::action_label(); } else { - const action_multiset_t alpha_ = insert(*beta_first, alpha); + const action_multiset_t alpha_ = insert(beta.front(), alpha); if (comm_table.can_communicate(alpha_)!=process::action_label()) { @@ -485,46 +479,44 @@ bool xi(const action_multiset_t& alpha, } else { - if (comm_table.might_communicate(alpha_,std::next(beta_first), beta_last)) + const action_multiset_t& beta_tail = beta.tail(); + if (comm_table.might_communicate(alpha_, beta_tail)) { - result = xi(alpha,std::next(beta_first), beta_last,comm_table); + result = xi(alpha, beta_tail, comm_table); } - result = result || xi(alpha,std::next(beta_first), beta_last, comm_table); + result = result || xi(alpha, beta_tail, comm_table); } } return result; } inline -data::data_expression psi(const action_multiset_t& alpha, comm_entry& comm_table, const std::function& RewriteTerm) +data::data_expression psi(action_multiset_t alpha, comm_entry& comm_table, const std::function& RewriteTerm) { assert(std::is_sorted(alpha.begin(), alpha.end(), action_compare())); - action_multiset_t::const_iterator alpha_first = alpha.begin(); - const action_multiset_t::const_iterator alpha_last = alpha.end(); - data::data_expression cond = data::sort_bool::false_(); action_multiset_t actl; // used in inner loop. - while (alpha_first != alpha_last) + while (!alpha.empty()) { - action_multiset_t::const_iterator beta_first = std::next(alpha_first); - const action_multiset_t::const_iterator beta_last = alpha_last; + action_multiset_t beta = alpha.tail(); - while (beta_first != beta_last) + while (!beta.empty()) { actl = action_multiset_t(); - actl = insert(*alpha_first, actl); - actl = insert(*beta_first, actl); - if (comm_table.might_communicate(actl,std::next(beta_first), beta_last) && xi(actl,std::next(beta_first), beta_last,comm_table)) + actl = insert(alpha.front(), actl); + actl = insert(beta.front(), actl); + const action_multiset_t& beta_tail = beta.tail(); + if (comm_table.might_communicate(actl, beta_tail) && xi(actl, beta_tail, comm_table)) { // sort and remove duplicates?? - cond = data::lazy::or_(cond,pairwise_equal_to(alpha_first->arguments(),beta_first->arguments(),RewriteTerm)); + cond = data::lazy::or_(cond,pairwise_equal_to(alpha.front().arguments(), beta.front().arguments(), RewriteTerm)); } - beta_first = std::next(beta_first); + beta = beta_tail; } - alpha_first = std::next(alpha_first); + alpha = alpha.tail(); } cond = data::lazy::not_(cond); return cond; @@ -542,8 +534,7 @@ data::data_expression psi(const action_multiset_t& alpha, comm_entry& comm_table /// \param RewriteTerm Data rewriter for simplifying expressions. inline tuple_list makeMultiActionConditionList_aux( - action_multiset_t::const_iterator m_first, - action_multiset_t::const_iterator m_last, + const action_multiset_t& m, comm_entry& C, const action_multiset_t& r, const std::vector& allowed_actions, @@ -557,7 +548,7 @@ tuple_list makeMultiActionConditionList_aux( tuple_list S; // result // if m = [], then S := { (r, \psi(r, C)) } - if (m_first == m_last) + if (m.empty()) { if (r.empty()) { @@ -574,10 +565,11 @@ tuple_list makeMultiActionConditionList_aux( else { // m = [a(d)] \oplus n - const process::action& a = *m_first; + const process::action& a = m.front(); + const action_multiset_t& m_tail = m.tail(); // S = \phi(a(d), d, [], n, C, r) - S = phi({a}, a.arguments(), action_multiset_t(), std::next(m_first), m_last, + S = phi({a}, a.arguments(), action_multiset_t(), m_tail, r,C, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); // addActionCondition adds a to every multiaction in T; so if a is not part of any allowed action, @@ -585,7 +577,7 @@ tuple_list makeMultiActionConditionList_aux( if (maybe_allowed(a.label(), filter_unallowed_actions, allowed_actions, blocked_actions)) { // T = \overline{\gamma}(n, C, [a(d)] \oplus r) - tuple_list T = makeMultiActionConditionList_aux(std::next(m_first), m_last,C, + tuple_list T = makeMultiActionConditionList_aux(m_tail,C, insert(a, r), allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); // S := S \cup \{ (a,true) \oplus t \mid t \in T \} @@ -622,7 +614,7 @@ tuple_list makeMultiActionConditionList( assert(std::is_sorted(m.begin(), m.end(), action_compare())); comm_entry comm_table(C); const action_multiset_t r; - return makeMultiActionConditionList_aux(m.begin(), m.end(), comm_table, r, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); + return makeMultiActionConditionList_aux(m, comm_table, r, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); } /// Get a sorted list of action names that appear in any of the elements of allowlist From be96510105d4160cf1ba3ef91d66104b66aaf033 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Wed, 15 Jan 2025 08:56:17 +0100 Subject: [PATCH 50/54] Remove type aliases. --- .../mcrl2/lps/linearise_communication.h | 113 +++++++++--------- .../lps/test/linearise_communication_test.cpp | 16 +-- 2 files changed, 60 insertions(+), 69 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h index fd755c3ad4..2e5055b848 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_communication.h +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -28,11 +28,6 @@ namespace mcrl2 namespace lps { -using action_name_t = core::identifier_string; -using action_multiset_t = process::action_list; // sorted w.r.t. action_compare -using action_name_multiset_t = core::identifier_string_list; // sorted w.r.t. action_label_compare - - /// Calculate data expression for pairwise equality of the elements of l1 and l2. /// /// If the lengths of l1 and l2 differ, or for some index i, the sort l1[i] and l2[i] is different, the pairwise @@ -69,7 +64,7 @@ data::data_expression pairwise_equal_to(const data::data_expression_list& l1, co /// can occur. struct tuple_list { - std::vector < action_multiset_t > actions; + std::vector < process::action_list > actions; std::vector < data::data_expression > conditions; std::size_t size() const @@ -80,11 +75,11 @@ struct tuple_list tuple_list() = default; - tuple_list(const std::vector < action_multiset_t>& actions_, const std::vector< data::data_expression >& conditions_) + tuple_list(const std::vector < process::action_list>& actions_, const std::vector< data::data_expression >& conditions_) : actions(actions_), conditions(conditions_) {} - tuple_list(std::vector < action_multiset_t>&& actions_, std::vector< data::data_expression >&& conditions_) + tuple_list(std::vector < process::action_list>&& actions_, std::vector< data::data_expression >&& conditions_) : actions(std::move(actions_)), conditions(std::move(conditions_)) {} }; @@ -106,7 +101,7 @@ void addActionCondition(const process::action& a, const data::data_expression& c } else { - for (action_multiset_t& m: L.actions) + for (process::action_list& m: L.actions) { m = insert(a, m); S.actions.emplace_back(std::move(m)); @@ -132,14 +127,14 @@ class comm_entry { protected: /// Left-hand sides of communication expressions - const std::vector m_lhs; + const std::vector m_lhs; /// Right-hand sides of communication expressions - const std::vector m_rhs; + const std::vector m_rhs; /// Temporary data using in determining whether communication is allowed. /// See usages of the data structure below. - std::vector m_lhs_iters; // offset into lhs + std::vector m_lhs_iters; // offset into lhs std::vector m_match_failed; void reset_temporary_data() @@ -155,7 +150,7 @@ class comm_entry /// Returns true if this is the case, false otherwise. /// Postcondition: for every i such that m is not contained in lhs[i], match_failed[i] is true. /// NB: resets temporary data before performing computations. - bool match_multiaction(const action_multiset_t& multi_action) { + bool match_multiaction(const process::action_list& multi_action) { assert(std::is_sorted(multi_action.begin(), multi_action.end(), action_compare())); reset_temporary_data(); @@ -201,9 +196,9 @@ class comm_entry // Initialization of lhs, defined as static function so it can be used in the constructor. // Allows lhs to be defined as const. - static std::vector < action_name_multiset_t > init_lhs(const process::communication_expression_list& communications) + static std::vector < core::identifier_string_list > init_lhs(const process::communication_expression_list& communications) { - std::vector result; + std::vector result; for (const process::communication_expression& l: communications) { const core::identifier_string_list& names = l.action_name().names(); @@ -215,9 +210,9 @@ class comm_entry // Initialization of rhs, defined as static function so it can be used in the constructor. // Allows rhs to be defined as const. - static std::vector init_rhs(const process::communication_expression_list& communications) + static std::vector init_rhs(const process::communication_expression_list& communications) { - std::vector result; + std::vector result; for (const process::communication_expression& l: communications) { result.push_back(l.name()); @@ -253,7 +248,7 @@ class comm_entry /// can take place. /// /// if \exists_{(b,c) \in C} b = \mu(m), return c, otherwise return action_label() - process::action_label can_communicate(const action_multiset_t& m) + process::action_label can_communicate(const process::action_list& m) { assert(std::is_sorted(m.begin(), m.end(), action_compare())); @@ -285,7 +280,7 @@ class comm_entry /// The function calculates whether the actions in m (consisting of actions and data) occur in a left hand side /// of a communication a1|...|ak -> b in C (that is, the names of m are a subbag of a1|...|ak), and the actions /// a1|...|ak that are not in m are in n. I.e., there is a subbag o of n such that m+o can communicate. - bool might_communicate(const action_multiset_t& m, const action_multiset_t& n) + bool might_communicate(const process::action_list& m, const process::action_list& n) { assert(std::is_sorted(m.begin(), m.end(), action_compare())); assert(std::is_sorted(n.begin(), n.end(), action_compare())); @@ -307,7 +302,7 @@ class comm_entry // before matching all actions in the lhs, we set it to std::nullopt. // N.B. when rest[i] becomes empty after matching all actions in the lhs, // rest[i].empty() is a meaningful result: we have a successful match. - std::vector> + std::vector> rest(size(), n.begin()); // pairs of iterator into n; the second element of the pair indicates the end of the range in n. // check every lhs @@ -329,8 +324,8 @@ class comm_entry break; } // get first action in lhs i - const action_name_t& comm_name = *m_lhs_iters[i]; - action_name_t rest_name = (*rest[i])->label().name(); + const core::identifier_string& comm_name = *m_lhs_iters[i]; + core::identifier_string rest_name = (*rest[i])->label().name(); // find it in rest[i] while (comm_name != rest_name) { @@ -367,7 +362,7 @@ class comm_entry /// Determine if the action is allowable, that is, it is part of an allow expression /// and it is not part of a block expression inline -bool maybe_allowed(const process::action_label& a, bool filter_unallowed_actions, const std::vector& allowed_actions, const std::vector& blocked_actions) +bool maybe_allowed(const process::action_label& a, bool filter_unallowed_actions, const std::vector& allowed_actions, const std::vector& blocked_actions) { assert(std::is_sorted(allowed_actions.begin(), allowed_actions.end(), action_name_compare())); assert(std::is_sorted(blocked_actions.begin(), blocked_actions.end(), action_name_compare())); @@ -378,11 +373,11 @@ bool maybe_allowed(const process::action_label& a, bool filter_unallowed_actions /// \prototype inline tuple_list makeMultiActionConditionList_aux( - const action_multiset_t& m, + const process::action_list& m, comm_entry& C, - const action_multiset_t& r, - const std::vector& allowed_actions, - const std::vector& blocked_actions, + const process::action_list& r, + const std::vector& allowed_actions, + const std::vector& blocked_actions, bool filter_unallowed_actions, const std::function& RewriteTerm); @@ -395,14 +390,14 @@ tuple_list makeMultiActionConditionList_aux( /// d is the data parameter of the communication and comm_table contains a list of multiaction action pairs /// indicating possible communications. inline -tuple_list phi(const action_multiset_t& m, +tuple_list phi(const process::action_list& m, const data::data_expression_list& d, - const action_multiset_t& w, - const action_multiset_t& n, - const action_multiset_t& r, + const process::action_list& w, + const process::action_list& n, + const process::action_list& r, comm_entry& comm_table, - const std::vector& allowed_actions, - const std::vector& blocked_actions, + const std::vector& allowed_actions, + const std::vector& blocked_actions, bool filter_unallowed_actions, const std::function& RewriteTerm) { @@ -438,7 +433,7 @@ tuple_list phi(const action_multiset_t& m, { // n = [a(f)] \oplus o const process::action& a = n.front(); - const action_multiset_t& n_tail = n.tail(); + const process::action_list& n_tail = n.tail(); const data::data_expression condition=pairwise_equal_to(d,a.arguments(),RewriteTerm); if (condition==data::sort_bool::false_()) @@ -459,8 +454,8 @@ tuple_list phi(const action_multiset_t& m, return S; } -bool xi(const action_multiset_t& alpha, - const action_multiset_t& beta, +bool xi(const process::action_list& alpha, + const process::action_list& beta, comm_entry& comm_table) { bool result = false; @@ -471,7 +466,7 @@ bool xi(const action_multiset_t& alpha, } else { - const action_multiset_t alpha_ = insert(beta.front(), alpha); + const process::action_list alpha_ = insert(beta.front(), alpha); if (comm_table.can_communicate(alpha_)!=process::action_label()) { @@ -479,7 +474,7 @@ bool xi(const action_multiset_t& alpha, } else { - const action_multiset_t& beta_tail = beta.tail(); + const process::action_list& beta_tail = beta.tail(); if (comm_table.might_communicate(alpha_, beta_tail)) { result = xi(alpha, beta_tail, comm_table); @@ -492,22 +487,22 @@ bool xi(const action_multiset_t& alpha, } inline -data::data_expression psi(action_multiset_t alpha, comm_entry& comm_table, const std::function& RewriteTerm) +data::data_expression psi(process::action_list alpha, comm_entry& comm_table, const std::function& RewriteTerm) { assert(std::is_sorted(alpha.begin(), alpha.end(), action_compare())); data::data_expression cond = data::sort_bool::false_(); - action_multiset_t actl; // used in inner loop. + process::action_list actl; // used in inner loop. while (!alpha.empty()) { - action_multiset_t beta = alpha.tail(); + process::action_list beta = alpha.tail(); while (!beta.empty()) { - actl = action_multiset_t(); + actl = process::action_list(); actl = insert(alpha.front(), actl); actl = insert(beta.front(), actl); - const action_multiset_t& beta_tail = beta.tail(); + const process::action_list& beta_tail = beta.tail(); if (comm_table.might_communicate(actl, beta_tail) && xi(actl, beta_tail, comm_table)) { // sort and remove duplicates?? @@ -534,11 +529,11 @@ data::data_expression psi(action_multiset_t alpha, comm_entry& comm_table, const /// \param RewriteTerm Data rewriter for simplifying expressions. inline tuple_list makeMultiActionConditionList_aux( - const action_multiset_t& m, + const process::action_list& m, comm_entry& C, - const action_multiset_t& r, - const std::vector& allowed_actions, - const std::vector& blocked_actions, + const process::action_list& r, + const std::vector& allowed_actions, + const std::vector& blocked_actions, const bool filter_unallowed_actions, const std::function& RewriteTerm) { @@ -566,10 +561,10 @@ tuple_list makeMultiActionConditionList_aux( { // m = [a(d)] \oplus n const process::action& a = m.front(); - const action_multiset_t& m_tail = m.tail(); + const process::action_list& m_tail = m.tail(); // S = \phi(a(d), d, [], n, C, r) - S = phi({a}, a.arguments(), action_multiset_t(), m_tail, + S = phi({a}, a.arguments(), process::action_list(), m_tail, r,C, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); // addActionCondition adds a to every multiaction in T; so if a is not part of any allowed action, @@ -604,16 +599,16 @@ tuple_list makeMultiActionConditionList_aux( /// \param RewriteTerm The rewriter that should be used to simplify the conditions. inline tuple_list makeMultiActionConditionList( - const action_multiset_t& m, + const process::action_list& m, const process::communication_expression_list& C, - const std::vector& allowed_actions, - const std::vector& blocked_actions, + const std::vector& allowed_actions, + const std::vector& blocked_actions, bool filter_unallowed_actions, const std::function& RewriteTerm) { assert(std::is_sorted(m.begin(), m.end(), action_compare())); comm_entry comm_table(C); - const action_multiset_t r; + const process::action_list r; return makeMultiActionConditionList_aux(m, comm_table, r, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); } @@ -622,12 +617,12 @@ tuple_list makeMultiActionConditionList( /// We use this to remove possible communication results that are not relevant because they /// are guaranteed to be removed from the result after the fact. inline -std::vector get_actions(const process::action_name_multiset_list& allowlist) +std::vector get_actions(const process::action_name_multiset_list& allowlist) { - std::vector result; + std::vector result; for (const process::action_name_multiset& l : allowlist) { - const action_name_multiset_t& names = l.names(); + const core::identifier_string_list& names = l.names(); result.insert(result.end(), names.begin(), names.end()); std::sort(result.begin(), result.end(), action_name_compare()); auto it = std::unique(result.begin(), result.end()); @@ -677,19 +672,19 @@ void communicationcomposition( // We precompute a vector of blocked actions and a vector of allowed actions. NB the allowed actions are those actions // that appear in any allowed multiaction. - std::vector blocked_actions; + std::vector blocked_actions; if (is_block) { blocked_actions = get_actions(allowlist); } - std::vector allowed_actions; + std::vector allowed_actions; if (is_allow) { allowed_actions = get_actions(allowlist); // Ensure that the termination action is always allowed, even when it is not an explicit part of // the list of allowed actions. // We maintains sort order of the vector - std::vector::iterator it = std::upper_bound(allowed_actions.begin(), allowed_actions.end(), terminationAction.label().name(), action_name_compare()); + std::vector::iterator it = std::upper_bound(allowed_actions.begin(), allowed_actions.end(), terminationAction.label().name(), action_name_compare()); allowed_actions.insert(it, terminationAction.label().name()); } diff --git a/libraries/lps/test/linearise_communication_test.cpp b/libraries/lps/test/linearise_communication_test.cpp index 5d4c4320a7..8874904f50 100644 --- a/libraries/lps/test/linearise_communication_test.cpp +++ b/libraries/lps/test/linearise_communication_test.cpp @@ -17,8 +17,6 @@ #include "../include/mcrl2/lps/linearise_communication.h" #include "../include/mcrl2/lps/parse.h" -//#define SKIP_LONG_TESTS - using namespace mcrl2; using namespace mcrl2::process; using namespace mcrl2::lps; @@ -129,30 +127,29 @@ BOOST_AUTO_TEST_CASE(test_multact_19) data::rewriter R(data_spec()); action_name_multiset_list allow_set_ = allow_set(); - tuple_list result = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set_), std::vector(), true, R); + tuple_list result = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set_), std::vector(), true, R); BOOST_CHECK_EQUAL(result.size(), 0); // This shows we have a dramatic set size! allow_set_ = sort_multi_action_labels(process::detail::parse_allow_set("{ a1|a4|a5|a9|a11|a13|a56|a57|a64|a82_r }")); - result = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set_), std::vector(), true, R); + result = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set_), std::vector(), true, R); BOOST_CHECK_EQUAL(result.size(), 1); result = filter_allow(result, allow_set_); BOOST_CHECK_EQUAL(result.size(), 1); allow_set_ = sort_multi_action_labels(process::detail::parse_allow_set("{ a1|a4|a5|a9|a11|a13|a56|a57|a64|a82_r, a1|a4_r|a4_s|a5|a9|a11|a13|a56|a57|a64|a82_r }")); - result = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set_), std::vector(), true, R); + result = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set_), std::vector(), true, R); BOOST_CHECK_EQUAL(result.size(), 2); result = filter_allow(result, allow_set_); BOOST_CHECK_EQUAL(result.size(), 2); allow_set_ = sort_multi_action_labels(process::detail::parse_allow_set("{ a1|a4|a5|a9|a11|a13|a56|a57|a64|a82 }")); - result = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set_), std::vector(), true, R); + result = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set_), std::vector(), true, R); BOOST_CHECK_EQUAL(result.size(), 0); result = filter_allow(result, allow_set_); BOOST_CHECK_EQUAL(result.size(), 0); } -#ifndef SKIP_LONG_TESTS BOOST_AUTO_TEST_CASE(test_multact_41a) { const std::string multiact_41a_string("a22_r(d16)|a22_s(true)|a23_r(d17)|a23_s(true)|a24_r(d18)|a24_s(true)|a25_r(d19)|a25_s(true)|a26_r(d20)|a26_s(true)|a27_r(d21)|a27_s(true)|a28_r(d22)|a28_s(true)|a29_r(d23)|a29_s(true)|a30_r(d24)|a30_s(true)|a31_r(d25)|a31_s(true)|a32_r(d26)|a32_s(d27)|a33_r(d28)|a33_s(d29)|a34_r(d30)|a34_s(d31)|a35_r(d32)|a35_s(d33)|a36_r(d34)|a36_s(d35)|a37_r(d36)|a37_s(d37)|a38_r(d38)|a38_s(d39)|a39_r(d40)|a39_s(d41)|a40_r(d42)|a40_s(d43)|a41_r(d44)|a41_s(d45)|b4"); @@ -163,7 +160,7 @@ BOOST_AUTO_TEST_CASE(test_multact_41a) data::rewriter R(data_spec()); - tuple_list S = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set()), std::vector(), true, R); + tuple_list S = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set()), std::vector(), true, R); BOOST_CHECK_EQUAL(S.size(), 1); // This shows we have a dramatic set size for communication result! tuple_list result = filter_allow(S, allow_set()); BOOST_CHECK_EQUAL(result.size(), 0); @@ -179,8 +176,7 @@ BOOST_AUTO_TEST_CASE(test_multact_41b) data::rewriter R(data_spec()); - tuple_list S = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set()), std::vector(), true, R); + tuple_list S = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set()), std::vector(), true, R); BOOST_CHECK_EQUAL(S.size(), 0); // This shows we have a dramatic set size for communication result! } -#endif From 064a184f19dfa28211331a6a6340aff1d073c661 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Wed, 15 Jan 2025 11:13:35 +0100 Subject: [PATCH 51/54] Improve unit test structure --- .../lps/test/linearise_communication_test.cpp | 280 +++++++++++------- 1 file changed, 180 insertions(+), 100 deletions(-) diff --git a/libraries/lps/test/linearise_communication_test.cpp b/libraries/lps/test/linearise_communication_test.cpp index 8874904f50..c38e8c8e17 100644 --- a/libraries/lps/test/linearise_communication_test.cpp +++ b/libraries/lps/test/linearise_communication_test.cpp @@ -30,57 +30,112 @@ struct LogDebug }; BOOST_GLOBAL_FIXTURE(LogDebug); -// The following data specification and multiactions are for a large testcase that is extracted from a real-life case -// study (translating Cordis models to mCRL2). Calculating communication expressions, especially on the large sets -// used to take close to 10 minutes. +/// Return all tuples in l from which the action is in allowed. inline -data::data_specification data_spec() +tuple_list filter_allow(const tuple_list& l, const action_name_multiset_list& allowed) { - data::data_specification data_spec = data::detail::default_specification(); - data_spec.add_sort(data::basic_sort("D")); - for (std::size_t i = 0; i < 48; ++i) + tuple_list result; + for (std::size_t i = 0; i < l.size(); ++i) { - data::sort_expression s = (16 <= i && i <= 25?data::sort_bool::bool_():data::basic_sort("D")); - data_spec.add_mapping(data::function_symbol("d"+ std::to_string(i), s)); + if (allow_(allowed, l.actions[i], action(action_label("Terminate", data::sort_expression_list()), data::data_expression_list()))) + { + result.conditions.push_back(l.conditions[i]); + result.actions.push_back(l.actions[i]); + } } - return data_spec; + return result; } inline -action_label_list action_declarations() +void run_test_case(const std::string& multiaction_str, const data::data_specification& data_spec, + const process::action_label_list& act_decls, const action_name_multiset_list& allow_set, + const communication_expression_list& comm_exprs, std::size_t expected_number_of_multiactions, + std::size_t expected_number_of_multiactions_filtered, + bool prune_result = true) { - const std::vector bool_indices({22, 23, 24, 25, 26, 27, 28, 29, 30, 31}); - const std::vector d_indices({1, 4, 5, 9, 11, 13, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 56, 57, 64, 83}); - action_label_list result; - for (std::size_t i = 0; i < 6; ++i) - { - result.emplace_front(core::identifier_string("b" + std::to_string(i)), data::sort_expression_list()); - } + const multi_action multiaction(parse_multi_action(multiaction_str, act_decls, data_spec)); + const action_list actions = sort_actions(multiaction.actions()); - for (std::size_t i = 0; i < 84; ++i) - { - data::sort_expression_list s; - if (std::find(d_indices.begin(), d_indices.end(), i) != d_indices.end()) { s.push_front(data::basic_sort("D")); } - else if (std::find(bool_indices.begin(), bool_indices.end(), i) != bool_indices.end()) { s.push_front(data::sort_bool::bool_()) ;} + data::rewriter R(data_spec); + tuple_list result = makeMultiActionConditionList(actions, comm_exprs, get_actions(allow_set), std::vector(), prune_result, R); + BOOST_CHECK_EQUAL(result.size(), expected_number_of_multiactions); - result.emplace_front(core::identifier_string("a" + std::to_string(i)), s); - result.emplace_front(core::identifier_string("a" + std::to_string(i) + "_r"), s); - result.emplace_front(core::identifier_string("a" + std::to_string(i) + "_s"), s); - } - return result; + result = filter_allow(result, allow_set); + BOOST_CHECK_EQUAL(result.size(), expected_number_of_multiactions_filtered); } +// The following data specification and multiactions are for a large testcase that is extracted from a real-life case +// study (translating Cordis models to mCRL2). Calculating communication expressions, especially on the large sets +// used to take close to 10 minutes. inline -action_name_multiset_list allow_set() +data::data_specification data_spec_large() { - const std::string allow_string("{ b1, b2, b3, a1 | a4 | a5 | a9 | a10 | a11 | a13 | a56 | a57 | a64, a1 | a4 | a5 | a9 | a11 | a13 | a56 | a57 | a64, a1 | a4 | a5 | a9 | a11 | a13 | a64, a1 | a8 | a64, a1 | a8 | a64 | a63, a1 | a11 | a64, a1 | a12 | a64, a1 | a20 | a64, a1 | a64, a1 | a64 | a63, a1 | a64 | a63 | a65, a2, a3, a4 | a5 | a9 | a20 | a64, a4 | a5 | a9 | a40 | a54 | a64 | a72, a4 | a5 | a13 | a20 | a64, a4 | a5 | a13 | a39 | a52 | a64 | a71, a4 | a9 | a20 | a64, a4 | a9 | a54 | a64 | a70, a4 | a20 | a64, a4 | a20 | a64 | a73, a4 | a39 | a52 | a64, a4 | a39 | a52 | a64 | a71, a4 | a52 | a64, a4 | a52 | a64 | a68, a5 | a13 | a20 | a64, a5 | a13 | a52 | a64 | a69, a5 | a20 | a64, a5 | a20 | a64 | a74, a5 | a40 | a54 | a64, a5 | a40 | a54 | a64 | a72, a5 | a54 | a64, a5 | a54 | a64 | a68, a6, a7, a8 | a20 | a52 | a64 | a63 | a76, a8 | a20 | a54 | a64 | a63 | a76, a8 | a20 | a64 | a63, a8 | a20 | a64 | a63 | a76, a9 | a20 | a64, a9 | a52 | a64, a13 | a20 | a64, a13 | a54 | a64, a14, a15, a16, a17, a18, a19, a20, a20 | a52 | a54 | a64 | a63 | a65, a20 | a52 | a64 | a63 | a65, a20 | a54 | a64 | a63 | a65, a20 | a64, a20 | a64 | a63 | a65, a20 | a64 | a66 | a67, a20 | a64 | a74, a21, a22 | a23 | a24 | a25 | a26 | a27 | a28 | a29 | a30 | a31 | a32 | a33 | a34 | a35 | a36 | a37 | a38 | a39 | a40 | a41, a22 | a32, a23 | a33, a27 | a37, a28 | a38, a29 | a39, a30 | a40, a31 | a41, a39 | a52 | a64, a40 | a54 | a64, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52 | a64, a52 | a64 | a68, a52 | a64 | a71, a53, a54 | a64, a54 | a64 | a68, a54 | a64 | a72, a55, a58, a59, a60, a61, a62, a64, a64 | a63, a64 | a63 | a65, a64 | a66 | a67, a75, a77, a78, a79, a80, a81, a82, a83, b4, b5 }"); - action_name_multiset_list result = process::detail::parse_allow_set(allow_string); - result = sort_multi_action_labels(result); - return result; + const std::string spec( + "sort D;\n" + "map d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d26, d27, d28, d29, d30, d31, d32, d33, " + " d34, d35, d36, d37, d38, d39, d40, d41, d42, d43, d44, d45, d46, d47: D;\n" + " d16, d17, d18, d19, d20, d21, d22, d23, d24, d25: Bool;\n" + ); + + return data::parse_data_specification(spec); +} + +inline +action_label_list action_declarations_large() +{ + const std::string decl_d( + "a1, a4, a5, a9, a11, a13, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a56, a57, a64, a83," + "a1_r, a4_r, a5_r, a9_r, a11_r, a13_r, a32_r, a33_r, a34_r, a35_r, a36_r, a37_r, a38_r, a39_r, a40_r, a41_r, a56_r, a57_r, a64_r, a83_r," + "a1_s, a4_s, a5_s, a9_s, a11_s, a13_s, a32_s, a33_s, a34_s, a35_s, a36_s, a37_s, a38_s, a39_s, a40_s, a41_s, a56_s, a57_s, a64_s, a83_s: D;"); + const std::string decl_bool( + "a22, a23, a24, a25, a26, a27, a28, a29, a30, a31," + "a22_r, a23_r, a24_r, a25_r, a26_r, a27_r, a28_r, a29_r, a30_r, a31_r," + "a22_s, a23_s, a24_s, a25_s, a26_s, a27_s, a28_s, a29_s, a30_s, a31_s: Bool;" + ); + const std::string decl( + "a2, a3, a6, a7, a8, a10, a12, a14, a15, a16, a17, a18, a19, a20, a21, a42, a43, a44, a45, a46, a47, a48, a49," + "a50, a51, a52, a53, a54, a55, a58, a59, a60, a61, a62, a63, a65, a66, a67, a68, a69, a70, a71, a72," + "a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, " + "a2_r, a3_r, a6_r, a7_r, a8_r, a10_r, a12_r, a14_r, a15_r, a16_r, a17_r, a18_r, a19_r, a20_r, a21_r, a42_r, a43_r, a44_r, a45_r, a46_r, a47_r, a48_r, a49_r," + "a50_r, a51_r, a52_r, a53_r, a54_r, a55_r, a58_r, a59_r, a60_r, a61_r, a62_r, a63_r, a65_r, a66_r, a67_r, a68_r, a69_r, a70_r, a71_r, a72_r," + "a73_r, a74_r, a75_r, a76_r, a77_r, a78_r, a79_r, a80_r, a81_r, a82_r, " + "a2_s, a3_s, a6_s, a7_s, a8_s, a10_s, a12_s, a14_s, a15_s, a16_s, a17_s, a18_s, a19_s, a20_s, a21_s, a42_s, a43_s, a44_s, a45_s, a46_s, a47_s, a48_s, a49_s," + "a50_s, a51_s, a52_s, a53_s, a54_s, a55_s, a58_s, a59_s, a60_s, a61_s, a62_s, a63_s, a65_s, a66_s, a67_s, a68_s, a69_s, a70_s, a71_s, a72_s," + "a73_s, a74_s, a75_s, a76_s, a77_s, a78_s, a79_s, a80_s, a81_s, a82_s," + "b0, b1, b2, b3, b4, b5;" + ); + const action_label_list act_d(parse_action_declaration(decl_d, data_spec_large())); + const action_label_list act_bool(parse_action_declaration(decl_bool, data_spec_large())); + const action_label_list act(parse_action_declaration(decl, data_spec_large())); + + return act_d + act_bool + act; +} + +inline +action_name_multiset_list allow_set_large() +{ + const std::string allow_string( + "{ b1, b2, b3, a1 | a4 | a5 | a9 | a10 | a11 | a13 | a56 | a57 | a64, a1 | a4 | a5 | a9 | a11 | a13 | a56 | a57 | a64," + " a1 | a4 | a5 | a9 | a11 | a13 | a64, a1 | a8 | a64, a1 | a8 | a64 | a63, a1 | a11 | a64, a1 | a12 | a64, a1 | a20 | a64," + " a1 | a64, a1 | a64 | a63, a1 | a64 | a63 | a65, a2, a3, a4 | a5 | a9 | a20 | a64, a4 | a5 | a9 | a40 | a54 | a64 | a72," + " a4 | a5 | a13 | a20 | a64, a4 | a5 | a13 | a39 | a52 | a64 | a71, a4 | a9 | a20 | a64, a4 | a9 | a54 | a64 | a70," + " a4 | a20 | a64, a4 | a20 | a64 | a73, a4 | a39 | a52 | a64, a4 | a39 | a52 | a64 | a71, a4 | a52 | a64, a4 | a52 | a64 | a68," + " a5 | a13 | a20 | a64, a5 | a13 | a52 | a64 | a69, a5 | a20 | a64, a5 | a20 | a64 | a74, a5 | a40 | a54 | a64," + " a5 | a40 | a54 | a64 | a72, a5 | a54 | a64, a5 | a54 | a64 | a68, a6, a7, a8 | a20 | a52 | a64 | a63 | a76," + " a8 | a20 | a54 | a64 | a63 | a76, a8 | a20 | a64 | a63, a8 | a20 | a64 | a63 | a76, a9 | a20 | a64, a9 | a52 | a64," + " a13 | a20 | a64, a13 | a54 | a64, a14, a15, a16, a17, a18, a19, a20, a20 | a52 | a54 | a64 | a63 | a65," + " a20 | a52 | a64 | a63 | a65, a20 | a54 | a64 | a63 | a65, a20 | a64, a20 | a64 | a63 | a65, a20 | a64 | a66 | a67," + " a20 | a64 | a74, a21, a22 | a23 | a24 | a25 | a26 | a27 | a28 | a29 | a30 | a31 | a32 | a33 | a34 | a35 | a36 | a37 | a38 | a39 | a40 | a41," + " a22 | a32, a23 | a33, a27 | a37, a28 | a38, a29 | a39, a30 | a40, a31 | a41, a39 | a52 | a64, a40 | a54 | a64," + " a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52 | a64, a52 | a64 | a68, a52 | a64 | a71, a53, a54 | a64," + " a54 | a64 | a68, a54 | a64 | a72, a55, a58, a59, a60, a61, a62, a64, a64 | a63, a64 | a63 | a65, a64 | a66 | a67," + " a75, a77, a78, a79, a80, a81, a82, a83, b4, b5 }" + ); + return sort_multi_action_labels(process::detail::parse_allow_set(allow_string)); } inline -communication_expression_list comm_set() +communication_expression_list comm_set_large() { const std::string comm_string("{ a1_r | a1_s -> a1, a2_r | a2_s -> a2, a3_r | a3_s -> a3, a4_r | a4_s -> a4, a5_r | a5_s -> a5," "a6_r | a6_s -> a6, a7_r | a7_s -> a7, a8_r | a8_s -> a8, a9_r | a9_s -> a9, a10_r | a10_s -> a10, a11_r | a11_s -> a11," @@ -97,86 +152,111 @@ communication_expression_list comm_set() "a72_r | a72_s -> a72, a73_r | a73_s -> a73, a74_r | a74_s -> a74, a75_r | a75_s -> a75, a76_r | a76_s -> a76, a77_r | a77_s -> a77," "a78_r | a78_s -> a78, a79_r | a79_s -> a79, a80_r | a80_s -> a80, a81_r | a81_s -> a81, a82_r | a82_s -> a82, a83_r | a83_s -> a83 }"); - communication_expression_list result = process::detail::parse_comm_set(comm_string); - result = sort_communications((result)); - return result; + return sort_communications(process::detail::parse_comm_set(comm_string)); } -inline -tuple_list filter_allow(const tuple_list& l, const action_name_multiset_list& allowed) +// Show that the number of multiactions in the result can grow exponentially without pruning. +BOOST_AUTO_TEST_CASE(test_multact_19_no_pruning) { - tuple_list result; - for (std::size_t i = 0; i < l.size(); ++i) - { - if (allow_(allowed, l.actions[i], action(action_label("Terminate", data::sort_expression_list()), data::data_expression_list()))) - { - result.conditions.push_back(l.conditions[i]); - result.actions.push_back(l.actions[i]); - } - } - return result; + run_test_case( + "a1_r(d1)|a1_s(d2)|a4_r(d3)|a4_s(d4)|a5_r(d5)|a5_s(d6)|a9_r(d7)|a9_s(d8)|a11_r(d9)|a11_s(d10)|a13_r(d9)|a13_s(d11)|" + "a56_r(d12)|a56_s(d13)|a57_r(d9)|a57_s(d14)|a64_r(d15)|a64_s(d47)|a82_r", + data_spec_large(), + action_declarations_large(), + allow_set_large(), + comm_set_large(), + 512, + 0, + false + ); } -BOOST_AUTO_TEST_CASE(test_multact_19) +// Show that the number of multiactions in the result can be dramatically reduced +BOOST_AUTO_TEST_CASE(test_multact_19_pruning) { - const std::string multiact_19_string("a1_r(d1)|a1_s(d2)|a4_r(d3)|a4_s(d4)|a5_r(d5)|a5_s(d6)|a9_r(d7)|a9_s(d8)|a11_r(d9)|a11_s(d10)|a13_r(d9)|a13_s(d11)|a56_r(d12)|a56_s(d13)|a57_r(d9)|a57_s(d14)|a64_r(d15)|a64_s(d47)|a82_r"); - const multi_action multact_19 = parse_multi_action(multiact_19_string, action_declarations(), data_spec()); - const action_list actions = sort_actions(multact_19.actions()); - assert(std::is_sorted(actions.begin(), actions.end(), action_compare())); - - data::rewriter R(data_spec()); - - action_name_multiset_list allow_set_ = allow_set(); - tuple_list result = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set_), std::vector(), true, R); - BOOST_CHECK_EQUAL(result.size(), 0); // This shows we have a dramatic set size! - - allow_set_ = sort_multi_action_labels(process::detail::parse_allow_set("{ a1|a4|a5|a9|a11|a13|a56|a57|a64|a82_r }")); - result = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set_), std::vector(), true, R); - BOOST_CHECK_EQUAL(result.size(), 1); - result = filter_allow(result, allow_set_); - BOOST_CHECK_EQUAL(result.size(), 1); - - allow_set_ = sort_multi_action_labels(process::detail::parse_allow_set("{ a1|a4|a5|a9|a11|a13|a56|a57|a64|a82_r, a1|a4_r|a4_s|a5|a9|a11|a13|a56|a57|a64|a82_r }")); - result = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set_), std::vector(), true, R); - BOOST_CHECK_EQUAL(result.size(), 2); - result = filter_allow(result, allow_set_); - BOOST_CHECK_EQUAL(result.size(), 2); - - allow_set_ = sort_multi_action_labels(process::detail::parse_allow_set("{ a1|a4|a5|a9|a11|a13|a56|a57|a64|a82 }")); - result = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set_), std::vector(), true, R); - BOOST_CHECK_EQUAL(result.size(), 0); - result = filter_allow(result, allow_set_); - BOOST_CHECK_EQUAL(result.size(), 0); - + run_test_case( + "a1_r(d1)|a1_s(d2)|a4_r(d3)|a4_s(d4)|a5_r(d5)|a5_s(d6)|a9_r(d7)|a9_s(d8)|a11_r(d9)|a11_s(d10)|a13_r(d9)|a13_s(d11)|" + "a56_r(d12)|a56_s(d13)|a57_r(d9)|a57_s(d14)|a64_r(d15)|a64_s(d47)|a82_r", + data_spec_large(), + action_declarations_large(), + allow_set_large(), + comm_set_large(), + 0, + 0 + ); } -BOOST_AUTO_TEST_CASE(test_multact_41a) +BOOST_AUTO_TEST_CASE(test_multact_19a) { - const std::string multiact_41a_string("a22_r(d16)|a22_s(true)|a23_r(d17)|a23_s(true)|a24_r(d18)|a24_s(true)|a25_r(d19)|a25_s(true)|a26_r(d20)|a26_s(true)|a27_r(d21)|a27_s(true)|a28_r(d22)|a28_s(true)|a29_r(d23)|a29_s(true)|a30_r(d24)|a30_s(true)|a31_r(d25)|a31_s(true)|a32_r(d26)|a32_s(d27)|a33_r(d28)|a33_s(d29)|a34_r(d30)|a34_s(d31)|a35_r(d32)|a35_s(d33)|a36_r(d34)|a36_s(d35)|a37_r(d36)|a37_s(d37)|a38_r(d38)|a38_s(d39)|a39_r(d40)|a39_s(d41)|a40_r(d42)|a40_s(d43)|a41_r(d44)|a41_s(d45)|b4"); - const multi_action multact_41a = parse_multi_action(multiact_41a_string, action_declarations(), data_spec()); - - const action_list actions = sort_actions(multact_41a.actions()); - assert(std::is_sorted(actions.begin(), actions.end(), action_compare())); + run_test_case( + "a1_r(d1)|a1_s(d2)|a4_r(d3)|a4_s(d4)|a5_r(d5)|a5_s(d6)|a9_r(d7)|a9_s(d8)|a11_r(d9)|a11_s(d10)|a13_r(d9)|a13_s(d11)|" + "a56_r(d12)|a56_s(d13)|a57_r(d9)|a57_s(d14)|a64_r(d15)|a64_s(d47)|a82_r", + data_spec_large(), + action_declarations_large(), + sort_multi_action_labels(process::detail::parse_allow_set("{ a1|a4|a5|a9|a11|a13|a56|a57|a64|a82_r }")), + comm_set_large(), + 1, + 1 + ); +} - data::rewriter R(data_spec()); - tuple_list S = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set()), std::vector(), true, R); - BOOST_CHECK_EQUAL(S.size(), 1); // This shows we have a dramatic set size for communication result! - tuple_list result = filter_allow(S, allow_set()); - BOOST_CHECK_EQUAL(result.size(), 0); +BOOST_AUTO_TEST_CASE(test_multact_19b) +{ + run_test_case( + "a1_r(d1)|a1_s(d2)|a4_r(d3)|a4_s(d4)|a5_r(d5)|a5_s(d6)|a9_r(d7)|a9_s(d8)|a11_r(d9)|a11_s(d10)|a13_r(d9)|a13_s(d11)|" + "a56_r(d12)|a56_s(d13)|a57_r(d9)|a57_s(d14)|a64_r(d15)|a64_s(d47)|a82_r", + data_spec_large(), + action_declarations_large(), + sort_multi_action_labels(process::detail::parse_allow_set("{ a1|a4|a5|a9|a11|a13|a56|a57|a64|a82_r, a1|a4_r|a4_s|a5|a9|a11|a13|a56|a57|a64|a82_r }")), + comm_set_large(), + 2, + 2 + ); } -BOOST_AUTO_TEST_CASE(test_multact_41b) +BOOST_AUTO_TEST_CASE(test_multact_19c) { - const std::string multiact_41b_string("a22_r(d16)|a22_s(true)|a23_r(d17)|a23_s(true)|a24_r(d18)|a24_s(true)|a25_r(d19)|a25_s(true)|a26_r(d20)|a26_s(true)|a27_r(d21)|a27_s(true)|a28_r(d22)|a28_s(true)|a29_r(d23)|a29_s(true)|a30_r(d24)|a30_s(true)|a31_r(d25)|a31_s(true)|a32_r(d26)|a32_s(d27)|a33_r(d28)|a33_s(d29)|a34_r(d30)|a34_s(d31)|a35_r(d32)|a35_s(d33)|a36_r(d34)|a36_s(d35)|a37_r(d36)|a37_s(d37)|a38_r(d38)|a38_s(d39)|a39_r(d40)|a39_s(d41)|a40_r(d42)|a40_s(d43)|a41_r(d44)|a41_s(d45)|a83_s(d46)"); - const multi_action multact_41b = parse_multi_action(multiact_41b_string, action_declarations(), data_spec()); - - const action_list actions = sort_actions(multact_41b.actions()); - assert(std::is_sorted(actions.begin(), actions.end(), action_compare())); - - data::rewriter R(data_spec()); + run_test_case( + "a1_r(d1)|a1_s(d2)|a4_r(d3)|a4_s(d4)|a5_r(d5)|a5_s(d6)|a9_r(d7)|a9_s(d8)|a11_r(d9)|a11_s(d10)|a13_r(d9)|a13_s(d11)|" + "a56_r(d12)|a56_s(d13)|a57_r(d9)|a57_s(d14)|a64_r(d15)|a64_s(d47)|a82_r", + data_spec_large(), + action_declarations_large(), + sort_multi_action_labels(process::detail::parse_allow_set("{ a1|a4|a5|a9|a11|a13|a56|a57|a64|a82 }")), + comm_set_large(), + 0, + 0 + ); +} - tuple_list S = makeMultiActionConditionList(actions, comm_set(), get_actions(allow_set()), std::vector(), true, R); - BOOST_CHECK_EQUAL(S.size(), 0); // This shows we have a dramatic set size for communication result! +BOOST_AUTO_TEST_CASE(test_multact_41a) +{ + run_test_case( + "a22_r(d16)|a22_s(true)|a23_r(d17)|a23_s(true)|a24_r(d18)|a24_s(true)|a25_r(d19)|a25_s(true)|a26_r(d20)|" + "a26_s(true)|a27_r(d21)|a27_s(true)|a28_r(d22)|a28_s(true)|a29_r(d23)|a29_s(true)|a30_r(d24)|a30_s(true)|a31_r(d25)|" + "a31_s(true)|a32_r(d26)|a32_s(d27)|a33_r(d28)|a33_s(d29)|a34_r(d30)|a34_s(d31)|a35_r(d32)|a35_s(d33)|a36_r(d34)|" + "a36_s(d35)|a37_r(d36)|a37_s(d37)|a38_r(d38)|a38_s(d39)|a39_r(d40)|a39_s(d41)|a40_r(d42)|a40_s(d43)|a41_r(d44)|a41_s(d45)|b4", + data_spec_large(), + action_declarations_large(), + allow_set_large(), + comm_set_large(), + 1, + 0 + ); } +BOOST_AUTO_TEST_CASE(test_multact_41b) +{ + run_test_case( + "a22_r(d16)|a22_s(true)|a23_r(d17)|a23_s(true)|a24_r(d18)|a24_s(true)|a25_r(d19)|a25_s(true)|a26_r(d20)|" + "a26_s(true)|a27_r(d21)|a27_s(true)|a28_r(d22)|a28_s(true)|a29_r(d23)|a29_s(true)|a30_r(d24)|a30_s(true)|a31_r(d25)|" + "a31_s(true)|a32_r(d26)|a32_s(d27)|a33_r(d28)|a33_s(d29)|a34_r(d30)|a34_s(d31)|a35_r(d32)|a35_s(d33)|a36_r(d34)|" + "a36_s(d35)|a37_r(d36)|a37_s(d37)|a38_r(d38)|a38_s(d39)|a39_r(d40)|a39_s(d41)|a40_r(d42)|a40_s(d43)|a41_r(d44)|a41_s(d45)|a83_s(d46)", + data_spec_large(), + action_declarations_large(), + allow_set_large(), + comm_set_large(), + 0, + 0 + ); +} From cc702bd6b3e292f631e12c4c1fbc5d9af81670fe Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Thu, 16 Jan 2025 09:07:47 +0100 Subject: [PATCH 52/54] Move communication calculation into a class. This significantly cleans up the parameter lists of the methods used. --- .../mcrl2/lps/linearise_communication.h | 664 +++++++++--------- .../lps/test/linearise_communication_test.cpp | 30 +- 2 files changed, 348 insertions(+), 346 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h index 2e5055b848..6ac6e09512 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_communication.h +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -20,7 +20,8 @@ #include "mcrl2/lps/sumelm.h" #include "mcrl2/process/process_expression.h" -#define MCRL2_COUNT_COMMUNICATION_OPERATIONS +// Define the following to add logging of statistics for the calculation of communication. +//#define MCRL2_COUNT_COMMUNICATION_OPERATIONS namespace mcrl2 { @@ -28,34 +29,25 @@ namespace mcrl2 namespace lps { -/// Calculate data expression for pairwise equality of the elements of l1 and l2. -/// -/// If the lengths of l1 and l2 differ, or for some index i, the sort l1[i] and l2[i] is different, the pairwise -/// match is false, otherwise an expression equivalent to \bigwegde_i (l1[i] == l2[i]) is returned. -inline -data::data_expression pairwise_equal_to(const data::data_expression_list& l1, const data::data_expression_list& l2, const std::function& RewriteTerm) +namespace detail { - data::data_expression result = data::sort_bool::true_(); - if (l1.size()!=l2.size()) - { - result = data::sort_bool::false_(); - } - - auto i1 = l1.begin(); - auto i2 = l2.begin(); - - while (i1 != l1.end() && i2 != l2.end() && result != data::sort_bool::false_()) +/// Get a sorted list of action names that appear in any of the elements of allowlist +/// The result does not contain duplicates. +/// We use this to remove possible communication results that are not relevant because they +/// are guaranteed to be removed from the result after the fact. +inline +std::vector get_actions(const process::action_name_multiset_list& allowlist) +{ + std::vector result; + for (const process::action_name_multiset& l : allowlist) { - if (i1->sort() != i2->sort()) - { - result = data::sort_bool::false_(); - } - else - { - result = data::lazy::and_(result, RewriteTerm(equal_to(*i1++, *i2++))); - } + const core::identifier_string_list& names = l.names(); + result.insert(result.end(), names.begin(), names.end()); } + std::sort(result.begin(), result.end(), action_name_compare()); + const auto it = std::unique(result.begin(), result.end()); + result.erase(it, result.end()); return result; } @@ -359,229 +351,28 @@ class comm_entry } }; -/// Determine if the action is allowable, that is, it is part of an allow expression -/// and it is not part of a block expression -inline -bool maybe_allowed(const process::action_label& a, bool filter_unallowed_actions, const std::vector& allowed_actions, const std::vector& blocked_actions) -{ - assert(std::is_sorted(allowed_actions.begin(), allowed_actions.end(), action_name_compare())); - assert(std::is_sorted(blocked_actions.begin(), blocked_actions.end(), action_name_compare())); - return !filter_unallowed_actions || (std::binary_search(allowed_actions.begin(), allowed_actions.end(), a.name(), action_name_compare()) && - !std::binary_search(blocked_actions.begin(), blocked_actions.end(), a.name(), action_name_compare())); -} - -/// \prototype -inline -tuple_list makeMultiActionConditionList_aux( - const process::action_list& m, - comm_entry& C, - const process::action_list& r, - const std::vector& allowed_actions, - const std::vector& blocked_actions, - bool filter_unallowed_actions, - const std::function& RewriteTerm); - -/// Calculate $\phi(m, d, w, n, C, r)$ as described in M. v. Weerdenburg. Calculation of Communication -/// with Open Terms in GenSpect Process Algebra. -/// -/// phi is a function that yields a list of pairs indicating how the actions in m|w|n can communicate. -/// The pairs contain the resulting multi action and a condition on data indicating when communication can take place. -/// In the communication all actions of m, none of w and a subset of n can take part in the communication. -/// d is the data parameter of the communication and comm_table contains a list of multiaction action pairs -/// indicating possible communications. -inline -tuple_list phi(const process::action_list& m, - const data::data_expression_list& d, - const process::action_list& w, - const process::action_list& n, - const process::action_list& r, - comm_entry& comm_table, - const std::vector& allowed_actions, - const std::vector& blocked_actions, - bool filter_unallowed_actions, - const std::function& RewriteTerm) -{ - assert(std::is_sorted(m.begin(), m.end(), action_compare())); - assert(std::is_sorted(w.begin(), w.end(), action_compare())); - assert(std::is_sorted(n.begin(), n.end(), action_compare())); - assert(std::is_sorted(r.begin(), r.end(), action_compare())); - - tuple_list S; - - // \exists_{o,c} (\mu(m) \oplus o, c) \in C, with o \subseteq n - if (comm_table.might_communicate(m, n)) - { - if (n.empty()) // b \land n = [] - { - // There is communication expression whose lhs matches m, result is c. - const process::action_label c = comm_table.can_communicate(m); /* returns process::action_label() if no communication - is possible */ - - if (c!=process::action_label() && maybe_allowed(c, filter_unallowed_actions, allowed_actions, blocked_actions)) - { - // \exists_{(b,c) \in C} b = \mu(m) - // the result of the communication is part of an allow, not in the block set. - // Calculate communication for multiaction w. - // T := \overline{\gamma}(w, C) - tuple_list T = makeMultiActionConditionList_aux(w,comm_table, r, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); - - // S := \{ (c(d) \oplus r, e) \mid (r,e) \in T \} - addActionCondition(process::action(c,d), data::sort_bool::true_(), std::move(T), S); - } - } - else - { - // n = [a(f)] \oplus o - const process::action& a = n.front(); - const process::action_list& n_tail = n.tail(); - - const data::data_expression condition=pairwise_equal_to(d,a.arguments(),RewriteTerm); - if (condition==data::sort_bool::false_()) - { - // a(f) cannot take part in communication as the arguments do not match. Move to w and continue with next action - S = phi(m,d,insert(a, w), n_tail, r,comm_table, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); - } - else - { - tuple_list T=phi(insert(a, m),d,w, n_tail,r,comm_table, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); - - S = phi(m,d,insert(a, w), n_tail,r,comm_table,allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); - addActionCondition(process::action(), condition, std::move(T), S); - } - } - } - - return S; -} - -bool xi(const process::action_list& alpha, - const process::action_list& beta, - comm_entry& comm_table) -{ - bool result = false; - - if (beta.empty()) - { - result = comm_table.can_communicate(alpha)!=process::action_label(); - } - else - { - const process::action_list alpha_ = insert(beta.front(), alpha); - - if (comm_table.can_communicate(alpha_)!=process::action_label()) - { - result = true; - } - else - { - const process::action_list& beta_tail = beta.tail(); - if (comm_table.might_communicate(alpha_, beta_tail)) - { - result = xi(alpha, beta_tail, comm_table); - } - - result = result || xi(alpha, beta_tail, comm_table); - } - } - return result; -} - -inline -data::data_expression psi(process::action_list alpha, comm_entry& comm_table, const std::function& RewriteTerm) +template +class apply_communication_algorithm { - assert(std::is_sorted(alpha.begin(), alpha.end(), action_compare())); - data::data_expression cond = data::sort_bool::false_(); - - process::action_list actl; // used in inner loop. - while (!alpha.empty()) - { - process::action_list beta = alpha.tail(); - - while (!beta.empty()) - { - actl = process::action_list(); - actl = insert(alpha.front(), actl); - actl = insert(beta.front(), actl); - const process::action_list& beta_tail = beta.tail(); - if (comm_table.might_communicate(actl, beta_tail) && xi(actl, beta_tail, comm_table)) - { - // sort and remove duplicates?? - cond = data::lazy::or_(cond,pairwise_equal_to(alpha.front().arguments(), beta.front().arguments(), RewriteTerm)); - } - beta = beta_tail; - } - - alpha = alpha.tail(); - } - cond = data::lazy::not_(cond); - return cond; -} - -/// Calculate the communication operator applied to a multiaction. -/// -/// This is the function $\overline(\gamma, C, r)$ as described in M. v. Weerdenburg. Calculation of Communication -/// with Open Terms in GenSpect Process Algebra. -/// -/// \param m_first Start of a range of multiactions to which the communication operator should be applied -/// \param m_last End of a range of multiactions to which the communication operator should be applied -/// \param C The communication expressions that must be applied to the multiaction -/// \param r -/// \param RewriteTerm Data rewriter for simplifying expressions. -inline -tuple_list makeMultiActionConditionList_aux( - const process::action_list& m, - comm_entry& C, - const process::action_list& r, - const std::vector& allowed_actions, - const std::vector& blocked_actions, - const bool filter_unallowed_actions, - const std::function& RewriteTerm) -{ - assert(std::is_sorted(m_first, m_last, action_compare())); - assert(std::is_sorted(r.begin(), r.end(), action_compare())); - - tuple_list S; // result - - // if m = [], then S := { (r, \psi(r, C)) } - if (m.empty()) - { - if (r.empty()) - { - S.conditions.emplace_back(data::sort_bool::true_()); - } - else - { - S.conditions.emplace_back(psi(r,C, RewriteTerm)); - } - - // TODO: Why don't we insert r here, as van Weerdenburg writes? - S.actions.emplace_back(); - } - else - { - // m = [a(d)] \oplus n - const process::action& a = m.front(); - const process::action_list& m_tail = m.tail(); - - // S = \phi(a(d), d, [], n, C, r) - S = phi({a}, a.arguments(), process::action_list(), m_tail, - r,C, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); +public: + apply_communication_algorithm(const process::action& termination_action, + DataRewriter& data_rewriter, + const process::communication_expression_list& communications, + const process::action_name_multiset_list& allowlist, + bool is_allow, + bool is_block) + : m_terminationAction(termination_action), + m_data_rewriter(data_rewriter), + m_communications(sort_communications(communications)), + m_allowlist(sort_multi_action_labels(allowlist)), + m_blocked_actions(is_block?get_actions(allowlist):std::vector()), + m_allowed_actions(init_allowed_actions(is_allow, allowlist, termination_action)), + m_comm_table(m_communications), + m_is_allow(is_allow), + m_is_block(is_block) + {} - // addActionCondition adds a to every multiaction in T; so if a is not part of any allowed action, - // we can skip this part. - if (maybe_allowed(a.label(), filter_unallowed_actions, allowed_actions, blocked_actions)) - { - // T = \overline{\gamma}(n, C, [a(d)] \oplus r) - tuple_list T = makeMultiActionConditionList_aux(m_tail,C, - insert(a, r), allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); - - // S := S \cup \{ (a,true) \oplus t \mid t \in T \} - // TODO: van Weerdenburg in his note only calculates S := S \cup T. Understand why that is not correct. - addActionCondition(a,data::sort_bool::true_(),std::move(T),S); - } - } - return S; -} + ~apply_communication_algorithm() = default; /// Calculate the communication operator applied to a multiaction. /// @@ -597,55 +388,20 @@ tuple_list makeMultiActionConditionList_aux( /// \param m The multiaction to which the communication operator is applied /// \param C The communication expressions to be applied /// \param RewriteTerm The rewriter that should be used to simplify the conditions. -inline -tuple_list makeMultiActionConditionList( - const process::action_list& m, - const process::communication_expression_list& C, - const std::vector& allowed_actions, - const std::vector& blocked_actions, - bool filter_unallowed_actions, - const std::function& RewriteTerm) +tuple_list apply(const process::action_list& m) { assert(std::is_sorted(m.begin(), m.end(), action_compare())); - comm_entry comm_table(C); const process::action_list r; - return makeMultiActionConditionList_aux(m, comm_table, r, allowed_actions, blocked_actions, filter_unallowed_actions, RewriteTerm); -} - -/// Get a sorted list of action names that appear in any of the elements of allowlist -/// The result does not contain duplicates. -/// We use this to remove possible communication results that are not relevant because they -/// are guaranteed to be removed from the result after the fact. -inline -std::vector get_actions(const process::action_name_multiset_list& allowlist) -{ - std::vector result; - for (const process::action_name_multiset& l : allowlist) - { - const core::identifier_string_list& names = l.names(); - result.insert(result.end(), names.begin(), names.end()); - std::sort(result.begin(), result.end(), action_name_compare()); - auto it = std::unique(result.begin(), result.end()); - result.erase(it, result.end()); - } - return result; + return makeMultiActionConditionList_aux(m, r); } /// Apply the communication composition to a list of action summands. -inline -void communicationcomposition( - process::communication_expression_list communications, - process::action_name_multiset_list allowlist, // This is a list of list of identifierstring. - const bool is_allow, // If is_allow or is_block is set, perform inline allow/block filtering. - const bool is_block, +void apply( stochastic_action_summand_vector& action_summands, deadlock_summand_vector& deadlock_summands, - const process::action& terminationAction, - const bool nosumelm, - const bool nodeltaelimination, - const bool ignore_time, - const std::function& RewriteTerm) - + bool nosumelm, + bool nodeltaelimination, + bool ignore_time) { assert(!(is_allow && is_block)); @@ -653,45 +409,20 @@ void communicationcomposition( a note: Calculation of communication with open terms. */ mCRL2log(mcrl2::log::verbose) << - (is_allow ? "- calculating the communication operator modulo the allow operator on " : - is_block ? "- calculating the communication operator modulo the block operator on " : + (m_is_allow ? "- calculating the communication operator modulo the allow operator on " : + m_is_block ? "- calculating the communication operator modulo the block operator on " : "- calculating the communication operator on ") << action_summands.size() << " action summands"; #ifdef MCRL2_COUNT_COMMUNICATION_OPERATIONS - mCRL2log(mcrl2::log::info) << "Calculating communication operator using a set of " << communications.size() << " communication expressions." << std::endl; - mCRL2log(mcrl2::log::info) << "Communication expressions: " << std::endl << core::detail::print_set(communications) << std::endl; - mCRL2log(mcrl2::log::info) << "Allow list: " << std::endl << core::detail::print_set(allowlist) << std::endl; + mCRL2log(mcrl2::log::info) << "Calculating communication operator using a set of " << m_communications.size() << " communication expressions." << std::endl; + mCRL2log(mcrl2::log::info) << "Communication expressions: " << std::endl << core::detail::print_set(m_communications) << std::endl; + mCRL2log(mcrl2::log::info) << "Allow list: " << std::endl << core::detail::print_set(m_allowlist) << std::endl; #endif - // Ensure communications and allowlist are sorted. We rely on the sort order later. - communications = sort_communications(communications); - if (is_allow) - { - allowlist = sort_multi_action_labels(allowlist); - } - - // We precompute a vector of blocked actions and a vector of allowed actions. NB the allowed actions are those actions - // that appear in any allowed multiaction. - std::vector blocked_actions; - if (is_block) - { - blocked_actions = get_actions(allowlist); - } - std::vector allowed_actions; - if (is_allow) - { - allowed_actions = get_actions(allowlist); - // Ensure that the termination action is always allowed, even when it is not an explicit part of - // the list of allowed actions. - // We maintains sort order of the vector - std::vector::iterator it = std::upper_bound(allowed_actions.begin(), allowed_actions.end(), terminationAction.label().name(), action_name_compare()); - allowed_actions.insert(it, terminationAction.label().name()); - } - deadlock_summand_vector resulting_deadlock_summands; deadlock_summands.swap(resulting_deadlock_summands); - const bool inline_allow = is_allow || is_block; + const bool inline_allow = m_is_allow || m_is_block; if (inline_allow) { // Inline allow is only supported for ignore_time, @@ -749,7 +480,7 @@ void communicationcomposition( mCRL2log(mcrl2::log::info) << " Multiaction: " << process::pp(multiaction) << std::endl; #endif - const tuple_list multiactionconditionlist= makeMultiActionConditionList(multiaction, communications, allowed_actions, blocked_actions, is_allow || is_block, RewriteTerm); + const tuple_list multiactionconditionlist = apply(multiaction); #ifdef MCRL2_COUNT_COMMUNICATION_OPERATIONS mCRL2log(mcrl2::log::info) << "Calculating communication on multiaction with " << multiaction.size() << " actions results in " << multiactionconditionlist.size() << " potential summands" << std::endl; @@ -764,14 +495,14 @@ void communicationcomposition( const process::action_list& multiaction=multiactionconditionlist.actions[i]; - if (is_allow && !allow_(allowlist, multiaction,terminationAction)) + if (m_is_allow && !allow_(m_allowlist, multiaction,m_terminationAction)) { #ifdef MCRL2_COUNT_COMMUNICATION_OPERATIONS ++disallowed_summands; #endif continue; } - if (is_block && encap(allowlist,multiaction)) + if (m_is_block && encap(m_allowlist,multiaction)) { #ifdef MCRL2_COUNT_COMMUNICATION_OPERATIONS ++blocked_summands; @@ -780,9 +511,9 @@ void communicationcomposition( } const data::data_expression communicationcondition= - RewriteTerm(multiactionconditionlist.conditions[i]); + m_data_rewriter(multiactionconditionlist.conditions[i]); - const data::data_expression newcondition=RewriteTerm( + const data::data_expression newcondition=m_data_rewriter( data::lazy::and_(condition,communicationcondition)); stochastic_action_summand new_summand(sumvars, newcondition, @@ -793,7 +524,7 @@ void communicationcomposition( { if (sumelm(new_summand)) { - new_summand.condition() = RewriteTerm(new_summand.condition()); + new_summand.condition() = m_data_rewriter(new_summand.condition()); } } #ifdef MCRL2_COUNT_COMMUNICATION_OPERATIONS @@ -835,6 +566,291 @@ void communicationcomposition( mCRL2log(mcrl2::log::verbose) << " resulting in " << action_summands.size() << " action summands and " << deadlock_summands.size() << " delta summands\n"; } +protected: + +const process::action& m_terminationAction; +DataRewriter& m_data_rewriter; +const process::communication_expression_list m_communications; +const process::action_name_multiset_list m_allowlist; // This is a list of list of identifierstring. +const std::vector m_blocked_actions; +const std::vector m_allowed_actions; +comm_entry m_comm_table; +const bool m_is_allow; // If is_allow or is_block is set, perform inline allow/block filtering. +const bool m_is_block; + +/// Static initialization function to ensure m_allowed_actions can be const. +static const std::vector +init_allowed_actions(bool is_allow, const process::action_name_multiset_list& allow_list, const process::action& termination_action) +{ +std::vector result; +if (is_allow) +{ + result = get_actions(allow_list); + // Ensure that the termination action is always allowed, even when it is not an explicit part of + // the list of allowed actions. + // We maintains sort order of the vector + std::vector::iterator it = std::upper_bound(result.begin(), result.end(), termination_action.label().name(), action_name_compare()); + result.insert(it, termination_action.label().name()); +} + +return result; +} + +/// Calculate data expression for pairwise equality of the elements of l1 and l2. +/// +/// If the lengths of l1 and l2 differ, or for some index i, the sort l1[i] and l2[i] is different, the pairwise +/// match is false, otherwise an expression equivalent to \bigwegde_i (l1[i] == l2[i]) is returned. +data::data_expression pairwise_equal_to(const data::data_expression_list& l1, const data::data_expression_list& l2) const +{ +data::data_expression result = data::sort_bool::true_(); + +if (l1.size()!=l2.size()) +{ + result = data::sort_bool::false_(); +} + +auto i1 = l1.begin(); +auto i2 = l2.begin(); + +while (i1 != l1.end() && i2 != l2.end() && result != data::sort_bool::false_()) +{ + if (i1->sort() != i2->sort()) + { + result = data::sort_bool::false_(); + } + else + { + result = data::lazy::and_(result, m_data_rewriter(equal_to(*i1++, *i2++))); + } +} + +return result; +} + + +/// Determine if the action is allowable, that is, it is part of an allow expression +/// and it is not part of a block expression +bool maybe_allowed(const process::action_label& a) const +{ +assert(std::is_sorted(m_allowed_actions.begin(), m_allowed_actions.end(), action_name_compare())); +assert(std::is_sorted(m_blocked_actions.begin(), m_blocked_actions.end(), action_name_compare())); +return !(m_is_allow || m_is_block) + || (std::binary_search(m_allowed_actions.begin(), m_allowed_actions.end(), a.name(), action_name_compare()) + && !std::binary_search(m_blocked_actions.begin(), m_blocked_actions.end(), a.name(), action_name_compare())); +} + + +/// Calculate the communication operator applied to a multiaction. +/// +/// This is the function $\overline(\gamma, C, r)$ as described in M. v. Weerdenburg. Calculation of Communication +/// with Open Terms in GenSpect Process Algebra. +/// +/// \param m_first Start of a range of multiactions to which the communication operator should be applied +/// \param m_last End of a range of multiactions to which the communication operator should be applied +/// \param C The communication expressions that must be applied to the multiaction +/// \param r +/// \param RewriteTerm Data rewriter for simplifying expressions. +inline +tuple_list makeMultiActionConditionList_aux( + const process::action_list& m, + const process::action_list& r) +{ + assert(std::is_sorted(m_first, m_last, action_compare())); + assert(std::is_sorted(r.begin(), r.end(), action_compare())); + + tuple_list S; // result + + // if m = [], then S := { (r, \psi(r, C)) } + if (m.empty()) + { + if (r.empty()) + { + S.conditions.emplace_back(data::sort_bool::true_()); + } + else + { + S.conditions.emplace_back(psi(r)); + } + + // TODO: Why don't we insert r here, as van Weerdenburg writes? + S.actions.emplace_back(); + } + else + { + // m = [a(d)] \oplus n + const process::action& a = m.front(); + const process::action_list& m_tail = m.tail(); + + // S = \phi(a(d), d, [], n, C, r) + S = phi({a}, a.arguments(), process::action_list(), m_tail, r); + + // addActionCondition adds a to every multiaction in T; so if a is not part of any allowed action, + // we can skip this part. + if (maybe_allowed(a.label())) + { + // T = \overline{\gamma}(n, C, [a(d)] \oplus r) + tuple_list T = makeMultiActionConditionList_aux(m_tail, insert(a, r)); + + // S := S \cup \{ (a,true) \oplus t \mid t \in T \} + // TODO: van Weerdenburg in his note only calculates S := S \cup T. Understand why that is not correct. + addActionCondition(a,data::sort_bool::true_(),std::move(T),S); + } + } + return S; +} + +/// Calculate $\phi(m, d, w, n, C, r)$ as described in M. v. Weerdenburg. Calculation of Communication +/// with Open Terms in GenSpect Process Algebra. +/// +/// phi is a function that yields a list of pairs indicating how the actions in m|w|n can communicate. +/// The pairs contain the resulting multi action and a condition on data indicating when communication can take place. +/// In the communication all actions of m, none of w and a subset of n can take part in the communication. +/// d is the data parameter of the communication and comm_table contains a list of multiaction action pairs +/// indicating possible communications. +inline +tuple_list phi(const process::action_list& m, + const data::data_expression_list& d, + const process::action_list& w, + const process::action_list& n, + const process::action_list& r) +{ + assert(std::is_sorted(m.begin(), m.end(), action_compare())); + assert(std::is_sorted(w.begin(), w.end(), action_compare())); + assert(std::is_sorted(n.begin(), n.end(), action_compare())); + assert(std::is_sorted(r.begin(), r.end(), action_compare())); + + tuple_list S; + + // \exists_{o,c} (\mu(m) \oplus o, c) \in C, with o \subseteq n + if (m_comm_table.might_communicate(m, n)) + { + if (n.empty()) // b \land n = [] + { + // There is communication expression whose lhs matches m, result is c. + const process::action_label c = m_comm_table.can_communicate(m); /* returns process::action_label() if no communication + is possible */ + + if (c!=process::action_label() && maybe_allowed(c)) + { + // \exists_{(b,c) \in C} b = \mu(m) + // the result of the communication is part of an allow, not in the block set. + // Calculate communication for multiaction w. + // T := \overline{\gamma}(w, C) + tuple_list T = makeMultiActionConditionList_aux(w, r); + + // S := \{ (c(d) \oplus r, e) \mid (r,e) \in T \} + addActionCondition(process::action(c,d), data::sort_bool::true_(), std::move(T), S); + } + } + else + { + // n = [a(f)] \oplus o + const process::action& a = n.front(); + const process::action_list& n_tail = n.tail(); + + const data::data_expression condition=pairwise_equal_to(d,a.arguments()); + if (condition==data::sort_bool::false_()) + { + // a(f) cannot take part in communication as the arguments do not match. Move to w and continue with next action + S = phi(m,d,insert(a, w), n_tail, r); + } + else + { + tuple_list T=phi(insert(a, m),d,w, n_tail,r); + + S = phi(m,d,insert(a, w), n_tail,r); + addActionCondition(process::action(), condition, std::move(T), S); + } + } + } + + return S; +} + +bool xi(const process::action_list& alpha, + const process::action_list& beta) +{ + bool result = false; + + if (beta.empty()) + { + result = m_comm_table.can_communicate(alpha)!=process::action_label(); + } + else + { + const process::action_list alpha_ = insert(beta.front(), alpha); + + if (m_comm_table.can_communicate(alpha_)!=process::action_label()) + { + result = true; + } + else + { + const process::action_list& beta_tail = beta.tail(); + if (m_comm_table.might_communicate(alpha_, beta_tail)) + { + result = xi(alpha, beta_tail); + } + + result = result || xi(alpha, beta_tail); + } + } + return result; +} + +data::data_expression psi(process::action_list alpha) +{ + assert(std::is_sorted(alpha.begin(), alpha.end(), action_compare())); + data::data_expression cond = data::sort_bool::false_(); + + process::action_list actl; // used in inner loop. + while (!alpha.empty()) + { + process::action_list beta = alpha.tail(); + + while (!beta.empty()) + { + actl = process::action_list(); + actl = insert(alpha.front(), actl); + actl = insert(beta.front(), actl); + const process::action_list& beta_tail = beta.tail(); + if (m_comm_table.might_communicate(actl, beta_tail) && xi(actl, beta_tail)) + { + // sort and remove duplicates?? + cond = data::lazy::or_(cond,pairwise_equal_to(alpha.front().arguments(), beta.front().arguments())); + } + beta = beta_tail; + } + + alpha = alpha.tail(); + } + cond = data::lazy::not_(cond); + return cond; +} + +}; +} + +inline +void communicationcomposition( + const process::communication_expression_list& communications, + const process::action_name_multiset_list& allowlist, // This is a list of list of identifierstring. + const bool is_allow, // If is_allow or is_block is set, perform inline allow/block filtering. + const bool is_block, + stochastic_action_summand_vector& action_summands, + deadlock_summand_vector& deadlock_summands, + const process::action& terminationAction, + const bool nosumelm, + const bool nodeltaelimination, + const bool ignore_time, + const std::function& RewriteTerm) + +{ + return detail::apply_communication_algorithm(terminationAction, RewriteTerm, communications, allowlist, is_allow, is_block).apply( + action_summands, deadlock_summands, nosumelm, nodeltaelimination, ignore_time); +} + + } // namespace lps } // namespace mcrl2 diff --git a/libraries/lps/test/linearise_communication_test.cpp b/libraries/lps/test/linearise_communication_test.cpp index c38e8c8e17..3a0a4c90d7 100644 --- a/libraries/lps/test/linearise_communication_test.cpp +++ b/libraries/lps/test/linearise_communication_test.cpp @@ -32,9 +32,9 @@ BOOST_GLOBAL_FIXTURE(LogDebug); /// Return all tuples in l from which the action is in allowed. inline -tuple_list filter_allow(const tuple_list& l, const action_name_multiset_list& allowed) +lps::detail::tuple_list filter_allow(const lps::detail::tuple_list& l, const action_name_multiset_list& allowed) { - tuple_list result; + lps::detail::tuple_list result; for (std::size_t i = 0; i < l.size(); ++i) { if (allow_(allowed, l.actions[i], action(action_label("Terminate", data::sort_expression_list()), data::data_expression_list()))) @@ -50,14 +50,16 @@ inline void run_test_case(const std::string& multiaction_str, const data::data_specification& data_spec, const process::action_label_list& act_decls, const action_name_multiset_list& allow_set, const communication_expression_list& comm_exprs, std::size_t expected_number_of_multiactions, - std::size_t expected_number_of_multiactions_filtered, - bool prune_result = true) + std::size_t expected_number_of_multiactions_filtered) { + data::rewriter R(data_spec); + const process::action terminate(process::action_label("Terminate", data::sort_expression_list()), data::data_expression_list()); + lps::detail::apply_communication_algorithm alg(terminate, R, comm_exprs, allow_set, true, false); + const multi_action multiaction(parse_multi_action(multiaction_str, act_decls, data_spec)); const action_list actions = sort_actions(multiaction.actions()); - data::rewriter R(data_spec); - tuple_list result = makeMultiActionConditionList(actions, comm_exprs, get_actions(allow_set), std::vector(), prune_result, R); + lps::detail::tuple_list result = alg.apply(actions); BOOST_CHECK_EQUAL(result.size(), expected_number_of_multiactions); result = filter_allow(result, allow_set); @@ -155,22 +157,6 @@ communication_expression_list comm_set_large() return sort_communications(process::detail::parse_comm_set(comm_string)); } -// Show that the number of multiactions in the result can grow exponentially without pruning. -BOOST_AUTO_TEST_CASE(test_multact_19_no_pruning) -{ - run_test_case( - "a1_r(d1)|a1_s(d2)|a4_r(d3)|a4_s(d4)|a5_r(d5)|a5_s(d6)|a9_r(d7)|a9_s(d8)|a11_r(d9)|a11_s(d10)|a13_r(d9)|a13_s(d11)|" - "a56_r(d12)|a56_s(d13)|a57_r(d9)|a57_s(d14)|a64_r(d15)|a64_s(d47)|a82_r", - data_spec_large(), - action_declarations_large(), - allow_set_large(), - comm_set_large(), - 512, - 0, - false - ); -} - // Show that the number of multiactions in the result can be dramatically reduced BOOST_AUTO_TEST_CASE(test_multact_19_pruning) { From 3910ff61372caf8cd568fcbd59f6a32f5afd1171 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Mon, 20 Jan 2025 16:29:47 +0100 Subject: [PATCH 53/54] Fix assertions. --- libraries/lps/include/mcrl2/lps/linearise_communication.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h index 6ac6e09512..52822f1abd 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_communication.h +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -403,7 +403,7 @@ void apply( bool nodeltaelimination, bool ignore_time) { - assert(!(is_allow && is_block)); + assert(!(m_is_allow && m_is_block)); /* We follow the implementation of Muck van Weerdenburg, described in a note: Calculation of communication with open terms. */ @@ -655,7 +655,7 @@ tuple_list makeMultiActionConditionList_aux( const process::action_list& m, const process::action_list& r) { - assert(std::is_sorted(m_first, m_last, action_compare())); + assert(std::is_sorted(m.begin(), m.end(), action_compare())); assert(std::is_sorted(r.begin(), r.end(), action_compare())); tuple_list S; // result From a87f69bed830d809cff7602f3d5573b5398725b6 Mon Sep 17 00:00:00 2001 From: Jeroen Keiren Date: Mon, 20 Jan 2025 16:30:35 +0100 Subject: [PATCH 54/54] Add missing include for Windows --- libraries/lps/include/mcrl2/lps/linearise_communication.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/lps/include/mcrl2/lps/linearise_communication.h b/libraries/lps/include/mcrl2/lps/linearise_communication.h index 52822f1abd..d975ca39e5 100644 --- a/libraries/lps/include/mcrl2/lps/linearise_communication.h +++ b/libraries/lps/include/mcrl2/lps/linearise_communication.h @@ -12,6 +12,7 @@ #ifndef MCRL2_LPS_LINEARISE_COMMUNICATION_H #define MCRL2_LPS_LINEARISE_COMMUNICATION_H +#include #include "mcrl2/atermpp/aterm_list.h" #include "mcrl2/core/detail/print_utility.h" #include "mcrl2/lps/linearise_allow_block.h"