11#include <cp/limex_handle.h>
17 : scenario(flattenedGraph->scenario)
18 , config(
std::move(config))
19 , flattenedGraph(flattenedGraph)
20 , model(CP::Model::ObjectiveSense::MAXIMIZE)
23 throw std::runtime_error(
"CPModel: scenario must be static");
25 if ( this->config.instantEntry ) {
26 throw std::runtime_error(
"CPModel: instant entry is not supported");
28 if ( !this->config.instantChoices ) {
29 throw std::runtime_error(
"CPModel: non-instant choices are not supported");
31 if ( !this->config.instantExit ) {
32 throw std::runtime_error(
"CPModel: non-instant exit is not supported");
36 model.setCollectionLookup(
37 [](
size_t key) ->
const std::vector<double>& {
38 return collectionRegistry[key];
44 for (
auto& lookupTable : scenario->model->lookupTables ) {
47 [&lookupTable](const std::vector<CP::Expression>& args) -> CP::Expression
49 std::vector<CP::Operand> operands = { CP::Expression::getCustomIndex(lookupTable->name) };
50 operands.insert(operands.end(), args.begin(), args.end());
51 return CP::Expression(CP::Expression::Operator::custom, std::move(operands));
65std::optional< BPMN::Activity::LoopCharacteristics> CPModel::getLoopCharacteristics(
const Vertex* vertex)
const {
74void CPModel::createCP() {
79 auto& sequence = model.addSequence(
"position", vertices.size() );
80 for (
size_t i = 0; i < vertices.size(); i++ ) {
81 position.emplace(vertices[i], sequence.variables[i]);
82 indexMap.emplace(vertices[i], i);
86 createMessageFlowVariables();
89 createGlobalVariables();
93 for (
auto vertex : vertices ) {
94 createVertexVariables(vertex);
98 constrainGlobalVariables();
100 for (
auto vertex : vertices ) {
103 constrainDataVariables(vertex);
107 constrainEventBasedGateway(vertex);
110 constrainTypedStartEvent(vertex);
115 constrainSequentialActivities();
118 createMessagingConstraints();
123void CPModel::createGlobalVariables() {
124 for (
auto attribute : scenario->model->attributeRegistry.globalAttributes ) {
125 assert( attribute->index == globals.size() );
126 globals.emplace_back(
127 model.addIndexedVariables(CP::Variable::Type::BOOLEAN,
"defined_" + attribute->id ),
128 model.addIndexedVariables(CP::Variable::Type::REAL,
"value_" + attribute->id)
130 auto& [defined,value] = globals.back();
132 auto& initialValue = scenario->globals[attribute->index];
133 if ( initialValue.has_value() ) {
135 model.addIndexedVariable(defined,
true,
true);
136 model.addIndexedVariable(value, (
double)initialValue.value(), (
double)initialValue.value());
140 model.addIndexedVariable(defined,
false,
false);
141 model.addIndexedVariable(value, 0.0, 0.0);
144 if ( attribute->isImmutable ) {
148 model.addIndexedVariable(defined, defined[0]);
149 model.addIndexedVariable(value, value[0]);
155 model.addIndexedVariable(defined);
156 model.addIndexedVariable(value);
159 addToObjective( attribute, value[ value.size() -1 ] );
163void CPModel::createMessageFlowVariables() {
164 for (
auto& vertex : flattenedGraph->
vertices ) {
166 messageRecipients.push_back(vertex.get());
167 CP::reference_vector<const CP::Variable> messages;
168 for (
auto sender : vertex->senders ) {
171 messages.emplace_back( model.addBinaryVariable(
"message_{" + sender->reference() +
" → " + vertex->reference() +
"}" ) );
173 messageFlow.emplace( std::make_pair(sender,vertex.get()), messages.back() );
177 messageSenders.push_back(vertex.get());
182void CPModel::createMessagingConstraints() {
183 for (
auto recipient : messageRecipients ) {
185 CP::Expression messagesDelivered(0);
187 for (
auto sender : recipient->senders ) {
190 auto& message = messageFlow.at({sender,recipient});
191 messagesDelivered = messagesDelivered + message;
193 model.addConstraint( message <= visit.at(recipient) );
194 model.addConstraint( message <= visit.at(sender) );
196 model.addConstraint( message.implies( position.at(sender) < position.at(recipient) ) );
210 auto& recipientStatus =
212 std::get<0>(locals.at(recipient)[0]) :
225 assert( messageHeader.contains(entry(recipient)) );
226 assert( messageHeader.contains(sender) );
227 auto& recipientHeader = messageHeader.at(entry(recipient));
228 auto& senderHeader = messageHeader.at(sender);
229 for (
auto& [key, recipientHeaderVariables] : recipientHeader ) {
230 if ( !senderHeader.contains(key) ) {
231 throw std::runtime_error(
"CPModel: illegal message flow from '" + sender->node->id +
"' to '" + recipient->node->id +
"'");
234 ( message && recipientHeaderVariables.defined && senderHeader.at(key).defined ).implies (
235 recipientHeaderVariables.value == senderHeader.at(key).value
242 auto& recipientContent = messageContent.at(recipient);
243 auto& senderContent = messageContent.at(sender);
244 for (
auto& [key, recipientContentVariables] : recipientContent ) {
245 if ( !senderContent.contains(key) ) {
246 throw std::runtime_error(
"CPModel: illegal message flow from '" + sender->node->id +
"' to '" + recipient->node->id +
"'");
248 model.addConstraint( message.implies ( recipientContentVariables.defined == senderContent.at(key).defined ) );
249 model.addConstraint( message.implies ( recipientContentVariables.value == senderContent.at(key).value ) );
254 model.addConstraint( visit.at(recipient) == messagesDelivered );
257 for (
auto sender : messageSenders ) {
259 CP::Expression messagesDelivered(0);
260 for (
auto recipient : sender->recipients ) {
262 auto& message = messageFlow.at({sender,recipient});
263 messagesDelivered = messagesDelivered + message;
267 model.addConstraint( visit.at(sender) == messagesDelivered );
271 model.addConstraint( visit.at(sender) >= messagesDelivered );
276void CPModel::createMessageHeader(
const Vertex* vertex) {
278 assert( extensionElements );
280 if ( extensionElements->messageDefinitions.size() > 1 ) {
281 assert(!
"Not yet implemented");
284 auto messageDefinition = extensionElements->messageDefinitions.size() == 1 ? extensionElements->messageDefinitions[0].get() :
nullptr;
285 assert( messageDefinition );
286 auto& messageHeaderVariables = messageHeader[vertex];
288 for (
size_t i = 0; i < messageDefinition->header.size(); i++ ) {
293 auto& header = messageDefinition->header[i];
299 messageHeaderVariables.emplace(
302 model.addVariable(CP::Variable::Type::BOOLEAN,
"header_defined_{" + vertex->shortReference() +
"}," + header,
true ),
303 model.addVariable(CP::Variable::Type::REAL,
"header_value_{" + vertex->shortReference() +
"}," + header, (
double)vertex->instanceId )
310 if ( messageDefinition->parameterMap.contains(header) ) {
312 messageDefinition->parameterMap.at(header)->
expression ?
313 messageDefinition->parameterMap.at(header)->expression->isAttribute() :
317 auto [defined,value] = getAttributeVariables(vertex, attribute);
318 messageHeaderVariables.emplace(
321 model.addVariable(CP::Variable::Type::BOOLEAN,
"header_defined_{" + vertex->shortReference() +
"}," + header, defined ),
322 model.addVariable(CP::Variable::Type::REAL,
"header_value_{" + vertex->shortReference() +
"}," + header, value )
328 if ( messageDefinition->parameterMap.at(header)->expression ) {
329 auto headerValue = createExpression( vertex, *messageDefinition->parameterMap.at(header)->expression );
330 messageHeaderVariables.emplace(
333 model.addVariable(CP::Variable::Type::BOOLEAN,
"header_defined_{" + vertex->shortReference() +
"}," + header, visit.at(vertex) ),
334 model.addVariable(CP::Variable::Type::REAL,
"header_value_{" + vertex->shortReference() +
"}," + header, visit.at(vertex) * headerValue )
341 messageHeaderVariables.emplace(
344 model.addVariable(CP::Variable::Type::BOOLEAN,
"header_defined_{" + vertex->shortReference() +
"}," + header,
false ),
345 model.addVariable(CP::Variable::Type::REAL,
"header_value_{" + vertex->shortReference() +
"}," + header, 0.0 )
352void CPModel::createMessageContent(
const Vertex* vertex) {
355 if ( extensionElements->messageDefinitions.size() > 1 ) {
356 assert(!
"Not yet implemented");
359 auto messageDefinition = extensionElements->
messageDefinitions.size() == 1 ? extensionElements->messageDefinitions[0].get() :
nullptr;
360 assert( messageDefinition );
361 auto& messageContentVariables = messageContent[vertex];
365 for (
auto& [key,content] : messageDefinition->contentMap ) {
366 auto [defined,value] = getAttributeVariables(vertex, content->attribute);
367 messageContentVariables.emplace(
370 model.addVariable(CP::Variable::Type::BOOLEAN,
"content_defined_{" + vertex->shortReference() +
"}," + content->key, defined ),
371 model.addVariable(CP::Variable::Type::REAL,
"content_value_{" + vertex->shortReference() +
"}," + content->key, value )
378 for (
auto& [key,content] : messageDefinition->contentMap ) {
379 messageContentVariables.emplace(
382 model.addBinaryVariable(
"content_defined_{" + vertex->shortReference() +
"}," + content->key ),
383 model.addRealVariable(
"content_value_{" + vertex->shortReference() +
"}," + content->key )
392 for (
auto& attribute : extensionElements->data ) {
393 IndexedAttributeVariables variables(
394 model.addIndexedVariables(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->
shortReference() +
"}," + attribute->
id ),
395 model.addIndexedVariables(CP::Variable::Type::REAL,
"value_{" + vertex->
shortReference() +
"}," + attribute->
id )
398 auto given = scenario->getKnownValue(vertex->
rootId, attribute.get(), scenario->getEarliestInstantiationTime());
399 if ( given.has_value() ) {
401 model.addIndexedVariable(variables.defined, visit.at(vertex));
402 model.addIndexedVariable(variables.value, CP::if_then_else( visit.at(vertex), (
double)given.value(), 0.0));
406 assert( attribute->
expression->type == Model::Expression::Type::ASSIGN );
407 CP::Expression assignment = createExpression( vertex, *attribute->
expression );
408 model.addIndexedVariable(variables.defined, visit.at(vertex));
409 model.addIndexedVariable(variables.value, CP::if_then_else( visit.at(vertex), assignment, 0.0));
413 model.addIndexedVariable(variables.defined,
false,
false);
414 model.addIndexedVariable(variables.value, 0.0, 0.0);
420 for ( [[maybe_unused]]
auto _ : flattenedGraph->
dataModifiers.at(vertex) ) {
422 model.addIndexedVariable(variables.defined, variables.defined[0]);
423 model.addIndexedVariable(variables.value, variables.value[0]);
427 for ( [[maybe_unused]]
auto _ : flattenedGraph->
dataModifiers.at(vertex) ) {
429 model.addIndexedVariable(variables.defined);
430 model.addIndexedVariable(variables.value);
433 addToObjective( attribute.get(), variables.value[ variables.value.size() -1 ] );
434 data.emplace( std::make_pair(vertex, attribute.get()), std::move(variables) );
440 if ( extensionElements->data.empty() )
return;
443 auto& dataModifiers = flattenedGraph->
dataModifiers.at(vertex);
444 for (
auto& [entry,exit] : dataModifiers ) {
446 auto& [localStatus,localData,localGlobals] = locals.at(exit).back();
447 for (
unsigned int i = 0; i < dataModifiers.size(); i++ ) {
448 for (
auto& attribute : extensionElements->data ) {
456 auto& index = dataIndex.at(exit)[ exit->dataOwnerIndex(attribute.get()) ];
459 ( index == i ).implies(
460 data.at({vertex,attribute.get()}).defined[i] == localData[attribute->
index].defined
464 ( index == i ).implies(
465 data.at({vertex,attribute.get()}).value[i] == localData[attribute->
index].value
470 assert( entry->node->represents<
BPMN::Task>() );
472 auto& index = dataIndex.at(entry)[ entry->dataOwnerIndex(attribute.get()) ];
475 ( index == i ).implies(
476 data.at({vertex,attribute.get()}).defined[i + 1] == localData[attribute->
index].defined
480 ( index == i ).implies(
481 data.at({vertex,attribute.get()}).value[i + 1] == localData[attribute->
index].value
490void CPModel::constrainGlobalVariables() {
492 auto& [localStatus,localData,localGlobals] = locals.at(exit).back();
493 for (
unsigned int i = 0; i < flattenedGraph->
globalModifiers.size(); i++ ) {
494 for (
auto attribute : scenario->model->attributeRegistry.globalAttributes ) {
504 ( globalIndex.at(exit) == i && visit.at(exit) ).implies(
505 globals[attribute->
index].defined[i] == localGlobals[attribute->
index].defined
509 ( globalIndex.at(exit) == i && visit.at(exit) ).implies(
510 globals[attribute->
index].value[i] == localGlobals[attribute->
index].value
515 assert( entry->node->represents<
BPMN::Task>() );
519 ( globalIndex.at(entry) == i && visit.at(entry) ).implies(
520 globals[attribute->
index].defined[i + 1] == localGlobals[attribute->
index].defined
524 ( globalIndex.at(entry) == i && visit.at(entry) ).implies(
525 globals[attribute->
index].value[i + 1] == localGlobals[attribute->
index].value
537 CP::Expression outflows(0);
538 std::vector<CP::Expression> timers;
539 for (
auto& [sequenceFlow,event] : gateway->
outflows ) {
541 outflows = outflows + tokenFlow.at({gateway,
event});
546 model.addConstraint( outflows == visit.at(gateway) );
549 for (
auto& [sequenceFlow,event] : gateway->
outflows ) {
550 for (
auto& timer : timers ) {
552 visit.at(event).implies(
563void CPModel::constrainSequentialActivities() {
565 for (
size_t i = 0; i + 1 < sequentialActivities.size(); i++ ) {
566 for (
size_t j = i + 1; j < sequentialActivities.size(); j++ ) {
567 auto& [entry1,exit1] = sequentialActivities[i];
568 auto& [entry2,exit2] = sequentialActivities[j];
570 ( position.at(entry1) < position.at(entry2)).implies( position.at(exit1) < position.at(entry2) )
577void CPModel::createStatus(
const Vertex* vertex) {
579 if ( vertex->type == Vertex::Type::ENTRY ) {
580 createEntryStatus(vertex);
583 createExitStatus(vertex);
584 addObjectiveCoefficients( vertex );
588 assert( visit.contains(vertex) );
589 assert( status.contains(vertex) );
590 for (
auto& attributeVars : status.at(vertex) ) {
591 model.addConstraint( attributeVars.defined <= visit.at(vertex) );
592 model.addConstraint( (!attributeVars.defined).implies( attributeVars.value == 0.0 ) );
596void CPModel::addAttributes(
const Vertex* vertex, std::vector<AttributeVariables>& variables,
const BPMNOS::Model::Attribute* loopIndex) {
599 variables.reserve(extensionElements->attributeRegistry.statusAttributes.size());
600 for (
size_t i = variables.size(); i < extensionElements->attributeRegistry.statusAttributes.size(); i++) {
601 auto attribute = extensionElements->attributeRegistry.statusAttributes[i];
604 if (
auto given = scenario->getKnownValue(vertex->rootId, attribute, scenario->getEarliestInstantiationTime()); given.has_value() ) {
606 variables.emplace_back(
607 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, visit.at(vertex) ),
608 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->
id, CP::if_then_else( visit.at(vertex), (
double)given.value(), 0.0))
611 else if ( attribute == loopIndex ) {
614 variables.emplace_back(
615 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, visit.at(vertex) ),
616 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->
id, CP::if_then_else( visit.at(vertex), getLoopIndex(vertex), 0 ) )
621 assert( attribute->
expression->type == Model::Expression::Type::ASSIGN );
622 CP::Expression assignment = createExpression( vertex, *attribute->
expression );
623 variables.emplace_back(
624 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, visit.at(vertex) ),
625 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->
id, CP::if_then_else( visit.at(vertex), assignment, 0.0) )
631 variables.emplace_back(
632 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, defined, defined ),
633 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->
id, 0.0, 0.0)
640void CPModel::createEntryStatus(
const Vertex* vertex) {
642 status.emplace( vertex, std::vector<AttributeVariables>() );
643 auto& variables = status.at(vertex);
644 auto loopCharacteristics = getLoopCharacteristics(vertex);
646 if ( loopCharacteristics.has_value() && !flattenedGraph->
dummies.contains(vertex) ) {
647 createLoopEntryStatus(vertex);
661 if ( vertex->parent.has_value() ) {
666 assert( extensionElements );
668 assert( vertex->parent.value().first->node == scope );
669 assert( status.contains(vertex->parent.value().first) );
670 variables = createUniquelyDeducedEntryStatus(vertex, extensionElements->attributeRegistry, status.at(vertex->parent.value().first) );
675 assert( status.contains(vertex->parent.value().first) );
678 variables = createUniquelyDeducedEntryStatus(vertex, attributeRegistry, status.at(vertex->parent.value().first) );
682 std::vector< std::pair<const CP::Variable&, std::vector<AttributeVariables>& > > alternatives;
683 for (
auto& [sequenceFlow,predecessor] : vertex->inflows ) {
685 assert( statusFlow.contains({predecessor,vertex}) );
686 alternatives.emplace_back( tokenFlow.at({predecessor,vertex}), statusFlow.at({predecessor,vertex}) );
688 variables = createAlternativeEntryStatus(vertex, extensionElements->attributeRegistry, std::move(alternatives));
690 else if ( vertex->entry<
BPMN::FlowNode>() && vertex->inflows.size() > 1 ) {
692 assert( vertex->predecessors.size() == 1 );
694 throw std::runtime_error(
"CPModel: illegal join at '" + vertex->node->id +
"'");
696 std::vector< std::pair<const CP::Variable&, std::vector<AttributeVariables>& > > inputs;
697 for (
auto& [sequenceFlow,predecessor] : vertex->inflows ) {
699 inputs.emplace_back( tokenFlow.at({predecessor,vertex}), statusFlow.at({predecessor,vertex}) );
701 variables = createMergedStatus(vertex, extensionElements->attributeRegistry, std::move(inputs));
704 assert( vertex->inflows.size() == 1 );
705 auto& [sequenceFlow,predecessor] = vertex->inflows.front();
706 if ( sequenceFlow ) {
707 assert( statusFlow.contains({predecessor,vertex}) );
708 variables = createUniquelyDeducedEntryStatus(vertex, extensionElements->attributeRegistry, statusFlow.at({predecessor,vertex}) );
713 assert( status.contains(predecessor) );
714 variables = createUniquelyDeducedEntryStatus(vertex, extensionElements->attributeRegistry, status.at(predecessor) );
720 flattenedGraph->
dummies.contains(vertex) &&
721 loopCharacteristics.has_value() &&
728 addAttributes(vertex,variables);
731void CPModel::createExitStatus(
const Vertex* vertex) {
735 if (
auto loopCharacteristics = getLoopCharacteristics(vertex);
736 loopCharacteristics.has_value() &&
737 flattenedGraph->
dummies.contains(vertex)
741 status.emplace( vertex, createLoopExitStatus(vertex) );
745 std::vector< std::pair<const CP::Variable&, std::vector<AttributeVariables>& > > inputs;
746 for (
auto& [_,multiInstanceExit] : vertex->inflows ) {
747 inputs.emplace_back( visit.at(multiInstanceExit), status.at(multiInstanceExit) );
749 status.emplace( vertex, createMergedStatus(vertex, extensionElements->attributeRegistry, std::move(inputs)) );
755 if ( vertex->inflows.empty() ) {
756 throw std::runtime_error(
"CPModel: empty scopes are not supported");
760 auto getEndVertices = [&]() {
761 std::vector< const Vertex* > endVertices;
762 for (
auto& [_,candidate] : vertex->inflows ) {
764 if ( visit.contains(candidate) ) {
765 endVertices.push_back(candidate);
768 if ( endVertices.empty() ) {
769 throw std::runtime_error(
"CPModel: unable to determine end nodes for scope '" + vertex->node->id +
"'");
773 std::vector< std::pair<const CP::Variable&, std::vector<AttributeVariables>& > > inputs;
774 for (
auto endVertex : getEndVertices() ) {
775 assert(visit.contains(endVertex));
776 assert(status.contains(endVertex));
777 inputs.emplace_back( visit.at(endVertex), status.at(endVertex) );
780 assert(extensionElements);
781 status.emplace( vertex, createMergedStatus(vertex, extensionElements->attributeRegistry, std::move(inputs)) );
789 const Vertex* entryVertex = entry(vertex);
794 auto& entryStatus = status.at(entryVertex);
795 std::vector<AttributeVariables> variables;
798 for (
auto attribute : extensionElements->attributeRegistry.statusAttributes ) {
802 variables.emplace_back(
803 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, entryStatus[attribute->
index].defined ),
804 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->
id, entryStatus[attribute->
index].defined * CP::max( entryStatus[attribute->
index].value, trigger ) )
808 variables.emplace_back(
809 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, entryStatus[attribute->
index].defined ),
810 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->
id, entryStatus[attribute->
index].value )
814 status.emplace( vertex, std::move(variables) );
817 else if ( !extensionElements ) {
821 auto& entryStatus = status.at(entryVertex);
822 std::vector<AttributeVariables> variables;
823 for (
auto attribute : extensionElements->attributeRegistry.statusAttributes ) {
826 variables.emplace_back(
827 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, entryStatus[attribute->
index].defined ),
828 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->
id, entryStatus[attribute->
index].value )
831 status.emplace( vertex, std::move(variables) );
837 createLocalAttributeVariables(vertex);
839 auto& [ localStatus, localData, localGlobals ] = locals.at(vertex).back();
842 std::vector<AttributeVariables> variables;
843 for (
auto attribute : extensionElements->attributeRegistry.statusAttributes ) {
844 auto& [ defined, value ] = localStatus.at(attribute->
index);
847 variables.emplace_back(
848 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, visit.at(vertex) ),
849 model.addRealVariable(
"value_{" + vertex->reference() +
"}," + attribute->
id )
851 auto& timestamp = variables.back().value;
852 model.addConstraint( visit.at(vertex).implies( timestamp >= value ) );
855 variables.emplace_back(
856 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, defined ),
857 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->
id, value )
861 status.emplace( vertex, std::move(variables) );
866 assert( status.contains(entryVertex) );
867 std::vector<AttributeVariables> currentStatus = status.at(entryVertex);
879 assert( extensionElements );
881 if ( extensionElements->messageDefinitions.size() > 1 ) {
882 assert(!
"Not yet implemented");
885 auto messageDefinition = extensionElements->messageDefinitions.size() == 1 ? extensionElements->messageDefinitions[0].get() :
nullptr;
886 assert( messageContent.contains(vertex) );
887 auto& messageContentVariables = messageContent.at(vertex);
890 std::vector<AttributeVariables> variables;
891 auto& entryStatus = status.at( entry(vertex) );
892 for (
auto attribute : extensionElements->attributeRegistry.statusAttributes ) {
895 variables.emplace_back(
896 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, visit.at(vertex) ),
897 model.addRealVariable(
"value_{" + vertex->reference() +
"}," + attribute->
id )
900 else if (
auto content = findContent(messageDefinition, attribute) ) {
901 auto& [ defined, value ] = messageContentVariables.at(content->key);
905 throw std::runtime_error(
"CPModel: timestamp must not be changed by message content");
908 variables.emplace_back(
909 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, defined ),
910 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->
id, value )
914 auto& [ defined, value ] = entryStatus.at(attribute->
index);
915 variables.emplace_back(
916 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, defined ),
917 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->
id, value )
921 status.emplace( vertex, std::move(variables) );
930 std::vector<AttributeVariables> variables;
931 for (
auto attribute : extensionElements->attributeRegistry.statusAttributes ) {
932 assert( attribute->
index == variables.size() );
933 variables.emplace_back(
934 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, currentStatus[attribute->
index].defined ),
935 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->
id, currentStatus[attribute->
index].value )
939 status.emplace( vertex, std::move(variables) );
942std::pair< CP::Expression, CP::Expression > CPModel::getLocalAttributeVariables(
const Model::Attribute* attribute, std::tuple< std::vector<AttributeVariables>, std::vector<AttributeVariables>, std::vector<AttributeVariables> >& localVariables ) {
943 auto& [ status, data, globals ] = localVariables;
944 if ( attribute->
category == Model::Attribute::Category::STATUS ) {
945 return std::make_pair<CP::Expression,CP::Expression>(
946 status[attribute->
index].defined,
947 status[attribute->
index].value
950 else if ( attribute->
category == Model::Attribute::Category::DATA ) {
951 return std::make_pair<CP::Expression,CP::Expression>(
952 data[attribute->
index].defined,
953 data[attribute->
index].value
957 assert( attribute->
category == Model::Attribute::Category::GLOBAL );
958 return std::make_pair<CP::Expression,CP::Expression>(
959 globals[attribute->
index].defined,
960 globals[attribute->
index].value
965CP::Expression CPModel::createOperatorExpression(
const Model::Expression& operator_, std::tuple< std::vector<AttributeVariables>, std::vector<AttributeVariables>, std::vector<AttributeVariables> >& localVariables ) {
968 std::vector<CP::Expression> variables;
969 for (
auto& variableName : compiled.getVariables() ) {
971 if( attribute->
type == ValueType::COLLECTION ) {
972 throw std::runtime_error(
"CPModel: illegal operator expression '" + operator_.
expression +
"'");
975 auto [defined,value] = getLocalAttributeVariables(attribute,localVariables);
976 variables.push_back( value );
979 std::vector<CP::Expression> collectionVariables;
980 for (
auto& variableName : compiled.getCollections() ) {
982 if( attribute->
type != ValueType::COLLECTION ) {
983 throw std::runtime_error(
"CPModel: illegal operator expression '" + operator_.
expression +
"'");
986 auto [defined,value] = getLocalAttributeVariables(attribute,localVariables);
987 collectionVariables.push_back( value );
990 return compiled.evaluate(variables,collectionVariables);
996 if ( choices.empty() )
return nullptr;
997 auto it = std::find_if(
1000 [&](
const auto& choice) {
1002 return choice->attribute == attribute;
1005 return ( it != choices.end() ? it->get() :
nullptr );
1009 if ( !messageDefinition )
return nullptr;
1010 auto it = std::find_if(
1013 [&](
const auto& mapEntry) {
1014 auto& [key, content] = mapEntry;
1015 return content->attribute == attribute;
1018 return ( it != messageDefinition->
contentMap.end() ? it->second.get() :
nullptr );
1022void CPModel::createLocalAttributeVariables(
const Vertex* vertex) {
1029 throw std::runtime_error(
"CPModel: illegal typed start event");
1031 auto extensionElements =
1038 if ( messageDefinitions.size() > 1 ) {
1039 assert(!
"Not yet implemented");
1042 auto messageDefinition =
1044 messageDefinitions[0].get() :
1048 auto localAttributeVariables = [&](
const auto& attributes) -> std::vector<AttributeVariables> {
1049 std::vector<AttributeVariables> variables;
1053 auto entryVertex = entry(vertex);
1055 return std::make_pair<CP::Expression,CP::Expression>(
1056 status.at(entryVertex)[attribute->
index].defined,
1057 status.at(entryVertex)[attribute->
index].value
1061 auto& index = dataIndex.at(entryVertex)[ entryVertex->dataOwnerIndex(attribute) ];
1062 return std::make_pair<CP::Expression,CP::Expression>(
1063 data.at( {entryVertex->dataOwner(attribute).first, attribute } ).defined[ index ],
1064 data.at( {entryVertex->dataOwner(attribute).first, attribute } ).value[ index ]
1069 auto& index = globalIndex.at(entryVertex);
1070 return std::make_pair<CP::Expression,CP::Expression>(
1071 globals[attribute->
index].defined[ index ],
1072 globals[attribute->
index].value[ index ]
1077 for (
auto attribute : attributes ) {
1079 auto [defined,value] = initialVariables(attribute);
1081 if (
auto choice = findChoice(extensionElements->choices, attribute) ) {
1086 throw std::runtime_error(
"CPModel: timestamp must not be a choice");
1090 variables.emplace_back(
1091 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->shortReference() +
",0}," + attribute->
id, visit.at(vertex) ),
1092 model.addRealVariable(
"value_{" + vertex->shortReference() +
",0}," + attribute->
id )
1094 auto& variable = variables.back().value;
1098 if ( choice->lowerBound.has_value() ) {
1099 auto& [LB,strictLB] = choice->lowerBound.value();
1101 model.addConstraint( visit.at(vertex).implies( variable > createExpression(entry(vertex),*LB) ) );
1104 model.addConstraint( visit.at(vertex).implies( variable >= createExpression(entry(vertex),*LB) ) );
1107 if ( choice->upperBound.has_value() ) {
1108 auto& [UB,strictUB] = choice->upperBound.value();
1110 model.addConstraint( visit.at(vertex).implies( variable < createExpression(entry(vertex),*UB) ) );
1113 model.addConstraint( visit.at(vertex).implies( variable <= createExpression(entry(vertex),*UB) ) );
1116 if ( choice->multipleOf ) {
1117 discretizerMap.emplace(
1118 std::make_pair(vertex, attribute),
1119 model.addIntegerVariable(
"discretizer_{" + vertex->reference() +
"}," + attribute->
id)
1121 auto& discretizer = discretizerMap.at({vertex, attribute});
1122 model.addConstraint( (!visit.at(vertex)).implies( discretizer == 0.0 ) );
1123 model.addConstraint( visit.at(vertex).implies( variable == discretizer * createExpression(entry(vertex),*choice->multipleOf) ) );
1125 if ( !choice->enumeration.empty() ) {
1126 CP::Expression enumerationContainsVariable(
false);
1127 for (
auto& expression : choice->enumeration ) {
1128 enumerationContainsVariable = enumerationContainsVariable || ( variable == createExpression(entry(vertex),*expression) );
1130 model.addConstraint( visit.at(vertex).implies( enumerationContainsVariable ) );
1134 else if (
auto content = findContent(messageDefinition, attribute) ) {
1140 throw std::runtime_error(
"CPModel: timestamp must not be changed by message content");
1142 assert(messageContent.contains(vertex));
1143 assert(messageContent.at(vertex).contains(content->key));
1145 variables.emplace_back(
1146 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->shortReference() +
",0}," + attribute->
id, messageContent.at(vertex).at(content->key).defined ),
1147 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->shortReference() +
",0}," + attribute->
id, messageContent.at(vertex).at(content->key).value )
1156 variables.emplace_back(
1157 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->shortReference() +
",0}," + attribute->
id, visit.at(vertex) ),
1158 model.addRealVariable(
"value_{" + vertex->shortReference() +
",0}," + attribute->
id )
1160 auto& timestamp = variables.back().value;
1165 variables.emplace_back(
1166 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->shortReference() +
",0}," + attribute->
id, defined ),
1167 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->shortReference() +
",0}," + attribute->
id, value )
1180 localAttributeVariables(extensionElements->attributeRegistry.statusAttributes),
1181 localAttributeVariables(extensionElements->attributeRegistry.dataAttributes),
1182 localAttributeVariables(extensionElements->attributeRegistry.globalAttributes)
1187 auto& current = locals.at(vertex);
1188 for (
auto& operator_ : extensionElements->operators ) {
1189 auto& [ currentStatus, currentData, currentGlobals ] = current.back();
1191 auto updatedLocalAttributeVariables = [&](
const auto& attributes, std::vector<AttributeVariables>& attributeVariables ) -> std::vector<AttributeVariables> {
1192 std::vector<AttributeVariables> variables;
1193 for (
auto attribute : attributes ) {
1194 if ( attribute == operator_->attribute ) {
1196 if ( operator_->
expression.type == Model::Expression::Type::UNASSIGN ) {
1197 variables.emplace_back(
1198 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->shortReference() +
"," + std::to_string(current.size()) +
"}," + attribute->
id, CP::if_then_else( visit.at(vertex),
false, attributeVariables[attribute->
index].defined) ),
1199 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->shortReference() +
"," + std::to_string(current.size()) +
"}," + attribute->
id, CP::if_then_else( visit.at(vertex), 0.0, attributeVariables[attribute->
index].value) )
1202 else if ( operator_->
expression.type == Model::Expression::Type::ASSIGN ) {
1203 CP::Expression value = createOperatorExpression( operator_->
expression, current.back() );
1204 variables.emplace_back(
1205 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->shortReference() +
"," + std::to_string(current.size()) +
"}," + attribute->
id, CP::if_then_else( visit.at(vertex),
true, attributeVariables[attribute->
index].defined) ),
1206 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->shortReference() +
"," + std::to_string(current.size()) +
"}," + attribute->
id, CP::if_then_else( visit.at(vertex), value, attributeVariables[attribute->
index].value) )
1211 throw std::runtime_error(
"CPModel: illegal operator expression: " + operator_->
expression.expression );
1216 variables.emplace_back(
1217 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->shortReference() +
"," + std::to_string(current.size()) +
"}," + attribute->
id, attributeVariables[attribute->
index].defined ),
1218 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->shortReference() +
"," + std::to_string(current.size()) +
"}," + attribute->
id, attributeVariables[attribute->
index].value )
1226 current.emplace_back(
1227 updatedLocalAttributeVariables(extensionElements->attributeRegistry.statusAttributes,currentStatus),
1228 updatedLocalAttributeVariables(extensionElements->attributeRegistry.dataAttributes,currentData),
1229 updatedLocalAttributeVariables(extensionElements->attributeRegistry.globalAttributes,currentGlobals)
1234std::vector<CPModel::AttributeVariables> CPModel::createUniquelyDeducedEntryStatus(
const Vertex* vertex,
const BPMNOS::Model::AttributeRegistry& attributeRegistry, std::vector<AttributeVariables>& inheritedStatus) {
1236 assert( vertex->type == Vertex::Type::ENTRY );
1239 std::vector<AttributeVariables> variables;
1243 assert( variables.size() == attribute->
index );
1244 auto& [ defined, value ] = inheritedStatus.at(attribute->
index);
1251 variables.emplace_back(
1252 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, visit.at(vertex) ),
1253 model.addRealVariable(
"value_{" + vertex->reference() +
"}," + attribute->
id )
1255 auto& timestamp = variables.back().value;
1256 model.addConstraint( visit.at(vertex).implies( timestamp >= value ) );
1260 CP::Expression timestamp(0);
1261 for (
auto predecessor : vertex->predecessors ) {
1262 timestamp = CP::max( timestamp, status.at(predecessor)[attribute->
index].value );
1265 variables.emplace_back(
1266 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, visit.at(vertex) ),
1267 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->
id, visit.at(vertex) * timestamp )
1272 variables.emplace_back(
1273 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, visit.at(vertex) * defined ),
1274 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->
id, visit.at(vertex) * value )
1280 variables.emplace_back(
1281 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, defined ),
1282 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->
id, value )
1291std::vector<CPModel::AttributeVariables> CPModel::createAlternativeEntryStatus(
const Vertex* vertex,
const BPMNOS::Model::AttributeRegistry& attributeRegistry, std::vector< std::pair<
const CP::Variable&, std::vector<AttributeVariables>& > > alternatives) {
1292 assert( vertex->type == Vertex::Type::ENTRY );
1295 std::vector<AttributeVariables> variables;
1299 CP::Expression defined(
false);
1300 CP::Expression value(0.0);
1301 for (
auto& [ active, attributeVariables] : alternatives ) {
1302 assert( attributeVariables.size() == attributeRegistry.
statusAttributes.size() );
1303 defined = defined || attributeVariables[attribute->
index].defined;
1304 value = value + attributeVariables[attribute->
index].defined * attributeVariables[attribute->
index].value;
1306 variables.emplace_back(
1307 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, defined ),
1308 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->
id, value )
1316std::vector<CPModel::AttributeVariables> CPModel::createMergedStatus(
const Vertex* vertex,
const BPMNOS::Model::AttributeRegistry& attributeRegistry, std::vector< std::pair<
const CP::Variable&, std::vector<AttributeVariables>& > > inputs) {
1319 std::vector<AttributeVariables> variables;
1323 std::vector<CP::Expression> terms;
1324 assert( !inputs.empty() );
1325 for (
auto& [ active, attributeVariables] : inputs ) {
1326 terms.emplace_back( attributeVariables[attribute->
index].defined * attributeVariables[attribute->
index].value );
1330 variables.emplace_back(
1331 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, visit.at(vertex) ),
1332 model.addRealVariable(
"value_{" + vertex->reference() +
"}," + attribute->
id )
1334 model.addConstraint( visit.at(vertex).implies( variables.back().value >= CP::max(terms) ) );
1338 variables.emplace_back(
1339 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, visit.at(vertex) ),
1340 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->
id, CP::if_then_else( visit.at(vertex), CP::max(terms), 0.0 ) )
1348 CP::Expression exists(
false);
1349 for (
auto& [ _, attributeVariables] : inputs ) {
1351 exists = exists || attributeVariables[attribute->
index].defined;
1353 CP::Expression allSame(
true);
1355 for (
size_t i = 0; i + 1 < inputs.size(); i++ ) {
1356 for (
size_t j = i + 1; j < inputs.size(); j++ ) {
1357 auto& someVariables = inputs[i].second;
1358 auto& otherVariables = inputs[j].second;
1359 allSame = allSame && CP::if_then_else( someVariables[attribute->
index].defined && otherVariables[attribute->
index].defined, someVariables[attribute->
index].value == otherVariables[attribute->
index].value,
true);
1362 auto& defined = model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->
id, exists && allSame);
1366 for (
auto& [ active, attributeVariables] : inputs ) {
1367 cases.emplace_back( attributeVariables[attribute->
index].defined, attributeVariables[attribute->
index].value );
1369 auto& value = model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->
id, CP::if_then_else( defined, CP::n_ary_if( cases, 0.0 ), 0.0) );
1371 variables.emplace_back( defined, value );
1377CP::Expression CPModel::getLoopIndex(
const Vertex* vertex) {
1378 auto predecessor = vertex->inflows.front().second;
1382 extensionElements->loopIndex.has_value() ?
1383 extensionElements->loopIndex.value()->
expression->isAttribute() :
1386 if ( attribute->index >= status.at(predecessor).size() ) {
1389 auto& [ defined, value ] = status.at(predecessor).at(attribute->index);
1394 for ( ; i < predecessor->outflows.size(); i++ ) {
1395 if ( vertex == predecessor->outflows[i].second ) {
1403void CPModel::createLoopEntryStatus(
const Vertex* vertex) {
1405 assert( vertex->inflows.size() == 1 );
1406 assert( status.contains(vertex) );
1407 auto& variables = status.at(vertex);
1408 auto predecessor = vertex->inflows.front().second;
1409 auto& priorStatus = status.at(predecessor);
1413 extensionElements->loopIndex.has_value() ?
1414 extensionElements->loopIndex.value()->
expression->isAttribute() :
1420 variables.reserve( extensionElements->attributeRegistry.statusAttributes.size() );
1422 for (
size_t index = 0; index < priorStatus.size(); index++ ) {
1423 auto attribute = extensionElements->attributeRegistry.statusAttributes[index];
1424 assert( variables.size() == attribute->index );
1426 bool ownedByLoop = (index >= extensionElements->attributeRegistry.statusAttributes.size() - extensionElements->attributes.size());
1429 auto& [ defined, value ] = priorStatus.at(attribute->index);
1432 variables.emplace_back(
1433 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->id, visit.at(vertex) ),
1434 model.addRealVariable(
"value_{" + vertex->reference() +
"}," + attribute->id )
1436 auto& timestamp = variables.back().value;
1437 model.addConstraint( visit.at(vertex).implies( timestamp >= value ) );
1439 else if ( ownedByLoop && attribute == loopIndex ) {
1441 variables.emplace_back(
1442 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->id, visit.at(vertex) ),
1443 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->id, CP::if_then_else( visit.at(vertex), getLoopIndex(vertex), 0 ) )
1446 else if ( ownedByLoop && attribute->expression ) {
1448 assert( attribute->expression->type == Model::Expression::Type::ASSIGN );
1449 CP::Expression assignment = createExpression( vertex, *attribute->expression );
1450 variables.emplace_back(
1451 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->id, visit.at(vertex) ),
1452 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->id, CP::if_then_else( visit.at(vertex), assignment, 0.0) )
1457 variables.emplace_back(
1458 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->id, CP::if_then_else( visit.at(vertex), defined,
false ) ),
1459 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->id, CP::if_then_else( visit.at(vertex), value, 0.0 ) )
1464 if ( priorStatus.size() < extensionElements->attributeRegistry.statusAttributes.size() ) {
1465 addAttributes(vertex,variables,loopIndex);
1470std::vector<CPModel::AttributeVariables> CPModel::createLoopExitStatus(
const Vertex* vertex) {
1471 assert( vertex->inflows.size() >= 1 );
1474 std::vector<AttributeVariables> variables;
1476 variables.reserve( extensionElements->attributeRegistry.statusAttributes.size() );
1478 for (
auto attribute : extensionElements->attributeRegistry.statusAttributes ) {
1479 CP::Expression defined(
false);
1480 CP::Expression value(0.0);
1481 for (
size_t i = 0; i < vertex->inflows.size(); i++ ) {
1482 auto predecessor = vertex->inflows[i].second;
1484 CP::Expression condition = ( i + 1 < vertex->inflows.size() ) ? visit.at(predecessor) && !visit.at(vertex->inflows[i+1].second) : visit.at(predecessor);
1486 auto &priorStatus = status.at(predecessor);
1487 defined = defined || ( condition * priorStatus.at(attribute->index).defined );
1488 value = value + ( condition * priorStatus.at(attribute->index).value );
1490 variables.emplace_back(
1491 model.addVariable(CP::Variable::Type::BOOLEAN,
"defined_{" + vertex->reference() +
"}," + attribute->id, defined ),
1492 model.addVariable(CP::Variable::Type::REAL,
"value_{" + vertex->reference() +
"}," + attribute->id, value )
1502 createGlobalIndexVariable(vertex);
1504 createDataIndexVariables(vertex);
1506 if ( vertex->
type == Vertex::Type::ENTRY ) {
1508 createEntryVariables(vertex);
1513 createDataVariables(vertex);
1518 createMessageContent(vertex);
1522 createStatus(vertex);
1526 createMessageHeader(vertex);
1531 createMessageContent(vertex);
1534 if ( vertex->
type == Vertex::Type::EXIT ) {
1535 createExitVariables(vertex);
1539 createSequenceConstraints(vertex);
1542 createRestrictions(vertex);
1547std::pair< CP::Expression, CP::Expression > CPModel::getAttributeVariables(
const Vertex* vertex,
const Model::Attribute* attribute) {
1548 if ( attribute->
category == Model::Attribute::Category::STATUS ) {
1549 assert( status.contains(vertex) );
1550 assert( attribute->
index < status.at(vertex).size() );
1551 return std::make_pair<CP::Expression,CP::Expression>(
1552 status.at(vertex)[attribute->
index].defined,
1553 status.at(vertex)[attribute->
index].value
1556 else if ( attribute->
category == Model::Attribute::Category::DATA ) {
1558 return std::make_pair<CP::Expression,CP::Expression>(
1560 (
double)vertex->instanceId
1563 assert( data.contains( {vertex->dataOwner(attribute).first, attribute } ) );
1565 return std::make_pair<CP::Expression,CP::Expression>(
1566 data.at( {vertex->dataOwner(attribute).first, attribute } ).defined[ 0 ],
1567 data.at( {vertex->dataOwner(attribute).first, attribute } ).value[ 0 ]
1571 auto& index = dataIndex.at(vertex)[ vertex->dataOwnerIndex(attribute) ];
1572 return std::make_pair<CP::Expression,CP::Expression>(
1573 data.at( {vertex->dataOwner(attribute).first, attribute } ).defined[ index ],
1574 data.at( {vertex->dataOwner(attribute).first, attribute } ).value[ index ]
1579 assert( attribute->
category == Model::Attribute::Category::GLOBAL );
1580 assert( globals.size() > attribute->
index );
1582 return std::make_pair<CP::Expression,CP::Expression>(
1583 globals[attribute->
index].defined[ 0 ],
1584 globals[attribute->
index].value[ 0 ]
1588 auto& index = globalIndex.at(vertex);
1589 return std::make_pair<CP::Expression,CP::Expression>(
1590 globals[attribute->
index].defined[ index ],
1591 globals[attribute->
index].value[ index ]
1601 assert( vertex->
inflows.size() == 1 );
1603 assert( vertex->
senders.empty() );
1606 auto& deducedVisit = model.addVariable(CP::Variable::Type::BOOLEAN,
"visit_{" + vertex->
shortReference() +
"}", visit.at( vertex->
parent.value().first ) );
1607 visit.emplace(vertex, deducedVisit );
1608 visit.emplace(exit(vertex), deducedVisit );
1613 typedStartEvent->isInterrupting ||
1616 throw std::runtime_error(
"CPModel: typed start event '" + typedStartEvent->id +
"' is not supported");
1619 CP::Expression messageDelivered(
false);
1620 for (
auto sender : exit(vertex)->senders ) {
1621 assert( messageFlow.contains({sender,exit(vertex)}) );
1622 messageDelivered = messageDelivered || messageFlow.at({sender,exit(vertex)});
1624 auto& deducedVisit = model.addVariable(CP::Variable::Type::BOOLEAN,
"visit_{" + vertex->
shortReference() +
"}", messageDelivered );
1625 visit.emplace(vertex, deducedVisit );
1626 visit.emplace(exit(vertex), deducedVisit );
1629 model.addConstraint( visit.at(vertex) <= visit.at(predecessor) );
1634 if (
auto loopCharacteristics = getLoopCharacteristics(vertex);
1635 loopCharacteristics.has_value() &&
1636 !flattenedGraph->
dummies.contains(vertex)
1641 auto predecessor = vertex->
inflows.front().second;
1642 if ( flattenedGraph->
dummies.contains(predecessor) ) {
1644 auto& deducedVisit = model.addVariable(CP::Variable::Type::BOOLEAN,
"visit_{" + vertex->
shortReference() +
"}" , visit.at( predecessor ) );
1645 visit.emplace(vertex, deducedVisit );
1646 visit.emplace(exit(vertex), deducedVisit );
1651 if ( extensionElements->loopCondition.has_value() ) {
1653 auto condition = createExpression(predecessor, *extensionElements->loopCondition.value()->expression);
1654 auto& deducedVisit = model.addVariable(CP::Variable::Type::BOOLEAN,
"visit_{" + vertex->
shortReference() +
"}" , visit.at( predecessor ) && condition );
1655 visit.emplace(vertex, deducedVisit );
1656 visit.emplace(exit(vertex), deducedVisit );
1661 auto& deducedVisit = model.addVariable(CP::Variable::Type::BOOLEAN,
"visit_{" + vertex->
shortReference() +
"}" , visit.at( predecessor ) );
1662 visit.emplace(vertex, deducedVisit );
1663 visit.emplace(exit(vertex), deducedVisit );
1670 auto predecessor = vertex->
inflows.front().second;
1672 assert( extensionElements->loopCardinality.has_value() );
1673 CP::Expression cardinality = createExpression( predecessor, *extensionElements->loopCardinality.value()->expression );
1674 auto index = getLoopIndex(vertex);
1675 auto& deducedVisit = model.addVariable(CP::Variable::Type::BOOLEAN,
"visit_{" + vertex->
shortReference() +
"}" , visit.at( predecessor ) && ( index <= cardinality ) );
1676 visit.emplace(vertex, deducedVisit );
1677 visit.emplace(exit(vertex), deducedVisit );
1682 else if ( vertex->
inflows.size() == 1 ) {
1684 auto& [sequenceFlow, predecessor] = vertex->
inflows.front();
1685 if ( sequenceFlow ) {
1687 auto& deducedVisit = model.addVariable(CP::Variable::Type::BOOLEAN,
"visit_{" + vertex->
shortReference() +
"}" , tokenFlow.at( std::make_pair(predecessor, vertex) ) );
1688 visit.emplace(vertex, deducedVisit );
1689 visit.emplace(exit(vertex), deducedVisit );
1696 auto& deducedVisit = model.addVariable(CP::Variable::Type::BOOLEAN,
"visit_{" + vertex->
shortReference() +
"}" , visit.at( predecessor ) );
1697 visit.emplace(vertex, deducedVisit );
1698 visit.emplace(exit(vertex), deducedVisit );
1702 else if ( vertex->
inflows.size() > 1 ) {
1705 throw std::runtime_error(
"CPModel: converging gateways must be explicit");
1709 CP::Expression incomingToken(
false);
1710 for (
auto& [sequenceFlow, predecessor] : vertex->
inflows ) {
1711 incomingToken = incomingToken || tokenFlow.at({predecessor,vertex});
1713 auto& deducedVisit = model.addVariable(CP::Variable::Type::BOOLEAN,
"visit_{" + vertex->
shortReference() +
"}" , incomingToken );
1714 visit.emplace(vertex, deducedVisit );
1715 visit.emplace(exit(vertex), deducedVisit );
1721 assert( vertex->
inflows.empty() );
1723 throw std::logic_error(
"CPModel: Every vertex except process entry should have inflow!");
1729 auto& knownVisit = model.addVariable(CP::Variable::Type::BOOLEAN,
"visit_{" + vertex->
shortReference() +
"}",
true,
true );
1731 visit.emplace(vertex, knownVisit );
1732 visit.emplace(exit(vertex), knownVisit );
1736void CPModel::createExitVariables(
const Vertex* vertex) {
1739 if (
auto loopCharacteristics = getLoopCharacteristics(vertex);
1740 loopCharacteristics.has_value() &&
1743 if ( vertex->loopIndices.size() == flattenedGraph->
loopIndexAttributes.at(vertex->node).size() ) {
1749 if ( vertex->outflows.size() == 1 ) {
1750 auto& [sequenceFlow,target] = vertex->outflows.front();
1751 if ( sequenceFlow ) {
1752 createSequenceFlowVariables( vertex, target );
1755 else if ( vertex->outflows.size() > 1 ) {
1758 throw std::runtime_error(
"CPModel: diverging gateways must be explicit");
1762 for (
auto& [sequenceFlow,target] : vertex->outflows ) {
1764 std::make_pair(vertex,target),
1765 model.addBinaryVariable(
"tokenflow_{" + vertex->reference() +
" → " + target->reference() +
"}" )
1767 createStatusFlowVariables(vertex,target);
1771 throw std::runtime_error(
"CPModel: complex gateways are not supported");
1775 for (
auto& [sequenceFlow,target] : vertex->outflows ) {
1776 assert( sequenceFlow );
1777 createSequenceFlowVariables( vertex, target );
1782 for (
auto& [sequenceFlow,target] : vertex->outflows ) {
1783 assert( sequenceFlow );
1787 CP::Expression outflows(0);
1788 for (
auto& [sequenceFlow,target] : vertex->outflows ) {
1789 outflows = outflows + tokenFlow.at({vertex,target});
1791 model.addConstraint( outflows == visit.at(vertex) );
1799void CPModel::createSequenceFlowVariables(
const Vertex* source,
const Vertex* target,
const BPMNOS::Model::Gatekeeper* gatekeeper) {
1801 CP::Expression gatekeeperCondition(
true);
1802 for (
auto& condition : gatekeeper->
conditions ) {
1803 gatekeeperCondition = gatekeeperCondition && createExpression(source,condition->expression);
1806 std::make_pair(source,target),
1807 model.addVariable(CP::Variable::Type::BOOLEAN,
"tokenflow_{" + source->reference() +
" → " + target->reference() +
"}", visit.at(source) && gatekeeperCondition )
1812 std::make_pair(source,target),
1813 model.addVariable(CP::Variable::Type::BOOLEAN,
"tokenflow_{" + source->reference() +
" → " + target->reference() +
"}", visit.at(source) )
1817 createStatusFlowVariables(source,target);
1820void CPModel::createStatusFlowVariables(
const Vertex* source,
const Vertex* target) {
1825 assert( extensionElements );
1826 std::vector<AttributeVariables> variables;
1827 variables.reserve( extensionElements->attributeRegistry.statusAttributes.size() );
1828 for (
auto attribute : extensionElements->attributeRegistry.statusAttributes ) {
1830 assert( tokenFlow.contains({source,target}) );
1831 assert( status.contains(source) );
1833 assert( attribute->
index < status.at(source).size() );
1835 variables.emplace_back(
1836 model.addVariable(CP::Variable::Type::BOOLEAN,
"statusflow_defined_{" + source->reference() +
" → " + target->reference() +
"}," + attribute->
id,
1838 tokenFlow.at({source,target}),
1839 status.at(source)[attribute->
index].defined,
1843 model.addVariable(CP::Variable::Type::REAL,
"statusflow_value_{" + source->reference() +
" → " + target->reference() +
"}," + attribute->
id,
1845 tokenFlow.at({source,target}),
1846 status.at(source)[attribute->
index].value,
1854 std::make_pair(source,target),
1855 std::move(variables)
1859void CPModel::createSequenceConstraints(
const Vertex* vertex) {
1860 auto addConstraints = [&](
const Vertex* predecessor) {
1861 assert( position.contains(predecessor) );
1862 assert( position.contains(vertex) );
1863 assert( visit.contains(vertex) );
1864 assert( status.contains(predecessor) );
1866 assert( status.contains(vertex) );
1869 model.addConstraint( position.at(vertex) == position.at(entry(vertex)) + 1 );
1872 model.addConstraint( position.at(predecessor) < position.at(vertex) );
1880 if ( predecessor->node == vertex->node ) {
1881 model.addConstraint(
1883 ( visit.at(vertex) ).implies(
1890 model.addConstraint(
1892 ( visit.at(predecessor) && visit.at(vertex) ).implies(
1902 for (
auto& [_, predecessor] : vertex->inflows ) {
1903 addConstraints(predecessor);
1906 for (
auto predecessor : vertex->predecessors ) {
1907 addConstraints(predecessor);
1913 auto parentExit = startEvent->
parent.value().second;
1915 for (
auto& [_, predecessor] : parentExit->inflows ) {
1917 assert(visit.contains(startEvent));
1919 assert(visit.contains(predecessor));
1922 model.addConstraint(
1923 ( !visit.at(startEvent) && visit.at(predecessor) ).implies(
1924 position.at(predecessor) < position.at(startEvent)
1931 std::vector<CP::Expression> visitedPositions;
1932 for (
auto predecessor : parentExit->predecessors ) {
1935 predecessor->type == Vertex::Type::EXIT &&
1941 visitedPositions.push_back( visit.at(predecessor) * position.at(predecessor) );
1945 if ( visitedPositions.empty() ) {
1946 throw std::runtime_error(
"CPModel: event-subprocesses in a scope without activities and message catch events are not supported");
1950 model.addConstraint(
1951 visit.at(startEvent) * position.at(startEvent) <= CP::max( visitedPositions )
1957void CPModel::createRestrictions(
const Vertex* vertex) {
1959 if ( !extensionElements )
return;
1961 for (
auto& restriction : extensionElements->restrictions ) {
1963 restriction->scope == Model::Restriction::Scope::FULL ||
1964 ( vertex->type == Vertex::Type::ENTRY && restriction->scope == Model::Restriction::Scope::ENTRY ) ||
1965 ( vertex->type == Vertex::Type::EXIT && restriction->scope != Model::Restriction::Scope::ENTRY )
1968 if ( restriction->expression.type == Model::Expression::Type::IS_NULL ) {
1969 assert( restriction->expression.variables.size() == 1 );
1970 auto attribute = restriction->
expression.variables.front();
1971 auto [defined,value] = getAttributeVariables(vertex,attribute);
1972 model.addConstraint( visit.at(vertex).implies(defined ==
false) );
1974 else if ( restriction->expression.type == Model::Expression::Type::IS_NOT_NULL ) {
1975 assert( restriction->expression.variables.size() == 1 );
1976 auto attribute = restriction->
expression.variables.front();
1977 auto [defined,value] = getAttributeVariables(vertex,attribute);
1978 model.addConstraint( visit.at(vertex).implies(defined ==
true) );
1981 assert( restriction->expression.type == Model::Expression::Type::OTHER );
1982 for (
auto attribute : restriction->expression.variables ) {
1984 auto [defined,value] = getAttributeVariables(vertex,attribute);
1985 model.addConstraint( visit.at(vertex).implies(defined ==
true) );
1988 auto constraint = createExpression( vertex, restriction->expression );
1989 model.addConstraint( visit.at(vertex).implies(constraint) );
1995CP::Expression CPModel::createExpression(
const Vertex* vertex,
const Model::Expression& expression) {
1997 if ( !extensionElements ) {
2004 std::vector<CP::Expression> variables;
2005 for (
auto& variableName : compiled.getVariables() ) {
2006 auto attribute = extensionElements->attributeRegistry[variableName];
2007 if( attribute->
type == ValueType::COLLECTION ) {
2008 throw std::runtime_error(
"CPModel: illegal expression '" + expression.
expression +
"'");
2011 auto [defined,value] = getAttributeVariables(vertex,attribute);
2013 variables.push_back( value );
2016 std::vector<CP::Expression> collectionVariables;
2017 for (
auto& variableName : compiled.getCollections() ) {
2018 auto attribute = extensionElements->attributeRegistry[variableName];
2019 if( attribute->
type != ValueType::COLLECTION ) {
2020 throw std::runtime_error(
"CPModel: illegal expression '" + expression.
expression +
"'");
2023 auto [defined,value] = getAttributeVariables(vertex,attribute);
2025 collectionVariables.push_back( value );
2028 return compiled.evaluate(variables,collectionVariables);
2031void CPModel::createGlobalIndexVariable(
const Vertex* vertex) {
2032 CP::Expression index;
2033 for (
auto& [modifierEntry, modifierExit] : flattenedGraph->
globalModifiers ) {
2035 index = index + model.addVariable(CP::Variable::Type::BOOLEAN,
"weakly_precedes_{" + modifierExit->reference() +
" → " + vertex->reference() +
"}", position.at(modifierExit) <= position.at(vertex) );
2037 globalIndex.emplace( vertex, model.addVariable(CP::Variable::Type::INTEGER,
"globals_index_{" + vertex->reference() +
"}", index ) );
2041void CPModel::createDataIndexVariables(
const Vertex* vertex) {
2042 CP::reference_vector< const CP::Variable > dataIndices;
2043 dataIndices.reserve( vertex->dataOwners.size() );
2044 for (
auto dataOwner : vertex->dataOwners ) {
2045 assert( flattenedGraph->
dataModifiers.contains(dataOwner) );
2046 CP::Expression index;
2047 for (
auto& [modifierEntry, modifierExit] : flattenedGraph->
dataModifiers.at(dataOwner) ) {
2049 index = index + model.addVariable(CP::Variable::Type::BOOLEAN,
"weakly_precedes_{" + modifierExit->reference() +
" → " + vertex->reference() +
"}", position.at(modifierExit) <= position.at(vertex) );
2052 dataIndices.emplace_back( model.addVariable(CP::Variable::Type::INTEGER,
"data_index[" + dataOwner->node->id +
"]_{" + vertex->reference() +
"}", index ) );
2054 dataIndex.emplace( vertex, std::move(dataIndices) );
2058 if ( attribute->
weight != 0.0 ) {
2059 model.setObjective( model.getObjective() + attribute->
weight * variable );
2063void CPModel::addObjectiveCoefficients(
const Vertex* vertex) {
2064 assert( vertex->type == Vertex::Type::EXIT );
2065 auto loopCharacteristics = getLoopCharacteristics(vertex);
2067 flattenedGraph->
dummies.contains(vertex) &&
2068 loopCharacteristics.has_value() &&
2076 for (
size_t i = extensionElements->attributeRegistry.statusAttributes.size() - extensionElements->attributes.size(); i < extensionElements->attributeRegistry.
statusAttributes.size(); i++) {
2077 auto attribute = extensionElements->attributeRegistry.statusAttributes[i];
2078 addToObjective( attribute, status.at(vertex)[attribute->
index].value );
CollectionRegistry collectionRegistry
std::vector< std::pair< const BPMN::SequenceFlow *, Vertex * > > outflows
Container holding vertices connecting by an incoming sequence flow, for loop activities the sequence ...
std::vector< Vertex * > senders
Container holding successors according to the execution logic (excl. sequence flows)
std::vector< std::pair< const BPMN::SequenceFlow *, Vertex * > > inflows
Parent vertices.
std::string shortReference() const
Returns a unique reference of the vertex.
std::vector< Vertex * > predecessors
Container holding vertices connecting by an outgoing sequence flow, for loop activities the sequence ...
std::optional< std::pair< Vertex *, Vertex * > > parent
const BPMNOS::number rootId
Represents a graph containing all BPMN nodes that may receive a token during execution of a scenario.
std::unordered_set< const Vertex * > dummies
Container holding entry and exit vertices of each possible instantiation of a node.
std::vector< const Vertex * > sortVertices() const
Container holding entry vertices of all process instances.
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...
std::unordered_map< const Vertex *, std::vector< std::pair< const Vertex *, const Vertex * > > > sequentialActivities
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::unordered_map< const BPMN::Node *, std::vector< const BPMNOS::Model::Attribute * > > loopIndexAttributes
std::vector< std::unique_ptr< Vertex > > vertices
Returns a topologically sorted vector of all vertices reachable from the initial vertices.
std::vector< Attribute * > statusAttributes
double weight
Weight to be used for objective (assuming maximization).
std::unique_ptr< const Expression > expression
size_t index
Index of attribute (is automatically set by attribute registry).
bool isImmutable
Flag indicating whether attribute value may be changed by operator, choice, or intermediate catch eve...
Class representing a choice to be made within a BPMNOS::Model::DecisionTask.
Class representing a task in which one or more choices have to be made.
Class representing a mathematical expression.
const AttributeRegistry & attributeRegistry
const std::string expression
Class holding extension elements representing execution data for nodes.
AttributeRegistry attributeRegistry
Registry allowing to look up all status and data attributes by their names.
std::vector< std::unique_ptr< MessageDefinition > > messageDefinitions
Vector containing message definition(s) provided for the node.
Class holding extension elements representing gatekeeper conditions for sequence flows.
std::vector< std::unique_ptr< Restriction > > conditions
ContentMap contentMap
Map allowing to look up contents by their keys.
std::unique_ptr< const Expression > expression
Class representing adhoc subprocesses with sequential ordering.
A scenario implementation where all data is known from the start.
Class holding extension elements representing the trigger of timer events.
std::unique_ptr< BPMNOS::Model::Parameter > trigger
std::optional< LoopCharacteristics > loopCharacteristics
std::unique_ptr< ExtensionElements > extensionElements
Scope * parent
Reference to the parent node.
T * as()
Casts the element to the specified type T.
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 encodeQuotedStrings(std::string text)
static constexpr size_t Instance
static constexpr size_t Timestamp