53template<
typename T, 
typename ExtraDataType>
 
   54class FullComponentWithExtraStore;
 
   59class EdgeWeightedGraph;
 
   64#define OGDF_STEINERTREE_LPRELAXATIONSER_SEPARATE_CONNECTED_COMPONENTS  
   65#define OGDF_STEINERTREE_LPRELAXATIONSER_SEPARATE_YVAR_CONSTRAINTS  
   68namespace steiner_tree {
 
   87#ifdef OGDF_STEINERTREE_LPRELAXATIONSER_SEPARATE_CONNECTED_COMPONENTS 
  122        G.setOriginalGraph(
m_G);
 
  124        source = G.newNode();
 
  128        target = G.newNode();
 
  129        for (
int j = 0; j < activeComponents.
size(); ++j) {
 
  130            const int i = activeComponents[j];
 
  135            const auto it0 = terminals.
begin();
 
  136            const node rC = G.copy(*it0);
 
  137            capacity[G.newEdge(source, rC)] = cap;
 
  138            if (terminals.
size() > 2) {
 
  139                const node v = G.newNode();
 
  140                capacity[G.newEdge(rC, v)] = cap;
 
  141                for (
auto it = it0 + 1; it != terminals.
end(); ++it) {
 
  142                    const node w = G.copy(*it);
 
  143                    capacity[G.newEdge(v, w)] = cap;
 
  146                capacity[G.newEdge(rC, G.copy(*terminals.
rbegin()))] = cap;
 
  153            const node v = G.copy(t);
 
  158                if (adj->twinNode() != source) {
 
  159                    y_v += capacity[adj->theEdge()];
 
  163#ifdef OGDF_STEINERTREE_LPRELAXATIONSER_SEPARATE_YVAR_CONSTRAINTS 
  170                capacity[G.newEdge(v, target)] = y_v;
 
  176        for (
edge e : G.edges) {
 
  177            if (G.original(e->source())) {
 
  178                std::cout << 
" T:" << G.original(e->source());
 
  180                std::cout << 
" " << e->source();
 
  183            if (G.original(e->target())) {
 
  184                std::cout << 
"T:" << G.original(e->target());
 
  186                std::cout << e->target();
 
  188            std::cout << 
" " << capacity[e] << 
"\n";
 
 
  208            T upperBound = 0, 
int cliqueSize = 0, 
double eps = 1e-8)
 
  227#ifndef OGDF_STEINERTREE_LPRELAXATIONSER_SEPARATE_YVAR_CONSTRAINTS 
 
 
  249    const int n = m_fullCompStore.size();
 
  251    m_matrix->setDimensions(0, n);
 
  252    for (
int i = 0; i < n; ++i) {
 
  253        m_lowerBounds[i] = 0;
 
  254        m_upperBounds[i] = 1;
 
  256    for (
int i = 0; i < n; ++i) {
 
  257        m_objective[i] = m_fullCompStore.cost(i);
 
  259    m_osiSolver->loadProblem(*m_matrix, m_lowerBounds, m_upperBounds, m_objective, 
nullptr, 
nullptr);
 
  261    if (m_upperBound > 0) { 
 
  262        CoinPackedVector row;
 
  263        row.setFull(m_fullCompStore.size(), m_objective);
 
  264        m_osiSolver->addRow(row, 0, m_upperBound);
 
 
  270    CoinPackedVector row;
 
  272    for (
int i = 0; i < m_fullCompStore.size(); ++i) {
 
  273        if (m_fullCompStore.isTerminal(i, t)) { 
 
  278    m_osiSolver->addRow(row, 1, m_osiSolver->getInfinity());
 
 
  284    CoinPackedVector row;
 
  287    for (
int i = 0; i < m_fullCompStore.size(); ++i) {
 
  289        int intersectionCard = 0;
 
  290        auto terminals = m_fullCompStore.terminals(i);
 
  291        node* it1 = terminals.begin();
 
  292        auto it2 = subset.
begin();
 
  293        while (it1 != terminals.end() && it2 != subset.
end()) {
 
  294            if ((*it1)->index() < (*it2)->index()) {
 
  296            } 
else if ((*it1)->index() > (*it2)->index()) {
 
  305        if (intersectionCard > 1) {
 
  306            row.insert(i, intersectionCard - 1);
 
  307            test += (intersectionCard - 1) * m_fullCompStore.extra(i);
 
  310    if (test > subset.
size() - 1) {
 
  311        m_osiSolver->addRow(row, 0, subset.
size() - 1);
 
 
  321    CoinPackedVector row;
 
  323    for (
int i = 0; i < m_fullCompStore.size(); ++i) {
 
  324        row.insert(i, m_fullCompStore.terminals(i).size() - 1);
 
  327    int value = m_terminals.size() - 1;
 
  328    m_osiSolver->addRow(row, value, value);
 
 
  333    bool initialIteration = 
true;
 
  336        if (initialIteration) {
 
  337            m_osiSolver->initialSolve();
 
  338#ifdef OGDF_STEINERTREE_LPRELAXATIONSER_LOGGING 
  339            std::cout << 
"Objective value " << m_osiSolver->getObjValue() << 
" of initial solution." 
  342            initialIteration = 
false;
 
  344            m_osiSolver->resolve();
 
  345#ifdef OGDF_STEINERTREE_LPRELAXATIONSER_LOGGING 
  346            std::cout << 
"Objective value " << m_osiSolver->getObjValue() << 
" after resolve." 
  351        if (!m_osiSolver->isProvenOptimal()) {
 
  352            if (m_upperBound > 0) { 
 
  355                std::cerr << 
"Failed to optimize LP!" << std::endl;
 
  359    } 
while (separate());
 
  361    const double* constSol = m_osiSolver->getColSolution();
 
  362    const int numberOfColumns = m_osiSolver->getNumCols();
 
  364    for (
int i = 0; i < numberOfColumns; ++i) {
 
  365        m_fullCompStore.extra(i) = constSol[i];
 
  368#ifdef OGDF_STEINERTREE_LPRELAXATIONSER_OUTPUT_LP 
  369    m_osiSolver->writeLp(stderr);
 
 
  377    const double* constSol = m_osiSolver->getColSolution();
 
  380    for (
int i = 0; i < m_fullCompStore.size(); ++i) {
 
  381        m_fullCompStore.extra(i) = constSol[i];
 
  382        if (m_fullCompStore.extra(i) > m_eps) {
 
  383            activeComponents.
push(i);
 
  387#ifdef OGDF_STEINERTREE_LPRELAXATIONSER_SEPARATE_CONNECTED_COMPONENTS 
  388    if (!m_separationStage) {
 
  389        if (separateConnected(activeComponents)) {
 
  392        m_separationStage = 1;
 
  395    if (separateMinCut(activeComponents)) {
 
  398    return m_separateCliqueSize > 2 ? separateCycles(activeComponents) : 
false;
 
 
  405    for (
node t : m_terminals) {
 
  410    for (
int j = 0; j < activeComponents.
size(); ++j) {
 
  411        auto terminals = m_fullCompStore.terminals(activeComponents[j]);
 
  412        auto it = terminals.begin();
 
  413        const int s1 = setID[*it];
 
  414        for (++it; it != terminals.end(); ++it) {
 
  425    for (
node t : m_terminals) {
 
  426        const int k = uf.
find(setID[t]);
 
  427        if (components[k].empty()) {
 
  430        components[k].push(t);
 
  433    for (
const int k : usedComp) {
 
  434        cutsFound += addSubsetCoverConstraint(components[k]);
 
 
  446    double y_R = generateMinCutSeparationGraph(activeComponents, source, pseudotarget, auxG,
 
  447            capacity, cutsFound);
 
  449#ifdef OGDF_STEINERTREE_LPRELAXATIONSER_SEPARATE_YVAR_CONSTRAINTS 
  456    capacity[auxG.
newEdge(pseudotarget, target)] = y_R;
 
  461    for (
node t : m_terminals) {
 
  465        capacity[v_to_target] = std::numeric_limits<double>::max(); 
 
  467        maxFlow.
init(auxG, &flow);
 
  469        const double cutVal = maxFlow.
computeValue(capacity, source, target);
 
  470        if (cutVal - y_R < 1 - m_eps) {
 
  471            minSTCut.
call(auxG, capacity, flow, source, target);
 
  473            for (
node tOrig : m_terminals) {
 
  474                const node tCopy = auxG.
copy(tOrig);
 
  480            cutsFound += addSubsetCoverConstraint(subset);
 
  485    return cutsFound != 0;
 
 
  495    for (
int i : activeComponents) {
 
  498    for (
node u1 : G.nodes) {
 
  499        const int i1 = 
id[u1];
 
  500        const Array<node>& terminals1 = m_fullCompStore.terminals(i1);
 
  502            const int i2 = 
id[u2];
 
  503            const Array<node>& terminals2 = m_fullCompStore.terminals(i2);
 
  505            int intersectionCard = 0;
 
  508            while (it1 != terminals1.
end() && it2 != terminals2.
end()) {
 
  509                if ((*it1)->index() < (*it2)->index()) {
 
  511                } 
else if ((*it1)->index() > (*it2)->index()) {
 
  515                    if (intersectionCard == 2) {
 
  526    if (G.numberOfEdges() == 0) {
 
  532    for (
node v : G.nodes) {
 
  533        int idx = v->degree();
 
  537        if (idx >= m_separateCliqueSize) {
 
  538            idx = m_separateCliqueSize - 1;
 
  541        degrees[idx].pushBack(v);
 
  544    for (
int k = degrees.
size(); k >= 2; --k) {
 
  545        degrees[k - 2].conc(degrees[k - 1]);
 
  546        if (degrees[k - 2].size() >= k) {
 
  548            for (nodeSubset.
begin(k); nodeSubset.
valid(); nodeSubset.
next()) {
 
  549                int countEdges = (k * (k - 1)) / 2;
 
  550                for (
int j = 0; j < nodeSubset.
size(); ++j) {
 
  551                    test[nodeSubset[j]] = 
true;
 
  553                for (
edge e : G.edges) {
 
  554                    if (test[e->source()] && test[e->target()]) {
 
  559                if (countEdges == 0) {
 
  562                    CoinPackedVector row;
 
  564                    for (
int j = 0; j < nodeSubset.
size(); ++j) {
 
  565                        int i = 
id[nodeSubset[j]];
 
  566                        val += m_fullCompStore.extra(i);
 
  569                    if (val >= 1 + m_eps) {
 
  570                        m_osiSolver->addRow(row, 0, 1);
 
  574                for (
int j = 0; j < nodeSubset.
size(); ++j) {
 
  575                    test[nodeSubset[j]] = 
false;
 
 
Declaration and implementation of Array class and Array algorithms.
 
Declaration and implementation of ArrayBuffer class.
 
Implementation of disjoint sets data structures (union-find functionality).
 
Includes declaration of graph class.
 
Declaration of graph copy classes.
 
Decralation of GraphElement and GraphList classes.
 
#define OGDF_STEINERTREE_LPRELAXATIONSER_SEPARATE_CONNECTED_COMPONENTS
 
Declaration of doubly linked lists and iterators.
 
Declaration and implementation of Goldberg-Tarjan max-flow algorithm with global relabeling and gap r...
 
Declaration of min-st-cut algorithms parameterized by max-flow alorithm.
 
A class that allows to enumerate k-subsets.
 
Basic declarations, included by all source files.
 
Class for adjacency list elements.
 
An array that keeps track of the number of inserted elements; also usable as an efficient stack.
 
iterator end()
Returns an iterator to one past the last element.
 
void push(E e)
Puts a new element in the buffer.
 
iterator begin()
Returns an iterator to the first element.
 
INDEX size() const
Returns number of elements in the buffer.
 
The parameterized class Array implements dynamic arrays of type E.
 
iterator begin()
Returns an iterator to the first element.
 
reverse_iterator rbegin()
Returns an reverse iterator to the last element.
 
iterator end()
Returns an iterator to one past the last element.
 
INDEX size() const
Returns the size (number of elements) of the array.
 
If you use COIN-OR, you should use this class.
 
A Union/Find data structure for maintaining disjoint sets.
 
int makeSet()
Initializes a singleton set.
 
int getNumberOfSets()
Returns the current number of disjoint sets.
 
int find(disjoint_sets::CompressionOption< CompressionOptions::PathCompression >, int set)
 
int link(disjoint_sets::LinkOption< LinkOptions::Naive >, int set1, int set2)
 
Class for the representation of edges.
 
node newNode(node vOrig)
Creates a new node in the graph copy with original node vOrig.
 
Copies of graphs supporting edge splitting.
 
edge copy(edge e) const override
Returns the first edge in the list of edges corresponding to edge e.
 
void delEdge(edge e) override
Removes edge e and clears the list of edges corresponding to e's original edge.
 
edge newEdge(edge eOrig)
Creates a new edge (v,w) with original edge eOrig.
 
Data type for general directed graphs (adjacency list representation).
 
Doubly linked lists (maintaining the length of the list).
 
Computes a max flow via Preflow-Push (global relabeling and gap relabeling heuristic).
 
TCap computeValue(const EdgeArray< TCap > &cap, const node &s, const node &t)
Compute only the value of the flow.
 
virtual void init(const Graph &graph, EdgeArray< T > *flow=nullptr)
Initialize the problem with a graph and optional flow array. If no flow array is given,...
 
Min-st-cut algorithm, that calculates the cut via maxflow.
 
bool isInBackCut(const node v) const
Returns whether this node is part of the back cut.
 
virtual bool call(const Graph &graph, const EdgeArray< TCost > &weight, node s, node t, List< edge > &edgeList, edge e_st=nullptr) override
The actual algorithm call.
 
Class for the representation of nodes.
 
internal::GraphObjectContainer< AdjElement > adjEntries
The container containing all entries in the adjacency list of this node.
 
node succ() const
Returns the successor in the list of all nodes.
 
void init(const Base *base=nullptr)
Reinitializes the array. Associates the array with the matching registry of base.
 
Enumerator for k-subsets of a given type.
 
void begin(int low, int high)
Initializes the SubsetEnumerator to enumerate subsets of cardinalities from low to high.
 
int size() const
Returns the cardinality of the subset.
 
bool valid() const
Checks if the current subset is valid. If not, the subset is either not initialized or all subsets ha...
 
void next()
Obtains the next subset if possible. The result should be checked using the valid() method.
 
RegisteredArray for edges of a graph, specialized for EdgeArray<edge>.
 
RegisteredArray for nodes, edges and adjEntries of a graph.
 
const Array< node > & terminals(int id) const
Returns the list of terminals in the full component with given id.
 
Class managing the component-based subtour elimination LP relaxation for the Steiner tree problem and...
 
bool separate()
Perform all available separation algorithms.
 
bool separateConnected(const ArrayBuffer< int > &activeComponents)
Separate to ensure that the solution is connected.
 
LPRelaxationSER(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal, FullComponentWithExtraStore< T, double > &fullCompStore, T upperBound=0, int cliqueSize=0, double eps=1e-8)
Initialize the LP.
 
CoinPackedMatrix * m_matrix
 
bool solve()
Solve the LP. The solution will be written to the extra data of the full component store.
 
double generateMinCutSeparationGraph(const ArrayBuffer< int > &activeComponents, node &source, node &target, GraphCopy &G, EdgeArray< double > &capacity, int &cutsFound)
Generates an auxiliary multi-graph for separation (during LP solving): directed, with special source ...
 
bool separateCycles(const ArrayBuffer< int > &activeComponents)
Perform the separation algorithm for cycle constraints (to obtain stronger LP solution)
 
const double m_eps
epsilon for double operations
 
void generateProblem()
Generate the basic LP model.
 
bool addSubsetCoverConstraint(const ArrayBuffer< node > &subset)
Add subset cover constraints to the LP for a given subset of terminals.
 
const int m_separateCliqueSize
 
const EdgeWeightedGraph< T > & m_G
 
const NodeArray< bool > & m_isTerminal
 
bool separateMinCut(const ArrayBuffer< int > &activeComponents)
Perform the general cut-based separation algorithm.
 
OsiSolverInterface * m_osiSolver
 
void addYConstraint(const node t)
Add constraint that the sum of x_C over all components C spanning terminal t is at least 1 to ensure ...
 
const List< node > & m_terminals
List of terminals.
 
FullComponentWithExtraStore< T, double > & m_fullCompStore
all enumerated full components, with solution
 
void addTerminalCoverConstraint()
Add terminal cover constraints to the LP.
 
Definition of ogdf::CoinManager.
 
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
 
The namespace for all OGDF objects.