12 , model(CP::Model::ObjectiveSense::MAXIMIZE)
22 std::shared_ptr<Decision> best =
nullptr;
29std::cerr <<
"initializeVertices" << std::endl;
35std::cerr <<
"create sequence position variables" << std::endl;
37 auto sequence =
model.addSequence(
"sequence",
vertices.size() );
38 for (
size_t i = 0; i <
vertices.size(); i++ ) {
42std::cerr <<
"createGlobalVariables" << std::endl;
51std::cerr <<
"createMessageVariables" << std::endl;
53std::cerr <<
"Done" << std::endl;
59 assert( attribute->index ==
globals.size() );
61 model.addIndexedVariables(CP::Variable::Type::BOOLEAN,
"defined_" + attribute->id ),
62 model.addIndexedVariables(CP::Variable::Type::REAL,
"value_" + attribute->id)
64 auto& [defined,value] =
globals.back();
67 if ( initialValue.has_value() ) {
69 defined.emplace_back((
double)
true,(
double)
true);
70 value.emplace_back((
double)initialValue.value(), (
double)initialValue.value());
74 defined.emplace_back((
double)
false,(
double)
false);
75 value.emplace_back(0.0, 0.0);
78 if ( attribute->isImmutable ) {
81 defined.emplace_back(defined[0]);
82 value.emplace_back(value[0]);
88 defined.emplace_back();
98 CP::reference_vector<const CP::Variable> messages;
101 messages.emplace_back(
model.addBinaryVariable(
"message_" + sender.
reference() +
"→" + recipient->reference() ) );
102 const CP::Variable& message = messages.back();
103 messageFlow.emplace( std::make_pair(&sender,recipient), message );
104 model.addConstraint( message <=
visit.at(recipient) );
105 model.addConstraint( message <=
visit.at(&sender) );
117 auto senderExit = &sender + 1;
129 for (
const CP::Variable& message : messages ) {
132 model.addConstraint( sum ==
visit.at(recipient) );
147 std::vector< IndexedAttributeVariables > variables;
149 for (
auto& attribute : extensionElements->data ) {
150 assert( attribute->index == variables.size() );
151 variables.emplace_back(
155 auto& [defined,value] = variables.back();
158 if ( initialValue.has_value() ) {
160 defined.emplace_back((
double)
true,(
double)
true);
161 value.emplace_back((
double)initialValue.value(), (
double)initialValue.value());
165 defined.emplace_back((
double)
false,(
double)
false);
166 value.emplace_back(0.0, 0.0);
170 if ( attribute->isImmutable ) {
173 defined.emplace_back(defined[0]);
174 value.emplace_back(value[0]);
180 defined.emplace_back();
181 value.emplace_back();
186 data.emplace( &vertex, std::move(variables) );
190std::cerr <<
"createStatus: " << vertex.
reference() << std::endl;
202 std::vector<AttributeVariables> variables;
203 if ( vertex.
parent.has_value() ) {
204 auto scope = vertex.
parent.value().first.node;
209 variables =
createAlternativeEntryStatus(vertex, extensionElements->attributeRegistry, {{visit.at(&vertex.predecessors.front().get()), status.at(&vertex.predecessors.front().get())}} );
215 std::vector< std::pair<const CP::Variable&, std::vector<AttributeVariables>& > > alternatives;
216 for (
auto& [sequenceFlow,predecessor] : vertex.
inflows ) {
218 alternatives.emplace_back(
tokenFlow.at({&predecessor,&vertex}),
statusFlow.at({&predecessor,&vertex}) );
226 throw std::runtime_error(
"CPController: illegal join at '" + vertex.
node->
id +
"'");
228 std::vector< std::pair<const CP::Variable&, std::vector<AttributeVariables>& > > inputs;
229 for (
auto& [sequenceFlow,predecessor] : vertex.
inflows ) {
231 inputs.emplace_back(
tokenFlow.at({&predecessor,&vertex}),
statusFlow.at({&predecessor,&vertex}) );
233 variables =
createMergedStatus(vertex, extensionElements->attributeRegistry, std::move(inputs));
236 assert( vertex.
inflows.size() == 1 );
237 auto& [sequenceFlow,predecessor] = vertex.
inflows.front();
238 variables =
createAlternativeEntryStatus(vertex, extensionElements->attributeRegistry, {{tokenFlow.at({&predecessor,&vertex}), statusFlow.at({&predecessor,&vertex})}} );
243 variables.reserve(extensionElements->attributeRegistry.statusAttributes.size());
245 auto it = extensionElements->attributeRegistry.statusAttributes.begin();
246 std::advance(it, variables.size());
247 for ( ; it != extensionElements->attributeRegistry.statusAttributes.end(); it++ ) {
248 auto attribute = it->second;
250 auto value = scenario->getKnownValue(vertex.rootId, attribute, 0);
252 if ( value.has_value() ) {
254 variables.emplace_back(
255 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_" + vertex.reference() +
"," + attribute->id, (
double)
true,(
double)
true ),
256 model.addVariable(CP::Variable::Type::REAL,
"value_" + vertex.reference() +
"," + attribute->id, (
double)value.value(), (
double)value.value())
262 variables.emplace_back(
263 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_" + vertex.reference() +
"," + attribute->id, (
double)defined,(
double)defined ),
264 model.addVariable(CP::Variable::Type::REAL,
"value_" + vertex.reference() +
"," + attribute->id, 0.0, 0.0)
270 status.emplace( &vertex, std::move(variables) );
274std::cerr <<
"createExitStatus" << std::endl;
275 std::vector<AttributeVariables> variables;
280 auto getEndVertices = [&]() {
281 std::vector< std::reference_wrapper<const Vertex> > endVertices;
284 candidate.get().parent.has_value() &&
285 &candidate.get().parent.value().second == &vertex &&
286 candidate.get().
parent.value().second.outflows.empty()
288 endVertices.push_back( candidate );
293 std::vector< std::pair<const CP::Variable&, std::vector<AttributeVariables>& > > inputs;
294 for (
const Vertex& endVertex : getEndVertices() ) {
295 assert(
visit.contains(&endVertex));
296 assert(
status.contains(&endVertex));
297 inputs.emplace_back(
visit.at(&endVertex),
status.at(&endVertex) );
300 assert(extensionElements);
301 variables =
createMergedStatus(vertex, extensionElements->attributeRegistry, std::move(inputs));
303 status.emplace( &vertex, std::move(variables) );
310 const Vertex& entry = *(&vertex - 1);
312 if ( !extensionElements ) {
315 auto& entryStatus =
status.at(&entry);
316 for (
auto& [name,attribute] : extensionElements->attributeRegistry.statusAttributes ) {
317 assert( attribute->index == variables.size() );
318 variables.emplace_back(
319 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_" + vertex.
reference() +
"," + attribute->id, entryStatus[attribute->index].defined ),
320 model.addVariable(CP::Variable::Type::REAL,
"value_" + vertex.
reference() +
"," + attribute->id, entryStatus[attribute->index].value )
323 status.emplace( &vertex, std::move(variables) );
334 std::vector<AttributeVariables> currentStatus =
status.at(&entry);
345 if ( extensionElements->messageDefinitions.size() == 1 ) {
346 std::vector< std::tuple< std::string_view, size_t, AttributeVariables> > contentVariables;
347 auto& messageDefinition = extensionElements->messageDefinitions.front();
348 for (
auto& [key,content] : messageDefinition->contentMap ) {
349 auto attribute = content->attribute;
353 model.addBinaryVariable(
"content[" + content->key +
"]_defined_" + vertex.
reference() +
"," + attribute->id ),
354 model.addRealVariable(
"content[" + content->key +
"]_value_" + vertex.
reference() +
"," + attribute->id )
358 currentStatus.at(attribute->index) = attributeVariables;
360 contentVariables.emplace_back( content->key, attribute->index, std::move(attributeVariables) );
365 else if ( extensionElements->messageDefinitions.size() > 1 ) {
367 assert(!
"Not yet supported");
381 for (
auto& [name,attribute] : extensionElements->attributeRegistry.statusAttributes ) {
382 assert( attribute->index == variables.size() );
383 variables.emplace_back(
384 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_" + vertex.
reference() +
"," + attribute->id, currentStatus[attribute->index].defined ),
385 model.addVariable(CP::Variable::Type::REAL,
"value_" + vertex.
reference() +
"," + attribute->id, currentStatus[attribute->index].value )
389 assert(
visit.contains(&vertex) );
390 model.addConstraint( variables[attribute->index].defined <=
visit.at(&vertex) );
391 model.addConstraint( (!variables[attribute->index].defined).implies( variables[attribute->index].value == 0.0 ) );
394 status.emplace( &vertex, std::move(variables) );
400 std::vector<AttributeVariables> variables;
404 CP::Expression defined(
false);
405 CP::Expression value = 0.0;
406 for (
auto& [ active, attributeVariables] : alternatives ) {
407 assert( attributeVariables.size() == attributeRegistry.
statusAttributes.size() );
408 defined = defined || attributeVariables[attribute->index].defined;
409 value = value + attributeVariables[attribute->index].value;
411 variables.emplace_back(
412 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_" + vertex.
reference() +
"," + attribute->id, defined ),
413 model.addVariable(CP::Variable::Type::REAL,
"value_" + vertex.
reference() +
"," + attribute->id, value )
422 std::vector<AttributeVariables> variables;
440 CP::Expression defined(
false);
442 for (
auto& [ active, attributeVariables] : inputs ) {
443 defined = defined || attributeVariables[attribute->index].defined;
444 cases.emplace_back( attributeVariables[attribute->index].defined, attributeVariables[attribute->index].value );
446 variables.emplace_back(
447 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_" + vertex.
reference() +
"," + attribute->id, defined ),
448 model.addVariable(CP::Variable::Type::REAL,
"value_" + vertex.
reference() +
"," + attribute->id, CP::n_ary_if( cases, 0.0 ))
452 auto& mergedValue = variables.back().value;
453 for (
auto& [ _, attributeVariables] : inputs ) {
454 auto& [ hasValue, value ] = attributeVariables[attribute->index];
455 model.addConstraint( hasValue.implies( value == mergedValue ) );
465 std::vector< std::reference_wrapper<const FlattenedGraph::Vertex> > reachableVertices;
466 std::unordered_map<const FlattenedGraph::Vertex*, size_t> inDegree;
468 std::deque<const FlattenedGraph::Vertex*> queue;
469 queue.push_back(&initialVertex);
472 while ( !queue.empty() ) {
473std::cerr <<
"Queue " << queue.size() << std::endl;
476 reachableVertices.push_back(*current);
477 inDegree.erase(current);
479 for (
auto& [_,vertex] : current->
outflows ) {
480 if ( !inDegree.contains(&vertex) ) {
482 inDegree[&vertex] = vertex.inflows.size() + vertex.predecessors.size();
485 if ( --inDegree.at(&vertex) == 0 ) {
486 queue.push_back(&vertex);
490 if ( !inDegree.contains(&vertex) ) {
494std::cerr <<
"Vertex " << vertex.
reference() <<
" has inDegree " << inDegree.at(&vertex) <<
"/" << vertex.
inflows.size() <<
"/" << vertex.
predecessors.size() << std::endl;
496 if ( --inDegree.at(&vertex) == 0 ) {
497 queue.push_back(&vertex);
502 if ( inDegree.size() ) {
505std::cerr <<
"reachableVertices:" << reachableVertices.size() << std::endl;
506 return reachableVertices;
516std::cerr <<
"createVertexVariables: " << vertex.
reference() << std::endl;
518std::cerr <<
"createGlobalIndexVariable" << std::endl;
520std::cerr <<
"createDataIndexVariables" << std::endl;
524std::cerr <<
"createEntryVariables" << std::endl;
528std::cerr <<
"createExitVariables" << std::endl;
532std::cerr <<
"createDataVariables: " << vertex.
reference() << std::endl;
537std::cerr <<
"createStatus" << std::endl;
540std::cerr <<
"createSequenceConstraints" << std::endl;
542std::cerr <<
"Done" << std::endl;
548 assert( vertex.
inflows.empty() );
550 assert( vertex.
senders.empty() );
554 visit.emplace(&vertex, deducedVisit );
555 visit.emplace(&vertex+1, deducedVisit );
558 assert(!
"Not yet implemented");
561 if ( vertex.
inflows.size() == 1 ) {
564 visit.emplace(&vertex, deducedVisit );
565 visit.emplace(&vertex+1, deducedVisit );
568 assert(!
"Not yet implemented");
574 visit.emplace(&vertex, knownVisit );
575 visit.emplace(&vertex+1, knownVisit );
585 if ( vertex.
outflows.size() == 1 ) {
587 std::make_pair(&vertex,&vertex.
outflows.front().second),
588 model.addVariable(CP::Variable::Type::BOOLEAN,
"tokenflow_" + vertex.
reference() +
"→" + vertex.
outflows.front().second.reference(),
visit.at(&vertex) )
591 std::vector<AttributeVariables> variables;
592 variables.reserve( extensionElements->attributeRegistry.statusAttributes.size() );
593 for (
auto& [name,attribute] : extensionElements->attributeRegistry.statusAttributes ) {
595 variables.emplace_back(
596 model.addVariable(CP::Variable::Type::BOOLEAN,
"statusflow_defined_" + vertex.
reference() +
"→" + vertex.
outflows.front().second.reference() +
"," + attribute->id,
598 tokenFlow.at({&vertex,&vertex.outflows.front().second}),
599 status.at(&vertex)[attribute->index].defined,
603 model.addVariable(CP::Variable::Type::REAL,
"statusflow_value_" + vertex.
reference() +
"→" + vertex.
outflows.front().second.reference() +
"," + attribute->id,
605 tokenFlow.at({&vertex,&vertex.outflows.front().second}),
606 status.at(&vertex)[attribute->index].value,
614 std::make_pair(&vertex,&vertex.
outflows.front().second),
618 else if ( vertex.
outflows.size() > 1 ) {
619 assert(!
"Not yet implemented");
625 auto addConstraints = [&](
const Vertex& predecessor) {
626 assert(
position.contains(&predecessor) );
627 assert(
position.contains(&vertex) );
628 assert(
visit.contains(&vertex) );
629 assert(
status.contains(&predecessor) );
630 assert(
status.contains(&vertex) );
636 visit.at(&vertex).implies (
643 for (
auto& [sequenceFlow, predecessor] : vertex.
inflows ) {
644 addConstraints(predecessor);
648 addConstraints(predecessor);
654 CP::Expression index;
657 index = index +
model.addVariable(CP::Variable::Type::BOOLEAN,
"precedes_" + modifierExit.reference() +
"" + vertex.
reference(),
position.at(&modifierExit) <=
position.at(&vertex) );
659 globalIndex.emplace( &vertex,
model.addVariable(CP::Variable::Type::INTEGER,
"globals_index_" + vertex.
reference(), index ) );
664 std::vector< CP::reference_vector< const CP::Variable > > dataIndices;
665 dataIndices.resize( vertex.
dataOwners.size() );
666 for (
size_t i = 0; i < vertex.
dataOwners.size(); i++ ) {
670 CP::Expression index;
673 index = index +
model.addVariable(CP::Variable::Type::BOOLEAN,
"precedes_" + modifierExit.reference() +
"→" + vertex.
reference(),
position.at(&modifierExit) <=
position.at(&vertex) );
675 dataIndices[i].emplace_back(
model.addVariable(CP::Variable::Type::INTEGER,
"data_index[" + dataOwner.node->id +
"]_" + vertex.
reference(), index ) );
677 dataIndex.emplace( &vertex, std::move(dataIndices) );
894 if ( flowNode->incoming.empty() ) {
895 statusVariables.emplace_back(
896 model.addVariable(CP::Variable::Type::BOOLEAN, "defined_entry_" + identifier(reference) + "_" + name, entryStatus.at(NodeReference{instance,flowNode->parent})[attribute->index].defined ),
897 model.addVariable(CP::Variable::Type::REAL, "value_entry_" + identifier(reference) + "_" + name, entryStatus.at(NodeReference{instance,flowNode->parent})[attribute->index].value )
900 else if ( flowNode->incoming.size() == 1 ) {
901 auto& predecessor = flowNode->incoming.front()->source;
902 statusVariables.emplace_back(
903 model.addVariable(CP::Variable::Type::BOOLEAN, "defined_entry_" + identifier(reference) + "_" + name, exitStatus.at(NodeReference{instance,predecessor})[attribute->index].defined ),
904 model.addVariable(CP::Variable::Type::REAL, "value_entry_" + identifier(reference) + "_" + name, exitStatus.at(NodeReference{instance,predecessor})[attribute->index].value )
907 else if ( node->represents<BPMN::ExclusiveGateway>() ) {
908 assert(!"Exclusive join not yet supported");
911 assert(!"Non-exclusive join not yet supported");
917 entryStatus.emplace( reference , std::move(statusVariables) );
920void CPController::deduceEntryAttributeVariable(std::vector<AttributeVariables>& attributeVariables, const BPMNOS::Model::Attribute* attribute, NodeReference reference, variable_map<NodeReference, std::vector<AttributeVariables> >& entryAttributes, variable_map<NodeReference, std::vector<AttributeVariables> >& exitAttributes) {
921 auto& [instance,node] = reference;
922 auto flowNode = node->as<BPMN::FlowNode>();
924 if ( flowNode->incoming.empty() ) {
925 // attribute values of start node is deduced from entry value of parent
926 attributeVariables.emplace_back(
927 model.addVariable(CP::Variable::Type::BOOLEAN, "defined^entry_{" + identifier(reference) + "," + attribute->name + "}", entryAttributes.at(NodeReference{instance,flowNode->parent})[attribute->index].defined ),
928 model.addVariable(CP::Variable::Type::REAL, "value^entry_{" + identifier(reference) + "," + attribute->name + "}", entryAttributes.at(NodeReference{instance,flowNode->parent})[attribute->index].value )
931 else if ( flowNode->incoming.size() == 1 ) {
932 // attribute values of flow node with exactle one predecessor is deduced from exit value of predecessor
933 auto& predecessor = flowNode->incoming.front()->source;
934 attributeVariables.emplace_back(
935 model.addVariable(CP::Variable::Type::BOOLEAN, "defined^entry_{" + identifier(reference) + "," + attribute->name, exitAttributes.at(NodeReference{instance,predecessor})[attribute->index].defined ),
936 model.addVariable(CP::Variable::Type::REAL, "value^entry_{" + identifier(reference) + "," + attribute->name + "}", exitAttributes.at(NodeReference{instance,predecessor})[attribute->index].value )
939 else if ( flowNode->represents<BPMN::ExclusiveGateway>() ) {
940 assert(!"Exclusive join not yet supported");
943 assert(!"Non-exclusive join not yet supported");
948void CPController::createNodeTraversalVariables(NodeReference reference) {
949 auto& [instance,node] = reference;
951 // create node variables
952 if ( node->represents<BPMN::Process>() ) {
953 // process must be executed
954 nodeVariables.emplace(
955 NodeReference{instance, node},
956 model.addVariable(CP::Variable::Type::BOOLEAN, std::string("x_{") + identifier(reference) + "}", (double)true, (double)true)
959 else if ( auto startEvent = node->represents<BPMN::UntypedStartEvent>() ) {
960 // start event must be executed if parent is executed
961 nodeVariables.emplace(
962 NodeReference{instance, node},
964 CP::Variable::Type::BOOLEAN,
965 std::string("x_{") + identifier(reference) + "}",
966 nodeVariables.at(NodeReference{instance,startEvent->parent})
970 else if ( auto startEvent = node->represents<BPMN::TypedStartEvent>() ) {
971 if ( startEvent->isInterrupting ) {
972 throw std::runtime_error("CPController: unsupported interrupting start event '" + node->id + "'");
975 if ( auto messageStartEvent = node->represents<BPMN::MessageStartEvent>() ) {
976 CP::OrExpression messageReceived;
977 auto originCandidates = messageStartEvent->extensionElements->as<BPMNOS::Model::ExtensionElements>()->messageCandidates;
978 for ( auto candidate : originCandidates ) {
979 for ( auto originInstance : originInstances[candidate->as<BPMN::MessageThrowEvent>()] ) {
980 messageReceived = messageReceived || messageFlowVariables.at(MessageCatchReference{instance,node->as<BPMN::MessageCatchEvent>()}).at(MessageThrowReference{(size_t)originInstance,candidate->as<BPMN::MessageThrowEvent>()});
983 nodeVariables.emplace(
984 NodeReference{instance, node},
986 CP::Variable::Type::BOOLEAN,
987 std::string("x_{") + identifier(reference) +"}",
993 throw std::runtime_error("CPController: unsupported start event type for '" + node->id + "'");
996 else if ( auto flowNode = node->represents<BPMN::FlowNode>() ) {
997 assert( flowNode->incoming.size() );
998 CP::OrExpression tokenArrives;
999 for (auto sequenceFlow : flowNode->incoming ) {
1000 tokenArrives = tokenArrives || sequenceFlowVariables.at(SequenceFlowReference{instance,sequenceFlow});
1002 nodeVariables.emplace(
1003 NodeReference{instance, node},
1005 CP::Variable::Type::BOOLEAN,
1006 std::string("x_{") + identifier(reference) + "}",
1014void CPController::createEntryDataStateVariables(NodeReference reference) {
1015 // create entry data states
1016 auto& [instance,node] = reference;
1017// auto extensionElements = node->extensionElements->as<BPMNOS::Model::ExtensionElements>();
1020 // the entry data state is non-decreasing along sequence flows and subprocesses
1021 for ( auto scopeOwningData : scopesOwningData.at(reference) ) {
1022 auto scopeReference = ScopeReference{instance,node->as<BPMN::Scope>()};
1023 std::vector< std::reference_wrapper< const CP::Variable > > variables;
1024 CP::LinearExpression sumVariables(0);
1025 for ( size_t i = 0; i <= sequentialActivities[scopeOwningData].size(); i++ ) {
1026 if ( scopeOwningData == node ) {
1027 // use first entry data state
1028 variables.emplace_back(
1029 model.addVariable( CP::Variable::Type::BOOLEAN, std::string("y^{entry,") + std::to_string(i) + "," + scopeOwningData->id + "}_{" + identifier(reference) + "}", (double)(i == 0), (double)(i == 0) )
1033 if ( auto activity = node->represents<BPMN::Activity>() ) {
1034 // for each activity, the entry data state is a decision (with constraints)
1035 if ( activity->incoming.size() > 1 ) {
1036 throw std::runtime_error("CPController: implicit join for activity '" + activity->id + "'");
1038 else if ( activity->incoming.empty() && !activity->parent->represents<BPMNOS::Model::SequentialAdHocSubProcess>() ) {
1039 throw std::runtime_error("CPController: activitiy '" + activity->id + "' has no incoming sequence flow");
1042 if ( activity->incoming.size() == 1 ) {
1043 if ( !activity->parent->represents<BPMNOS::Model::SequentialAdHocSubProcess>() ) {
1044 variables.emplace_back(
1045 model.addBinaryVariable( std::string("y^{entry,") + std::to_string(i) + "," + scopeOwningData->id + "}_{" + identifier(reference) + "}" )
1048 // ensure that first i entry data states are smaller or equal first i exit data states of predecessor
1049 auto& predecessor = activity->incoming.front()->source;
1050 CP::LinearExpression sumFirstExitDataStatesOfPredecessor(0);
1051 CP::LinearExpression sumFirstEntryDataStatesOfActivity(0);
1052 for ( size_t j = 0; j <= i ; j++ ) {
1053 sumFirstExitDataStatesOfPredecessor += exitDataState.at(NodeReference{instance,predecessor}).at(scopeReference)[j].get();
1054 sumFirstEntryDataStatesOfActivity += variables[j].get();
1056 model.addConstraint( sumFirstEntryDataStatesOfActivity <= sumFirstExitDataStatesOfPredecessor );
1060 else if ( auto startEvent = node->represents<BPMN::UntypedStartEvent>() ) {
1061 // for each untyped start event, the entry data state must be the same as for its parent
1062 auto& parentEntryDataState = entryDataState.at(NodeReference{instance,startEvent->parent}).at( scopeReference )[i].get();
1063 variables.emplace_back(
1064 model.addVariable( CP::Variable::Type::BOOLEAN, std::string("y^{entry,") + std::to_string(i) + "," + scopeOwningData->id + "}_{" + identifier(reference) + "}", parentEntryDataState )
1067 else if ( auto flowNode = node->represents<BPMN::FlowNode>() ) {
1068 assert( flowNode->incoming.size() );
1069 assert(!node->represents<BPMN::Activity>());
1071 if ( flowNode->incoming.size() == 1 ) {
1072 // entry data states must be the same as exit state of predecessor
1073 auto& predecessor = flowNode->incoming.front()->source;
1074 auto& predecessorExitDataState = exitDataState.at(NodeReference{instance,predecessor}).at( scopeReference )[i].get();
1075 variables.emplace_back(
1076 model.addVariable( CP::Variable::Type::BOOLEAN, std::string("y^{entry,") + std::to_string(i) + "," + scopeOwningData->id + "}_{" + identifier(reference) + "}", predecessorExitDataState )
1079 else if ( node->represents<BPMN::ExclusiveGateway>() ) {
1080 assert(!"Exclusive join not yet supported");
1083 assert(!"Non-exclusive join not yet supported");
1087 sumVariables += variables.back().get();
1089 entryDataState[reference].emplace( scopeReference, std::move(variables) );
1091 // if and only if the node is visited, exactly one of the variables must be true
1092 model.addConstraint( sumVariables == nodeVariables.at(reference) );
1098void CPController::addChildren(ScopeReference reference) {
1099 auto& [instance,scope] = reference;
1100 if ( scope->startNodes.empty() ) {
1101 // no flow nodes within scope
1104 else if ( scope->startNodes.size() > 1 ) {
1105 throw std::runtime_error("CPController: multiple start nodes for scope '" + scope->id + "'");
1107 pendingEntry.insert(NodeReference{instance,scope->startNodes.front()});
1109 for ( auto eventSubProcess : scope->eventSubProcesses ) {
1110 assert(!"Event-subprocesses not yet supported");
1115void CPController::addSuccessors(NodeReference reference) {
1116 auto& [instance,node] = reference;
1117 assert(node->represents<BPMN::FlowNode>());
1118 auto flowNode = node->as<BPMN::FlowNode>();
1119 if ( flowNode->outgoing.size() == 1 || node->represents<BPMN::ParallelGateway>() ) {
1120 for ( auto sequenceFlow : flowNode->outgoing ) {
1121 auto& successor = sequenceFlow->target;
1122 // token traverses sequence flow when node is visited
1123 sequenceFlowVariables.emplace(
1124 SequenceFlowReference{instance, sequenceFlow},
1126 CP::Variable::Type::BOOLEAN,
1127 std::string("x^sequence_{") + identifier(reference) + "," + successor->id + "}",
1128 nodeVariables.at(reference)
1131 pendingEntry.insert(NodeReference{instance,successor});
1134 else if ( node->represents<BPMN::ExclusiveGateway>() || node->represents<BPMN::InclusiveGateway>() ) {
1135 assert(!"Exclusive and inclusive fork not yet supported");
1137 else if ( node->represents<BPMN::EventBasedGateway>() ) {
1138 assert(!"Event-based gateways not yet supported");
1142void CPController::addExitVariables(NodeReference reference) {
1143 auto& [instance,node] = reference;
1144 auto extensionElements = node->extensionElements->as<BPMNOS::Model::ExtensionElements>();
1146 createExitDataStateVariables(reference);
1148 // create exit data variables
1149 auto dataVariables = std::vector<AttributeVariables>();
1150 for ( auto& [name,attribute] : extensionElements->attributeRegistry.dataAttributes ) {
1151 auto scopeOwningData = dataOwner.at(attribute);
1152 assert( attribute->index == dataVariables.size() );
1155 auto scopeReference = ScopeReference{instance,dataOwner.at(attribute)};
1156 auto it = exitDataState[reference].find(scopeReference);
1157 if ( it != exitDataState.at(reference).end() ) {
1158 auto& exitDataStateVariables = it->second;
1159 CP::Cases defined_cases;
1160 CP::Cases value_cases;
1161 for ( size_t i = 0; i < exitDataStateVariables.size(); i++ ) {
1162 auto& [defined,value] = dataState[DataReference{instance,attribute}][i];
1163 defined_cases.emplace_back( exitDataStateVariables[i], defined );
1164 value_cases.emplace_back( exitDataStateVariables[i], value );
1166 dataVariables.emplace_back(
1167 model.addVariable(CP::Variable::Type::BOOLEAN, "defined^exit_[" + identifier(reference) + "," + name+ "}", CP::n_ary_if(defined_cases,0) ),
1168 model.addVariable(CP::Variable::Type::REAL, "value^exit_{" + identifier(reference) + "," + name+ "}", CP::n_ary_if(value_cases,0))
1172 exitData.emplace( reference , std::move(dataVariables) );
1174 // create exit status variables
1176 auto statusVariables = std::vector<AttributeVariables>();
1177 for ( auto& [name,attribute] : extensionElements->attributeRegistry.statusAttributes ) {
1178 assert( attribute->index == statusVariables.size() );
1179 if ( auto scope = node->represents<BPMN::Scope>() ) {
1180 // create exit status variables
1181 if ( scope->flowNodes.empty() ) {
1182 // exit status is the same as entry status
1183 statusVariables.emplace_back(
1184 model.addVariable(CP::Variable::Type::BOOLEAN, "defined^exit_[" + identifier(reference) + "," + name+ "}", entryStatus.at(reference)[attribute->index].defined ),
1185 model.addVariable(CP::Variable::Type::REAL, "value^exit_{" + identifier(reference) + "," + name+ "}", entryStatus.at(reference)[attribute->index].value )
1189 // assume an exclusive end node
1192 else if ( auto event = node->represents<BPMN::CatchEvent>() ) {
1194 else if ( auto gateway = node->represents<BPMN::Gateway>();
1195 gateway && gateway->incoming.size() > 1
1198 else if ( auto task = node->represents<BPMN::Task>() ) {
1206void CPController::createExitDataStateVariables(NodeReference reference) {
1207 auto& [instance,node] = reference;
1208 for ( auto scopeOwningData : scopesOwningData.at(reference) ) {
1209 std::vector< std::reference_wrapper< const CP::Variable > > variables;
1210 CP::LinearExpression sumVariables(0);
1211 for ( size_t i = 0; i <= sequentialActivities[scopeOwningData].size(); i++ ) {
1213 if ( auto activity = node->represents<BPMN::Activity>() ) {
1214 if ( auto adhoc = activity->parent->represents<BPMNOS::Model::SequentialAdHocSubProcess>();
1215 adhoc && adhoc->performer == scopeOwningData
1217 // TODO: exit data state must be exactly one higher than entry data state of each child
1220 // for all other activities, the exit data state is a decision (with constraints)
1221 variables.emplace_back(
1222 model.addBinaryVariable( std::string("y^{exit_{") + std::to_string(i) + "," + scopeOwningData->id + "}_{" + identifier(reference) + "}" )
1225 if ( auto scope = node->represents<BPMN::Scope>() ) {
1226 constrainExitDataStateVariables(ScopeReference{instance,scope});
1230 else if ( auto flowNode = node->represents<BPMN::CatchEvent>() ) {
1231 // TODO: exit data state must be higher than exit data state of each child
1233 else if ( auto process = node->represents<BPMN::Process>() ) {
1234 // the exit data state is a decision (with constraints)
1235 variables.emplace_back(
1236 model.addBinaryVariable( std::string("y^{exit,") + std::to_string(i) + "," + scopeOwningData->id + "}_{" + identifier(reference) + "}" )
1238 constrainExitDataStateVariables(ScopeReference{instance,process});
1241 // TODO: exit data state must be the same as entry data state
1243 sumVariables += variables.back().get();
1245 // ensure that first i exit data states are smaller or equal first i entry data states
1246 CP::LinearExpression sumFirstEntryDataStates(0);
1247 CP::LinearExpression sumFirstExitDataStates(0);
1248 for ( size_t j = 0; j <= i ; j++ ) {
1249 sumFirstEntryDataStates += entryDataState.at(reference).at(ScopeReference{instance,scopeOwningData})[j].get();
1250 sumFirstExitDataStates += variables[j].get();
1252 model.addConstraint( sumFirstExitDataStates <= sumFirstEntryDataStates );
1254 exitDataState[reference].emplace( ScopeReference{instance,scopeOwningData}, std::move(variables) );
1255 // if and only if the node is visited, exactly one of the variables must be true
1256 model.addConstraint( sumVariables == nodeVariables.at(reference) );
1260void CPController::constrainExitDataStateVariables(ScopeReference reference) {
1261 // TODO: exit data state of scope must be higher or equal than exit data state of each descendant
1264void CPController::createSequenceFlowTraversalVariables(SequenceFlowReference reference) {
std::vector< std::reference_wrapper< const Vertex > > getReachableVertices(const Vertex &initialVertex)
std::unordered_map< const Vertex *, const CP::Variable & > visit
Variables holding sequence positions for all vertices.
std::unordered_map< const Vertex *, const CP::Variable & > globalIndex
Variables representing global attributes after i-th modification.
void createEntryVariables(const Vertex &vertex)
void connect(Mediator *mediator)
std::unordered_map< const Vertex *, const CP::Variable & > position
Container of all vertices catching a message.
void createGlobalVariables()
Method creating the constraint program.
std::shared_ptr< Event > dispatchEvent(const SystemState *systemState)
const BPMNOS::Model::Scenario * scenario
std::unordered_map< std::pair< const Vertex *, const Vertex * >, std::vector< AttributeVariables >, pair_hash > statusFlow
Variables representing status attributes of a vertex.
void createEntryStatus(const Vertex &vertex)
std::unordered_map< const Vertex *, std::vector< CP::reference_vector< const CP::Variable > > > dataIndex
Variables representing data attributes owned by an entry vertex after i-th modification.
void createGlobalIndexVariable(const Vertex &vertex)
std::unordered_map< const Vertex *, std::vector< AttributeVariables > > status
Variables representing an index representing the state of the data attributes for each data owner.
std::unordered_map< const Vertex *, std::vector< IndexedAttributeVariables > > data
Variables representing an index representing the state of the global attributes.
void createDataIndexVariables(const Vertex &vertex)
CPController(const BPMNOS::Model::Scenario *scenario)
void createVertexVariables(const Vertex &vertex)
void createMessageContent(const Vertex &vertex)
std::vector< IndexedAttributeVariables > globals
Variables indicating whether the a token flows from one vertex to another.
std::vector< const Vertex * > messageRecipients
Container of all vertices considered.
std::vector< AttributeVariables > createMergedStatus(const Vertex &vertex, const BPMNOS::Model::AttributeRegistry &attributeRegistry, std::vector< std::pair< const CP::Variable &, std::vector< AttributeVariables > & > > inputs)
void createExitVariables(const Vertex &vertex)
std::unordered_map< const Vertex *, std::vector< std::tuple< std::string_view, size_t, AttributeVariables > > > messageContent
Variables representing status attributes flowing from one vertex to another.
void createSequenceConstraints(const Vertex &vertex)
std::unordered_map< std::pair< const Vertex *, const Vertex * >, const CP::Variable &, pair_hash > messageFlow
Variables indicating whether the a token flows from one vertex to another.
std::vector< AttributeVariables > createAlternativeEntryStatus(const Vertex &vertex, const BPMNOS::Model::AttributeRegistry &attributeRegistry, std::vector< std::pair< const CP::Variable &, std::vector< AttributeVariables > & > > alternatives)
void initializeVertices(const Vertex &initialVertex)
Returns a topologically sorted vector of all vertices reachable from the given vertex.
void createMessageVariables()
std::unordered_map< std::pair< const Vertex *, const Vertex * >, const CP::Variable &, pair_hash > tokenFlow
Variables indicating whether the a token enters or leaves a vertex.
void createDataVariables(const Vertex &vertex)
void createStatus(const Vertex &vertex)
void createExitStatus(const Vertex &vertex)
const FlattenedGraph flattenedGraph
std::vector< const Vertex * > vertices
virtual void connect(Mediator *mediator)
std::optional< std::pair< Vertex &, Vertex & > > parent
std::vector< std::pair< const BPMN::SequenceFlow *, Vertex & > > inflows
Parent vertices.
std::vector< std::reference_wrapper< Vertex > > successors
Container holding predecessors according to the execution logic (excl. sequence flows)
std::vector< std::reference_wrapper< Vertex > > dataOwners
Container holding all possible vertices receiving a message (or the message delivery notfication for ...
bool entry() const
Returns a unique reference of the vertex.
std::vector< std::pair< const BPMN::SequenceFlow *, Vertex & > > outflows
Container holding vertices connecting by an incoming sequence flow.
std::string reference() const
Returns the vertices of the owner of a data attribute.
std::vector< std::reference_wrapper< Vertex > > predecessors
Container holding vertices connecting by an outgoing sequence flow.
const BPMNOS::number rootId
const BPMNOS::number instanceId
std::vector< std::reference_wrapper< Vertex > > senders
Container holding successors according to the execution logic (excl. sequence flows)
Represents a graph containing all BPMN nodes that may receive a token during execution of a scenario.
std::unordered_map< const Vertex *, std::vector< std::pair< const Vertex &, const Vertex & > > > dataModifiers
Container allowing to look up vertices of sequential activities given a pointer to the entry vertex o...
std::deque< Vertex > vertices
Container holding entry vertices of all process instances.
std::vector< std::reference_wrapper< Vertex > > initialVertices
std::vector< std::pair< const Vertex &, const Vertex & > > globalModifiers
Container allowing to look up vertices of tasks modifying data attributes given a pointer to the entr...
A class representing the state that the execution or simulation of a given scenario is in.
std::map< std::string, Attribute * > globalAttributes
std::map< std::string, Attribute * > statusAttributes
Class representing a task in which one or more choices have to be made.
Class holding extension elements representing execution data for nodes.
AttributeRegistry attributeRegistry
Registry allowing to look up all status and data attributes by their names.
The Scenario class holds data for all BPMN instances.
std::optional< BPMNOS::number > getKnownValue(const Scenario::InstanceData *instance, const BPMNOS::Model::Attribute *attribute, const BPMNOS::number currentTime) const
Method returning a known value of an attribute.
const Model * model
Pointer to the BPMN model.
std::unique_ptr< ExtensionElements > extensionElements
std::string id
Id of element.
T * represents()
Attempts to cast the element to the specified type T.
Base class for BPMN elements that may contain incoming and outgoing sequence flows.
Base class for BPMN elements that may contain a ChildNode elements.
Base class for all start events with an event definition.
std::string to_string(number numericValue, const ValueType &type)
Converts a number to a string.
static constexpr size_t Timestamp