BPMN-OS
BPMN for optimization and simulation
Loading...
Searching...
No Matches
Choice.cpp
Go to the documentation of this file.
1#include "Choice.h"
3#include <cmath>
4#include <strutil.h>
7
8using namespace BPMNOS::Model;
9
10Choice::Choice(XML::bpmnos::tDecision* decision, const AttributeRegistry& attributeRegistry)
11 : element(decision)
12 , attributeRegistry(attributeRegistry)
13 , attribute(nullptr)
14{
15 auto input = encodeQuotedStrings(decision->condition.value.value);
16 strutil::replace_all( input, "∈", " in ");
17
18 if ( strutil::contains(input," in ") ) {
19 parseEnumeration(input);
20 }
21 else if ( !strutil::contains(input,"<") ) {
22 throw std::runtime_error("Choice: no enumeration or bounds given in '" + input + "'");
23 }
24 else {
25 strutil::replace_all( input, "|", " divides ");
26 auto [bounds,discretizer] = [&input]() -> std::pair<std::string, std::string> {
27 if ( !strutil::contains(input," divides ") ) {
28 // no discretizer provided
29 return { input, "" };
30 }
31
32 // discretizer is assumed to be provided following the bounds, separated by comma
33 auto pos = input.rfind(',');
34 if ( pos == std::string::npos ) {
35 throw std::runtime_error("Choice: illegal condition '" + input + "'");
36 }
37 return {
38 strutil::trim_copy(input.substr(0, pos)),
39 strutil::trim_copy(input.substr(pos + 1))
40 };
41 }();
42
43 parseBounds(bounds);
44
45 if ( !discretizer.empty() ) {
46 parseDiscretizer(discretizer);
47 }
48 }
49
50 if ( attribute->type == STRING && enumeration.empty() ) {
51 throw std::runtime_error("Choice: no enumeration provided for string");
52 }
53 if ( attribute->type == COLLECTION ) {
54 throw std::runtime_error("Choice: attribute is a collection");
55 }
56
57 attribute->isImmutable = false;
58}
59
60void Choice::parseEnumeration(const std::string& input) {
61 auto parts = strutil::split(input," in ");
62 if ( parts.size() != 2 ) {
63 throw std::runtime_error("Choice: illegal enumeration '" + input + "'");
64 }
65
66 std::string attributeName = strutil::trim_copy(parts.front());
67 if ( attributeName == "" ) {
68 throw std::runtime_error("Choice: unable to determine attribute name");
69 }
70 attribute = attributeRegistry[ attributeName ];
71
72 auto rhs = strutil::trim_copy(parts.back());
73
74 if ( (rhs.front() == '[' && rhs.back() == ']') || (rhs.front() == '{' && rhs.back() == '}') ) {
75 auto alternatives = strutil::split( encodeCollection( rhs.substr(1, rhs.size()-2) ), ',' );
76 for ( auto& alternative : alternatives ) {
77 enumeration.emplace_back( std::make_unique<Expression>(strutil::trim_copy(alternative), attributeRegistry) );
78 for ( auto dependency : enumeration.back()->inputs ) {
79 dependencies.insert(dependency);
80 }
81 }
82 if ( enumeration.empty() ) {
83 throw std::runtime_error("Choice: empty enumeration");
84 }
85 }
86 else {
87 throw std::runtime_error("Choice: invalid enumeration '" + rhs + "'");
88 }
89}
90
91void Choice::parseBounds(const std::string& input) {
92 // check bounds
93 auto conditions = strutil::split(input,'<');
94 if ( conditions.size() == 3 ) {
95 bool strictLB = false;
96 // condition has two inequalities
97 if ( conditions[1][0] == '=' ) {
98 // inequality, remove '=' and trim
99 conditions[1].erase(0, 1);
100 }
101 else {
102 // strict inequality
103 strictLB = true;
104 }
105 lowerBound.emplace(
106 std::make_unique<Expression>(strutil::trim_copy(conditions[0]), attributeRegistry),
107 strictLB
108 );
109 for ( auto dependency : lowerBound.value().first->inputs ) {
110 dependencies.insert(dependency);
111 }
112
113 // determine attribute
114 std::string attributeName = strutil::trim_copy(conditions[1]);
115 if ( attributeName == "" ) {
116 throw std::runtime_error("Choice: unable to determine attribute name");
117 }
118 attribute = attributeRegistry[ attributeName ];
119
120 bool strictUB = false;
121 if ( conditions[2][0] == '=' ) {
122 // inequality, remove '=' and trim
123 conditions[2].erase(0, 1);
124 }
125 else {
126 // strict inequality
127 strictUB = true;
128 }
129
130 upperBound.emplace(
131 std::make_unique<Expression>(strutil::trim_copy(conditions[2]), attributeRegistry),
132 strictUB
133 );
134 for ( auto dependency : upperBound.value().first->inputs ) {
135 dependencies.insert(dependency);
136 }
137
138 }
139 else {
140 // unbounded
141 throw std::runtime_error("Choice: condition '" + input + "' is unbounded");
142 }
143}
144
145void Choice::parseDiscretizer(const std::string& input) {
146 auto parts = strutil::split(input," divides ");
147 std::string attributeName = strutil::trim_copy(parts[1]);
148 if ( attribute->name != attributeName ) {
149 throw std::runtime_error("Choice: inconsistent attribute name '" + attributeName + "' in '" + input + "'");
150 }
151 multipleOf = std::make_unique<Expression>(strutil::trim_copy(parts[0]), attributeRegistry);
152 for ( auto dependency : multipleOf->inputs ) {
153 dependencies.insert(dependency);
154 }
155}
156
157
158template <typename DataType>
159std::pair<BPMNOS::number,BPMNOS::number> Choice::getBounds(const BPMNOS::Values& status, const DataType& data, const BPMNOS::Values& globals) const {
160 assert( attribute->type != STRING );
161 assert( lowerBound.has_value() );
162 assert( upperBound.has_value() );
163 auto& [LB,strictLB] = lowerBound.value();
164 BPMNOS::number min = LB->execute(status,data,globals).value_or(std::numeric_limits<BPMNOS::number>::lowest());
165 if ( strictLB ) {
167 }
168
169 auto& [UB,strictUB] = upperBound.value();
170 BPMNOS::number max = UB->execute(status,data,globals).value_or(std::numeric_limits<BPMNOS::number>::max());
171 if ( strictUB ) {
173 }
174
175 if ( attribute->type != DECIMAL ) {
176 min = std::ceil((double)min);
177 max = std::floor((double)max);
178 }
179
180 return {min,max};
181}
182
183template std::pair<BPMNOS::number,BPMNOS::number> Choice::getBounds<BPMNOS::Values>(const BPMNOS::Values& status, const BPMNOS::Values& data, const BPMNOS::Values& globals) const;
184template std::pair<BPMNOS::number,BPMNOS::number> Choice::getBounds<BPMNOS::SharedValues>(const BPMNOS::Values& status, const BPMNOS::SharedValues& data, const BPMNOS::Values& globals) const;
185
186
187template <typename DataType>
188std::vector<BPMNOS::number> Choice::getEnumeration(const BPMNOS::Values& status, const DataType& data, const BPMNOS::Values& globals) const {
189 assert( !enumeration.empty() || multipleOf );
190 std::vector<BPMNOS::number> allowedValues;
191 if ( !enumeration.empty() ) {
192 for ( auto& alternative : enumeration ) {
193 auto allowedValue = alternative->execute(status,data,globals);
194 if ( allowedValue.has_value() ) {
195 allowedValues.push_back( allowedValue.value() );
196 }
197 }
198 }
199 else {
200 auto [LB, UB] = getBounds(status, data, globals);
201 auto discretizer = multipleOf->execute(status, data, globals);
202 if ( !discretizer.has_value() ) {
204 discretizer = 1;
205 }
206 else {
207 throw std::runtime_error("Choice: cannot determine discretizer for '" + multipleOf->expression + "'");
208 }
209 }
210 BPMNOS::number DELTA = std::abs((double)discretizer.value());
211 for ( BPMNOS::number value = (double)DELTA * std::ceil((double)LB / (double)DELTA); value <= UB; value += DELTA ) {
212 allowedValues.push_back(value);
213 }
214 }
215
216 return allowedValues;
217}
218
219template std::vector<BPMNOS::number> Choice::getEnumeration<BPMNOS::Values>(const BPMNOS::Values& status, const BPMNOS::Values& data, const BPMNOS::Values& globals) const;
220template std::vector<BPMNOS::number> Choice::getEnumeration<BPMNOS::SharedValues>(const BPMNOS::Values& status, const BPMNOS::SharedValues& data, const BPMNOS::Values& globals) const;
221
#define BPMNOS_NUMBER_PRECISION
Definition Number.h:40
const std::string name
Definition Attribute.h:32
bool isImmutable
Flag indicating whether attribute value may be changed by operator, choice, or intermediate catch eve...
Definition Attribute.h:38
std::optional< std::pair< std::unique_ptr< Expression >, bool > > upperBound
Definition Choice.h:28
std::set< const Attribute * > dependencies
Definition Choice.h:31
Choice(XML::bpmnos::tDecision *decision, const AttributeRegistry &attributeRegistry)
Definition Choice.cpp:10
std::optional< std::pair< std::unique_ptr< Expression >, bool > > lowerBound
Definition Choice.h:27
std::pair< BPMNOS::number, BPMNOS::number > getBounds(const BPMNOS::Values &status, const DataType &data, const BPMNOS::Values &globals) const
Returns the minimal and maximal value the attribute may take.
Definition Choice.cpp:159
std::vector< BPMNOS::number > getEnumeration(const BPMNOS::Values &status, const DataType &data, const BPMNOS::Values &globals) const
Returns the allowed values the attribute may take.
Definition Choice.cpp:188
Attribute * attribute
Definition Choice.h:26
const AttributeRegistry & attributeRegistry
Definition Choice.h:25
std::unique_ptr< Expression > multipleOf
Definition Choice.h:29
std::vector< std::unique_ptr< Expression > > enumeration
Definition Choice.h:30
Attribute & condition
Attribute value can be expected to be of type 'std::string'.
Definition tDecision.h:45
std::string encodeQuotedStrings(std::string text)
std::string encodeCollection(std::string text)
Function to replace a collection that is not preceded by an alphanumeric or underscore.
BPMNOS_NUMBER_TYPE number
Definition Number.h:50
@ INTEGER
Definition Value.h:9
@ STRING
Definition Value.h:9
@ BOOLEAN
Definition Value.h:9
@ COLLECTION
Definition Value.h:9
@ DECIMAL
Definition Value.h:9
Value value
Definition bpmn++.h:78
std::string value
Definition bpmn++.h:50