BPMN-OS
BPMN for optimization and simulation
Loading...
Searching...
No Matches
CPSolutionObserver.cpp
Go to the documentation of this file.
1#ifdef USE_CP
2
10#include <iostream>
11
12using namespace BPMNOS::Execution;
13
14CPSolutionObserver::CPSolutionObserver(const CPModel* cp)
15 : cp(cp)
16 , flattenedGraph( cp->flattenedGraph )
17 , _solution( cp->getModel() )
18 , lastPosition(0)
19{
20 // add evaluators for lookup tables
21 for ( auto& lookupTable : cp->scenario->model->lookupTables ) {
22 _solution.addEvaluator(
23 lookupTable->name,
24 [&lookupTable](const std::vector<double>& operands) -> double {
25 return lookupTable->at(operands);
26 }
27 );
28 }
29
30 std::vector<double> positions;
31 positions.resize( flattenedGraph->vertices.size() );
32 std::iota(positions.begin(), positions.end(), 1);
33 initializePositions(positions);
34}
35
36void CPSolutionObserver::subscribe(Engine* engine) {
37 lastPosition = 0;
38 engine->addSubscriber(this,
39 Execution::Observable::Type::Event,
40 Execution::Observable::Type::Token
41 );
42}
43
44void CPSolutionObserver::unsubscribe(Engine* engine) {
45 engine->removeSubscriber(this,
46 Execution::Observable::Type::Event,
47 Execution::Observable::Type::Token
48 );
49}
50
51void CPSolutionObserver::notice(const Observable* observable) {
52 if ( observable->getObservableType() == Execution::Observable::Type::Token ) {
53 synchronize( static_cast<const Token*>(observable) );
54 }
55 else {
56 assert( observable->getObservableType() == Execution::Observable::Type::Event );
57 synchronize( static_cast<const Event*>(observable) );
58 }
59}
60
61void CPSolutionObserver::synchronize(const Token* token) {
62//std::cerr << "Token: " << token->jsonify() << std::endl;
63 if (
64 token->state == Token::State::CREATED ||
65 token->state == Token::State::ARRIVED ||
66 token->state == Token::State::READY ||
67 token->state == Token::State::BUSY ||
68 token->state == Token::State::DEPARTED ||
69 token->state == Token::State::WAITING ||
70 token->state == Token::State::FAILING ||
71 token->state == Token::State::FAILED
72 ) {
73 return;
74 }
75
76 auto vertex = flattenedGraph->getVertex(token);
77 if ( !vertex ) {
78 // a token may enter the start event of an event-subprocess that cannot be triggered
79 // such a token will be withdrawn and no vertex exists
80 return;
81 }
82
83//std::cerr << lastPosition << " - Synchronise: " << token->jsonify() << std::endl;
84 if ( token->state == Token::State::ENTERED ) {
85 if (
86 vertex->node->represents<BPMN::TypedStartEvent>() ||
87 ( vertex->node->represents<BPMN::CatchEvent>() && vertex->inflows.front().second->node->represents<BPMN::EventBasedGateway>() )
88 ) {
89 // vertex enters a node that is either a typed start event or one alternative following an event-based gateway
90 // typed start event and alternatives following an event-based gateway are considered when triggered
91 return;
92 }
93
94 // entry at node
95
96 // set position
97 finalizePosition( vertex );
98
99 // set visit, status, data, globals
100 _solution.setVariableValue( cp->visit.at(vertex), true );
101 synchronizeStatus(token->status,vertex);
102 synchronizeData(*token->data,vertex);
103 synchronizeGlobals(token->globals,vertex);
104
105 if (
106 vertex->entry<BPMN::UntypedStartEvent>() ||
107 vertex->entry<BPMN::Gateway>() ||
108 ( vertex->entry<BPMN::ThrowEvent>() && !vertex->entry<BPMN::SendTask>() )
109 ) {
110 // vertex is instantaneous
111//std::cerr << "clear instantaneous exit: " << token->jsonify() << std::endl;
112 finalizePosition(exit(vertex));
113 // finalize unvisited subsequent positions
114 finalizeUnvisitedSubsequentPositions(exit(vertex));
115 }
116 }
117 else if ( token->node && token->state == Token::State::COMPLETED && token->node->represents<BPMN::TypedStartEvent>() ) {
118 // set position
119 finalizePosition( entry(vertex) );
120 finalizePosition( vertex );
121
122 // event-subprocess is triggered
123 _solution.setVariableValue( cp->visit.at(vertex), true );
124 // for typed start events, operators of event-subprocess are applied before completion
125 synchronizeData(*token->data,vertex);
126 synchronizeGlobals(token->globals,vertex);
127
128 finalizeUnvisitedSubsequentPositions(vertex);
129 }
130 else if ( token->node && token->state == Token::State::WITHDRAWN && token->node->represents<BPMN::TypedStartEvent>() ) {
131 // event-subprocess is not triggered
132 setPosition( entry(vertex), ++lastPosition );
133 unvisitEntry( entry(vertex) );
134 setPosition( vertex, ++lastPosition );
135 unvisitExit( vertex );
136 finalizeUnvisitedSubsequentPositions(vertex);
137// _solution.setVariableValue( cp->visit.at(vertex), false );
138 }
139 else if (
140 ( !token->node && token->state == Token::State::DONE ) || // Process
141 ( token->node && token->state == Token::State::EXITING && !token->node->represents<BPMN::TypedStartEvent>() ) || // Activity
142 ( token->node && token->state == Token::State::COMPLETED && token->node->represents<BPMN::CatchEvent>() && !token->node->represents<BPMN::ReceiveTask>() )
143 ) {
144
145 auto entryVertex = entry(vertex);
146 if (
147 entryVertex->inflows.size() == 1 &&
148 entryVertex->inflows.front().second->node->represents<BPMN::EventBasedGateway>()
149 ) {
150 assert( vertex->exit<BPMN::CatchEvent>() );
151 auto gateway = entryVertex->inflows.front().second;
152 setTriggeredEvent( gateway, entryVertex );
153 finalizePosition( entryVertex );
154 finalizePosition( vertex );
155
156 // exit of node
157 _solution.setVariableValue( cp->visit.at(vertex), true );
158
159 synchronizeStatus(token->status,vertex);
160 synchronizeData(*token->data,vertex);
161 synchronizeGlobals(token->globals,vertex);
162
163 finalizeUnvisitedSubsequentPositions(gateway);
164 }
165 else {
166 // set position
167 finalizePosition( vertex );
168
169 synchronizeStatus(token->status,vertex);
170 synchronizeData(*token->data,vertex);
171 synchronizeGlobals(token->globals,vertex);
172
173 finalizeUnvisitedSubsequentPositions(vertex);
174 }
175 }
176//std::cerr << "#Token" << std::endl;
177}
178
179void CPSolutionObserver::synchronize(const Event* event) {
180//std::cerr << "Event: " << event->jsonify() << std::endl;
181 if ( dynamic_cast<const EntryEvent*>(event) ) {
182//std::cerr << "Entry: " << event->jsonify() << std::endl;
183 auto vertex = flattenedGraph->getVertex( event->token );
184 if ( !flattenedGraph->dummies.contains(vertex) ) {
185 auto timestamp = event->token->owner->systemState->getTime();
186 setTimestamp(vertex, timestamp );
187 }
188 }
189 else if ( dynamic_cast<const ExitEvent*>(event) ) {
190//std::cerr << "Exit: " << event->jsonify() << std::endl;
191 auto vertex = flattenedGraph->getVertex( event->token );
192 if ( !flattenedGraph->dummies.contains(vertex) ) {
193 auto timestamp = event->token->owner->systemState->getTime();
194 setTimestamp(vertex, timestamp );
195 }
196 }
197 else if ( auto messageDeliveryEvent = dynamic_cast<const MessageDeliveryEvent*>(event) ) {
198//std::cerr << "Message delivery: " << event->jsonify() << std::endl;
199 auto vertex = flattenedGraph->getVertex( event->token );
200 // set timestamp of message delivery
201 auto timestamp = event->token->owner->systemState->getTime();
202 if ( vertex->node->represents<BPMN::ReceiveTask>() ) {
203 setLocalStatusValue(vertex,BPMNOS::Model::ExtensionElements::Index::Timestamp,timestamp);
204 }
205 else {
206 setTimestamp(vertex,timestamp);
207 }
208 auto message = messageDeliveryEvent->message.lock();
209 assert( message );
210 auto senderId = message->header[ BPMNOS::Model::MessageDefinition::Index::Sender ].value();
211 assert( flattenedGraph->vertexMap.contains({senderId,{},message->origin}) );
212 // TODO: sender must not be within loop
213 auto& [senderEntry,senderExit] = flattenedGraph->vertexMap.at({senderId,{},message->origin});
214 setMessageDeliveryVariableValues(senderEntry,vertex,timestamp);
215 }
216 else if ( auto choiceEvent = dynamic_cast<const ChoiceEvent*>(event) ) {
217//std::cerr << "Choice: " << event->jsonify() << std::endl;
218 auto vertex = flattenedGraph->getVertex( event->token );
219 // set timestamp of choice
220 auto timestamp = event->token->owner->systemState->getTime();
221 setLocalStatusValue(vertex,BPMNOS::Model::ExtensionElements::Index::Timestamp,timestamp);
222 // apply choices
223 auto extensionElements = vertex->node->extensionElements->as<BPMNOS::Model::ExtensionElements>();
224 assert( extensionElements );
225 assert( extensionElements->choices.size() == choiceEvent->choices.size() );
226 for (size_t i = 0; i < extensionElements->choices.size(); i++) {
227 auto choice = extensionElements->choices[i].get();
228 auto choiceValue = choiceEvent->choices[i];
229 setLocalStatusValue(vertex, choice->attribute->index, choiceValue);
230 // set discretizer if multipleOf constraint exists
231 if ( choice->multipleOf && cp->discretizerMap.contains({vertex, choice->attribute}) ) {
232 auto multipleOf = choice->multipleOf->execute(event->token->status, *event->token->data, event->token->globals);
233 if ( !multipleOf.has_value() ) {
234 throw std::logic_error("CPSolutionObserver: Unable to evaluate multipleOf for choice '" + choice->attribute->id + "'");
235 }
236 _solution.setVariableValue( cp->discretizerMap.at({vertex, choice->attribute}), (double)(choiceValue / multipleOf.value()) );
237 }
238 }
239 }
240//std::cerr << "#Event" << std::endl;
241}
242
243
244
245void CPSolutionObserver::setMessageDeliveryVariableValues( const Vertex* sender, const Vertex* recipient, BPMNOS::number timestamp ) {
246//std::cerr << "Sen:" << sender->jsonify() << std::endl;
247//std::cerr << "Rec:" << recipient->jsonify() << std::endl;
248 for ( auto candidate : sender->recipients ) {
249 assert( cp->messageFlow.contains({sender,candidate}) );
250//std::cerr << "message_{" << sender->reference() << " -> " << candidate.reference() << "} := " << (&candidate == recipient) << std::endl;
251 _solution.setVariableValue( cp->messageFlow.at({sender,candidate}), (double)(candidate == recipient) );
252 }
253 for ( auto candidate : recipient->senders ) {
254 assert( cp->messageFlow.contains({candidate,recipient}) );
255 _solution.setVariableValue( cp->messageFlow.at({candidate,recipient}), (double)(candidate == sender) );
256//std::cerr << "message_{" << candidate.reference() << " -> " << recipient->reference() << "} := " << (&candidate == sender) << std::endl;
257 }
258 // set message delivery time
259 if ( recipient->node->represents<BPMN::ReceiveTask>() || recipient->node->represents<BPMN::MessageStartEvent>() ) {
260 setLocalStatusValue( recipient, BPMNOS::Model::ExtensionElements::Index::Timestamp, (double)timestamp );
261 }
262 else {
263 _solution.setVariableValue( cp->status.at(recipient)[BPMNOS::Model::ExtensionElements::Index::Timestamp].value, (double)timestamp );
264 }
265
266 // set recipient content variables
267 auto& recipientContentMap = cp->messageContent.at(recipient);
268 auto& senderContentMap = cp->messageContent.at(sender);
269 for ( auto& [key, recipientContent ] : recipientContentMap ) {
270 assert( senderContentMap.contains(key) );
271 auto& senderContent = senderContentMap.at(key);
272 CPModel::AttributeEvaluation evaluation(
273 _solution.evaluate( senderContent.defined ),
274 _solution.evaluate( senderContent.value )
275 );
276 if ( evaluation ) {
277 _solution.setVariableValue( recipientContent.defined, evaluation.defined() );
278 _solution.setVariableValue( recipientContent.value, evaluation.value() );
279 }
280 }
281}
282
283std::optional< BPMN::Activity::LoopCharacteristics> CPSolutionObserver::getLoopCharacteristics(const Vertex* vertex) const {
284 auto activity = vertex->node->represents<BPMN::Activity>();
285 if ( !activity ) {
286 return std::nullopt;
287 }
288 return activity->loopCharacteristics;
289}
290
291void CPSolutionObserver::unvisitEntry(const Vertex* vertex) {
292//std::cerr << "unvisited " << vertex->reference() << std::endl;
293// _solution.setVariableValue( cp->position.at(vertex), (double)position );
294 assert( cp->visit.contains(vertex) );
295 _solution.setVariableValue( cp->visit.at(vertex), false);
296 assert( cp->status.contains(vertex) );
297 _solution.setVariableValue( cp->status.at(vertex)[BPMNOS::Model::ExtensionElements::Index::Timestamp].value, 0.0 );
298
299 if ( vertex->node->represents<BPMN::MessageThrowEvent>() ) {
300 for ( auto candidate : vertex->recipients ) {
301 assert( candidate->exit<BPMN::MessageCatchEvent>() );
302 assert( cp->messageFlow.contains({vertex,candidate}) );
303 _solution.setVariableValue( cp->messageFlow.at({vertex,candidate}), (double)false );
304 }
305 }
306
307 if (
308 vertex->node->represents<BPMN::Task>() &&
309 !(vertex->node->represents<BPMN::TypedStartEvent>() || vertex->node->represents<BPMN::ReceiveTask>())
310 ) {
311 // if visited, operators may modify data and globals upon entry
312 // if unvisited, data and globals are not changed
313 for ( size_t i = 0; i < vertex->dataOwners.size(); i++ ) {
314 auto dataOwner = vertex->dataOwners[i];
315 if ( flattenedGraph->modifiesData(vertex,dataOwner) ) {
316 auto index = (size_t)_solution.evaluate( cp->dataIndex.at(vertex)[i] ).value();
317
318 auto extensionElements = dataOwner->node->extensionElements->as<BPMNOS::Model::ExtensionElements>();
319 assert( extensionElements );
320 for ( auto& attribute : extensionElements->data ) {
321 auto defined = _solution.evaluate( cp->data.at({dataOwner,attribute.get()}).defined[index] ).value();
322 auto value = _solution.evaluate( cp->data.at({dataOwner,attribute.get()}).value[index] ).value();
323 _solution.setVariableValue( cp->data.at({dataOwner,attribute.get()}).defined[index+1], defined );
324 _solution.setVariableValue( cp->data.at({dataOwner,attribute.get()}).value[index+1], value );
325 }
326 }
327 }
328 if ( flattenedGraph->modifiesGlobals(vertex) ) {
329 auto index = (size_t)_solution.evaluate( cp->globalIndex.at(vertex) ).value();
330 for ( size_t attributeIndex = 0; attributeIndex < cp->globals.size(); attributeIndex++ ) {
331 auto defined = _solution.evaluate( cp->globals.at(attributeIndex).defined[index] ).value();
332 auto value = _solution.evaluate( cp->globals.at(attributeIndex).value[index] ).value();
333 _solution.setVariableValue( cp->globals.at(attributeIndex).defined[index+1], defined );
334 _solution.setVariableValue( cp->globals.at(attributeIndex).value[index+1], value );
335 }
336 }
337 }
338}
339
340void CPSolutionObserver::unvisitExit(const Vertex* vertex) {
341//std::cerr << "unvisited " << vertex->reference() << std::endl;
342 assert( cp->visit.contains(vertex) );
343// _solution.setVariableValue( cp->position.at(vertex), (double)position );
344 _solution.setVariableValue( cp->visit.at(vertex), false);
345 assert( cp->status.contains(vertex) );
346 _solution.setVariableValue( cp->status.at(vertex)[BPMNOS::Model::ExtensionElements::Index::Timestamp].value, 0.0 );
347
348 if ( vertex->node->represents<BPMN::MessageCatchEvent>() ) {
349 for ( auto candidate : vertex->senders ) {
350 assert( candidate->entry<BPMN::MessageThrowEvent>() );
351 assert( cp->messageFlow.contains({candidate,vertex}) );
352 _solution.setVariableValue( cp->messageFlow.at({candidate,vertex}), (double)false );
353 }
354
355 if ( cp->locals.contains(vertex) ) {
356 // only receive tasks and message start events have locals
357 setLocalStatusValue( vertex, BPMNOS::Model::ExtensionElements::Index::Timestamp, 0.0 );
358 }
359
360 assert( cp->messageContent.contains(vertex) );
361 for ( auto& [_, contentVariables] : cp->messageContent.at(vertex) ) {
362 _solution.setVariableValue( contentVariables.defined, (double)false );
363 _solution.setVariableValue( contentVariables.value, 0.0 );
364 }
365 }
366
367 if ( vertex->node->represents<BPMNOS::Model::DecisionTask>() ) {
368 // instant choices: timestamp is deduced from entry, only reset choice values and discretizers
369 auto extensionElements = vertex->node->extensionElements->as<BPMNOS::Model::ExtensionElements>();
370 for ( auto& choice : extensionElements->choices ) {
371 // reset choice value
372 setLocalStatusValue( vertex, choice->attribute->index, 0.0 );
373 // reset discretizer if multipleOf constraint exists
374 if ( choice->multipleOf && cp->discretizerMap.contains({vertex, choice->attribute}) ) {
375 _solution.setVariableValue( cp->discretizerMap.at({vertex, choice->attribute}), 0.0 );
376 }
377 }
378 }
379
380 if ( vertex->node->represents<BPMN::TypedStartEvent>() || vertex->node->represents<BPMN::ReceiveTask>() ) {
381 // if visited, operators may modify data and globals upon exit
382 // if unvisited, data and globals are not changed
383 for ( size_t i = 0; i < vertex->dataOwners.size(); i++ ) {
384 auto dataOwner = vertex->dataOwners[i];
385 if ( flattenedGraph->modifiesData(vertex,dataOwner) ) {
386 auto index = (size_t)_solution.evaluate( cp->dataIndex.at(exit(vertex))[i] ).value();
387
388 auto extensionElements = dataOwner->node->extensionElements->as<BPMNOS::Model::ExtensionElements>();
389 assert( extensionElements );
390 for ( auto& attribute : extensionElements->data ) {
391 auto defined = _solution.evaluate( cp->data.at({dataOwner,attribute.get()}).defined[index-1] ).value();
392 auto value = _solution.evaluate( cp->data.at({dataOwner,attribute.get()}).value[index-1] ).value();
393 _solution.setVariableValue( cp->data.at({dataOwner,attribute.get()}).defined[index], defined );
394 _solution.setVariableValue( cp->data.at({dataOwner,attribute.get()}).value[index], value );
395 }
396 }
397 }
398 if ( flattenedGraph->modifiesGlobals(vertex) ) {
399 auto index = (size_t)_solution.evaluate( cp->globalIndex.at(exit(vertex)) ).value();
400 for ( size_t attributeIndex = 0; attributeIndex < cp->globals.size(); attributeIndex++ ) {
401 auto defined = _solution.evaluate( cp->globals.at(attributeIndex).defined[index-1] ).value();
402 auto value = _solution.evaluate( cp->globals.at(attributeIndex).value[index-1] ).value();
403 _solution.setVariableValue( cp->globals.at(attributeIndex).defined[index], defined );
404 _solution.setVariableValue( cp->globals.at(attributeIndex).value[index], value );
405 }
406 }
407 }
408
409 if ( vertex->node->represents<BPMN::TypedStartEvent>() ) {
410 // unvisit subsequent typed start events
411 for ( auto successor : vertex->successors ) {
412 if ( successor->node == vertex->node ) {
413 setPosition( successor, ++lastPosition );
414 unvisitEntry(successor);
415 setPosition( exit(successor), ++lastPosition );
416 unvisitExit(exit(successor));
417 finalizeUnvisitedSubsequentPositions(exit(successor));
418 break;
419 }
420 }
421 }
422}
423
424void CPSolutionObserver::synchronizeStatus(const BPMNOS::Values& status, const CPSolutionObserver::Vertex* vertex) {
425 assert( cp->status.contains(vertex) );
426 auto& statusVariables = cp->status.at(vertex);
427 assert( status.size() == statusVariables.size() );
428 for (size_t i = 0; i < statusVariables.size(); i++) {
429 CPModel::AttributeEvaluation evaluation(
430 _solution.evaluate( statusVariables[i].defined ),
431 _solution.evaluate( statusVariables[i].value )
432 );
433 if ( status[i].has_value() ) {
434 if ( !evaluation ) {
435 // set solution value
436 _solution.setVariableValue( statusVariables[i].defined, true );
437 _solution.setVariableValue( statusVariables[i].value, (double)status[i].value() );
438 }
439 else if (
440 !evaluation.defined() ||
441 evaluation.value() != status[i].value()
442 ) {
443
444std::cerr << "status[" << i << "].defined: " << (evaluation.defined() ? "true" : "false") << ", value: " << evaluation.value() << std::endl;
445std::cerr << statusVariables[i].defined.stringify() << std::endl;
446std::cerr << statusVariables[i].value.stringify() << std::endl;
447//std::cerr << "Model: " << cp->stringify() << std::endl;
448//std::cerr << "Solution: " << _solution.stringify() << std::endl;
449 throw std::logic_error("CPSolutionObserver: inconsistent status '" + _solution.stringify(statusVariables[i].defined) + "' (observed as 'true') or '" + _solution.stringify(statusVariables[i].value) + "' (observed as '" + std::to_string((double)status[i].value_or(0)) + "')" );
450 }
451 }
452 else {
453 if ( !evaluation ) {
454 // set solution value
455 _solution.setVariableValue( statusVariables[i].defined, false );
456 _solution.setVariableValue( statusVariables[i].value, 0.0 );
457 }
458 else if (
459 evaluation.defined() ||
460 evaluation.value() != 0.0
461 ) {
462//std::cerr << "defined: " << (evaluation.defined() ? "true" : "false") << ", value: " << evaluation.value() << std::endl;
463 throw std::logic_error("CPSolutionObserver: inconsistent status '" + _solution.stringify(statusVariables[i].defined) + "' (observed as 'false') or '" + _solution.stringify(statusVariables[i].value) + "' (observed as '0.0')" );
464 }
465 }
466 }
467}
468
469void CPSolutionObserver::synchronizeData(const BPMNOS::SharedValues& data, const CPSolutionObserver::Vertex* vertex) {
470 assert( cp->dataIndex.contains(vertex) );
471 auto& dataIndices = cp->dataIndex.at(vertex);
472 assert( dataIndices.size() == vertex->dataOwners.size() );
473 // iterate over the data indices for each data owner
474 for ( size_t i = 0; i < dataIndices.size(); i++ ) {
475 auto indexEvaluation = _solution.evaluate( dataIndices[i] );
476 if ( !indexEvaluation ) {
477std::cerr << dataIndices[i].stringify() << std::endl;
478 throw std::logic_error("CPSolutionObserver: Unable to determine data index for '" + vertex->reference() + "\n'" + indexEvaluation.error());
479 }
480 auto index = (size_t)indexEvaluation.value();
481 auto ownerVertex = vertex->dataOwners[i];
482 assert( ownerVertex->entry<BPMN::Scope>() );
483//std::cerr << "set data[" << ownerVertex.shortReference() << ", "<< index << "] for " << vertex->reference() << std::endl;
484 auto scope = ownerVertex->node;
485 auto extensionElements = scope->extensionElements->as<BPMNOS::Model::ExtensionElements>();
486 for ( auto& attribute : extensionElements->data ) {
487 if ( attribute->index == BPMNOS::Model::ExtensionElements::Index::Instance ) {
488 continue;
489 }
490 auto& indexedAttributeVariables = cp->data.at({ownerVertex,attribute.get()});
491 // override solution value (positions and indices may have changed)
492 if ( data[attribute->index].get().has_value() ) {
493 _solution.setVariableValue( indexedAttributeVariables.defined[index], true );
494 _solution.setVariableValue( indexedAttributeVariables.value[index], (double)data[attribute->index].get().value() );
495 }
496 else {
497 _solution.setVariableValue( indexedAttributeVariables.defined[index], false );
498 _solution.setVariableValue( indexedAttributeVariables.value[index], 0.0 );
499 }
500 }
501 }
502}
503
504void CPSolutionObserver::synchronizeGlobals(const BPMNOS::Values& globals, const CPSolutionObserver::Vertex* vertex) {
505 assert( cp->globalIndex.contains(vertex) );
506 auto indexEvaluation = _solution.evaluate( cp->globalIndex.at(vertex) );
507 if ( !indexEvaluation ) {
508 throw std::logic_error("CPSolutionObserver: Unable to determine global index for '" + vertex->reference() + "\n'" + indexEvaluation.error());
509 }
510 auto index = (size_t)indexEvaluation.value();
511 for ( size_t attributeIndex = 0; attributeIndex < globals.size(); attributeIndex++ ) {
512 auto& indexedAttributeVariables = cp->globals[attributeIndex];
513 // override solution value (positions and indices may have changed)
514 if ( globals[attributeIndex].has_value() ) {
515 _solution.setVariableValue( indexedAttributeVariables.defined[index], true );
516 _solution.setVariableValue( indexedAttributeVariables.value[index], (double)globals[attributeIndex].value() );
517 }
518 else {
519 _solution.setVariableValue( indexedAttributeVariables.defined[index], false );
520 _solution.setVariableValue( indexedAttributeVariables.value[index], 0.0 );
521 }
522 }
523}
524
525/*
526const CP::Solution& CPSolutionObserver::getSolution() const {
527 return _solution;
528}
529*/
530
531std::vector<size_t> CPSolutionObserver::getSequence() const {
532 std::vector<size_t> sequence(flattenedGraph->vertices.size());
533 for ( size_t i = 0; i < flattenedGraph->vertices.size(); i++ ) {
534 sequence[ (size_t)_solution.getVariableValue( cp->position.at( flattenedGraph->vertices[i].get() ) ).value() -1 ] = i + 1;
535 }
536
537 return sequence;
538}
539
540size_t CPSolutionObserver::getPosition(const Vertex* vertex) const {
541 assert( cp->position.contains( vertex ) );
542 return (size_t)_solution.getVariableValue( cp->position.at( vertex ) ).value_or(0);
543}
544
545void CPSolutionObserver::initializePositions(const std::vector<double>& positions) {
546 assert( positions.size() == flattenedGraph->vertices.size() );
547 assert( cp->getModel().getSequences().size() == 1 );
548 auto& sequenceVariable = cp->getModel().getSequences().front();
549 _solution.setSequenceValues( sequenceVariable, positions );
550}
551
552void CPSolutionObserver::setPosition(const Vertex* vertex, size_t position) {
553//std::cerr << "position(" << cp->indexMap.at(vertex) << ":" << vertex->reference() << ") = " << position << " / " << _solution.evaluate(cp->visit.at(vertex)).value_or(-1) << std::endl;
554 assert( cp->position.contains( vertex ) );
555 assert( _solution.getVariableValue( cp->position.at(vertex) ).has_value() );
556 auto priorPosition = (size_t)_solution.getVariableValue( cp->position.at(vertex) ).value();
557 assert( position <= priorPosition );
558 if ( position < priorPosition ) {
559 for ( auto& other : flattenedGraph->vertices ) {
560 if ( other.get() == vertex ) {
561 continue;
562 }
563 assert( _solution.getVariableValue( cp->position.at(other.get()) ).has_value() );
564 auto otherPosition = (size_t)_solution.getVariableValue( cp->position.at(other.get()) ).value();
565 if ( otherPosition >= position && otherPosition < priorPosition ) {
566 // increment position of other vertex
567 _solution.setVariableValue( cp->position.at(other.get()), (double)++otherPosition );
568 }
569 }
570 }
571 // change vertex position
572 _solution.setVariableValue( cp->position.at(vertex), (double)position );
573//std::cerr << "changed position(" << vertex->reference() << ") from " << priorPosition << " to " << position << std::endl;
574}
575
576void CPSolutionObserver::finalizePosition(const Vertex* vertex) {
577//std::cerr << "finalizePosition " << vertex->reference() << std::endl;
578 if ( vertex->type == Vertex::Type::ENTRY && getLoopCharacteristics(vertex) ) {
579 // finalize position of dummy for loop or multi-instance activity
580 for ( auto [_,other] : vertex->inflows ) {
581 if (
582 other->node == vertex->node &&
583 flattenedGraph->dummies.contains(other) &&
584 _solution.getVariableValue( cp->position.at(other) ).value() > (double)lastPosition
585 ) {
586 // finalize position of dummy entry
587 setPosition(other, ++lastPosition);
588 break;
589 }
590 }
591 }
592
593 setPosition(vertex, ++lastPosition);
594
595 if ( vertex->type == Vertex::Type::EXIT && getLoopCharacteristics(vertex) ) {
596 // finalize position of dummy for loop or multi-instance activity
597 for ( auto [_1,other] : vertex->outflows ) {
598 if (
599 other->node == vertex->node &&
600 flattenedGraph->dummies.contains(other)
601 ) {
602 bool isLast = true;
603 for ( auto [_2,sibling] : other->inflows ) {
604 if ( _solution.getVariableValue( cp->position.at(sibling) ).value() > (double)lastPosition ) {
605 isLast = false;
606 break;
607 }
608 }
609 if ( isLast ) {
610 // finalize position of dummy exit
611 setPosition(other, ++lastPosition);
612 break;
613 }
614 }
615 }
616 }
617}
618
619void CPSolutionObserver::finalizeUnvisitedSubsequentPositions(const Vertex* vertex) {
620 for ( auto& [_1,other] : vertex->outflows ) {
621 auto isVisited = _solution.evaluate( cp->visit.at(other) );
622 if ( isVisited.has_value() && !isVisited.value() ) {
623 // successor is not visited
624 bool canBeFinalized = true;
625 for ( auto& [_2, predecessor] : other->inflows ) {
626 if ( predecessor != vertex && !markedFlows.contains({predecessor,other}) ) {
627//std::cerr << "Unmarked flow from: " << predecessor->reference() << std::endl;
628 canBeFinalized = false;
629 break;
630 }
631 }
632
633 markedFlows.insert({vertex,other});
634//std::cerr << "Cannot finalize: " << other->reference() << std::endl;
635 if ( !canBeFinalized ) {
636 continue;
637 }
638
639 bool hasPendingPredecessors = false;
640 for ( auto predecessor : other->predecessors ) {
641 if ( _solution.getVariableValue( cp->position.at(predecessor) ).value() > (double)lastPosition ) {
642//std::cerr << "hasPendingPredecessor: " << other->reference() << std::endl;
643 hasPendingPredecessors = true;
644 break;
645 }
646 }
647
648 if ( hasPendingPredecessors ) {
649 continue;
650 }
651
652 finalizePosition(other);
653//std::cerr << "finalized: " << other->reference() << " position=" << lastPosition << " visited=false" << std::endl;
654
655 if ( other->type == Vertex::Type::ENTRY ) {
656 assert( !_solution.getVariableValue( cp->visit.at(other) ).has_value() );
657 unvisitEntry(other);
658 }
659 else {
660 assert( other->node->represents<BPMN::TypedStartEvent>() || _solution.getVariableValue( cp->visit.at(other) ).has_value() );
661 unvisitExit(other);
662 }
663 finalizeUnvisitedSubsequentPositions(other);
664 }
665 }
666}
667
668bool CPSolutionObserver::isVisited(const Vertex* vertex) const {
669 assert( cp->visit.contains( vertex ) );
670 return _solution.evaluate( cp->visit.at( vertex ) ).value_or(false);
671}
672
673bool CPSolutionObserver::isUnvisited(const Vertex* vertex) const {
674 assert( cp->visit.contains( vertex ) );
675 return !_solution.evaluate( cp->visit.at( vertex ) ).value_or(true);
676}
677
678bool CPSolutionObserver::messageFlows( const Vertex* sender, const Vertex* recipient ) {
679 assert( cp->messageFlow.contains( {sender,recipient} ) );
680 return _solution.evaluate( cp->messageFlow.at( {sender,recipient} ) ).value_or(false);
681}
682
683
684std::optional< BPMNOS::number > CPSolutionObserver::getTimestamp( const Vertex* vertex ) const {
685 assert( cp->status.contains(vertex) );
686 assert( cp->status.at(vertex).size() > BPMNOS::Model::ExtensionElements::Index::Timestamp );
687 auto timestamp = _solution.evaluate( cp->status.at(vertex)[BPMNOS::Model::ExtensionElements::Index::Timestamp].value );
688 if ( timestamp ) {
689 return (number)timestamp.value();
690 }
691 return std::nullopt;
692}
693
694void CPSolutionObserver::setTimestamp( const Vertex* vertex, BPMNOS::number timestamp ) {
695 _solution.setVariableValue( cp->status.at(vertex)[BPMNOS::Model::ExtensionElements::Index::Timestamp].value, (double)timestamp );
696}
697
698std::optional< BPMNOS::number > CPSolutionObserver::getStatusValue( const Vertex* vertex, size_t attributeIndex ) const {
699 if ( !_solution.evaluate( cp->status.at( vertex )[attributeIndex].defined ).has_value() || !_solution.evaluate( cp->status.at( vertex )[attributeIndex].defined ).value() ) {
700 return std::nullopt;
701 }
702 return _solution.evaluate( cp->status.at(vertex)[attributeIndex].value ).value();
703}
704
705void CPSolutionObserver::setTriggeredEvent( const Vertex* gateway, const Vertex* event ) {
706//std::cerr << gateway->reference() << "/" << event->reference() << std::endl;
707 assert( event->entry<BPMN::CatchEvent>() );
708 assert( gateway->exit<BPMN::EventBasedGateway>() );
709 for ( auto& [ _, target ] : gateway->outflows ) {
710 _solution.setVariableValue( cp->tokenFlow.at({gateway,target}), ( target == event ) );
711//std::cerr << "Token flow " << gateway.reference() << " to " << target.reference() << " = " << ( &target == entryVertex ) << std::endl;
712 }
713}
714
715
716void CPSolutionObserver::setLocalStatusValue( const Vertex* vertex, size_t attributeIndex, BPMNOS::number value ) {
717 auto& initialStatus = std::get<0>(cp->locals.at(vertex)[0]);
718 _solution.setVariableValue( initialStatus[attributeIndex].value, (double)value );
719}
720
721#endif // USE_CP
722
void removeSubscriber(Observer *subscriber, ObservableTypes... observableTypes)
Definition Notifier.h:22
void addSubscriber(Observer *subscriber, ObservableTypes... observableTypes)
Definition Notifier.h:17
Represents a token running through a (sub)process.
Definition Token.h:35
const BPMN::FlowNode * node
Definition Token.h:46
SharedValues * data
Pointer to the data of the owner or owned state machine subprocesses)
Definition Token.h:58
Class representing a task in which one or more choices have to be made.
Class holding extension elements representing execution data for nodes.
std::optional< LoopCharacteristics > loopCharacteristics
Definition bpmn++.h:17411
T * represents()
Attempts to cast the element to the specified type T.
Definition bpmn++.h:16236
Base class for BPMN elements that may contain a ChildNode elements.
Definition bpmn++.h:16510
Base class for all start events with an event definition.
Definition bpmn++.h:16880
BPMNOS_NUMBER_TYPE number
Definition Number.h:50
Represents the event that choices are made for a DecisionTask.
Definition ChoiceEvent.h:15
Represents the event of a token entering a node.
Definition EntryEvent.h:15
const Token * token
Definition Event.h:18
Represents the event of a token exiting a node.
Definition ExitEvent.h:15
Represents the event of a message from the message pool being delivered.
virtual constexpr Type getObservableType() const =0