14CPSolutionObserver::CPSolutionObserver(
const CPModel* cp)
16 , flattenedGraph( cp->flattenedGraph )
17 , _solution( cp->getModel() )
21 for (
auto& lookupTable : cp->scenario->model->lookupTables ) {
22 _solution.addEvaluator(
24 [&lookupTable](const std::vector<double>& operands) -> double {
25 return lookupTable->at(operands);
30 std::vector<double> positions;
31 positions.resize( flattenedGraph->vertices.size() );
32 std::iota(positions.begin(), positions.end(), 1);
33 initializePositions(positions);
36void CPSolutionObserver::subscribe(
Engine* engine) {
39 Execution::Observable::Type::Event,
40 Execution::Observable::Type::Token
44void CPSolutionObserver::unsubscribe(
Engine* engine) {
46 Execution::Observable::Type::Event,
47 Execution::Observable::Type::Token
51void CPSolutionObserver::notice(
const Observable* observable) {
53 synchronize(
static_cast<const Token*
>(observable) );
57 synchronize(
static_cast<const Event*
>(observable) );
61void CPSolutionObserver::synchronize(
const Token* token) {
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
76 auto vertex = flattenedGraph->getVertex(token);
84 if ( token->
state == Token::State::ENTERED ) {
97 finalizePosition( vertex );
100 _solution.setVariableValue( cp->visit.at(vertex),
true );
101 synchronizeStatus(token->
status,vertex);
102 synchronizeData(*token->
data,vertex);
103 synchronizeGlobals(token->
globals,vertex);
112 finalizePosition(exit(vertex));
114 finalizeUnvisitedSubsequentPositions(exit(vertex));
119 finalizePosition( entry(vertex) );
120 finalizePosition( vertex );
123 _solution.setVariableValue( cp->visit.at(vertex),
true );
125 synchronizeData(*token->
data,vertex);
126 synchronizeGlobals(token->
globals,vertex);
128 finalizeUnvisitedSubsequentPositions(vertex);
132 setPosition( entry(vertex), ++lastPosition );
133 unvisitEntry( entry(vertex) );
134 setPosition( vertex, ++lastPosition );
135 unvisitExit( vertex );
136 finalizeUnvisitedSubsequentPositions(vertex);
140 ( !token->
node && token->
state == Token::State::DONE ) ||
145 auto entryVertex = entry(vertex);
147 entryVertex->inflows.size() == 1 &&
151 auto gateway = entryVertex->inflows.front().second;
152 setTriggeredEvent( gateway, entryVertex );
153 finalizePosition( entryVertex );
154 finalizePosition( vertex );
157 _solution.setVariableValue( cp->visit.at(vertex),
true );
159 synchronizeStatus(token->
status,vertex);
160 synchronizeData(*token->
data,vertex);
161 synchronizeGlobals(token->
globals,vertex);
163 finalizeUnvisitedSubsequentPositions(gateway);
167 finalizePosition( vertex );
169 synchronizeStatus(token->
status,vertex);
170 synchronizeData(*token->
data,vertex);
171 synchronizeGlobals(token->
globals,vertex);
173 finalizeUnvisitedSubsequentPositions(vertex);
179void CPSolutionObserver::synchronize(
const Event* event) {
181 if (
dynamic_cast<const EntryEvent*
>(event) ) {
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 );
189 else if (
dynamic_cast<const ExitEvent*
>(event) ) {
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 );
199 auto vertex = flattenedGraph->getVertex( event->
token );
201 auto timestamp =
event->token->owner->systemState->getTime();
206 setTimestamp(vertex,timestamp);
208 auto message = messageDeliveryEvent->message.lock();
211 assert( flattenedGraph->vertexMap.contains({senderId,{},message->origin}) );
213 auto& [senderEntry,senderExit] = flattenedGraph->vertexMap.at({senderId,{},message->origin});
214 setMessageDeliveryVariableValues(senderEntry,vertex,timestamp);
216 else if (
auto choiceEvent =
dynamic_cast<const ChoiceEvent*
>(event) ) {
218 auto vertex = flattenedGraph->getVertex( event->
token );
220 auto timestamp =
event->token->owner->systemState->getTime();
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);
231 if ( choice->multipleOf && cp->discretizerMap.contains({vertex, choice->attribute}) ) {
233 if ( !multipleOf.has_value() ) {
234 throw std::logic_error(
"CPSolutionObserver: Unable to evaluate multipleOf for choice '" + choice->attribute->id +
"'");
236 _solution.setVariableValue( cp->discretizerMap.at({vertex, choice->attribute}), (
double)(choiceValue / multipleOf.value()) );
245void CPSolutionObserver::setMessageDeliveryVariableValues(
const Vertex* sender,
const Vertex* recipient,
BPMNOS::number timestamp ) {
248 for (
auto candidate : sender->recipients ) {
249 assert( cp->messageFlow.contains({sender,candidate}) );
251 _solution.setVariableValue( cp->messageFlow.at({sender,candidate}), (
double)(candidate == recipient) );
253 for (
auto candidate : recipient->senders ) {
254 assert( cp->messageFlow.contains({candidate,recipient}) );
255 _solution.setVariableValue( cp->messageFlow.at({candidate,recipient}), (
double)(candidate == sender) );
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 )
277 _solution.setVariableValue( recipientContent.defined, evaluation.defined() );
278 _solution.setVariableValue( recipientContent.value, evaluation.value() );
283std::optional< BPMN::Activity::LoopCharacteristics> CPSolutionObserver::getLoopCharacteristics(
const Vertex* vertex)
const {
291void CPSolutionObserver::unvisitEntry(
const Vertex* vertex) {
294 assert( cp->visit.contains(vertex) );
295 _solution.setVariableValue( cp->visit.at(vertex),
false);
296 assert( cp->status.contains(vertex) );
300 for (
auto candidate : vertex->recipients ) {
302 assert( cp->messageFlow.contains({vertex,candidate}) );
303 _solution.setVariableValue( cp->messageFlow.at({vertex,candidate}), (
double)
false );
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();
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 );
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 );
340void CPSolutionObserver::unvisitExit(
const Vertex* vertex) {
342 assert( cp->visit.contains(vertex) );
344 _solution.setVariableValue( cp->visit.at(vertex),
false);
345 assert( cp->status.contains(vertex) );
349 for (
auto candidate : vertex->senders ) {
351 assert( cp->messageFlow.contains({candidate,vertex}) );
352 _solution.setVariableValue( cp->messageFlow.at({candidate,vertex}), (
double)
false );
355 if ( cp->locals.contains(vertex) ) {
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 );
370 for (
auto& choice : extensionElements->choices ) {
372 setLocalStatusValue( vertex, choice->attribute->index, 0.0 );
374 if ( choice->multipleOf && cp->discretizerMap.contains({vertex, choice->attribute}) ) {
375 _solution.setVariableValue( cp->discretizerMap.at({vertex, choice->attribute}), 0.0 );
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();
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 );
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 );
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));
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 )
433 if ( status[i].has_value() ) {
436 _solution.setVariableValue( statusVariables[i].defined,
true );
437 _solution.setVariableValue( statusVariables[i].value, (
double)status[i].value() );
440 !evaluation.defined() ||
441 evaluation.value() != status[i].value()
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;
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)) +
"')" );
455 _solution.setVariableValue( statusVariables[i].defined,
false );
456 _solution.setVariableValue( statusVariables[i].value, 0.0 );
459 evaluation.defined() ||
460 evaluation.value() != 0.0
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')" );
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() );
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());
480 auto index = (size_t)indexEvaluation.value();
481 auto ownerVertex = vertex->dataOwners[i];
484 auto scope = ownerVertex->node;
486 for (
auto& attribute : extensionElements->data ) {
490 auto& indexedAttributeVariables = cp->data.at({ownerVertex,attribute.get()});
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() );
497 _solution.setVariableValue( indexedAttributeVariables.defined[index],
false );
498 _solution.setVariableValue( indexedAttributeVariables.value[index], 0.0 );
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());
510 auto index = (size_t)indexEvaluation.value();
511 for (
size_t attributeIndex = 0; attributeIndex < globals.size(); attributeIndex++ ) {
512 auto& indexedAttributeVariables = cp->globals[attributeIndex];
514 if ( globals[attributeIndex].has_value() ) {
515 _solution.setVariableValue( indexedAttributeVariables.defined[index],
true );
516 _solution.setVariableValue( indexedAttributeVariables.value[index], (
double)globals[attributeIndex].value() );
519 _solution.setVariableValue( indexedAttributeVariables.defined[index],
false );
520 _solution.setVariableValue( indexedAttributeVariables.value[index], 0.0 );
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;
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);
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 );
552void CPSolutionObserver::setPosition(
const Vertex* vertex,
size_t position) {
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 ) {
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 ) {
567 _solution.setVariableValue( cp->position.at(other.get()), (
double)++otherPosition );
572 _solution.setVariableValue( cp->position.at(vertex), (
double)position );
576void CPSolutionObserver::finalizePosition(
const Vertex* vertex) {
578 if ( vertex->type == Vertex::Type::ENTRY && getLoopCharacteristics(vertex) ) {
580 for (
auto [_,other] : vertex->inflows ) {
582 other->node == vertex->node &&
583 flattenedGraph->dummies.contains(other) &&
584 _solution.getVariableValue( cp->position.at(other) ).value() > (
double)lastPosition
587 setPosition(other, ++lastPosition);
593 setPosition(vertex, ++lastPosition);
595 if ( vertex->type == Vertex::Type::EXIT && getLoopCharacteristics(vertex) ) {
597 for (
auto [_1,other] : vertex->outflows ) {
599 other->node == vertex->node &&
600 flattenedGraph->dummies.contains(other)
603 for (
auto [_2,sibling] : other->inflows ) {
604 if ( _solution.getVariableValue( cp->position.at(sibling) ).value() > (
double)lastPosition ) {
611 setPosition(other, ++lastPosition);
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() ) {
624 bool canBeFinalized =
true;
625 for (
auto& [_2, predecessor] : other->inflows ) {
626 if ( predecessor != vertex && !markedFlows.contains({predecessor,other}) ) {
628 canBeFinalized =
false;
633 markedFlows.insert({vertex,other});
635 if ( !canBeFinalized ) {
639 bool hasPendingPredecessors =
false;
640 for (
auto predecessor : other->predecessors ) {
641 if ( _solution.getVariableValue( cp->position.at(predecessor) ).value() > (
double)lastPosition ) {
643 hasPendingPredecessors =
true;
648 if ( hasPendingPredecessors ) {
652 finalizePosition(other);
655 if ( other->type == Vertex::Type::ENTRY ) {
656 assert( !_solution.getVariableValue( cp->visit.at(other) ).has_value() );
660 assert( other->node->represents<
BPMN::TypedStartEvent>() || _solution.getVariableValue( cp->visit.at(other) ).has_value() );
663 finalizeUnvisitedSubsequentPositions(other);
668bool CPSolutionObserver::isVisited(
const Vertex* vertex)
const {
669 assert( cp->visit.contains( vertex ) );
670 return _solution.evaluate( cp->visit.at( vertex ) ).value_or(
false);
673bool CPSolutionObserver::isUnvisited(
const Vertex* vertex)
const {
674 assert( cp->visit.contains( vertex ) );
675 return !_solution.evaluate( cp->visit.at( vertex ) ).value_or(
true);
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);
684std::optional< BPMNOS::number > CPSolutionObserver::getTimestamp(
const Vertex* vertex )
const {
685 assert( cp->status.contains(vertex) );
689 return (
number)timestamp.value();
694void CPSolutionObserver::setTimestamp(
const Vertex* vertex,
BPMNOS::number timestamp ) {
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() ) {
702 return _solution.evaluate( cp->status.at(vertex)[attributeIndex].value ).value();
705void CPSolutionObserver::setTriggeredEvent(
const Vertex* gateway,
const Vertex* event ) {
709 for (
auto& [ _, target ] : gateway->outflows ) {
710 _solution.setVariableValue( cp->tokenFlow.at({gateway,target}), ( target == event ) );
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 );
void removeSubscriber(Observer *subscriber, ObservableTypes... observableTypes)
void addSubscriber(Observer *subscriber, ObservableTypes... observableTypes)
Represents a token running through a (sub)process.
const BPMN::FlowNode * node
SharedValues * data
Pointer to the data of the owner or owned state machine subprocesses)
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
T * represents()
Attempts to cast the element to the specified type T.
Base class for BPMN elements that may contain a ChildNode elements.
Base class for all start events with an event definition.
BPMNOS_NUMBER_TYPE number
Represents the event that choices are made for a DecisionTask.
Represents the event of a token entering a node.
Represents the event of a token exiting a node.
Represents the event of a message from the message pool being delivered.
virtual constexpr Type getObservableType() const =0
static constexpr size_t Instance
static constexpr size_t Timestamp