BPMN-OS
BPMN for optimization and simulation
Loading...
Searching...
No Matches
ExpectedValueDataProvider.cpp
Go to the documentation of this file.
5#include <ranges>
6
7using namespace BPMNOS::Model;
8
9ExpectedValueDataProvider::ExpectedValueDataProvider(const std::string& modelFile, const std::string& instanceFileOrString)
10 : ExpectedValueDataProvider(modelFile, {}, instanceFileOrString)
11{
12}
13
14ExpectedValueDataProvider::ExpectedValueDataProvider(const std::string& modelFile, const std::vector<std::string>& folders, const std::string& instanceFileOrString)
15 : StaticDataProvider(modelFile, folders)
16{
18
19 reader = std::make_unique<CSVReader>(instanceFileOrString);
21}
22
24 // Copy lookup tables from model
25 for (auto& lookupTable : model->lookupTables) {
26 auto* table = lookupTable.get();
27 expectedValueHandle.add(table->name, [table](const std::vector<double>& args) {
28 return table->at(args);
29 });
30 }
31
32 // Register expected value functions
34}
35
36BPMNOS::number ExpectedValueDataProvider::evaluateExpression(const std::string& expressionString) const {
37 Values globals(model->attributes.size());
38 for (auto& [attribute, value] : globalValueMap) {
39 globals[attribute->index] = value;
40 }
41 Expression expression(expectedValueHandle, expressionString, model->attributeRegistry);
42 for (auto* attribute : expression.variables) {
43 if (attribute->category != Attribute::Category::GLOBAL) {
44 throw std::runtime_error("ExpectedValueDataProvider: expression '" + expressionString +
45 "' references non-global attribute '" + attribute->name + "'");
46 }
47 }
48 auto value = expression.execute(Values{}, Values{}, globals);
49 if (!value.has_value()) {
50 throw std::runtime_error("ExpectedValueDataProvider: failed to evaluate expression '" + expressionString + "'");
51 }
52 return value.value();
53}
54
56 CSVReader::Table table = reader->read();
57 if (table.empty()) {
58 throw std::runtime_error("ExpectedValueDataProvider: table is empty");
59 }
60 if (table.size() < 2) {
61 throw std::runtime_error("ExpectedValueDataProvider: table must have at least a header and one data row");
62 }
63
64 size_t columnCount = table[0].size();
65 if (columnCount == 3 || columnCount == 4 || columnCount == 6) {
66 // 3-column: INSTANCE_ID, NODE_ID, INITIALIZATION
67 // 4-column: + DISCLOSURE (ignored)
68 // 6-column: + ARRIVAL, COMPLETION (ignored)
69 readInstancesExtendedFormat(table, columnCount);
70 }
71 else {
72 throw std::runtime_error("ExpectedValueDataProvider: expected 3, 4, or 6 columns, got " + std::to_string(columnCount));
73 }
74
75 // Finalize instances
76 for (auto& [id, instance] : instances) {
79 instance.instantiation = instance.data.at(attributes[instance.process][Keyword::Timestamp]);
80
81 if (earliestInstantiation > instance.instantiation) {
82 earliestInstantiation = instance.instantiation;
83 }
84 if (latestInstantiation < instance.instantiation) {
85 latestInstantiation = instance.instantiation;
86 }
87 }
88}
89
91 // Column indices: INSTANCE_ID(0), NODE_ID(1), INITIALIZATION(2), [DISCLOSURE(3)], [ARRIVAL(4), COMPLETION(5)]
92 // For 3-column format, only first 3 columns exist
93 enum { INSTANCE_ID, NODE_ID, INITIALIZATION, DISCLOSURE, ARRIVAL, COMPLETION };
94
95 for (auto& row : table | std::views::drop(1)) {
96 if (row.empty()) continue;
97 if (row.size() < 3 || row.size() != columnCount) {
98 throw std::runtime_error("ExpectedValueDataProvider: inconsistent column count");
99 }
100
101 // Get instance ID
102 if (!std::holds_alternative<std::string>(row.at(INSTANCE_ID))) {
103 throw std::runtime_error("ExpectedValueDataProvider: illegal instance id");
104 }
105 std::string instanceIdentifier = std::get<std::string>(row.at(INSTANCE_ID));
106
107 // Get node ID
108 if (!std::holds_alternative<std::string>(row.at(NODE_ID))) {
109 throw std::runtime_error("ExpectedValueDataProvider: illegal node id");
110 }
111 std::string nodeId = std::get<std::string>(row.at(NODE_ID));
112
113 // Get initialization
114 if (!std::holds_alternative<std::string>(row.at(INITIALIZATION))) {
115 throw std::runtime_error("ExpectedValueDataProvider: illegal initialization");
116 }
117 std::string initialization = std::get<std::string>(row.at(INITIALIZATION));
118
119 // DISCLOSURE column is ignored (index 3) - all data disclosed at time 0
120 // ARRIVAL column is ignored (index 4) - not applicable for expected value computation
121 // COMPLETION column is ignored (index 5) - operators can be used to compute expected values
122
123 if (instanceIdentifier.empty() && nodeId.empty()) {
124 // Global attribute
125 if (initialization.empty()) continue;
126 auto [attributeName, expressionString] = parseInitialization(initialization);
127
128 const Attribute* attribute = nullptr;
129 for (auto& [id, globalAttribute] : attributes[nullptr]) {
130 if (globalAttribute->name == attributeName) {
131 attribute = globalAttribute;
132 break;
133 }
134 }
135 if (!attribute) {
136 throw std::runtime_error("ExpectedValueDataProvider: unknown global attribute '" + attributeName + "'");
137 }
138
139 Values globals(model->attributes.size());
140 for (auto& [globalAttribute, value] : globalValueMap) {
141 globals[globalAttribute->index] = value;
142 }
143 Expression expression(expectedValueHandle, expressionString, model->attributeRegistry);
144 auto value = expression.execute(Values{}, Values{}, globals);
145 if (!value.has_value()) {
146 throw std::runtime_error("ExpectedValueDataProvider: failed to evaluate global '" + attributeName + "'");
147 }
148 globalValueMap[attribute] = value.value();
149 }
150 else if (instanceIdentifier.empty()) {
151 throw std::runtime_error("ExpectedValueDataProvider: instance id required when node id is provided");
152 }
153 else {
154 auto instanceId = (size_t)BPMNOS::to_number(instanceIdentifier, STRING);
155 BPMN::Node* node = findNode(nodeId);
156
157 // First occurrence must be a process
158 if (!instances.contains(instanceId)) {
159 if (!node->represents<BPMN::Process>()) {
160 throw std::runtime_error("ExpectedValueDataProvider: first row for instance must reference a process");
161 }
162 auto process = dynamic_cast<BPMN::Process*>(node);
163 instances[instanceId] = StaticInstanceData({process, instanceId, std::numeric_limits<BPMNOS::number>::max(), {}});
164 }
165
166 auto& instance = instances[instanceId];
167
168 // Process initialization
169 if (!initialization.empty()) {
170 auto [attributeName, expressionString] = parseInitialization(initialization);
171 auto extensionElements = node->extensionElements->as<BPMNOS::Model::ExtensionElements>();
172 if (!extensionElements->attributeRegistry.contains(attributeName)) {
173 throw std::runtime_error("ExpectedValueDataProvider: node '" + nodeId + "' has no attribute '" + attributeName + "'");
174 }
175 auto attribute = extensionElements->attributeRegistry[attributeName];
176 if (attribute->expression) {
177 throw std::runtime_error("ExpectedValueDataProvider: attribute '" + attributeName + "' is initialized by expression");
178 }
179 instance.data[attribute] = evaluateExpression(expressionString);
180 }
181 }
182 }
183}
184
185std::unique_ptr<Scenario> ExpectedValueDataProvider::createScenario([[maybe_unused]] unsigned int scenarioId) {
186 // ExpectedValueScenario is identical to StaticScenario - just returns pre-computed values
187 auto scenario = std::make_unique<ExpectedValueScenario>(model.get(), earliestInstantiation, latestInstantiation, globalValueMap);
188
189 // Add instances
190 for (auto& [id, instance] : instances) {
191 auto& timestampAttribute = attributes[instance.process][Keyword::Timestamp];
192 auto instantiationTime = instance.data[timestampAttribute];
193 scenario->addInstance(instance.process, id, instantiationTime);
194 for (auto& [attribute, value] : instance.data) {
195 scenario->setValue(id, attribute, value);
196 }
197 }
198
199 return scenario;
200}
std::vector< Row > Table
Definition CSVReader.h:17
void registerFunctions(LIMEX::Handle< double > &handle)
Register all expected value functions with the given LIMEX handle.
DataInput attributes
Map holding all attributes in the model with keys being the process (or nullptr for global attributes...
BPMN::Node * findNode(const std::string &nodeId) const
std::unique_ptr< Model > model
Pointer to the BPMN model.
Data provider that accepts stochastic CSV format but uses expected values.
ExpectedValueDataProvider(const std::string &modelFile, const std::string &instanceFileOrString)
BPMNOS::number evaluateExpression(const std::string &expression) const override
Override to use expectedValueHandle instead of model->limexHandle.
std::unique_ptr< Scenario > createScenario(unsigned int scenarioId=0) override
void readInstancesExtendedFormat(const CSVReader::Table &table, size_t columnCount)
Class representing a mathematical expression.
Definition Expression.h:17
std::vector< const Attribute * > variables
Vector containing all input attributes used by the expression.
Definition Expression.h:37
std::optional< BPMNOS::number > execute(const BPMNOS::Values &status, const DataType &data, const BPMNOS::Values &globals) const
Class holding extension elements representing execution data for nodes.
AttributeRegistry attributeRegistry
Registry allowing to look up all status and data attributes by their names.
Class representing a data provider for static BPMN instance data.
std::pair< std::string, std::string > parseInitialization(const std::string &initialization) const
std::unordered_map< const Attribute *, BPMNOS::number > globalValueMap
std::unique_ptr< CSVReader > reader
std::unordered_map< long unsigned int, StaticInstanceData > instances
void ensureDefaultValue(StaticInstanceData &instance, const std::string attributeId, std::optional< BPMNOS::number > value=std::nullopt)
std::unique_ptr< ExtensionElements > extensionElements
Definition bpmn++.h:16299
T * represents()
Attempts to cast the element to the specified type T.
Definition bpmn++.h:16236
Base class for all nodes in a BPMN model.
Definition bpmn++.h:16444
const std::string Instance
Definition Keywords.h:13
const std::string Timestamp
Definition Keywords.h:12
number to_number(const std::string &valueString, const ValueType &type)
Converts a string to a number.
Definition Number.cpp:59
BPMNOS_NUMBER_TYPE number
Definition Number.h:50
@ STRING
Definition Value.h:9