From e566cde23b7dad468a79edaaed9c0458a9e0c90b Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 7 May 2024 17:04:54 +0200 Subject: [PATCH 01/63] Add network matrix detection methods --- src/scip/network.c | 8001 +++++++++++++++++++++++++++++++++++ src/scip/network.h | 198 + tests/src/network/network.c | 1 + 3 files changed, 8200 insertions(+) create mode 100644 src/scip/network.c create mode 100644 src/scip/network.h create mode 100644 tests/src/network/network.c diff --git a/src/scip/network.c b/src/scip/network.c new file mode 100644 index 0000000000..1734b73f4a --- /dev/null +++ b/src/scip/network.c @@ -0,0 +1,8001 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* This file is part of the program and library */ +/* SCIP --- Solving Constraint Integer Programs */ +/* */ +/* Copyright (c) 2002-2024 Zuse Institute Berlin (ZIB) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/* You should have received a copy of the Apache-2.0 license */ +/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/**@file network.c + * @ingroup OTHER_CFILES + * @brief Methods for detecting network (sub)matrices + * @author Rolf van der Hulst + */ + +#include "scip/network.h" +#include + +///Types which define matrix sizes +typedef int spqr_matrix_size; +typedef spqr_matrix_size spqr_row; +typedef spqr_matrix_size spqr_col; + +#define SPQR_INVALID INT_MAX +#define SPQR_INVALID_ROW SPQR_INVALID +#define SPQR_INVALID_COL SPQR_INVALID + +#ifndef NDEBUG +static SCIP_Bool SPQRrowIsInvalid(spqr_row row){ + return row == SPQR_INVALID_ROW; +} + +static SCIP_Bool SPQRcolIsInvalid(spqr_col col){ + return col == SPQR_INVALID_COL; +} +static SCIP_Bool SPQRrowIsValid(spqr_row row){ + return !SPQRrowIsInvalid(row); +} +static SCIP_Bool SPQRcolIsValid(spqr_col col){ + return !SPQRcolIsInvalid(col); +} +#endif + +//Columns 0..x correspond to elements 0..x +//Rows 0..y correspond to elements -1.. -y-1 +#define MARKER_ROW_ELEMENT (INT_MIN) +#define MARKER_COLUMN_ELEMENT (INT_MAX) +typedef int spqr_element; + +static SCIP_Bool SPQRelementIsRow(spqr_element element){ + return element < 0; +} +static SCIP_Bool SPQRelementIsColumn(spqr_element element){ + return !SPQRelementIsRow(element); +} +static spqr_row SPQRelementToRow(spqr_element element){ + assert(SPQRelementIsRow(element)); + return (spqr_row) (-element - 1); +} +static spqr_element SPQRrowToElement(spqr_row row){ + assert(SPQRrowIsValid(row)); + return (spqr_element) -row-1; +} +static spqr_col SPQRelementToColumn(spqr_element element){ + assert(SPQRelementIsColumn(element)); + return (spqr_col) element; +} +static spqr_element SPQRcolumnToElement(spqr_col column){ + assert(SPQRcolIsValid(column)); + return (spqr_element) column; +} + +typedef int spqr_node; +#define SPQR_INVALID_NODE (-1) + +static SCIP_Bool SPQRnodeIsInvalid(spqr_node node) { + return node < 0; +} +static SCIP_Bool SPQRnodeIsValid(spqr_node node) { + return !SPQRnodeIsInvalid(node); +} + +typedef int spqr_member; +#define SPQR_INVALID_MEMBER (-1) + +static SCIP_Bool SPQRmemberIsInvalid(spqr_member member) { + return member < 0; +} +static SCIP_Bool SPQRmemberIsValid(spqr_member member) { + return !SPQRmemberIsInvalid(member); +} + +typedef int spqr_arc; +#define SPQR_INVALID_ARC (-1) + +static SCIP_Bool SPQRarcIsInvalid(spqr_arc arc) { + return arc < 0; +} +static SCIP_Bool SPQRarcIsValid(spqr_arc arc){ + return !SPQRarcIsInvalid(arc); +} + +typedef enum { + SPQR_MEMBERTYPE_RIGID = 0, //Also known as triconnected components + SPQR_MEMBERTYPE_PARALLEL = 1,//Also known as a 'bond' + SPQR_MEMBERTYPE_SERIES = 2, //Also known as 'polygon' or 'cycle' + SPQR_MEMBERTYPE_LOOP = 3, + SPQR_MEMBERTYPE_UNASSIGNED = 4 // To indicate that the member has been merged/is not representative +} SPQRMemberType; + + +typedef struct{ + spqr_arc previous; + spqr_arc next; +}SPQRNetworkDecompositionArcListNode; + +typedef struct { + spqr_node representativeNode; + spqr_arc firstArc;//first arc of the neighbouring arcs + int numArcs; +} SPQRNetworkDecompositionNode; + +typedef struct { + spqr_node head; + spqr_node tail; + spqr_member member; + spqr_member childMember; + SPQRNetworkDecompositionArcListNode headArcListNode; + SPQRNetworkDecompositionArcListNode tailArcListNode; + SPQRNetworkDecompositionArcListNode arcListNode; //Linked-list node of the array of arcs of the member which this arc is in + + spqr_element element; + + //Signed union-find for arc directions + //For non-rigid members every arc is it's own representative, and the direction is simply given by the boolean + //For rigid members, every arc is represented by another arc in the member, + //and the direction can be found by multiplying the signs along the union-find path + spqr_arc representative; + SCIP_Bool reversed; +} SPQRNetworkDecompositionArc; + +typedef struct { + spqr_member representativeMember; + SPQRMemberType type; + + spqr_member parentMember; + spqr_arc markerToParent; + spqr_arc markerOfParent; + + spqr_arc firstArc; //First of the members' linked-list arc array + int numArcs; +} SPQRNetworkDecompositionMember; + +struct SCIP_NetworkDecomposition { + int numArcs; + int memArcs; + SPQRNetworkDecompositionArc *arcs; + spqr_arc firstFreeArc; + + int memMembers; + int numMembers; + SPQRNetworkDecompositionMember *members; + + int memNodes; + int numNodes; + SPQRNetworkDecompositionNode *nodes; + + int memRows; + int numRows; + spqr_arc * rowArcs; + + int memColumns; + int numColumns; + spqr_arc * columnArcs; + + SCIP * env; + + int numConnectedComponents; +}; + +static void swap_ints(int* a, int* b){ + int temp = *a; + *a = *b; + *b = temp; +} +#ifndef NDEBUG +static SCIP_Bool nodeIsRepresentative(const SCIP_NETWORKDECOMP *dec, spqr_node node) { + assert(dec); + assert(node < dec->memNodes); + assert(SPQRnodeIsValid(node)); + + return SPQRnodeIsInvalid(dec->nodes[node].representativeNode); +} +#endif + +static spqr_node findNode(SCIP_NETWORKDECOMP *dec, spqr_node node) { + assert(dec); + assert(SPQRnodeIsValid(node)); + assert(node < dec->memNodes); + + spqr_node current = node; + spqr_node next; + + //traverse down tree to find the root + while (SPQRnodeIsValid(next = dec->nodes[current].representativeNode)) { + current = next; + assert(current < dec->memNodes); + } + + spqr_node root = current; + current = node; + + //update all pointers along path to point to root, flattening the tree + while (SPQRnodeIsValid(next = dec->nodes[current].representativeNode)) { + dec->nodes[current].representativeNode = root; + current = next; + assert(current < dec->memNodes); + } + return root; +} + +static spqr_node findNodeNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_node node) { + assert(dec); + assert(SPQRnodeIsValid(node)); + assert(node < dec->memNodes); + + spqr_node current = node; + spqr_node next; + + //traverse down tree to find the root + while (SPQRnodeIsValid(next = dec->nodes[current].representativeNode)) { + current = next; + assert(current < dec->memNodes); + } + spqr_node root = current; + return root; +} + +static spqr_node findArcTail(SCIP_NETWORKDECOMP *dec, spqr_arc arc) { + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + + spqr_node representative = findNode(dec, dec->arcs[arc].tail); + dec->arcs[arc].tail = representative; //update the arc information + + return representative; +} +static spqr_node findArcHead(SCIP_NETWORKDECOMP *dec, spqr_arc arc) { + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + + spqr_node representative = findNode(dec, dec->arcs[arc].head); + dec->arcs[arc].head = representative;//update the arc information + + return representative; +} +static spqr_node findArcHeadNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_arc arc) { + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + + spqr_node representative = findNodeNoCompression(dec, dec->arcs[arc].head); + return representative; +} +static spqr_node findArcTailNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_arc arc) { + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + + spqr_node representative = findNodeNoCompression(dec, dec->arcs[arc].tail); + return representative; +} + + +static spqr_arc getFirstNodeArc(const SCIP_NETWORKDECOMP * dec, spqr_node node){ + assert(dec); + assert(SPQRnodeIsValid(node)); + assert(node < dec->memNodes); + return dec->nodes[node].firstArc; +} +static spqr_arc getNextNodeArcNoCompression(const SCIP_NETWORKDECOMP * dec, spqr_arc arc, spqr_node node){ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + assert(nodeIsRepresentative(dec,node)); + + if(findArcHeadNoCompression(dec,arc) == node){ + arc = dec->arcs[arc].headArcListNode.next; + }else{ + assert(findArcTailNoCompression(dec,arc) == node); + arc = dec->arcs[arc].tailArcListNode.next; + } + return arc; +} +static spqr_arc getNextNodeArc(SCIP_NETWORKDECOMP * dec, spqr_arc arc, spqr_node node){ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + assert(nodeIsRepresentative(dec,node)); + + if(findArcHead(dec,arc) == node){ + arc = dec->arcs[arc].headArcListNode.next; + }else{ + assert(findArcTailNoCompression(dec,arc) == node); + dec->arcs[arc].tail = node; //This assignment is not necessary but speeds up future queries. + arc = dec->arcs[arc].tailArcListNode.next; + } + return arc; +} +static spqr_arc getPreviousNodeArc(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_node node){ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + assert(nodeIsRepresentative(dec,node)); + + if(findArcHead(dec,arc) == node){ + arc = dec->arcs[arc].headArcListNode.previous; + }else{ + assert(findArcTailNoCompression(dec,arc) == node); + dec->arcs[arc].tail = node; //This assignment is not necessary but speeds up future queries. + arc = dec->arcs[arc].tailArcListNode.previous; + } + return arc; +} + +static void mergeNodeArcList(SCIP_NETWORKDECOMP *dec, spqr_node toMergeInto, spqr_node toRemove){ + + spqr_arc firstIntoArc = getFirstNodeArc(dec, toMergeInto); + spqr_arc firstFromArc = getFirstNodeArc(dec, toRemove); + if(SPQRarcIsInvalid(firstIntoArc)){ + //new node has no arcs + dec->nodes[toMergeInto].numArcs += dec->nodes[toRemove].numArcs; + dec->nodes[toRemove].numArcs = 0; + + dec->nodes[toMergeInto].firstArc = dec->nodes[toRemove].firstArc; + dec->nodes[toRemove].firstArc = SPQR_INVALID_ARC; + + return; + }else if (SPQRarcIsInvalid(firstFromArc)){ + //Old node has no arcs; we can just return + return; + } + + spqr_arc lastIntoArc = getPreviousNodeArc(dec, firstIntoArc, toMergeInto); + assert(SPQRarcIsValid(lastIntoArc)); + spqr_arc lastFromArc = getPreviousNodeArc(dec, firstFromArc, toRemove); + assert(SPQRarcIsValid(lastFromArc)); + + + SPQRNetworkDecompositionArcListNode * firstIntoNode = findArcHead(dec, firstIntoArc) == toMergeInto ? + &dec->arcs[firstIntoArc].headArcListNode : + &dec->arcs[firstIntoArc].tailArcListNode; + SPQRNetworkDecompositionArcListNode * lastIntoNode = findArcHead(dec, lastIntoArc) == toMergeInto ? + &dec->arcs[lastIntoArc].headArcListNode : + &dec->arcs[lastIntoArc].tailArcListNode; + + SPQRNetworkDecompositionArcListNode * firstFromNode = findArcHead(dec, firstFromArc) == toRemove ? + &dec->arcs[firstFromArc].headArcListNode : + &dec->arcs[firstFromArc].tailArcListNode; + SPQRNetworkDecompositionArcListNode * lastFromNode = findArcHead(dec, lastFromArc) == toRemove ? + &dec->arcs[lastFromArc].headArcListNode : + &dec->arcs[lastFromArc].tailArcListNode; + + firstIntoNode->previous = lastFromArc; + lastIntoNode->next = firstFromArc; + firstFromNode->previous = lastIntoArc; + lastFromNode->next = firstIntoArc; + + dec->nodes[toMergeInto].numArcs += dec->nodes[toRemove].numArcs; + dec->nodes[toRemove].numArcs = 0; + dec->nodes[toRemove].firstArc = SPQR_INVALID_ARC; +} + +static void arcFlipReversed(SCIP_NETWORKDECOMP *dec, spqr_arc arc){ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + dec->arcs[arc].reversed = !dec->arcs[arc].reversed; +} +static void arcSetReversed(SCIP_NETWORKDECOMP *dec, spqr_arc arc, SCIP_Bool reversed){ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + dec->arcs[arc].reversed = reversed; +} +static void arcSetRepresentative(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_arc representative){ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + assert(representative == SPQR_INVALID_ARC || SPQRarcIsValid(representative)); + dec->arcs[arc].representative = representative; +} + +static spqr_node mergeNodes(SCIP_NETWORKDECOMP *dec, spqr_node first, spqr_node second) { + assert(dec); + assert(nodeIsRepresentative(dec, first)); + assert(nodeIsRepresentative(dec, second)); + assert(first != second); //We cannot merge a node into itself + assert(first < dec->memNodes); + assert(second < dec->memNodes); + + //The rank is stored as a negative number: we decrement it making the negative number larger. + // We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement. + spqr_node firstRank = dec->nodes[first].representativeNode; + spqr_node secondRank = dec->nodes[second].representativeNode; + if (firstRank > secondRank) { + swap_ints(&first, &second); + } + //first becomes representative; we merge all of the arcs of second into first + mergeNodeArcList(dec,first,second); + dec->nodes[second].representativeNode = first; + if (firstRank == secondRank) { + --dec->nodes[first].representativeNode; + } + return first; +} + +static SCIP_Bool memberIsRepresentative(const SCIP_NETWORKDECOMP *dec, spqr_member member) { + assert(dec); + assert(member < dec->memMembers); + assert(SPQRmemberIsValid(member)); + + return SPQRmemberIsInvalid(dec->members[member].representativeMember); +} + +static spqr_member findMember(SCIP_NETWORKDECOMP *dec, spqr_member member) { + assert(dec); + assert(member < dec->memMembers); + assert(SPQRmemberIsValid(member)); + + spqr_member current = member; + spqr_member next; + + //traverse down tree to find the root + while (SPQRmemberIsValid(next = dec->members[current].representativeMember)) { + current = next; + assert(current < dec->memMembers); + } + + spqr_member root = current; + current = member; + + //update all pointers along path to point to root, flattening the tree + while (SPQRmemberIsValid(next = dec->members[current].representativeMember)) { + dec->members[current].representativeMember = root; + current = next; + assert(current < dec->memMembers); + } + return root; +} + +static spqr_member findMemberNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_member member) { + assert(dec); + assert(member < dec->memMembers); + assert(SPQRmemberIsValid(member)); + + spqr_member current = member; + spqr_member next; + + //traverse down tree to find the root + while (SPQRmemberIsValid(next = dec->members[current].representativeMember)) { + current = next; + assert(current < dec->memMembers); + } + + spqr_member root = current; + return root; +} + +static spqr_member mergeMembers(SCIP_NETWORKDECOMP *dec, spqr_member first, spqr_member second) { + assert(dec); + assert(memberIsRepresentative(dec, first)); + assert(memberIsRepresentative(dec, second)); + assert(first != second); //We cannot merge a member into itself + assert(first < dec->memMembers); + assert(second < dec->memMembers); + + //The rank is stored as a negative number: we decrement it making the negative number larger. + // We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement. + spqr_member firstRank = dec->members[first].representativeMember; + spqr_member secondRank = dec->members[second].representativeMember; + if (firstRank > secondRank) { + swap_ints(&first, &second); + } + dec->members[second].representativeMember = first; + if (firstRank == secondRank) { + --dec->members[first].representativeMember; + } + return first; +} + +static spqr_member findArcMember(SCIP_NETWORKDECOMP *dec, spqr_arc arc) { + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + + spqr_member representative = findMember(dec, dec->arcs[arc].member); + dec->arcs[arc].member = representative; + return representative; +} + +static spqr_member findArcMemberNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_arc arc) { + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + + spqr_member representative = findMemberNoCompression(dec, dec->arcs[arc].member); + return representative; +} + +static spqr_member findMemberParent(SCIP_NETWORKDECOMP *dec, spqr_member member) { + assert(dec); + assert(member < dec->memMembers); + assert(SPQRmemberIsValid(member)); + assert(memberIsRepresentative(dec,member)); + + + if(SPQRmemberIsInvalid(dec->members[member].parentMember)){ + return dec->members[member].parentMember; + } + spqr_member parent_representative = findMember(dec, dec->members[member].parentMember); + dec->members[member].parentMember = parent_representative; + + return parent_representative; +} + +static spqr_member findMemberParentNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_member member) { + assert(dec); + assert(member < dec->memMembers); + assert(SPQRmemberIsValid(member)); + assert(memberIsRepresentative(dec,member)); + + if(SPQRmemberIsInvalid(dec->members[member].parentMember)){ + return dec->members[member].parentMember; + } + spqr_member parent_representative = findMemberNoCompression(dec, dec->members[member].parentMember); + return parent_representative; +} + +static spqr_member findArcChildMember(SCIP_NETWORKDECOMP *dec, spqr_arc arc) { + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + + spqr_member representative = findMember(dec, dec->arcs[arc].childMember); + dec->arcs[arc].childMember = representative; + return representative; +} + +static spqr_member findArcChildMemberNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_arc arc) { + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + + spqr_member representative = findMemberNoCompression(dec, dec->arcs[arc].childMember); + return representative; +} + +// Only accounts for CHILD markers, not parent markers! +static SCIP_Bool arcIsMarker(const SCIP_NETWORKDECOMP *dec, spqr_arc arc) { + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + + return SPQRmemberIsValid(dec->arcs[arc].childMember); +} + +static SCIP_Bool arcIsTree(const SCIP_NETWORKDECOMP *dec, spqr_arc arc) { + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + + return SPQRelementIsRow(dec->arcs[arc].element); +} + +typedef struct { + spqr_arc representative; + SCIP_Bool reversed; +} ArcSign; +//find +#ifndef NDEBUG +static SCIP_Bool arcIsRepresentative(const SCIP_NETWORKDECOMP *dec, spqr_arc arc) { + assert(dec); + assert(arc < dec->memArcs); + assert(SPQRarcIsValid(arc)); + + return SPQRarcIsInvalid(dec->arcs[arc].representative); +} +#endif +static ArcSign findArcSign(SCIP_NETWORKDECOMP *dec, spqr_arc arc) { + assert(dec); + assert(arc < dec->memArcs); + assert(SPQRarcIsValid(arc)); + + spqr_arc current = arc; + spqr_arc next; + + SCIP_Bool totalReversed = dec->arcs[current].reversed; + //traverse down tree to find the root + while (SPQRarcIsValid(next = dec->arcs[current].representative)) { + current = next; + assert(current < dec->memArcs); + //swap boolean only if new arc is reversed + totalReversed = (totalReversed != dec->arcs[current].reversed); + } + + spqr_arc root = current; + current = arc; + + SCIP_Bool currentReversed = totalReversed != dec->arcs[root].reversed; + //update all pointers along path to point to root, flattening the tree + + while (SPQRarcIsValid(next = dec->arcs[current].representative)) { + SCIP_Bool wasReversed = dec->arcs[current].reversed; + + dec->arcs[current].reversed = currentReversed; + currentReversed = (currentReversed != wasReversed); + + dec->arcs[current].representative = root; + current = next; + assert(current < dec->memArcs); + } + + ArcSign sign; + sign.reversed = totalReversed; + sign.representative = root; + return sign; +} + +static ArcSign findArcSignNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_arc arc) { + assert(dec); + assert(arc < dec->memArcs); + assert(SPQRarcIsValid(arc)); + + spqr_arc current = arc; + spqr_arc next; + + SCIP_Bool totalReversed = dec->arcs[current].reversed; + //traverse down tree to find the root + while (SPQRarcIsValid(next = dec->arcs[current].representative)) { + current = next; + assert(current < dec->memArcs); + //swap boolean only if new arc is reversed + totalReversed = (totalReversed != dec->arcs[current].reversed); + } + ArcSign sign; + sign.reversed = totalReversed; + sign.representative = current; + return sign; +} +//Find the arc tail/head, but accounting for reflection +static spqr_node findEffectiveArcHead(SCIP_NETWORKDECOMP *dec, spqr_arc arc){ + assert(dec); + if(findArcSign(dec,arc).reversed){ + return findArcTail(dec,arc); + }else{ + return findArcHead(dec,arc); + } +} +static spqr_node findEffectiveArcTail(SCIP_NETWORKDECOMP *dec, spqr_arc arc){ + assert(dec); + if(findArcSign(dec,arc).reversed){ + return findArcHead(dec,arc); + }else{ + return findArcTail(dec,arc); + } +} +static spqr_node findEffectiveArcHeadNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_arc arc){ + assert(dec); + if(findArcSignNoCompression(dec,arc).reversed){ + return findArcTailNoCompression(dec,arc); + }else{ + return findArcHeadNoCompression(dec,arc); + } +} +static spqr_node findEffectiveArcTailNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_arc arc){ + assert(dec); + if(findArcSignNoCompression(dec,arc).reversed){ + return findArcHeadNoCompression(dec,arc); + }else{ + return findArcTailNoCompression(dec,arc); + } +} +///Merge for signed union find of the arc directions. +///Is not symmetric, in the sense that the arc directions of coponent first are guaranteed not to change but those of second may change +///Based on whether one wants the reflection or not +static spqr_arc mergeArcSigns(SCIP_NETWORKDECOMP *dec, spqr_arc first, spqr_arc second, + SCIP_Bool reflectRelative) { + assert(dec); + assert(arcIsRepresentative(dec, first)); + assert(arcIsRepresentative(dec, second)); + assert(first != second); //We cannot merge a member into itself + assert(first < dec->memArcs); + assert(second < dec->memArcs); + + //The rank is stored as a negative number: we decrement it making the negative number larger. + // We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement. + spqr_member firstRank = dec->arcs[first].representative; + spqr_member secondRank = dec->arcs[second].representative; + + if (firstRank > secondRank) { + swap_ints(&first, &second); + } + dec->arcs[second].representative = first; + if (firstRank == secondRank) { + --dec->arcs[first].representative; + } + //These boolean formula's cover all 16 possible cases, such that the relative orientation of the first is not changed + SCIP_Bool equal = dec->arcs[first].reversed == dec->arcs[second].reversed; + dec->arcs[second].reversed = (equal == reflectRelative); + if(firstRank > secondRank){ + dec->arcs[first].reversed = (dec->arcs[first].reversed != reflectRelative); + } + return first; +} + +static SCIP_Bool arcIsReversedNonRigid(const SCIP_NETWORKDECOMP *dec, spqr_arc arc){ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + + return dec->arcs[arc].reversed; +} + + +static spqr_element arcGetElement(const SCIP_NETWORKDECOMP * dec, spqr_arc arc){ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + + return dec->arcs[arc].element; +} +SCIP_Bool SCIPNetworkDecompositionContainsRow(const SCIP_NETWORKDECOMP * dec, spqr_row row){ + assert(SPQRrowIsValid(row) && (int) row < dec->memRows); + assert(dec); + return SPQRarcIsValid(dec->rowArcs[row]); +} +SCIP_Bool SCIPNetworkDecompositionContainsColumn(const SCIP_NETWORKDECOMP *dec, spqr_col col){ + assert(SPQRcolIsValid(col) && (int) col < dec->memColumns); + assert(dec); + return SPQRarcIsValid(dec->columnArcs[col]); +} +static void setDecompositionColumnArc(SCIP_NETWORKDECOMP *dec, spqr_col col, spqr_arc arc){ + assert(SPQRcolIsValid(col) && (int)col < dec->memColumns); + assert(dec); + assert(SPQRarcIsValid(arc)); + dec->columnArcs[col] = arc; +} +static void setDecompositionRowArc(SCIP_NETWORKDECOMP *dec, spqr_row row, spqr_arc arc){ + assert(SPQRrowIsValid(row) && (int) row < dec->memRows); + assert(dec); + assert(SPQRarcIsValid(arc)); + dec->rowArcs[row] = arc; +} +static spqr_arc getDecompositionColumnArc(const SCIP_NETWORKDECOMP *dec, spqr_col col){ + assert(SPQRcolIsValid(col) && (int) col < dec->memColumns); + assert(dec); + return dec->columnArcs[col]; +} +static spqr_arc getDecompositionRowArc(const SCIP_NETWORKDECOMP *dec, spqr_row row){ + assert(SPQRrowIsValid(row) && (int) row < dec->memRows); + assert(dec); + return dec->rowArcs[row]; +} + +SCIP_RETCODE SCIPNetworkDecompositionCreate(SCIP* env, SCIP_NETWORKDECOMP **pDecomposition, int numRows, int numColumns){ + assert(env); + assert(pDecomposition); + assert(!*pDecomposition); + + SCIP_CALL(SCIPallocBlockMemory(env, pDecomposition)); + SCIP_NETWORKDECOMP *dec = *pDecomposition; + dec->env = env; + + //Initialize arc array data + int initialMemArcs = 8; + { + assert(initialMemArcs > 0); + dec->memArcs = initialMemArcs; + dec->numArcs = 0; + SCIP_CALL(SCIPallocBlockMemoryArray(env, &dec->arcs, (size_t) dec->memArcs)); + for (spqr_arc i = 0; i < dec->memArcs; ++i) { + dec->arcs[i].arcListNode.next = i + 1; + dec->arcs[i].member = SPQR_INVALID_MEMBER; + } + dec->arcs[dec->memArcs - 1].arcListNode.next = SPQR_INVALID_ARC; + dec->firstFreeArc = 0; + } + + //Initialize member array data + int initialMemMembers = 8; + { + assert(initialMemMembers > 0); + dec->memMembers = initialMemMembers; + dec->numMembers = 0; + SCIP_CALL(SCIPallocBlockMemoryArray(env, &dec->members, (size_t) dec->memMembers)); + } + + //Initialize node array data + int initialMemNodes = 8; + { + assert(initialMemNodes > 0); + dec->memNodes = initialMemNodes; + dec->numNodes = 0; + SCIP_CALL(SCIPallocBlockMemoryArray(env, &dec->nodes, (size_t) dec->memNodes)); + } + + //Initialize mappings for rows + { + dec->memRows = numRows; + SCIP_CALL(SCIPallocBlockMemoryArray(env, &dec->rowArcs, (size_t) dec->memRows)); + for (int i = 0; i < dec->memRows; ++i) { + dec->rowArcs[i] = SPQR_INVALID_ARC; + } + } + //Initialize mappings for columns + { + dec->memColumns = numColumns; + dec->numColumns = 0; + SCIP_CALL(SCIPallocBlockMemoryArray(env, &dec->columnArcs, (size_t) dec->memColumns)); + for (int i = 0; i < dec->memColumns; ++i) { + dec->columnArcs[i] = SPQR_INVALID_ARC; + } + } + + dec->numConnectedComponents = 0; + return SCIP_OKAY; +} + +void SCIPNetworkDecompositionFree(SCIP_NETWORKDECOMP **pDec){ + assert(pDec); + assert(*pDec); + + SCIP_NETWORKDECOMP *dec = *pDec; + SCIPfreeBlockMemoryArray(dec->env, &dec->columnArcs,dec->memColumns); + SCIPfreeBlockMemoryArray(dec->env, &dec->rowArcs,dec->memRows); + SCIPfreeBlockMemoryArray(dec->env, &dec->nodes,dec->memNodes); + SCIPfreeBlockMemoryArray(dec->env, &dec->members,dec->memMembers); + SCIPfreeBlockMemoryArray(dec->env, &dec->arcs,dec->memArcs); + + SCIPfreeBlockMemory(dec->env, pDec); + +} +static spqr_arc getFirstMemberArc(const SCIP_NETWORKDECOMP * dec, spqr_member member){ + assert(dec); + assert(SPQRmemberIsValid(member)); + assert(member < dec->memMembers); + return dec->members[member].firstArc; +} +static spqr_arc getNextMemberArc(const SCIP_NETWORKDECOMP * dec, spqr_arc arc){ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + arc = dec->arcs[arc].arcListNode.next; + return arc; +} +static spqr_arc getPreviousMemberArc(const SCIP_NETWORKDECOMP *dec, spqr_arc arc){ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + arc = dec->arcs[arc].arcListNode.previous; + return arc; +} + +static void addArcToMemberArcList(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_member member){ + spqr_arc firstMemberArc = getFirstMemberArc(dec, member); + + if(SPQRarcIsValid(firstMemberArc)){ + spqr_arc lastMemberArc = getPreviousMemberArc(dec, firstMemberArc); + dec->arcs[arc].arcListNode.next = firstMemberArc; + dec->arcs[arc].arcListNode.previous = lastMemberArc; + dec->arcs[firstMemberArc].arcListNode.previous = arc; + dec->arcs[lastMemberArc].arcListNode.next = arc; + }else{ + assert(dec->members[member].numArcs == 0); + dec->arcs[arc].arcListNode.next = arc; + dec->arcs[arc].arcListNode.previous = arc; + } + dec->members[member].firstArc = arc; + ++(dec->members[member].numArcs); +} +static SCIP_RETCODE createArc(SCIP_NETWORKDECOMP *dec, spqr_member member,SCIP_Bool reversed, spqr_arc *pArc) { + assert(dec); + assert(pArc); + assert(SPQRmemberIsInvalid(member) || memberIsRepresentative(dec, member)); + + spqr_arc index = dec->firstFreeArc; + if (SPQRarcIsValid(index)) { + dec->firstFreeArc = dec->arcs[index].arcListNode.next; + } else { + //Enlarge array, no free nodes in arc list + int newSize = 2 * dec->memArcs; + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &dec->arcs, dec->memArcs, (size_t) newSize)); + for (int i = dec->memArcs + 1; i < newSize; ++i) { + dec->arcs[i].arcListNode.next = i + 1; + dec->arcs[i].member = SPQR_INVALID_MEMBER; + } + dec->arcs[newSize - 1].arcListNode.next = SPQR_INVALID_ARC; + dec->firstFreeArc = dec->memArcs + 1; + index = dec->memArcs; + dec->memArcs = newSize; + } + + dec->arcs[index].tail = SPQR_INVALID_NODE; + dec->arcs[index].head = SPQR_INVALID_NODE; + dec->arcs[index].member = member; + dec->arcs[index].childMember = SPQR_INVALID_MEMBER; + dec->arcs[index].reversed = reversed; + + dec->arcs[index].headArcListNode.next = SPQR_INVALID_ARC; + dec->arcs[index].headArcListNode.previous = SPQR_INVALID_ARC; + dec->arcs[index].tailArcListNode.next = SPQR_INVALID_ARC; + dec->arcs[index].tailArcListNode.previous = SPQR_INVALID_ARC; + + dec->numArcs++; + + *pArc = index; + + return SCIP_OKAY; +} +static SCIP_RETCODE createRowArc(SCIP_NETWORKDECOMP *dec, spqr_member member, spqr_arc *pArc, + spqr_row row, SCIP_Bool reversed){ + SCIP_CALL(createArc(dec,member,reversed,pArc)); + setDecompositionRowArc(dec,row,*pArc); + addArcToMemberArcList(dec,*pArc,member); + dec->arcs[*pArc].element = SPQRrowToElement(row); + + return SCIP_OKAY; +} +static SCIP_RETCODE createColumnArc(SCIP_NETWORKDECOMP *dec, spqr_member member, spqr_arc *pArc, + spqr_col column, SCIP_Bool reversed){ + SCIP_CALL(createArc(dec,member,reversed,pArc)); + setDecompositionColumnArc(dec,column,*pArc); + addArcToMemberArcList(dec,*pArc,member); + dec->arcs[*pArc].element = SPQRcolumnToElement(column); + + return SCIP_OKAY; +} +static SCIP_RETCODE createMember(SCIP_NETWORKDECOMP *dec, SPQRMemberType type, spqr_member * pMember){ + assert(dec); + assert(pMember); + + if(dec->numMembers == dec->memMembers){ + int newSize = dec->memMembers * 2; + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&dec->members,dec->memMembers,newSize)); + dec->memMembers = newSize; + + } + SPQRNetworkDecompositionMember *data = &dec->members[dec->numMembers]; + data->markerOfParent = SPQR_INVALID_ARC; + data->markerToParent = SPQR_INVALID_ARC; + data->firstArc = SPQR_INVALID_ARC; + data->representativeMember = SPQR_INVALID_MEMBER; + data->numArcs = 0; + data->parentMember = SPQR_INVALID_MEMBER; + data->type = type; + + *pMember = dec->numMembers; + + dec->numMembers++; + return SCIP_OKAY; +} + +static SCIP_RETCODE createNode(SCIP_NETWORKDECOMP *dec, spqr_node * pNode){ + + if(dec->numNodes == dec->memNodes){ + int newSize = dec->memNodes * 2; + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&dec->nodes,dec->memNodes,newSize)); + dec->memNodes = newSize; + } + *pNode = dec->numNodes; + dec->nodes[dec->numNodes].representativeNode = SPQR_INVALID_NODE; + dec->nodes[dec->numNodes].firstArc = SPQR_INVALID_ARC; + dec->nodes[dec->numNodes].numArcs = 0; + dec->numNodes++; + + return SCIP_OKAY; +} +static void removeArcFromNodeArcList(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_node node, SCIP_Bool nodeIsHead){ + SPQRNetworkDecompositionArcListNode * arcListNode = nodeIsHead ? &dec->arcs[arc].headArcListNode : &dec->arcs[arc].tailArcListNode; + + if(dec->nodes[node].numArcs == 1){ + dec->nodes[node].firstArc = SPQR_INVALID_ARC; + }else{ + spqr_arc next_arc = arcListNode->next; + spqr_arc prev_arc = arcListNode->previous; + SPQRNetworkDecompositionArcListNode * nextListNode = findArcHead(dec, next_arc) == node ? &dec->arcs[next_arc].headArcListNode : &dec->arcs[next_arc].tailArcListNode; + SPQRNetworkDecompositionArcListNode * prevListNode = findArcHead(dec, prev_arc) == node ? &dec->arcs[prev_arc].headArcListNode : &dec->arcs[prev_arc].tailArcListNode; + + nextListNode->previous = prev_arc; + prevListNode->next = next_arc; + + if(dec->nodes[node].firstArc == arc){ + dec->nodes[node].firstArc = next_arc; + } + } + --(dec->nodes[node].numArcs); +} +static void addArcToNodeArcList(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_node node, SCIP_Bool nodeIsHead){ + assert(nodeIsRepresentative(dec,node)); + + spqr_arc firstNodeArc = getFirstNodeArc(dec, node); + + SPQRNetworkDecompositionArcListNode * arcListNode = nodeIsHead ? &dec->arcs[arc].headArcListNode : &dec->arcs[arc].tailArcListNode; + if(SPQRarcIsValid(firstNodeArc)){ + SCIP_Bool nextIsHead = findArcHead(dec,firstNodeArc) == node; + SPQRNetworkDecompositionArcListNode *nextListNode = nextIsHead ? &dec->arcs[firstNodeArc].headArcListNode : &dec->arcs[firstNodeArc].tailArcListNode; + spqr_arc lastNodeArc = nextListNode->previous; + + arcListNode->next = firstNodeArc; + arcListNode->previous = lastNodeArc; + + + SCIP_Bool previousIsHead = findArcHead(dec,lastNodeArc) == node; + SPQRNetworkDecompositionArcListNode *previousListNode = previousIsHead ? &dec->arcs[lastNodeArc].headArcListNode : &dec->arcs[lastNodeArc].tailArcListNode; + previousListNode->next = arc; + nextListNode->previous = arc; + + }else{ + arcListNode->next = arc; + arcListNode->previous = arc; + } + dec->nodes[node].firstArc = arc; + ++dec->nodes[node].numArcs; + if(nodeIsHead){ + dec->arcs[arc].head = node; + }else{ + dec->arcs[arc].tail = node; + } +} +static void setArcHeadAndTail(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_node head, spqr_node tail){ + addArcToNodeArcList(dec,arc,head,TRUE); + addArcToNodeArcList(dec,arc,tail,FALSE); +} +static void clearArcHeadAndTail(SCIP_NETWORKDECOMP *dec, spqr_arc arc){ + removeArcFromNodeArcList(dec,arc,findArcHead(dec,arc),TRUE); + removeArcFromNodeArcList(dec,arc,findArcTail(dec,arc),FALSE); + dec->arcs[arc].head = SPQR_INVALID_NODE; + dec->arcs[arc].tail = SPQR_INVALID_NODE; +} +static void changeArcHead(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_node oldHead, spqr_node newHead){ + assert(nodeIsRepresentative(dec,oldHead)); + assert(nodeIsRepresentative(dec,newHead)); + removeArcFromNodeArcList(dec,arc,oldHead,TRUE); + addArcToNodeArcList(dec,arc,newHead,TRUE); +} +static void changeArcTail(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_node oldTail, spqr_node newTail){ + assert(nodeIsRepresentative(dec,oldTail)); + assert(nodeIsRepresentative(dec,newTail)); + removeArcFromNodeArcList(dec,arc,oldTail,FALSE); + addArcToNodeArcList(dec,arc,newTail,FALSE); +} + +static int nodeDegree(SCIP_NETWORKDECOMP *dec, spqr_node node){ + assert(dec); + assert(SPQRnodeIsValid(node)); + assert(node < dec->memNodes); + return dec->nodes[node].numArcs; +} +static SPQRMemberType getMemberType(const SCIP_NETWORKDECOMP *dec, spqr_member member){ + assert(dec); + assert(SPQRmemberIsValid(member)); + assert(member < dec->memMembers); + assert(memberIsRepresentative(dec,member)); + return dec->members[member].type; +} +static void updateMemberType(const SCIP_NETWORKDECOMP *dec, spqr_member member, SPQRMemberType type){ + assert(dec); + assert(SPQRmemberIsValid(member)); + assert(member < dec->memMembers); + assert(memberIsRepresentative(dec,member)); + + dec->members[member].type = type; +} +static spqr_arc markerToParent(const SCIP_NETWORKDECOMP *dec, spqr_member member){ + assert(dec); + assert(SPQRmemberIsValid(member)); + assert(member < dec->memMembers); + assert(memberIsRepresentative(dec,member)); + return dec->members[member].markerToParent; +} + +static void updateMemberParentInformation(SCIP_NETWORKDECOMP *dec, const spqr_member newMember, const spqr_member toRemove){ + assert(memberIsRepresentative(dec,newMember)); + assert(findMemberNoCompression(dec,toRemove) == newMember); + + dec->members[newMember].markerOfParent = dec->members[toRemove].markerOfParent; + dec->members[newMember].markerToParent = dec->members[toRemove].markerToParent; + dec->members[newMember].parentMember = dec->members[toRemove].parentMember; + + dec->members[toRemove].markerOfParent = SPQR_INVALID_ARC; + dec->members[toRemove].markerToParent = SPQR_INVALID_ARC; + dec->members[toRemove].parentMember = SPQR_INVALID_MEMBER; +} +static spqr_arc markerOfParent(const SCIP_NETWORKDECOMP *dec, spqr_member member) { + assert(dec); + assert(SPQRmemberIsValid(member)); + assert(member < dec->memMembers); + assert(memberIsRepresentative(dec,member)); + return dec->members[member].markerOfParent; +} + + + +static int getNumMemberArcs(const SCIP_NETWORKDECOMP * dec, spqr_member member){ + assert(dec); + assert(SPQRmemberIsValid(member)); + assert(member < dec->memMembers); + assert(memberIsRepresentative(dec,member)); + return dec->members[member].numArcs; +} + +static int getNumNodes(const SCIP_NETWORKDECOMP *dec){ + assert(dec); + return dec->numNodes; +} +static int getNumMembers(const SCIP_NETWORKDECOMP *dec){ + assert(dec); + return dec->numMembers; +} +static SCIP_RETCODE createStandaloneParallel(SCIP_NETWORKDECOMP *dec, spqr_col * columns, SCIP_Bool * reversed, + int num_columns, spqr_row row, spqr_member * pMember){ + spqr_member member; + SCIP_CALL(createMember(dec, num_columns <= 1 ? SPQR_MEMBERTYPE_LOOP : SPQR_MEMBERTYPE_PARALLEL, &member)); + + spqr_arc row_arc; + SCIP_CALL(createRowArc(dec,member,&row_arc,row,num_columns <= 1)); + + spqr_arc col_arc; + for (int i = 0; i < num_columns; ++i) { + SCIP_CALL(createColumnArc(dec,member,&col_arc,columns[i],reversed[i])); + } + *pMember = member; + + ++dec->numConnectedComponents; + return SCIP_OKAY; +} + +//TODO: fix tracking connectivity more cleanly, should not be left up to the algorithms ideally +static SCIP_RETCODE createConnectedParallel(SCIP_NETWORKDECOMP *dec, spqr_col * columns, SCIP_Bool * reversed, + int num_columns, spqr_row row, spqr_member * pMember){ + spqr_member member; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &member)); + + spqr_arc row_arc; + SCIP_CALL(createRowArc(dec,member,&row_arc,row,FALSE)); + + spqr_arc col_arc; + for (int i = 0; i < num_columns; ++i) { + SCIP_CALL(createColumnArc(dec,member,&col_arc,columns[i],reversed[i])); + } + *pMember = member; + + return SCIP_OKAY; +} + +static SCIP_RETCODE createStandaloneSeries(SCIP_NETWORKDECOMP *dec, spqr_row * rows, SCIP_Bool *reversed, + int numRows, spqr_col col, spqr_member * pMember){ + spqr_member member; + SCIP_CALL(createMember(dec, numRows <= 1 ? SPQR_MEMBERTYPE_LOOP : SPQR_MEMBERTYPE_SERIES, &member)); + + spqr_arc colArc; + SCIP_CALL(createColumnArc(dec,member,&colArc,col,FALSE)); + + spqr_arc rowArc; + for (int i = 0; i < numRows; ++i) { + SCIP_CALL(createRowArc(dec,member,&rowArc,rows[i],!reversed[i])); + } + *pMember = member; + ++dec->numConnectedComponents; + return SCIP_OKAY; +} +static SCIP_RETCODE createConnectedSeries(SCIP_NETWORKDECOMP *dec, spqr_row * rows, SCIP_Bool *reversed, + int numRows, spqr_col col, spqr_member * pMember){ + spqr_member member; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &member)); + + spqr_arc colArc; + SCIP_CALL(createColumnArc(dec,member,&colArc,col,FALSE)); + + spqr_arc rowArc; + for (int i = 0; i < numRows; ++i) { + SCIP_CALL(createRowArc(dec,member,&rowArc,rows[i],!reversed[i])); + } + *pMember = member; + return SCIP_OKAY; +} + +static void removeArcFromMemberArcList(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_member member){ + assert(findArcMemberNoCompression(dec,arc) == member); + assert(memberIsRepresentative(dec,member)); + + if(dec->members[member].numArcs == 1){ + dec->members[member].firstArc = SPQR_INVALID_ARC; + + }else{ + spqr_arc nextArc = dec->arcs[arc].arcListNode.next; + spqr_arc prevArc = dec->arcs[arc].arcListNode.previous; + + dec->arcs[nextArc].arcListNode.previous = prevArc; + dec->arcs[prevArc].arcListNode.next = nextArc; + + if(dec->members[member].firstArc == arc){ + dec->members[member].firstArc = nextArc; + } + } + + + --(dec->members[member].numArcs); +} + +typedef struct { + spqr_arc arc; + SCIP_Bool reversed; +} FindCycleCall; +static void process_arc(spqr_row * fundamental_cycle_arcs, int * num_cycle_arcs, + FindCycleCall * callStack, + int * callStackSize, + spqr_arc arc, + const SCIP_NETWORKDECOMP * dec, + SCIP_Bool * fundamental_cycle_direction, + SCIP_Bool arcIsReversed){ + assert(arcIsTree(dec,arc)); + if(!arcIsMarker(dec,arc)){ + spqr_member current_member = findArcMemberNoCompression(dec, arc); + if(markerToParent(dec,current_member) == arc){ + spqr_arc other_arc = markerOfParent(dec, current_member); + assert(!arcIsTree(dec,other_arc)); + callStack[*callStackSize].arc = other_arc; + callStack[*callStackSize].reversed = arcIsReversed; + ++(*callStackSize); + }else{ + spqr_element element = arcGetElement(dec,arc); + assert(SPQRelementIsRow(element)); + spqr_row row = SPQRelementToRow(element); + fundamental_cycle_arcs[*num_cycle_arcs] = row; + fundamental_cycle_direction[*num_cycle_arcs] = arcIsReversed; + ++(*num_cycle_arcs); + } + }else{ + spqr_member child_member = findArcChildMemberNoCompression(dec, arc); + spqr_arc other_arc = markerToParent(dec, child_member); + assert(!arcIsTree(dec,other_arc)); + callStack[*callStackSize].arc = other_arc; + callStack[*callStackSize].reversed = arcIsReversed; + ++(*callStackSize); + } +} + +static int decompositionGetFundamentalCycleRows(const SCIP_NETWORKDECOMP *dec, spqr_col column, spqr_row * output, + SCIP_Bool * computedSignStorage){ + spqr_arc arc = getDecompositionColumnArc(dec, column); + if(SPQRarcIsInvalid(arc)){ + return 0; + } + int num_rows = 0; + + FindCycleCall * callStack; + SCIP_RETCODE result = SCIPallocBlockMemoryArray(dec->env,&callStack,(size_t) dec->memRows); + if(result != SCIP_OKAY){ + return -1; + } + int callStackSize = 1; + callStack[0].arc = arc; + callStack[0].reversed = FALSE; + + SCIP_Bool * nodeVisited; + result = SCIPallocBlockMemoryArray(dec->env,&nodeVisited,(size_t) dec->numNodes); + if(result != SCIP_OKAY){ + return -1; + } + for (int i = 0; i < dec->numNodes; ++i) { + nodeVisited[i] = FALSE; + } + + typedef struct { + spqr_node node; + spqr_arc nodeArc; + } DFSCallData; + DFSCallData * pathSearchCallStack; + result = SCIPallocBlockMemoryArray(dec->env,&pathSearchCallStack,(size_t) dec->numNodes); + if(result != SCIP_OKAY){ + return -1; + } + int pathSearchCallStackSize = 0; + + while(callStackSize > 0){ + spqr_arc column_arc = callStack[callStackSize - 1].arc; + SCIP_Bool reverseEverything = callStack[callStackSize-1].reversed; + --callStackSize; + spqr_member column_arc_member = findArcMemberNoCompression(dec, column_arc); + switch(getMemberType(dec,column_arc_member)){ + case SPQR_MEMBERTYPE_RIGID: + { + spqr_node source = findEffectiveArcTailNoCompression(dec, column_arc); + spqr_node target = findEffectiveArcHeadNoCompression(dec, column_arc); + + assert(pathSearchCallStackSize == 0); + pathSearchCallStack[0].node = source; + pathSearchCallStack[0].nodeArc = getFirstNodeArc(dec,source); + pathSearchCallStackSize++; + while(pathSearchCallStackSize > 0){ + DFSCallData * dfsData = &pathSearchCallStack[pathSearchCallStackSize-1]; + nodeVisited[dfsData->node] = TRUE; + //cannot be a tree arc which is its parent + if(arcIsTree(dec,dfsData->nodeArc) && + (pathSearchCallStackSize <= 1 || dfsData->nodeArc != pathSearchCallStack[pathSearchCallStackSize-2].nodeArc)){ + spqr_node head = findEffectiveArcHeadNoCompression(dec, dfsData->nodeArc); + spqr_node tail = findEffectiveArcTailNoCompression(dec, dfsData->nodeArc); + spqr_node other = head == dfsData->node ? tail : head; + assert(other != dfsData->node); + assert(!nodeVisited[other]); + if(other == target){ + break; + } + //We go up a level: add new node to the call stack + + pathSearchCallStack[pathSearchCallStackSize].node = other; + pathSearchCallStack[pathSearchCallStackSize].nodeArc = getFirstNodeArc(dec,other); + ++pathSearchCallStackSize; + continue; + } + do{ + dfsData->nodeArc = getNextNodeArcNoCompression(dec,dfsData->nodeArc,dfsData->node); + if(dfsData->nodeArc == getFirstNodeArc(dec,dfsData->node)){ + --pathSearchCallStackSize; + dfsData = &pathSearchCallStack[pathSearchCallStackSize-1]; + }else{ + break; + } + }while(pathSearchCallStackSize > 0); + } + for (int i = 0; i < pathSearchCallStackSize; ++i) { + if(arcIsTree(dec,pathSearchCallStack[i].nodeArc)){ + SCIP_Bool arcReversedInPath = findEffectiveArcHeadNoCompression(dec,pathSearchCallStack[i].nodeArc) == pathSearchCallStack[i].node; + process_arc(output,&num_rows,callStack,&callStackSize,pathSearchCallStack[i].nodeArc,dec, + computedSignStorage,arcReversedInPath != reverseEverything); + } + } + + pathSearchCallStackSize = 0; + break; + } + case SPQR_MEMBERTYPE_PARALLEL: + { + SCIP_Bool columnReversed = arcIsReversedNonRigid(dec,column_arc); + + spqr_arc first_arc = getFirstMemberArc(dec, column_arc_member); + spqr_arc iter_arc = first_arc; + int tree_count = 0; + do + { + if(arcIsTree(dec,iter_arc)){ + SCIP_Bool treeIsReversed = arcIsReversedNonRigid(dec,iter_arc); + process_arc(output,&num_rows,callStack,&callStackSize,iter_arc,dec, + computedSignStorage,(columnReversed != treeIsReversed) != reverseEverything); + tree_count++; + } + iter_arc = getNextMemberArc(dec,iter_arc); + } + while(iter_arc != first_arc); + if(tree_count != 1){ + return -1; + } + break; + } + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_SERIES: + { + SCIP_Bool columnReversed = arcIsReversedNonRigid(dec,column_arc); + spqr_arc first_arc = getFirstMemberArc(dec, column_arc_member); + spqr_arc iter_arc = first_arc; + int nontree_count = 0; + do + { + if(arcIsTree(dec,iter_arc)){ + SCIP_Bool treeIsReversed = arcIsReversedNonRigid(dec,iter_arc); + process_arc(output,&num_rows,callStack,&callStackSize,iter_arc,dec, + computedSignStorage,(columnReversed == treeIsReversed) != reverseEverything); + }else{ + nontree_count++; + } + iter_arc = getNextMemberArc(dec,iter_arc); + } + while(iter_arc != first_arc); + if(nontree_count != 1){ + return -1; + } + break; + } + case SPQR_MEMBERTYPE_UNASSIGNED: + assert(FALSE); + } + } + SCIPfreeBlockMemoryArray(dec->env,&pathSearchCallStack,dec->numNodes); + SCIPfreeBlockMemoryArray(dec->env,&nodeVisited,dec->numNodes); + SCIPfreeBlockMemoryArray(dec->env,&callStack,dec->memRows); + return num_rows; +} +typedef struct{ + spqr_row row; + SCIP_Bool reversed; +} Nonzero; +static int qsort_comparison (const void * a, const void * b) +{ + Nonzero *s1 = (Nonzero *)a; + Nonzero *s2 = (Nonzero *)b; + + if(s1->row > s2->row) { + return 1; + } + else if(s1->row == s2->row) { + return 0; + } + else { + return -1; + } +} +SCIP_Bool SCIPNetworkDecompositionVerifyCycle(SCIP * scip, + const SCIP_NETWORKDECOMP * dec, spqr_col column, spqr_row * column_rows, + double * column_values, int num_rows, + spqr_row * computed_column_storage, + SCIP_Bool * computedSignStorage){ + int num_found_rows = decompositionGetFundamentalCycleRows(dec,column,computed_column_storage,computedSignStorage); + + if(num_found_rows != num_rows){ + return FALSE; + } + if(num_rows == 0){ + return TRUE; + } + Nonzero *array; + SCIP_RETCODE code = SCIPallocBufferArray(scip,&array,num_rows); + if(code != SCIP_OKAY){ + return FALSE; + } + for (int i = 0; i < num_rows; ++i) { + array[i].row = computed_column_storage[i]; + array[i].reversed = computedSignStorage[i]; + } + qsort(array,(size_t) num_rows,sizeof(Nonzero),qsort_comparison); + + Nonzero *secondArray; + code = SCIPallocBufferArray(scip,&secondArray,num_rows); + if(code != SCIP_OKAY){ + SCIPfreeBufferArray(scip,&array); + return FALSE; + } + for (int i = 0; i < num_rows; ++i) { + secondArray[i].row = column_rows[i]; + secondArray[i].reversed = column_values[i] < 0.0; + } + + qsort(secondArray,(size_t) num_rows,sizeof(Nonzero),qsort_comparison); + + SCIP_Bool good = TRUE; + for (int i = 0; i < num_rows; ++i) { + if(array[i].row != secondArray[i].row || array[i].reversed != secondArray[i].reversed){ + good = FALSE; + break; + } + } + SCIPfreeBufferArray(scip,&secondArray); + SCIPfreeBufferArray(scip,&array); + return good; +} + +static spqr_member largestMemberID(const SCIP_NETWORKDECOMP *dec){ + return dec->numMembers; +} +static spqr_arc largestArcID(const SCIP_NETWORKDECOMP *dec){ + return dec->numArcs; +} +static spqr_node largestNodeID(const SCIP_NETWORKDECOMP *dec){ + return dec->numNodes; +} +static int numConnectedComponents(const SCIP_NETWORKDECOMP *dec){ + return dec->numConnectedComponents; +} +static SCIP_RETCODE createChildMarker(SCIP_NETWORKDECOMP *dec, spqr_member member, spqr_member child, SCIP_Bool isTree, + spqr_arc * pArc, SCIP_Bool reversed){ + SCIP_CALL(createArc(dec,member,reversed,pArc)); + dec->arcs[*pArc].element = isTree ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; + dec->arcs[*pArc].childMember = child; + + addArcToMemberArcList(dec,*pArc,member); + return SCIP_OKAY; +} +static SCIP_RETCODE createParentMarker(SCIP_NETWORKDECOMP *dec, spqr_member member, SCIP_Bool isTree, spqr_member parent, spqr_arc parentMarker + , spqr_arc * arc,SCIP_Bool reversed){ + + SCIP_CALL(createArc(dec,member,reversed,arc)); + dec->arcs[*arc].element = isTree ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; + + addArcToMemberArcList(dec,*arc,member); + + dec->members[member].parentMember = parent; + dec->members[member].markerOfParent = parentMarker; + dec->members[member].markerToParent = *arc; + return SCIP_OKAY; +} +static SCIP_RETCODE createMarkerPair(SCIP_NETWORKDECOMP *dec, spqr_member parentMember, spqr_member childMember, + SCIP_Bool parentIsTree, + SCIP_Bool childMarkerReversed, + SCIP_Bool parentMarkerReversed){ + spqr_arc parentToChildMarker = SPQR_INVALID_ARC; + SCIP_CALL(createChildMarker(dec,parentMember,childMember,parentIsTree,&parentToChildMarker,childMarkerReversed)); + + spqr_arc childToParentMarker = SPQR_INVALID_ARC; + SCIP_CALL(createParentMarker(dec,childMember,!parentIsTree,parentMember,parentToChildMarker,&childToParentMarker,parentMarkerReversed)); + + return SCIP_OKAY; +} +static SCIP_RETCODE createMarkerPairWithReferences(SCIP_NETWORKDECOMP *dec, spqr_member parentMember, spqr_member childMember, SCIP_Bool parentIsTree, + SCIP_Bool childMarkerReversed, + SCIP_Bool parentMarkerReversed, + spqr_arc * parentToChild, spqr_arc *childToParent){ + SCIP_CALL(createChildMarker(dec,parentMember,childMember,parentIsTree,parentToChild,childMarkerReversed)); + SCIP_CALL(createParentMarker(dec,childMember,!parentIsTree,parentMember,*parentToChild,childToParent,parentMarkerReversed)); + + return SCIP_OKAY; +} + +static void moveArcToNewMember(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_member oldMember, spqr_member newMember){ + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + assert(dec); + + assert(memberIsRepresentative(dec,oldMember)); + assert(memberIsRepresentative(dec,newMember)); + //Need to change the arc's member, remove it from the current member list and add it to the new member list + assert(findArcMemberNoCompression(dec,arc) == oldMember); + + removeArcFromMemberArcList(dec,arc,oldMember); + addArcToMemberArcList(dec,arc,newMember); + + dec->arcs[arc].member = newMember; + + //If this arc has a childMember, update the information correctly! + spqr_member childMember = dec->arcs[arc].childMember; + if(SPQRmemberIsValid(childMember)){ + spqr_member childRepresentative = findArcChildMember(dec, arc); + dec->members[childRepresentative].parentMember = newMember; + } + //If this arc is a marker to the parent, update the child arc marker of the parent to reflect the move + if(dec->members[oldMember].markerToParent == arc){ + dec->members[newMember].markerToParent = arc; + dec->members[newMember].parentMember = dec->members[oldMember].parentMember; + dec->members[newMember].markerOfParent = dec->members[oldMember].markerOfParent; + + assert(findArcChildMemberNoCompression(dec,dec->members[oldMember].markerOfParent) == oldMember); + dec->arcs[dec->members[oldMember].markerOfParent].childMember = newMember; + } +} +static void mergeMemberArcList(SCIP_NETWORKDECOMP *dec, spqr_member toMergeInto, spqr_member toRemove){ + spqr_arc firstIntoArc = getFirstMemberArc(dec, toMergeInto); + spqr_arc firstFromArc = getFirstMemberArc(dec, toRemove); + assert(SPQRarcIsValid(firstIntoArc)); + assert(SPQRarcIsValid(firstFromArc)); + + spqr_arc lastIntoArc = getPreviousMemberArc(dec, firstIntoArc); + spqr_arc lastFromArc = getPreviousMemberArc(dec, firstFromArc); + + //Relink linked lists to merge them effectively + dec->arcs[firstIntoArc].arcListNode.previous = lastFromArc; + dec->arcs[lastIntoArc].arcListNode.next = firstFromArc; + dec->arcs[firstFromArc].arcListNode.previous = lastIntoArc; + dec->arcs[lastFromArc].arcListNode.next = firstIntoArc; + + //Clean up old + dec->members[toMergeInto].numArcs += dec->members[toRemove].numArcs; + dec->members[toRemove].numArcs = 0; + dec->members[toRemove].firstArc = SPQR_INVALID_ARC; + +} + +static void changeLoopToSeries(SCIP_NETWORKDECOMP * dec, spqr_member member){ + assert(SPQRmemberIsValid(member)); + assert(member < dec->memMembers); + assert(dec); + assert((getMemberType(dec,member) == SPQR_MEMBERTYPE_PARALLEL || getMemberType(dec, member) == SPQR_MEMBERTYPE_SERIES || + getMemberType(dec,member) == SPQR_MEMBERTYPE_LOOP) && getNumMemberArcs(dec, member) == 2); + assert(memberIsRepresentative(dec,member)); + dec->members[member].type = SPQR_MEMBERTYPE_SERIES; +} +static void changeLoopToParallel(SCIP_NETWORKDECOMP * dec, spqr_member member){ + assert(SPQRmemberIsValid(member)); + assert(member < dec->memMembers); + assert(dec); + assert((getMemberType(dec,member) == SPQR_MEMBERTYPE_PARALLEL || getMemberType(dec, member) == SPQR_MEMBERTYPE_SERIES || + getMemberType(dec,member) == SPQR_MEMBERTYPE_LOOP) && getNumMemberArcs(dec, member) == 2); + assert(memberIsRepresentative(dec,member)); + dec->members[member].type = SPQR_MEMBERTYPE_PARALLEL; +} +SCIP_Bool SCIPNetworkDecompositionIsMinimal(const SCIP_NETWORKDECOMP * dec){ + //Relies on parents/children etc. being set correctly in the tree + SCIP_Bool isMinimal = TRUE; + for (spqr_member member = 0; member < dec->numMembers; ++member) { + if (!memberIsRepresentative(dec, member) || getMemberType(dec,member) == SPQR_MEMBERTYPE_UNASSIGNED ){ + continue; + } + spqr_member memberParent = findMemberParentNoCompression(dec, member); + if(SPQRmemberIsValid(memberParent)){ + assert(memberIsRepresentative(dec,memberParent)); + SPQRMemberType memberType = getMemberType(dec, member); + SPQRMemberType parentType = getMemberType(dec, memberParent); + if(memberType == parentType && memberType != SPQR_MEMBERTYPE_RIGID){ + isMinimal = FALSE; + break; + } + } + + } + return isMinimal; +} + +static void decreaseNumConnectedComponents(SCIP_NETWORKDECOMP *dec, int by){ + dec->numConnectedComponents-= by; + assert(dec->numConnectedComponents >= 1); +} + +static void reorderComponent(SCIP_NETWORKDECOMP *dec, spqr_member newRoot){ + assert(dec); + assert(memberIsRepresentative(dec,newRoot)); + //If the newRoot has no parent, it is already the root, so then there's no need to reorder. + if(SPQRmemberIsValid(dec->members[newRoot].parentMember)){ + spqr_member member = findMemberParent(dec, newRoot); + spqr_member newParent = newRoot; + spqr_arc newMarkerToParent = dec->members[newRoot].markerOfParent; + spqr_arc markerOfNewParent = dec->members[newRoot].markerToParent; + + //Recursively update the parent + do{ + assert(SPQRmemberIsValid(member)); + assert(SPQRmemberIsValid(newParent)); + spqr_member oldParent = findMemberParent(dec, member); + spqr_arc oldMarkerToParent = dec->members[member].markerToParent; + spqr_arc oldMarkerOfParent = dec->members[member].markerOfParent; + + dec->members[member].markerToParent = newMarkerToParent; + dec->members[member].markerOfParent = markerOfNewParent; + dec->members[member].parentMember = newParent; + dec->arcs[markerOfNewParent].childMember = member; + dec->arcs[newMarkerToParent].childMember = SPQR_INVALID_MEMBER; + + if (SPQRmemberIsValid(oldParent)){ + newParent = member; + member = oldParent; + newMarkerToParent = oldMarkerOfParent; + markerOfNewParent = oldMarkerToParent; + }else{ + break; + } + }while(TRUE); + dec->members[newRoot].parentMember = SPQR_INVALID_MEMBER; + dec->members[newRoot].markerToParent = SPQR_INVALID_ARC; + dec->members[newRoot].markerOfParent = SPQR_INVALID_ARC; + + } +} + + +void SCIPNetworkDecompositionRemoveComponents(SCIP_NETWORKDECOMP *dec, const int * componentRows, + int numRows, const int * componentCols, int numCols){ + //The below just removes the 'link' but not the internal datastructures. + //This is sufficient for our purposes, as long as we do not re-introduce any of the 'negated' rows/columns back into the decomposition. + + for (int i = 0; i < numRows; ++i) { + spqr_row row = componentRows[i]; + if(SPQRarcIsValid(dec->rowArcs[row])){ + dec->rowArcs[row] = SPQR_INVALID_ARC; + } + } + + for (int i = 0; i < numCols; ++i) { + spqr_col col = componentCols[i]; + if(SPQRarcIsValid(dec->columnArcs[col])){ + dec->columnArcs[col] = SPQR_INVALID_ARC; + } + } +} + +#ifdef SCIP_DEBUG +//Debugging functions to print the decomposition +static char typeToChar(SPQRMemberType type){ + switch (type) { + case SPQR_MEMBERTYPE_RIGID: + return 'R'; + case SPQR_MEMBERTYPE_PARALLEL: + return 'P'; + case SPQR_MEMBERTYPE_SERIES: + return 'S'; + case SPQR_MEMBERTYPE_LOOP: + return 'L'; + default: + return '?'; + } +} + +static void arcToDot(FILE * stream, const SCIP_NETWORKDECOMP * dec, + spqr_arc arc, unsigned long dot_head, unsigned long dot_tail, SCIP_Bool useElementNames){ + assert(SPQRarcIsValid(arc)); + spqr_member member = findArcMemberNoCompression(dec, arc); + SPQRMemberType member_type = getMemberType(dec, member); + char type = typeToChar(member_type); + const char* color = arcIsTree(dec,arc) ? ",color=red" :",color=blue"; + + int arc_name = arc; + + if(markerToParent(dec,member) == arc){ + if(useElementNames){ + arc_name = -1; + } + fprintf(stream, " %c_%d_%lu -> %c_p_%d [label=\"%d\",style=dashed%s];\n", type, member, dot_tail, type, member, arc_name, color); + fprintf(stream, " %c_p_%d -> %c_%d_%lu [label=\"%d\",style=dashed%s];\n", type, member, type, member, dot_head, arc_name, color); + fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_tail); + fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_head); + fprintf(stream, " %c_p_%d [style=dashed];\n", type, member); + }else if(arcIsMarker(dec,arc)){ + spqr_member child = findArcChildMemberNoCompression(dec, arc); + char childType = typeToChar(getMemberType(dec,child)); + if(useElementNames){ + arc_name = -1; + } + fprintf(stream, " %c_%d_%lu -> %c_c_%d [label=\"%d\",style=dotted%s];\n", type, member, dot_tail, type, child, arc_name, color); + fprintf(stream, " %c_c_%d -> %c_%d_%lu [label=\"%d\",style=dotted%s];\n", type, child, type, member, dot_head, arc_name, color); + fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_tail); + fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_head); + fprintf(stream, " %c_c_%d [style=dotted];\n", type, child); + fprintf(stream, " %c_p_%d -> %c_c_%d [style=dashed,dir=forward];\n", childType, child, type, child); + }else{ + if(useElementNames){ + spqr_element element = dec->arcs[arc].element; + if(SPQRelementIsRow(element)){ + arc_name = (int) SPQRelementToRow(element); + }else{ + arc_name = (int) SPQRelementToColumn(element); + } + } + + fprintf(stream, " %c_%d_%lu -> %c_%d_%lu [label=\"%d \",style=bold%s];\n", type, member, dot_tail, type, member, dot_head, + arc_name, color); + fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_tail); + fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_head); + } +} + +static void decompositionToDot(FILE * stream, const SCIP_NETWORKDECOMP *dec, SCIP_Bool useElementNames ){ + fprintf(stream, "//decomposition\ndigraph decomposition{\n compound = TRUE;\n"); + for (spqr_member member = 0; member < dec->numMembers; ++member){ + if(!memberIsRepresentative(dec,member)) continue; + fprintf(stream," subgraph member_%d{\n",member); + switch(getMemberType(dec,member)){ + case SPQR_MEMBERTYPE_RIGID: + { + spqr_arc first_arc = getFirstMemberArc(dec, member); + spqr_arc arc = first_arc; + do + { + unsigned long arcHead = (unsigned long) findEffectiveArcHeadNoCompression(dec,arc); + unsigned long arcTail = (unsigned long) findEffectiveArcTailNoCompression(dec,arc); + arcToDot(stream,dec,arc,arcHead,arcTail,useElementNames); + arc = getNextMemberArc(dec,arc); + } + while(arc != first_arc); + break; + } + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_PARALLEL: + { + spqr_arc first_arc = getFirstMemberArc(dec, member); + spqr_arc arc = first_arc; + do + { + if(arcIsReversedNonRigid(dec,arc)){ + arcToDot(stream,dec,arc,1,0,useElementNames); + }else{ + arcToDot(stream,dec,arc,0,1,useElementNames); + } + arc = getNextMemberArc(dec,arc); + } + while(arc != first_arc); + break; + } + case SPQR_MEMBERTYPE_SERIES: + { + unsigned long i = 0; + unsigned long num_member_arcs = (unsigned long) getNumMemberArcs(dec, member); + spqr_arc first_arc = getFirstMemberArc(dec, member); + spqr_arc arc = first_arc; + do { + unsigned long head = i; + unsigned long tail = (i+1) % num_member_arcs; + if(arcIsReversedNonRigid(dec,arc)){ + unsigned long temp = head; + head = tail; + tail = temp; + } + arcToDot(stream, dec, arc, head,tail,useElementNames); + arc = getNextMemberArc(dec, arc); + i++; + } while (arc != first_arc); + break; + } + case SPQR_MEMBERTYPE_UNASSIGNED: + break; + } + fprintf(stream," }\n"); + } + fprintf(stream,"}\n"); +} +#endif + +static SCIP_RETCODE mergeGivenMemberIntoParent(SCIP_NETWORKDECOMP *dec, + spqr_member member, + spqr_member parent, + spqr_arc parentToChild, + spqr_arc childToParent, + SCIP_Bool headToHead, + spqr_member * mergedMember){ + assert(dec); + assert(SPQRmemberIsValid(member)); + assert(memberIsRepresentative(dec,member)); + assert(SPQRmemberIsValid(parent)); + assert(memberIsRepresentative(dec,parent)); + assert(findMemberParentNoCompression(dec,member) == parent); + assert(markerOfParent(dec, member) == parentToChild); + assert(markerToParent(dec, member) == childToParent); + + removeArcFromMemberArcList(dec,parentToChild,parent); + removeArcFromMemberArcList(dec,childToParent,member); + + spqr_node parentArcNodes[2] = {findEffectiveArcTail(dec, parentToChild), findEffectiveArcHead(dec, parentToChild)}; + spqr_node childArcNodes[2] = {findEffectiveArcTail(dec, childToParent), findEffectiveArcHead(dec, childToParent)}; + + clearArcHeadAndTail(dec,parentToChild); + clearArcHeadAndTail(dec,childToParent); + + spqr_node first = childArcNodes[headToHead ? 0 : 1]; + spqr_node second = childArcNodes[headToHead ? 1 : 0]; + { + spqr_node newNode = mergeNodes(dec, parentArcNodes[0], first); + spqr_node toRemoveFrom = newNode == first ? parentArcNodes[0] : first; + mergeNodeArcList(dec,newNode,toRemoveFrom); + } + { + spqr_node newNode = mergeNodes(dec, parentArcNodes[1], second); + spqr_node toRemoveFrom = newNode == second ? parentArcNodes[1] : second; + mergeNodeArcList(dec,newNode,toRemoveFrom); + } + + + spqr_member newMember = mergeMembers(dec, member, parent); + spqr_member toRemoveFrom = newMember == member ? parent : member; + mergeMemberArcList(dec,newMember,toRemoveFrom); + if(toRemoveFrom == parent){ + updateMemberParentInformation(dec,newMember,toRemoveFrom); + } + updateMemberType(dec, newMember, SPQR_MEMBERTYPE_RIGID); + *mergedMember = newMember; + return SCIP_OKAY; +} + +static int max(int a, int b){ + return (a > b) ? a : b; +} + +typedef int path_arc_id; +#define INVALID_PATH_ARC (-1) + +static SCIP_Bool pathArcIsInvalid(const path_arc_id arc) { + return arc < 0; +} + +static SCIP_Bool pathArcIsValid(const path_arc_id arc) { + return !pathArcIsInvalid(arc); +} + +typedef struct { + spqr_arc arc; + spqr_node arcHead; //These can be used in various places to prevent additional find()'s + spqr_node arcTail; + path_arc_id nextMember; + path_arc_id nextOverall; + SCIP_Bool reversed; +} PathArcListNode; + +typedef int reduced_member_id; +#define INVALID_REDUCED_MEMBER (-1) + +static SCIP_Bool reducedMemberIsInvalid(const reduced_member_id id) { + return id < 0; +} +static SCIP_Bool reducedMemberIsValid(const reduced_member_id id){ + return !reducedMemberIsInvalid(id); +} + +typedef int children_idx; + +typedef enum { + REDUCEDMEMBER_TYPE_UNASSIGNED = 0, + REDUCEDMEMBER_TYPE_CYCLE = 1, + REDUCEDMEMBER_TYPE_MERGED = 2, + REDUCEDMEMBER_TYPE_NOT_NETWORK = 3 +} ReducedMemberType; + +typedef enum { + INTO_HEAD = 0, + INTO_TAIL = 1, + OUT_HEAD = 2, + OUT_TAIL = 3 +} MemberPathType; + +static SCIP_Bool isInto(MemberPathType type){ + return type < 2; +} +static SCIP_Bool isHead(MemberPathType type){ + return (type & 1) == 0; +} + +typedef struct { + spqr_member member; + spqr_member rootMember; + int depth; + ReducedMemberType type; + reduced_member_id parent; + + children_idx firstChild; + children_idx numChildren; + + path_arc_id firstPathArc; + int numPathArcs; + + SCIP_Bool reverseArcs; + spqr_node rigidPathStart; + spqr_node rigidPathEnd; + + SCIP_Bool pathBackwards; + + int numPropagatedChildren; + int componentIndex; + + MemberPathType pathType; + reduced_member_id nextPathMember; + SCIP_Bool nextPathMemberIsParent; + spqr_arc pathSourceArc; + spqr_arc pathTargetArc; +} SPQRColReducedMember; + +typedef struct { + int rootDepth; + reduced_member_id root; + + reduced_member_id pathEndMembers[2]; + int numPathEndMembers; +} SPQRColReducedComponent; + +typedef struct { + reduced_member_id reducedMember; + reduced_member_id rootDepthMinimizer; +} MemberInfo; + +typedef struct { + spqr_member member; +} CreateReducedMembersCallstack; + +struct SCIP_NetworkColAddition { + SCIP_Bool remainsNetwork; + + SPQRColReducedMember *reducedMembers; + int memReducedMembers; + int numReducedMembers; + + SPQRColReducedComponent *reducedComponents; + int memReducedComponents; + int numReducedComponents; + + MemberInfo *memberInformation; + int memMemberInformation; + int numMemberInformation; + + reduced_member_id *childrenStorage; + int memChildrenStorage; + int numChildrenStorage; + + PathArcListNode *pathArcs; + int memPathArcs; + int numPathArcs; + path_arc_id firstOverallPathArc; + + int *nodeInPathDegree; + int *nodeOutPathDegree; + int memNodePathDegree; + + SCIP_Bool *arcInPath; + SCIP_Bool *arcInPathReversed; + int memArcsInPath; + + CreateReducedMembersCallstack * createReducedMembersCallStack; + int memCreateReducedMembersCallStack; + + spqr_col newColIndex; + + spqr_row *newRowArcs; + SCIP_Bool * newRowArcReversed; + int memNewRowArcs; + int numNewRowArcs; + + spqr_arc *decompositionRowArcs; + SCIP_Bool *decompositionArcReversed; + int memDecompositionRowArcs; + int numDecompositionRowArcs; + + spqr_member * leafMembers; + int numLeafMembers; + int memLeafMembers; +}; + +static void cleanupPreviousIteration(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol) { + assert(dec); + assert(newCol); + + path_arc_id pathArc = newCol->firstOverallPathArc; + while (pathArcIsValid(pathArc)) { + spqr_node head = newCol->pathArcs[pathArc].arcHead; + spqr_node tail = newCol->pathArcs[pathArc].arcTail; + if(SPQRnodeIsValid(head)){ + newCol->nodeInPathDegree[head] = 0; + } + if(SPQRnodeIsValid(tail)){ + newCol->nodeOutPathDegree[tail] = 0; + } + + spqr_arc arc = newCol->pathArcs[pathArc].arc; + if(arc < newCol->memArcsInPath){ + newCol->arcInPath[arc] = FALSE; + newCol->arcInPathReversed[arc] = FALSE; + } + pathArc = newCol->pathArcs[pathArc].nextOverall; + } +#ifndef NDEBUG + for (int i = 0; i < newCol->memArcsInPath; ++i) { + assert(newCol->arcInPath[i] == FALSE); + assert(newCol->arcInPathReversed[i] == FALSE); + } + + for (int i = 0; i < newCol->memNodePathDegree; ++i) { + assert(newCol->nodeInPathDegree[i] == 0); + assert(newCol->nodeOutPathDegree[i] == 0); + } +#endif + + newCol->firstOverallPathArc = INVALID_PATH_ARC; + newCol->numPathArcs = 0; +} + +SCIP_RETCODE SCIPNetworkColAdditionCreate(SCIP*env, SCIP_NETWORKCOLADDITION **pNewCol) { + assert(env); + + SCIP_CALL(SCIPallocBlockMemory(env, pNewCol)); + SCIP_NETWORKCOLADDITION *newCol = *pNewCol; + + newCol->remainsNetwork = FALSE; + newCol->reducedMembers = NULL; + newCol->memReducedMembers = 0; + newCol->numReducedMembers = 0; + + newCol->reducedComponents = NULL; + newCol->memReducedComponents = 0; + newCol->numReducedComponents = 0; + + newCol->memberInformation = NULL; + newCol->memMemberInformation = 0; + newCol->numMemberInformation = 0; + + newCol->childrenStorage = NULL; + newCol->memChildrenStorage = 0; + newCol->numChildrenStorage = 0; + + newCol->pathArcs = NULL; + newCol->memPathArcs = 0; + newCol->numPathArcs = 0; + newCol->firstOverallPathArc = INVALID_PATH_ARC; + + newCol->nodeInPathDegree = NULL; + newCol->nodeOutPathDegree = NULL; + newCol->memNodePathDegree = 0; + + newCol->arcInPath = NULL; + newCol->arcInPathReversed = NULL; + newCol->memArcsInPath = 0; + + newCol->createReducedMembersCallStack = NULL; + newCol->memCreateReducedMembersCallStack = 0; + + newCol->newColIndex = SPQR_INVALID_COL; + + newCol->newRowArcs = NULL; + newCol->newRowArcReversed = NULL; + newCol->memNewRowArcs = 0; + newCol->numNewRowArcs = 0; + + newCol->decompositionRowArcs = NULL; + newCol->decompositionArcReversed = NULL; + newCol->memDecompositionRowArcs = 0; + newCol->numDecompositionRowArcs = 0; + + newCol->leafMembers = NULL; + newCol->numLeafMembers = 0; + newCol->memLeafMembers = 0; + + return SCIP_OKAY; +} + +void SCIPNetworkColAdditionFree(SCIP * env, SCIP_NETWORKCOLADDITION **pNewCol) { + assert(env); + SCIP_NETWORKCOLADDITION *newCol = *pNewCol; + SCIPfreeBlockMemoryArray(env, &newCol->decompositionRowArcs,newCol->memDecompositionRowArcs); + SCIPfreeBlockMemoryArray(env, &newCol->decompositionArcReversed,newCol->memDecompositionRowArcs); + SCIPfreeBlockMemoryArray(env, &newCol->newRowArcs,newCol->memNewRowArcs); + SCIPfreeBlockMemoryArray(env, &newCol->newRowArcReversed,newCol->memNewRowArcs); + SCIPfreeBlockMemoryArray(env, &newCol->createReducedMembersCallStack,newCol->memCreateReducedMembersCallStack); + SCIPfreeBlockMemoryArray(env, &newCol->arcInPath,newCol->memArcsInPath); + SCIPfreeBlockMemoryArray(env, &newCol->arcInPathReversed,newCol->memArcsInPath); + SCIPfreeBlockMemoryArray(env, &newCol->nodeInPathDegree,newCol->memNodePathDegree); + SCIPfreeBlockMemoryArray(env, &newCol->nodeOutPathDegree,newCol->memNodePathDegree); + SCIPfreeBlockMemoryArray(env, &newCol->pathArcs,newCol->memPathArcs); + SCIPfreeBlockMemoryArray(env, &newCol->childrenStorage,newCol->memChildrenStorage); + SCIPfreeBlockMemoryArray(env, &newCol->memberInformation,newCol->memMemberInformation); + SCIPfreeBlockMemoryArray(env, &newCol->reducedComponents,newCol->memReducedComponents); + SCIPfreeBlockMemoryArray(env, &newCol->reducedMembers,newCol->memReducedMembers); + SCIPfreeBlockMemoryArray(env, &newCol->leafMembers,newCol->memLeafMembers); + + SCIPfreeBlockMemory(env, pNewCol); +} + + +static reduced_member_id createReducedMembersToRoot(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, const spqr_member firstMember ){ + assert(SPQRmemberIsValid(firstMember)); + + CreateReducedMembersCallstack * callstack = newCol->createReducedMembersCallStack; + callstack[0].member = firstMember; + int callDepth = 0; + + while(callDepth >= 0){ + spqr_member member = callstack[callDepth].member; + reduced_member_id reducedMember = newCol->memberInformation[member].reducedMember; + + SCIP_Bool reducedValid = reducedMemberIsValid(reducedMember); + if(!reducedValid) { + //reduced member was not yet created; we create it + reducedMember = newCol->numReducedMembers; + + SPQRColReducedMember *reducedMemberData = &newCol->reducedMembers[reducedMember]; + ++newCol->numReducedMembers; + + reducedMemberData->member = member; + reducedMemberData->numChildren = 0; + + reducedMemberData->type = REDUCEDMEMBER_TYPE_UNASSIGNED; + reducedMemberData->numPropagatedChildren = 0; + reducedMemberData->firstPathArc = INVALID_PATH_ARC; + reducedMemberData->numPathArcs = 0; + reducedMemberData->rigidPathStart = SPQR_INVALID_NODE; + reducedMemberData->rigidPathEnd = SPQR_INVALID_NODE; + + reducedMemberData->componentIndex = -1; + //The children are set later + + newCol->memberInformation[member].reducedMember = reducedMember; + assert(memberIsRepresentative(dec, member)); + spqr_member parentMember = findMemberParent(dec, member); + + if (SPQRmemberIsValid(parentMember)) { + //recursive call to parent member + ++callDepth; + assert(callDepth < newCol->memCreateReducedMembersCallStack); + callstack[callDepth].member = parentMember; + continue; + + } else { + //we found a new reduced decomposition component + + reducedMemberData->parent = INVALID_REDUCED_MEMBER; + reducedMemberData->depth = 0; + reducedMemberData->rootMember = member; + reducedMemberData->componentIndex = newCol->numReducedComponents; + + assert(newCol->numReducedComponents < newCol->memReducedComponents); + newCol->reducedComponents[newCol->numReducedComponents].root = reducedMember; + newCol->reducedComponents[newCol->numReducedComponents].numPathEndMembers = 0; + newCol->reducedComponents[newCol->numReducedComponents].pathEndMembers[0] = INVALID_REDUCED_MEMBER; + newCol->reducedComponents[newCol->numReducedComponents].pathEndMembers[1] = INVALID_REDUCED_MEMBER; + ++newCol->numReducedComponents; + } + } + if(reducedValid){ + assert(reducedMember < newCol->numReducedMembers); + //Reduced member was already created in earlier call + //update the depth of the root if appropriate + reduced_member_id * depthMinimizer = &newCol->memberInformation[newCol->reducedMembers[reducedMember].rootMember].rootDepthMinimizer; + if(reducedMemberIsInvalid(*depthMinimizer) || + newCol->reducedMembers[reducedMember].depth < newCol->reducedMembers[*depthMinimizer].depth){ + *depthMinimizer = reducedMember; + } + } + while(TRUE){ + --callDepth; + if(callDepth < 0 ) break; + spqr_member parentMember = callstack[callDepth + 1].member; + reduced_member_id parentReducedMember = newCol->memberInformation[parentMember].reducedMember; + spqr_member currentMember = callstack[callDepth].member; + reduced_member_id currentReducedMember = newCol->memberInformation[currentMember].reducedMember; + + SPQRColReducedMember *parentReducedMemberData = &newCol->reducedMembers[parentReducedMember]; + SPQRColReducedMember *reducedMemberData = &newCol->reducedMembers[currentReducedMember]; + + reducedMemberData->parent = parentReducedMember; + reducedMemberData->depth = parentReducedMemberData->depth + 1; + reducedMemberData->rootMember = parentReducedMemberData->rootMember; + //ensure that all newly created reduced members are pointing to the correct component + assert(parentReducedMemberData->componentIndex >= 0); + reducedMemberData->componentIndex = parentReducedMemberData->componentIndex; + + newCol->reducedMembers[parentReducedMember].numChildren++; + } + + } + + reduced_member_id returnedMember = newCol->memberInformation[callstack[0].member].reducedMember; + return returnedMember; +} + +static SCIP_RETCODE constructReducedDecomposition(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol) { + assert(dec); + assert(newCol); +#ifndef NDEBUG + for (int i = 0; i < newCol->memMemberInformation; ++i) { + assert(reducedMemberIsInvalid(newCol->memberInformation[i].reducedMember)); + } +#endif + newCol->numReducedComponents = 0; + newCol->numReducedMembers = 0; + if (newCol->numDecompositionRowArcs == 0) { //Early return in case the reduced decomposition will be empty + return SCIP_OKAY; + } + assert(newCol->numReducedMembers == 0); + assert(newCol->numReducedComponents == 0); + + int newSize = largestMemberID(dec); //Is this sufficient? + if (newSize > newCol->memReducedMembers) { + int newArraySize = max(2 * newCol->memReducedMembers, newSize); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->reducedMembers,newCol->memReducedMembers,newArraySize)); + newCol->memReducedMembers = newArraySize; + } + if (newSize > newCol->memMemberInformation) { + int updatedSize = max(2 * newCol->memMemberInformation, newSize); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->memberInformation,newCol->memMemberInformation,updatedSize)); + for (int i = newCol->memMemberInformation; i < updatedSize; ++i) { + newCol->memberInformation[i].reducedMember = INVALID_REDUCED_MEMBER; + newCol->memberInformation[i].rootDepthMinimizer = INVALID_REDUCED_MEMBER; + } + newCol->memMemberInformation = updatedSize; + + } + + int numComponents = numConnectedComponents(dec); + if (numComponents > newCol->memReducedComponents) { + int updatedSize = max(2 * newCol->memReducedComponents, numComponents); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->reducedComponents,newCol->memReducedComponents,updatedSize)); + newCol->memReducedComponents = updatedSize; + } + + int numMembers = getNumMembers(dec); + if (newCol->memCreateReducedMembersCallStack < numMembers) { + int updatedSize = max(2 * newCol->memCreateReducedMembersCallStack, numMembers); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->createReducedMembersCallStack, + newCol->memCreateReducedMembersCallStack, updatedSize)); + newCol->memCreateReducedMembersCallStack = updatedSize; + + } + + //Create the reduced members (recursively) + for (int i = 0; i < newCol->numDecompositionRowArcs; ++i) { + assert(i < newCol->memDecompositionRowArcs); + spqr_arc arc = newCol->decompositionRowArcs[i]; + spqr_member arcMember = findArcMember(dec, arc); + reduced_member_id reducedMember = createReducedMembersToRoot(dec, newCol, arcMember); + reduced_member_id *depthMinimizer = &newCol->memberInformation[newCol->reducedMembers[reducedMember].rootMember].rootDepthMinimizer; + if (reducedMemberIsInvalid(*depthMinimizer)) { + *depthMinimizer = reducedMember; + } + } + + //Set the reduced roots according to the root depth minimizers + for (int i = 0; i < newCol->numReducedComponents; ++i) { + SPQRColReducedComponent *component = &newCol->reducedComponents[i]; + spqr_member rootMember = newCol->reducedMembers[component->root].member; + reduced_member_id reducedMinimizer = newCol->memberInformation[rootMember].rootDepthMinimizer; + component->rootDepth = newCol->reducedMembers[reducedMinimizer].depth; + component->root = reducedMinimizer; + + //This simplifies code further down which does not need to be component-aware; just pretend that the reduced member is the new root. + newCol->reducedMembers[component->root].parent = INVALID_REDUCED_MEMBER; + assert(memberIsRepresentative(dec, rootMember)); + } + + //update the children array + int numTotalChildren = 0; + for (int i = 0; i < newCol->numReducedMembers; ++i) { + SPQRColReducedMember *reducedMember = &newCol->reducedMembers[i]; + reduced_member_id minimizer = newCol->memberInformation[reducedMember->rootMember].rootDepthMinimizer; + if (reducedMember->depth >= newCol->reducedMembers[minimizer].depth) { + reducedMember->firstChild = numTotalChildren; + numTotalChildren += reducedMember->numChildren; + reducedMember->numChildren = 0; + } + } + + if (newCol->memChildrenStorage < numTotalChildren) { + int newMemSize = max(newCol->memChildrenStorage * 2, numTotalChildren); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->childrenStorage, newCol->memChildrenStorage, newMemSize)); + newCol->memChildrenStorage = newMemSize; + } + newCol->numChildrenStorage = numTotalChildren; + + //Fill up the children array` + for (reduced_member_id reducedMember = 0; reducedMember < newCol->numReducedMembers; ++reducedMember) { + SPQRColReducedMember *reducedMemberData = &newCol->reducedMembers[reducedMember]; + if (reducedMemberData->depth <= + newCol->reducedMembers[newCol->memberInformation[reducedMemberData->rootMember].rootDepthMinimizer].depth) { + continue; + } + spqr_member parentMember = findMemberParent(dec, reducedMemberData->member); + reduced_member_id parentReducedMember = SPQRmemberIsValid(parentMember) + ? newCol->memberInformation[parentMember].reducedMember + : INVALID_REDUCED_MEMBER; + if (reducedMemberIsValid(parentReducedMember)) { + SPQRColReducedMember *parentReducedMemberData = &newCol->reducedMembers[parentReducedMember]; + newCol->childrenStorage[parentReducedMemberData->firstChild + + parentReducedMemberData->numChildren] = reducedMember; + ++parentReducedMemberData->numChildren; + } + } + + //Clean up the root depth minimizers. + for (int i = 0; i < newCol->numReducedMembers; ++i) { + SPQRColReducedMember *reducedMember = &newCol->reducedMembers[i]; + assert(reducedMember); + spqr_member rootMember = reducedMember->rootMember; + assert(rootMember >= 0); + assert(rootMember < dec->memMembers); + newCol->memberInformation[rootMember].rootDepthMinimizer = INVALID_REDUCED_MEMBER; + } + + return SCIP_OKAY; +} + +static void cleanUpMemberInformation(SCIP_NETWORKCOLADDITION * newCol){ + //This loop is at the end as memberInformation is also used to assign the cut arcs during propagation + //Clean up the memberInformation array + for (int i = 0; i < newCol->numReducedMembers; ++i) { + newCol->memberInformation[newCol->reducedMembers[i].member].reducedMember = INVALID_REDUCED_MEMBER; + } +#ifndef NDEBUG + for (int i = 0; i < newCol->memMemberInformation; ++i) { + assert(reducedMemberIsInvalid(newCol->memberInformation[i].reducedMember)); + } +#endif +} + +static void createPathArc( + SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION * newCol, + const spqr_arc arc, const reduced_member_id reducedMember, SCIP_Bool reversed){ + assert(dec); + assert(newCol); + + path_arc_id path_arc = newCol->numPathArcs; + PathArcListNode * listNode = &newCol->pathArcs[path_arc]; + listNode->arc = arc; + + listNode->nextMember = newCol->reducedMembers[reducedMember].firstPathArc; + newCol->reducedMembers[reducedMember].firstPathArc = path_arc; + newCol->reducedMembers[reducedMember].numPathArcs += 1; + + listNode->nextOverall = newCol->firstOverallPathArc; + newCol->firstOverallPathArc = path_arc; + + ++newCol->numPathArcs; + assert(newCol->numPathArcs <= newCol->memPathArcs); + + assert(arc < newCol->memArcsInPath); + newCol->arcInPath[arc] = TRUE; + newCol->arcInPathReversed[arc] = reversed; + assert(memberIsRepresentative(dec,newCol->reducedMembers[reducedMember].member)); + if(getMemberType(dec,newCol->reducedMembers[reducedMember].member) == SPQR_MEMBERTYPE_RIGID){ + + listNode->arcHead = findEffectiveArcHead(dec,arc); + listNode->arcTail = findEffectiveArcTail(dec,arc); + if(reversed){ + swap_ints(&listNode->arcHead,&listNode->arcTail); + } + assert(SPQRnodeIsValid(listNode->arcHead) && SPQRnodeIsValid(listNode->arcTail)); + assert(listNode->arcHead < newCol->memNodePathDegree && listNode->arcTail < newCol->memNodePathDegree); + ++newCol->nodeInPathDegree[listNode->arcHead]; + ++newCol->nodeOutPathDegree[listNode->arcTail]; + }else{ + listNode->arcHead = SPQR_INVALID_NODE; + listNode->arcTail = SPQR_INVALID_NODE; + } + listNode->reversed = reversed; + +} + +static SCIP_RETCODE createPathArcs(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol){ + int maxNumPathArcs = newCol->numDecompositionRowArcs + getNumMembers(dec); + if(newCol->memPathArcs < maxNumPathArcs){ + int newMaxNumArcs = 2*maxNumPathArcs; //safety factor to prevent very frequent reallocations + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newCol->pathArcs, newCol->memPathArcs, newMaxNumArcs)); + newCol->memPathArcs = newMaxNumArcs; + } + int maxPathArcIndex = largestArcID(dec); + if(newCol->memArcsInPath < maxPathArcIndex){ + int newSize = 2*maxPathArcIndex; //safety factor to prevent very frequent reallocations + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newCol->arcInPath,newCol->memArcsInPath,newSize)); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newCol->arcInPathReversed,newCol->memArcsInPath,newSize)); + + for (int i = newCol->memArcsInPath; i < newSize; ++i) { + newCol->arcInPath[i] = FALSE; + newCol->arcInPathReversed[i] = FALSE; + } + newCol->memArcsInPath = newSize; + } + int maxNumNodes = largestNodeID(dec); + if(newCol->memNodePathDegree < maxNumNodes){ + int newSize = 2*maxNumNodes; //safety factor to prevent very frequent reallocations + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newCol->nodeInPathDegree,newCol->memNodePathDegree, newSize)); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newCol->nodeOutPathDegree,newCol->memNodePathDegree,newSize)); + for (int i = newCol->memNodePathDegree; i < newSize; ++i) { + newCol->nodeInPathDegree[i] = 0; + newCol->nodeOutPathDegree[i] = 0; + } + newCol->memNodePathDegree = newSize; + } + for (int i = 0; i < newCol->numDecompositionRowArcs; ++i) { + spqr_arc arc = newCol->decompositionRowArcs[i]; + spqr_member member = findArcMember(dec, arc); + reduced_member_id reducedMember = newCol->memberInformation[member].reducedMember; + createPathArc(dec,newCol,arc,reducedMember,newCol->decompositionArcReversed[i]); + } + + return SCIP_OKAY; +} + + +/** + * Saves the information of the current row and partitions it based on whether or not the given columns are + * already part of the decomposition. + */ +static SCIP_RETCODE +newColUpdateColInformation(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, spqr_col column, + const spqr_row * nonzeroRows, const double * nonzeroValues, size_t numNonzeros) { + newCol->newColIndex = column; + + newCol->numDecompositionRowArcs = 0; + newCol->numNewRowArcs = 0; + + for (size_t i = 0; i < numNonzeros; ++i) { + spqr_arc rowArc = getDecompositionRowArc(dec, nonzeroRows[i]); + SCIP_Bool reversed = nonzeroValues[i] < 0.0; + if (SPQRarcIsValid(rowArc)) { //If the arc is the current decomposition: save it in the array + if (newCol->numDecompositionRowArcs == newCol->memDecompositionRowArcs) { + int newNumArcs = newCol->memDecompositionRowArcs == 0 ? 8 : 2 * newCol->memDecompositionRowArcs; + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->decompositionRowArcs, + newCol->memDecompositionRowArcs,newNumArcs)); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->decompositionArcReversed, + (size_t) newCol->memDecompositionRowArcs,newNumArcs)); + newCol->memDecompositionRowArcs = newNumArcs; + + } + newCol->decompositionRowArcs[newCol->numDecompositionRowArcs] = rowArc; + newCol->decompositionArcReversed[newCol->numDecompositionRowArcs] = reversed; + ++newCol->numDecompositionRowArcs; + } else { + //Not in the decomposition: add it to the set of arcs which are newly added with this row. + if (newCol->numNewRowArcs == newCol->memNewRowArcs) { + int newNumArcs = newCol->memNewRowArcs == 0 ? 8 : 2 * newCol->memNewRowArcs; + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->newRowArcs, + (size_t) newCol->memNewRowArcs,newNumArcs)); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->newRowArcReversed, + (size_t) newCol->memNewRowArcs,newNumArcs)); + newCol->memNewRowArcs = newNumArcs; + + } + newCol->newRowArcs[newCol->numNewRowArcs] = nonzeroRows[i]; + newCol->newRowArcReversed[newCol->numNewRowArcs] = reversed; + newCol->numNewRowArcs++; + } + } + + return SCIP_OKAY; +} + +static SCIP_RETCODE computeLeafMembers(const SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol) { + if (newCol->numReducedMembers > newCol->memLeafMembers) { + int newSize = max(newCol->numReducedMembers, 2 * newCol->memLeafMembers); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->leafMembers,newCol->memLeafMembers, newSize)); + newCol->memLeafMembers = newSize; + } + newCol->numLeafMembers = 0; + + for (reduced_member_id reducedMember = 0; reducedMember < newCol->numReducedMembers; ++reducedMember) { + if (newCol->reducedMembers[reducedMember].numChildren == 0) { + newCol->leafMembers[newCol->numLeafMembers] = reducedMember; + ++newCol->numLeafMembers; + } + } + return SCIP_OKAY; +} + +static void determineRigidPath(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, SPQRColReducedMember * redMem){ + assert(dec); + assert(newCol); + assert(redMem); + + SCIP_Bool isValidPath = TRUE; + redMem->rigidPathStart = SPQR_INVALID_NODE; + redMem->rigidPathEnd = SPQR_INVALID_NODE; + for(path_arc_id pathArc = redMem->firstPathArc; pathArcIsValid(pathArc); pathArc = newCol->pathArcs[pathArc].nextMember){ + spqr_node head = newCol->pathArcs[pathArc].arcHead; + spqr_node tail = newCol->pathArcs[pathArc].arcTail; + assert(nodeIsRepresentative(dec,head) && nodeIsRepresentative(dec,tail)); + + if(newCol->nodeInPathDegree[head] > 1 || newCol->nodeOutPathDegree[tail] > 1){ + //not network -> stop + isValidPath = FALSE; + break; + } + if(newCol->nodeOutPathDegree[head] == 0){ + //found end node + //If this is the second, stop + if(SPQRnodeIsValid(redMem->rigidPathEnd)){ + isValidPath = FALSE; + break; + } + redMem->rigidPathEnd = head; + } + if(newCol->nodeInPathDegree[tail] == 0){ + //Found start node. + //If this is the second, stop. + if(SPQRnodeIsValid(redMem->rigidPathStart)){ + isValidPath = FALSE; + break; + } + redMem->rigidPathStart = tail; + } + } + //assert that both a start and end node have been found + assert(!isValidPath || (SPQRnodeIsValid(redMem->rigidPathStart) && SPQRnodeIsValid(redMem->rigidPathEnd))); + if(!isValidPath){ + redMem->rigidPathStart = SPQR_INVALID_NODE; + redMem->rigidPathEnd = SPQR_INVALID_NODE; + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + } + +} +static void determineSingleRigidType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, reduced_member_id reducedMember, + spqr_member member){ + assert(dec); + assert(newCol); + assert(reducedMemberIsValid(reducedMember)); + SPQRColReducedMember * redMem = &newCol->reducedMembers[reducedMember]; + assert(pathArcIsValid(redMem->firstPathArc)); + determineRigidPath(dec,newCol,redMem); + if(redMem->type != REDUCEDMEMBER_TYPE_NOT_NETWORK){ + redMem->type = REDUCEDMEMBER_TYPE_MERGED; + } +} +//TODO: type seems somewhat duplicate +static void determineSingleComponentType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, reduced_member_id reducedMember){ + assert(dec); + assert(newCol); + + int numNonPropagatedAdjacent = newCol->reducedMembers[reducedMember].numChildren-newCol->reducedMembers[reducedMember].numPropagatedChildren; + if(reducedMemberIsValid(newCol->reducedMembers[reducedMember].parent) && + newCol->reducedMembers[newCol->reducedMembers[reducedMember].parent].type != REDUCEDMEMBER_TYPE_CYCLE){ + ++numNonPropagatedAdjacent; + } + + if(numNonPropagatedAdjacent > 2){ + newCol->reducedMembers[reducedMember].type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + + + spqr_member member = findMember(dec, newCol->reducedMembers[reducedMember].member); + SPQRMemberType type = getMemberType(dec, member); + switch(type){ + case SPQR_MEMBERTYPE_RIGID: + { + determineSingleRigidType(dec,newCol,reducedMember,member); + break; + } + case SPQR_MEMBERTYPE_PARALLEL: + { + SPQRColReducedMember *redMem =&newCol->reducedMembers[reducedMember]; + assert(pathArcIsValid(redMem->firstPathArc)); + SCIP_Bool pathForwards = newCol->pathArcs[redMem->firstPathArc].reversed == + arcIsReversedNonRigid(dec,newCol->pathArcs[redMem->firstPathArc].arc); + redMem->pathBackwards = !pathForwards; + redMem->type = REDUCEDMEMBER_TYPE_CYCLE; + break; + } + case SPQR_MEMBERTYPE_SERIES: + case SPQR_MEMBERTYPE_LOOP: + { + SPQRColReducedMember *redMem =&newCol->reducedMembers[reducedMember]; + int countedPathArcs = 0; + SCIP_Bool good = TRUE; + SCIP_Bool passesForwards = TRUE; + for(path_arc_id pathArc = redMem->firstPathArc; pathArcIsValid(pathArc); + pathArc = newCol->pathArcs[pathArc].nextMember){ + if(countedPathArcs == 0){ + passesForwards = newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec,newCol->pathArcs[pathArc].arc); + }else if((newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec,newCol->pathArcs[pathArc].arc)) != passesForwards){ + good = FALSE; + break; + } + ++countedPathArcs; + } + if(!good){ + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + break; + } + + redMem->pathBackwards = !passesForwards; + if (countedPathArcs == getNumMemberArcs(dec, findMember(dec,redMem->member)) -1){ + //Type -> Cycle; + //Propagate arc + redMem->type = REDUCEDMEMBER_TYPE_CYCLE; + }else{ + //Type -> single_end + redMem->type = REDUCEDMEMBER_TYPE_MERGED; + } + break; + } + case SPQR_MEMBERTYPE_UNASSIGNED: + { + assert(FALSE); + break; + } + } +} + + +static void determinePathSeriesType(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION *newCol, + reduced_member_id reducedMember, spqr_member member, MemberPathType previousType, + spqr_arc source, spqr_arc target){ + assert(dec); + assert(newCol); + assert(reducedMemberIsValid(reducedMember)); + assert(SPQRmemberIsValid(member) &&memberIsRepresentative(dec,member)); + assert(getMemberType(dec,member) == SPQR_MEMBERTYPE_SERIES); + + SPQRColReducedMember *redMem =&newCol->reducedMembers[reducedMember]; + int countedPathArcs = 0; + + SCIP_Bool good = TRUE; + SCIP_Bool passesForwards = TRUE; + for(path_arc_id pathArc = redMem->firstPathArc; pathArcIsValid(pathArc); + pathArc = newCol->pathArcs[pathArc].nextMember){ + if(countedPathArcs == 0){ + passesForwards = newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec,newCol->pathArcs[pathArc].arc); + }else if((newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec,newCol->pathArcs[pathArc].arc)) != passesForwards){ + good = FALSE; + break; + } + ++countedPathArcs; + } + //If the internal directions of the arcs do not agree, we have no way to realize + if(!good){ + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + //If we are in the first skeleton processed, ignore the previous member type + if(!SPQRarcIsValid(source)){ + assert(countedPathArcs > 0); + assert(SPQRarcIsValid(target)); + SCIP_Bool firstReversed = arcIsReversedNonRigid(dec,newCol->pathArcs[redMem->firstPathArc].arc); + SCIP_Bool targetReversed = arcIsReversedNonRigid(dec,target); + SCIP_Bool reversePath = newCol->pathArcs[redMem->firstPathArc].reversed; + + if((firstReversed == targetReversed ) == reversePath){ + redMem->pathType = INTO_HEAD; + }else{ + redMem->pathType = OUT_HEAD; + } + redMem->pathBackwards = !passesForwards; + return; + } + SCIP_Bool sourceReversed = arcIsReversedNonRigid(dec,source); + if(countedPathArcs > 0){ + SCIP_Bool isIntoHeadOrOutTail = isInto(previousType) == isHead(previousType); + SCIP_Bool isGood = isIntoHeadOrOutTail == (sourceReversed == passesForwards); + if(!isGood){ + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + } + redMem->pathBackwards = !passesForwards; + if(SPQRarcIsValid(target)){ + SCIP_Bool targetReversed = arcIsReversedNonRigid(dec,target); + SCIP_Bool inSameDirection = sourceReversed == targetReversed; + + MemberPathType currentType; + switch(previousType){ + case INTO_HEAD:{ + currentType = inSameDirection ? INTO_TAIL : INTO_HEAD; + break; + } + case INTO_TAIL: + { + currentType = inSameDirection ? INTO_HEAD : INTO_TAIL; + break; + } + case OUT_HEAD:{ + currentType = inSameDirection ? OUT_TAIL : OUT_HEAD; + break; + } + case OUT_TAIL: + { + currentType = inSameDirection ? OUT_HEAD : OUT_TAIL; + break; + } + default:{ + assert(FALSE); + } + } + redMem->pathType = currentType; + return; + } + //If we are in the last skeleton, we only have a source, so nothing further to do + assert(countedPathArcs > 0); + //Strictly speaking below are no-ops within the algorithm, but help with debugging + if(isInto(previousType)){ + redMem->pathType = INTO_HEAD; + }else{ + redMem->pathType = OUT_HEAD; + } +} +static void determinePathParallelType(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION *newCol, + reduced_member_id reducedMember, spqr_member member, MemberPathType previousType, + spqr_arc source, spqr_arc target) { + assert(dec); + assert(newCol); + assert(reducedMemberIsValid(reducedMember)); + assert(SPQRmemberIsValid(member) && memberIsRepresentative(dec, member)); + assert(getMemberType(dec, member) == SPQR_MEMBERTYPE_PARALLEL); + + //Parallel members must always be of degree two; if they are a leaf, they must contain an edge, which could have been propagated + assert(SPQRarcIsValid(source) && SPQRarcIsValid(target)); + SCIP_Bool sourceReversed = arcIsReversedNonRigid(dec,source); + SCIP_Bool targetReversed = arcIsReversedNonRigid(dec,target); + SCIP_Bool inSameDirection = sourceReversed == targetReversed; + + SPQRColReducedMember *redMem =&newCol->reducedMembers[reducedMember]; + + path_arc_id pathArc = redMem->firstPathArc; + + SCIP_Bool arcPresent = FALSE; + if(pathArcIsValid(pathArc)){ + SCIP_Bool pathArcReversed = newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec,newCol->pathArcs[pathArc].arc); + SCIP_Bool intoHeadOrOutTail = isInto(previousType) == isHead(previousType); + SCIP_Bool good = intoHeadOrOutTail == (pathArcReversed != sourceReversed); + if(!good){ + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + arcPresent = TRUE; + } + + SCIP_Bool swapHeadTail = arcPresent != inSameDirection; + MemberPathType currentType; + switch(previousType){ + case INTO_HEAD:{ + currentType = swapHeadTail ? INTO_HEAD : INTO_TAIL; + break; + } + case INTO_TAIL:{ + currentType = swapHeadTail ? INTO_TAIL : INTO_HEAD; + break; + } + case OUT_HEAD:{ + currentType = swapHeadTail ? OUT_HEAD : OUT_TAIL; + break; + } + case OUT_TAIL:{ + currentType = swapHeadTail ? OUT_TAIL : OUT_HEAD; + break; + } + } + redMem->pathType = currentType; +} +static void determinePathRigidType(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION *newCol, + reduced_member_id reducedMember, spqr_member member, MemberPathType previousType, + spqr_arc source, spqr_arc target) { + + SPQRColReducedMember *redMem = &newCol->reducedMembers[reducedMember]; + if(pathArcIsInvalid(redMem->firstPathArc)){ + assert(SPQRarcIsValid(source)); + assert(SPQRarcIsValid(target)); + //In this case, we need to check if the source and target are adjacent in any node + + spqr_node sourceTail = findEffectiveArcTail(dec, source); + spqr_node sourceHead = findEffectiveArcHead(dec, source); + spqr_node targetTail = findEffectiveArcTail(dec, target); + spqr_node targetHead = findEffectiveArcHead(dec, target); + SCIP_Bool sourceHeadIsTargetHead = sourceHead == targetHead; + SCIP_Bool sourceTailIsTargetHead = sourceTail == targetHead; + SCIP_Bool sourceHeadIsTargetTail = sourceHead == targetTail; + SCIP_Bool sourceTailIsTargetTail = sourceTail == targetTail; + + if(!(sourceHeadIsTargetHead || sourceTailIsTargetHead || sourceHeadIsTargetTail || sourceTailIsTargetTail)){ + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + assert((sourceHeadIsTargetHead ? 1 : 0) + (sourceHeadIsTargetTail ? 1 : 0) + + (sourceTailIsTargetHead ? 1 : 0) + (sourceTailIsTargetTail ? 1 : 0) == 1 ); + //assert simplicity; they can be adjacent in only exactly one node + SCIP_Bool isSourceHead = sourceHeadIsTargetHead || sourceHeadIsTargetTail; + SCIP_Bool isTargetTail = sourceHeadIsTargetTail || sourceTailIsTargetTail; + if (isHead(previousType) == isSourceHead){ + //no need to reflect + redMem->reverseArcs = FALSE; + if(isInto(previousType)){ + if(isTargetTail){ + redMem->pathType = INTO_TAIL; + }else{ + redMem->pathType = INTO_HEAD; + } + }else{ + if(isTargetTail){ + redMem->pathType = OUT_TAIL; + }else{ + redMem->pathType = OUT_HEAD; + } + } + }else{ + redMem->reverseArcs = TRUE; + //because of the reversal, all the heads/tails are switched below + if(isInto(previousType)){ + if(isTargetTail){ + redMem->pathType = INTO_HEAD; + }else{ + redMem->pathType = INTO_TAIL; + } + }else{ + if(isTargetTail){ + redMem->pathType = OUT_HEAD; + }else{ + redMem->pathType = OUT_TAIL; + } + } + } + assert(isInto(redMem->pathType) == isInto(previousType)); + return; + } + determineRigidPath(dec, newCol, redMem); + if (redMem->type == REDUCEDMEMBER_TYPE_NOT_NETWORK) { + return; + } + if(SPQRarcIsInvalid(source)){ + assert(SPQRarcIsValid(target)); + spqr_node targetTail = findEffectiveArcTail(dec, target); + spqr_node targetHead = findEffectiveArcHead(dec, target); + redMem->reverseArcs = FALSE; + if(redMem->rigidPathEnd == targetHead){ + redMem->pathType = INTO_HEAD; + }else if(redMem->rigidPathEnd == targetTail){ + redMem->pathType = INTO_TAIL; + }else if(redMem->rigidPathStart == targetHead){ + redMem->pathType = OUT_HEAD; + }else if(redMem->rigidPathStart == targetTail){ + redMem->pathType = OUT_TAIL; + }else { + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + } + return; + } + assert(SPQRarcIsValid(source)); + spqr_node sourceTail = findEffectiveArcTail(dec, source); + spqr_node sourceHead = findEffectiveArcHead(dec, source); + + SCIP_Bool startsAtHead = sourceHead == redMem->rigidPathStart; + SCIP_Bool endsAtTail = sourceTail == redMem->rigidPathEnd; + SCIP_Bool startsAtTail = sourceTail == redMem->rigidPathStart; + SCIP_Bool endsAtHead = sourceHead == redMem->rigidPathEnd; + + SCIP_Bool isIntoHeadOrOutTail = isInto(previousType) == isHead(previousType); + if(isIntoHeadOrOutTail){ //into head or outTail + //Check if path starts at head or ends at tail + if(!startsAtHead && !endsAtTail){ + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + assert(startsAtHead || endsAtTail); //both can hold; they can form cycle but other components can not be reduced +// redMem->reverseArcs = isInto(previousType) != startsAtHead; //Reverse only if there is no path starting at head + }else{ //Into tail or outHead + //Check if path starts at tail or ends at head + if(!startsAtTail && !endsAtHead){ + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + assert(startsAtTail || endsAtHead); // both can hold; they can form cycle but other components can not be reduced +// redMem->reverseArcs = isInto(previousType) != startsAtTail; //Reverse only if there is no path starting at tail + } + + if(SPQRarcIsValid(target)){ + spqr_node targetTail = findEffectiveArcTail(dec, target); + spqr_node targetHead = findEffectiveArcHead(dec, target); + + //Check if they are not parallel; (below logic relies on this fact) + assert(!((targetHead == sourceHead && targetTail == sourceTail) || (targetHead == sourceTail && targetTail == sourceHead))); + + SCIP_Bool startsAtTargetHead = redMem->rigidPathStart == targetHead; + SCIP_Bool startsAtTargetTail = redMem->rigidPathStart == targetTail; + SCIP_Bool endsAtTargetHead = redMem->rigidPathEnd == targetHead; + SCIP_Bool endsAtTargetTail = redMem->rigidPathEnd == targetTail; + + if(!(startsAtTargetHead || startsAtTargetTail || endsAtTargetHead || endsAtTargetTail)){ + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + SCIP_Bool outReverse = FALSE; + SCIP_Bool outHead = FALSE; + + if(isInto(previousType) == isHead(previousType)) { + if (startsAtHead && endsAtTail) { + outReverse = (startsAtTargetHead || startsAtTargetTail) == isInto(previousType); + } else if(startsAtHead) { + outReverse = !isInto(previousType); + } else { + assert(endsAtTail); + outReverse = isInto(previousType); + } + }else{ + if (startsAtTail && endsAtHead) { + outReverse = (startsAtTargetHead || startsAtTargetTail) == isInto(previousType); + } else if (startsAtTail) { + outReverse = !isInto(previousType); + } else { + assert(endsAtHead); + outReverse = isInto(previousType); + } + } + + //TODO: these can probably be simplified significantly, but this might pose risk of introducing incorrect assumptions + SCIP_Bool isBad = FALSE; + if(isInto(previousType) == isHead(previousType)){ + if(startsAtHead && endsAtTail){ + outHead = (startsAtTargetTail || endsAtTargetHead) == isInto(previousType); + }else if(startsAtHead){ + if(endsAtTargetHead){ + outHead = isInto(previousType); + }else if (endsAtTargetTail){ + outHead = !isInto(previousType); + }else{ + isBad = TRUE; + } + }else{ + assert(endsAtTail); + if(startsAtTargetTail){ + outHead = isInto(previousType); + }else if (startsAtTargetHead){ + outHead = !isInto(previousType); + }else{ + isBad = TRUE; + } + } + }else{ + if(startsAtTail && endsAtHead){ + outHead = (startsAtTargetTail || endsAtTargetHead) == isInto(previousType); + }else if(startsAtTail){ + if(endsAtTargetHead){ + outHead = isInto(previousType); + }else if(endsAtTargetTail){ + outHead = !isInto(previousType); + }else{ + isBad = TRUE; + } + }else{ + assert(endsAtHead); + if(startsAtTargetTail){ + outHead = isInto(previousType); + }else if (startsAtTargetHead){ + outHead = !isInto(previousType); + }else{ + isBad = TRUE; + } + } + } + if(isBad){ + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + + redMem->reverseArcs = outReverse; + if(isInto(previousType)){ + redMem->pathType = outHead ? INTO_HEAD : INTO_TAIL; + }else{ + redMem->pathType = outHead ? OUT_HEAD : OUT_TAIL; + } + return; + } + + //TODO: is this simplifyable? + if(isInto(previousType) == isHead(previousType)){ + redMem->reverseArcs = startsAtHead != isInto(previousType); + }else{ + redMem->reverseArcs = startsAtTail != isInto(previousType); + } + //last member of the path. Since we already checked the source, + //Below is technically no op, but helps with debugging + if(isInto(previousType)){ + redMem->pathType = INTO_HEAD; + }else{ + redMem->pathType = OUT_HEAD; + } +} +static void determinePathMemberType(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION *newCol, + reduced_member_id reducedMember, spqr_member member, MemberPathType previousType, + spqr_arc source, spqr_arc target){ + newCol->reducedMembers[reducedMember].pathSourceArc = source; + newCol->reducedMembers[reducedMember].pathTargetArc = target; + //Check if the marked edges with the given signs + //form a (reverse) directed path from one of the source's end nodes to one of the target's end nodes + switch(getMemberType(dec,member)){ + case SPQR_MEMBERTYPE_RIGID:{ + determinePathRigidType(dec,newCol,reducedMember,member,previousType,source,target); + break; + } + case SPQR_MEMBERTYPE_PARALLEL:{ + determinePathParallelType(dec,newCol,reducedMember,member,previousType,source,target); + break; + } + case SPQR_MEMBERTYPE_SERIES:{ + determinePathSeriesType(dec,newCol,reducedMember,member,previousType,source,target); + break; + } + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_UNASSIGNED: + { + assert(FALSE); + //In release, + + newCol->remainsNetwork = FALSE; + break; + } + } + +} + +static void determinePathTypes(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, SPQRColReducedComponent * component){ + assert(dec); + assert(newCol); + assert(component); + assert(component->numPathEndMembers == 2); + + assert(component->pathEndMembers[0] != component->root); + + //We check the path by going from end to end. We start at the leaf and process every component, + //walking down until we hit the root. + //Then, we walk up from the root and process each component in the same manner. + reduced_member_id reducedStart = component->pathEndMembers[0]; + spqr_arc toPrevious = SPQR_INVALID_ARC; + spqr_arc toNext = SPQR_INVALID_ARC; + MemberPathType previousType = INTO_HEAD; //Arbitrary, is ignored in the first call + + spqr_member member = newCol->reducedMembers[reducedStart].member; + reduced_member_id reducedMember = reducedStart; + reduced_member_id previousReducedMember = reducedStart; + while(reducedMember != component->root){ + toNext = markerToParent(dec,member); + determinePathMemberType(dec,newCol,reducedMember,member,previousType,toPrevious,toNext); + if(!newCol->remainsNetwork){ + return; + } + previousType = newCol->reducedMembers[reducedMember].pathType; + toPrevious = markerOfParent(dec,member); + member = findMemberParent(dec,newCol->reducedMembers[reducedMember].member); + previousReducedMember = reducedMember; + reducedMember = newCol->memberInformation[member].reducedMember; + newCol->reducedMembers[previousReducedMember].nextPathMember = reducedMember; + newCol->reducedMembers[previousReducedMember].nextPathMemberIsParent = TRUE; + } + + while(reducedMember != component->pathEndMembers[1]){ + //Search the (other) child node + reduced_member_id child = INVALID_REDUCED_MEMBER; + //a bit ugly linear search, but not a problem for time complexity + for (children_idx i = newCol->reducedMembers[reducedMember].firstChild; + i < newCol->reducedMembers[reducedMember].firstChild + newCol->reducedMembers[reducedMember].numChildren; ++i) { + reduced_member_id childReduced = newCol->childrenStorage[i]; + if(newCol->reducedMembers[childReduced].type != REDUCEDMEMBER_TYPE_CYCLE && + childReduced != previousReducedMember){ + child = childReduced; + break; + } + } + assert(reducedMemberIsValid(child)); + + spqr_member childMember = newCol->reducedMembers[child].member; + toNext = markerOfParent(dec,childMember); + + determinePathMemberType(dec,newCol,reducedMember,member,previousType,toPrevious,toNext); + if(!newCol->remainsNetwork){ + return; + } + previousType = newCol->reducedMembers[reducedMember].pathType; + toPrevious = markerToParent(dec,childMember); + member = childMember; + previousReducedMember = reducedMember; + reducedMember = child; + newCol->reducedMembers[previousReducedMember].nextPathMember = reducedMember; + newCol->reducedMembers[previousReducedMember].nextPathMemberIsParent = FALSE; + } + //The last iteration is not performed by the loops above. + //We explicitly set the target arc to invalid in order to indicate that this is the last iteration. + toNext = SPQR_INVALID_ARC; + determinePathMemberType(dec,newCol,reducedMember,member,previousType,toPrevious,toNext); + newCol->reducedMembers[reducedMember].nextPathMember = INVALID_REDUCED_MEMBER; + //since we return anyways, no need to check newCol->remainsNetwork explicitly +} +static void checkRigidLeaf(SCIP_NETWORKDECOMP * dec,SCIP_NETWORKCOLADDITION * newCol, reduced_member_id leaf, + spqr_arc toParent, reduced_member_id parent, spqr_arc toChild){ + SPQRColReducedMember * leafMember = &newCol->reducedMembers[leaf]; + determineRigidPath(dec,newCol,leafMember); + if(leafMember->type == REDUCEDMEMBER_TYPE_NOT_NETWORK){ + return; + } + spqr_node targetHead = findEffectiveArcHead(dec,toParent); + spqr_node targetTail = findEffectiveArcTail(dec,toParent); + SCIP_Bool matches = leafMember->rigidPathStart == targetTail && leafMember->rigidPathEnd == targetHead; + SCIP_Bool opposite = leafMember->rigidPathStart == targetHead && leafMember->rigidPathEnd == targetTail; + if(matches || opposite) { + leafMember->type = REDUCEDMEMBER_TYPE_CYCLE; + createPathArc(dec,newCol,toChild,parent, opposite); + return; + } + leafMember->type = REDUCEDMEMBER_TYPE_MERGED; +} +static ReducedMemberType checkLeaf(SCIP_NETWORKDECOMP * dec,SCIP_NETWORKCOLADDITION * newCol, reduced_member_id leaf, + spqr_arc toParent, reduced_member_id parent, spqr_arc toChild){ + assert(dec); + assert(newCol); + assert(SPQRarcIsValid(toParent)); + assert(SPQRarcIsValid(toChild)); + assert(!arcIsTree(dec,toParent)); + assert(reducedMemberIsValid(leaf)); + assert(reducedMemberIsValid(parent)); + + switch(getMemberType(dec,newCol->reducedMembers[leaf].member)){ + case SPQR_MEMBERTYPE_RIGID: + { + checkRigidLeaf(dec,newCol,leaf,toParent,parent,toChild); + break; + } + case SPQR_MEMBERTYPE_PARALLEL: + { + SPQRColReducedMember *reducedMember =&newCol->reducedMembers[leaf]; + assert(pathArcIsValid(reducedMember->firstPathArc)); + reducedMember->type = REDUCEDMEMBER_TYPE_CYCLE; + + SCIP_Bool pathArcReversed = newCol->pathArcs[reducedMember->firstPathArc].reversed; + SCIP_Bool arcPathArcIsReverse = arcIsReversedNonRigid(dec, newCol->pathArcs[reducedMember->firstPathArc].arc); + SCIP_Bool parentReversed = arcIsReversedNonRigid(dec, toParent); + createPathArc(dec, newCol, toChild, parent, (arcPathArcIsReverse == parentReversed) == pathArcReversed); + break; + } + case SPQR_MEMBERTYPE_SERIES: + case SPQR_MEMBERTYPE_LOOP: + { + SPQRColReducedMember *reducedMember =&newCol->reducedMembers[leaf]; + int countedPathArcs = 0; + SCIP_Bool good = TRUE; + SCIP_Bool passesForwards = TRUE; + for(path_arc_id pathArc = reducedMember->firstPathArc; pathArcIsValid(pathArc); + pathArc = newCol->pathArcs[pathArc].nextMember){ + if(countedPathArcs == 0){ + passesForwards = newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec,newCol->pathArcs[pathArc].arc); + }else if((newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec,newCol->pathArcs[pathArc].arc)) != passesForwards){ + good = FALSE; + break; + } + ++countedPathArcs; + } + if(!good){ + reducedMember->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + break; + } + + reducedMember->pathBackwards = !passesForwards; + if (countedPathArcs == getNumMemberArcs(dec, findMember(dec,reducedMember->member)) -1){ + //Type -> Cycle; + //Propagate arc + reducedMember->type = REDUCEDMEMBER_TYPE_CYCLE; + + SCIP_Bool firstArcReversed = arcIsReversedNonRigid(dec,newCol->pathArcs[reducedMember->firstPathArc].arc); + SCIP_Bool firstArcInPathReverse = newCol->pathArcs[reducedMember->firstPathArc].reversed; + SCIP_Bool parentReversed = arcIsReversedNonRigid(dec,toParent); + createPathArc(dec,newCol,toChild,parent,(parentReversed == firstArcReversed) != firstArcInPathReverse); + + }else{ + //Type -> single_end + reducedMember->type = REDUCEDMEMBER_TYPE_MERGED; + } + + break; + } + case SPQR_MEMBERTYPE_UNASSIGNED: + { + assert(FALSE); + break; + } + } + return newCol->reducedMembers[leaf].type; +} + +static void propagateCycles(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION * newCol){ + assert(dec); + assert(newCol); + int leafArrayIndex = 0; + + while(leafArrayIndex != newCol->numLeafMembers){ + reduced_member_id leaf = newCol->leafMembers[leafArrayIndex]; + //next is invalid if the member is not in the reduced decomposition. + reduced_member_id next = newCol->reducedMembers[leaf].parent; + spqr_arc parentMarker = markerToParent(dec, newCol->reducedMembers[leaf].member); + if(reducedMemberIsValid(next) && !arcIsTree(dec,parentMarker)){ + assert(reducedMemberIsValid(next)); + assert(SPQRarcIsValid(parentMarker)); + ReducedMemberType type = checkLeaf(dec,newCol,leaf,parentMarker,next,markerOfParent(dec,newCol->reducedMembers[leaf].member)); + if(type == REDUCEDMEMBER_TYPE_CYCLE){ + ++newCol->reducedMembers[next].numPropagatedChildren; + if(newCol->reducedMembers[next].numPropagatedChildren == newCol->reducedMembers[next].numChildren){ + newCol->leafMembers[leafArrayIndex] = next; + }else{ + ++leafArrayIndex; + } + }else if(type == REDUCEDMEMBER_TYPE_NOT_NETWORK){ + return; + }else{ + assert(type == REDUCEDMEMBER_TYPE_MERGED); + ++leafArrayIndex; + int component = newCol->reducedMembers[leaf].componentIndex; + if(newCol->reducedComponents[component].numPathEndMembers >= 2){ + newCol->remainsNetwork = FALSE; + return; + } + assert(newCol->reducedComponents[component].numPathEndMembers < 2); + newCol->reducedComponents[component].pathEndMembers[newCol->reducedComponents[component].numPathEndMembers] = leaf; + ++newCol->reducedComponents[component].numPathEndMembers; + } + }else{ + ++leafArrayIndex; + int component = newCol->reducedMembers[leaf].componentIndex; + if(newCol->reducedComponents[component].numPathEndMembers >= 2){ + newCol->remainsNetwork = FALSE; + return; + } + assert(newCol->reducedComponents[component].numPathEndMembers < 2); + newCol->reducedComponents[component].pathEndMembers[newCol->reducedComponents[component].numPathEndMembers] = leaf; + ++newCol->reducedComponents[component].numPathEndMembers; + } + + } + + for (int j = 0; j < newCol->numReducedComponents; ++j) { + //The reduced root might be a leaf as well: we propagate it last + reduced_member_id root = newCol->reducedComponents[j].root; + + while(TRUE){ + if(newCol->reducedMembers[root].numPropagatedChildren != newCol->reducedMembers[root].numChildren -1) { + break; + } + //TODO: bit ugly, have to do a linear search for the child + reduced_member_id child = INVALID_REDUCED_MEMBER; + spqr_arc markerToChild = SPQR_INVALID_ARC; + for (children_idx i = newCol->reducedMembers[root].firstChild; + i < newCol->reducedMembers[root].firstChild + newCol->reducedMembers[root].numChildren; ++i) { + reduced_member_id childReduced = newCol->childrenStorage[i]; + if (newCol->reducedMembers[childReduced].type != REDUCEDMEMBER_TYPE_CYCLE) { + child = childReduced; + markerToChild = markerOfParent(dec, newCol->reducedMembers[child].member); + break; + } + } + assert(SPQRmemberIsValid(newCol->reducedMembers[child].member)); + assert(SPQRarcIsValid(markerToChild)); + if (!arcIsTree(dec, markerToChild)) { + ReducedMemberType type = checkLeaf(dec, newCol, root, markerToChild, child, + markerToParent(dec, newCol->reducedMembers[child].member)); + if (type == REDUCEDMEMBER_TYPE_CYCLE) { + root = child; + continue; + } else if (type == REDUCEDMEMBER_TYPE_NOT_NETWORK) { + return; + } + } + //If the root has exactly one neighbour and is not contained, it is also considered a path end member + int component = newCol->reducedMembers[root].componentIndex; + SCIP_Bool rootPresent = FALSE; + for (int i = 0; i < newCol->reducedComponents[component].numPathEndMembers; ++i) { + rootPresent = rootPresent || (newCol->reducedComponents[component].pathEndMembers[i] == root); + } + if(!rootPresent){ + if(newCol->reducedComponents[component].numPathEndMembers >= 2){ + newCol->remainsNetwork = FALSE; + return; + } + newCol->reducedComponents[component].pathEndMembers[newCol->reducedComponents[component].numPathEndMembers] = root; + ++newCol->reducedComponents[component].numPathEndMembers; + } + break; + + } + + newCol->reducedComponents[j].root = root; + newCol->reducedMembers[root].parent = INVALID_REDUCED_MEMBER; + } +} +static void determineComponentTypes(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION * newCol, SPQRColReducedComponent * component){ + assert(dec); + assert(newCol); + assert(component); + + if(component->numPathEndMembers == 1){ + assert(component->root == component->pathEndMembers[0]); + determineSingleComponentType(dec,newCol,component->root); + }else{ + assert(component->numPathEndMembers == 2); + determinePathTypes(dec,newCol,component); + } +} + +SCIP_RETCODE +SCIPNetworkColAdditionCheck(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, spqr_col column, const spqr_row * nonzeroRows, + const double * nonzeroValues, size_t numNonzeros) { + assert(dec); + assert(newCol); + assert(numNonzeros == 0 || (nonzeroRows && nonzeroValues)); + + newCol->remainsNetwork = TRUE; + cleanupPreviousIteration(dec, newCol); + //assert that previous iteration was cleaned up + + //Store call data + SCIP_CALL(newColUpdateColInformation(dec, newCol, column, nonzeroRows, nonzeroValues, numNonzeros)); + + //compute reduced decomposition + SCIP_CALL(constructReducedDecomposition(dec, newCol)); + //initialize path arcs in reduced decomposition + SCIP_CALL(createPathArcs(dec,newCol)); + SCIP_CALL(computeLeafMembers(dec,newCol)); + propagateCycles(dec,newCol); + //determine types + if(newCol->remainsNetwork){ + for (int i = 0; i < newCol->numReducedComponents; ++i) { + determineComponentTypes(dec,newCol,&newCol->reducedComponents[i]); + } + } + //clean up memberInformation + cleanUpMemberInformation(newCol); + + return SCIP_OKAY; +} + +///Contains the data which tells us where to store the new column after the graph has been modified +///In case member is a parallel or series node, the respective new column and rows are placed in parallel (or series) with it +///Otherwise, the rigid member has a free spot between firstNode and secondNode +typedef struct { + spqr_member member; + spqr_node head; + spqr_node tail; + spqr_arc representative; + SCIP_Bool reversed; +} NewColInformation; + +static NewColInformation emptyNewColInformation(void){ + NewColInformation information; + information.member = SPQR_INVALID_MEMBER; + information.head = SPQR_INVALID_NODE; + information.tail = SPQR_INVALID_NODE; + information.reversed = FALSE; + information.representative = SPQR_INVALID_ARC; + return information; +} +static void setTerminalHead(NewColInformation * info, spqr_node node){ + assert(SPQRnodeIsValid(node)); + assert(SPQRnodeIsInvalid(info->head)); + assert(info); + info->head = node; +} +static void setTerminalTail(NewColInformation * info, spqr_node node){ + assert(SPQRnodeIsValid(node)); + assert(info); + assert(SPQRnodeIsInvalid(info->tail)); + info->tail = node; +} +static void setTerminalReversed(NewColInformation *info, SCIP_Bool reversed){ + assert(info); + info->reversed = reversed; +} +static void setTerminalMember(NewColInformation * info, spqr_member member){ + assert(info); + info->member = member; +} +static void setTerminalRepresentative(NewColInformation * info, spqr_arc representative){ + assert(info); + info->representative = representative; +} + +static SCIP_RETCODE splitParallel(SCIP_NETWORKDECOMP *dec, spqr_member parallel, + spqr_arc arc1, spqr_arc arc2, + spqr_member * childParallel){ + assert(dec); + assert(SPQRarcIsValid(arc1)); + assert(SPQRarcIsValid(arc2)); + assert(SPQRmemberIsValid(parallel)); + + SCIP_Bool childContainsTree = arcIsTree(dec,arc1) || arcIsTree(dec,arc2); + spqr_arc toParent = markerToParent(dec, parallel); + SCIP_Bool parentMoved = toParent == arc1 || toParent == arc2; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, childParallel)); + + moveArcToNewMember(dec,arc1,parallel,*childParallel); + moveArcToNewMember(dec,arc2,parallel,*childParallel); + + if(parentMoved){ + SCIP_CALL(createMarkerPair(dec,*childParallel,parallel,!childContainsTree,FALSE,FALSE)); + }else{ + SCIP_CALL(createMarkerPair(dec,parallel,*childParallel,childContainsTree,FALSE,FALSE)); + } + return SCIP_OKAY; +} + +static SCIP_RETCODE splitSeries(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, + SPQRColReducedMember * reducedMember, + spqr_member member, spqr_member * loopMember, NewColInformation * newColInfo){ + assert(dec); + assert(reducedMember); + assert(SPQRmemberIsValid(member)); + assert(memberIsRepresentative(dec,member)); + + assert(reducedMember->numPathArcs != 0); + SCIP_Bool createPathSeries = reducedMember->numPathArcs > 1; + SCIP_Bool convertOriginal = reducedMember->numPathArcs == getNumMemberArcs(dec,member) -1; + + //TODO: for now very elaborate to first get logic correct. Can probably merge some branches later... + if(!createPathSeries && !convertOriginal){ + spqr_member parallel; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, ¶llel)); + path_arc_id pathArcId = reducedMember->firstPathArc; + assert(pathArcIsValid(pathArcId)); + spqr_arc marked = newCol->pathArcs[pathArcId].arc; + assert(marked != markerToParent(dec,member));//TODO: handle this case later + moveArcToNewMember(dec,marked,member,parallel); + SCIP_Bool reversed = arcIsReversedNonRigid(dec,marked) ; + SCIP_CALL(createMarkerPair(dec,member,parallel,TRUE, reversed,reversed)); + *loopMember = parallel; + + if(reversed == reducedMember->pathBackwards){ + setTerminalReversed(newColInfo,!reversed); + }else{ + setTerminalReversed(newColInfo,reversed); + } + + setTerminalMember(newColInfo,*loopMember); + return SCIP_OKAY; + } + if(!createPathSeries && convertOriginal){ + //only one path arc; we are in a loop; no need to change anything + assert(getNumMemberArcs(dec,member) == 2); + assert(reducedMember->numPathArcs == 1); + *loopMember = member; + changeLoopToParallel(dec,member); + + path_arc_id pathArcId = reducedMember->firstPathArc; + spqr_arc marked = newCol->pathArcs[pathArcId].arc; + //The 'reversed' field has different meaning for parallels, so we need to change orientation when converting to a parallel + arcFlipReversed(dec,marked); + + setTerminalReversed(newColInfo,reducedMember->pathBackwards); + setTerminalMember(newColInfo,*loopMember); + return SCIP_OKAY; + } + if(createPathSeries && !convertOriginal){ + spqr_member pathMember; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember)); + + path_arc_id pathArcId = reducedMember->firstPathArc; + SCIP_Bool parentMoved = FALSE; + while(pathArcIsValid(pathArcId)){ + spqr_arc pathArc = newCol->pathArcs[pathArcId].arc; + pathArcId = newCol->pathArcs[pathArcId].nextMember; + if(pathArc == markerToParent(dec,member)){ + parentMoved = TRUE; + } + moveArcToNewMember(dec,pathArc,member,pathMember); + } + + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, loopMember)); + + if(!parentMoved){ + SCIP_CALL(createMarkerPair(dec,member,*loopMember,TRUE,FALSE,FALSE)); + SCIP_CALL(createMarkerPair(dec,*loopMember,pathMember,TRUE,FALSE,TRUE)); + }else{ + SCIP_CALL(createMarkerPair(dec,pathMember,*loopMember,FALSE,FALSE,TRUE)); + SCIP_CALL(createMarkerPair(dec,*loopMember,member,FALSE,FALSE,FALSE)); + } + + setTerminalReversed(newColInfo,!reducedMember->pathBackwards); + setTerminalMember(newColInfo,*loopMember); + return SCIP_OKAY; + } + assert(createPathSeries && convertOriginal); + //There's one exception in this case + //if the single unmarked (column) marker is a parent or child marker to a parallel member, we add the edge there + { + spqr_member adjacentMember = SPQR_INVALID_MEMBER; + spqr_arc adjacentMarker = SPQR_INVALID_ARC; + spqr_arc memberMarker = SPQR_INVALID_ARC; + spqr_arc firstArc = getFirstMemberArc(dec, reducedMember->member); + spqr_arc arc = firstArc; + do { + if (!newCol->arcInPath[arc]) { + if (arc == markerToParent(dec, reducedMember->member)) { + adjacentMember = findMemberParent(dec, reducedMember->member); + adjacentMarker = markerOfParent(dec, reducedMember->member); + memberMarker = arc; + } else if (arcIsMarker(dec, arc)) { + adjacentMember = findArcChildMember(dec, arc); + adjacentMarker = markerToParent(dec, adjacentMember); + memberMarker = arc; + } + + break; //There is only a singular such arc + } + arc = getNextMemberArc(dec, arc); + } while (arc != firstArc); + + if (SPQRmemberIsValid(adjacentMember)) { + SPQRMemberType adjacentType = getMemberType(dec, adjacentMember); + if (adjacentType == SPQR_MEMBERTYPE_PARALLEL) { + //Figure out if the markers are the same or opposite orientations + //If they are the same, we can proceed as normal, otherwise, we need to flip the placed edge + SCIP_Bool markersHaveSameOrientation = + arcIsReversedNonRigid(dec, adjacentMarker) == arcIsReversedNonRigid(dec, memberMarker); + setTerminalReversed(newColInfo, reducedMember->pathBackwards == markersHaveSameOrientation); + setTerminalMember(newColInfo, adjacentMember); + return SCIP_OKAY; + } + } + } + + spqr_member pathMember; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember)); + + path_arc_id pathArcId = reducedMember->firstPathArc; + SCIP_Bool parentMoved = FALSE; + while(pathArcIsValid(pathArcId)){ + spqr_arc pathArc = newCol->pathArcs[pathArcId].arc; + pathArcId = newCol->pathArcs[pathArcId].nextMember; + if(pathArc == markerToParent(dec,member)){ + parentMoved = TRUE; + } + moveArcToNewMember(dec,pathArc,member,pathMember); + } + if(parentMoved){ + SCIP_CALL(createMarkerPair(dec,pathMember,member,FALSE,FALSE,FALSE)); + }else{ + SCIP_CALL(createMarkerPair(dec,member,pathMember,TRUE,FALSE,FALSE)); + } + + changeLoopToParallel(dec,member); + + *loopMember = member; + setTerminalReversed(newColInfo,reducedMember->pathBackwards); + setTerminalMember(newColInfo,*loopMember); + return SCIP_OKAY; +} + + +static SCIP_RETCODE splitSeriesMerging(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, + SPQRColReducedMember * reducedMember, + spqr_member member, + spqr_arc * pathRepresentative, + spqr_arc * nonPathRepresentative, + spqr_arc exceptionArc1, + spqr_arc exceptionArc2){ + assert(dec); + assert(reducedMember); + assert(SPQRmemberIsValid(member)); + assert(memberIsRepresentative(dec,member)); + + int numExceptionArcs = (exceptionArc1 == SPQR_INVALID_ARC ? 0 : 1) + (exceptionArc2 == SPQR_INVALID_ARC ? 0 : 1); + int numNonPathArcs = getNumMemberArcs(dec,member) - reducedMember->numPathArcs - numExceptionArcs; + SCIP_Bool createPathSeries = reducedMember->numPathArcs > 1; + //If this holds, there are 2 or more non-parent marker non-path arcs + SCIP_Bool createNonPathSeries = numNonPathArcs > 1; + assert(exceptionArc1 == SPQR_INVALID_ARC || !newCol->arcInPath[exceptionArc1]); + assert(exceptionArc2 == SPQR_INVALID_ARC || !newCol->arcInPath[exceptionArc2]); + + if(createPathSeries){ + spqr_member pathMember; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember)); + + path_arc_id pathArcId = reducedMember->firstPathArc; + SCIP_Bool parentMoved = FALSE; + while(pathArcIsValid(pathArcId)){ + spqr_arc pathArc = newCol->pathArcs[pathArcId].arc; + pathArcId = newCol->pathArcs[pathArcId].nextMember; + assert(pathArc != exceptionArc1 && pathArc != exceptionArc2); + parentMoved = parentMoved || markerToParent(dec,member) == pathArc; + moveArcToNewMember(dec,pathArc,member,pathMember); + } + assert(getNumMemberArcs(dec,pathMember) >= 2); + + spqr_arc ignored; + SCIP_Bool inOldReversed = TRUE; + SCIP_Bool inNewReversed = FALSE; + if(parentMoved){ + SCIP_CALL(createMarkerPairWithReferences(dec,pathMember,member,FALSE,inNewReversed,inOldReversed, + &ignored,pathRepresentative)); + }else{ + SCIP_CALL(createMarkerPairWithReferences(dec,member,pathMember,TRUE,inOldReversed,inNewReversed, + pathRepresentative,&ignored)); + } + }else{ + if(pathArcIsValid(reducedMember->firstPathArc)){ + *pathRepresentative = newCol->pathArcs[reducedMember->firstPathArc].arc; + }else{ + *pathRepresentative = SPQR_INVALID_ARC; + } + } + + if(createNonPathSeries){ + spqr_member nonPathMember; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &nonPathMember)); + + spqr_arc arc = getFirstMemberArc(dec, member); + SCIP_Bool parentMoved = FALSE; + SCIP_Bool canStop = FALSE; //hack when the first arc is moved in the below loop to prevent that we immediately terminate + do{ + spqr_arc nextArc = getNextMemberArc(dec, arc); + if(arc != *pathRepresentative && arc != exceptionArc1 && arc != exceptionArc2){ + parentMoved = parentMoved || markerToParent(dec,member) == arc; + moveArcToNewMember(dec,arc,member,nonPathMember); + }else{ + canStop = TRUE; + } + arc = nextArc; + if(canStop && arc == getFirstMemberArc(dec,member)){ + break; + } + }while(TRUE); + assert(getNumMemberArcs(dec,nonPathMember) >= 2); + SCIP_Bool representativeIsTree = !arcIsTree(dec,exceptionArc1); + if(SPQRarcIsValid(exceptionArc2)){ + representativeIsTree = representativeIsTree || !arcIsTree(dec,exceptionArc2); + } + spqr_arc ignored; + SCIP_Bool inOldReversed = TRUE; + SCIP_Bool inNewReversed = FALSE; + if(parentMoved){ + SCIP_CALL(createMarkerPairWithReferences(dec,nonPathMember,member,!representativeIsTree, + inNewReversed,inOldReversed,&ignored,nonPathRepresentative)); + }else{ + SCIP_CALL(createMarkerPairWithReferences(dec,member,nonPathMember,representativeIsTree, + inOldReversed,inNewReversed,nonPathRepresentative,&ignored)); + } + }else{ + *nonPathRepresentative = SPQR_INVALID_ARC; + if(numNonPathArcs != 0) { + spqr_arc firstArc = getFirstMemberArc(dec, member); + spqr_arc arc = firstArc; + do { + if (arc != *pathRepresentative && arc != exceptionArc1 && arc != exceptionArc2) { + *nonPathRepresentative = arc; + break; + } + arc = getNextMemberArc(dec, arc); + } while (arc != firstArc); + assert(*nonPathRepresentative != SPQR_INVALID_ARC); + } + } + + return SCIP_OKAY; +} + +static SCIP_RETCODE transformFirstPathMember(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, + reduced_member_id reducedMember, NewColInformation * newColInfo, + spqr_arc * representativeArc, + spqr_member * mergedMember ){ + spqr_member member = newCol->reducedMembers[reducedMember].member; + SPQRMemberType type = getMemberType(dec,member); + if(type == SPQR_MEMBERTYPE_RIGID){ + //The nodes are already created, we only need to assign the correct start/end node + switch(newCol->reducedMembers[reducedMember].pathType){ + case INTO_HEAD: + case INTO_TAIL: + setTerminalTail(newColInfo,newCol->reducedMembers[reducedMember].rigidPathStart); + break; + case OUT_HEAD: + case OUT_TAIL: + setTerminalHead(newColInfo,newCol->reducedMembers[reducedMember].rigidPathEnd); + break; + } + *representativeArc = findArcSign(dec,newCol->reducedMembers[reducedMember].pathTargetArc).representative; + *mergedMember = member; + + return SCIP_OKAY; + } + assert(type == SPQR_MEMBERTYPE_SERIES); + //Split off sets of multiple path non-path edges so that the series has exactly 3 edges + + spqr_arc target = newCol->reducedMembers[reducedMember].pathTargetArc; + SPQRColReducedMember * redMem = &newCol->reducedMembers[reducedMember]; + spqr_arc pathRepresentative = SPQR_INVALID_ARC; + spqr_arc nonPathRepresentative = SPQR_INVALID_ARC; + SCIP_CALL(splitSeriesMerging(dec,newCol,redMem,member,&pathRepresentative,&nonPathRepresentative,target,SPQR_INVALID_ARC)); + + assert(target != pathRepresentative + && target != nonPathRepresentative && pathRepresentative != nonPathRepresentative); + assert(SPQRarcIsValid(pathRepresentative) && SPQRarcIsValid(nonPathRepresentative) && SPQRarcIsValid(target)); + assert(getNumMemberArcs(dec,member) == 3); + + //Create nodes + spqr_node a = SPQR_INVALID_NODE; + spqr_node b = SPQR_INVALID_NODE; + spqr_node c = SPQR_INVALID_NODE; + SCIP_CALL(createNode(dec, &a)); + SCIP_CALL(createNode(dec, &b)); + SCIP_CALL(createNode(dec, &c)); + + // a -- b + // \ / + // c + + //Set arc nodes + //Set target from b to c, + SCIP_Bool targetReversed = arcIsReversedNonRigid(dec,target); + setArcHeadAndTail(dec, target, c, b); + + MemberPathType pathType = newCol->reducedMembers[reducedMember].pathType; + assert(pathType == INTO_HEAD || pathType == OUT_HEAD); + if(arcIsReversedNonRigid(dec,pathRepresentative) == targetReversed){ + setArcHeadAndTail(dec,pathRepresentative,a,c); + }else{ + setArcHeadAndTail(dec,pathRepresentative,c,a); + } + if(arcIsReversedNonRigid(dec,nonPathRepresentative) == targetReversed){ + setArcHeadAndTail(dec,nonPathRepresentative,b,a); + }else{ + setArcHeadAndTail(dec,nonPathRepresentative,a,b); + } + //setup signed union find; all arcs are placed are not reversed. We pick an arbitrary arc as 'root' arc for this skeleton + arcSetReversed(dec,target,FALSE); + arcSetReversed(dec,pathRepresentative,FALSE); + arcSetReversed(dec,nonPathRepresentative,FALSE); + arcSetRepresentative(dec,target,SPQR_INVALID_ARC); + arcSetRepresentative(dec,pathRepresentative,target); + arcSetRepresentative(dec,nonPathRepresentative,target); + *representativeArc = target; + + if(pathType == INTO_HEAD){ + setTerminalTail(newColInfo,a); + }else{ + setTerminalHead(newColInfo,a); + } + + *mergedMember = member; + + return SCIP_OKAY; +} +static SCIP_RETCODE transformAndMergeParallel(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, + reduced_member_id current, reduced_member_id next, + spqr_member nextMember, SCIP_Bool nextIsParent, + spqr_arc * representativeArc, + spqr_member * mergedMember){ + //Split off edges not in current subtree to form one parallel (or one edge) + spqr_member childParallel = INVALID_REDUCED_MEMBER; + spqr_arc source = newCol->reducedMembers[next].pathSourceArc; + spqr_arc target = newCol->reducedMembers[next].pathTargetArc; + if(getNumMemberArcs(dec,nextMember) > 3){ + SCIP_CALL(splitParallel(dec, nextMember, source, target, &childParallel)); + nextMember = childParallel; + newCol->reducedMembers[next].member = childParallel; + } + assert(getNumMemberArcs(dec,nextMember) == 3); + + spqr_node sourceHead = SPQR_INVALID_NODE; + spqr_node sourceTail = SPQR_INVALID_NODE; + SCIP_CALL(createNode(dec,&sourceHead)); + SCIP_CALL(createNode(dec,&sourceTail)); + + //set edge nodes and arc union-find data + { + spqr_arc firstArc = getFirstMemberArc(dec,nextMember); + spqr_arc arc = firstArc; + + SCIP_Bool sourceReversed = arcIsReversedNonRigid(dec,source); + do{ + if(arcIsReversedNonRigid(dec,arc) == sourceReversed){ + setArcHeadAndTail(dec,arc,sourceHead,sourceTail); + }else{ + setArcHeadAndTail(dec,arc,sourceTail,sourceHead); + } + arcSetRepresentative(dec,arc,source); + arcSetReversed(dec,arc,FALSE); + + arc = getNextMemberArc(dec,arc); + }while(arc != firstArc); + + arcSetRepresentative(dec,source,SPQR_INVALID_ARC); + } + + //fix arc orientations of members; we cannot reflect for parallels. + *representativeArc = mergeArcSigns(dec,*representativeArc,source,FALSE); + + spqr_member newMergedMember = SPQR_INVALID_MEMBER; + if(nextIsParent){ + mergeGivenMemberIntoParent(dec,*mergedMember,nextMember, + source,newCol->reducedMembers[current].pathTargetArc + ,TRUE,&newMergedMember); + }else{ + mergeGivenMemberIntoParent(dec,nextMember,*mergedMember, + newCol->reducedMembers[current].pathTargetArc,source ,TRUE,&newMergedMember); + } + * mergedMember = newMergedMember; + + return SCIP_OKAY; +} +static SCIP_RETCODE transformAndMergeSeries(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, + reduced_member_id current, reduced_member_id next, + spqr_member nextMember, SCIP_Bool nextIsParent, + spqr_arc * representativeArc, + spqr_member * mergedMember, + NewColInformation * info){ + + SPQRColReducedMember * redMem = &newCol->reducedMembers[next]; + spqr_arc source = redMem->pathSourceArc; + spqr_arc target = redMem->pathTargetArc; + + spqr_arc pathRepresentative = SPQR_INVALID_ARC; + spqr_arc nonPathRepresentative = SPQR_INVALID_ARC; + SCIP_CALL(splitSeriesMerging(dec,newCol,redMem,nextMember,&pathRepresentative,&nonPathRepresentative,source,target)); + //After splitting there is the following possibilities for nodes a-d: + //(a)-source-(b)-path-(c)-target-(d)-nonpath-(a) + //(a)-source-(b)-path-(c)-target-(d==a) + //(a)-source-(b)=(c)-target-(d)-nonpath-(a) + //(a)-source-(b)-path-(c)=(d) -nonpath-(a) + //Note that the given arc is always between the same nodes + assert(getNumMemberArcs(dec,nextMember) == 3 || getNumMemberArcs(dec,nextMember) == 4); + assert(pathRepresentative != source && nonPathRepresentative != source && (SPQRarcIsInvalid(target) || ( + target != pathRepresentative && target != nonPathRepresentative + ))); + spqr_node a = SPQR_INVALID_NODE; + spqr_node b = SPQR_INVALID_NODE; + spqr_node c = SPQR_INVALID_NODE; + spqr_node d = SPQR_INVALID_NODE; + SCIP_CALL(createNode(dec, &a)); + SCIP_CALL(createNode(dec, &b)); + if (SPQRarcIsValid(pathRepresentative)) { + SCIP_CALL(createNode(dec, &c)); + } else { + c = b; + } + SCIP_Bool hasNonPath = SPQRarcIsValid(nonPathRepresentative); + SCIP_Bool hasTarget = SPQRarcIsValid(target); + if (hasNonPath && hasTarget) { + SCIP_CALL(createNode(dec, &d)); + } else { + if(hasNonPath){ + d = c; + }else{ + d = a; + } + } + + SCIP_Bool sourceReversed = arcIsReversedNonRigid(dec,source); + SCIP_Bool pathStartInHead = isHead(newCol->reducedMembers[current].pathType); + if(pathStartInHead){ + setArcHeadAndTail(dec,source,b,a); + }else{ + setArcHeadAndTail(dec,source,a,b); + } + if(SPQRarcIsValid(pathRepresentative)){ + if((arcIsReversedNonRigid(dec,pathRepresentative) == sourceReversed) == pathStartInHead){ + setArcHeadAndTail(dec,pathRepresentative,c,b); + }else{ + setArcHeadAndTail(dec,pathRepresentative,b,c); + } + arcSetReversed(dec,pathRepresentative,FALSE); + arcSetRepresentative(dec,pathRepresentative,source); + } + if(hasTarget){ + if((arcIsReversedNonRigid(dec,target) == sourceReversed) == pathStartInHead){ + setArcHeadAndTail(dec,target,d,c); + }else{ + setArcHeadAndTail(dec,target,c,d); + } + arcSetReversed(dec,target,FALSE); + arcSetRepresentative(dec,target,source); + } + if(hasNonPath){ + if((arcIsReversedNonRigid(dec,nonPathRepresentative) == sourceReversed) == pathStartInHead){ + setArcHeadAndTail(dec,nonPathRepresentative,a,d); + }else{ + setArcHeadAndTail(dec,nonPathRepresentative,d,a); + } + arcSetReversed(dec,nonPathRepresentative,FALSE); + arcSetRepresentative(dec,nonPathRepresentative,source); + } + + arcSetReversed(dec,source,FALSE); + arcSetRepresentative(dec,source,SPQR_INVALID_ARC); + + //fix arc orientations of members; we cannot reflect for series + + spqr_member newMergedMember = SPQR_INVALID_MEMBER; + if(nextIsParent){ + mergeGivenMemberIntoParent(dec,*mergedMember,nextMember, + source,newCol->reducedMembers[current].pathTargetArc + ,TRUE,&newMergedMember); + }else{ + mergeGivenMemberIntoParent(dec,nextMember,*mergedMember, + newCol->reducedMembers[current].pathTargetArc,source ,TRUE,&newMergedMember); + } + * mergedMember = newMergedMember; + + *representativeArc = mergeArcSigns(dec,*representativeArc,source,FALSE); + if(!hasTarget){ + //We are in the last node; finish the path + setTerminalReversed(info,FALSE); + if(isInto(newCol->reducedMembers[current].pathType)) { + setTerminalHead(info, c); + }else{ + setTerminalTail(info, c); + } + setTerminalMember(info,*mergedMember); + setTerminalRepresentative(info,*representativeArc); + } + return SCIP_OKAY; +} +static SCIP_RETCODE transformAndMergeRigid(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, + reduced_member_id current, reduced_member_id next, + spqr_member nextMember, SCIP_Bool nextIsParent, + spqr_arc * representativeArc, + spqr_member * mergedMember, + NewColInformation * info){ + SPQRColReducedMember * redMem = &newCol->reducedMembers[next]; + spqr_arc source = redMem->pathSourceArc; + spqr_arc sourceRepresentative = findArcSign(dec,source).representative; + + + spqr_member newMergedMember = SPQR_INVALID_MEMBER; + + if(nextIsParent){ + mergeGivenMemberIntoParent(dec,*mergedMember,nextMember, + source,newCol->reducedMembers[current].pathTargetArc + ,!redMem->reverseArcs,&newMergedMember); + }else{ + mergeGivenMemberIntoParent(dec,nextMember,*mergedMember, + newCol->reducedMembers[current].pathTargetArc,source ,!redMem->reverseArcs,&newMergedMember); + } + + * mergedMember = newMergedMember; + + *representativeArc = mergeArcSigns(dec,*representativeArc,sourceRepresentative,redMem->reverseArcs); + + if(SPQRarcIsInvalid(redMem->pathTargetArc)){ + //We are in the last node; finish the path + setTerminalReversed(info,FALSE); + if(isInto(newCol->reducedMembers[current].pathType)) { + if(redMem->reverseArcs){ + setTerminalHead(info,redMem->rigidPathStart); + }else{ + setTerminalHead(info, redMem->rigidPathEnd); + } + }else{ + if(redMem->reverseArcs){ + setTerminalTail(info,redMem->rigidPathEnd); + }else{ + setTerminalTail(info, redMem->rigidPathStart); + } + } + setTerminalMember(info,*mergedMember); + setTerminalRepresentative(info,*representativeArc); + } + + return SCIP_OKAY; +} +static SCIP_RETCODE transformAndMerge(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, + reduced_member_id current, reduced_member_id next, + spqr_arc * representativeArc, + spqr_member * mergedMember, + SCIP_Bool nextIsParent, NewColInformation * info){ + spqr_member nextMember = newCol->reducedMembers[next].member; + switch(getMemberType(dec,nextMember)){ + case SPQR_MEMBERTYPE_RIGID:{ + SCIP_CALL(transformAndMergeRigid(dec,newCol,current,next,nextMember,nextIsParent, + representativeArc,mergedMember,info)); + break; + } + case SPQR_MEMBERTYPE_PARALLEL:{ + SCIP_CALL(transformAndMergeParallel(dec,newCol,current,next,nextMember,nextIsParent, + representativeArc, mergedMember)); + break; + } + case SPQR_MEMBERTYPE_SERIES:{ + SCIP_CALL(transformAndMergeSeries(dec,newCol,current,next,nextMember,nextIsParent, + representativeArc, mergedMember,info)); + break; + } + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_UNASSIGNED: + { + assert(FALSE); + break; + } + } + return SCIP_OKAY; +} + +static SCIP_RETCODE transformPath(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, + SPQRColReducedComponent * component, + NewColInformation * newColInfo){ + //Realize first member + reduced_member_id firstPathMember = component->pathEndMembers[0]; + + spqr_arc representativeArc = SPQR_INVALID_ARC; + spqr_member mergedMember = SPQR_INVALID_MEMBER; + SCIP_CALL(transformFirstPathMember(dec,newCol,firstPathMember,newColInfo,&representativeArc,&mergedMember)); + //Iteratively call function which realizes next member and merges them together. + reduced_member_id current = firstPathMember; + reduced_member_id next = newCol->reducedMembers[current].nextPathMember; + SCIP_Bool nextIsParent = newCol->reducedMembers[current].nextPathMemberIsParent; + while(reducedMemberIsValid(next)){ + SCIP_CALL(transformAndMerge(dec,newCol,current,next,&representativeArc,&mergedMember,nextIsParent,newColInfo)); + current = next; + next = newCol->reducedMembers[current].nextPathMember; + nextIsParent = newCol->reducedMembers[current].nextPathMemberIsParent; + } + return SCIP_OKAY; +} + +static SCIP_RETCODE columnTransformSingleParallel(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, + reduced_member_id reducedMemberId, spqr_member member, + NewColInformation *newColInfo){ + SPQRColReducedMember * reducedMember = &newCol->reducedMembers[reducedMemberId]; + assert(pathArcIsValid(reducedMember->firstPathArc) && reducedMember->numPathArcs == 1); + //The new arc can be placed in parallel; just add it to this member + setTerminalReversed(newColInfo,reducedMember->pathBackwards); + setTerminalMember(newColInfo,member); + return SCIP_OKAY; +} +static SCIP_RETCODE columnTransformSingleSeries(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, + reduced_member_id reducedMemberId, spqr_member member, + NewColInformation *newColInfo){ + + if(getNumMemberArcs(dec,member) == 1){ + newColInfo->member = member; + newColInfo->reversed = newCol->arcInPathReversed[getFirstMemberArc(dec,member)]; + return SCIP_OKAY; + } + //Isolated single cycle + spqr_member loopMember; + SPQRColReducedMember *reducedMember = &newCol->reducedMembers[reducedMemberId]; + SCIP_CALL(splitSeries(dec,newCol,reducedMember,member,&loopMember,newColInfo)); + + return SCIP_OKAY; +} +static SCIP_RETCODE columnTransformSingleRigid(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, + reduced_member_id reducedMemberId, spqr_member member, + NewColInformation *newColInfo){ + assert(dec); + assert(newCol); + assert(newColInfo); + assert(reducedMemberIsValid(reducedMemberId)); + //The path is already computed, so we can simply take the start and end nodes. + //However, there is one exception, which is that an arc connecting these two nodes already exists in the member + //If so, we create a new parallel member with the new arc and this member, unless the existing arc already points + //to a parallel member + SPQRColReducedMember * reducedMember = &newCol->reducedMembers[reducedMemberId]; + assert(SPQRnodeIsValid(reducedMember->rigidPathStart) && SPQRnodeIsValid(reducedMember->rigidPathEnd)); + { + spqr_arc existingArcWithPath = SPQR_INVALID_ARC; + spqr_arc firstArc = getFirstNodeArc(dec,reducedMember->rigidPathStart); + spqr_arc arc = firstArc; + SCIP_Bool pathInSameDirection = FALSE; + do{ + spqr_node head = findArcHead(dec,arc); + spqr_node tail = findArcTail(dec,arc); + spqr_node other = head == reducedMember->rigidPathStart ? tail : head; + if(other == reducedMember->rigidPathEnd){ + existingArcWithPath = arc; + pathInSameDirection = (head == other) != findArcSign(dec,existingArcWithPath).reversed; + break; + } + arc = getNextNodeArc(dec,arc,reducedMember->rigidPathStart); + }while(arc != firstArc); + if(SPQRarcIsValid(existingArcWithPath)){ + SCIP_Bool isParent = FALSE; + spqr_member adjacentMember = arcIsMarker(dec,existingArcWithPath) ? findArcChildMember(dec,existingArcWithPath) : SPQR_INVALID_MEMBER; + if(existingArcWithPath == markerToParent(dec,member)){ + adjacentMember = findMemberParent(dec,member); + isParent = TRUE; + } + if(SPQRmemberIsValid(adjacentMember) && getMemberType(dec,adjacentMember) == SPQR_MEMBERTYPE_PARALLEL){ + spqr_arc parallelMarker = isParent ? markerOfParent(dec,member) : markerToParent(dec,adjacentMember); + SCIP_Bool markerReversed = arcIsReversedNonRigid(dec,parallelMarker); + setTerminalMember(newColInfo,adjacentMember); + setTerminalReversed(newColInfo,markerReversed == pathInSameDirection); + }else{ + //create a new parallel and move the edge there + //This is a bit painful, because we cannot actually remove edges because of the union-find data structure + //So what we do instead, is convert the current edge to a marker edge, and 'duplicate' + //it in the new parallel member, and add the new marker there too, manually + spqr_member adjacentParallel = SPQR_INVALID_MEMBER; + SCIP_CALL(createMember(dec,SPQR_MEMBERTYPE_PARALLEL,&adjacentParallel)); + //'duplicate' a new arc in the parallel to be the current arc + spqr_arc duplicate = SPQR_INVALID_ARC; + spqr_element element = arcGetElement(dec,existingArcWithPath); + if( element != MARKER_COLUMN_ELEMENT && element != MARKER_ROW_ELEMENT){ + if(SPQRelementIsColumn(element)){ + SCIP_CALL(createColumnArc(dec,adjacentParallel,&duplicate, SPQRelementToColumn(element),FALSE)); + }else{ + SCIP_CALL(createRowArc(dec,adjacentParallel,&duplicate, SPQRelementToRow(element),FALSE)); + } + }else if(isParent){ + SCIP_CALL(createParentMarker(dec,adjacentParallel, arcIsTree(dec,existingArcWithPath),adjacentMember, + markerOfParent(dec,member) + ,&duplicate,FALSE)); + }else{ + SCIP_CALL(createChildMarker(dec,adjacentParallel,adjacentMember,arcIsTree(dec,existingArcWithPath),&duplicate,FALSE)); + dec->members[adjacentMember].parentMember = adjacentParallel; + dec->members[adjacentMember].markerOfParent = duplicate; + } + //Create the other marker edge + spqr_arc parallelMarker = SPQR_INVALID_ARC; + if(isParent){ + SCIP_CALL(createChildMarker(dec,adjacentParallel,member,!arcIsTree(dec,existingArcWithPath), + ¶llelMarker,FALSE)); + }else{ + SCIP_CALL(createParentMarker(dec,adjacentParallel,!arcIsTree(dec,existingArcWithPath), + member,existingArcWithPath,¶llelMarker,FALSE)); + } + + //Change the existing edge to a marker + if(isParent){ + assert(markerToParent(dec,member) == existingArcWithPath); + dec->arcs[markerOfParent(dec,member)].childMember = adjacentParallel; + dec->members[member].parentMember = adjacentParallel; + dec->members[member].markerToParent = existingArcWithPath; + dec->members[member].markerOfParent = parallelMarker; + dec->arcs[existingArcWithPath].element = arcIsTree(dec,existingArcWithPath) ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT;; + dec->arcs[existingArcWithPath].childMember = adjacentParallel; + + }else{ + dec->arcs[existingArcWithPath].element = arcIsTree(dec,existingArcWithPath) ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; + dec->arcs[existingArcWithPath].childMember = adjacentParallel; + } + + setTerminalMember(newColInfo,adjacentParallel); + setTerminalReversed(newColInfo,!pathInSameDirection); + } + return SCIP_OKAY; + } + } + + setTerminalMember(newColInfo,member); + setTerminalReversed(newColInfo,FALSE); + setTerminalTail(newColInfo,reducedMember->rigidPathStart); + setTerminalHead(newColInfo,reducedMember->rigidPathEnd); + setTerminalRepresentative(newColInfo,findArcSign(dec,newCol->pathArcs[reducedMember->firstPathArc].arc).representative); + return SCIP_OKAY; +} +static SCIP_RETCODE transformComponent(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, SPQRColReducedComponent * component, NewColInformation * newColInfo){ + assert(dec); + assert(newCol); + assert(component); + assert(newColInfo); + + if(newCol->reducedMembers[component->root].numChildren == newCol->reducedMembers[component->root].numPropagatedChildren){ + //No merging necessary, only a single component + reduced_member_id reducedMember = component->root; + assert(reducedMemberIsValid(reducedMember)); + spqr_member member = newCol->reducedMembers[reducedMember].member; + SPQRMemberType type = getMemberType(dec, member); + + switch(type) { + case SPQR_MEMBERTYPE_RIGID: { + SCIP_CALL(columnTransformSingleRigid(dec,newCol,reducedMember,member,newColInfo)); + break; + } + case SPQR_MEMBERTYPE_PARALLEL: { + SCIP_CALL(columnTransformSingleParallel(dec, newCol, reducedMember, member, newColInfo)); + break; + } + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_SERIES: { + SCIP_CALL(columnTransformSingleSeries(dec, newCol, reducedMember, member, newColInfo)); + break; + } + default: { + assert(FALSE); + break; + } + } + return SCIP_OKAY; + } + // Otherwise, the reduced members form a path which can be merged into a single component of type R + SCIP_CALL(transformPath(dec,newCol,component,newColInfo)); + + return SCIP_OKAY; +} + +SCIP_RETCODE SCIPNetworkColAdditionAdd(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol){ + assert(dec); + assert(newCol); + + if(newCol->numReducedComponents == 0){ + spqr_member member; + SCIP_CALL(createStandaloneSeries(dec,newCol->newRowArcs,newCol->newRowArcReversed, + newCol->numNewRowArcs,newCol->newColIndex,&member)); + }else if(newCol->numReducedComponents == 1){ + NewColInformation information = emptyNewColInformation(); + SCIP_CALL(transformComponent(dec,newCol,&newCol->reducedComponents[0],&information)); + assert(memberIsRepresentative(dec,information.member)); + if(newCol->numNewRowArcs == 0){ + spqr_arc colArc = SPQR_INVALID_ARC; + SCIP_CALL(createColumnArc(dec,information.member,&colArc,newCol->newColIndex,information.reversed)); + if(SPQRnodeIsValid(information.head)){ + assert(SPQRnodeIsValid(information.tail)); + assert(SPQRarcIsValid(information.representative)); + setArcHeadAndTail(dec,colArc,findNode(dec,information.head),findNode(dec,information.tail)); + arcSetRepresentative(dec,colArc,information.representative); + arcSetReversed(dec,colArc,information.reversed != arcIsReversedNonRigid(dec,information.representative)); + } + }else{ + spqr_member newSeries = SPQR_INVALID_MEMBER; + SCIP_CALL(createConnectedSeries(dec,newCol->newRowArcs,newCol->newRowArcReversed,newCol->numNewRowArcs,newCol->newColIndex,&newSeries)); + spqr_arc markerArc = SPQR_INVALID_ARC; + spqr_arc ignore = SPQR_INVALID_ARC; + SCIP_CALL(createMarkerPairWithReferences(dec,information.member,newSeries,FALSE,information.reversed,TRUE,&markerArc,&ignore)); + if(SPQRnodeIsValid(information.head)){ + assert(SPQRnodeIsValid(information.tail)); + assert(SPQRarcIsValid(information.representative)); + setArcHeadAndTail(dec,markerArc,findNode(dec,information.head),findNode(dec,information.tail)); + arcSetRepresentative(dec,markerArc,information.representative); + arcSetReversed(dec,markerArc,information.reversed != arcIsReversedNonRigid(dec,information.representative)); + } + } + if(getMemberType(dec,information.member) == SPQR_MEMBERTYPE_LOOP){ + assert(getNumMemberArcs(dec,information.member) == 2 || getNumMemberArcs(dec,information.member) == 3); + if(getNumMemberArcs(dec,information.member) == 3){ + changeLoopToParallel(dec,information.member); + } + } + }else{ +#ifndef NDEBUG + int numDecComponentsBefore = numConnectedComponents(dec); +#endif + spqr_member newSeries = SPQR_INVALID_MEMBER; + SCIP_CALL(createConnectedSeries(dec,newCol->newRowArcs,newCol->newRowArcReversed, + newCol->numNewRowArcs,newCol->newColIndex,&newSeries)); + for (int i = 0; i < newCol->numReducedComponents; ++i) { + NewColInformation information = emptyNewColInformation(); + SCIP_CALL(transformComponent(dec,newCol,&newCol->reducedComponents[i],&information)); + if(getMemberType(dec,information.member) == SPQR_MEMBERTYPE_LOOP){ + assert(getNumMemberArcs(dec,information.member) == 1); + spqr_arc arc = getFirstMemberArc(dec,information.member); + assert(newCol->arcInPath[arc]); + moveArcToNewMember(dec,arc,information.member,newSeries); + arcSetReversed(dec,arc, !newCol->arcInPathReversed[arc]); + dec->members[information.member].type = SPQR_MEMBERTYPE_UNASSIGNED; + }else { + reorderComponent(dec, + information.member); //reorder the subtree so that the newly series member is a parent + spqr_arc markerArc = SPQR_INVALID_ARC; + spqr_arc ignore = SPQR_INVALID_ARC; + SCIP_CALL(createMarkerPairWithReferences(dec, newSeries, information.member, TRUE, information.reversed, + TRUE, &ignore, &markerArc)); + if (SPQRnodeIsValid(information.head)) { + assert(SPQRnodeIsValid(information.tail)); + assert(SPQRarcIsValid(information.representative)); + setArcHeadAndTail(dec, markerArc, findNode(dec, information.head), findNode(dec, information.tail)); + arcSetRepresentative(dec, markerArc, information.representative); + arcSetReversed(dec, markerArc, information.reversed == arcIsReversedNonRigid(dec, information.representative)); + } + } + } + decreaseNumConnectedComponents(dec,newCol->numReducedComponents-1); + assert(numConnectedComponents(dec) == (numDecComponentsBefore - newCol->numReducedComponents + 1)); + } + return SCIP_OKAY; +} + +SCIP_Bool SCIPNetworkColAdditionRemainsNetwork(SCIP_NETWORKCOLADDITION *newCol){ + return newCol->remainsNetwork; +} + + +static int min(int a, int b){ + return a < b ? a : b; +} + +typedef int cut_arc_id; +#define INVALID_CUT_ARC (-1) + +static SCIP_Bool cutArcIsInvalid(const cut_arc_id arc){ + return arc < 0; +} +static SCIP_Bool cutArcIsValid(const cut_arc_id arc){ + return !cutArcIsInvalid(arc); +} + +typedef struct { //TODO:test if memory overhead of pointers is worth it? + spqr_arc arc; + spqr_node arcHead; + spqr_node arcTail; + cut_arc_id nextMember; + cut_arc_id nextOverall; + SCIP_Bool arcReversed; +} CutArcListNode; + + +typedef enum{ + TYPE_UNDETERMINED = 0, + TYPE_PROPAGATED = 1, + TYPE_MERGED = 2, + TYPE_NOT_NETWORK = 3 +} RowReducedMemberType; + + +typedef struct{ + int low; + int discoveryTime; +} ArticulationNodeInformation; + +//We allocate the callstacks of recursive algorithms (usually DFS, bounded by some linear number of calls) +//If one does not do this, we overflow the stack for large matrices/graphs through the number of recursive function calls +//Then, we can write the recursive algorithms as while loops and allocate the function call data on the heap, preventing +//Stack overflows +typedef struct { + spqr_node node; + spqr_arc nodeArc; +} DFSCallData; + +typedef struct{ + children_idx currentChild; + reduced_member_id id; +} MergeTreeCallData; + +typedef struct{ + spqr_node node; + spqr_arc arc; +} ColorDFSCallData; + +typedef struct{ + spqr_arc arc; + spqr_node node; + spqr_node parent; + SCIP_Bool isAP; +} ArticulationPointCallStack; + +typedef enum{ + UNCOLORED = 0, + COLOR_SOURCE = 1, + COLOR_SINK = 2 +} COLOR_STATUS; + +typedef struct { + spqr_member member; + spqr_member rootMember; + int depth; + RowReducedMemberType type; + reduced_member_id parent; + + children_idx firstChild; + children_idx numChildren; + children_idx numPropagatedChildren; + + cut_arc_id firstCutArc; + int numCutArcs; + + //For non-rigid members + spqr_arc splitArc; + SCIP_Bool splitHead; //Otherwise the tail of this arc is split + SCIP_Bool otherIsSource; //Otherwise the other end node is part of the sink partition. + // For non-rigid members this refers to splitArc, for rigid members it refers to articulation arc + + //For rigid members + spqr_node otherNode; + spqr_node splitNode; + SCIP_Bool allHaveCommonNode; + SCIP_Bool otherNodeSplit; + SCIP_Bool willBeReversed; + spqr_arc articulationArc; + spqr_node coloredNode; //points to a colored node so that we can efficiently zero out the colors again. + +} SPQRRowReducedMember; + +typedef struct { + int rootDepth; + reduced_member_id root; +} SPQRRowReducedComponent; + +struct SCIP_NetworkRowAddition { + SCIP_Bool remainsNetwork; + + SPQRRowReducedMember *reducedMembers; + int memReducedMembers; + int numReducedMembers; + + SPQRRowReducedComponent *reducedComponents; + int memReducedComponents; + int numReducedComponents; + + MemberInfo *memberInformation; + int memMemberInformation; + int numMemberInformation; + + reduced_member_id *childrenStorage; + int memChildrenStorage; + int numChildrenStorage; + + CutArcListNode *cutArcs; + int memCutArcs; + int numCutArcs; + cut_arc_id firstOverallCutArc; + + spqr_row newRowIndex; + + spqr_col *newColumnArcs; + SCIP_Bool *newColumnReversed; + int memColumnArcs; + int numColumnArcs; + + reduced_member_id *leafMembers; + int numLeafMembers; + int memLeafMembers; + + spqr_arc *decompositionColumnArcs; + SCIP_Bool *decompositionColumnArcReversed; + int memDecompositionColumnArcs; + int numDecompositionColumnArcs; + + SCIP_Bool *isArcCut; + SCIP_Bool *isArcCutReversed; + int numIsArcCut; + int memIsArcCut; + + COLOR_STATUS *nodeColors; + int memNodeColors; + + spqr_node *articulationNodes; + int numArticulationNodes; + int memArticulationNodes; + + ArticulationNodeInformation *articulationNodeSearchInfo; + int memNodeSearchInfo; + + int *crossingPathCount; + int memCrossingPathCount; + + DFSCallData *intersectionDFSData; + int memIntersectionDFSData; + + ColorDFSCallData *colorDFSData; + int memColorDFSData; + + ArticulationPointCallStack *artDFSData; + int memArtDFSData; + + CreateReducedMembersCallstack *createReducedMembersCallstack; + int memCreateReducedMembersCallstack; + + int *intersectionPathDepth; + int memIntersectionPathDepth; + + spqr_node *intersectionPathParent; + int memIntersectionPathParent; + + MergeTreeCallData *mergeTreeCallData; + int memMergeTreeCallData; +}; + +typedef struct { + spqr_member member; + spqr_node head; + spqr_node tail; + spqr_arc representative; + SCIP_Bool reversed; +} NewRowInformation; + +static NewRowInformation emptyNewRowInformation(void){ + NewRowInformation information; + information.member = SPQR_INVALID_MEMBER; + information.head = SPQR_INVALID_NODE; + information.tail = SPQR_INVALID_NODE; + information.representative = SPQR_INVALID_ARC; + information.reversed = FALSE; + return information; +} + +/** + * Saves the information of the current row and partitions it based on whether or not the given columns are + * already part of the decomposition. + */ +static SCIP_RETCODE newRowUpdateRowInformation(const SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + const spqr_row row, const spqr_col * columns, const double * columnValues, + const size_t numColumns) +{ + newRow->newRowIndex = row; + + newRow->numDecompositionColumnArcs = 0; + newRow->numColumnArcs = 0; + + for (size_t i = 0; i < numColumns; ++i) { + spqr_arc columnArc = getDecompositionColumnArc(dec, columns[i]); + SCIP_Bool reversed = columnValues[i] < 0.0; + if(SPQRarcIsValid(columnArc)){ //If the arc is the current decomposition: save it in the array + if(newRow->numDecompositionColumnArcs == newRow->memDecompositionColumnArcs){ + int newNumArcs = newRow->memDecompositionColumnArcs == 0 ? 8 : 2*newRow->memDecompositionColumnArcs; + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->decompositionColumnArcs, + newRow->memDecompositionColumnArcs, newNumArcs)); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->decompositionColumnArcReversed, + newRow->memDecompositionColumnArcs, newNumArcs)); + newRow->memDecompositionColumnArcs = newNumArcs; + } + newRow->decompositionColumnArcs[newRow->numDecompositionColumnArcs] = columnArc; + newRow->decompositionColumnArcReversed[newRow->numDecompositionColumnArcs] = reversed; + ++newRow->numDecompositionColumnArcs; + }else{ + //Not in the decomposition: add it to the set of arcs which are newly added with this row. + if(newRow->numColumnArcs == newRow->memColumnArcs){ + int newNumArcs = newRow->memColumnArcs == 0 ? 8 : 2*newRow->memColumnArcs; + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->newColumnArcs, + newRow->memColumnArcs,newNumArcs)); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->newColumnReversed, + newRow->memColumnArcs,newNumArcs)); + newRow->memColumnArcs = newNumArcs; + + } + newRow->newColumnArcs[newRow->numColumnArcs] = columns[i]; + newRow->newColumnReversed[newRow->numColumnArcs] = reversed; + newRow->numColumnArcs++; + } + } + + return SCIP_OKAY; +} + +/** + * Recursively creates reduced members from this member to the root of the decomposition tree. + * @param dec + * @param newRow + * @param member + * @return + */ +static reduced_member_id createRowReducedMembersToRoot(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION * newRow, const spqr_member firstMember){ + assert(SPQRmemberIsValid(firstMember)); + + CreateReducedMembersCallstack * callstack = newRow->createReducedMembersCallstack; + callstack[0].member = firstMember; + int callDepth = 0; + + while(callDepth >= 0){ + spqr_member member = callstack[callDepth].member; + reduced_member_id reducedMember = newRow->memberInformation[member].reducedMember; + + SCIP_Bool reducedValid = reducedMemberIsValid(reducedMember); + if(!reducedValid) { + //reduced member was not yet created; we create it + reducedMember = newRow->numReducedMembers; + + SPQRRowReducedMember *reducedMemberData = &newRow->reducedMembers[reducedMember]; + ++newRow->numReducedMembers; + + reducedMemberData->member = member; + reducedMemberData->numChildren = 0; + reducedMemberData->numCutArcs = 0; + reducedMemberData->firstCutArc = INVALID_CUT_ARC; + reducedMemberData->type = TYPE_UNDETERMINED; + reducedMemberData->numPropagatedChildren = 0; + reducedMemberData->articulationArc = SPQR_INVALID_ARC; + reducedMemberData->splitNode = SPQR_INVALID_NODE; + reducedMemberData->otherNode = SPQR_INVALID_NODE; + reducedMemberData->splitArc = SPQR_INVALID_ARC; + reducedMemberData->splitHead = FALSE; + reducedMemberData->allHaveCommonNode = FALSE; + reducedMemberData->otherNodeSplit = FALSE; + reducedMemberData->willBeReversed = FALSE; + reducedMemberData->coloredNode = SPQR_INVALID_NODE; + + newRow->memberInformation[member].reducedMember = reducedMember; + assert(memberIsRepresentative(dec, member)); + spqr_member parentMember = findMemberParent(dec, member); + + if (SPQRmemberIsValid(parentMember)) { + //recursive call to parent member + ++callDepth; + assert(callDepth < newRow->memCreateReducedMembersCallstack); + callstack[callDepth].member = parentMember; + continue; + + } else { + //we found a new reduced decomposition component + + reducedMemberData->parent = INVALID_REDUCED_MEMBER; + reducedMemberData->depth = 0; + reducedMemberData->rootMember = member; + + assert(newRow->numReducedComponents < newRow->memReducedComponents); + newRow->reducedComponents[newRow->numReducedComponents].root = reducedMember; + ++newRow->numReducedComponents; + } + } + if(reducedValid){ + assert(reducedMember < newRow->numReducedMembers); + //Reduced member was already created in earlier call + //update the depth of the root if appropriate + reduced_member_id * depthMinimizer = &newRow->memberInformation[newRow->reducedMembers[reducedMember].rootMember].rootDepthMinimizer; + if(reducedMemberIsInvalid(*depthMinimizer) || + newRow->reducedMembers[reducedMember].depth < newRow->reducedMembers[*depthMinimizer].depth){ + *depthMinimizer = reducedMember; + } + } + while(TRUE){ + --callDepth; + if(callDepth < 0 ) break; + spqr_member parentMember = callstack[callDepth + 1].member; + reduced_member_id parentReducedMember = newRow->memberInformation[parentMember].reducedMember; + spqr_member currentMember = callstack[callDepth].member; + reduced_member_id currentReducedMember = newRow->memberInformation[currentMember].reducedMember; + + SPQRRowReducedMember *parentReducedMemberData = &newRow->reducedMembers[parentReducedMember]; + SPQRRowReducedMember *reducedMemberData = &newRow->reducedMembers[currentReducedMember]; + + reducedMemberData->parent = parentReducedMember; + reducedMemberData->depth = parentReducedMemberData->depth + 1; + reducedMemberData->rootMember = parentReducedMemberData->rootMember; + + newRow->reducedMembers[parentReducedMember].numChildren++; + } + + } + + reduced_member_id returnedMember = newRow->memberInformation[callstack[0].member].reducedMember; + return returnedMember; +} + + +/** + * Construct a smaller sub tree of the decomposition on which the cut arcs lie. + * @return + */ +static SCIP_RETCODE constructRowReducedDecomposition(SCIP_NETWORKDECOMP* dec, SCIP_NETWORKROWADDITION* newRow){ +#ifndef NDEBUG + for (int i = 0; i < newRow->memMemberInformation; ++i) { + assert(reducedMemberIsInvalid(newRow->memberInformation[i].reducedMember)); + } +#endif + + newRow->numReducedComponents = 0; + newRow->numReducedMembers = 0; + if(newRow->numDecompositionColumnArcs == 0){ //Early return in case the reduced decomposition will be empty + return SCIP_OKAY; + } + assert(newRow->numReducedMembers == 0); + assert(newRow->numReducedComponents == 0); + + int newSize = largestMemberID(dec); //Is this sufficient? + if(newSize > newRow->memReducedMembers){ + int updatedSize = max(2*newRow->memReducedMembers,newSize); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->reducedMembers, newRow->memReducedMembers, updatedSize)); + newRow->memReducedMembers = updatedSize; + } + if(newSize > newRow->memMemberInformation){ + int updatedSize = max(2*newRow->memMemberInformation,newSize); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->memberInformation,newRow->memMemberInformation, updatedSize)); + for (int i = newRow->memMemberInformation; i < updatedSize; ++i) { + newRow->memberInformation[i].reducedMember = INVALID_REDUCED_MEMBER; + newRow->memberInformation[i].rootDepthMinimizer = INVALID_REDUCED_MEMBER; + } + newRow->memMemberInformation = updatedSize; + + } + + int numComponents = numConnectedComponents(dec); + if(numComponents > newRow->memReducedComponents){ + int updatedSize = max(2*newRow->memReducedComponents,numComponents); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->reducedComponents,newRow->memReducedComponents,updatedSize)); + newRow->memReducedComponents = updatedSize; + } + + int numMembers = getNumMembers(dec); + if(newRow->memCreateReducedMembersCallstack < numMembers){ + int updatedSize = max(2*newRow->memCreateReducedMembersCallstack,numMembers); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->createReducedMembersCallstack, newRow->memCreateReducedMembersCallstack,updatedSize)); + newRow->memCreateReducedMembersCallstack = updatedSize; + } + + //Create the reduced members (recursively) + for (int i = 0; i < newRow->numDecompositionColumnArcs; ++i) { + assert(i < newRow->memDecompositionColumnArcs); + spqr_arc arc = newRow->decompositionColumnArcs[i]; + spqr_member arcMember = findArcMember(dec, arc); + reduced_member_id reducedMember = createRowReducedMembersToRoot(dec,newRow,arcMember); + reduced_member_id* depthMinimizer = &newRow->memberInformation[newRow->reducedMembers[reducedMember].rootMember].rootDepthMinimizer; + if(reducedMemberIsInvalid(*depthMinimizer)){ + *depthMinimizer = reducedMember; + } + } + + //Set the reduced roots according to the root depth minimizers + for (int i = 0; i < newRow->numReducedComponents; ++i) { + SPQRRowReducedComponent * component = &newRow->reducedComponents[i]; + spqr_member rootMember = newRow->reducedMembers[component->root].member; + reduced_member_id reducedMinimizer = newRow->memberInformation[rootMember].rootDepthMinimizer; + component->rootDepth = newRow->reducedMembers[reducedMinimizer].depth; + component->root = reducedMinimizer; + + //This simplifies code further down which does not need to be component-aware; just pretend that the reduced member is the new root. + newRow->reducedMembers[component->root].parent = INVALID_REDUCED_MEMBER; + assert(memberIsRepresentative(dec,rootMember)); + } + + //update the children array + int numTotalChildren = 0; + for (int i = 0; i < newRow->numReducedMembers; ++i) { + SPQRRowReducedMember * reducedMember = &newRow->reducedMembers[i]; + reduced_member_id minimizer = newRow->memberInformation[reducedMember->rootMember].rootDepthMinimizer; + if(reducedMember->depth >= newRow->reducedMembers[minimizer].depth){ + reducedMember->firstChild = numTotalChildren; + numTotalChildren += reducedMember->numChildren; + reducedMember->numChildren = 0; + } + } + + if(newRow->memChildrenStorage < numTotalChildren){ + int newMemSize = max(newRow->memChildrenStorage*2, numTotalChildren); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->childrenStorage,(size_t) newRow->memChildrenStorage,newMemSize)); + newRow->memChildrenStorage = newMemSize; + } + newRow->numChildrenStorage = numTotalChildren; + + //Fill up the children array` + for(reduced_member_id reducedMember = 0; reducedMember < newRow->numReducedMembers; ++reducedMember){ + SPQRRowReducedMember * reducedMemberData = &newRow->reducedMembers[reducedMember]; + if(reducedMemberData->depth <= newRow->reducedMembers[newRow->memberInformation[reducedMemberData->rootMember].rootDepthMinimizer].depth){ + continue; + } + spqr_member parentMember = findMemberParent(dec, reducedMemberData->member); + reduced_member_id parentReducedMember = SPQRmemberIsValid(parentMember) ? newRow->memberInformation[parentMember].reducedMember : INVALID_REDUCED_MEMBER; + if(reducedMemberIsValid(parentReducedMember)){ + SPQRRowReducedMember * parentReducedMemberData = &newRow->reducedMembers[parentReducedMember]; + newRow->childrenStorage[parentReducedMemberData->firstChild + parentReducedMemberData->numChildren] = reducedMember; + ++parentReducedMemberData->numChildren; + } + } + + //Clean up the root depth minimizers. + for (int i = 0; i < newRow->numReducedMembers; ++i) { + SPQRRowReducedMember * reducedMember = &newRow->reducedMembers[i]; + assert(reducedMember); + spqr_member rootMember = reducedMember->rootMember; + assert(rootMember >= 0); + assert(rootMember < dec->memMembers); + newRow->memberInformation[rootMember].rootDepthMinimizer = INVALID_REDUCED_MEMBER; + } + + return SCIP_OKAY; +} + + +/** + * Marks an arc as 'cut'. This implies that its cycle in the decomposition must be elongated + * @param newRow + * @param arc + * @param reducedMember + */ +static void createCutArc(SCIP_NETWORKDECOMP * dec, + SCIP_NETWORKROWADDITION* newRow, const spqr_arc arc, const reduced_member_id reducedMember, + SCIP_Bool reversed){ + cut_arc_id cut_arc = newRow->numCutArcs; + + CutArcListNode * listNode = &newRow->cutArcs[cut_arc]; + listNode->arc = arc; + + listNode->nextMember = newRow->reducedMembers[reducedMember].firstCutArc; + newRow->reducedMembers[reducedMember].firstCutArc = cut_arc; + + listNode->nextOverall = newRow->firstOverallCutArc; + newRow->firstOverallCutArc = cut_arc; + + newRow->numCutArcs++; + newRow->reducedMembers[reducedMember].numCutArcs++; + assert(newRow->numCutArcs <= newRow->memCutArcs); + + assert(arc < newRow->memIsArcCut); + newRow->isArcCut[arc] = TRUE; + newRow->isArcCutReversed[arc] = reversed; + + assert(memberIsRepresentative(dec,newRow->reducedMembers[reducedMember].member)); + if(getMemberType(dec,newRow->reducedMembers[reducedMember].member) == SPQR_MEMBERTYPE_RIGID){ + + listNode->arcHead = findEffectiveArcHead(dec,arc); + listNode->arcTail = findEffectiveArcTail(dec,arc); + if(reversed){ + swap_ints(&listNode->arcHead,&listNode->arcTail); + } + assert(SPQRnodeIsValid(listNode->arcHead) && SPQRnodeIsValid(listNode->arcTail)); + }else{ + listNode->arcHead = SPQR_INVALID_NODE; + listNode->arcTail = SPQR_INVALID_NODE; + } + + listNode->arcReversed = reversed; +} + +/** + * Creates all cut arcs within the decomposition for the new row. + * Note this preallocates memory for cut arcs which may be created by propagation. + */ +static SCIP_RETCODE createReducedDecompositionCutArcs(SCIP_NETWORKDECOMP* dec, SCIP_NETWORKROWADDITION* newRow){ + //Allocate memory for cut arcs + spqr_arc maxArcID = largestArcID(dec); + if(maxArcID > newRow->memIsArcCut){ + int newSize = max(maxArcID,2*newRow->memIsArcCut); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->isArcCut,newRow->memIsArcCut, newSize)); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->isArcCutReversed,newRow->memIsArcCut,newSize)); + for (int i = newRow->memIsArcCut; i < newSize ; ++i) { + newRow->isArcCut[i] = FALSE; + newRow->isArcCutReversed[i] = FALSE; + } + newRow->memIsArcCut = newSize; + } +#ifndef NDEBUG + for (int i = 0; i < newRow->memIsArcCut; ++i) { + assert(!newRow->isArcCut[i]); + assert(!newRow->isArcCutReversed[i]); + } +#endif + + int numNeededArcs = newRow->numDecompositionColumnArcs*4; + if(numNeededArcs > newRow->memCutArcs){ + int newSize = max(newRow->memCutArcs*2, numNeededArcs); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->cutArcs,newRow->memCutArcs, newSize)); + newRow->memCutArcs = newSize; + } + newRow->numCutArcs = 0; + newRow->firstOverallCutArc = INVALID_CUT_ARC; + for (int i = 0; i < newRow->numDecompositionColumnArcs; ++i) { + spqr_arc arc = newRow->decompositionColumnArcs[i]; + spqr_member member = findArcMember(dec, arc); + reduced_member_id reduced_member = newRow->memberInformation[member].reducedMember; + assert(reducedMemberIsValid(reduced_member)); + createCutArc(dec,newRow,arc,reduced_member,newRow->decompositionColumnArcReversed[i]); + } + + return SCIP_OKAY; +} + +/** + * Determines the members of the reduced decomposition which are leafs. + * This is used in propagation to ensure propagation is only checked for components which have at most one neighbour + * which is not propagated. + */ +static SCIP_RETCODE determineLeafReducedMembers(const SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow) { + if (newRow->numDecompositionColumnArcs > newRow->memLeafMembers) { + int updatedSize = max(newRow->numDecompositionColumnArcs, 2 * newRow->memLeafMembers); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->leafMembers,newRow->memLeafMembers,updatedSize)); + newRow->memLeafMembers = updatedSize; + } + newRow->numLeafMembers = 0; + + for (reduced_member_id reducedMember = 0; reducedMember < newRow->numReducedMembers; ++reducedMember) { + if (newRow->reducedMembers[reducedMember].numChildren == 0) { + newRow->leafMembers[newRow->numLeafMembers] = reducedMember; + ++newRow->numLeafMembers; + } + } + return SCIP_OKAY; +} + +/** + * Preallocates memory arrays necessary for searching rigid components. + */ +static SCIP_RETCODE allocateRigidSearchMemory(const SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow){ + int totalNumNodes = getNumNodes(dec); + int maxNumNodes = 2*dec->numArcs; + if(maxNumNodes > newRow->memNodeColors){ + int newSize = max(2*newRow->memNodeColors,maxNumNodes); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->nodeColors,newRow->memNodeColors, newSize)); + for (int i = newRow->memNodeColors; i < newSize; ++i) { + newRow->nodeColors[i] = UNCOLORED; + } + newRow->memNodeColors = newSize; + } + + if(totalNumNodes > newRow->memArticulationNodes){ + int newSize = max(2*newRow->memArticulationNodes,totalNumNodes); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->articulationNodes,newRow->memArticulationNodes, newSize)); + newRow->memArticulationNodes = newSize; + } + if(totalNumNodes > newRow->memNodeSearchInfo){ + int newSize = max(2*newRow->memNodeSearchInfo,totalNumNodes); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->articulationNodeSearchInfo,newRow->memNodeSearchInfo, newSize)); + newRow->memNodeSearchInfo = newSize; + } + if(totalNumNodes > newRow->memCrossingPathCount){ + int newSize = max(2*newRow->memCrossingPathCount,totalNumNodes); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->crossingPathCount,newRow->memCrossingPathCount, newSize)); + newRow->memCrossingPathCount = newSize; + } + + //TODO: see if tradeoff for performance bound by checking max # of nodes of rigid is worth it to reduce size + //of the following allocations + int largestID = largestNodeID(dec); //TODO: only update the stack sizes of the following when needed? The preallocation might be causing performance problems + if(largestID > newRow->memIntersectionDFSData){ + int newSize = max(2*newRow->memIntersectionDFSData,largestID); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->intersectionDFSData,newRow->memIntersectionDFSData, newSize)); + newRow->memIntersectionDFSData = newSize; + } + if(largestID > newRow->memColorDFSData){ + int newSize = max(2*newRow->memColorDFSData, largestID); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->colorDFSData,newRow->memColorDFSData, newSize)); + newRow->memColorDFSData = newSize; + } + if(largestID > newRow->memArtDFSData){ + int newSize = max(2*newRow->memArtDFSData,largestID); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->artDFSData,newRow->memArtDFSData, newSize)); + newRow->memArtDFSData = newSize; + } + + for (int i = 0; i < newRow->memIntersectionPathDepth; ++i) { + newRow->intersectionPathDepth[i] = -1; + } + + if(largestID > newRow->memIntersectionPathDepth){ + int newSize = max(2*newRow->memIntersectionPathDepth,largestID); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->intersectionPathDepth,newRow->memIntersectionPathDepth, newSize)); + for (int i = newRow->memIntersectionPathDepth; i < newSize; ++i) { + newRow->intersectionPathDepth[i] = -1; + } + newRow->memIntersectionPathDepth = newSize; + } + for (int i = 0; i < newRow->memIntersectionPathParent; ++i) { + newRow->intersectionPathParent[i] = SPQR_INVALID_NODE; + } + if(largestID > newRow->memIntersectionPathParent){ + int newSize = max(2*newRow->memIntersectionPathParent,largestID); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->intersectionPathParent,newRow->memIntersectionPathParent, newSize)); + for (int i = newRow->memIntersectionPathParent; i intersectionPathParent[i] = SPQR_INVALID_NODE; + } + newRow->memIntersectionPathParent = newSize; + } + + return SCIP_OKAY; +} +static void zeroOutColors(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, const spqr_node firstRemoveNode){ + assert(firstRemoveNode < newRow->memNodeColors); + + newRow->nodeColors[firstRemoveNode] = UNCOLORED; + ColorDFSCallData * data = newRow->colorDFSData; + if(newRow->colorDFSData == NULL){ + return; + } + + data[0].node = firstRemoveNode; + data[0].arc = getFirstNodeArc(dec,firstRemoveNode); + if(SPQRarcIsInvalid(data[0].arc)){ + return; + } + + int depth = 0; + + while(depth >= 0){ + assert(depth < newRow->memColorDFSData); + ColorDFSCallData * callData = &data[depth]; + spqr_node head = findArcHead(dec, callData->arc); + spqr_node tail = findArcTail(dec, callData->arc); + spqr_node otherNode = callData->node == head ? tail : head; + assert(otherNode < newRow->memNodeColors); + if(newRow->nodeColors[otherNode] != UNCOLORED){ + callData->arc = getNextNodeArc(dec,callData->arc,callData->node); + + newRow->nodeColors[otherNode] = UNCOLORED; + ++depth; + data[depth].node = otherNode; + data[depth].arc = getFirstNodeArc(dec,otherNode); + continue; + } + + callData->arc = getNextNodeArc(dec,callData->arc,callData->node); + while(depth >= 0 && data[depth].arc == getFirstNodeArc(dec,data[depth].node)){ + --depth; + } + } + + +} +static void cleanUpPreviousIteration(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKROWADDITION * newRow){ + //zero out coloring information from previous check + for (int i = 0; i < newRow->numReducedMembers; ++i) { + if (SPQRnodeIsValid(newRow->reducedMembers[i].coloredNode)) { + zeroOutColors(dec, newRow, newRow->reducedMembers[i].coloredNode); + } + } + +#ifndef NDEBUG + for (int i = 0; i < newRow->memNodeColors; ++i) { + assert(newRow->nodeColors[i] == UNCOLORED); + } +#endif + + //For cut arcs: clear them from the array from previous iteration + cut_arc_id cutArcIdx = newRow->firstOverallCutArc; + while(cutArcIsValid(cutArcIdx)){ + spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; + cutArcIdx = newRow->cutArcs[cutArcIdx].nextOverall; + newRow->isArcCut[cutArc] = FALSE; + newRow->isArcCutReversed[cutArc] = FALSE; + } +#ifndef NDEBUG + for (int i = 0; i < newRow->memIsArcCut; ++i) { + assert(!newRow->isArcCut[i]); + assert(!newRow->isArcCutReversed[i]); + } +#endif +} + +static void rigidFindStarNodes(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKROWADDITION * newRow, + const reduced_member_id toCheck){ + //4 cases: + //Only a single edge; both head/tail are okay => network + //All are adjacent to a single node, but do not have it as head or tail => not network + //All are adjacent to a single node, and have it as head or tail => network + //Not all are adjacent to a single node => check articulation nodes + assert(newRow->reducedMembers[toCheck].numCutArcs > 0);//calling this function otherwise is nonsensical + + cut_arc_id cutArcIdx = newRow->reducedMembers[toCheck].firstCutArc; + spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; + spqr_node head = findArcHead(dec, cutArc); + spqr_node tail = findArcTail(dec, cutArc); + + SCIP_Bool reverse = findArcSign(dec,cutArc).reversed != newRow->cutArcs[cutArcIdx].arcReversed; + spqr_node cutArcsHead = reverse ? tail : head; + spqr_node cutArcsTail = reverse ? head : tail; + + if(newRow->reducedMembers[toCheck].numCutArcs == 1){ + newRow->reducedMembers[toCheck].articulationArc = cutArc; + newRow->reducedMembers[toCheck].splitNode = cutArcsHead; + newRow->reducedMembers[toCheck].otherNode = cutArcsTail; + newRow->reducedMembers[toCheck].otherIsSource = TRUE; + newRow->reducedMembers[toCheck].allHaveCommonNode = TRUE; + return;// Only a single cut arc + } + + spqr_node intersectionNodes[2] = {head,tail}; + + while (cutArcIsValid(newRow->cutArcs[cutArcIdx].nextMember)) { + cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; + cutArc = newRow->cutArcs[cutArcIdx].arc; + head = findArcHead(dec, cutArc); + tail = findArcTail(dec, cutArc); + reverse = findArcSign(dec,cutArc).reversed != newRow->cutArcs[cutArcIdx].arcReversed; + spqr_node effectiveHead = reverse ? tail : head; + spqr_node effectiveTail = reverse ? head : tail; + if(effectiveHead != cutArcsHead){ + cutArcsHead = SPQR_INVALID_NODE; + } + if(effectiveTail != cutArcsTail){ + cutArcsTail = SPQR_INVALID_NODE; + } + + //intersection between intersectionNodes and head and tail + for (int i = 0; i < 2; ++i) { + if(intersectionNodes[i] != head && intersectionNodes[i] != tail){ + intersectionNodes[i] = SPQR_INVALID_NODE; + } + } + if(SPQRnodeIsInvalid(intersectionNodes[0]) && SPQRnodeIsInvalid(intersectionNodes[1])){ + newRow->reducedMembers[toCheck].splitNode = SPQR_INVALID_NODE; + newRow->reducedMembers[toCheck].allHaveCommonNode = FALSE; + return; //not all arcs are adjacent to a single node, need to check articulation nodes + } + } + if(SPQRnodeIsInvalid(cutArcsHead) && SPQRnodeIsInvalid(cutArcsTail)){ + //All arcs adjacent to a single node, but not in same direction; not network + newRow->remainsNetwork = FALSE; + newRow->reducedMembers[toCheck].type = TYPE_NOT_NETWORK; + return; + } + SCIP_Bool headSplittable = SPQRnodeIsValid(cutArcsHead); + //Check if the n arcs are in a n+1 degree node; if so, the other endpoint of this non split arc is also splittable + //By virtue of the spanning tree, this arc must be a tree arc. + spqr_node splitNode = headSplittable ? cutArcsHead : cutArcsTail; + newRow->reducedMembers[toCheck].splitNode = splitNode; + newRow->reducedMembers[toCheck].otherIsSource = headSplittable; + newRow->reducedMembers[toCheck].allHaveCommonNode = TRUE; + if(newRow->reducedMembers[toCheck].numCutArcs == nodeDegree(dec,splitNode) - 1){ + spqr_arc firstNodeArc = getFirstNodeArc(dec,splitNode); + spqr_arc neighbourArc = firstNodeArc; + do{ + if(arcIsTree(dec,neighbourArc)){ + break; + } + neighbourArc = getNextNodeArc(dec,neighbourArc,splitNode); + }while(neighbourArc != firstNodeArc); + + newRow->reducedMembers[toCheck].articulationArc = neighbourArc; + spqr_arc arcHead = findArcHead(dec,neighbourArc); + newRow->reducedMembers[toCheck].otherNode = arcHead == splitNode ? findArcTail(dec,neighbourArc) : arcHead; + + } +} + +//TODO: remove SCIP_RETCODE from below functions (until propagation function, basically) and refactor memory allocation +static SCIP_RETCODE zeroOutColorsExceptNeighbourhood(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + const spqr_node articulationNode, const spqr_node startRemoveNode){ + COLOR_STATUS * neighbourColors; + int degree = nodeDegree(dec,articulationNode); + SCIP_CALL(SCIPallocBlockMemoryArray(dec->env,&neighbourColors,(size_t) degree)); + + { + int i = 0; + spqr_arc artFirstArc = getFirstNodeArc(dec, articulationNode); + spqr_arc artItArc = artFirstArc; + do{ + spqr_node head = findArcHead(dec, artItArc); + spqr_node tail = findArcTail(dec, artItArc); + spqr_node otherNode = articulationNode == head ? tail : head; + neighbourColors[i] = newRow->nodeColors[otherNode]; + i++; + assert(i <= degree); + artItArc = getNextNodeArc(dec,artItArc,articulationNode); + }while(artItArc != artFirstArc); + } + zeroOutColors(dec,newRow,startRemoveNode); + + { + int i = 0; + spqr_arc artFirstArc = getFirstNodeArc(dec, articulationNode); + spqr_arc artItArc = artFirstArc; + do{ + spqr_node head = findArcHead(dec, artItArc); + spqr_node tail = findArcTail(dec, artItArc); + spqr_node otherNode = articulationNode == head ? tail : head; + newRow->nodeColors[otherNode] = neighbourColors[i]; + i++; + assert(i <= degree); + artItArc = getNextNodeArc(dec,artItArc,articulationNode); + }while(artItArc != artFirstArc); + } + + SCIPfreeBlockMemoryArray(dec->env,&neighbourColors,degree); + return SCIP_OKAY; +} + +//Theoretically, there is a faster algorithm, but it is quite complicated to implement. +//Thus, we stick with the 'simple' version below, which is easily fast enough in practice. +static void intersectionOfAllPaths(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKROWADDITION *newRow, + const reduced_member_id toCheck, int * const nodeNumPaths){ + int * intersectionPathDepth = newRow->intersectionPathDepth; + spqr_node * intersectionPathParent = newRow->intersectionPathParent; + + //First do a dfs over the tree, storing all the tree-parents and depths for each node + //TODO: maybe cache this tree and also update it so we can prevent this DFS call? + + //pick an arbitrary node as root; we just use the first cutArc here + { + spqr_node root = findArcHead(dec, newRow->cutArcs[newRow->reducedMembers[toCheck].firstCutArc].arc); + DFSCallData *pathSearchCallStack = newRow->intersectionDFSData; + + assert(intersectionPathDepth[root] == -1); + assert(intersectionPathParent[root] == SPQR_INVALID_NODE); + + int pathSearchCallStackSize = 0; + + intersectionPathDepth[root] = 0; + intersectionPathParent[root] = SPQR_INVALID_NODE; + + pathSearchCallStack[0].node = root; + pathSearchCallStack[0].nodeArc = getFirstNodeArc(dec, root); + pathSearchCallStackSize++; + while (pathSearchCallStackSize > 0) { + assert(pathSearchCallStackSize <= newRow->memIntersectionDFSData); + DFSCallData *dfsData = &pathSearchCallStack[pathSearchCallStackSize - 1]; + //cannot be a tree arc which is its parent + if (arcIsTree(dec, dfsData->nodeArc) && + (pathSearchCallStackSize <= 1 || + dfsData->nodeArc != pathSearchCallStack[pathSearchCallStackSize - 2].nodeArc)) { + spqr_node head = findArcHeadNoCompression(dec, dfsData->nodeArc); + spqr_node tail = findArcTailNoCompression(dec, dfsData->nodeArc); + spqr_node other = head == dfsData->node ? tail : head; + assert(other != dfsData->node); + + //We go up a level: add new node to the call stack + pathSearchCallStack[pathSearchCallStackSize].node = other; + pathSearchCallStack[pathSearchCallStackSize].nodeArc = getFirstNodeArc(dec, other); + //Every time a new node is discovered/added, we update its parent and depth information + assert(intersectionPathDepth[other] == -1); + assert(intersectionPathParent[other] == SPQR_INVALID_NODE); + intersectionPathParent[other] = dfsData->node; + intersectionPathDepth[other] = pathSearchCallStackSize; + ++pathSearchCallStackSize; + continue; + } + do { + dfsData->nodeArc = getNextNodeArc(dec, dfsData->nodeArc, dfsData->node); + if (dfsData->nodeArc == getFirstNodeArc(dec, dfsData->node)) { + --pathSearchCallStackSize; + dfsData = &pathSearchCallStack[pathSearchCallStackSize - 1]; + } else { + break; + } + } while (pathSearchCallStackSize > 0); + } + } + + //For each cut arc, trace back both ends until they meet + cut_arc_id cutArc = newRow->reducedMembers[toCheck].firstCutArc; + do{ + spqr_arc arc = newRow->cutArcs[cutArc].arc; + cutArc = newRow->cutArcs[cutArc].nextMember; + + //Iteratively jump up to the parents until they reach a common parent + spqr_node source = findArcHead(dec, arc); + spqr_node target = findArcTail(dec, arc); + int sourceDepth = intersectionPathDepth[source]; + int targetDepth = intersectionPathDepth[target]; + nodeNumPaths[source]++; + nodeNumPaths[target]++; + + while (sourceDepth > targetDepth){ + assert(source != target); + source = intersectionPathParent[source]; + nodeNumPaths[source]++; + --sourceDepth; + } + while(targetDepth > sourceDepth){ + assert(source != target); + target = intersectionPathParent[target]; + nodeNumPaths[target]++; + --targetDepth; + } + while(source != target && targetDepth >= 0){ + source = intersectionPathParent[source]; + target = intersectionPathParent[target]; + nodeNumPaths[source]++; + nodeNumPaths[target]++; + --targetDepth; + } + //In all the above, the lowest common ancestor is increased twice, so we correct for it ad-hoc + nodeNumPaths[source]--; + assert(SPQRnodeIsValid(source) && SPQRnodeIsValid(target)); + assert(source == target); + + }while (cutArcIsValid(cutArc)); + +} + +static void addArticulationNode(SCIP_NETWORKROWADDITION *newRow, spqr_node articulationNode){ +#ifndef NDEBUG + for (int i = 0; i < newRow->numArticulationNodes; ++i) { + assert(newRow->articulationNodes[i] != articulationNode); + } +#endif + newRow->articulationNodes[newRow->numArticulationNodes] = articulationNode; + ++newRow->numArticulationNodes; +} +static void articulationPoints(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION * newRow, ArticulationNodeInformation *nodeInfo, reduced_member_id reducedMember){ + const SCIP_Bool * arcRemoved = newRow->isArcCut; + + int rootChildren = 0; + spqr_node root_node = findArcHead(dec, getFirstMemberArc(dec, newRow->reducedMembers[reducedMember].member));; + + ArticulationPointCallStack * callStack = newRow->artDFSData; + + int depth = 0; + int time = 1; + + callStack[depth].arc = getFirstNodeArc(dec,root_node); + callStack[depth].node = root_node; + callStack[depth].parent = SPQR_INVALID_NODE; + callStack[depth].isAP = FALSE; + + nodeInfo[root_node].low = time; + nodeInfo[root_node].discoveryTime = time; + + while(depth >= 0){ + if(!arcRemoved[callStack[depth].arc]){ + spqr_node node = callStack[depth].node; + spqr_node head = findArcHead(dec, callStack[depth].arc); + spqr_node tail = findArcTail(dec, callStack[depth].arc); + spqr_node otherNode = node == head ? tail : head; + if(otherNode != callStack[depth].parent){ + if(nodeInfo[otherNode].discoveryTime == 0){ + if(depth == 0){ + rootChildren++; + } + //recursive call + ++depth; + assert(depth < newRow->memArtDFSData); + callStack[depth].parent = node; + callStack[depth].node = otherNode; + callStack[depth].arc = getFirstNodeArc(dec,otherNode); + callStack[depth].isAP = FALSE; + + ++time; + nodeInfo[otherNode].low = time; + nodeInfo[otherNode].discoveryTime = time; + continue; + + }else{ + nodeInfo[node].low = min(nodeInfo[node].low, nodeInfo[otherNode].discoveryTime); + } + } + } + + while(TRUE){ + callStack[depth].arc = getNextNodeArc(dec,callStack[depth].arc,callStack[depth].node); + if(callStack[depth].arc != getFirstNodeArc(dec,callStack[depth].node)) break; + --depth; + if (depth < 0) break; + + spqr_node current_node = callStack[depth].node; + spqr_node other_node = callStack[depth + 1].node; + nodeInfo[current_node].low = min(nodeInfo[current_node].low, + nodeInfo[other_node].low); + if (depth != 0 && + !callStack[depth].isAP && + nodeInfo[current_node].discoveryTime <= nodeInfo[other_node].low) { + addArticulationNode(newRow, current_node); + callStack[depth].isAP = TRUE; + } + } + + } + if(rootChildren > 1 ){ + addArticulationNode(newRow,root_node); + } +} + +static void rigidConnectedColoringRecursive(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION * newRow, spqr_node articulationNode, + spqr_node firstProcessNode, COLOR_STATUS firstColor, + SCIP_Bool *isGood){ + const SCIP_Bool * isArcCut = newRow->isArcCut; + COLOR_STATUS * nodeColors = newRow->nodeColors; + ColorDFSCallData * data = newRow->colorDFSData; + + data[0].node = firstProcessNode; + data[0].arc = getFirstNodeArc(dec,firstProcessNode); + newRow->nodeColors[firstProcessNode] = firstColor; + + int depth = 0; + while(depth >= 0){ + assert(depth < newRow->memColorDFSData); + assert(newRow->nodeColors[articulationNode] == UNCOLORED); + + ColorDFSCallData * callData = &data[depth]; + spqr_node head = findArcHead(dec, callData->arc); + spqr_node tail = findArcTail(dec, callData->arc); + spqr_node otherNode = callData->node == head ? tail : head; + COLOR_STATUS currentColor = nodeColors[callData->node]; + COLOR_STATUS otherColor = nodeColors[otherNode]; + //Checks the direction of the arc; in the rest of the algorithm, we just need to check partition + if(isArcCut[callData->arc] && currentColor != otherColor){ + SCIP_Bool otherIsTail = callData->node == head; + SCIP_Bool arcReversed = findArcSign(dec,callData->arc).reversed != newRow->isArcCutReversed[callData->arc]; + SCIP_Bool good = (currentColor == COLOR_SOURCE) == (otherIsTail == arcReversed); + if(!good){ + *isGood = FALSE; + break; + } + } + if(otherNode != articulationNode){ + if(otherColor == UNCOLORED){ + if(isArcCut[callData->arc]){ + nodeColors[otherNode] = currentColor == COLOR_SOURCE ? COLOR_SINK : COLOR_SOURCE; //reverse the colors + }else{ + nodeColors[otherNode] = currentColor; + } + callData->arc = getNextNodeArc(dec,callData->arc,callData->node); + + depth++; + assert(depth < newRow->memColorDFSData); + data[depth].node = otherNode; + data[depth].arc = getFirstNodeArc(dec,otherNode); + continue; + } + if(isArcCut[callData->arc] != (currentColor != otherColor)){ + *isGood = FALSE; + break; + } + } + callData->arc = getNextNodeArc(dec,callData->arc,callData->node); + while(depth >= 0 && data[depth].arc == getFirstNodeArc(dec,data[depth].node)){ + --depth; + } + } +} + +static void rigidConnectedColoring(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + const reduced_member_id reducedMember, const spqr_node node, SCIP_Bool * const isGood){ + + //we should only perform this function if there's more than one cut arc + assert(newRow->reducedMembers[reducedMember].numCutArcs > 1); +#ifndef NDEBUG + { + + spqr_member member = newRow->reducedMembers[reducedMember].member; + spqr_arc firstArc = getFirstMemberArc(dec, member); + spqr_arc memberArc = firstArc; + do{ + assert(newRow->nodeColors[findArcHeadNoCompression(dec,memberArc)] == UNCOLORED); + assert(newRow->nodeColors[findArcTailNoCompression(dec,memberArc)] == UNCOLORED); + memberArc = getNextMemberArc(dec,memberArc); + }while(firstArc != memberArc); + } +#endif + + spqr_node firstProcessNode; + COLOR_STATUS firstColor; + { + cut_arc_id cutArc = newRow->reducedMembers[reducedMember].firstCutArc; + spqr_arc arc = newRow->cutArcs[cutArc].arc; + assert(SPQRarcIsValid(arc)); + spqr_node head = findArcHead(dec, arc); + spqr_node tail = findArcTail(dec, arc); + if(findArcSign(dec,arc).reversed != newRow->cutArcs[cutArc].arcReversed){ + spqr_node temp = head; + head = tail; + tail = temp; + } + if(tail != node){ + firstProcessNode = tail; + firstColor = COLOR_SOURCE; + }else{ + assert(head != node); + firstProcessNode = head; + firstColor = COLOR_SINK; + } + } + assert(SPQRnodeIsValid(firstProcessNode) && firstProcessNode != node); + *isGood = TRUE; + newRow->reducedMembers[reducedMember].coloredNode = firstProcessNode; + rigidConnectedColoringRecursive(dec,newRow,node,firstProcessNode,firstColor,isGood); + + // Need to zero all colors for next attempts if we failed + if(!(*isGood)){ + zeroOutColors(dec,newRow,firstProcessNode); + newRow->reducedMembers[reducedMember].coloredNode = SPQR_INVALID_NODE; + }else{ + //Otherwise, we zero out all colors but the ones which we need + zeroOutColorsExceptNeighbourhood(dec,newRow,node, firstProcessNode); + newRow->reducedMembers[reducedMember].coloredNode = node; + } +} + +static spqr_node checkNeighbourColoringArticulationNode(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKROWADDITION *newRow, + const spqr_node articulationNode, spqr_arc * const adjacentSplittingArc){ + spqr_node firstSideCandidate = SPQR_INVALID_NODE; + spqr_node secondSideCandidate = SPQR_INVALID_NODE; + spqr_arc firstSideArc = SPQR_INVALID_ARC; + spqr_arc secondSideArc = SPQR_INVALID_ARC; + int numFirstSide = 0; + int numSecondSide = 0; + + spqr_arc firstArc = getFirstNodeArc(dec, articulationNode); + spqr_arc moveArc = firstArc; + do{ + spqr_node head = findArcHead(dec, moveArc); + spqr_node tail = findArcTail(dec, moveArc); + spqr_node otherNode = articulationNode == head ? tail : head; + assert(newRow->nodeColors[otherNode] != UNCOLORED); + if((newRow->nodeColors[otherNode] == COLOR_SOURCE) != newRow->isArcCut[moveArc] ){ + if(numFirstSide == 0 && arcIsTree(dec,moveArc)){ + firstSideCandidate = otherNode; + firstSideArc = moveArc; + }else if (numFirstSide > 0){ + firstSideCandidate = SPQR_INVALID_NODE; + } + ++numFirstSide; + }else{ + if(numSecondSide == 0 && arcIsTree(dec,moveArc)){ + secondSideCandidate = otherNode; + secondSideArc = moveArc; + }else if (numSecondSide > 0){ + secondSideCandidate = SPQR_INVALID_NODE; + } + ++numSecondSide; + } + moveArc = getNextNodeArc(dec,moveArc,articulationNode); + }while(moveArc != firstArc); + + if(numFirstSide == 1){ + *adjacentSplittingArc = firstSideArc; + return firstSideCandidate; + }else if (numSecondSide == 1){ + *adjacentSplittingArc = secondSideArc; + return secondSideCandidate; + } + return SPQR_INVALID_NODE; +} + + +static void rigidGetSplittableArticulationPointsOnPath(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + const reduced_member_id toCheck, + spqr_node firstNode, spqr_node secondNode){ + assert(newRow->reducedMembers[toCheck].numCutArcs > 1); + + int totalNumNodes = getNumNodes(dec); + int * nodeNumPaths = newRow->crossingPathCount; + + for (int i = 0; i < totalNumNodes; ++i) { + nodeNumPaths[i] = 0; + } + + intersectionOfAllPaths(dec,newRow,toCheck,nodeNumPaths); + + newRow->numArticulationNodes = 0; + + ArticulationNodeInformation * artNodeInfo = newRow->articulationNodeSearchInfo; + //TODO: ugly; we clean up over all decomposition nodes for every component + //clean up can not easily be done in the search, unfortunately + for (int i = 0; i < totalNumNodes; ++i) { + artNodeInfo[i].low = 0 ; + artNodeInfo[i].discoveryTime = 0; + } + + articulationPoints(dec,newRow,artNodeInfo,toCheck); + + int numCutArcs = newRow->reducedMembers[toCheck].numCutArcs; + for (int i = 0; i < newRow->numArticulationNodes; i++) { + spqr_node articulationNode = newRow->articulationNodes[i]; + assert(nodeIsRepresentative(dec, articulationNode)); + SCIP_Bool isOnPath = nodeNumPaths[articulationNode] == numCutArcs; + if (isOnPath && ( + (SPQRnodeIsInvalid(firstNode) &&SPQRnodeIsInvalid(secondNode)) + || articulationNode == firstNode || articulationNode == secondNode )){ + SCIP_Bool isGood = TRUE; + rigidConnectedColoring(dec, newRow, toCheck, articulationNode, &isGood); + if(!isGood){ + continue; + } + newRow->reducedMembers[toCheck].splitNode = articulationNode; + + //Once we have found one node, we can (possibly) find another by looking at the coloring of the neighbourhood of it. + + spqr_arc adjacentSplittingArc = SPQR_INVALID_ARC; + spqr_node adjacentSplittingNode = checkNeighbourColoringArticulationNode(dec, newRow, articulationNode, + &adjacentSplittingArc); + + //If the neighbour-coloring works, we still need to check that the adjacent node + //is also an articulation node + if(SPQRnodeIsValid(adjacentSplittingNode) && ((SPQRnodeIsInvalid(firstNode) && SPQRnodeIsInvalid(secondNode)) + || adjacentSplittingNode == firstNode || adjacentSplittingNode == secondNode)){ + SCIP_Bool isArticulationNode = FALSE; + for (int j = 0; j < newRow->numArticulationNodes; ++j) { + if (newRow->articulationNodes[j] == adjacentSplittingNode) { + isArticulationNode = TRUE; + break; + } + } + if (isArticulationNode) { + newRow->reducedMembers[toCheck].articulationArc = adjacentSplittingArc; + newRow->reducedMembers[toCheck].otherNode = adjacentSplittingNode; + newRow->reducedMembers[toCheck].otherIsSource = newRow->nodeColors[adjacentSplittingNode] == COLOR_SOURCE; + + //Cleaning up the colors + { + spqr_arc firstNodeArc = getFirstNodeArc(dec, articulationNode); + spqr_arc itArc = firstNodeArc; + do { + spqr_node head = findArcHead(dec, itArc); + spqr_node tail = findArcTail(dec, itArc); + spqr_node otherNode = articulationNode == head ? tail : head; + newRow->nodeColors[otherNode] = UNCOLORED; + itArc = getNextNodeArc(dec, itArc, articulationNode); + } while (itArc != firstNodeArc); + + } + } + } + return; + } + } + + newRow->reducedMembers[toCheck].type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; +} + +static void determineParallelType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + const reduced_member_id toCheckMember, const spqr_arc markerToOther, + const reduced_member_id otherMember, const spqr_arc markerToCheck){ + + SPQRRowReducedMember * member = &newRow->reducedMembers[toCheckMember]; + assert(cutArcIsValid(member->firstCutArc)); + + SCIP_Bool good = TRUE; + SCIP_Bool isReversed = TRUE; + int countedCutArcs = 0; + for (cut_arc_id cutArc = member->firstCutArc; cutArcIsValid(cutArc); + cutArc = newRow->cutArcs[cutArc].nextMember){ + spqr_arc arc = newRow->cutArcs[cutArc].arc; + SCIP_Bool arcIsReversed = arcIsReversedNonRigid(dec,arc) != newRow->cutArcs[cutArc].arcReversed; + if(countedCutArcs == 0){ + isReversed = arcIsReversed; + }else if(arcIsReversed != isReversed){ + good = FALSE; + break; + } + ++countedCutArcs; + } + if(!good){ + member->type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + return; + } + //we can only propagate if the marker arc is a tree arc and all other arcs are cut + if(!arcIsTree(dec,markerToOther) || + countedCutArcs != (getNumMemberArcs(dec,newRow->reducedMembers[toCheckMember].member) - 1)){ + //In all other cases, the bond can be split so that the result will be okay! + newRow->reducedMembers[toCheckMember].type = TYPE_MERGED; + }else{ + SCIP_Bool markerIsReversed = arcIsReversedNonRigid(dec,markerToOther); + createCutArc(dec,newRow,markerToCheck,otherMember, markerIsReversed != isReversed); + newRow->reducedMembers[toCheckMember].type = TYPE_PROPAGATED; + } + +} + +static void determineSeriesType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + const reduced_member_id toCheckMember, const spqr_arc markerToOther, + const reduced_member_id otherMember, const spqr_arc markerToCheck){ + //Propagation only calls this function if the arc is tree already, so we do not check it here. + assert(arcIsTree(dec,markerToOther)); + assert(newRow->reducedMembers[toCheckMember].numCutArcs == 1); + assert(cutArcIsValid(newRow->reducedMembers[toCheckMember].firstCutArc)); + spqr_arc cutArc = newRow->reducedMembers[toCheckMember].firstCutArc; + spqr_arc arc = newRow->cutArcs[cutArc].arc; + newRow->reducedMembers[toCheckMember].type = TYPE_PROPAGATED; + createCutArc(dec,newRow,markerToCheck,otherMember, + (arcIsReversedNonRigid(dec,arc) == arcIsReversedNonRigid(dec,markerToOther)) != newRow->cutArcs[cutArc].arcReversed); +} + +static void determineRigidType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + const reduced_member_id toCheckMember, const spqr_arc markerToOther, + const reduced_member_id otherMember, const spqr_arc markerToCheck){ + assert(newRow->reducedMembers[toCheckMember].numCutArcs > 0);//Checking for propagation only makes sense if there is at least one cut arc + + rigidFindStarNodes(dec,newRow,toCheckMember); + if(!newRow->remainsNetwork){ + return; + } + spqr_node markerHead = findArcHead(dec,markerToOther); + spqr_node markerTail = findArcTail(dec,markerToOther); + if(findArcSign(dec,markerToOther).reversed){ + spqr_node temp = markerHead; + markerHead = markerTail; + markerTail = temp; + } + if(SPQRnodeIsInvalid(newRow->reducedMembers[toCheckMember].splitNode)){ + //not a star => attempt to find splittable nodes using articulation node algorithms + //We save some work by telling the methods that only the marker nodes should be checked + rigidGetSplittableArticulationPointsOnPath(dec,newRow,toCheckMember,markerHead,markerTail); + } + if(!newRow->remainsNetwork){ + return; + } + + + if(SPQRarcIsValid(newRow->reducedMembers[toCheckMember].articulationArc) && + newRow->reducedMembers[toCheckMember].articulationArc == markerToOther){ + newRow->reducedMembers[toCheckMember].type = TYPE_PROPAGATED; + SCIP_Bool reverse =(markerHead == newRow->reducedMembers[toCheckMember].splitNode) != + newRow->reducedMembers[toCheckMember].otherIsSource; + + createCutArc(dec,newRow,markerToCheck,otherMember,reverse); + }else if(newRow->reducedMembers[toCheckMember].splitNode == markerHead || + newRow->reducedMembers[toCheckMember].splitNode == markerTail){ + newRow->reducedMembers[toCheckMember].type = TYPE_MERGED; + }else if(SPQRarcIsValid(newRow->reducedMembers[toCheckMember].articulationArc) && + (newRow->reducedMembers[toCheckMember].otherNode == markerHead || + newRow->reducedMembers[toCheckMember].otherNode == markerTail) + ){ + newRow->reducedMembers[toCheckMember].type = TYPE_MERGED; + }else{ + //Found source or sinks, but not adjacent to the marker + newRow->reducedMembers[toCheckMember].type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + } + +} +static RowReducedMemberType determineType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + const reduced_member_id toCheckMember, const spqr_arc markerToOther, + const reduced_member_id otherMember, const spqr_arc markerToCheck){ + assert(newRow->reducedMembers[toCheckMember].type == TYPE_UNDETERMINED); + switch (getMemberType(dec,newRow->reducedMembers[toCheckMember].member)) { + case SPQR_MEMBERTYPE_RIGID: + { + determineRigidType(dec,newRow,toCheckMember,markerToOther,otherMember,markerToCheck); + break; + + } + case SPQR_MEMBERTYPE_PARALLEL: + { + determineParallelType(dec,newRow,toCheckMember,markerToOther,otherMember,markerToCheck); + break; + } + case SPQR_MEMBERTYPE_SERIES: + { + determineSeriesType(dec,newRow,toCheckMember,markerToOther,otherMember,markerToCheck); + break; + } + default: + assert(FALSE); + newRow->reducedMembers[toCheckMember].type = TYPE_NOT_NETWORK; + } + + return newRow->reducedMembers[toCheckMember].type; +} + +static void propagateComponents(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow){ + int leafArrayIndex = 0; + + reduced_member_id leaf; + reduced_member_id next; + + while(leafArrayIndex != newRow->numLeafMembers){ + leaf = newRow->leafMembers[leafArrayIndex]; + //next is invalid if the member is not in the reduced decomposition. + next = newRow->reducedMembers[leaf].parent; + spqr_arc parentMarker = markerToParent(dec, newRow->reducedMembers[leaf].member); + if(next != INVALID_REDUCED_MEMBER && arcIsTree(dec,parentMarker)){ + assert(reducedMemberIsValid(next)); + assert(SPQRarcIsValid(parentMarker)); + RowReducedMemberType type = determineType(dec,newRow,leaf,parentMarker,next,markerOfParent(dec,newRow->reducedMembers[leaf].member)); + if(type == TYPE_PROPAGATED){ + ++newRow->reducedMembers[next].numPropagatedChildren; + if(newRow->reducedMembers[next].numPropagatedChildren == newRow->reducedMembers[next].numChildren){ + newRow->leafMembers[leafArrayIndex] = next; + }else{ + ++leafArrayIndex; + } + }else if(type == TYPE_NOT_NETWORK){ + return; + }else{ + assert(type == TYPE_MERGED); + ++leafArrayIndex; + } + }else{ + ++leafArrayIndex; + } + + } + + for (int j = 0; j < newRow->numReducedComponents; ++j) { + //The reduced root might be a leaf as well: we propagate it last + reduced_member_id root = newRow->reducedComponents[j].root; + + while(TRUE){ + if(newRow->reducedMembers[root].numPropagatedChildren == newRow->reducedMembers[root].numChildren -1){ + //TODO: bit ugly, have to do a linear search for the child + reduced_member_id child = INVALID_REDUCED_MEMBER; + spqr_arc markerToChild = SPQR_INVALID_ARC; + for (children_idx i = newRow->reducedMembers[root].firstChild; i < newRow->reducedMembers[root].firstChild + newRow->reducedMembers[root].numChildren; ++i) { + reduced_member_id childReduced = newRow->childrenStorage[i]; + if(newRow->reducedMembers[childReduced].type != TYPE_PROPAGATED){ + child = childReduced; + markerToChild = markerOfParent(dec,newRow->reducedMembers[child].member); + break; + } + } + assert(SPQRmemberIsValid(newRow->reducedMembers[child].member)); + assert(SPQRarcIsValid(markerToChild)); + if(!arcIsTree(dec,markerToChild)){ + break; + } + RowReducedMemberType type = determineType(dec,newRow,root,markerToChild,child,markerToParent(dec,newRow->reducedMembers[child].member)); + if(type == TYPE_PROPAGATED){ + root = child; + }else if(type == TYPE_NOT_NETWORK){ + return; + }else{ + break; + } + }else{ + break; + } + } + newRow->reducedComponents[j].root = root; + newRow->reducedMembers[root].parent = INVALID_REDUCED_MEMBER; + } +} + +typedef struct { + spqr_node first; + spqr_node second; +} Nodes; +static Nodes +rigidDetermineCandidateNodesFromAdjacentComponents(SCIP_NETWORKDECOMP *dec, + SCIP_NETWORKROWADDITION *newRow, const reduced_member_id toCheck) { + + Nodes pair; + pair.first = SPQR_INVALID_NODE; + pair.second = SPQR_INVALID_NODE; + + //take union of children's arcs nodes to find one or two candidates + for (children_idx i = newRow->reducedMembers[toCheck].firstChild; + i < newRow->reducedMembers[toCheck].firstChild + newRow->reducedMembers[toCheck].numChildren; ++i) { + reduced_member_id reducedChild = newRow->childrenStorage[i]; + if (newRow->reducedMembers[reducedChild].type != TYPE_PROPAGATED) { + spqr_arc arc = markerOfParent(dec, newRow->reducedMembers[reducedChild].member); + spqr_node head = findArcHead(dec, arc); + spqr_node tail = findArcTail(dec, arc); + if(SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second)){ + pair.first = head; + pair.second = tail; + }else{ + if(pair.first != head && pair.first != tail){ + pair.first = SPQR_INVALID_NODE; + } + if(pair.second != head && pair.second != tail){ + pair.second = SPQR_INVALID_NODE; + } + } + if (SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second)) { + return pair; + } + } + } + if (reducedMemberIsValid(newRow->reducedMembers[toCheck].parent) && + newRow->reducedMembers[newRow->reducedMembers[toCheck].parent].type != TYPE_PROPAGATED) { + + spqr_arc arc = markerToParent(dec, newRow->reducedMembers[toCheck].member); + spqr_node head = findArcHead(dec, arc); + spqr_node tail = findArcTail(dec, arc); + if(SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second)){ + pair.first = head; + pair.second = tail; + }else{ + if(pair.first != head && pair.first != tail){ + pair.first = SPQR_INVALID_NODE; + } + if(pair.second != head && pair.second != tail){ + pair.second = SPQR_INVALID_NODE; + } + } + } + return pair; +} + +static SCIP_RETCODE allocateTreeSearchMemory(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow){ + int necessarySpace = newRow->numReducedMembers; + if( necessarySpace > newRow->memMergeTreeCallData ){ + int updatedSize = max(2*newRow->memMergeTreeCallData,necessarySpace); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->mergeTreeCallData,newRow->memMergeTreeCallData, + updatedSize)); + newRow->memMergeTreeCallData = updatedSize; + } + return SCIP_OKAY; +} + +static void determineSingleRowRigidType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id reducedMember, spqr_member member){ + assert(newRow->reducedMembers[reducedMember].numCutArcs > 0);//Checking for propagation only makes sense if there is at least one cut arc + rigidFindStarNodes(dec,newRow,reducedMember); + if(!newRow->remainsNetwork){ + return; + } + + if(SPQRnodeIsInvalid(newRow->reducedMembers[reducedMember].splitNode)){ + //not a star => attempt to find splittable nodes using articulation node algorithms + rigidGetSplittableArticulationPointsOnPath(dec,newRow,reducedMember,SPQR_INVALID_NODE,SPQR_INVALID_NODE); + } + if(SPQRnodeIsValid(newRow->reducedMembers[reducedMember].splitNode)){ + newRow->reducedMembers[reducedMember].type = TYPE_MERGED; + }else{ + newRow->reducedMembers[reducedMember].type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + } + +} +static void determineSingleParallelType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id reducedMember, spqr_member member){ + SPQRRowReducedMember * redMember = &newRow->reducedMembers[reducedMember]; + assert(cutArcIsValid(redMember->firstCutArc)); + + SCIP_Bool good = TRUE; + SCIP_Bool isReversed = TRUE; + int countedCutArcs = 0; + for (cut_arc_id cutArc = redMember->firstCutArc; cutArcIsValid(cutArc); + cutArc = newRow->cutArcs[cutArc].nextMember){ + spqr_arc arc = newRow->cutArcs[cutArc].arc; + SCIP_Bool arcIsReversed = arcIsReversedNonRigid(dec,arc) != newRow->cutArcs[cutArc].arcReversed; + if(countedCutArcs == 0){ + isReversed = arcIsReversed; + }else if(arcIsReversed != isReversed){ + good = FALSE; + break; + } + ++countedCutArcs; + } + if(!good){ + redMember->type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + }else{ + redMember->type = TYPE_MERGED; + } +} +static void determineSingleSeriesType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id reducedMember, spqr_member member){ + assert(newRow->reducedMembers[reducedMember].numCutArcs == 1); + assert(cutArcIsValid(newRow->reducedMembers[reducedMember].firstCutArc)); + newRow->reducedMembers[reducedMember].type = TYPE_MERGED; +} + +static spqr_node determineAndColorSplitNode(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKROWADDITION * newRow, + reduced_member_id id, spqr_node candidateOne, spqr_node candidateTwo){ + if(SPQRnodeIsInvalid(newRow->reducedMembers[id].splitNode)){ + return SPQR_INVALID_NODE; + } + spqr_node splitNode = newRow->reducedMembers[id].splitNode; + if(splitNode == candidateOne || splitNode == candidateTwo){ + if(newRow->reducedMembers[id].allHaveCommonNode){ + spqr_arc firstNodeArc = getFirstNodeArc(dec,splitNode); + spqr_arc iterArc = firstNodeArc; + COLOR_STATUS color = newRow->reducedMembers[id].otherIsSource ? COLOR_SOURCE : COLOR_SINK; + do{ + spqr_node head = findArcHead(dec,iterArc); + spqr_node other = head == splitNode ? findArcTail(dec,iterArc) : head; + newRow->nodeColors[other] = color; + iterArc = getNextNodeArc(dec,iterArc,splitNode); + }while(iterArc != firstNodeArc); + newRow->reducedMembers[id].coloredNode = splitNode; + } + return splitNode; + } + splitNode = newRow->reducedMembers[id].otherNode; + if(SPQRnodeIsInvalid(splitNode) || (splitNode != candidateOne && splitNode != candidateTwo)){ + return SPQR_INVALID_NODE; + } + assert(SPQRarcIsValid(newRow->reducedMembers[id].articulationArc)); + if(newRow->reducedMembers[id].allHaveCommonNode){ + spqr_arc firstNodeArc = getFirstNodeArc(dec,splitNode); + spqr_arc iterArc = firstNodeArc; + COLOR_STATUS color; + if(newRow->reducedMembers[id].numCutArcs == 1){ + color = newRow->reducedMembers[id].otherIsSource ? COLOR_SINK : COLOR_SOURCE; + }else{ + color = newRow->reducedMembers[id].otherIsSource ? COLOR_SOURCE : COLOR_SINK; + } + do{ + spqr_node head = findArcHead(dec,iterArc); + spqr_node other = head == splitNode ? findArcTail(dec,iterArc) : head; + newRow->nodeColors[other] = color; + iterArc = getNextNodeArc(dec,iterArc,splitNode); + }while(iterArc != firstNodeArc); + newRow->nodeColors[newRow->reducedMembers[id].splitNode] = newRow->reducedMembers[id].otherIsSource ? COLOR_SINK : COLOR_SOURCE; + + }else{ + COLOR_STATUS splitColor = newRow->nodeColors[splitNode]; + + spqr_arc firstNodeArc = getFirstNodeArc(dec,splitNode); + spqr_arc iterArc = firstNodeArc; + do{ + spqr_node head = findArcHead(dec,iterArc); + spqr_node other = head == splitNode ? findArcTail(dec,iterArc) : head; + newRow->nodeColors[other] = splitColor; + iterArc = getNextNodeArc(dec,iterArc,splitNode); + }while(iterArc != firstNodeArc); + newRow->nodeColors[newRow->reducedMembers[id].splitNode] = splitColor == COLOR_SOURCE ? COLOR_SINK : COLOR_SOURCE; + newRow->nodeColors[splitNode] = UNCOLORED; + } + + newRow->reducedMembers[id].coloredNode = splitNode; + return splitNode; +} +static void determineSplitTypeFirstLeaf(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id reducedId){ + spqr_member member = newRow->reducedMembers[reducedId].member; + SPQRMemberType type = getMemberType(dec,member); + assert(type == SPQR_MEMBERTYPE_PARALLEL || type == SPQR_MEMBERTYPE_RIGID); + assert(cutArcIsValid(newRow->reducedMembers[reducedId].firstCutArc)); + SPQRRowReducedMember * redMember = &newRow->reducedMembers[reducedId]; + + if(type == SPQR_MEMBERTYPE_PARALLEL){ + //TODO: duplicate-ish + + SCIP_Bool good = TRUE; + SCIP_Bool isReversed = TRUE; + int countedCutArcs = 0; + for (cut_arc_id cutArc = redMember->firstCutArc; cutArcIsValid(cutArc); + cutArc = newRow->cutArcs[cutArc].nextMember){ + spqr_arc arc = newRow->cutArcs[cutArc].arc; + SCIP_Bool arcIsReversed = arcIsReversedNonRigid(dec,arc) != newRow->cutArcs[cutArc].arcReversed; + if(countedCutArcs == 0){ + isReversed = arcIsReversed; + }else if(arcIsReversed != isReversed){ + good = FALSE; + break; + } + ++countedCutArcs; + } + if(!good){ + redMember->type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + }else{ + spqr_arc marker = markerToParent(dec,member); + redMember->type = TYPE_MERGED; + redMember->splitArc = marker; + redMember->splitHead = TRUE; + redMember->otherIsSource = arcIsReversedNonRigid(dec,marker) == isReversed; + } + return; + } + assert(type == SPQR_MEMBERTYPE_RIGID); + + spqr_arc marker = markerToParent(dec,member); + spqr_node markerHead = findArcHead(dec,marker); + spqr_node markerTail = findArcTail(dec,marker); + if(findArcSign(dec,marker).reversed){ + spqr_node temp = markerHead; + markerHead = markerTail; + markerTail = temp; + } + + if(!SPQRnodeIsValid(newRow->reducedMembers[reducedId].splitNode)){ + assert(newRow->reducedMembers[reducedId].numCutArcs > 0);//Checking for propagation only makes sense if there is at least one cut arc + + rigidFindStarNodes(dec,newRow,reducedId); + if(!newRow->remainsNetwork){ + return; + } + + if(SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode)){ + //not a star => attempt to find splittable nodes using articulation node algorithms + //We save some work by telling the methods that only the marker nodes should be checked + rigidGetSplittableArticulationPointsOnPath(dec,newRow,reducedId,markerHead,markerTail); + } + if(!newRow->remainsNetwork){ + return; + } + if(SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode)){ + redMember->type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + return; + } + + } + + spqr_node splitNode = newRow->reducedMembers[reducedId].splitNode; +#ifndef NDEBUG + spqr_node otherNode = newRow->reducedMembers[reducedId].otherNode; +#endif + //We cannot have both splittable (should have been propagated) + assert(!((splitNode == markerTail || splitNode == markerHead) + && (otherNode == markerTail || otherNode == markerHead))); + + splitNode = determineAndColorSplitNode(dec,newRow,reducedId,markerHead,markerTail); + if(SPQRnodeIsInvalid(splitNode)){ + redMember->type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + return; + } + assert(splitNode == markerHead || splitNode == markerTail); + + newRow->reducedMembers[reducedId].splitNode = splitNode; + newRow->reducedMembers[reducedId].willBeReversed = FALSE; + redMember->type = TYPE_MERGED; + +} +typedef struct{ + SCIP_Bool headSplit; + SCIP_Bool otherIsSource; +} SplitOrientation; + +static SplitOrientation getRelativeOrientationRigid(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id reducedId, spqr_arc arcToNext){ + assert(findArcMemberNoCompression(dec,arcToNext) == newRow->reducedMembers[reducedId].member); + assert(SPQRnodeIsValid(newRow->reducedMembers[reducedId].splitNode)); + + SplitOrientation orientation; + if(newRow->reducedMembers[reducedId].numCutArcs == 0){ + spqr_node splitNode = newRow->reducedMembers[reducedId].splitNode; + spqr_node head = findEffectiveArcHead(dec,arcToNext); + + assert(head == splitNode || splitNode == findEffectiveArcTailNoCompression(dec,arcToNext)); + + orientation.headSplit = newRow->reducedMembers[reducedId].willBeReversed == (head != splitNode); + orientation.otherIsSource = newRow->reducedMembers[reducedId].otherIsSource; + return orientation; + } + spqr_node splitNode = newRow->reducedMembers[reducedId].splitNode; + spqr_node arcHead = findArcHead(dec,arcToNext); + spqr_node arcTail = findArcTail(dec,arcToNext); + if(findArcSign(dec,arcToNext).reversed){ + spqr_node temp = arcHead; + arcHead = arcTail; + arcTail = temp; + } + assert(arcHead == splitNode || arcTail == splitNode); + spqr_node other = arcHead == splitNode ? arcTail : arcHead; + + assert(newRow->nodeColors[other] == COLOR_SOURCE || newRow->nodeColors[other] == COLOR_SINK); + + if(newRow->reducedMembers[reducedId].willBeReversed){ + orientation.headSplit = arcHead != splitNode; + orientation.otherIsSource = newRow->nodeColors[other] != COLOR_SOURCE; + }else{ + orientation.headSplit = arcHead == splitNode; + orientation.otherIsSource = newRow->nodeColors[other] == COLOR_SOURCE; + } + return orientation; +} + +static SplitOrientation getRelativeOrientationParallel(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id reducedId, spqr_arc arcToNext){ + assert(findArcMemberNoCompression(dec,arcToNext) == newRow->reducedMembers[reducedId].member); + assert(SPQRarcIsValid(newRow->reducedMembers[reducedId].splitArc) && SPQRarcIsValid(arcToNext)); + SplitOrientation orientation; + orientation.otherIsSource = newRow->reducedMembers[reducedId].otherIsSource; + if(arcIsReversedNonRigid(dec,arcToNext) == arcIsReversedNonRigid(dec,newRow->reducedMembers[reducedId].splitArc)){ + orientation.headSplit = newRow->reducedMembers[reducedId].splitHead; + }else{ + orientation.headSplit = !newRow->reducedMembers[reducedId].splitHead; + } + return orientation; +} +static SplitOrientation getRelativeOrientationSeries(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id reducedId, spqr_arc arcToNext){ + assert(findArcMemberNoCompression(dec,arcToNext) == newRow->reducedMembers[reducedId].member); + assert(SPQRarcIsValid(newRow->reducedMembers[reducedId].splitArc) && SPQRarcIsValid(arcToNext)); + SplitOrientation orientation; + + orientation.otherIsSource = newRow->reducedMembers[reducedId].otherIsSource; + if(arcIsReversedNonRigid(dec,arcToNext) == arcIsReversedNonRigid(dec,newRow->reducedMembers[reducedId].splitArc)){ + orientation.headSplit = !newRow->reducedMembers[reducedId].splitHead; + }else{ + orientation.headSplit = newRow->reducedMembers[reducedId].splitHead; + } + return orientation; +} + +static SplitOrientation getRelativeOrientation(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id reducedId, spqr_arc arcToNext){ + switch(getMemberType(dec,newRow->reducedMembers[reducedId].member)){ + case SPQR_MEMBERTYPE_RIGID: + return getRelativeOrientationRigid(dec,newRow,reducedId,arcToNext); + case SPQR_MEMBERTYPE_PARALLEL: + return getRelativeOrientationParallel(dec,newRow,reducedId,arcToNext); + case SPQR_MEMBERTYPE_SERIES: + return getRelativeOrientationSeries(dec,newRow,reducedId,arcToNext); + default: + assert(FALSE); + } + SplitOrientation orientation; + orientation.headSplit = FALSE; + orientation.otherIsSource = FALSE; + return orientation; +} +static void determineSplitTypeSeries(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id reducedId, spqr_member member, spqr_arc marker, + SplitOrientation previousOrientation){ + int numAdjacentMembers = newRow->reducedMembers[reducedId].numChildren-newRow->reducedMembers[reducedId].numPropagatedChildren; + if(reducedMemberIsValid(newRow->reducedMembers[reducedId].parent) && + newRow->reducedMembers[newRow->reducedMembers[reducedId].parent].type != TYPE_PROPAGATED){ + ++numAdjacentMembers; + } + assert(numAdjacentMembers > 1); + if(numAdjacentMembers > 2) { + newRow->remainsNetwork = FALSE; + newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; + return; + } + cut_arc_id cutArc = newRow->reducedMembers[reducedId].firstCutArc; + if(cutArcIsValid(cutArc)){ + spqr_arc arc = newRow->cutArcs[cutArc].arc; + SCIP_Bool good = (((arcIsReversedNonRigid(dec,arc) == arcIsReversedNonRigid(dec,marker)) + == newRow->cutArcs[cutArc].arcReversed ) == previousOrientation.headSplit) + == previousOrientation.otherIsSource; + if(!good){ + newRow->remainsNetwork = FALSE; + newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; + return; + } + newRow->reducedMembers[reducedId].splitArc = marker; + newRow->reducedMembers[reducedId].splitHead = previousOrientation.headSplit; + newRow->reducedMembers[reducedId].otherIsSource = !previousOrientation.otherIsSource; + newRow->reducedMembers[reducedId].type = TYPE_MERGED; + return; + } + + newRow->reducedMembers[reducedId].splitArc = marker; + newRow->reducedMembers[reducedId].splitHead = previousOrientation.headSplit; + newRow->reducedMembers[reducedId].otherIsSource = previousOrientation.otherIsSource; + newRow->reducedMembers[reducedId].type = TYPE_MERGED; +} +static void determineSplitTypeParallel(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id reducedId, spqr_member member, spqr_arc marker, + SplitOrientation previousOrientation){ + SPQRRowReducedMember * redMember = &newRow->reducedMembers[reducedId]; + + SCIP_Bool good = TRUE; + SCIP_Bool isReversed = TRUE; + int countedCutArcs = 0; + for (cut_arc_id cutArc = redMember->firstCutArc; cutArcIsValid(cutArc); + cutArc = newRow->cutArcs[cutArc].nextMember){ + spqr_arc arc = newRow->cutArcs[cutArc].arc; + SCIP_Bool arcIsReversed = arcIsReversedNonRigid(dec,arc) != newRow->cutArcs[cutArc].arcReversed; + if(countedCutArcs == 0){ + isReversed = arcIsReversed; + }else if(arcIsReversed != isReversed){ + good = FALSE; + break; + } + ++countedCutArcs; + } + if(!good){ + redMember->type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + return; + } + if(countedCutArcs == 0){ + redMember->splitArc = marker; + redMember->splitHead = previousOrientation.headSplit; + redMember->otherIsSource = previousOrientation.otherIsSource; + redMember->type = TYPE_MERGED; + return; + } + SCIP_Bool isHeadSourceOrTailTarget = previousOrientation.headSplit == previousOrientation.otherIsSource; + SCIP_Bool isOkay = isHeadSourceOrTailTarget == (isReversed == arcIsReversedNonRigid(dec,marker)); + if(!isOkay){ + redMember->type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + return; + } + redMember->splitArc = marker; + redMember->splitHead = previousOrientation.headSplit; + redMember->otherIsSource = previousOrientation.otherIsSource; + redMember->type = TYPE_MERGED; +} +static void determineSplitTypeRigid(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id reducedId, spqr_member member, spqr_arc marker, + SplitOrientation previousOrientation){ + assert(dec); + assert(newRow); + assert(getMemberType(dec,member) == SPQR_MEMBERTYPE_RIGID); + + Nodes nodes = rigidDetermineCandidateNodesFromAdjacentComponents(dec,newRow,reducedId); + if(SPQRnodeIsInvalid(nodes.first) && SPQRnodeIsInvalid(nodes.second)){ + newRow->remainsNetwork = FALSE; + newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; + return; + } + if(SPQRnodeIsInvalid(nodes.first) && SPQRnodeIsValid(nodes.second)){ + nodes.first = nodes.second; + nodes.second = SPQR_INVALID_NODE; + } + + spqr_node markerHead = findArcHead(dec,marker); + spqr_node markerTail = findArcTail(dec,marker); + if(findArcSign(dec,marker).reversed){ + spqr_node temp = markerHead; + markerHead = markerTail; + markerTail = temp; + } + + if(newRow->reducedMembers[reducedId].numCutArcs == 0){ + assert(SPQRnodeIsInvalid(nodes.second)); //There must be at least two adjacent components + if(nodes.first != markerHead && nodes.first != markerTail){ + newRow->remainsNetwork = FALSE; + newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; + return; + } + SCIP_Bool reverse = previousOrientation.headSplit == (nodes.first == markerTail); + newRow->reducedMembers[reducedId].splitNode = nodes.first; + newRow->reducedMembers[reducedId].otherIsSource = previousOrientation.otherIsSource; + newRow->reducedMembers[reducedId].willBeReversed = reverse; + newRow->reducedMembers[reducedId].type = TYPE_MERGED; + return; + } + if(!SPQRnodeIsValid(newRow->reducedMembers[reducedId].splitNode)){ + assert(newRow->reducedMembers[reducedId].numCutArcs > 0);//Checking for propagation only makes sense if there is at least one cut arc + + rigidFindStarNodes(dec,newRow,reducedId); + if(!newRow->remainsNetwork){ + return; + } + + if(SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode)){ + //not a star => attempt to find splittable nodes using articulation node algorithms + //We save some work by telling the methods that only the marker nodes should be checked + rigidGetSplittableArticulationPointsOnPath(dec,newRow,reducedId,nodes.first,nodes.second); + } + if(!newRow->remainsNetwork){ + return; + } + if(SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode)){ + newRow->remainsNetwork = FALSE; + newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; + return; + } + } + spqr_node splitNode = determineAndColorSplitNode(dec,newRow,reducedId,nodes.first,nodes.second); + + if(SPQRnodeIsInvalid(splitNode)){ + newRow->remainsNetwork = FALSE; + newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; + return; + } + assert(splitNode == nodes.first || splitNode == nodes.second); + assert(splitNode == markerHead || splitNode == markerTail); + + spqr_node otherNode = splitNode == markerHead ? markerTail : markerHead; + SCIP_Bool headsMatch = previousOrientation.headSplit == (splitNode == markerHead); + + COLOR_STATUS otherColor = newRow->nodeColors[otherNode]; + assert(otherColor == COLOR_SOURCE || otherColor == COLOR_SINK); + SCIP_Bool otherIsSource = otherColor == COLOR_SOURCE; + + SCIP_Bool good = headsMatch == (previousOrientation.otherIsSource == otherIsSource); + + if(!good){ + newRow->remainsNetwork = FALSE; + newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; + return; + } + + newRow->reducedMembers[reducedId].splitNode = splitNode; + newRow->reducedMembers[reducedId].willBeReversed = !headsMatch; + newRow->reducedMembers[reducedId].type = TYPE_MERGED; +} +static void determineSplitTypeNext(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id current, reduced_member_id next, SCIP_Bool currentIsParent){ + spqr_member member = newRow->reducedMembers[next].member; + SPQRMemberType type = getMemberType(dec,member); + spqr_arc nextArc = currentIsParent ? markerToParent(dec,member) : markerOfParent(dec,newRow->reducedMembers[current].member); + spqr_arc currentArc = currentIsParent ? markerOfParent(dec,member) : markerToParent(dec,newRow->reducedMembers[current].member); + SplitOrientation orientation = getRelativeOrientation(dec,newRow,current,currentArc); + assert(type == SPQR_MEMBERTYPE_PARALLEL || type == SPQR_MEMBERTYPE_RIGID || type == SPQR_MEMBERTYPE_SERIES); + switch(type){ + case SPQR_MEMBERTYPE_RIGID:{ + determineSplitTypeRigid(dec,newRow,next,member,nextArc,orientation); + break; + } + case SPQR_MEMBERTYPE_PARALLEL:{ + determineSplitTypeParallel(dec,newRow,next,member,nextArc,orientation); + break; + } + case SPQR_MEMBERTYPE_SERIES:{ + determineSplitTypeSeries(dec,newRow,next,member,nextArc,orientation); + break; + } + default: + assert(FALSE); + newRow->remainsNetwork = FALSE; + } +} +static void determineTypesChildrenNodes(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id parent, reduced_member_id node, + reduced_member_id skipNode){ + if(node == skipNode || newRow->reducedMembers[node].type == TYPE_PROPAGATED){ + return; + } + //check merging + determineSplitTypeNext(dec,newRow,parent,node,TRUE); + if(!newRow->remainsNetwork){ + return; + } + //merge all children + for (int i = 0; i < newRow->reducedMembers[node].numChildren; ++i) { + children_idx idx = newRow->reducedMembers[node].firstChild + i; + reduced_member_id child = newRow->childrenStorage[idx]; + determineTypesChildrenNodes(dec,newRow,node,child,skipNode); + } +} + +static void determineMergeableTypes(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, reduced_member_id root){ + assert(newRow->numReducedMembers <= newRow->memMergeTreeCallData); + if(newRow->reducedMembers[root].numPropagatedChildren == newRow->reducedMembers[root].numChildren){ + //Determine single component; + if(newRow->reducedMembers[root].type == TYPE_UNDETERMINED){ + spqr_member member = newRow->reducedMembers[root].member; + switch(getMemberType(dec,member)){ + case SPQR_MEMBERTYPE_RIGID: + determineSingleRowRigidType(dec,newRow,root,member); + break; + case SPQR_MEMBERTYPE_PARALLEL: + determineSingleParallelType(dec,newRow,root,member); + break; + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_SERIES: + determineSingleSeriesType(dec,newRow,root,member); + break; + default: + assert(FALSE); + newRow->remainsNetwork = FALSE; + } + } + return; + } + + //go to a leaf. We need to start in a leaf to avoid the ambiguity of choosing an orientation + //in members which have no cut arcs; otherwise, we might choose the wrong one + reduced_member_id leaf = root; + while(newRow->reducedMembers[leaf].numChildren != newRow->reducedMembers[leaf].numPropagatedChildren){ + for(int i = 0; i < newRow->reducedMembers[leaf].numChildren;++i){ + children_idx idx = newRow->reducedMembers[leaf].firstChild + i; + reduced_member_id child = newRow->childrenStorage[idx]; + if(newRow->reducedMembers[child].type != TYPE_PROPAGATED){ + leaf = child; + break; + } + } + } + determineSplitTypeFirstLeaf(dec,newRow,leaf); + + if(!newRow->remainsNetwork){ + return; + } + reduced_member_id baseNode = leaf; + reduced_member_id nextNode = newRow->reducedMembers[baseNode].parent; + + while(reducedMemberIsValid(nextNode)){ + //check this node + determineSplitTypeNext(dec,newRow,baseNode,nextNode,FALSE); + if(!newRow->remainsNetwork){ + return; + } + //Add other nodes in the subtree + for (int i = 0; i < newRow->reducedMembers[nextNode].numChildren; ++i) { + children_idx idx = newRow->reducedMembers[nextNode].firstChild + i; + reduced_member_id child = newRow->childrenStorage[idx]; + determineTypesChildrenNodes(dec,newRow,nextNode,child,baseNode); + if(!newRow->remainsNetwork){ + return; + } + } + + //Move up one layer + baseNode = nextNode; + nextNode = newRow->reducedMembers[nextNode].parent; + } +} + +static void cleanUpRowMemberInformation(SCIP_NETWORKROWADDITION * newRow){ + //This loop is at the end as memberInformation is also used to assign the cut arcs during propagation + //Clean up the memberInformation array + for (int i = 0; i < newRow->numReducedMembers; ++i) { + newRow->memberInformation[newRow->reducedMembers[i].member].reducedMember = INVALID_REDUCED_MEMBER; + } +#ifndef NDEBUG + for (int i = 0; i < newRow->memMemberInformation; ++i) { + assert(reducedMemberIsInvalid(newRow->memberInformation[i].reducedMember)); + } +#endif +} + +static SCIP_RETCODE rigidTransformArcIntoCycle(SCIP_NETWORKDECOMP *dec, + const spqr_member member, + const spqr_arc arc, + const SCIP_Bool reverseArcDirection, + NewRowInformation * const newRowInformation){ + //If a cycle already exists, just expand it with the new arc. + spqr_member markerCycleMember = SPQR_INVALID_MEMBER; + spqr_arc markerCycleArc = SPQR_INVALID_ARC; + SCIP_Bool isParent = arc == markerToParent(dec,member); + spqr_member adjacentMember = SPQR_INVALID_MEMBER; + if (isParent) { + adjacentMember = findMemberParent(dec, member); + if (getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_SERIES) { + markerCycleMember = adjacentMember; + markerCycleArc = markerOfParent(dec,member); + } + } else if (arcIsMarker(dec, arc)) { + adjacentMember = findArcChildMember(dec, arc); + if (getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_SERIES) { + markerCycleMember = adjacentMember; + markerCycleArc = markerToParent(dec,adjacentMember); + } + } + if (markerCycleMember != SPQR_INVALID_MEMBER) { + newRowInformation->member = markerCycleMember; + if(arcIsReversedNonRigid(dec,markerCycleArc)){ + newRowInformation->reversed = reverseArcDirection; + }else{ + newRowInformation->reversed = !reverseArcDirection; + } + + return SCIP_OKAY; + } + + //Otherwise, we create a new cycle + spqr_member newCycle; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &newCycle)); + //We would like to move the edge but unfortunately cannot do so without destroying the arc union-find datastructure. + //Thus, we 'convert' the arc into a marker and add the new series + + spqr_arc duplicate = SPQR_INVALID_ARC; + spqr_element element = arcGetElement(dec,arc); + if(element != MARKER_COLUMN_ELEMENT && element != MARKER_ROW_ELEMENT){ + if(SPQRelementIsColumn(element)){ + SCIP_CALL(createColumnArc(dec,newCycle,&duplicate, SPQRelementToColumn(element),TRUE)); + }else{ + SCIP_CALL(createRowArc(dec,newCycle,&duplicate, SPQRelementToRow(element),TRUE)); + } + }else if(isParent){ + //create parent marker + SCIP_CALL(createParentMarker(dec, newCycle, arcIsTree(dec, arc), adjacentMember, + markerOfParent(dec, member),&duplicate,TRUE)); + }else{ + //create child marker + SCIP_CALL(createChildMarker(dec,newCycle,adjacentMember,arcIsTree(dec,arc),&duplicate,TRUE)); + dec->members[adjacentMember].parentMember = newCycle; + dec->members[adjacentMember].markerOfParent = duplicate; + } + //Create the other marker edge + spqr_arc cycleMarker = SPQR_INVALID_ARC; + if(isParent){ + SCIP_CALL(createChildMarker(dec,newCycle,member,!arcIsTree(dec,arc), + &cycleMarker,FALSE)); + }else{ + SCIP_CALL(createParentMarker(dec,newCycle,!arcIsTree(dec,arc), + member,arc,&cycleMarker,FALSE)); + } + //Change the existing edge to a marker + if(isParent){ + assert(markerToParent(dec,member) == arc); + dec->arcs[markerOfParent(dec,member)].childMember = newCycle; + dec->members[member].parentMember = newCycle; + dec->members[member].markerToParent = arc; + dec->members[member].markerOfParent = cycleMarker; + dec->arcs[arc].element = arcIsTree(dec,arc) ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT;; + dec->arcs[arc].childMember = SPQR_INVALID_MEMBER; + + }else{ + dec->arcs[arc].element = arcIsTree(dec,arc) ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; + dec->arcs[arc].childMember = newCycle; + } + newRowInformation->member = newCycle; + newRowInformation->reversed = !reverseArcDirection; + + return SCIP_OKAY; +} + +static SCIP_RETCODE transformSingleRigid(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + const reduced_member_id reducedMember, + const spqr_member member, + NewRowInformation * const newRowInformation){ + if(SPQRarcIsValid(newRow->reducedMembers[reducedMember].articulationArc)){ + spqr_arc arc = newRow->reducedMembers[reducedMember].articulationArc; + //Cut arc is propagated into a cycle with new arc + assert(newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcHeadNoCompression(dec,arc) || + newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcTailNoCompression(dec,arc)); + + + SCIP_Bool reversed; + + if(newRow->isArcCut[arc]){ + reversed = (newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcHead(dec,arc)) == + newRow->reducedMembers[reducedMember].otherIsSource; + }else{ + reversed = (newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcHead(dec,arc)) != + newRow->reducedMembers[reducedMember].otherIsSource; + } + + SCIP_CALL(rigidTransformArcIntoCycle(dec,member,newRow->reducedMembers[reducedMember].articulationArc, + reversed, newRowInformation)); + + return SCIP_OKAY; + } + //Single splittable node + assert(SPQRnodeIsValid(newRow->reducedMembers[reducedMember].splitNode)); + + spqr_node splitNode = newRow->reducedMembers[reducedMember].splitNode; + if(newRow->reducedMembers[reducedMember].allHaveCommonNode){ + //Create a new node; move all cut arcs end of split node to it and add new arc between new node and split node + spqr_node newNode = SPQR_INVALID_NODE; + SCIP_CALL(createNode(dec, &newNode)); + + cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; + do { + spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; + spqr_node arcHead = findArcHead(dec, cutArc); + if (arcHead == splitNode) { + changeArcHead(dec, cutArc, arcHead, newNode); + } else { + changeArcTail(dec, cutArc, findArcTail(dec, cutArc), newNode); + } + + cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; + } while (cutArcIsValid(cutArcIdx)); + + newRowInformation->member = member; + if(newRow->reducedMembers[reducedMember].otherIsSource){ + newRowInformation->head = newNode; + newRowInformation->tail = splitNode; + }else{ + newRowInformation->head = splitNode; + newRowInformation->tail = newNode; + } + newRowInformation->representative = findArcSign(dec,newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc).representative; + newRowInformation->reversed = FALSE; + + return SCIP_OKAY; + } + //Articulation point was split (based on coloring) + + spqr_node newNode = SPQR_INVALID_NODE; + SCIP_CALL(createNode(dec, &newNode)); + + spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode); + spqr_arc iterArc = firstNodeArc; + + do{ + SCIP_Bool isCut = newRow->isArcCut[iterArc]; + spqr_node otherHead = findArcHead(dec, iterArc); + spqr_node otherTail = findArcTail(dec, iterArc); + spqr_node otherEnd = otherHead == splitNode ? otherTail : otherHead; + SCIP_Bool isMoveColor = newRow->nodeColors[otherEnd] == COLOR_SOURCE; + spqr_arc nextArc = getNextNodeArc(dec, iterArc, splitNode); //Need to do this before we modify the arc :) + + SCIP_Bool changeArcEnd = isCut == isMoveColor; + if(changeArcEnd){ + if(otherHead == splitNode){ + changeArcHead(dec,iterArc,otherHead,newNode); + }else{ + changeArcTail(dec,iterArc,otherTail,newNode); + } + } + newRow->nodeColors[otherEnd] = UNCOLORED; //Clean up + + //Ugly hack to make sure we can iterate neighbourhood whilst changing arc ends. + spqr_arc previousArc = iterArc; + iterArc = nextArc; + if(iterArc == firstNodeArc){ + break; + } + if(changeArcEnd && previousArc == firstNodeArc){ + firstNodeArc = iterArc; + } + }while(TRUE); + newRow->reducedMembers[reducedMember].coloredNode = SPQR_INVALID_NODE; + + newRowInformation->member = member; + newRowInformation->head = newNode; + newRowInformation->tail = splitNode; + newRowInformation->representative = findArcSign(dec,newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc).representative; + newRowInformation->reversed = FALSE; + + return SCIP_OKAY; +} + + +static SCIP_RETCODE splitParallelRowAddition(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + const reduced_member_id reducedMember, + const spqr_member member, + NewRowInformation * newRowInfo){ + assert(newRow->reducedMembers[reducedMember].numCutArcs > 0); + + int numCutArcs = newRow->reducedMembers[reducedMember].numCutArcs; + int numParallelArcs = getNumMemberArcs(dec,member); + + SCIP_Bool createCutParallel = numCutArcs > 1; + SCIP_Bool convertOriginalParallel = (numCutArcs + 1) == numParallelArcs; + + //Do linear search to find non-marked arc + spqr_arc treeArc = getFirstMemberArc(dec,member); + do{ + if(arcIsTree(dec,treeArc)){ + break; + } + treeArc = getNextMemberArc(dec,treeArc); + }while(treeArc != getFirstMemberArc(dec,member)); + assert(arcIsTree(dec,treeArc)); + + SCIP_Bool treeReversed = arcIsReversedNonRigid(dec,treeArc); + + assert(!(!createCutParallel && convertOriginalParallel));//This can only happen if the parallel member is actually a loop, which means it is mislabeled + if(createCutParallel) { + if(convertOriginalParallel){ + spqr_member adjacentMember = SPQR_INVALID_MEMBER; + spqr_arc adjacentArc = SPQR_INVALID_ARC; + if(treeArc == markerToParent(dec,member)){ + adjacentMember = findMemberParent(dec,member); + adjacentArc = markerOfParent(dec,member); + }else if(arcIsMarker(dec,treeArc)){ + adjacentMember = findArcChildMember(dec,treeArc); + adjacentArc = markerToParent(dec,adjacentMember); + assert(markerOfParent(dec,adjacentMember) == treeArc); + } + cut_arc_id firstCut = newRow->reducedMembers[reducedMember].firstCutArc; + SCIP_Bool firstReversed = newRow->cutArcs[firstCut].arcReversed != arcIsReversedNonRigid(dec,newRow->cutArcs[firstCut].arc); + + if(SPQRmemberIsValid(adjacentMember) && getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_SERIES){ + newRowInfo->member = adjacentMember; + if(arcIsReversedNonRigid(dec,treeArc) == arcIsReversedNonRigid(dec,adjacentArc)){ + if(treeReversed){ + newRowInfo->reversed = !firstReversed; + }else{ + newRowInfo->reversed = !firstReversed; + } + }else{ + if(treeReversed) { + newRowInfo->reversed = firstReversed; + }else{ + newRowInfo->reversed = firstReversed; + } + } + return SCIP_OKAY; + } + spqr_member cutMember = SPQR_INVALID_MEMBER; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember)); + + cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; + assert(cutArcIsValid(cutArcIdx)); + SCIP_Bool parentCut = FALSE; + + while(cutArcIsValid(cutArcIdx)){ + spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; + cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; + moveArcToNewMember(dec,cutArc,member,cutMember); + if (cutArc == markerToParent(dec,member)){ + parentCut = TRUE; + } + } + if(parentCut){ + SCIP_CALL(createMarkerPair(dec,cutMember,member,TRUE,FALSE,TRUE)); + }else{ + SCIP_CALL(createMarkerPair(dec,member,cutMember,FALSE,TRUE,FALSE)); + } + changeLoopToSeries(dec,member); + newRowInfo->member = member; + if(treeReversed){ + newRowInfo->reversed = firstReversed == arcIsReversedNonRigid(dec,treeArc); + }else{ + newRowInfo->reversed = firstReversed != arcIsReversedNonRigid(dec,treeArc); + } + }else{ + spqr_member cutMember = SPQR_INVALID_MEMBER; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember)); + + cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; + assert(cutArcIsValid(cutArcIdx)); + SCIP_Bool parentCut = FALSE; + + while(cutArcIsValid(cutArcIdx)){ + spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; + cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; + moveArcToNewMember(dec,cutArc,member,cutMember); + if (cutArc == markerToParent(dec,member)){ + parentCut = TRUE; + } + } + spqr_member newSeries; + SCIP_CALL(createMember(dec,SPQR_MEMBERTYPE_SERIES,&newSeries)); + if(parentCut){ + SCIP_CALL(createMarkerPair(dec,newSeries,member,TRUE,FALSE,FALSE)); + SCIP_CALL(createMarkerPair(dec,cutMember,newSeries,TRUE,FALSE,TRUE)); + }else{ + SCIP_CALL(createMarkerPair(dec,member,newSeries,FALSE,FALSE,FALSE)); + SCIP_CALL(createMarkerPair(dec,newSeries,cutMember,FALSE,TRUE,FALSE)); + } + newRowInfo->member = newSeries; + cut_arc_id firstCut = newRow->reducedMembers[reducedMember].firstCutArc; + SCIP_Bool firstReversed = newRow->cutArcs[firstCut].arcReversed != arcIsReversedNonRigid(dec,newRow->cutArcs[firstCut].arc); + if(treeReversed){ + newRowInfo->reversed = firstReversed; + }else{ + newRowInfo->reversed = firstReversed; + } + } + + return SCIP_OKAY; + } + + assert(!createCutParallel && !convertOriginalParallel); + assert(numCutArcs == 1); +#ifndef NDEBUG + spqr_arc arc = newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc; + spqr_member adjacentMember = SPQR_INVALID_MEMBER; + if(arc == markerToParent(dec,member)){ + adjacentMember = findMemberParent(dec,member); + }else if(arcIsMarker(dec,arc)){ + adjacentMember = findArcChildMember(dec,arc); + } + if(SPQRmemberIsValid(adjacentMember)){ + assert(getMemberType(dec,adjacentMember) != SPQR_MEMBERTYPE_SERIES); + } +#endif + spqr_member newSeries; + SCIP_CALL(createMember(dec,SPQR_MEMBERTYPE_SERIES,&newSeries)); + cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; + assert(cutArcIsValid(cutArcIdx)); + SCIP_Bool parentCut = FALSE; + + while(cutArcIsValid(cutArcIdx)){ + spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; + cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; + moveArcToNewMember(dec,cutArc,member,newSeries); + if (cutArc == markerToParent(dec,member)){ + parentCut = TRUE; + } + } + if(parentCut){ + SCIP_CALL(createMarkerPair(dec,newSeries,member,TRUE,TRUE,FALSE)); + }else{ + SCIP_CALL(createMarkerPair(dec,member,newSeries,FALSE,FALSE,TRUE)); + } + newRowInfo->member = newSeries; + cut_arc_id cutArcId = newRow->reducedMembers[reducedMember].firstCutArc; + if(treeReversed){ + newRowInfo->reversed = newRow->cutArcs[cutArcId].arcReversed == arcIsReversedNonRigid(dec,newRow->cutArcs[cutArcId].arc); + }else{ + newRowInfo->reversed = newRow->cutArcs[cutArcId].arcReversed == arcIsReversedNonRigid(dec,newRow->cutArcs[cutArcId].arc); + } + return SCIP_OKAY; +} + +static SCIP_RETCODE transformSingleParallel(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + const reduced_member_id reducedMember, + const spqr_member member, + NewRowInformation * info){ + SCIP_CALL(splitParallelRowAddition(dec,newRow,reducedMember,member,info)); + return SCIP_OKAY; +} + +static SCIP_RETCODE splitSeriesMergingRowAddition(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + const reduced_member_id reducedMember, + const spqr_member member, + spqr_member * const mergingMember, SCIP_Bool * const isCut, + spqr_arc * representativeEdge){ + assert(getNumMemberArcs(dec,member) >= 3); + * isCut = newRow->reducedMembers[reducedMember].numCutArcs > 0; + + if(getNumMemberArcs(dec,member) == 3){ + spqr_arc splitArc = newRow->reducedMembers[reducedMember].splitArc; + spqr_arc otherArc = SPQR_INVALID_ARC; + for (children_idx i = newRow->reducedMembers[reducedMember].firstChild; + i < newRow->reducedMembers[reducedMember].firstChild + newRow->reducedMembers[reducedMember].numChildren; ++i) { + reduced_member_id child = newRow->childrenStorage[i]; + if(newRow->reducedMembers[child].type == TYPE_MERGED){ + spqr_arc testArc = markerOfParent(dec, findMember(dec,newRow->reducedMembers[child].member)); + if(testArc != splitArc){ + otherArc = testArc; + break; + } + } + } + if(SPQRarcIsInvalid(otherArc)){ +#ifndef NDEBUG + reduced_member_id parent = newRow->reducedMembers[reducedMember].parent; +#endif + assert(newRow->reducedMembers[parent].type == TYPE_MERGED || newRow->reducedMembers[parent].type == TYPE_PROPAGATED); + assert(reducedMemberIsValid(parent) && newRow->reducedMembers[parent].type == TYPE_MERGED); + otherArc = markerToParent(dec, member); + + } + spqr_arc firstArc = getFirstMemberArc(dec,member); + spqr_arc arc = firstArc; + do{ + if(arc != splitArc && arc != otherArc){ + *representativeEdge = arc; + break; + } + arc = getNextMemberArc(dec,arc); + }while(arc != firstArc); + *mergingMember = member; + return SCIP_OKAY; + } + //Split off the relevant part of the series member + spqr_member mergingSeries = SPQR_INVALID_MEMBER; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &mergingSeries)); + //Move all marker arcs which point to another component in the reduced decomposition to the new member + //This should be exactly 2, as with 3 the result is not network anymore + //move all mergeable children and parent arcs to the mergingMember + + SCIP_Bool coTreeToMergingMember = FALSE; + SCIP_Bool parentToMergingMember = FALSE; + for (children_idx i = newRow->reducedMembers[reducedMember].firstChild; + i < newRow->reducedMembers[reducedMember].firstChild + newRow->reducedMembers[reducedMember].numChildren; ++i) { + reduced_member_id child = newRow->childrenStorage[i]; + assert(newRow->reducedMembers[child].type == TYPE_MERGED || newRow->reducedMembers[child].type == TYPE_PROPAGATED); + if(newRow->reducedMembers[child].type == TYPE_MERGED){ + spqr_arc moveArc = markerOfParent(dec, findMember(dec,newRow->reducedMembers[child].member)); + moveArcToNewMember(dec,moveArc,member,mergingSeries); + if(!arcIsTree(dec,moveArc)){ + coTreeToMergingMember = TRUE; + } + } + } + + reduced_member_id parent = newRow->reducedMembers[reducedMember].parent; + assert(reducedMemberIsInvalid(parent) || (newRow->reducedMembers[parent].type == TYPE_MERGED || newRow->reducedMembers[parent].type == TYPE_PROPAGATED)); + + if(reducedMemberIsValid(parent) && + newRow->reducedMembers[parent].type == TYPE_MERGED ){ + spqr_arc moveArc = markerToParent(dec, member); + moveArcToNewMember(dec,moveArc,member,mergingSeries); + parentToMergingMember = TRUE; + if(!arcIsTree(dec,moveArc)){ + coTreeToMergingMember = TRUE; + } + } + spqr_arc ignoreArc = SPQR_INVALID_ARC; + if(parentToMergingMember){ + SCIP_CALL(createMarkerPairWithReferences(dec,mergingSeries,member,coTreeToMergingMember,TRUE,FALSE,representativeEdge,&ignoreArc)); + }else{ + SCIP_CALL(createMarkerPairWithReferences(dec,member,mergingSeries,!coTreeToMergingMember,FALSE,TRUE,&ignoreArc,representativeEdge)); + } + + *mergingMember = mergingSeries; + assert(getNumMemberArcs(dec,mergingSeries) == 3 ); + assert(getNumMemberArcs(dec,member) >= 3); + return SCIP_OKAY; +} + +static SCIP_RETCODE splitParallelMerging(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id reducedMember, spqr_member member,spqr_member * const pMergeMember, + spqr_arc * const cutRepresentative){ + //When merging, we cannot have propagated members; + assert(newRow->reducedMembers[reducedMember].numCutArcs < (getNumMemberArcs(dec,member)-1)); + + int numMergeableAdjacent = newRow->reducedMembers[reducedMember].numChildren - newRow->reducedMembers[reducedMember].numPropagatedChildren; + if(reducedMemberIsValid(newRow->reducedMembers[reducedMember].parent) && + newRow->reducedMembers[newRow->reducedMembers[reducedMember].parent].type == TYPE_MERGED){ + numMergeableAdjacent++; + } + + int numCutArcs = newRow->reducedMembers[reducedMember].numCutArcs; + //All arcs which are not in the mergeable decomposition or cut + int numBaseSplitAwayArcs = getNumMemberArcs(dec,member) - numMergeableAdjacent - numCutArcs ; + + SCIP_Bool createCutParallel = numCutArcs > 1; + SCIP_Bool keepOriginalParallel = numBaseSplitAwayArcs <= 1; + + spqr_member cutMember = SPQR_INVALID_MEMBER; + //The below cases can probably be aggregated in some way, but for now we first focus on the correct logic + if(createCutParallel && keepOriginalParallel){ + SCIP_Bool parentCut = FALSE; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember)); + + cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; + assert(cutArcIsValid(cutArcIdx)); + + while(cutArcIsValid(cutArcIdx)){ + spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; + cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; + moveArcToNewMember(dec,cutArc,member,cutMember); + if (cutArc == markerToParent(dec,member)){ + parentCut = TRUE; + } + } + spqr_arc ignoreArc = SPQR_INVALID_ARC; + if(parentCut){ + SCIP_CALL(createMarkerPairWithReferences(dec,cutMember,member,TRUE,FALSE,FALSE,&ignoreArc,cutRepresentative)); + }else{ + SCIP_CALL(createMarkerPairWithReferences(dec,member,cutMember,FALSE,FALSE,FALSE,cutRepresentative,&ignoreArc)); + } + + *pMergeMember = member; + }else if(createCutParallel){ + assert(!keepOriginalParallel); + + SCIP_Bool parentCut = FALSE; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember)); + + cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; + assert(cutArcIsValid(cutArcIdx)); + + while(cutArcIsValid(cutArcIdx)){ + spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; + cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; + moveArcToNewMember(dec,cutArc,member,cutMember); + if (cutArc == markerToParent(dec,member)){ + parentCut = TRUE; + } + } + spqr_arc ignoreArc = SPQR_INVALID_ARC; + if(parentCut){ + SCIP_CALL(createMarkerPairWithReferences(dec,cutMember,member,TRUE,FALSE,FALSE,&ignoreArc,cutRepresentative)); + }else{ + SCIP_CALL(createMarkerPairWithReferences(dec,member,cutMember,FALSE,FALSE,FALSE,cutRepresentative,&ignoreArc)); + } + + + spqr_arc noCutRepresentative = SPQR_INVALID_ARC; + spqr_member mergingMember = member; + SCIP_Bool parentToMergingMember = FALSE; + SCIP_Bool treeToMergingMember = FALSE; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &mergingMember)); + //move all mergeable children and parent arcs to the mergingMember + for (children_idx i = newRow->reducedMembers[reducedMember].firstChild; + i < newRow->reducedMembers[reducedMember].firstChild + + newRow->reducedMembers[reducedMember].numChildren; ++i) { + reduced_member_id child = newRow->childrenStorage[i]; + assert(newRow->reducedMembers[child].type == TYPE_MERGED || newRow->reducedMembers[child].type == TYPE_PROPAGATED); + if (newRow->reducedMembers[child].type == TYPE_MERGED) { + spqr_arc moveArc = markerOfParent(dec, findMember(dec,newRow->reducedMembers[child].member)); + moveArcToNewMember(dec, moveArc, member, mergingMember); + if (arcIsTree(dec, moveArc)) { + treeToMergingMember = TRUE; + } + } + } + reduced_member_id parent = newRow->reducedMembers[reducedMember].parent; + if (reducedMemberIsValid(parent) && + newRow->reducedMembers[parent].type == TYPE_MERGED) { + spqr_arc moveArc = markerToParent(dec, member); + moveArcToNewMember(dec, moveArc, member, mergingMember); + parentToMergingMember = TRUE; + if (arcIsTree(dec, moveArc)) { + treeToMergingMember = TRUE; + } + } + //If there is only one cut arc, we also move it. + if (SPQRarcIsValid(*cutRepresentative)) { + if (*cutRepresentative == markerToParent(dec, member)) { + parentToMergingMember = TRUE; + } + moveArcToNewMember(dec, *cutRepresentative, member, mergingMember); + } + spqr_arc ignoreArgument = SPQR_INVALID_ARC; + if(parentToMergingMember || parentCut){ + SCIP_CALL(createMarkerPairWithReferences(dec,mergingMember,member,!treeToMergingMember,FALSE,FALSE,&ignoreArgument,&noCutRepresentative)); + }else{ + SCIP_CALL(createMarkerPairWithReferences(dec,member,mergingMember,treeToMergingMember,FALSE,FALSE,&noCutRepresentative,&ignoreArgument)); + } + *pMergeMember = mergingMember; + }else if(keepOriginalParallel){ + assert(!createCutParallel); + if(cutArcIsValid( newRow->reducedMembers[reducedMember].firstCutArc)){ + *cutRepresentative = newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc; + } + *pMergeMember = member; + + }else{ + assert(!keepOriginalParallel && ! createCutParallel); + + if(cutArcIsValid( newRow->reducedMembers[reducedMember].firstCutArc)){ + *cutRepresentative = newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc; + } + + spqr_arc noCutRepresentative = SPQR_INVALID_ARC; + spqr_member mergingMember = member; + SCIP_Bool parentToMergingMember = FALSE; + SCIP_Bool treeToMergingMember = FALSE; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &mergingMember)); + //move all mergeable children and parent arcs to the mergingMember + for (children_idx i = newRow->reducedMembers[reducedMember].firstChild; + i < newRow->reducedMembers[reducedMember].firstChild + + newRow->reducedMembers[reducedMember].numChildren; ++i) { + reduced_member_id child = newRow->childrenStorage[i]; + assert(newRow->reducedMembers[child].type == TYPE_MERGED || newRow->reducedMembers[child].type == TYPE_PROPAGATED); + if (newRow->reducedMembers[child].type == TYPE_MERGED) { + spqr_arc moveArc = markerOfParent(dec, findMember(dec,newRow->reducedMembers[child].member)); + moveArcToNewMember(dec, moveArc, member, mergingMember); + if (arcIsTree(dec, moveArc)) { + treeToMergingMember = TRUE; + } + } + } + reduced_member_id parent = newRow->reducedMembers[reducedMember].parent; + if (reducedMemberIsValid(parent) && + newRow->reducedMembers[parent].type == TYPE_MERGED) { + spqr_arc moveArc = markerToParent(dec, member); + moveArcToNewMember(dec, moveArc, member, mergingMember); + parentToMergingMember = TRUE; + if (arcIsTree(dec, moveArc)) { + treeToMergingMember = TRUE; + } + } + //If there is only one cut arc, we also move it. + if (SPQRarcIsValid(*cutRepresentative)) { + if (*cutRepresentative == markerToParent(dec, member)) { + parentToMergingMember = TRUE; + } + moveArcToNewMember(dec, *cutRepresentative, member, mergingMember); + } + spqr_arc ignoreArgument = SPQR_INVALID_ARC; + if(parentToMergingMember){ + SCIP_CALL(createMarkerPairWithReferences(dec,mergingMember,member,!treeToMergingMember,FALSE,FALSE,&ignoreArgument,&noCutRepresentative)); + }else{ + SCIP_CALL(createMarkerPairWithReferences(dec,member,mergingMember,treeToMergingMember,FALSE,FALSE,&noCutRepresentative,&ignoreArgument)); + } + *pMergeMember = mergingMember; + } + return SCIP_OKAY; +} +static SCIP_RETCODE splitFirstLeaf(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id leaf, NewRowInformation * const newRowInformation){ + assert(cutArcIsValid(newRow->reducedMembers[leaf].firstCutArc)); + assert(newRow->reducedMembers[leaf].numCutArcs > 0); + spqr_member member = newRow->reducedMembers[leaf].member; + if(getMemberType(dec,member) == SPQR_MEMBERTYPE_PARALLEL){ + spqr_member mergeMember = SPQR_INVALID_MEMBER; + spqr_arc cutRepresentative = SPQR_INVALID_ARC; + SCIP_CALL(splitParallelMerging(dec,newRow,leaf,member,&mergeMember,&cutRepresentative)); + newRow->reducedMembers[leaf].member = mergeMember; + + spqr_node firstNode = SPQR_INVALID_NODE; + spqr_node secondNode = SPQR_INVALID_NODE; + spqr_node thirdNode = SPQR_INVALID_NODE; + SCIP_CALL(createNode(dec,&firstNode)); + SCIP_CALL(createNode(dec,&secondNode)); + SCIP_CALL(createNode(dec,&thirdNode)); + + spqr_arc splitArc = newRow->reducedMembers[leaf].splitArc; + assert(findArcMemberNoCompression(dec,splitArc) == mergeMember); + SCIP_Bool splitArcReversed = arcIsReversedNonRigid(dec,splitArc); + SCIP_Bool splitHead = newRow->reducedMembers[leaf].splitHead; + spqr_node splitArcHead = splitArcReversed ? secondNode : firstNode; + spqr_node splitArcTail = splitArcReversed ? firstNode : secondNode; + spqr_node splitNode = splitHead ? splitArcHead : splitArcTail; + spqr_node otherNode = splitHead ? splitArcTail : splitArcHead; + + spqr_arc first_arc = getFirstMemberArc(dec, mergeMember); + spqr_arc arc = first_arc; + + do { + if(arc != cutRepresentative){ + if(arcIsReversedNonRigid(dec,arc)){ + setArcHeadAndTail(dec,arc,secondNode,firstNode); + }else{ + setArcHeadAndTail(dec,arc,firstNode,secondNode); + } + }else{ + if ((arcIsReversedNonRigid(dec, arc) == splitArcReversed) == splitHead) { + setArcHeadAndTail(dec, arc, thirdNode, otherNode); + } else { + setArcHeadAndTail(dec, arc, otherNode, thirdNode); + } + } + arcSetReversed(dec,arc,FALSE); + if(arc == splitArc){ + arcSetRepresentative(dec,arc,SPQR_INVALID_ARC); + }else{ + arcSetRepresentative(dec,arc,splitArc); + } + arc = getNextMemberArc(dec, arc); + } while (arc != first_arc); + + + updateMemberType(dec, mergeMember, SPQR_MEMBERTYPE_RIGID); + newRowInformation->member = mergeMember; + if(newRow->reducedMembers[leaf].otherIsSource){ + newRowInformation->head = thirdNode; + newRowInformation->tail = splitNode; + }else{ + newRowInformation->head = splitNode; + newRowInformation->tail = thirdNode; + } + + newRowInformation->reversed = FALSE; + newRowInformation->representative = splitArc; + + return SCIP_OKAY; + } + assert(getMemberType(dec,member) == SPQR_MEMBERTYPE_RIGID); + + spqr_node newNode = SPQR_INVALID_NODE; //Sink node + SCIP_CALL(createNode(dec,&newNode)); + + spqr_node splitNode = newRow->reducedMembers[leaf].splitNode; + + spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode); + spqr_arc iterArc = firstNodeArc; + do { + spqr_node otherHead = findArcHead(dec, iterArc); + spqr_node otherTail = findArcTail(dec, iterArc); + spqr_node otherEnd = otherHead == splitNode ? otherTail : otherHead; + spqr_arc nextArc = getNextNodeArc(dec, iterArc, splitNode); //Need to do this before we modify the arc + + SCIP_Bool isCut = newRow->isArcCut[iterArc]; + SCIP_Bool isMoveColor = newRow->nodeColors[otherEnd] == COLOR_SOURCE; + SCIP_Bool changeArcEnd = isCut == isMoveColor; + if (changeArcEnd) { + if (otherHead == splitNode) { + changeArcHead(dec, iterArc, otherHead, newNode); + } else { + changeArcTail(dec, iterArc, otherTail, newNode); + } + } + //Ugly hack to make sure we can iterate neighbourhood whilst changing arc ends. + newRow->nodeColors[otherEnd] = UNCOLORED; + spqr_arc previousArc = iterArc; + iterArc = nextArc; + if (iterArc == firstNodeArc) { + break; + } + if (changeArcEnd && previousArc == firstNodeArc) { + firstNodeArc = iterArc; + } + } while (TRUE); + newRowInformation->head = newNode; + newRowInformation->tail = splitNode; + newRowInformation->member = member; + newRowInformation->reversed = FALSE; + newRowInformation->representative = findArcSign(dec,iterArc).representative; + + return SCIP_OKAY; +} + +static SCIP_RETCODE mergeSplitMemberIntoParent(SCIP_NETWORKDECOMP *dec, + SCIP_NETWORKROWADDITION * newRow, + spqr_member member, + spqr_member parent, + spqr_arc parentToChild, + spqr_arc childToParent, + SCIP_Bool headToHead, + spqr_node parentNode, + spqr_node childNode, + spqr_member * mergedMember, + spqr_node * arcNodeOne, + spqr_node * arcNodeTwo, + spqr_node * thirdNode) { + assert(dec); + assert(SPQRmemberIsValid(member)); + assert(memberIsRepresentative(dec, member)); + assert(SPQRmemberIsValid(parent)); + assert(memberIsRepresentative(dec, parent)); + assert(findMemberParentNoCompression(dec, member) == parent); + assert(markerOfParent(dec, member) == parentToChild); + assert(markerToParent(dec, member) == childToParent); + + removeArcFromMemberArcList(dec, parentToChild, parent); + removeArcFromMemberArcList(dec, childToParent, member); + + spqr_node parentArcNodes[2] = {findEffectiveArcTail(dec, parentToChild), findEffectiveArcHead(dec, parentToChild)}; + spqr_node childArcNodes[2] = {findEffectiveArcTail(dec, childToParent), findEffectiveArcHead(dec, childToParent)}; + + clearArcHeadAndTail(dec, parentToChild); + clearArcHeadAndTail(dec, childToParent); + + spqr_node first = childArcNodes[headToHead ? 0 : 1]; + spqr_node second = childArcNodes[headToHead ? 1 : 0]; + { + spqr_node newNode = mergeNodes(dec, parentArcNodes[0], first); + spqr_node toRemoveFrom = newNode == first ? parentArcNodes[0] : first; + mergeNodeArcList(dec, newNode, toRemoveFrom); + *arcNodeOne = newNode; + newRow->nodeColors[toRemoveFrom] = UNCOLORED; + } + { + spqr_node newNode = mergeNodes(dec, parentArcNodes[1], second); + spqr_node toRemoveFrom = newNode == second ? parentArcNodes[1] : second; + mergeNodeArcList(dec, newNode, toRemoveFrom); + *arcNodeTwo = newNode; + newRow->nodeColors[toRemoveFrom] = UNCOLORED; + } + { + spqr_node newNode = mergeNodes(dec, parentNode, childNode); + spqr_node toRemoveFrom = newNode == childNode ? parentNode : childNode; + mergeNodeArcList(dec,newNode,toRemoveFrom); + *thirdNode = newNode; + newRow->nodeColors[toRemoveFrom] = UNCOLORED; + } + + spqr_member newMember = mergeMembers(dec, member, parent); + spqr_member toRemoveFrom = newMember == member ? parent : member; + mergeMemberArcList(dec, newMember, toRemoveFrom); + if (toRemoveFrom == parent) { + updateMemberParentInformation(dec, newMember, toRemoveFrom); + } + updateMemberType(dec, newMember, SPQR_MEMBERTYPE_RIGID); + *mergedMember = newMember; + return SCIP_OKAY; +} +static SCIP_RETCODE splitAndMergeSeries(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id largeMember, reduced_member_id smallMember, + SCIP_Bool largeIsParent, NewRowInformation * const newRowInformation, + spqr_member member){ + SCIP_Bool isCut = FALSE; + spqr_member mergingMember = SPQR_INVALID_MEMBER; + spqr_arc nonVirtualArc = SPQR_INVALID_ARC; + SCIP_CALL(splitSeriesMergingRowAddition(dec,newRow,smallMember,member,&mergingMember,&isCut,&nonVirtualArc)); + assert(getNumMemberArcs(dec,mergingMember) == 3); + + //create the split series. There's two possible configurations, based on whether it contains a cut edge or not + spqr_node a = SPQR_INVALID_NODE; + spqr_node b = SPQR_INVALID_NODE; + spqr_node c = SPQR_INVALID_NODE; + spqr_node d = SPQR_INVALID_NODE; + SCIP_CALL(createNode(dec,&a)); + SCIP_CALL(createNode(dec,&b)); + SCIP_CALL(createNode(dec,&c)); + SCIP_CALL(createNode(dec,&d)); + + spqr_arc splitArc = newRow->reducedMembers[smallMember].splitArc; + + { + SCIP_Bool splitHead = newRow->reducedMembers[smallMember].splitHead; + spqr_arc firstArc = getFirstMemberArc(dec,mergingMember); + spqr_arc arc = firstArc; + SCIP_Bool splitReversed = arcIsReversedNonRigid(dec,splitArc); + do{ + if(arc == splitArc){ + if(splitHead){ + setArcHeadAndTail(dec,splitArc,b,a); + }else{ + setArcHeadAndTail(dec,splitArc,a,b); + } + }else if (arc == nonVirtualArc){ + if ((arcIsReversedNonRigid(dec, arc) == splitReversed) == splitHead) { + setArcHeadAndTail(dec, arc, a, d); + } else { + setArcHeadAndTail(dec, arc, d, a); + } + }else{ + spqr_node otherNode = cutArcIsValid(newRow->reducedMembers[smallMember].firstCutArc) ? c : b; + if((arcIsReversedNonRigid(dec,arc) == splitReversed) == splitHead){ + setArcHeadAndTail(dec, arc, d, otherNode); + }else{ + setArcHeadAndTail(dec, arc, otherNode, d); + } + } + arcSetReversed(dec,arc,FALSE); + arcSetRepresentative(dec,arc,splitArc); + arc = getNextMemberArc(dec,arc); + }while(arc != firstArc); + arcSetRepresentative(dec,splitArc,SPQR_INVALID_ARC); + } + + spqr_member otherMember = newRowInformation->member; + spqr_arc otherMarker = largeIsParent ? markerOfParent(dec,mergingMember) : markerToParent(dec,otherMember); + + assert(nodeIsRepresentative(dec,newRowInformation->tail)); + assert(nodeIsRepresentative(dec,newRowInformation->head)); + spqr_node splitNode = newRow->reducedMembers[smallMember].splitHead ? findEffectiveArcHead(dec,otherMarker) : + findEffectiveArcTail(dec,otherMarker); + + spqr_node otherNode = splitNode == newRowInformation->head ? newRowInformation->tail : newRowInformation->head; + assert(splitNode == newRowInformation->head || splitNode == newRowInformation->tail); + newRowInformation->representative = mergeArcSigns(dec,newRowInformation->representative,splitArc,FALSE); + + spqr_member mergedMember = SPQR_INVALID_MEMBER; + spqr_node arcNodeOne; + spqr_node arcNodeTwo; + spqr_node thirdNode; + if(largeIsParent){ + SCIP_CALL(mergeSplitMemberIntoParent(dec,newRow,mergingMember,otherMember,otherMarker,splitArc,TRUE, + otherNode,c,&mergedMember, + &arcNodeOne, + &arcNodeTwo, + &thirdNode)); + }else{ + SCIP_CALL(mergeSplitMemberIntoParent(dec,newRow,otherMember,mergingMember, splitArc, otherMarker,TRUE, + c,otherNode,&mergedMember, + &arcNodeOne, + &arcNodeTwo, + &thirdNode)); + } + newRow->reducedMembers[smallMember].member = mergedMember; + + newRowInformation->member = mergedMember; + SCIP_Bool splitIsReferenceHead = newRow->reducedMembers[smallMember].splitHead; + SCIP_Bool splitIsNewRowHead = splitNode == newRowInformation->head; + if(!splitIsReferenceHead && !splitIsNewRowHead){ + newRowInformation->head = thirdNode; + newRowInformation->tail = arcNodeOne; + }else if (!splitIsReferenceHead && splitIsNewRowHead){ + newRowInformation->head = arcNodeOne; + newRowInformation->tail = thirdNode; + }else if(splitIsReferenceHead && !splitIsNewRowHead){ + newRowInformation->head = thirdNode; + newRowInformation->tail = arcNodeTwo; + }else if (splitIsReferenceHead && splitIsNewRowHead){ + newRowInformation->head = arcNodeTwo; + newRowInformation->tail = thirdNode; + } + + return SCIP_OKAY; +} + +static SCIP_RETCODE splitAndMergeParallel(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id largeMember, reduced_member_id smallMember, + SCIP_Bool largeIsParent, NewRowInformation * const newRowInformation, + spqr_member member){ + spqr_member mergeMember = SPQR_INVALID_MEMBER; + spqr_arc cutRepresentative = SPQR_INVALID_ARC; + SCIP_CALL(splitParallelMerging(dec,newRow,smallMember,member,&mergeMember,&cutRepresentative)); + newRow->reducedMembers[smallMember].member = mergeMember; + + spqr_node firstNode = SPQR_INVALID_NODE; + spqr_node secondNode = SPQR_INVALID_NODE; + spqr_node thirdNode = SPQR_INVALID_NODE; + SCIP_CALL(createNode(dec,&firstNode)); + SCIP_CALL(createNode(dec,&secondNode)); + SCIP_CALL(createNode(dec,&thirdNode)); + + spqr_arc splitArc = newRow->reducedMembers[smallMember].splitArc; + assert(findArcMemberNoCompression(dec,splitArc) == mergeMember); + SCIP_Bool splitArcReversed = arcIsReversedNonRigid(dec,splitArc); + SCIP_Bool splitHead = newRow->reducedMembers[smallMember].splitHead; + + spqr_node splitArcHead = splitArcReversed ? secondNode : firstNode; + spqr_node splitArcTail = splitArcReversed ? firstNode : secondNode; + spqr_node otherNode = splitHead ? splitArcTail : splitArcHead; + + spqr_arc first_arc = getFirstMemberArc(dec, mergeMember); + spqr_arc arc = first_arc; + + do { + if(arc != cutRepresentative){ + if(arcIsReversedNonRigid(dec,arc)){ + setArcHeadAndTail(dec,arc,secondNode,firstNode); + }else{ + setArcHeadAndTail(dec,arc,firstNode,secondNode); + } + }else{ + if ((arcIsReversedNonRigid(dec, arc) == splitArcReversed) == splitHead) { + setArcHeadAndTail(dec, arc, thirdNode, otherNode); + } else { + setArcHeadAndTail(dec, arc, otherNode, thirdNode); + } + } + arcSetReversed(dec,arc,FALSE); + arcSetRepresentative(dec,arc,splitArc); + arc = getNextMemberArc(dec, arc); + } while (arc != first_arc); + arcSetRepresentative(dec,splitArc,SPQR_INVALID_ARC); + + spqr_member otherMember = newRowInformation->member; + spqr_arc otherMarker = largeIsParent ? markerOfParent(dec,mergeMember) : markerToParent(dec,otherMember); + + assert(nodeIsRepresentative(dec,newRowInformation->tail)); + assert(nodeIsRepresentative(dec,newRowInformation->head)); + spqr_node largeSplitNode = newRow->reducedMembers[smallMember].splitHead ? findEffectiveArcHead(dec,otherMarker) : + findEffectiveArcTail(dec,otherMarker); + + spqr_node largeOtherNode = largeSplitNode == newRowInformation->head ? newRowInformation->tail : newRowInformation->head; + assert(largeSplitNode == newRowInformation->head || largeSplitNode == newRowInformation->tail); + + newRowInformation->representative = mergeArcSigns(dec,newRowInformation->representative,splitArc,FALSE); + + spqr_member mergedMember = SPQR_INVALID_MEMBER; + spqr_node arcNodeOne; + spqr_node arcNodeTwo; + spqr_node mergeNodeThree; + if(largeIsParent){ + SCIP_CALL(mergeSplitMemberIntoParent(dec,newRow,mergeMember,otherMember,otherMarker,splitArc,TRUE, + largeOtherNode,thirdNode,&mergedMember, + &arcNodeOne, + &arcNodeTwo, + &mergeNodeThree)); + }else{ + SCIP_CALL(mergeSplitMemberIntoParent(dec,newRow,otherMember,mergeMember, splitArc, otherMarker,TRUE, + thirdNode,largeOtherNode,&mergedMember, + &arcNodeOne, + &arcNodeTwo, + &mergeNodeThree)); + } + + + newRowInformation->member = mergedMember; + + SCIP_Bool splitIsReferenceHead = newRow->reducedMembers[smallMember].splitHead; + SCIP_Bool splitIsNewRowHead = largeSplitNode == newRowInformation->head; + if(!splitIsReferenceHead && !splitIsNewRowHead){ + newRowInformation->head = mergeNodeThree; + newRowInformation->tail = arcNodeOne; + }else if (!splitIsReferenceHead && splitIsNewRowHead){ + newRowInformation->head = arcNodeOne; + newRowInformation->tail = mergeNodeThree; + }else if(splitIsReferenceHead && !splitIsNewRowHead){ + newRowInformation->head = mergeNodeThree; + newRowInformation->tail = arcNodeTwo; + }else if (splitIsReferenceHead && splitIsNewRowHead){ + newRowInformation->head = arcNodeTwo; + newRowInformation->tail = mergeNodeThree; + } + + return SCIP_OKAY; +} +static SCIP_RETCODE splitAndMergeRigid(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id largeMember, reduced_member_id smallMember, + SCIP_Bool largeIsParent, NewRowInformation * const newRowInformation, + spqr_member member){ + + spqr_node newNode = SPQR_INVALID_NODE; //Sink node + SCIP_CALL(createNode(dec,&newNode)); + + spqr_member smallMemberMember = member; + spqr_member largeMemberMember = newRowInformation->member; + + spqr_arc smallMarker = largeIsParent ? markerToParent(dec,smallMemberMember) : markerOfParent(dec,largeMemberMember); + spqr_arc largeMarker = largeIsParent ? markerOfParent(dec,smallMemberMember) : markerToParent(dec,largeMemberMember); + + spqr_node splitNode = newRow->reducedMembers[smallMember].splitNode; + spqr_node smallOtherNode = newNode; + + if(newRow->reducedMembers[smallMember].numCutArcs != 0) { + spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode); + spqr_arc iterArc = firstNodeArc; + do { + spqr_node otherHead = findArcHead(dec, iterArc); + spqr_node otherTail = findArcTail(dec, iterArc); + spqr_node otherEnd = otherHead == splitNode ? otherTail : otherHead; + spqr_arc nextArc = getNextNodeArc(dec, iterArc, splitNode); //Need to do this before we modify the arc + + SCIP_Bool isCut = newRow->isArcCut[iterArc]; + SCIP_Bool isMoveColor = newRow->nodeColors[otherEnd] == COLOR_SOURCE; + SCIP_Bool changeArcEnd = isCut == isMoveColor; + if (changeArcEnd) { + if (otherHead == splitNode) { + changeArcHead(dec, iterArc, otherHead, newNode); + } else { + changeArcTail(dec, iterArc, otherTail, newNode); + } + if(iterArc == smallMarker){ + smallOtherNode = splitNode; + } + } + newRow->nodeColors[otherEnd] = UNCOLORED; + //Ugly hack to make sure we can iterate neighbourhood whilst changing arc ends. + spqr_arc previousArc = iterArc; + iterArc = nextArc; + if (iterArc == firstNodeArc) { + break; + } + if (changeArcEnd && previousArc == firstNodeArc) { + firstNodeArc = iterArc; + } + } while (TRUE); + } + + spqr_arc representative = findArcSign(dec,smallMarker).representative; + + newRowInformation->representative = mergeArcSigns(dec,newRowInformation->representative,representative, + newRow->reducedMembers[smallMember].willBeReversed); + + spqr_node largeMarkerHead = findArcHead(dec,largeMarker); + spqr_node largeMarkerTail = findArcTail(dec,largeMarker); + if(findArcSign(dec,largeMarker).reversed){ + spqr_node temp = largeMarkerHead; + largeMarkerHead = largeMarkerTail; + largeMarkerTail = temp; + } + assert(newRowInformation->head == largeMarkerHead || newRowInformation->head == largeMarkerTail || + newRowInformation->tail == largeMarkerHead || newRowInformation->tail == largeMarkerTail); + spqr_node largeOtherNode = (newRowInformation->head == largeMarkerHead || newRowInformation->head == largeMarkerTail) + ? newRowInformation->tail : newRowInformation->head; + + spqr_member mergedMember = SPQR_INVALID_MEMBER; + spqr_node arcNodeOne; + spqr_node arcNodeTwo; + spqr_node mergeNodeThree; + if(largeIsParent){ + SCIP_CALL(mergeSplitMemberIntoParent(dec,newRow,smallMemberMember,largeMemberMember,largeMarker,smallMarker,TRUE, + largeOtherNode,smallOtherNode,&mergedMember, + &arcNodeOne, + &arcNodeTwo, + &mergeNodeThree)); + }else{ + SCIP_CALL(mergeSplitMemberIntoParent(dec,newRow,largeMemberMember,smallMemberMember,smallMarker,largeMarker,TRUE, + smallOtherNode,largeOtherNode,&mergedMember, + &arcNodeOne, + &arcNodeTwo, + &mergeNodeThree)); + } + newRowInformation->member = mergedMember; + + SCIP_Bool otherIsHead = largeOtherNode == newRowInformation->head; + SCIP_Bool adjacentToMarkerHead = (newRowInformation->tail == largeMarkerHead || newRowInformation->head == largeMarkerHead); + if(adjacentToMarkerHead){ + if(otherIsHead){ + newRowInformation->head = mergeNodeThree; + newRowInformation->tail = arcNodeTwo; + }else{ + newRowInformation->head = arcNodeTwo; + newRowInformation->tail = mergeNodeThree; + } + }else{ + if(otherIsHead){ + newRowInformation->head = mergeNodeThree; + newRowInformation->tail = arcNodeOne; + }else{ + newRowInformation->head = arcNodeOne; + newRowInformation->tail = mergeNodeThree; + } + } + + return SCIP_OKAY; +} +static SCIP_RETCODE splitAndMerge(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id largeMember, reduced_member_id smallMember, + SCIP_Bool largeIsParent, NewRowInformation * const newRowInformation){ + spqr_member member = newRow->reducedMembers[smallMember].member; + switch(getMemberType(dec,member)){ + case SPQR_MEMBERTYPE_RIGID:{ + SCIP_CALL(splitAndMergeRigid(dec,newRow,largeMember,smallMember,largeIsParent,newRowInformation,member)); + break; + } + case SPQR_MEMBERTYPE_PARALLEL:{ + SCIP_CALL(splitAndMergeParallel(dec,newRow,largeMember,smallMember,largeIsParent,newRowInformation,member)); + break; + } + case SPQR_MEMBERTYPE_SERIES:{ + SCIP_CALL(splitAndMergeSeries(dec,newRow,largeMember,smallMember,largeIsParent,newRowInformation,member)); + break; + } + default: + assert(FALSE); + newRow->remainsNetwork = FALSE; + } + return SCIP_OKAY; +} +static SCIP_RETCODE mergeChildrenNodes(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + reduced_member_id parent, reduced_member_id node, + reduced_member_id skipNode, NewRowInformation * const newRowInformation +){ + if(node == skipNode || newRow->reducedMembers[node].type == TYPE_PROPAGATED){ + return SCIP_OKAY; + } + //check merging + splitAndMerge(dec,newRow,parent,node,TRUE,newRowInformation); + + //merge all children + for (int i = 0; i < newRow->reducedMembers[node].numChildren; ++i) { + children_idx idx = newRow->reducedMembers[node].firstChild + i; + reduced_member_id child = newRow->childrenStorage[idx]; + SCIP_CALL(mergeChildrenNodes(dec,newRow,node,child,skipNode,newRowInformation)); + } + return SCIP_OKAY; +} +static SCIP_RETCODE mergeTree(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, reduced_member_id root, + NewRowInformation * const newRowInformation){ + + //We use the same ordering as when finding + //go to a leaf. We need to start in a leaf to avoid the ambiguity of choosing an orientation + //in members which have no cut arcs; otherwise, we might choose the wrong one + reduced_member_id leaf = root; + while(newRow->reducedMembers[leaf].numChildren != newRow->reducedMembers[leaf].numPropagatedChildren){ + for(int i = 0; i < newRow->reducedMembers[leaf].numChildren;++i){ + children_idx idx = newRow->reducedMembers[leaf].firstChild + i; + reduced_member_id child = newRow->childrenStorage[idx]; + if(newRow->reducedMembers[child].type != TYPE_PROPAGATED){ + leaf = child; + break; + } + } + } + SCIP_CALL(splitFirstLeaf(dec,newRow,leaf,newRowInformation)); + + reduced_member_id baseNode = leaf; + reduced_member_id nextNode = newRow->reducedMembers[baseNode].parent; + + while(reducedMemberIsValid(nextNode)){ + //check this node + SCIP_CALL(splitAndMerge(dec,newRow,baseNode,nextNode,FALSE,newRowInformation)); + + //Add other nodes in the subtree + for (int i = 0; i < newRow->reducedMembers[nextNode].numChildren; ++i) { + children_idx idx = newRow->reducedMembers[nextNode].firstChild + i; + reduced_member_id child = newRow->childrenStorage[idx]; + SCIP_CALL(mergeChildrenNodes(dec,newRow,nextNode,child,baseNode,newRowInformation)); + } + + //Move up one layer + baseNode = nextNode; + nextNode = newRow->reducedMembers[nextNode].parent; + } + + return SCIP_OKAY; +} +static SCIP_RETCODE transformComponentRowAddition(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, + SPQRRowReducedComponent* component, NewRowInformation * const newRowInformation){ + assert(component); + if(newRow->reducedMembers[component->root].numChildren == newRow->reducedMembers[component->root].numPropagatedChildren){ + //No merging necessary, only a single component + reduced_member_id reducedMember = component->root; + assert(reducedMemberIsValid(reducedMember)); + spqr_member member = newRow->reducedMembers[reducedMember].member; + SPQRMemberType type = getMemberType(dec, member); + + switch(type){ + case SPQR_MEMBERTYPE_RIGID: + SCIP_CALL(transformSingleRigid(dec,newRow,reducedMember,member,newRowInformation)); + break; + case SPQR_MEMBERTYPE_PARALLEL: { + SCIP_CALL(transformSingleParallel(dec,newRow,reducedMember,member,newRowInformation)); + break; + } + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_SERIES: { + + newRowInformation->member = member; + cut_arc_id cutArc = newRow->reducedMembers[reducedMember].firstCutArc; + spqr_arc arc = newRow->cutArcs[cutArc].arc; + newRowInformation->reversed = arcIsReversedNonRigid(dec,arc) == newRow->cutArcs[cutArc].arcReversed; + if(type == SPQR_MEMBERTYPE_LOOP ){ + if(getNumMemberArcs(dec,member) == 2){ + changeLoopToSeries(dec,member); + }else{ + + } + } + break; + } + default: + assert(FALSE); + break; + } + + return SCIP_OKAY; + } + + SCIP_CALL(mergeTree(dec,newRow,component->root,newRowInformation)); + + return SCIP_OKAY; +} + + +SCIP_RETCODE SCIPNetworkRowAdditionCreate(SCIP* env, SCIP_NETWORKROWADDITION** pNewRow ){ + assert(env); + SCIP_CALL(SCIPallocBlockMemory(env,pNewRow)); + SCIP_NETWORKROWADDITION * newRow = *pNewRow; + + newRow->remainsNetwork = TRUE; + + newRow->reducedMembers = NULL; + newRow->memReducedMembers = 0; + newRow->numReducedMembers = 0; + + newRow->reducedComponents = NULL; + newRow->memReducedComponents = 0; + newRow->numReducedComponents = 0; + + newRow->memberInformation = NULL; + newRow->memMemberInformation = 0; + newRow->numMemberInformation = 0; + + newRow->cutArcs = NULL; + newRow->memCutArcs = 0; + newRow->numCutArcs = 0; + newRow->firstOverallCutArc = INVALID_CUT_ARC; + + newRow->childrenStorage = NULL; + newRow->memChildrenStorage = 0; + newRow->numChildrenStorage = 0; + + newRow->newRowIndex = SPQR_INVALID_ROW; + + newRow->newColumnArcs = NULL; + newRow->newColumnReversed = NULL; + newRow->memColumnArcs = 0; + newRow->numColumnArcs = 0; + + newRow->leafMembers = NULL; + newRow->numLeafMembers = 0; + newRow->memLeafMembers = 0; + + newRow->decompositionColumnArcs = NULL; + newRow->decompositionColumnArcReversed = NULL; + newRow->memDecompositionColumnArcs = 0; + newRow->numDecompositionColumnArcs = 0; + + newRow->isArcCut = NULL; + newRow->isArcCutReversed = NULL; + newRow->memIsArcCut = 0; + newRow->numIsArcCut = 0; + + newRow->nodeColors = NULL; + newRow->memNodeColors = 0; + + newRow->articulationNodes = NULL; + newRow->memArticulationNodes = 0; + newRow->numArticulationNodes = 0; + + newRow->articulationNodeSearchInfo = NULL; + newRow->memNodeSearchInfo = 0; + + newRow->crossingPathCount = NULL; + newRow->memCrossingPathCount = 0; + + newRow->intersectionDFSData = NULL; + newRow->memIntersectionDFSData = 0; + + newRow->colorDFSData = NULL; + newRow->memColorDFSData = 0; + + newRow->artDFSData = NULL; + newRow->memArtDFSData = 0; + + newRow->createReducedMembersCallstack = NULL; + newRow->memCreateReducedMembersCallstack = 0; + + newRow->intersectionPathDepth = NULL; + newRow->memIntersectionPathDepth = 0; + + newRow->intersectionPathParent = NULL; + newRow->memIntersectionPathParent = 0; + + newRow->mergeTreeCallData = NULL; + newRow->memMergeTreeCallData = 0; + + return SCIP_OKAY; +} + +void SCIPNetworkRowAdditionFree(SCIP* env, SCIP_NETWORKROWADDITION ** pNewRow){ + assert(*pNewRow); + + SCIP_NETWORKROWADDITION * newRow = *pNewRow; + + SCIPfreeBlockMemoryArray(env,&newRow->createReducedMembersCallstack,newRow->memCreateReducedMembersCallstack); + SCIPfreeBlockMemoryArray(env,&newRow->artDFSData,newRow->memArtDFSData); + SCIPfreeBlockMemoryArray(env,&newRow->colorDFSData,newRow->memColorDFSData); + SCIPfreeBlockMemoryArray(env,&newRow->mergeTreeCallData,newRow->memMergeTreeCallData); + SCIPfreeBlockMemoryArray(env,&newRow->intersectionDFSData,newRow->memIntersectionDFSData); + SCIPfreeBlockMemoryArray(env,&newRow->intersectionPathParent,newRow->memIntersectionPathParent); + SCIPfreeBlockMemoryArray(env,&newRow->intersectionPathDepth,newRow->memIntersectionPathDepth); + SCIPfreeBlockMemoryArray(env,&newRow->crossingPathCount,newRow->memCrossingPathCount); + SCIPfreeBlockMemoryArray(env,&newRow->articulationNodeSearchInfo,newRow->memNodeSearchInfo); + SCIPfreeBlockMemoryArray(env,&newRow->articulationNodes,newRow->memArticulationNodes); + SCIPfreeBlockMemoryArray(env,&newRow->nodeColors,newRow->memNodeColors); + SCIPfreeBlockMemoryArray(env,&newRow->isArcCut,newRow->memIsArcCut); + SCIPfreeBlockMemoryArray(env,&newRow->isArcCutReversed,newRow->memIsArcCut); + SCIPfreeBlockMemoryArray(env,&newRow->decompositionColumnArcs,newRow->memDecompositionColumnArcs); + SCIPfreeBlockMemoryArray(env,&newRow->decompositionColumnArcReversed,newRow->memDecompositionColumnArcs); + SCIPfreeBlockMemoryArray(env,&newRow->newColumnArcs,newRow->memColumnArcs); + SCIPfreeBlockMemoryArray(env,&newRow->newColumnReversed,newRow->memColumnArcs); + SCIPfreeBlockMemoryArray(env,&newRow->childrenStorage,newRow->memChildrenStorage); + SCIPfreeBlockMemoryArray(env,&newRow->cutArcs,newRow->memCutArcs); + SCIPfreeBlockMemoryArray(env,&newRow->memberInformation,newRow->memMemberInformation); + SCIPfreeBlockMemoryArray(env,&newRow->reducedComponents,newRow->memReducedComponents); + SCIPfreeBlockMemoryArray(env,&newRow->reducedMembers,newRow->memReducedMembers); + SCIPfreeBlockMemoryArray(env,&newRow->leafMembers, newRow->memLeafMembers); + SCIPfreeBlockMemory(env,pNewRow); +} + +SCIP_RETCODE SCIPNetworkRowAdditionCheck(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKROWADDITION * newRow, + const spqr_row row, const spqr_col * columns, const double * columnValues, + size_t numColumns){ + assert(dec); + assert(newRow); + assert(numColumns == 0 || columns ); + + newRow->remainsNetwork = TRUE; + cleanUpPreviousIteration(dec,newRow); + + SCIP_CALL(newRowUpdateRowInformation(dec,newRow,row,columns,columnValues,numColumns)); + SCIP_CALL(constructRowReducedDecomposition(dec,newRow)); + SCIP_CALL(createReducedDecompositionCutArcs(dec,newRow)); + + SCIP_CALL(determineLeafReducedMembers(dec,newRow)); + SCIP_CALL(allocateRigidSearchMemory(dec,newRow)); + SCIP_CALL(allocateTreeSearchMemory(dec,newRow)); + //Check for each component if the cut arcs propagate through a row tree marker to a cut arc in another component + //From the leafs inward. + propagateComponents(dec,newRow); + //It can happen that we are not graphic by some of the checked components. + //In that case, further checking may lead to errors as some invariants that the code assumes will be broken. + if(newRow->remainsNetwork){ + for (int i = 0; i < newRow->numReducedComponents; ++i) { + determineMergeableTypes(dec,newRow,newRow->reducedComponents[i].root); + //exit early if one is not graphic + if(!newRow->remainsNetwork){ + break; + } + } + } + + cleanUpRowMemberInformation(newRow); + + return SCIP_OKAY; +} + +SCIP_RETCODE SCIPNetworkRowAdditionAdd(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow){ + assert(newRow->remainsNetwork); + if(newRow->numReducedComponents == 0){ + spqr_member newMember = SPQR_INVALID_MEMBER; + SCIP_CALL(createStandaloneParallel(dec,newRow->newColumnArcs, newRow->newColumnReversed, + newRow->numColumnArcs,newRow->newRowIndex,&newMember)); + }else if (newRow->numReducedComponents == 1){ + NewRowInformation information = emptyNewRowInformation(); + SCIP_CALL(transformComponentRowAddition(dec,newRow,&newRow->reducedComponents[0],&information)); + + if(newRow->numColumnArcs == 0){ + spqr_arc rowArc = SPQR_INVALID_ARC; + SCIP_CALL(createRowArc(dec,information.member,&rowArc,newRow->newRowIndex,information.reversed)); + if(SPQRnodeIsValid(information.head)){ + assert(SPQRnodeIsValid(information.tail)); + assert(SPQRarcIsValid(information.representative)); + setArcHeadAndTail(dec,rowArc,findNode(dec,information.head),findNode(dec,information.tail)); + arcSetRepresentative(dec,rowArc,information.representative); + arcSetReversed(dec,rowArc,information.reversed != arcIsReversedNonRigid(dec,information.representative)); + } + }else{ + spqr_member new_row_parallel = SPQR_INVALID_MEMBER; + SCIP_CALL(createConnectedParallel(dec,newRow->newColumnArcs,newRow->newColumnReversed,newRow->numColumnArcs, + newRow->newRowIndex,&new_row_parallel)); + spqr_arc markerArc = SPQR_INVALID_ARC; + spqr_arc ignore = SPQR_INVALID_ARC; + SCIP_CALL(createMarkerPairWithReferences(dec,information.member,new_row_parallel,TRUE, + information.reversed,FALSE, + &markerArc,&ignore)); + if(SPQRnodeIsValid(information.head)){ + assert(SPQRnodeIsValid(information.tail)); + assert(SPQRarcIsValid(information.representative)); + setArcHeadAndTail(dec,markerArc,findNode(dec,information.head),findNode(dec,information.tail)); + arcSetRepresentative(dec,markerArc,information.representative); + arcSetReversed(dec,markerArc,information.reversed != arcIsReversedNonRigid(dec,information.representative)); + } + } + if(getMemberType(dec,information.member) == SPQR_MEMBERTYPE_LOOP){ + assert(getNumMemberArcs(dec,information.member) == 2 || getNumMemberArcs(dec,information.member) == 3); + if(getNumMemberArcs(dec,information.member) == 3){ + changeLoopToSeries(dec,information.member); + } + } + }else{ +#ifndef NDEBUG + int numDecComponentsBefore = numConnectedComponents(dec); +#endif + spqr_member new_row_parallel = SPQR_INVALID_MEMBER; + SCIP_CALL(createConnectedParallel(dec,newRow->newColumnArcs,newRow->newColumnReversed,newRow->numColumnArcs, + newRow->newRowIndex,&new_row_parallel)); + for (int i = 0; i < newRow->numReducedComponents; ++i) { + NewRowInformation information = emptyNewRowInformation(); + + SCIP_CALL(transformComponentRowAddition(dec,newRow,&newRow->reducedComponents[i],&information)); + if(getMemberType(dec,information.member) == SPQR_MEMBERTYPE_LOOP){ + assert(getNumMemberArcs(dec,information.member) == 1); + spqr_arc arc = getFirstMemberArc(dec,information.member); + assert(newRow->isArcCut[arc]); + moveArcToNewMember(dec, arc,information.member,new_row_parallel); + arcSetReversed(dec,arc,newRow->isArcCutReversed[arc]); + dec->members[information.member].type = SPQR_MEMBERTYPE_UNASSIGNED; + }else{ + reorderComponent(dec,information.member); //Make sure the new component is the root of the local decomposition tree + spqr_arc markerArc = SPQR_INVALID_ARC; + spqr_arc ignore = SPQR_INVALID_ARC; + SCIP_CALL(createMarkerPairWithReferences(dec,new_row_parallel,information.member,FALSE, + FALSE,information.reversed, + &ignore,&markerArc)); + if(SPQRnodeIsValid(information.head)){ + assert(SPQRnodeIsValid(information.tail)); + assert(SPQRarcIsValid(information.representative)); + setArcHeadAndTail(dec,markerArc,findNode(dec,information.head),findNode(dec,information.tail)); + arcSetRepresentative(dec,markerArc,information.representative); + arcSetReversed(dec,markerArc,information.reversed != arcIsReversedNonRigid(dec,information.representative)); + } + } + } + decreaseNumConnectedComponents(dec,newRow->numReducedComponents-1); + assert(numConnectedComponents(dec) == (numDecComponentsBefore - newRow->numReducedComponents + 1)); + } + return SCIP_OKAY; +} + +SCIP_Bool SCIPNetworkRowAdditionRemainsNetwork(const SCIP_NETWORKROWADDITION *newRow){ + return newRow->remainsNetwork; +} diff --git a/src/scip/network.h b/src/scip/network.h new file mode 100644 index 0000000000..0826908a6e --- /dev/null +++ b/src/scip/network.h @@ -0,0 +1,198 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* This file is part of the program and library */ +/* SCIP --- Solving Constraint Integer Programs */ +/* */ +/* Copyright (c) 2002-2024 Zuse Institute Berlin (ZIB) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/* You should have received a copy of the Apache-2.0 license */ +/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/**@file network.h + * @ingroup OTHER_CFILES + * @brief Methods for detecting network matrices + * @author Rolf van der Hulst + * + * This file contains algorithms for incrementally growing (augmenting) network matrices, + * which are a large subclass of totally unimodular matrices. + * + * A $\pm 1$ matrix is a network matrix if there exists a directed graph G with a (not necessarily rooted) tree T such that + * for each arc \f$ a \in A\setminus T\f$ the column \f$ A_{:,a} \f$ corresponds to the oriented path between the tail + * and the head of \f$ a \f$. The main difficulty with detecting network matrices is that there may exist many graphs + * that realize a certain matrix. The algorithms in this file maintain and update an SPQR tree, + * which is a graph decomposition that represents all graphs that correspond to the current network matrix. + * + * TODO: add incidence matrix methods + * A large subclass of network matrices are node-arc incidence matrices, which are matrices that have at most + * one +1 entry and one -1 entry in every column, which correspond to the in and out-node of an arc. + * This file contains algorithms to detect reflected node-arc incidence matrices, where rows can be optionally negated. + * Although network matrices are a strictly larger class of totally unimodular matrices, reflected node-arc incidence + * matrices can be detected more quickly and are commonly used. + * + * Note that all addition algorithms expect that each nonzero is given exactly once and not more often; in particular, + * it is up to the user to ensure this when using both column and row addition steps. + * + * The column addition for network matrices is based on: + * + * The row addition for network matrices is based on: + * + * The row addition for incidence matrices is based on an adaptation from: + * + * + */ + +/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/ + +#ifndef __SCIP_NETWORK_H__ +#define __SCIP_NETWORK_H__ + +#include "scip.h" //TODO; reduce to minimal set of includes, probably only SCIP and memory functions are needed + +#ifdef cplusplus +extern "C" { +#endif + +/** + * This class stores the Network decomposition using an SPQR tree + */ +typedef struct SCIP_NetworkDecomposition SCIP_NETWORKDECOMP; + +SCIP_RETCODE SCIPNetworkDecompositionCreate(SCIP * env, SCIP_NETWORKDECOMP **pDecomposition, int numRows, int numColumns); + +void SCIPNetworkDecompositionFree(SCIP_NETWORKDECOMP **pDecomposition); + +/** + * Returns if the Network decomposition contains the given row + */ +SCIP_Bool SCIPNetworkDecompositionContainsRow(const SCIP_NETWORKDECOMP * decomposition, int row); + +/** + * Returns if the Network decomposition contains the given column + */ +SCIP_Bool SCIPNetworkDecompositionContainsColumn(const SCIP_NETWORKDECOMP *decomposition, int column); + +/** + * Checks if the Network decomposition of the graph is minimal. This method should only be used in tests. + */ +SCIP_Bool SCIPNetworkDecompositionIsMinimal(const SCIP_NETWORKDECOMP * decomposition); + +//TODO: method to convert decomposition into a graph +//TODO: method to remove complete components of the SPQR tree + +/** + * Removes. Removed rows and columns from the SPQR tree. + * Note that removed rows/columns can not be re-introduced into the SPQR tree; this is possible, but much more expensive + * to compute and typically not needed. + */ +void SCIPNetworkDecompositionRemoveComponents(SCIP_NETWORKDECOMP *dec, const int * componentRows, + int numRows, const int * componentCols, int numCols); + +/** + * A method to check if the cycle stored in the Decomposition matches the given array. This method should only be used in tests. + */ +SCIP_Bool SCIPNetworkDecompositionVerifyCycle(SCIP * scip, const SCIP_NETWORKDECOMP * dec, int column, int * column_rows, + double * column_values, int num_rows, int * computed_column_storage, + SCIP_Bool * computedSignStorage); + +/** + * This class stores all data for performing sequential column additions to a matrix and checking if it is network or not. + */ +typedef struct SCIP_NetworkColAddition SCIP_NETWORKCOLADDITION; + +/** + * @brief Creates the data structure for managing column-addition for an SPQR decomposition + */ +SCIP_RETCODE SCIPNetworkColAdditionCreate(SCIP* scip, SCIP_NETWORKCOLADDITION** pNewCol ); + +/** + * @brief Destroys the data structure for managing column-addition for SPQR decomposition + */ +void SCIPNetworkColAdditionFree(SCIP* scip, SCIP_NETWORKCOLADDITION ** pNewCol); + +/** + * Checks if adding a column of the given matrix creates a network SPQR decomposition. + * Adding a column which is already in the decomposition is undefined behavior and not checked for. + * @param dec Current SPQR-decomposition + * @param newRow Data structure to store information on how to add the new column (if applicable). + * @param column The index of the column to be added + * @param rows An array with the row indices of the nonzero entries of the column. + * @param numRows The number of nonzero entries of the column + */ +SCIP_RETCODE SCIPNetworkColAdditionCheck(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION * newCol, int column, + const int * nonzeroRows, const double * nonzeroValues, size_t numNonzeros); +/** + * @brief Adds the most recently checked column from checkNewRow() to the Decomposition. + * In Debug mode, adding a column for which SPQRNetworkColumnAdditionRemainsNetwork() returns false will exit the program. + * In Release mode, adding a column for which SPQRNetworkColumnAdditionRemainsNetwork() return false is undefined behavior + * @param dec Current SPQR-decomposition + * @param newRow Data structure containing information on how to add the new column. + */ +SCIP_RETCODE SCIPNetworkColAdditionAdd(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol); + +/** + * @param newColumn + * @return True if the most recently checked column is addable to the SPQR decomposition passed to it, i.e. the submatrix + * given by both remains network. + */ +SCIP_Bool SCIPNetworkColAdditionRemainsNetwork(SCIP_NETWORKCOLADDITION *newCol); + +/** + * This class stores all data for performing sequential row-additions to a matrix and checking if it is network or not. + */ +typedef struct SCIP_NetworkRowAddition SCIP_NETWORKROWADDITION; + +/** + * @brief Creates the data structure for managing row-addition for an SPQR decomposition + */ +SCIP_RETCODE SCIPNetworkRowAdditionCreate(SCIP* scip, SCIP_NETWORKROWADDITION** pNewRow ); +/** + * @brief Destroys the data structure for managing row-addition for SPQR decomposition + */ +void SCIPNetworkRowAdditionFree(SCIP* scip, SCIP_NETWORKROWADDITION ** pNewRow); + +/** + * Checks if adding a row of the given matrix creates a network SPQR decomposition. + * Adding a row which is already in the decomposition is undefined behavior and not checked for. + * @param dec Current SPQR-decomposition + * @param newRow Data structure to store information on how to add the new row (if applicable). + * @param row The index of the row to be added + * @param columns An array with the column indices of the nonzero entries of the row. + * @param numColumns The number of nonzero entries of the row + */ +SCIP_RETCODE SCIPNetworkRowAdditionCheck(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKROWADDITION * newRow, int row, + const int * nonzeroCols, const double * nonzeroValues, size_t numNonzeros); +/** + * @brief Adds the most recently checked column from checkNewRow() to the Decomposition. + * In Debug mode, adding a column for which rowAdditionRemainsNetwork() returns false will fail an assertion. + * In Release mode, adding a column for which rowAdditionRemainsNetwork() return false is undefined behavior + * @param dec Current SPQR-decomposition + * @param newRow Data structure containing information on how to add the new row. + */ +SCIP_RETCODE SCIPNetworkRowAdditionAdd(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow); + +/** + * @param newRow Data structure containing information on how to add the new row + * @return True if the most recently checked row is addable to the SPQR decomposition passed to it, i.e. the submatrix + * given by both remains network. + */ +SCIP_Bool SCIPNetworkRowAdditionRemainsNetwork(const SCIP_NETWORKROWADDITION *newRow); + +#ifdef cplusplus +} +#endif + +#endif //__SCIP_NETWORK_H__ diff --git a/tests/src/network/network.c b/tests/src/network/network.c new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/tests/src/network/network.c @@ -0,0 +1 @@ + From 0d466626edab838280d8baa51c2390b33cd7c6e1 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Wed, 8 May 2024 17:25:48 +0200 Subject: [PATCH 02/63] Add tests for network matrix methods --- src/CMakeLists.txt | 2 + src/scip/network.c | 4 +- src/scip/network.h | 39 +- tests/src/network/network.c | 1855 +++++++++++++++++++++++++++++++++++ 4 files changed, 1879 insertions(+), 21 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 670e6d125b..486e150868 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -168,6 +168,7 @@ set(scipsources scip/heur_zirounding.c scip/hypergraph.c scip/message_default.c + scip/network.c scip/nlhdlr_bilinear.c scip/nlhdlr_convex.c scip/nlhdlr_default.c @@ -656,6 +657,7 @@ set(scipheaders scip/message_default.h scip/message.h scip/misc.h + scip/network.h scip/nlhdlr_bilinear.h scip/nlhdlr_convex.h scip/nlhdlr_default.h diff --git a/src/scip/network.c b/src/scip/network.c index 1734b73f4a..f3271422e8 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -1434,8 +1434,8 @@ static int qsort_comparison (const void * a, const void * b) } } SCIP_Bool SCIPNetworkDecompositionVerifyCycle(SCIP * scip, - const SCIP_NETWORKDECOMP * dec, spqr_col column, spqr_row * column_rows, - double * column_values, int num_rows, + const SCIP_NETWORKDECOMP * dec, spqr_col column,const spqr_row * column_rows, + const double * column_values, int num_rows, spqr_row * computed_column_storage, SCIP_Bool * computedSignStorage){ int num_found_rows = decompositionGetFundamentalCycleRows(dec,column,computed_column_storage,computedSignStorage); diff --git a/src/scip/network.h b/src/scip/network.h index 0826908a6e..fabaca5ade 100644 --- a/src/scip/network.h +++ b/src/scip/network.h @@ -71,24 +71,24 @@ extern "C" { */ typedef struct SCIP_NetworkDecomposition SCIP_NETWORKDECOMP; -SCIP_RETCODE SCIPNetworkDecompositionCreate(SCIP * env, SCIP_NETWORKDECOMP **pDecomposition, int numRows, int numColumns); +SCIP_EXPORT SCIP_RETCODE SCIPNetworkDecompositionCreate(SCIP * env, SCIP_NETWORKDECOMP **pDecomposition, int numRows, int numColumns); -void SCIPNetworkDecompositionFree(SCIP_NETWORKDECOMP **pDecomposition); +SCIP_EXPORT void SCIPNetworkDecompositionFree(SCIP_NETWORKDECOMP **pDecomposition); /** * Returns if the Network decomposition contains the given row */ -SCIP_Bool SCIPNetworkDecompositionContainsRow(const SCIP_NETWORKDECOMP * decomposition, int row); +SCIP_EXPORT SCIP_Bool SCIPNetworkDecompositionContainsRow(const SCIP_NETWORKDECOMP * decomposition, int row); /** * Returns if the Network decomposition contains the given column */ -SCIP_Bool SCIPNetworkDecompositionContainsColumn(const SCIP_NETWORKDECOMP *decomposition, int column); +SCIP_EXPORT SCIP_Bool SCIPNetworkDecompositionContainsColumn(const SCIP_NETWORKDECOMP *decomposition, int column); /** * Checks if the Network decomposition of the graph is minimal. This method should only be used in tests. */ -SCIP_Bool SCIPNetworkDecompositionIsMinimal(const SCIP_NETWORKDECOMP * decomposition); +SCIP_EXPORT SCIP_Bool SCIPNetworkDecompositionIsMinimal(const SCIP_NETWORKDECOMP * decomposition); //TODO: method to convert decomposition into a graph //TODO: method to remove complete components of the SPQR tree @@ -98,15 +98,16 @@ SCIP_Bool SCIPNetworkDecompositionIsMinimal(const SCIP_NETWORKDECOMP * decomposi * Note that removed rows/columns can not be re-introduced into the SPQR tree; this is possible, but much more expensive * to compute and typically not needed. */ -void SCIPNetworkDecompositionRemoveComponents(SCIP_NETWORKDECOMP *dec, const int * componentRows, +SCIP_EXPORT void SCIPNetworkDecompositionRemoveComponents(SCIP_NETWORKDECOMP *dec, const int * componentRows, int numRows, const int * componentCols, int numCols); /** * A method to check if the cycle stored in the Decomposition matches the given array. This method should only be used in tests. */ -SCIP_Bool SCIPNetworkDecompositionVerifyCycle(SCIP * scip, const SCIP_NETWORKDECOMP * dec, int column, int * column_rows, - double * column_values, int num_rows, int * computed_column_storage, - SCIP_Bool * computedSignStorage); +SCIP_EXPORT SCIP_Bool SCIPNetworkDecompositionVerifyCycle(SCIP * scip, const SCIP_NETWORKDECOMP * dec, + int column, const int * column_rows, const double * column_values, + int num_rows, int * computed_column_storage, + SCIP_Bool * computedSignStorage); /** * This class stores all data for performing sequential column additions to a matrix and checking if it is network or not. @@ -116,12 +117,12 @@ typedef struct SCIP_NetworkColAddition SCIP_NETWORKCOLADDITION; /** * @brief Creates the data structure for managing column-addition for an SPQR decomposition */ -SCIP_RETCODE SCIPNetworkColAdditionCreate(SCIP* scip, SCIP_NETWORKCOLADDITION** pNewCol ); +SCIP_EXPORT SCIP_RETCODE SCIPNetworkColAdditionCreate(SCIP* scip, SCIP_NETWORKCOLADDITION** pNewCol ); /** * @brief Destroys the data structure for managing column-addition for SPQR decomposition */ -void SCIPNetworkColAdditionFree(SCIP* scip, SCIP_NETWORKCOLADDITION ** pNewCol); +SCIP_EXPORT void SCIPNetworkColAdditionFree(SCIP* scip, SCIP_NETWORKCOLADDITION ** pNewCol); /** * Checks if adding a column of the given matrix creates a network SPQR decomposition. @@ -132,7 +133,7 @@ void SCIPNetworkColAdditionFree(SCIP* scip, SCIP_NETWORKCOLADDITION ** pNewCol); * @param rows An array with the row indices of the nonzero entries of the column. * @param numRows The number of nonzero entries of the column */ -SCIP_RETCODE SCIPNetworkColAdditionCheck(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION * newCol, int column, +SCIP_EXPORT SCIP_RETCODE SCIPNetworkColAdditionCheck(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION * newCol, int column, const int * nonzeroRows, const double * nonzeroValues, size_t numNonzeros); /** * @brief Adds the most recently checked column from checkNewRow() to the Decomposition. @@ -141,14 +142,14 @@ SCIP_RETCODE SCIPNetworkColAdditionCheck(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKC * @param dec Current SPQR-decomposition * @param newRow Data structure containing information on how to add the new column. */ -SCIP_RETCODE SCIPNetworkColAdditionAdd(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol); +SCIP_EXPORT SCIP_RETCODE SCIPNetworkColAdditionAdd(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol); /** * @param newColumn * @return True if the most recently checked column is addable to the SPQR decomposition passed to it, i.e. the submatrix * given by both remains network. */ -SCIP_Bool SCIPNetworkColAdditionRemainsNetwork(SCIP_NETWORKCOLADDITION *newCol); +SCIP_EXPORT SCIP_Bool SCIPNetworkColAdditionRemainsNetwork(SCIP_NETWORKCOLADDITION *newCol); /** * This class stores all data for performing sequential row-additions to a matrix and checking if it is network or not. @@ -158,11 +159,11 @@ typedef struct SCIP_NetworkRowAddition SCIP_NETWORKROWADDITION; /** * @brief Creates the data structure for managing row-addition for an SPQR decomposition */ -SCIP_RETCODE SCIPNetworkRowAdditionCreate(SCIP* scip, SCIP_NETWORKROWADDITION** pNewRow ); +SCIP_EXPORT SCIP_RETCODE SCIPNetworkRowAdditionCreate(SCIP* scip, SCIP_NETWORKROWADDITION** pNewRow ); /** * @brief Destroys the data structure for managing row-addition for SPQR decomposition */ -void SCIPNetworkRowAdditionFree(SCIP* scip, SCIP_NETWORKROWADDITION ** pNewRow); +SCIP_EXPORT void SCIPNetworkRowAdditionFree(SCIP* scip, SCIP_NETWORKROWADDITION ** pNewRow); /** * Checks if adding a row of the given matrix creates a network SPQR decomposition. @@ -173,7 +174,7 @@ void SCIPNetworkRowAdditionFree(SCIP* scip, SCIP_NETWORKROWADDITION ** pNewRow); * @param columns An array with the column indices of the nonzero entries of the row. * @param numColumns The number of nonzero entries of the row */ -SCIP_RETCODE SCIPNetworkRowAdditionCheck(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKROWADDITION * newRow, int row, +SCIP_EXPORT SCIP_RETCODE SCIPNetworkRowAdditionCheck(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKROWADDITION * newRow, int row, const int * nonzeroCols, const double * nonzeroValues, size_t numNonzeros); /** * @brief Adds the most recently checked column from checkNewRow() to the Decomposition. @@ -182,14 +183,14 @@ SCIP_RETCODE SCIPNetworkRowAdditionCheck(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKR * @param dec Current SPQR-decomposition * @param newRow Data structure containing information on how to add the new row. */ -SCIP_RETCODE SCIPNetworkRowAdditionAdd(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow); +SCIP_EXPORT SCIP_RETCODE SCIPNetworkRowAdditionAdd(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow); /** * @param newRow Data structure containing information on how to add the new row * @return True if the most recently checked row is addable to the SPQR decomposition passed to it, i.e. the submatrix * given by both remains network. */ -SCIP_Bool SCIPNetworkRowAdditionRemainsNetwork(const SCIP_NETWORKROWADDITION *newRow); +SCIP_EXPORT SCIP_Bool SCIPNetworkRowAdditionRemainsNetwork(const SCIP_NETWORKROWADDITION *newRow); #ifdef cplusplus } diff --git a/tests/src/network/network.c b/tests/src/network/network.c index 8b13789179..ea2e04492b 100644 --- a/tests/src/network/network.c +++ b/tests/src/network/network.c @@ -1 +1,1856 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* This file is part of the program and library */ +/* SCIP --- Solving Constraint Integer Programs */ +/* */ +/* Copyright (c) 2002-2024 Zuse Institute Berlin (ZIB) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/* You should have received a copy of the Apache-2.0 license */ +/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/**@file network.c + * @brief unittests for network matrix detection methods + * @author Rolf van der Hulst + */ + +/*--+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/ + +#include + +#include "scip/scip.h" +#include "scip/network.h" + +#include "include/scip_test.h" + +/** + * Because the algorithm and data structures used to check for network matrices are rather complex, + * we extensively test them in this file. We do this by checking if the cycles of the graphs represented by + * the decomposition match the nonzero entries of the columns of the matrix that we supplied, for many different graphs. + * Most of the the specific graphs tested either contain some special case or posed challenges during development. + */ + +static SCIP* scip; + +static +void setup(void) +{ + /* create scip */ + SCIP_CALL( SCIPcreate(&scip) ); + +} + +static +void teardown(void) +{ + /* free scip */ + SCIP_CALL( SCIPfree(&scip) ); +} + +/** CSR/CSC matrix type to encode testing matrices **/ +typedef struct{ + int nrows; + int ncols; + int nnonzs; + + bool isRowWise; //True -> CSR matrix, False -> CSC matrix + + int * primaryIndexStart; // + int * entrySecondaryIndex; // column with CSR and row with CSC matrix + double * entryValue; +} DirectedTestCase; + +static DirectedTestCase stringToTestCase(const char * string, int rows, int cols){ + int row = 0; + int column = 0; + + DirectedTestCase testCase; + testCase.nrows = rows; + testCase.ncols = cols; + testCase.nnonzs = 0; + testCase.isRowWise = true; + + + testCase.primaryIndexStart = malloc(sizeof(int) * (rows + 1)); + + int nonzeroArraySize = 8; + testCase.entrySecondaryIndex = malloc(sizeof(int) * nonzeroArraySize); + testCase.entryValue = malloc(sizeof(double) * nonzeroArraySize); + + + const char * current = &string[0]; + int i = 0; + + while(*current != '\0'){ + char * next = NULL; + double num = strtod(current, &next); + if(i%cols == 0){ + testCase.primaryIndexStart[i/cols] = testCase.nnonzs; + } + if(num != 0.0){ + if(testCase.nnonzs == nonzeroArraySize){ + int newSize = nonzeroArraySize * 2; + testCase.entryValue = realloc(testCase.entryValue,sizeof(double) * newSize); + testCase.entrySecondaryIndex = realloc(testCase.entrySecondaryIndex,sizeof(int) * newSize); + nonzeroArraySize = newSize; + } + testCase.entryValue[testCase.nnonzs] = num; + testCase.entrySecondaryIndex[testCase.nnonzs] = i % cols; + ++testCase.nnonzs; + } + current = next; + ++i; + if(i == rows * cols){ + break; + } + } + testCase.primaryIndexStart[testCase.nrows] = testCase.nnonzs; + + return testCase; +} +static void transposeMatrixStorage(DirectedTestCase * testCase){ + int numPrimaryDimension = testCase->isRowWise ? testCase->nrows : testCase->ncols; + int numSecondaryDimension = testCase->isRowWise ? testCase->ncols : testCase->nrows; + + int * transposedFirstIndex = malloc(sizeof(int) * (numSecondaryDimension + 1)); + int * transposedEntryIndex = malloc(sizeof(int) * testCase->nnonzs); + double * transposedEntryValue = malloc(sizeof(double) * testCase->nnonzs); + + for (int i = 0; i <= numSecondaryDimension; ++i) { + transposedFirstIndex[i] = 0; + } + for (int i = 0; i < testCase->nnonzs; ++i) { + ++ (transposedFirstIndex[testCase->entrySecondaryIndex[i] + 1]); + } + + for (int i = 1; i < numSecondaryDimension; ++i) { + transposedFirstIndex[i] += transposedFirstIndex[i-1]; + } + + for (int i = 0; i < numPrimaryDimension; ++i) { + int first = testCase->primaryIndexStart[i]; + int beyond = testCase->primaryIndexStart[i+1]; + for (int entry = first; entry < beyond; ++entry) { + int index = testCase->entrySecondaryIndex[entry]; + int transIndex = transposedFirstIndex[index]; + transposedEntryIndex[transIndex] = i; + transposedEntryValue[transIndex] = testCase->entryValue[entry]; + ++ (transposedFirstIndex[index]); + } + } + for (int i = numSecondaryDimension; i > 0; --i) { + transposedFirstIndex[i] = transposedFirstIndex[i - 1]; + } + transposedFirstIndex[0] = 0; + + free(testCase->entrySecondaryIndex); + free(testCase->entryValue); + free(testCase->primaryIndexStart); + + testCase->primaryIndexStart = transposedFirstIndex; + testCase->entrySecondaryIndex = transposedEntryIndex; + testCase->entryValue = transposedEntryValue; + + testCase->isRowWise = !testCase->isRowWise; +} + +static DirectedTestCase copyTestCase(DirectedTestCase * testCase){ + DirectedTestCase copy; + copy.nrows = testCase->nrows; + copy.ncols = testCase->ncols; + copy.nnonzs = testCase->nnonzs; + copy.isRowWise = testCase->isRowWise; + + int size = (testCase->isRowWise ? testCase->nrows : testCase->ncols) + 1; + copy.primaryIndexStart = malloc(sizeof(int) * size); + for (int i = 0; i < size; ++i) { + copy.primaryIndexStart[i] = testCase->primaryIndexStart[i]; + } + copy.entrySecondaryIndex = malloc(sizeof(int) * testCase->nnonzs); + copy.entryValue = malloc(sizeof(double) * testCase->nnonzs); + + for (int i = 0; i < testCase->nnonzs; ++i) { + copy.entrySecondaryIndex[i] = testCase->entrySecondaryIndex[i]; + copy.entryValue[i] = testCase->entryValue[i]; + } + return copy; +} +static void freeTestCase(DirectedTestCase * testCase){ + free(testCase->primaryIndexStart); + free(testCase->entrySecondaryIndex); + free(testCase->entryValue); + +} + +static SCIP_RETCODE runColumnTestCase(DirectedTestCase * testCase, bool isExpectedNetwork, bool isExpectedNotNetwork){ + if(testCase->isRowWise){ + transposeMatrixStorage(testCase); + } + cr_expect(!testCase->isRowWise); + SCIP_NETWORKDECOMP * dec = NULL; + SCIP_CALL(SCIPNetworkDecompositionCreate(scip,&dec,testCase->nrows,testCase->ncols)); + + SCIP_NETWORKCOLADDITION * coladd = NULL; + SCIP_CALL(SCIPNetworkColAdditionCreate(scip,&coladd)); + bool isNetwork = true; + + int * tempColumnStorage; + SCIP_Bool * tempSignStorage; + + SCIP_CALL(SCIPallocBufferArray(scip,&tempColumnStorage,testCase->nrows)); + SCIP_CALL(SCIPallocBufferArray(scip,&tempSignStorage,testCase->nrows)); + + for (int i = 0; i < testCase->ncols; ++i) { + int colEntryStart = testCase->primaryIndexStart[i]; + int colEntryEnd = testCase->primaryIndexStart[i+1]; + const int* nonzeroRows = &testCase->entrySecondaryIndex[colEntryStart]; + const double * nonzeroValues = &testCase->entryValue[colEntryStart]; + int nonzeros = colEntryEnd-colEntryStart; + cr_assert(nonzeros >= 0); + //Check if adding the column preserves the network matrix + SCIP_CALL(SCIPNetworkColAdditionCheck(dec,coladd,i,nonzeroRows,nonzeroValues,nonzeros)); + if(SCIPNetworkColAdditionRemainsNetwork(coladd)){ + //If so, we add it. + SCIP_CALL(SCIPNetworkColAdditionAdd(dec,coladd)); + }else{ + isNetwork = false; + break; + } + cr_expect(SCIPNetworkDecompositionIsMinimal(dec)); + //Check if the computed network matrix indeed reflects the network matrix, + //by checking if the fundamental cycles are all correct + for (int j = 0; j <= i; ++j) { + int jColEntryStart = testCase->primaryIndexStart[j]; + int jColEntryEnd = testCase->primaryIndexStart[j+1]; + const int* jNonzeroRows = &testCase->entrySecondaryIndex[jColEntryStart]; + const double * jNonzeroValues = &testCase->entryValue[jColEntryStart]; + int jNonzeros = jColEntryEnd-jColEntryStart; + SCIP_Bool cycleIsCorrect = SCIPNetworkDecompositionVerifyCycle(scip,dec,j, + jNonzeroRows,jNonzeroValues, + jNonzeros,tempColumnStorage, + tempSignStorage); + + cr_expect(cycleIsCorrect); + } + } + + + if(isExpectedNetwork){ + //We expect that the given matrix is a network matrix. If not, something went wrong. + cr_expect(isNetwork); + } + if(isExpectedNotNetwork){ + //We expect that the given matrix is not a network matrix. If not, something went wrong. + cr_expect(!isNetwork); + } + SCIPfreeBufferArray(scip,&tempColumnStorage); + SCIPfreeBufferArray(scip,&tempSignStorage); + + SCIPNetworkColAdditionFree(scip,&coladd); + SCIPNetworkDecompositionFree(&dec); + return SCIP_OKAY; +} + +static SCIP_RETCODE runRowTestCase(DirectedTestCase * testCase, bool isExpectedNetwork, bool isExpectedNotNetwork){ + if(!testCase->isRowWise){ + transposeMatrixStorage(testCase); + } + cr_expect(testCase->isRowWise); + + //We keep a column-wise copy to check the columns easily + DirectedTestCase colWiseCase = copyTestCase(testCase); + transposeMatrixStorage(&colWiseCase); + + SCIP_NETWORKDECOMP * dec = NULL; + SCIP_CALL(SCIPNetworkDecompositionCreate(scip,&dec,testCase->nrows,testCase->ncols)); + + SCIP_NETWORKROWADDITION * rowadd = NULL; + SCIP_CALL(SCIPNetworkRowAdditionCreate(scip,&rowadd)); + bool isNetwork = true; + + int * tempColumnStorage; + SCIP_Bool * tempSignStorage; + + SCIP_CALL(SCIPallocBufferArray(scip,&tempColumnStorage,testCase->nrows)); + SCIP_CALL(SCIPallocBufferArray(scip,&tempSignStorage,testCase->nrows)); + + for (int i = 0; i < testCase->nrows; ++i) { + int rowEntryStart = testCase->primaryIndexStart[i]; + int rowEntryEnd = testCase->primaryIndexStart[i+1]; + const int* nonzeroCols = &testCase->entrySecondaryIndex[rowEntryStart]; + const double * nonzeroValues = &testCase->entryValue[rowEntryStart]; + int nonzeros = rowEntryEnd-rowEntryStart; + cr_assert(nonzeros >= 0); + //Check if adding the row preserves the network matrix + SCIP_CALL(SCIPNetworkRowAdditionCheck(dec,rowadd,i,nonzeroCols,nonzeroValues,nonzeros)); + if(SCIPNetworkRowAdditionRemainsNetwork(rowadd)){ + //If so, we add it. + SCIP_CALL(SCIPNetworkRowAdditionAdd(dec,rowadd)); + }else{ + isNetwork = false; + break; + } + cr_expect(SCIPNetworkDecompositionIsMinimal(dec)); + //Check if the computed network matrix indeed reflects the network matrix, + //by checking if the fundamental cycles are all correct + for (int j = 0; j < colWiseCase.ncols; ++j) { + int jColEntryStart = colWiseCase.primaryIndexStart[j]; + int jColEntryEnd = colWiseCase.primaryIndexStart[j+1]; + + //Count the number of rows in the column that should be in the current decomposition + int finalEntryIndex = jColEntryStart; + for (int testEntry = jColEntryStart; testEntry < jColEntryEnd; ++testEntry) { + if(colWiseCase.entrySecondaryIndex[testEntry] <= i){ + ++finalEntryIndex; + }else{ + break; + } + } + + const int* jNonzeroRows = &colWiseCase.entrySecondaryIndex[jColEntryStart]; + const double * jNonzeroValues = &colWiseCase.entryValue[jColEntryStart]; + + int jNonzeros = finalEntryIndex-jColEntryStart; + SCIP_Bool cycleIsCorrect = SCIPNetworkDecompositionVerifyCycle(scip,dec,j, + jNonzeroRows,jNonzeroValues, + jNonzeros,tempColumnStorage, + tempSignStorage); + + cr_expect(cycleIsCorrect); + } + } + + if(isExpectedNetwork){ + //We expect that the given matrix is a network matrix. If not, something went wrong. + cr_expect(isNetwork); + } + if(isExpectedNotNetwork){ + //We expect that the given matrix is not a network matrix. If not, something went wrong. + cr_expect(!isNetwork); + } + + freeTestCase(&colWiseCase); + + SCIPfreeBufferArray(scip,&tempColumnStorage); + SCIPfreeBufferArray(scip,&tempSignStorage); + + SCIPNetworkRowAdditionFree(scip,&rowadd); + SCIPNetworkDecompositionFree(&dec); + return SCIP_OKAY; +} + +TestSuite(network, .init = setup, .fini = teardown); + +Test(network, coladd_single_column, .description = "Try adding a single column") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 " + "+1 " + "-1 ", + 3,1); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} +Test(network, coladd_doublecolumn_invalid_sign, .description = "Try adding a second column that has invalid signing") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 +1 " + "+1 0 " + "-1 +1 ", + 3,2); + runColumnTestCase(&testCase,false,true); + freeTestCase(&testCase); +} + +Test(network, coladd_doublecolumn_invalid_sign_2, .description = "Try adding a second column that has invalid signing") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "+1 0 " + "-1 -1 ", + 3,2); + runColumnTestCase(&testCase,false,true); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_1, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "+1 0 " + " 0 0 ", + 3,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_2, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "+1 0 " + " 0 0 ", + 3,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_1r, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 -1 " + "+1 0 " + " 0 0 ", + 3,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_2r, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 +1 " + "+1 0 " + " 0 0 ", + 3,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_3, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "+1 0 " + " 0 +1 ", + 3,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_4, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 +1 " + "+1 0 " + " 0 +1 ", + 3,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_3r, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 -1 " + "+1 0 " + " 0 +1 ", + 3,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_4r, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 +1 " + "+1 0 " + " 0 +1 ", + 3,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_5, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 +1 " + " 0 0 " + " 0 0 ", + 3,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_6, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + " 0 0 " + " 0 0 ", + 3,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_7, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 +1 " + " 0 0 " + " 0 +1 ", + 3,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_8, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + " 0 0 " + " 0 +1 ", + 3,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_5r, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 +1 " + " 0 0 " + " 0 0 ", + 3,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_6r, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 -1 " + " 0 0 " + " 0 0 ", + 3,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_7r, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 +1 " + " 0 0 " + " 0 +1 ", + 3,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_8r, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 -1 " + " 0 0 " + " 0 +1 ", + 3,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_9, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "+1 0 " + "-1 +1 " + " 0 0 ", + 4,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_10, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 +1 " + "+1 0 " + "-1 -1 " + " 0 0 ", + 4,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_11, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 +1 " + "+1 0 " + "-1 -1 " + " 0 +1 ", + 4,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_12, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "+1 0 " + "-1 +1 " + " 0 +1 ", + 4,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_9r, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 -1 " + "-1 0 " + "+1 +1 " + " 0 0 ", + 4,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_10r, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 +1 " + "-1 0 " + "+1 -1 " + " 0 0 ", + 4,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_11r, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 +1 " + "-1 0 " + "+1 -1 " + " 0 +1 ", + 4,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_12r, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 -1 " + "-1 0 " + "+1 +1 " + " 0 +1 ", + 4,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_13, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 +1 " + "+1 +1 " + "-1 -1 " + " 0 0 ", + 4,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_14, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "+1 -1 " + "-1 +1 " + " 0 0 ", + 4,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_15, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 +1 " + "+1 +1 " + "-1 -1 " + " 0 +1 ", + 4,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_16, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "+1 -1 " + "-1 +1 " + " 0 +1 ", + 4,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_13r, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 +1 " + "-1 +1 " + "+1 -1 " + " 0 0 ", + 4,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_14r, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 -1 " + "-1 -1 " + "+1 +1 " + " 0 0 ", + 4,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_15r, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 +1 " + "-1 +1 " + "+1 -1 " + " 0 +1 ", + 4,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_splitseries_16r, .description = "Split a series component") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 -1 " + "-1 -1 " + "+1 +1 " + " 0 +1 ", + 4,2); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_parallelsimple_1, .description = "Extending a parallel component") +{ + DirectedTestCase testCase = stringToTestCase( + "1 1 1 -1 ", + 1,4); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_parallelsimple_2, .description = "Extending a parallel component") +{ + DirectedTestCase testCase = stringToTestCase( + "1 1 1 -1 " + "0 0 -1 0 ", + 2,4); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_parallelsimple_3, .description = "Extending a parallel component") +{ + DirectedTestCase testCase = stringToTestCase( + "1 1 1 1 " + "0 0 1 0 ", + 2,4); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_components_1, .description = "Merging multiple components into one") +{ + DirectedTestCase testCase = stringToTestCase( + "0 -1 -1 " + "1 0 1 ", + 2,3); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_components_2, .description = "Merging multiple components into one") +{ + DirectedTestCase testCase = stringToTestCase( + "0 1 -1 " + "1 0 1 ", + 2,3); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_components_3, .description = "Merging multiple components into one") +{ + DirectedTestCase testCase = stringToTestCase( + "0 1 1 " + "-1 0 1 ", + 2,3); + runColumnTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, coladd_3by3_1, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 1 1 " + "1 -1 -1 " + "-1 1 -1 ", + 3,3); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_3by3_2, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 -1 -1 " + "1 0 1 " + "0 0 0 ", + 3,3); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_3by3_3, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 -1 1 " + "1 -1 -1 " + "0 0 1 ", + 3,3); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_3by3_4, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 -1 1 " + "-1 1 0 " + "0 1 -1 ", + 3,3); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_3by3_5, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 1 1 " + "0 1 0 " + "-1 -1 -1 ", + 3,3); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_3by3_6, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 1 -1 " + "-1 1 -1 " + "-1 0 -1 ", + 3,3); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} +Test(network, coladd_3by3_7, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 1 -1 " + "0 1 1 " + "-1 0 0 ", + 3,3); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} +Test(network, coladd_3by3_8, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 1 -1 " + "0 -1 1 " + "1 0 0 ", + 3,3); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} +Test(network, coladd_3by3_9, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 0 0 " + "-1 -1 -1 " + "-1 -1 -1 ", + 3,3); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} +Test(network, coladd_3by3_10, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 1 -1 " + "-1 1 -1 " + "1 0 -1 ", + 3,3); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_3by3_11, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 1 0 " + "-1 0 1 " + "0 1 1 ", + 3,3); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_6by3_1, .description = "A six by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 -1 1 " + "-1 1 -1 " + "-1 1 0 " + "0 1 1 " + "1 0 -1 " + "0 -1 -1 ", + 6,3); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_6by3_2, .description = "A six by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 -1 1 " + "-1 1 0 " + "0 1 -1 " + "0 -1 0 " + "0 1 0 " + "0 0 1 ", + 6,3); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_3by4_1, .description = "A three by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 -1 1 1 " + "-1 -1 0 0 " + "1 0 1 -1 ", + 3,4); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_3by5_1, .description = "A three by five case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 1 1 0 1 " + "0 -1 -1 -1 -1 " + "1 -1 0 -1 -1 ", + 3,5); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_4by8_1, .description = "A four by eight case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 0 1 1 0 1 0 1 " + "0 0 0 0 0 -1 1 -1 " + "1 -1 0 0 1 1 0 0 " + "0 0 1 1 -1 0 -1 0 ", + 4,8); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_4by8_2, .description = "A four by eight case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 1 0 -1 0 0 -1 0 " + "1 1 0 0 1 1 -1 1 " + "0 -1 0 0 -1 -1 0 -1 " + "0 1 1 -1 1 0 -1 -1 ", + 4,8); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_4by8_3, .description = "A four by eight case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 1 -1 0 -1 0 -1 0 " + "-1 0 -1 0 1 -1 1 1 " + "0 0 -1 1 -1 -1 0 0 " + "-1 1 0 -1 0 -1 1 -1 ", + 4,8); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} +Test(network, coladd_4by8_4, .description = "A four by eight case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 0 1 1 0 1 0 0 " + "0 -1 0 -1 1 0 -1 -1 " + "-1 -1 1 0 1 1 -1 -1 " + "0 0 0 -1 0 -1 1 -1 ", + 4,8); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} +Test(network, coladd_4by8_5, .description = "A four by eight case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 0 0 -1 -1 -1 0 -1 " + "-1 -1 0 0 1 1 -1 0 " + "0 0 1 0 -1 -1 0 -1 " + "0 0 1 -1 -1 0 0 -1 ", + 4,8); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} +Test(network, coladd_4by8_6, .description = "A four by eight case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 0 0 1 1 1 0 -1 " + "0 -1 -1 0 0 1 0 -1 " + "0 0 1 -1 0 0 1 1 " + "0 -1 -1 1 1 1 -1 0 ", + 4,8); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} +Test(network, coladd_4by8_7, .description = "A four by eight case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 0 1 1 1 0 1 0 " + "0 -1 1 1 1 1 -1 0 " + "1 0 0 0 1 0 1 1 " + "1 -1 1 0 -1 -1 -1 -1 ", + 4,8); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} +Test(network, coladd_4by4_1, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 +1 -1 0 " + "-1 0 -1 0 " + "0 0 -1 +1 " + "-1 +1 0 -1", + 4,4); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} +Test(network, coladd_5by5_1, .description = "A five by five case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 1 1 0 0 " + "0 -1 -1 1 0 " + "0 0 -1 1 0 " + "-1 -1 0 0 -1 " + "-1 -1 -1 1 0 ", + 5,5); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_5by5_2, .description = "A five by five case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 0 1 1 -1 " + "-1 1 1 1 0 " + "0 0 1 1 1 " + "-1 1 0 -1 0 " + "-1 1 0 0 -1 ", + 5,5); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_5by5_3, .description = "A five by five case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 1 1 0 -1 " + "0 -1 0 1 -1 " + "1 1 1 0 1 " + "0 0 1 1 0 " + "-1 0 -1 0 1 ", + 5,5); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_5by5_4, .description = "A five by five case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 -1 -1 1 0 " + "0 0 -1 1 -1 " + "1 -1 0 0 1 " + "0 1 1 -1 0 " + "0 -1 0 1 0 ", + 5,5); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_5by5_5, .description = "A five by five case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 1 0 1 1 " + "-1 1 1 0 0 " + "0 0 0 -1 -1 " + "1 -1 0 0 -1 " + "-1 1 0 1 1 ", + 5,5); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_5by5_6, .description = "A five by five case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 -1 1 0 0 " + "-1 0 -1 0 0 " + "0 -1 1 1 -1 " + "-1 1 -1 -1 0 " + "1 0 0 -1 0 ", + 5,5); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_5by5_7, .description = "A five by five case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 0 1 0 0 " + "0 -1 0 1 0 " + "0 1 -1 -1 0 " + "-1 0 -1 -1 -1 " + "-1 -1 0 1 -1 ", + 5,5); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_5by5_8, .description = "A five by five case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 1 1 -1 0 " + "1 0 0 0 -1 " + "-1 0 -1 1 1 " + "0 0 0 -1 -1 " + "1 1 0 -1 0 ", + 5,5); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, coladd_5by5_9, .description = "A five by five case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 -1 0 -1 1 " + "1 -1 -1 -1 1 " + "0 0 -1 0 0 " + "0 -1 -1 0 0 " + "1 0 0 0 1 ", + 5,5); + runColumnTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_1by2_1, .description = "A one by two case") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 0 ", + 1,2); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_1by2_2, .description = "A one by two case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 1 ", + 1,2); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_1by2_3, .description = "A one by two case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 -1 ", + 1,2); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_2by3_1, .description = "A two by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 +1 " + "-1 +1 -1 ", + 2,3); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_2by3_2, .description = "A two by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 +1 " + "-1 +1 +1 ", + 2,3); + runRowTestCase(&testCase,false,true); + freeTestCase(&testCase); +} + +Test(network, rowadd_2by3_3, .description = "A two by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 +1 " + "+1 0 +1 ", + 2,3); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_2by3_4, .description = "A two by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 0 " + "+1 0 0 ", + 2,3); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_2by3_5, .description = "A two by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 0 " + "+0 +1 0 ", + 2,3); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_2by3_6, .description = "A two by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 0 " + "+0 -1 0 ", + 2,3); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_2by3_7, .description = "A two by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 0 " + "+0 +1 +1 ", + 2,3); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_2by3_8, .description = "A two by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 0 " + "+0 -1 +1 ", + 2,3); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_3by6_1, .description = "A three by six case") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 0 0 0 0 " + "0 0 +1 -1 0 0 " + "-1 +1 -1 0 0 0 ", + 3,6); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_3by6_2, .description = "A three by six case") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 0 0 0 0 " + "0 0 +1 -1 0 0 " + "-1 +1 -1 0 0 +1 ", + 3,6); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_3by2_1, .description = "A three by two case") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "-1 +1 " + "+1 -1 ", + 3,2); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_3by1_1, .description = "A three by one case") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 " + "-1 " + "+1 ", + 3,1); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_3by3_1, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 -1 1 " + "-1 1 0 " + "0 1 -1 ", + 3,3); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_3by3_2, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 1 1 " + "0 1 0 " + "-1 -1 -1 ", + 3,3); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_3by3_3, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 -1 0 " + "-1 0 -1 " + "-1 -1 0 ", + 3,3); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_3by3_4, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 0 1 " + "0 -1 1 " + "-1 -1 -1 ", + 3,3); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_3by3_5, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 1 -1 " + "-1 -1 0 " + "0 1 -1 ", + 3,3); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_3by3_6, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 1 1 " + "1 1 0 " + "-1 1 -1 ", + 3,3); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_3by3_7, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 -1 0 " + "0 1 -1 " + "-1 1 -1 ", + 3,3); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_3by3_8, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 1 1 " + "-1 1 0 " + "-1 0 -1 ", + 3,3); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_3by3_9, .description = "A three by three case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 -1 -1 " + "-1 -1 -1 " + "1 0 1 ", + 3,3); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_3by6_3, .description = "A three by six case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 -1 0 0 0 -1 " + "0 0 0 -1 -1 -1 " + "-1 1 0 0 1 1 ", + 3,3); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_3by6_4, .description = "A three by six case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 1 -1 -1 0 0 " + "0 0 0 1 -1 -1 " + "0 -1 1 1 -1 0 ", + 3,3); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_1, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 1 0 0 " + "-1 -1 -1 -1 " + "0 -1 -1 -1 " + "1 0 0 -1 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_2, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 0 -1 -1 " + "1 1 -1 -1 " + "0 0 -1 -1 " + "0 -1 0 1 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_3, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 1 0 1 " + "-1 0 -1 0 " + "0 0 1 1 " + "0 -1 -1 1 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_4, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 0 -1 0 " + "0 1 0 0 " + "-1 -1 1 1 " + "0 -1 -1 1 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_5, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 0 -1 1 " + "1 0 0 -1 " + "-1 0 -1 0 " + "1 1 1 0 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_6, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 0 -1 1 " + "0 1 -1 0 " + "0 -1 1 -1 " + "-1 -1 0 1 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_7, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 0 -1 1 " + "0 1 -1 0 " + "0 -1 1 -1 " + "-1 -1 0 1 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_8, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 0 1 1 " + "1 -1 -1 0 " + "-1 1 1 1 " + "1 0 0 -1 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_9, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 -1 0 0 " + "-1 1 0 -1 " + "0 1 1 -1 " + "-1 0 1 0 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_10, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 -1 1 -1 " + "-1 0 0 -1 " + "0 1 0 1 " + "0 -1 0 0 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_11, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 1 1 0 " + "-1 -1 0 1 " + "-1 0 0 1 " + "0 0 1 1 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_12, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 -1 0 -1 " + "0 1 0 1 " + "-1 0 1 0 " + "0 0 -1 -1 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_13, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 -1 -1 1 " + "-1 0 0 1 " + "1 1 1 -1 " + "1 0 -1 -1 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_14, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 1 1 1 " + "1 1 0 1 " + "0 1 1 0 " + "0 1 1 1 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_15, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 -1 -1 0 " + "1 1 0 1 " + "1 0 -1 0 " + "0 1 0 1 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_16, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 -1 1 0 " + "-1 0 0 -1 " + "0 -1 0 1 " + "-1 -1 0 0 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_17, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 -1 -1 0 " + "-1 -1 0 0 " + "0 -1 0 1 " + "0 1 1 -1 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_4by4_18, .description = "A four by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 -1 0 1 " + "1 0 -1 0 " + "-1 0 1 1 " + "-1 1 0 -1 ", + 4,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_5by5_1, .description = "A five by five case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 -1 1 -1 1 " + "0 0 1 -1 0 " + "-1 0 0 1 0 " + "0 0 1 0 1 " + "0 -1 0 1 1 ", + 5,5); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_5by5_2, .description = "A five by five case") +{ + DirectedTestCase testCase = stringToTestCase( + "1 -1 0 -1 -1 " + "0 -1 0 -1 -1 " + "0 0 1 -1 0 " + "0 -1 0 -1 -1 " + "-1 1 1 0 0 ", + 5,5); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_5by5_3, .description = "A five by five case") +{ + DirectedTestCase testCase = stringToTestCase( + "-1 0 1 0 -1 " + "0 1 1 0 -1 " + "1 0 -1 0 1 " + "-1 0 1 0 0 " + "1 0 -1 0 1 ", + 5,5); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_5by5_4, .description = "A five by five case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 -1 1 0 0 " + "0 1 -1 1 0 " + "0 -1 1 0 0 " + "1 -1 1 0 0 " + "0 1 0 1 1 ", + 5,5); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_5by5_5, .description = "A five by five case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 0 1 0 1 " + "1 0 0 1 -1 " + "1 -1 1 1 0 " + "0 0 -1 0 -1 " + "0 0 1 0 1 ", + 5,5); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_8by4, .description = "A eight by four case") +{ + DirectedTestCase testCase = stringToTestCase( + "0 0 0 0 " + "1 0 1 0 " + "-1 1 -1 -1 " + "1 0 1 1 " + "1 -1 1 0 " + "1 -1 0 0 " + "1 1 -1 1 " + "0 0 1 0 ", + 8,4); + runRowTestCase(&testCase,false,false); + freeTestCase(&testCase); +} +Test(network, rowadd_singlerigid_1, .description = "Updating a single rigid member") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 0 +1 " + "+1 +1 0 " + "0 -1 +1 " + "+1 +1 0 ", + 4,3); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_singlerigid_2, .description = "Updating a single rigid member") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 0 +1 " + "+1 +1 0 " + "0 -1 +1 " + "-1 -1 0 ", + 4,3); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_singlerigid_3, .description = "Updating a single rigid member") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 0 +1 " + "+1 +1 0 " + "0 -1 +1 " + "-1 +1 0 ", + 4,3); + runRowTestCase(&testCase,false,true); + freeTestCase(&testCase); +} + +Test(network, rowadd_singlerigid_4, .description = "Updating a single rigid member") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 0 +1 " + "-1 -1 -1 " + "0 +1 +1 " + "+1 +1 +1 ", + 4,3); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_singlerigid_5, .description = "Updating a single rigid member") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 0 +1 " + "-1 -1 -1 " + "0 +1 +1 " + "-1 -1 -1 ", + 4,3); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_singlerigid_6, .description = "Updating a single rigid member") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 0 +1 " + "-1 -1 -1 " + "0 +1 +1 " + "-1 +1 -1 ", + 4,3); + runRowTestCase(&testCase,false,true); + freeTestCase(&testCase); +} + +Test(network, rowadd_singlerigid_7, .description = "Updating a single rigid member") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 +1 0 0 +1 " + "+1 0 +1 0 0 " + "0 -1 +1 +1 -1 " + "0 0 0 -1 +1 " + "+1 +1 0 0 0 ", + 5,5); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} + +Test(network, rowadd_singlerigid_8, .description = "Updating a single rigid member") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 +1 0 0 +1 " + "+1 0 +1 0 0 " + "0 -1 +1 +1 -1 " + "0 0 0 -1 +1 " + "+1 +1 0 0 0 " + "+1 0 +1 +1 0 ", + 6,5); + runRowTestCase(&testCase,true,false); + freeTestCase(&testCase); +} +//TODO: test interleaved addition, test using random sampling + test erdos-renyi generated graphs \ No newline at end of file From d917a153d52050151b7770268cb89c7111ba68ed Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Mon, 13 May 2024 09:31:21 +0200 Subject: [PATCH 03/63] Add cmake-build-release to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0c93093761..afed299126 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ build* debug/ release/ cmake-build-debug/ +cmake-build-release/ # check folder, files created by or for `make test` check/results/ From dcff7adcf24ee8b4d014d7e076029aace394bd45 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Wed, 15 May 2024 09:20:35 +0200 Subject: [PATCH 04/63] Only use relevant includes --- src/scip/network.c | 1 + src/scip/network.h | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index f3271422e8..eea41d6304 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -29,6 +29,7 @@ */ #include "scip/network.h" +#include "scip/scip.h" #include ///Types which define matrix sizes diff --git a/src/scip/network.h b/src/scip/network.h index fabaca5ade..d4c03c972c 100644 --- a/src/scip/network.h +++ b/src/scip/network.h @@ -60,8 +60,10 @@ #ifndef __SCIP_NETWORK_H__ #define __SCIP_NETWORK_H__ -#include "scip.h" //TODO; reduce to minimal set of includes, probably only SCIP and memory functions are needed - +#include "scip/def.h" +#include "blockmemshell/memory.h" +#include "scip/type_retcode.h" +#include "scip/type_scip.h" #ifdef cplusplus extern "C" { #endif From 3c8a73f73d30f4dca286341e31fa178de4ab977d Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Wed, 15 May 2024 09:04:33 +0200 Subject: [PATCH 05/63] correct style to guidelines --- src/scip/network.c | 16999 +++++++++++++++++++--------------- src/scip/network.h | 278 +- tests/src/network/network.c | 2607 +++--- 3 files changed, 11043 insertions(+), 8841 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index eea41d6304..69dcbdbb9d 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -37,24 +37,32 @@ typedef int spqr_matrix_size; typedef spqr_matrix_size spqr_row; typedef spqr_matrix_size spqr_col; -#define SPQR_INVALID INT_MAX +#define SPQR_INVALID INT_MAX #define SPQR_INVALID_ROW SPQR_INVALID #define SPQR_INVALID_COL SPQR_INVALID #ifndef NDEBUG -static SCIP_Bool SPQRrowIsInvalid(spqr_row row){ - return row == SPQR_INVALID_ROW; + +static SCIP_Bool SPQRrowIsInvalid(spqr_row row) +{ + return row == SPQR_INVALID_ROW; } -static SCIP_Bool SPQRcolIsInvalid(spqr_col col){ - return col == SPQR_INVALID_COL; +static SCIP_Bool SPQRcolIsInvalid(spqr_col col) +{ + return col == SPQR_INVALID_COL; } -static SCIP_Bool SPQRrowIsValid(spqr_row row){ - return !SPQRrowIsInvalid(row); + +static SCIP_Bool SPQRrowIsValid(spqr_row row) +{ + return !SPQRrowIsInvalid(row); } -static SCIP_Bool SPQRcolIsValid(spqr_col col){ - return !SPQRcolIsInvalid(col); + +static SCIP_Bool SPQRcolIsValid(spqr_col col) +{ + return !SPQRcolIsInvalid(col); } + #endif //Columns 0..x correspond to elements 0..x @@ -63,2411 +71,3148 @@ static SCIP_Bool SPQRcolIsValid(spqr_col col){ #define MARKER_COLUMN_ELEMENT (INT_MAX) typedef int spqr_element; -static SCIP_Bool SPQRelementIsRow(spqr_element element){ - return element < 0; +static SCIP_Bool SPQRelementIsRow(spqr_element element) +{ + return element < 0; } -static SCIP_Bool SPQRelementIsColumn(spqr_element element){ - return !SPQRelementIsRow(element); + +static SCIP_Bool SPQRelementIsColumn(spqr_element element) +{ + return !SPQRelementIsRow(element); } -static spqr_row SPQRelementToRow(spqr_element element){ - assert(SPQRelementIsRow(element)); - return (spqr_row) (-element - 1); + +static spqr_row SPQRelementToRow(spqr_element element) +{ + assert(SPQRelementIsRow(element)); + return (spqr_row) ( -element - 1 ); } -static spqr_element SPQRrowToElement(spqr_row row){ - assert(SPQRrowIsValid(row)); - return (spqr_element) -row-1; + +static spqr_element SPQRrowToElement(spqr_row row) +{ + assert(SPQRrowIsValid(row)); + return (spqr_element) -row - 1; } -static spqr_col SPQRelementToColumn(spqr_element element){ - assert(SPQRelementIsColumn(element)); - return (spqr_col) element; + +static spqr_col SPQRelementToColumn(spqr_element element) +{ + assert(SPQRelementIsColumn(element)); + return (spqr_col) element; } -static spqr_element SPQRcolumnToElement(spqr_col column){ - assert(SPQRcolIsValid(column)); - return (spqr_element) column; + +static spqr_element SPQRcolumnToElement(spqr_col column) +{ + assert(SPQRcolIsValid(column)); + return (spqr_element) column; } typedef int spqr_node; #define SPQR_INVALID_NODE (-1) -static SCIP_Bool SPQRnodeIsInvalid(spqr_node node) { - return node < 0; +static SCIP_Bool SPQRnodeIsInvalid(spqr_node node) +{ + return node < 0; } -static SCIP_Bool SPQRnodeIsValid(spqr_node node) { - return !SPQRnodeIsInvalid(node); + +static SCIP_Bool SPQRnodeIsValid(spqr_node node) +{ + return !SPQRnodeIsInvalid(node); } typedef int spqr_member; #define SPQR_INVALID_MEMBER (-1) -static SCIP_Bool SPQRmemberIsInvalid(spqr_member member) { - return member < 0; +static SCIP_Bool SPQRmemberIsInvalid(spqr_member member) +{ + return member < 0; } -static SCIP_Bool SPQRmemberIsValid(spqr_member member) { - return !SPQRmemberIsInvalid(member); + +static SCIP_Bool SPQRmemberIsValid(spqr_member member) +{ + return !SPQRmemberIsInvalid(member); } typedef int spqr_arc; #define SPQR_INVALID_ARC (-1) -static SCIP_Bool SPQRarcIsInvalid(spqr_arc arc) { - return arc < 0; +static SCIP_Bool SPQRarcIsInvalid(spqr_arc arc) +{ + return arc < 0; } -static SCIP_Bool SPQRarcIsValid(spqr_arc arc){ - return !SPQRarcIsInvalid(arc); + +static SCIP_Bool SPQRarcIsValid(spqr_arc arc) +{ + return !SPQRarcIsInvalid(arc); } -typedef enum { - SPQR_MEMBERTYPE_RIGID = 0, //Also known as triconnected components - SPQR_MEMBERTYPE_PARALLEL = 1,//Also known as a 'bond' - SPQR_MEMBERTYPE_SERIES = 2, //Also known as 'polygon' or 'cycle' - SPQR_MEMBERTYPE_LOOP = 3, - SPQR_MEMBERTYPE_UNASSIGNED = 4 // To indicate that the member has been merged/is not representative +typedef enum +{ + SPQR_MEMBERTYPE_RIGID = 0, //Also known as triconnected components + SPQR_MEMBERTYPE_PARALLEL = 1,//Also known as a 'bond' + SPQR_MEMBERTYPE_SERIES = 2, //Also known as 'polygon' or 'cycle' + SPQR_MEMBERTYPE_LOOP = 3, + SPQR_MEMBERTYPE_UNASSIGNED = 4// To indicate that the member has been merged/is not representative } SPQRMemberType; -typedef struct{ - spqr_arc previous; - spqr_arc next; -}SPQRNetworkDecompositionArcListNode; +typedef struct +{ + spqr_arc previous; + spqr_arc next; +} SPQRNetworkDecompositionArcListNode; -typedef struct { - spqr_node representativeNode; - spqr_arc firstArc;//first arc of the neighbouring arcs - int numArcs; +typedef struct +{ + spqr_node representativeNode; + spqr_arc firstArc;//first arc of the neighbouring arcs + int numArcs; } SPQRNetworkDecompositionNode; -typedef struct { - spqr_node head; - spqr_node tail; - spqr_member member; - spqr_member childMember; - SPQRNetworkDecompositionArcListNode headArcListNode; - SPQRNetworkDecompositionArcListNode tailArcListNode; - SPQRNetworkDecompositionArcListNode arcListNode; //Linked-list node of the array of arcs of the member which this arc is in - - spqr_element element; - - //Signed union-find for arc directions - //For non-rigid members every arc is it's own representative, and the direction is simply given by the boolean - //For rigid members, every arc is represented by another arc in the member, - //and the direction can be found by multiplying the signs along the union-find path - spqr_arc representative; - SCIP_Bool reversed; +typedef struct +{ + spqr_node head; + spqr_node tail; + spqr_member member; + spqr_member childMember; + SPQRNetworkDecompositionArcListNode headArcListNode; + SPQRNetworkDecompositionArcListNode tailArcListNode; + SPQRNetworkDecompositionArcListNode arcListNode;//Linked-list node of the array of arcs of the member which this arc is in + + spqr_element element; + + //Signed union-find for arc directions + //For non-rigid members every arc is it's own representative, and the direction is simply given by the boolean + //For rigid members, every arc is represented by another arc in the member, + //and the direction can be found by multiplying the signs along the union-find path + spqr_arc representative; + SCIP_Bool reversed; } SPQRNetworkDecompositionArc; -typedef struct { - spqr_member representativeMember; - SPQRMemberType type; +typedef struct +{ + spqr_member representativeMember; + SPQRMemberType type; - spqr_member parentMember; - spqr_arc markerToParent; - spqr_arc markerOfParent; + spqr_member parentMember; + spqr_arc markerToParent; + spqr_arc markerOfParent; - spqr_arc firstArc; //First of the members' linked-list arc array - int numArcs; + spqr_arc firstArc;//First of the members' linked-list arc array + int numArcs; } SPQRNetworkDecompositionMember; -struct SCIP_NetworkDecomposition { - int numArcs; - int memArcs; - SPQRNetworkDecompositionArc *arcs; - spqr_arc firstFreeArc; +struct SCIP_Netmatdec +{ + int numArcs; + int memArcs; + SPQRNetworkDecompositionArc* arcs; + spqr_arc firstFreeArc; - int memMembers; - int numMembers; - SPQRNetworkDecompositionMember *members; + int memMembers; + int numMembers; + SPQRNetworkDecompositionMember* members; - int memNodes; - int numNodes; - SPQRNetworkDecompositionNode *nodes; + int memNodes; + int numNodes; + SPQRNetworkDecompositionNode* nodes; - int memRows; - int numRows; - spqr_arc * rowArcs; + int memRows; + int numRows; + spqr_arc* rowArcs; - int memColumns; - int numColumns; - spqr_arc * columnArcs; + int memColumns; + int numColumns; + spqr_arc* columnArcs; - SCIP * env; + SCIP* env; - int numConnectedComponents; + int numConnectedComponents; }; -static void swap_ints(int* a, int* b){ - int temp = *a; - *a = *b; - *b = temp; +static void swap_ints( + int* a, + int* b +) +{ + int temp = *a; + *a = *b; + *b = temp; } + #ifndef NDEBUG -static SCIP_Bool nodeIsRepresentative(const SCIP_NETWORKDECOMP *dec, spqr_node node) { - assert(dec); - assert(node < dec->memNodes); - assert(SPQRnodeIsValid(node)); - return SPQRnodeIsInvalid(dec->nodes[node].representativeNode); +static SCIP_Bool nodeIsRepresentative( + const SCIP_NETMATDEC* dec, + spqr_node node +) +{ + assert(dec); + assert(node < dec->memNodes); + assert(SPQRnodeIsValid(node)); + + return SPQRnodeIsInvalid(dec->nodes[node].representativeNode); } + #endif -static spqr_node findNode(SCIP_NETWORKDECOMP *dec, spqr_node node) { - assert(dec); - assert(SPQRnodeIsValid(node)); - assert(node < dec->memNodes); +static spqr_node findNode( + SCIP_NETMATDEC* dec, + spqr_node node +) +{ + assert(dec); + assert(SPQRnodeIsValid(node)); + assert(node < dec->memNodes); + + spqr_node current = node; + spqr_node next; + + //traverse down tree to find the root + while( SPQRnodeIsValid(next = dec->nodes[current].representativeNode)) + { + current = next; + assert(current < dec->memNodes); + } + + spqr_node root = current; + current = node; + + //update all pointers along path to point to root, flattening the tree + while( SPQRnodeIsValid(next = dec->nodes[current].representativeNode)) + { + dec->nodes[current].representativeNode = root; + current = next; + assert(current < dec->memNodes); + } + return root; +} + +static spqr_node findNodeNoCompression( + const SCIP_NETMATDEC* dec, + spqr_node node +) +{ + assert(dec); + assert(SPQRnodeIsValid(node)); + assert(node < dec->memNodes); + + spqr_node current = node; + spqr_node next; + + //traverse down tree to find the root + while( SPQRnodeIsValid(next = dec->nodes[current].representativeNode)) + { + current = next; + assert(current < dec->memNodes); + } + spqr_node root = current; + return root; +} + +static spqr_node findArcTail( + SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); - spqr_node current = node; - spqr_node next; + spqr_node representative = findNode(dec, dec->arcs[arc].tail); + dec->arcs[arc].tail = representative;//update the arc information - //traverse down tree to find the root - while (SPQRnodeIsValid(next = dec->nodes[current].representativeNode)) { - current = next; - assert(current < dec->memNodes); - } - - spqr_node root = current; - current = node; - - //update all pointers along path to point to root, flattening the tree - while (SPQRnodeIsValid(next = dec->nodes[current].representativeNode)) { - dec->nodes[current].representativeNode = root; - current = next; - assert(current < dec->memNodes); - } - return root; -} - -static spqr_node findNodeNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_node node) { - assert(dec); - assert(SPQRnodeIsValid(node)); - assert(node < dec->memNodes); - - spqr_node current = node; - spqr_node next; - - //traverse down tree to find the root - while (SPQRnodeIsValid(next = dec->nodes[current].representativeNode)) { - current = next; - assert(current < dec->memNodes); - } - spqr_node root = current; - return root; -} - -static spqr_node findArcTail(SCIP_NETWORKDECOMP *dec, spqr_arc arc) { - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); - - spqr_node representative = findNode(dec, dec->arcs[arc].tail); - dec->arcs[arc].tail = representative; //update the arc information - - return representative; -} -static spqr_node findArcHead(SCIP_NETWORKDECOMP *dec, spqr_arc arc) { - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); - - spqr_node representative = findNode(dec, dec->arcs[arc].head); - dec->arcs[arc].head = representative;//update the arc information - - return representative; -} -static spqr_node findArcHeadNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_arc arc) { - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); - - spqr_node representative = findNodeNoCompression(dec, dec->arcs[arc].head); - return representative; -} -static spqr_node findArcTailNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_arc arc) { - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); - - spqr_node representative = findNodeNoCompression(dec, dec->arcs[arc].tail); - return representative; -} - - -static spqr_arc getFirstNodeArc(const SCIP_NETWORKDECOMP * dec, spqr_node node){ - assert(dec); - assert(SPQRnodeIsValid(node)); - assert(node < dec->memNodes); - return dec->nodes[node].firstArc; -} -static spqr_arc getNextNodeArcNoCompression(const SCIP_NETWORKDECOMP * dec, spqr_arc arc, spqr_node node){ - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); - assert(nodeIsRepresentative(dec,node)); - - if(findArcHeadNoCompression(dec,arc) == node){ - arc = dec->arcs[arc].headArcListNode.next; - }else{ - assert(findArcTailNoCompression(dec,arc) == node); - arc = dec->arcs[arc].tailArcListNode.next; - } - return arc; -} -static spqr_arc getNextNodeArc(SCIP_NETWORKDECOMP * dec, spqr_arc arc, spqr_node node){ - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); - assert(nodeIsRepresentative(dec,node)); - - if(findArcHead(dec,arc) == node){ - arc = dec->arcs[arc].headArcListNode.next; - }else{ - assert(findArcTailNoCompression(dec,arc) == node); - dec->arcs[arc].tail = node; //This assignment is not necessary but speeds up future queries. - arc = dec->arcs[arc].tailArcListNode.next; - } - return arc; -} -static spqr_arc getPreviousNodeArc(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_node node){ - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); - assert(nodeIsRepresentative(dec,node)); - - if(findArcHead(dec,arc) == node){ - arc = dec->arcs[arc].headArcListNode.previous; - }else{ - assert(findArcTailNoCompression(dec,arc) == node); - dec->arcs[arc].tail = node; //This assignment is not necessary but speeds up future queries. - arc = dec->arcs[arc].tailArcListNode.previous; - } - return arc; -} - -static void mergeNodeArcList(SCIP_NETWORKDECOMP *dec, spqr_node toMergeInto, spqr_node toRemove){ - - spqr_arc firstIntoArc = getFirstNodeArc(dec, toMergeInto); - spqr_arc firstFromArc = getFirstNodeArc(dec, toRemove); - if(SPQRarcIsInvalid(firstIntoArc)){ - //new node has no arcs - dec->nodes[toMergeInto].numArcs += dec->nodes[toRemove].numArcs; - dec->nodes[toRemove].numArcs = 0; - - dec->nodes[toMergeInto].firstArc = dec->nodes[toRemove].firstArc; - dec->nodes[toRemove].firstArc = SPQR_INVALID_ARC; - - return; - }else if (SPQRarcIsInvalid(firstFromArc)){ - //Old node has no arcs; we can just return - return; - } - - spqr_arc lastIntoArc = getPreviousNodeArc(dec, firstIntoArc, toMergeInto); - assert(SPQRarcIsValid(lastIntoArc)); - spqr_arc lastFromArc = getPreviousNodeArc(dec, firstFromArc, toRemove); - assert(SPQRarcIsValid(lastFromArc)); - - - SPQRNetworkDecompositionArcListNode * firstIntoNode = findArcHead(dec, firstIntoArc) == toMergeInto ? - &dec->arcs[firstIntoArc].headArcListNode : - &dec->arcs[firstIntoArc].tailArcListNode; - SPQRNetworkDecompositionArcListNode * lastIntoNode = findArcHead(dec, lastIntoArc) == toMergeInto ? - &dec->arcs[lastIntoArc].headArcListNode : - &dec->arcs[lastIntoArc].tailArcListNode; - - SPQRNetworkDecompositionArcListNode * firstFromNode = findArcHead(dec, firstFromArc) == toRemove ? - &dec->arcs[firstFromArc].headArcListNode : - &dec->arcs[firstFromArc].tailArcListNode; - SPQRNetworkDecompositionArcListNode * lastFromNode = findArcHead(dec, lastFromArc) == toRemove ? - &dec->arcs[lastFromArc].headArcListNode : - &dec->arcs[lastFromArc].tailArcListNode; - - firstIntoNode->previous = lastFromArc; - lastIntoNode->next = firstFromArc; - firstFromNode->previous = lastIntoArc; - lastFromNode->next = firstIntoArc; - - dec->nodes[toMergeInto].numArcs += dec->nodes[toRemove].numArcs; - dec->nodes[toRemove].numArcs = 0; - dec->nodes[toRemove].firstArc = SPQR_INVALID_ARC; -} - -static void arcFlipReversed(SCIP_NETWORKDECOMP *dec, spqr_arc arc){ - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); - dec->arcs[arc].reversed = !dec->arcs[arc].reversed; -} -static void arcSetReversed(SCIP_NETWORKDECOMP *dec, spqr_arc arc, SCIP_Bool reversed){ - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); - dec->arcs[arc].reversed = reversed; -} -static void arcSetRepresentative(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_arc representative){ - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); - assert(representative == SPQR_INVALID_ARC || SPQRarcIsValid(representative)); - dec->arcs[arc].representative = representative; -} - -static spqr_node mergeNodes(SCIP_NETWORKDECOMP *dec, spqr_node first, spqr_node second) { - assert(dec); - assert(nodeIsRepresentative(dec, first)); - assert(nodeIsRepresentative(dec, second)); - assert(first != second); //We cannot merge a node into itself - assert(first < dec->memNodes); - assert(second < dec->memNodes); - - //The rank is stored as a negative number: we decrement it making the negative number larger. - // We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement. - spqr_node firstRank = dec->nodes[first].representativeNode; - spqr_node secondRank = dec->nodes[second].representativeNode; - if (firstRank > secondRank) { - swap_ints(&first, &second); - } - //first becomes representative; we merge all of the arcs of second into first - mergeNodeArcList(dec,first,second); - dec->nodes[second].representativeNode = first; - if (firstRank == secondRank) { - --dec->nodes[first].representativeNode; - } - return first; -} - -static SCIP_Bool memberIsRepresentative(const SCIP_NETWORKDECOMP *dec, spqr_member member) { - assert(dec); - assert(member < dec->memMembers); - assert(SPQRmemberIsValid(member)); - - return SPQRmemberIsInvalid(dec->members[member].representativeMember); -} - -static spqr_member findMember(SCIP_NETWORKDECOMP *dec, spqr_member member) { - assert(dec); - assert(member < dec->memMembers); - assert(SPQRmemberIsValid(member)); - - spqr_member current = member; - spqr_member next; - - //traverse down tree to find the root - while (SPQRmemberIsValid(next = dec->members[current].representativeMember)) { - current = next; - assert(current < dec->memMembers); - } - - spqr_member root = current; - current = member; - - //update all pointers along path to point to root, flattening the tree - while (SPQRmemberIsValid(next = dec->members[current].representativeMember)) { - dec->members[current].representativeMember = root; - current = next; - assert(current < dec->memMembers); - } - return root; -} - -static spqr_member findMemberNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_member member) { - assert(dec); - assert(member < dec->memMembers); - assert(SPQRmemberIsValid(member)); - - spqr_member current = member; - spqr_member next; - - //traverse down tree to find the root - while (SPQRmemberIsValid(next = dec->members[current].representativeMember)) { - current = next; - assert(current < dec->memMembers); - } - - spqr_member root = current; - return root; + return representative; } -static spqr_member mergeMembers(SCIP_NETWORKDECOMP *dec, spqr_member first, spqr_member second) { - assert(dec); - assert(memberIsRepresentative(dec, first)); - assert(memberIsRepresentative(dec, second)); - assert(first != second); //We cannot merge a member into itself - assert(first < dec->memMembers); - assert(second < dec->memMembers); - - //The rank is stored as a negative number: we decrement it making the negative number larger. - // We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement. - spqr_member firstRank = dec->members[first].representativeMember; - spqr_member secondRank = dec->members[second].representativeMember; - if (firstRank > secondRank) { - swap_ints(&first, &second); - } - dec->members[second].representativeMember = first; - if (firstRank == secondRank) { - --dec->members[first].representativeMember; - } - return first; -} - -static spqr_member findArcMember(SCIP_NETWORKDECOMP *dec, spqr_arc arc) { - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); - - spqr_member representative = findMember(dec, dec->arcs[arc].member); - dec->arcs[arc].member = representative; - return representative; -} +static spqr_node findArcHead( + SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); -static spqr_member findArcMemberNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_arc arc) { - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); + spqr_node representative = findNode(dec, dec->arcs[arc].head); + dec->arcs[arc].head = representative;//update the arc information - spqr_member representative = findMemberNoCompression(dec, dec->arcs[arc].member); - return representative; + return representative; } -static spqr_member findMemberParent(SCIP_NETWORKDECOMP *dec, spqr_member member) { - assert(dec); - assert(member < dec->memMembers); - assert(SPQRmemberIsValid(member)); - assert(memberIsRepresentative(dec,member)); +static spqr_node findArcHeadNoCompression( + const SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + spqr_node representative = findNodeNoCompression(dec, dec->arcs[arc].head); + return representative; +} - if(SPQRmemberIsInvalid(dec->members[member].parentMember)){ - return dec->members[member].parentMember; - } - spqr_member parent_representative = findMember(dec, dec->members[member].parentMember); - dec->members[member].parentMember = parent_representative; +static spqr_node findArcTailNoCompression( + const SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); - return parent_representative; + spqr_node representative = findNodeNoCompression(dec, dec->arcs[arc].tail); + return representative; } -static spqr_member findMemberParentNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_member member) { - assert(dec); - assert(member < dec->memMembers); - assert(SPQRmemberIsValid(member)); - assert(memberIsRepresentative(dec,member)); - if(SPQRmemberIsInvalid(dec->members[member].parentMember)){ - return dec->members[member].parentMember; - } - spqr_member parent_representative = findMemberNoCompression(dec, dec->members[member].parentMember); - return parent_representative; +static spqr_arc getFirstNodeArc( + const SCIP_NETMATDEC* dec, + spqr_node node +) +{ + assert(dec); + assert(SPQRnodeIsValid(node)); + assert(node < dec->memNodes); + return dec->nodes[node].firstArc; } -static spqr_member findArcChildMember(SCIP_NETWORKDECOMP *dec, spqr_arc arc) { - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); +static spqr_arc getNextNodeArcNoCompression( + const SCIP_NETMATDEC* dec, + spqr_arc arc, + spqr_node node +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + assert(nodeIsRepresentative(dec, node)); + + if( findArcHeadNoCompression(dec, arc) == node ) + { + arc = dec->arcs[arc].headArcListNode.next; + } else + { + assert(findArcTailNoCompression(dec, arc) == node); + arc = dec->arcs[arc].tailArcListNode.next; + } + return arc; +} + +static spqr_arc getNextNodeArc( + SCIP_NETMATDEC* dec, + spqr_arc arc, + spqr_node node +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + assert(nodeIsRepresentative(dec, node)); + + if( findArcHead(dec, arc) == node ) + { + arc = dec->arcs[arc].headArcListNode.next; + } else + { + assert(findArcTailNoCompression(dec, arc) == node); + dec->arcs[arc].tail = node;//This assignment is not necessary but speeds up future queries. + arc = dec->arcs[arc].tailArcListNode.next; + } + return arc; +} + +static spqr_arc getPreviousNodeArc( + SCIP_NETMATDEC* dec, + spqr_arc arc, + spqr_node node +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + assert(nodeIsRepresentative(dec, node)); + + if( findArcHead(dec, arc) == node ) + { + arc = dec->arcs[arc].headArcListNode.previous; + } else + { + assert(findArcTailNoCompression(dec, arc) == node); + dec->arcs[arc].tail = node;//This assignment is not necessary but speeds up future queries. + arc = dec->arcs[arc].tailArcListNode.previous; + } + return arc; +} + +static void mergeNodeArcList( + SCIP_NETMATDEC* dec, + spqr_node toMergeInto, + spqr_node toRemove +) +{ - spqr_member representative = findMember(dec, dec->arcs[arc].childMember); - dec->arcs[arc].childMember = representative; - return representative; + spqr_arc firstIntoArc = getFirstNodeArc(dec, toMergeInto); + spqr_arc firstFromArc = getFirstNodeArc(dec, toRemove); + if( SPQRarcIsInvalid(firstIntoArc)) + { + //new node has no arcs + dec->nodes[toMergeInto].numArcs += dec->nodes[toRemove].numArcs; + dec->nodes[toRemove].numArcs = 0; + + dec->nodes[toMergeInto].firstArc = dec->nodes[toRemove].firstArc; + dec->nodes[toRemove].firstArc = SPQR_INVALID_ARC; + + return; + } else if( SPQRarcIsInvalid(firstFromArc)) + { + //Old node has no arcs; we can just return + return; + } + + spqr_arc lastIntoArc = getPreviousNodeArc(dec, firstIntoArc, toMergeInto); + assert(SPQRarcIsValid(lastIntoArc)); + spqr_arc lastFromArc = getPreviousNodeArc(dec, firstFromArc, toRemove); + assert(SPQRarcIsValid(lastFromArc)); + + + SPQRNetworkDecompositionArcListNode* firstIntoNode = + findArcHead(dec, firstIntoArc) == toMergeInto ? &dec->arcs[firstIntoArc].headArcListNode + : &dec->arcs[firstIntoArc].tailArcListNode; + SPQRNetworkDecompositionArcListNode* lastIntoNode = + findArcHead(dec, lastIntoArc) == toMergeInto ? &dec->arcs[lastIntoArc].headArcListNode + : &dec->arcs[lastIntoArc].tailArcListNode; + + SPQRNetworkDecompositionArcListNode* firstFromNode = + findArcHead(dec, firstFromArc) == toRemove ? &dec->arcs[firstFromArc].headArcListNode + : &dec->arcs[firstFromArc].tailArcListNode; + SPQRNetworkDecompositionArcListNode* lastFromNode = + findArcHead(dec, lastFromArc) == toRemove ? &dec->arcs[lastFromArc].headArcListNode + : &dec->arcs[lastFromArc].tailArcListNode; + + firstIntoNode->previous = lastFromArc; + lastIntoNode->next = firstFromArc; + firstFromNode->previous = lastIntoArc; + lastFromNode->next = firstIntoArc; + + dec->nodes[toMergeInto].numArcs += dec->nodes[toRemove].numArcs; + dec->nodes[toRemove].numArcs = 0; + dec->nodes[toRemove].firstArc = SPQR_INVALID_ARC; +} + +static void arcFlipReversed( + SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + dec->arcs[arc].reversed = !dec->arcs[arc].reversed; } -static spqr_member findArcChildMemberNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_arc arc) { - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); - - spqr_member representative = findMemberNoCompression(dec, dec->arcs[arc].childMember); - return representative; +static void arcSetReversed( + SCIP_NETMATDEC* dec, + spqr_arc arc, + SCIP_Bool reversed +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + dec->arcs[arc].reversed = reversed; } -// Only accounts for CHILD markers, not parent markers! -static SCIP_Bool arcIsMarker(const SCIP_NETWORKDECOMP *dec, spqr_arc arc) { - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); +static void arcSetRepresentative( + SCIP_NETMATDEC* dec, + spqr_arc arc, + spqr_arc representative +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + assert(representative == SPQR_INVALID_ARC || SPQRarcIsValid(representative)); + dec->arcs[arc].representative = representative; +} + +static spqr_node mergeNodes( + SCIP_NETMATDEC* dec, + spqr_node first, + spqr_node second +) +{ + assert(dec); + assert(nodeIsRepresentative(dec, first)); + assert(nodeIsRepresentative(dec, second)); + assert(first != second);//We cannot merge a node into itself + assert(first < dec->memNodes); + assert(second < dec->memNodes); + + //The rank is stored as a negative number: we decrement it making the negative number larger. + // We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement. + spqr_node firstRank = dec->nodes[first].representativeNode; + spqr_node secondRank = dec->nodes[second].representativeNode; + if( firstRank > secondRank ) + { + swap_ints(&first, &second); + } + //first becomes representative; we merge all of the arcs of second into first + mergeNodeArcList(dec, first, second); + dec->nodes[second].representativeNode = first; + if( firstRank == secondRank ) + { + --dec->nodes[first].representativeNode; + } + return first; +} + +static SCIP_Bool memberIsRepresentative( + const SCIP_NETMATDEC* dec, + spqr_member member +) +{ + assert(dec); + assert(member < dec->memMembers); + assert(SPQRmemberIsValid(member)); - return SPQRmemberIsValid(dec->arcs[arc].childMember); + return SPQRmemberIsInvalid(dec->members[member].representativeMember); } -static SCIP_Bool arcIsTree(const SCIP_NETWORKDECOMP *dec, spqr_arc arc) { - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); +static spqr_member findMember( + SCIP_NETMATDEC* dec, + spqr_member member +) +{ + assert(dec); + assert(member < dec->memMembers); + assert(SPQRmemberIsValid(member)); + + spqr_member current = member; + spqr_member next; + + //traverse down tree to find the root + while( SPQRmemberIsValid(next = dec->members[current].representativeMember)) + { + current = next; + assert(current < dec->memMembers); + } + + spqr_member root = current; + current = member; + + //update all pointers along path to point to root, flattening the tree + while( SPQRmemberIsValid(next = dec->members[current].representativeMember)) + { + dec->members[current].representativeMember = root; + current = next; + assert(current < dec->memMembers); + } + return root; +} + +static spqr_member findMemberNoCompression( + const SCIP_NETMATDEC* dec, + spqr_member member +) +{ + assert(dec); + assert(member < dec->memMembers); + assert(SPQRmemberIsValid(member)); - return SPQRelementIsRow(dec->arcs[arc].element); -} + spqr_member current = member; + spqr_member next; -typedef struct { - spqr_arc representative; - SCIP_Bool reversed; -} ArcSign; -//find -#ifndef NDEBUG -static SCIP_Bool arcIsRepresentative(const SCIP_NETWORKDECOMP *dec, spqr_arc arc) { - assert(dec); - assert(arc < dec->memArcs); - assert(SPQRarcIsValid(arc)); + //traverse down tree to find the root + while( SPQRmemberIsValid(next = dec->members[current].representativeMember)) + { + current = next; + assert(current < dec->memMembers); + } - return SPQRarcIsInvalid(dec->arcs[arc].representative); -} -#endif -static ArcSign findArcSign(SCIP_NETWORKDECOMP *dec, spqr_arc arc) { - assert(dec); - assert(arc < dec->memArcs); - assert(SPQRarcIsValid(arc)); - - spqr_arc current = arc; - spqr_arc next; - - SCIP_Bool totalReversed = dec->arcs[current].reversed; - //traverse down tree to find the root - while (SPQRarcIsValid(next = dec->arcs[current].representative)) { - current = next; - assert(current < dec->memArcs); - //swap boolean only if new arc is reversed - totalReversed = (totalReversed != dec->arcs[current].reversed); - } - - spqr_arc root = current; - current = arc; - - SCIP_Bool currentReversed = totalReversed != dec->arcs[root].reversed; - //update all pointers along path to point to root, flattening the tree - - while (SPQRarcIsValid(next = dec->arcs[current].representative)) { - SCIP_Bool wasReversed = dec->arcs[current].reversed; - - dec->arcs[current].reversed = currentReversed; - currentReversed = (currentReversed != wasReversed); - - dec->arcs[current].representative = root; - current = next; - assert(current < dec->memArcs); - } - - ArcSign sign; - sign.reversed = totalReversed; - sign.representative = root; - return sign; -} - -static ArcSign findArcSignNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_arc arc) { - assert(dec); - assert(arc < dec->memArcs); - assert(SPQRarcIsValid(arc)); - - spqr_arc current = arc; - spqr_arc next; - - SCIP_Bool totalReversed = dec->arcs[current].reversed; - //traverse down tree to find the root - while (SPQRarcIsValid(next = dec->arcs[current].representative)) { - current = next; - assert(current < dec->memArcs); - //swap boolean only if new arc is reversed - totalReversed = (totalReversed != dec->arcs[current].reversed); - } - ArcSign sign; - sign.reversed = totalReversed; - sign.representative = current; - return sign; -} -//Find the arc tail/head, but accounting for reflection -static spqr_node findEffectiveArcHead(SCIP_NETWORKDECOMP *dec, spqr_arc arc){ - assert(dec); - if(findArcSign(dec,arc).reversed){ - return findArcTail(dec,arc); - }else{ - return findArcHead(dec,arc); - } -} -static spqr_node findEffectiveArcTail(SCIP_NETWORKDECOMP *dec, spqr_arc arc){ - assert(dec); - if(findArcSign(dec,arc).reversed){ - return findArcHead(dec,arc); - }else{ - return findArcTail(dec,arc); - } -} -static spqr_node findEffectiveArcHeadNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_arc arc){ - assert(dec); - if(findArcSignNoCompression(dec,arc).reversed){ - return findArcTailNoCompression(dec,arc); - }else{ - return findArcHeadNoCompression(dec,arc); - } -} -static spqr_node findEffectiveArcTailNoCompression(const SCIP_NETWORKDECOMP *dec, spqr_arc arc){ - assert(dec); - if(findArcSignNoCompression(dec,arc).reversed){ - return findArcHeadNoCompression(dec,arc); - }else{ - return findArcTailNoCompression(dec,arc); - } -} -///Merge for signed union find of the arc directions. -///Is not symmetric, in the sense that the arc directions of coponent first are guaranteed not to change but those of second may change -///Based on whether one wants the reflection or not -static spqr_arc mergeArcSigns(SCIP_NETWORKDECOMP *dec, spqr_arc first, spqr_arc second, - SCIP_Bool reflectRelative) { - assert(dec); - assert(arcIsRepresentative(dec, first)); - assert(arcIsRepresentative(dec, second)); - assert(first != second); //We cannot merge a member into itself - assert(first < dec->memArcs); - assert(second < dec->memArcs); - - //The rank is stored as a negative number: we decrement it making the negative number larger. - // We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement. - spqr_member firstRank = dec->arcs[first].representative; - spqr_member secondRank = dec->arcs[second].representative; - - if (firstRank > secondRank) { - swap_ints(&first, &second); - } - dec->arcs[second].representative = first; - if (firstRank == secondRank) { - --dec->arcs[first].representative; - } - //These boolean formula's cover all 16 possible cases, such that the relative orientation of the first is not changed - SCIP_Bool equal = dec->arcs[first].reversed == dec->arcs[second].reversed; - dec->arcs[second].reversed = (equal == reflectRelative); - if(firstRank > secondRank){ - dec->arcs[first].reversed = (dec->arcs[first].reversed != reflectRelative); - } - return first; -} - -static SCIP_Bool arcIsReversedNonRigid(const SCIP_NETWORKDECOMP *dec, spqr_arc arc){ - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); - - return dec->arcs[arc].reversed; -} - - -static spqr_element arcGetElement(const SCIP_NETWORKDECOMP * dec, spqr_arc arc){ - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); - - return dec->arcs[arc].element; -} -SCIP_Bool SCIPNetworkDecompositionContainsRow(const SCIP_NETWORKDECOMP * dec, spqr_row row){ - assert(SPQRrowIsValid(row) && (int) row < dec->memRows); - assert(dec); - return SPQRarcIsValid(dec->rowArcs[row]); -} -SCIP_Bool SCIPNetworkDecompositionContainsColumn(const SCIP_NETWORKDECOMP *dec, spqr_col col){ - assert(SPQRcolIsValid(col) && (int) col < dec->memColumns); - assert(dec); - return SPQRarcIsValid(dec->columnArcs[col]); -} -static void setDecompositionColumnArc(SCIP_NETWORKDECOMP *dec, spqr_col col, spqr_arc arc){ - assert(SPQRcolIsValid(col) && (int)col < dec->memColumns); - assert(dec); - assert(SPQRarcIsValid(arc)); - dec->columnArcs[col] = arc; -} -static void setDecompositionRowArc(SCIP_NETWORKDECOMP *dec, spqr_row row, spqr_arc arc){ - assert(SPQRrowIsValid(row) && (int) row < dec->memRows); - assert(dec); - assert(SPQRarcIsValid(arc)); - dec->rowArcs[row] = arc; -} -static spqr_arc getDecompositionColumnArc(const SCIP_NETWORKDECOMP *dec, spqr_col col){ - assert(SPQRcolIsValid(col) && (int) col < dec->memColumns); - assert(dec); - return dec->columnArcs[col]; -} -static spqr_arc getDecompositionRowArc(const SCIP_NETWORKDECOMP *dec, spqr_row row){ - assert(SPQRrowIsValid(row) && (int) row < dec->memRows); - assert(dec); - return dec->rowArcs[row]; -} - -SCIP_RETCODE SCIPNetworkDecompositionCreate(SCIP* env, SCIP_NETWORKDECOMP **pDecomposition, int numRows, int numColumns){ - assert(env); - assert(pDecomposition); - assert(!*pDecomposition); - - SCIP_CALL(SCIPallocBlockMemory(env, pDecomposition)); - SCIP_NETWORKDECOMP *dec = *pDecomposition; - dec->env = env; - - //Initialize arc array data - int initialMemArcs = 8; - { - assert(initialMemArcs > 0); - dec->memArcs = initialMemArcs; - dec->numArcs = 0; - SCIP_CALL(SCIPallocBlockMemoryArray(env, &dec->arcs, (size_t) dec->memArcs)); - for (spqr_arc i = 0; i < dec->memArcs; ++i) { - dec->arcs[i].arcListNode.next = i + 1; - dec->arcs[i].member = SPQR_INVALID_MEMBER; - } - dec->arcs[dec->memArcs - 1].arcListNode.next = SPQR_INVALID_ARC; - dec->firstFreeArc = 0; - } - - //Initialize member array data - int initialMemMembers = 8; - { - assert(initialMemMembers > 0); - dec->memMembers = initialMemMembers; - dec->numMembers = 0; - SCIP_CALL(SCIPallocBlockMemoryArray(env, &dec->members, (size_t) dec->memMembers)); - } - - //Initialize node array data - int initialMemNodes = 8; - { - assert(initialMemNodes > 0); - dec->memNodes = initialMemNodes; - dec->numNodes = 0; - SCIP_CALL(SCIPallocBlockMemoryArray(env, &dec->nodes, (size_t) dec->memNodes)); - } - - //Initialize mappings for rows - { - dec->memRows = numRows; - SCIP_CALL(SCIPallocBlockMemoryArray(env, &dec->rowArcs, (size_t) dec->memRows)); - for (int i = 0; i < dec->memRows; ++i) { - dec->rowArcs[i] = SPQR_INVALID_ARC; - } - } - //Initialize mappings for columns - { - dec->memColumns = numColumns; - dec->numColumns = 0; - SCIP_CALL(SCIPallocBlockMemoryArray(env, &dec->columnArcs, (size_t) dec->memColumns)); - for (int i = 0; i < dec->memColumns; ++i) { - dec->columnArcs[i] = SPQR_INVALID_ARC; - } - } - - dec->numConnectedComponents = 0; - return SCIP_OKAY; -} - -void SCIPNetworkDecompositionFree(SCIP_NETWORKDECOMP **pDec){ - assert(pDec); - assert(*pDec); - - SCIP_NETWORKDECOMP *dec = *pDec; - SCIPfreeBlockMemoryArray(dec->env, &dec->columnArcs,dec->memColumns); - SCIPfreeBlockMemoryArray(dec->env, &dec->rowArcs,dec->memRows); - SCIPfreeBlockMemoryArray(dec->env, &dec->nodes,dec->memNodes); - SCIPfreeBlockMemoryArray(dec->env, &dec->members,dec->memMembers); - SCIPfreeBlockMemoryArray(dec->env, &dec->arcs,dec->memArcs); - - SCIPfreeBlockMemory(dec->env, pDec); - -} -static spqr_arc getFirstMemberArc(const SCIP_NETWORKDECOMP * dec, spqr_member member){ - assert(dec); - assert(SPQRmemberIsValid(member)); - assert(member < dec->memMembers); - return dec->members[member].firstArc; -} -static spqr_arc getNextMemberArc(const SCIP_NETWORKDECOMP * dec, spqr_arc arc){ - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); - arc = dec->arcs[arc].arcListNode.next; - return arc; -} -static spqr_arc getPreviousMemberArc(const SCIP_NETWORKDECOMP *dec, spqr_arc arc){ - assert(dec); - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); - arc = dec->arcs[arc].arcListNode.previous; - return arc; -} - -static void addArcToMemberArcList(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_member member){ - spqr_arc firstMemberArc = getFirstMemberArc(dec, member); - - if(SPQRarcIsValid(firstMemberArc)){ - spqr_arc lastMemberArc = getPreviousMemberArc(dec, firstMemberArc); - dec->arcs[arc].arcListNode.next = firstMemberArc; - dec->arcs[arc].arcListNode.previous = lastMemberArc; - dec->arcs[firstMemberArc].arcListNode.previous = arc; - dec->arcs[lastMemberArc].arcListNode.next = arc; - }else{ - assert(dec->members[member].numArcs == 0); - dec->arcs[arc].arcListNode.next = arc; - dec->arcs[arc].arcListNode.previous = arc; - } - dec->members[member].firstArc = arc; - ++(dec->members[member].numArcs); -} -static SCIP_RETCODE createArc(SCIP_NETWORKDECOMP *dec, spqr_member member,SCIP_Bool reversed, spqr_arc *pArc) { - assert(dec); - assert(pArc); - assert(SPQRmemberIsInvalid(member) || memberIsRepresentative(dec, member)); - - spqr_arc index = dec->firstFreeArc; - if (SPQRarcIsValid(index)) { - dec->firstFreeArc = dec->arcs[index].arcListNode.next; - } else { - //Enlarge array, no free nodes in arc list - int newSize = 2 * dec->memArcs; - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &dec->arcs, dec->memArcs, (size_t) newSize)); - for (int i = dec->memArcs + 1; i < newSize; ++i) { - dec->arcs[i].arcListNode.next = i + 1; - dec->arcs[i].member = SPQR_INVALID_MEMBER; - } - dec->arcs[newSize - 1].arcListNode.next = SPQR_INVALID_ARC; - dec->firstFreeArc = dec->memArcs + 1; - index = dec->memArcs; - dec->memArcs = newSize; - } - - dec->arcs[index].tail = SPQR_INVALID_NODE; - dec->arcs[index].head = SPQR_INVALID_NODE; - dec->arcs[index].member = member; - dec->arcs[index].childMember = SPQR_INVALID_MEMBER; - dec->arcs[index].reversed = reversed; - - dec->arcs[index].headArcListNode.next = SPQR_INVALID_ARC; - dec->arcs[index].headArcListNode.previous = SPQR_INVALID_ARC; - dec->arcs[index].tailArcListNode.next = SPQR_INVALID_ARC; - dec->arcs[index].tailArcListNode.previous = SPQR_INVALID_ARC; - - dec->numArcs++; - - *pArc = index; - - return SCIP_OKAY; -} -static SCIP_RETCODE createRowArc(SCIP_NETWORKDECOMP *dec, spqr_member member, spqr_arc *pArc, - spqr_row row, SCIP_Bool reversed){ - SCIP_CALL(createArc(dec,member,reversed,pArc)); - setDecompositionRowArc(dec,row,*pArc); - addArcToMemberArcList(dec,*pArc,member); - dec->arcs[*pArc].element = SPQRrowToElement(row); - - return SCIP_OKAY; -} -static SCIP_RETCODE createColumnArc(SCIP_NETWORKDECOMP *dec, spqr_member member, spqr_arc *pArc, - spqr_col column, SCIP_Bool reversed){ - SCIP_CALL(createArc(dec,member,reversed,pArc)); - setDecompositionColumnArc(dec,column,*pArc); - addArcToMemberArcList(dec,*pArc,member); - dec->arcs[*pArc].element = SPQRcolumnToElement(column); - - return SCIP_OKAY; -} -static SCIP_RETCODE createMember(SCIP_NETWORKDECOMP *dec, SPQRMemberType type, spqr_member * pMember){ - assert(dec); - assert(pMember); - - if(dec->numMembers == dec->memMembers){ - int newSize = dec->memMembers * 2; - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&dec->members,dec->memMembers,newSize)); - dec->memMembers = newSize; - - } - SPQRNetworkDecompositionMember *data = &dec->members[dec->numMembers]; - data->markerOfParent = SPQR_INVALID_ARC; - data->markerToParent = SPQR_INVALID_ARC; - data->firstArc = SPQR_INVALID_ARC; - data->representativeMember = SPQR_INVALID_MEMBER; - data->numArcs = 0; - data->parentMember = SPQR_INVALID_MEMBER; - data->type = type; - - *pMember = dec->numMembers; - - dec->numMembers++; - return SCIP_OKAY; -} - -static SCIP_RETCODE createNode(SCIP_NETWORKDECOMP *dec, spqr_node * pNode){ - - if(dec->numNodes == dec->memNodes){ - int newSize = dec->memNodes * 2; - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&dec->nodes,dec->memNodes,newSize)); - dec->memNodes = newSize; - } - *pNode = dec->numNodes; - dec->nodes[dec->numNodes].representativeNode = SPQR_INVALID_NODE; - dec->nodes[dec->numNodes].firstArc = SPQR_INVALID_ARC; - dec->nodes[dec->numNodes].numArcs = 0; - dec->numNodes++; - - return SCIP_OKAY; -} -static void removeArcFromNodeArcList(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_node node, SCIP_Bool nodeIsHead){ - SPQRNetworkDecompositionArcListNode * arcListNode = nodeIsHead ? &dec->arcs[arc].headArcListNode : &dec->arcs[arc].tailArcListNode; - - if(dec->nodes[node].numArcs == 1){ - dec->nodes[node].firstArc = SPQR_INVALID_ARC; - }else{ - spqr_arc next_arc = arcListNode->next; - spqr_arc prev_arc = arcListNode->previous; - SPQRNetworkDecompositionArcListNode * nextListNode = findArcHead(dec, next_arc) == node ? &dec->arcs[next_arc].headArcListNode : &dec->arcs[next_arc].tailArcListNode; - SPQRNetworkDecompositionArcListNode * prevListNode = findArcHead(dec, prev_arc) == node ? &dec->arcs[prev_arc].headArcListNode : &dec->arcs[prev_arc].tailArcListNode; - - nextListNode->previous = prev_arc; - prevListNode->next = next_arc; - - if(dec->nodes[node].firstArc == arc){ - dec->nodes[node].firstArc = next_arc; - } - } - --(dec->nodes[node].numArcs); -} -static void addArcToNodeArcList(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_node node, SCIP_Bool nodeIsHead){ - assert(nodeIsRepresentative(dec,node)); - - spqr_arc firstNodeArc = getFirstNodeArc(dec, node); - - SPQRNetworkDecompositionArcListNode * arcListNode = nodeIsHead ? &dec->arcs[arc].headArcListNode : &dec->arcs[arc].tailArcListNode; - if(SPQRarcIsValid(firstNodeArc)){ - SCIP_Bool nextIsHead = findArcHead(dec,firstNodeArc) == node; - SPQRNetworkDecompositionArcListNode *nextListNode = nextIsHead ? &dec->arcs[firstNodeArc].headArcListNode : &dec->arcs[firstNodeArc].tailArcListNode; - spqr_arc lastNodeArc = nextListNode->previous; - - arcListNode->next = firstNodeArc; - arcListNode->previous = lastNodeArc; - - - SCIP_Bool previousIsHead = findArcHead(dec,lastNodeArc) == node; - SPQRNetworkDecompositionArcListNode *previousListNode = previousIsHead ? &dec->arcs[lastNodeArc].headArcListNode : &dec->arcs[lastNodeArc].tailArcListNode; - previousListNode->next = arc; - nextListNode->previous = arc; - - }else{ - arcListNode->next = arc; - arcListNode->previous = arc; - } - dec->nodes[node].firstArc = arc; - ++dec->nodes[node].numArcs; - if(nodeIsHead){ - dec->arcs[arc].head = node; - }else{ - dec->arcs[arc].tail = node; - } -} -static void setArcHeadAndTail(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_node head, spqr_node tail){ - addArcToNodeArcList(dec,arc,head,TRUE); - addArcToNodeArcList(dec,arc,tail,FALSE); -} -static void clearArcHeadAndTail(SCIP_NETWORKDECOMP *dec, spqr_arc arc){ - removeArcFromNodeArcList(dec,arc,findArcHead(dec,arc),TRUE); - removeArcFromNodeArcList(dec,arc,findArcTail(dec,arc),FALSE); - dec->arcs[arc].head = SPQR_INVALID_NODE; - dec->arcs[arc].tail = SPQR_INVALID_NODE; -} -static void changeArcHead(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_node oldHead, spqr_node newHead){ - assert(nodeIsRepresentative(dec,oldHead)); - assert(nodeIsRepresentative(dec,newHead)); - removeArcFromNodeArcList(dec,arc,oldHead,TRUE); - addArcToNodeArcList(dec,arc,newHead,TRUE); -} -static void changeArcTail(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_node oldTail, spqr_node newTail){ - assert(nodeIsRepresentative(dec,oldTail)); - assert(nodeIsRepresentative(dec,newTail)); - removeArcFromNodeArcList(dec,arc,oldTail,FALSE); - addArcToNodeArcList(dec,arc,newTail,FALSE); -} - -static int nodeDegree(SCIP_NETWORKDECOMP *dec, spqr_node node){ - assert(dec); - assert(SPQRnodeIsValid(node)); - assert(node < dec->memNodes); - return dec->nodes[node].numArcs; -} -static SPQRMemberType getMemberType(const SCIP_NETWORKDECOMP *dec, spqr_member member){ - assert(dec); - assert(SPQRmemberIsValid(member)); - assert(member < dec->memMembers); - assert(memberIsRepresentative(dec,member)); - return dec->members[member].type; -} -static void updateMemberType(const SCIP_NETWORKDECOMP *dec, spqr_member member, SPQRMemberType type){ - assert(dec); - assert(SPQRmemberIsValid(member)); - assert(member < dec->memMembers); - assert(memberIsRepresentative(dec,member)); - - dec->members[member].type = type; -} -static spqr_arc markerToParent(const SCIP_NETWORKDECOMP *dec, spqr_member member){ - assert(dec); - assert(SPQRmemberIsValid(member)); - assert(member < dec->memMembers); - assert(memberIsRepresentative(dec,member)); - return dec->members[member].markerToParent; -} - -static void updateMemberParentInformation(SCIP_NETWORKDECOMP *dec, const spqr_member newMember, const spqr_member toRemove){ - assert(memberIsRepresentative(dec,newMember)); - assert(findMemberNoCompression(dec,toRemove) == newMember); - - dec->members[newMember].markerOfParent = dec->members[toRemove].markerOfParent; - dec->members[newMember].markerToParent = dec->members[toRemove].markerToParent; - dec->members[newMember].parentMember = dec->members[toRemove].parentMember; - - dec->members[toRemove].markerOfParent = SPQR_INVALID_ARC; - dec->members[toRemove].markerToParent = SPQR_INVALID_ARC; - dec->members[toRemove].parentMember = SPQR_INVALID_MEMBER; -} -static spqr_arc markerOfParent(const SCIP_NETWORKDECOMP *dec, spqr_member member) { - assert(dec); - assert(SPQRmemberIsValid(member)); - assert(member < dec->memMembers); - assert(memberIsRepresentative(dec,member)); - return dec->members[member].markerOfParent; -} - - - -static int getNumMemberArcs(const SCIP_NETWORKDECOMP * dec, spqr_member member){ - assert(dec); - assert(SPQRmemberIsValid(member)); - assert(member < dec->memMembers); - assert(memberIsRepresentative(dec,member)); - return dec->members[member].numArcs; + spqr_member root = current; + return root; } -static int getNumNodes(const SCIP_NETWORKDECOMP *dec){ - assert(dec); - return dec->numNodes; -} -static int getNumMembers(const SCIP_NETWORKDECOMP *dec){ - assert(dec); - return dec->numMembers; -} -static SCIP_RETCODE createStandaloneParallel(SCIP_NETWORKDECOMP *dec, spqr_col * columns, SCIP_Bool * reversed, - int num_columns, spqr_row row, spqr_member * pMember){ - spqr_member member; - SCIP_CALL(createMember(dec, num_columns <= 1 ? SPQR_MEMBERTYPE_LOOP : SPQR_MEMBERTYPE_PARALLEL, &member)); +static spqr_member mergeMembers( + SCIP_NETMATDEC* dec, + spqr_member first, + spqr_member second +) +{ + assert(dec); + assert(memberIsRepresentative(dec, first)); + assert(memberIsRepresentative(dec, second)); + assert(first != second);//We cannot merge a member into itself + assert(first < dec->memMembers); + assert(second < dec->memMembers); + + //The rank is stored as a negative number: we decrement it making the negative number larger. + // We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement. + spqr_member firstRank = dec->members[first].representativeMember; + spqr_member secondRank = dec->members[second].representativeMember; + if( firstRank > secondRank ) + { + swap_ints(&first, &second); + } + dec->members[second].representativeMember = first; + if( firstRank == secondRank ) + { + --dec->members[first].representativeMember; + } + return first; +} + +static spqr_member findArcMember( + SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); - spqr_arc row_arc; - SCIP_CALL(createRowArc(dec,member,&row_arc,row,num_columns <= 1)); + spqr_member representative = findMember(dec, dec->arcs[arc].member); + dec->arcs[arc].member = representative; + return representative; +} - spqr_arc col_arc; - for (int i = 0; i < num_columns; ++i) { - SCIP_CALL(createColumnArc(dec,member,&col_arc,columns[i],reversed[i])); - } - *pMember = member; +static spqr_member findArcMemberNoCompression( + const SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); - ++dec->numConnectedComponents; - return SCIP_OKAY; + spqr_member representative = findMemberNoCompression(dec, dec->arcs[arc].member); + return representative; } -//TODO: fix tracking connectivity more cleanly, should not be left up to the algorithms ideally -static SCIP_RETCODE createConnectedParallel(SCIP_NETWORKDECOMP *dec, spqr_col * columns, SCIP_Bool * reversed, - int num_columns, spqr_row row, spqr_member * pMember){ - spqr_member member; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &member)); +static spqr_member findMemberParent( + SCIP_NETMATDEC* dec, + spqr_member member +) +{ + assert(dec); + assert(member < dec->memMembers); + assert(SPQRmemberIsValid(member)); + assert(memberIsRepresentative(dec, member)); - spqr_arc row_arc; - SCIP_CALL(createRowArc(dec,member,&row_arc,row,FALSE)); - spqr_arc col_arc; - for (int i = 0; i < num_columns; ++i) { - SCIP_CALL(createColumnArc(dec,member,&col_arc,columns[i],reversed[i])); - } - *pMember = member; + if( SPQRmemberIsInvalid(dec->members[member].parentMember)) + { + return dec->members[member].parentMember; + } + spqr_member parent_representative = findMember(dec, dec->members[member].parentMember); + dec->members[member].parentMember = parent_representative; - return SCIP_OKAY; + return parent_representative; } -static SCIP_RETCODE createStandaloneSeries(SCIP_NETWORKDECOMP *dec, spqr_row * rows, SCIP_Bool *reversed, - int numRows, spqr_col col, spqr_member * pMember){ - spqr_member member; - SCIP_CALL(createMember(dec, numRows <= 1 ? SPQR_MEMBERTYPE_LOOP : SPQR_MEMBERTYPE_SERIES, &member)); - - spqr_arc colArc; - SCIP_CALL(createColumnArc(dec,member,&colArc,col,FALSE)); +static spqr_member findMemberParentNoCompression( + const SCIP_NETMATDEC* dec, + spqr_member member +) +{ + assert(dec); + assert(member < dec->memMembers); + assert(SPQRmemberIsValid(member)); + assert(memberIsRepresentative(dec, member)); + + if( SPQRmemberIsInvalid(dec->members[member].parentMember)) + { + return dec->members[member].parentMember; + } + spqr_member parent_representative = findMemberNoCompression(dec, dec->members[member].parentMember); + return parent_representative; +} + +static spqr_member findArcChildMember( + SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); - spqr_arc rowArc; - for (int i = 0; i < numRows; ++i) { - SCIP_CALL(createRowArc(dec,member,&rowArc,rows[i],!reversed[i])); - } - *pMember = member; - ++dec->numConnectedComponents; - return SCIP_OKAY; + spqr_member representative = findMember(dec, dec->arcs[arc].childMember); + dec->arcs[arc].childMember = representative; + return representative; } -static SCIP_RETCODE createConnectedSeries(SCIP_NETWORKDECOMP *dec, spqr_row * rows, SCIP_Bool *reversed, - int numRows, spqr_col col, spqr_member * pMember){ - spqr_member member; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &member)); - spqr_arc colArc; - SCIP_CALL(createColumnArc(dec,member,&colArc,col,FALSE)); +static spqr_member findArcChildMemberNoCompression( + const SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); - spqr_arc rowArc; - for (int i = 0; i < numRows; ++i) { - SCIP_CALL(createRowArc(dec,member,&rowArc,rows[i],!reversed[i])); - } - *pMember = member; - return SCIP_OKAY; + spqr_member representative = findMemberNoCompression(dec, dec->arcs[arc].childMember); + return representative; } -static void removeArcFromMemberArcList(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_member member){ - assert(findArcMemberNoCompression(dec,arc) == member); - assert(memberIsRepresentative(dec,member)); +// Only accounts for CHILD markers, not parent markers! +static SCIP_Bool arcIsMarker( + const SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); - if(dec->members[member].numArcs == 1){ - dec->members[member].firstArc = SPQR_INVALID_ARC; + return SPQRmemberIsValid(dec->arcs[arc].childMember); +} - }else{ - spqr_arc nextArc = dec->arcs[arc].arcListNode.next; - spqr_arc prevArc = dec->arcs[arc].arcListNode.previous; +static SCIP_Bool arcIsTree( + const SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); - dec->arcs[nextArc].arcListNode.previous = prevArc; - dec->arcs[prevArc].arcListNode.next = nextArc; + return SPQRelementIsRow(dec->arcs[arc].element); +} - if(dec->members[member].firstArc == arc){ - dec->members[member].firstArc = nextArc; - } - } +typedef struct +{ + spqr_arc representative; + SCIP_Bool reversed; +} ArcSign; +//find +#ifndef NDEBUG +static SCIP_Bool arcIsRepresentative( + const SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(arc < dec->memArcs); + assert(SPQRarcIsValid(arc)); - --(dec->members[member].numArcs); + return SPQRarcIsInvalid(dec->arcs[arc].representative); } -typedef struct { - spqr_arc arc; - SCIP_Bool reversed; -} FindCycleCall; -static void process_arc(spqr_row * fundamental_cycle_arcs, int * num_cycle_arcs, - FindCycleCall * callStack, - int * callStackSize, - spqr_arc arc, - const SCIP_NETWORKDECOMP * dec, - SCIP_Bool * fundamental_cycle_direction, - SCIP_Bool arcIsReversed){ - assert(arcIsTree(dec,arc)); - if(!arcIsMarker(dec,arc)){ - spqr_member current_member = findArcMemberNoCompression(dec, arc); - if(markerToParent(dec,current_member) == arc){ - spqr_arc other_arc = markerOfParent(dec, current_member); - assert(!arcIsTree(dec,other_arc)); - callStack[*callStackSize].arc = other_arc; - callStack[*callStackSize].reversed = arcIsReversed; - ++(*callStackSize); - }else{ - spqr_element element = arcGetElement(dec,arc); - assert(SPQRelementIsRow(element)); - spqr_row row = SPQRelementToRow(element); - fundamental_cycle_arcs[*num_cycle_arcs] = row; - fundamental_cycle_direction[*num_cycle_arcs] = arcIsReversed; - ++(*num_cycle_arcs); - } - }else{ - spqr_member child_member = findArcChildMemberNoCompression(dec, arc); - spqr_arc other_arc = markerToParent(dec, child_member); - assert(!arcIsTree(dec,other_arc)); - callStack[*callStackSize].arc = other_arc; - callStack[*callStackSize].reversed = arcIsReversed; - ++(*callStackSize); - } -} - -static int decompositionGetFundamentalCycleRows(const SCIP_NETWORKDECOMP *dec, spqr_col column, spqr_row * output, - SCIP_Bool * computedSignStorage){ - spqr_arc arc = getDecompositionColumnArc(dec, column); - if(SPQRarcIsInvalid(arc)){ - return 0; - } - int num_rows = 0; - - FindCycleCall * callStack; - SCIP_RETCODE result = SCIPallocBlockMemoryArray(dec->env,&callStack,(size_t) dec->memRows); - if(result != SCIP_OKAY){ - return -1; - } - int callStackSize = 1; - callStack[0].arc = arc; - callStack[0].reversed = FALSE; - - SCIP_Bool * nodeVisited; - result = SCIPallocBlockMemoryArray(dec->env,&nodeVisited,(size_t) dec->numNodes); - if(result != SCIP_OKAY){ - return -1; - } - for (int i = 0; i < dec->numNodes; ++i) { - nodeVisited[i] = FALSE; - } - - typedef struct { - spqr_node node; - spqr_arc nodeArc; - } DFSCallData; - DFSCallData * pathSearchCallStack; - result = SCIPallocBlockMemoryArray(dec->env,&pathSearchCallStack,(size_t) dec->numNodes); - if(result != SCIP_OKAY){ - return -1; - } - int pathSearchCallStackSize = 0; - - while(callStackSize > 0){ - spqr_arc column_arc = callStack[callStackSize - 1].arc; - SCIP_Bool reverseEverything = callStack[callStackSize-1].reversed; - --callStackSize; - spqr_member column_arc_member = findArcMemberNoCompression(dec, column_arc); - switch(getMemberType(dec,column_arc_member)){ - case SPQR_MEMBERTYPE_RIGID: - { - spqr_node source = findEffectiveArcTailNoCompression(dec, column_arc); - spqr_node target = findEffectiveArcHeadNoCompression(dec, column_arc); - - assert(pathSearchCallStackSize == 0); - pathSearchCallStack[0].node = source; - pathSearchCallStack[0].nodeArc = getFirstNodeArc(dec,source); - pathSearchCallStackSize++; - while(pathSearchCallStackSize > 0){ - DFSCallData * dfsData = &pathSearchCallStack[pathSearchCallStackSize-1]; - nodeVisited[dfsData->node] = TRUE; - //cannot be a tree arc which is its parent - if(arcIsTree(dec,dfsData->nodeArc) && - (pathSearchCallStackSize <= 1 || dfsData->nodeArc != pathSearchCallStack[pathSearchCallStackSize-2].nodeArc)){ - spqr_node head = findEffectiveArcHeadNoCompression(dec, dfsData->nodeArc); - spqr_node tail = findEffectiveArcTailNoCompression(dec, dfsData->nodeArc); - spqr_node other = head == dfsData->node ? tail : head; - assert(other != dfsData->node); - assert(!nodeVisited[other]); - if(other == target){ - break; - } - //We go up a level: add new node to the call stack - - pathSearchCallStack[pathSearchCallStackSize].node = other; - pathSearchCallStack[pathSearchCallStackSize].nodeArc = getFirstNodeArc(dec,other); - ++pathSearchCallStackSize; - continue; - } - do{ - dfsData->nodeArc = getNextNodeArcNoCompression(dec,dfsData->nodeArc,dfsData->node); - if(dfsData->nodeArc == getFirstNodeArc(dec,dfsData->node)){ - --pathSearchCallStackSize; - dfsData = &pathSearchCallStack[pathSearchCallStackSize-1]; - }else{ - break; - } - }while(pathSearchCallStackSize > 0); - } - for (int i = 0; i < pathSearchCallStackSize; ++i) { - if(arcIsTree(dec,pathSearchCallStack[i].nodeArc)){ - SCIP_Bool arcReversedInPath = findEffectiveArcHeadNoCompression(dec,pathSearchCallStack[i].nodeArc) == pathSearchCallStack[i].node; - process_arc(output,&num_rows,callStack,&callStackSize,pathSearchCallStack[i].nodeArc,dec, - computedSignStorage,arcReversedInPath != reverseEverything); - } - } - - pathSearchCallStackSize = 0; - break; - } - case SPQR_MEMBERTYPE_PARALLEL: - { - SCIP_Bool columnReversed = arcIsReversedNonRigid(dec,column_arc); - - spqr_arc first_arc = getFirstMemberArc(dec, column_arc_member); - spqr_arc iter_arc = first_arc; - int tree_count = 0; - do - { - if(arcIsTree(dec,iter_arc)){ - SCIP_Bool treeIsReversed = arcIsReversedNonRigid(dec,iter_arc); - process_arc(output,&num_rows,callStack,&callStackSize,iter_arc,dec, - computedSignStorage,(columnReversed != treeIsReversed) != reverseEverything); - tree_count++; - } - iter_arc = getNextMemberArc(dec,iter_arc); - } - while(iter_arc != first_arc); - if(tree_count != 1){ - return -1; - } - break; - } - case SPQR_MEMBERTYPE_LOOP: - case SPQR_MEMBERTYPE_SERIES: - { - SCIP_Bool columnReversed = arcIsReversedNonRigid(dec,column_arc); - spqr_arc first_arc = getFirstMemberArc(dec, column_arc_member); - spqr_arc iter_arc = first_arc; - int nontree_count = 0; - do - { - if(arcIsTree(dec,iter_arc)){ - SCIP_Bool treeIsReversed = arcIsReversedNonRigid(dec,iter_arc); - process_arc(output,&num_rows,callStack,&callStackSize,iter_arc,dec, - computedSignStorage,(columnReversed == treeIsReversed) != reverseEverything); - }else{ - nontree_count++; - } - iter_arc = getNextMemberArc(dec,iter_arc); - } - while(iter_arc != first_arc); - if(nontree_count != 1){ - return -1; - } - break; - } - case SPQR_MEMBERTYPE_UNASSIGNED: - assert(FALSE); - } - } - SCIPfreeBlockMemoryArray(dec->env,&pathSearchCallStack,dec->numNodes); - SCIPfreeBlockMemoryArray(dec->env,&nodeVisited,dec->numNodes); - SCIPfreeBlockMemoryArray(dec->env,&callStack,dec->memRows); - return num_rows; -} -typedef struct{ - spqr_row row; - SCIP_Bool reversed; -} Nonzero; -static int qsort_comparison (const void * a, const void * b) -{ - Nonzero *s1 = (Nonzero *)a; - Nonzero *s2 = (Nonzero *)b; - - if(s1->row > s2->row) { - return 1; - } - else if(s1->row == s2->row) { - return 0; - } - else { - return -1; - } -} -SCIP_Bool SCIPNetworkDecompositionVerifyCycle(SCIP * scip, - const SCIP_NETWORKDECOMP * dec, spqr_col column,const spqr_row * column_rows, - const double * column_values, int num_rows, - spqr_row * computed_column_storage, - SCIP_Bool * computedSignStorage){ - int num_found_rows = decompositionGetFundamentalCycleRows(dec,column,computed_column_storage,computedSignStorage); - - if(num_found_rows != num_rows){ - return FALSE; - } - if(num_rows == 0){ - return TRUE; - } - Nonzero *array; - SCIP_RETCODE code = SCIPallocBufferArray(scip,&array,num_rows); - if(code != SCIP_OKAY){ - return FALSE; - } - for (int i = 0; i < num_rows; ++i) { - array[i].row = computed_column_storage[i]; - array[i].reversed = computedSignStorage[i]; - } - qsort(array,(size_t) num_rows,sizeof(Nonzero),qsort_comparison); - - Nonzero *secondArray; - code = SCIPallocBufferArray(scip,&secondArray,num_rows); - if(code != SCIP_OKAY){ - SCIPfreeBufferArray(scip,&array); - return FALSE; - } - for (int i = 0; i < num_rows; ++i) { - secondArray[i].row = column_rows[i]; - secondArray[i].reversed = column_values[i] < 0.0; - } - - qsort(secondArray,(size_t) num_rows,sizeof(Nonzero),qsort_comparison); - - SCIP_Bool good = TRUE; - for (int i = 0; i < num_rows; ++i) { - if(array[i].row != secondArray[i].row || array[i].reversed != secondArray[i].reversed){ - good = FALSE; - break; - } - } - SCIPfreeBufferArray(scip,&secondArray); - SCIPfreeBufferArray(scip,&array); - return good; -} +#endif -static spqr_member largestMemberID(const SCIP_NETWORKDECOMP *dec){ - return dec->numMembers; -} -static spqr_arc largestArcID(const SCIP_NETWORKDECOMP *dec){ - return dec->numArcs; -} -static spqr_node largestNodeID(const SCIP_NETWORKDECOMP *dec){ - return dec->numNodes; +static ArcSign findArcSign( + SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(arc < dec->memArcs); + assert(SPQRarcIsValid(arc)); + + spqr_arc current = arc; + spqr_arc next; + + SCIP_Bool totalReversed = dec->arcs[current].reversed; + //traverse down tree to find the root + while( SPQRarcIsValid(next = dec->arcs[current].representative)) + { + current = next; + assert(current < dec->memArcs); + //swap boolean only if new arc is reversed + totalReversed = ( totalReversed != dec->arcs[current].reversed ); + } + + spqr_arc root = current; + current = arc; + + SCIP_Bool currentReversed = totalReversed != dec->arcs[root].reversed; + //update all pointers along path to point to root, flattening the tree + + while( SPQRarcIsValid(next = dec->arcs[current].representative)) + { + SCIP_Bool wasReversed = dec->arcs[current].reversed; + + dec->arcs[current].reversed = currentReversed; + currentReversed = ( currentReversed != wasReversed ); + + dec->arcs[current].representative = root; + current = next; + assert(current < dec->memArcs); + } + + ArcSign sign; + sign.reversed = totalReversed; + sign.representative = root; + return sign; +} + +static ArcSign findArcSignNoCompression( + const SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(arc < dec->memArcs); + assert(SPQRarcIsValid(arc)); + + spqr_arc current = arc; + spqr_arc next; + + SCIP_Bool totalReversed = dec->arcs[current].reversed; + //traverse down tree to find the root + while( SPQRarcIsValid(next = dec->arcs[current].representative)) + { + current = next; + assert(current < dec->memArcs); + //swap boolean only if new arc is reversed + totalReversed = ( totalReversed != dec->arcs[current].reversed ); + } + ArcSign sign; + sign.reversed = totalReversed; + sign.representative = current; + return sign; } -static int numConnectedComponents(const SCIP_NETWORKDECOMP *dec){ - return dec->numConnectedComponents; + +//Find the arc tail/head, but accounting for reflection +static spqr_node findEffectiveArcHead( + SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + if( findArcSign(dec, arc).reversed ) + { + return findArcTail(dec, arc); + } else + { + return findArcHead(dec, arc); + } +} + +static spqr_node findEffectiveArcTail( + SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + if( findArcSign(dec, arc).reversed ) + { + return findArcHead(dec, arc); + } else + { + return findArcTail(dec, arc); + } +} + +static spqr_node findEffectiveArcHeadNoCompression( + const SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + if( findArcSignNoCompression(dec, arc).reversed ) + { + return findArcTailNoCompression(dec, arc); + } else + { + return findArcHeadNoCompression(dec, arc); + } +} + +static spqr_node findEffectiveArcTailNoCompression( + const SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + if( findArcSignNoCompression(dec, arc).reversed ) + { + return findArcHeadNoCompression(dec, arc); + } else + { + return findArcTailNoCompression(dec, arc); + } } -static SCIP_RETCODE createChildMarker(SCIP_NETWORKDECOMP *dec, spqr_member member, spqr_member child, SCIP_Bool isTree, - spqr_arc * pArc, SCIP_Bool reversed){ - SCIP_CALL(createArc(dec,member,reversed,pArc)); - dec->arcs[*pArc].element = isTree ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; - dec->arcs[*pArc].childMember = child; - addArcToMemberArcList(dec,*pArc,member); - return SCIP_OKAY; -} -static SCIP_RETCODE createParentMarker(SCIP_NETWORKDECOMP *dec, spqr_member member, SCIP_Bool isTree, spqr_member parent, spqr_arc parentMarker - , spqr_arc * arc,SCIP_Bool reversed){ - - SCIP_CALL(createArc(dec,member,reversed,arc)); - dec->arcs[*arc].element = isTree ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; - - addArcToMemberArcList(dec,*arc,member); - - dec->members[member].parentMember = parent; - dec->members[member].markerOfParent = parentMarker; - dec->members[member].markerToParent = *arc; - return SCIP_OKAY; -} -static SCIP_RETCODE createMarkerPair(SCIP_NETWORKDECOMP *dec, spqr_member parentMember, spqr_member childMember, - SCIP_Bool parentIsTree, - SCIP_Bool childMarkerReversed, - SCIP_Bool parentMarkerReversed){ - spqr_arc parentToChildMarker = SPQR_INVALID_ARC; - SCIP_CALL(createChildMarker(dec,parentMember,childMember,parentIsTree,&parentToChildMarker,childMarkerReversed)); - - spqr_arc childToParentMarker = SPQR_INVALID_ARC; - SCIP_CALL(createParentMarker(dec,childMember,!parentIsTree,parentMember,parentToChildMarker,&childToParentMarker,parentMarkerReversed)); - - return SCIP_OKAY; -} -static SCIP_RETCODE createMarkerPairWithReferences(SCIP_NETWORKDECOMP *dec, spqr_member parentMember, spqr_member childMember, SCIP_Bool parentIsTree, - SCIP_Bool childMarkerReversed, - SCIP_Bool parentMarkerReversed, - spqr_arc * parentToChild, spqr_arc *childToParent){ - SCIP_CALL(createChildMarker(dec,parentMember,childMember,parentIsTree,parentToChild,childMarkerReversed)); - SCIP_CALL(createParentMarker(dec,childMember,!parentIsTree,parentMember,*parentToChild,childToParent,parentMarkerReversed)); - - return SCIP_OKAY; -} - -static void moveArcToNewMember(SCIP_NETWORKDECOMP *dec, spqr_arc arc, spqr_member oldMember, spqr_member newMember){ - assert(SPQRarcIsValid(arc)); - assert(arc < dec->memArcs); - assert(dec); - - assert(memberIsRepresentative(dec,oldMember)); - assert(memberIsRepresentative(dec,newMember)); - //Need to change the arc's member, remove it from the current member list and add it to the new member list - assert(findArcMemberNoCompression(dec,arc) == oldMember); - - removeArcFromMemberArcList(dec,arc,oldMember); - addArcToMemberArcList(dec,arc,newMember); - - dec->arcs[arc].member = newMember; - - //If this arc has a childMember, update the information correctly! - spqr_member childMember = dec->arcs[arc].childMember; - if(SPQRmemberIsValid(childMember)){ - spqr_member childRepresentative = findArcChildMember(dec, arc); - dec->members[childRepresentative].parentMember = newMember; - } - //If this arc is a marker to the parent, update the child arc marker of the parent to reflect the move - if(dec->members[oldMember].markerToParent == arc){ - dec->members[newMember].markerToParent = arc; - dec->members[newMember].parentMember = dec->members[oldMember].parentMember; - dec->members[newMember].markerOfParent = dec->members[oldMember].markerOfParent; - - assert(findArcChildMemberNoCompression(dec,dec->members[oldMember].markerOfParent) == oldMember); - dec->arcs[dec->members[oldMember].markerOfParent].childMember = newMember; - } -} -static void mergeMemberArcList(SCIP_NETWORKDECOMP *dec, spqr_member toMergeInto, spqr_member toRemove){ - spqr_arc firstIntoArc = getFirstMemberArc(dec, toMergeInto); - spqr_arc firstFromArc = getFirstMemberArc(dec, toRemove); - assert(SPQRarcIsValid(firstIntoArc)); - assert(SPQRarcIsValid(firstFromArc)); - - spqr_arc lastIntoArc = getPreviousMemberArc(dec, firstIntoArc); - spqr_arc lastFromArc = getPreviousMemberArc(dec, firstFromArc); - - //Relink linked lists to merge them effectively - dec->arcs[firstIntoArc].arcListNode.previous = lastFromArc; - dec->arcs[lastIntoArc].arcListNode.next = firstFromArc; - dec->arcs[firstFromArc].arcListNode.previous = lastIntoArc; - dec->arcs[lastFromArc].arcListNode.next = firstIntoArc; - - //Clean up old - dec->members[toMergeInto].numArcs += dec->members[toRemove].numArcs; - dec->members[toRemove].numArcs = 0; - dec->members[toRemove].firstArc = SPQR_INVALID_ARC; - -} - -static void changeLoopToSeries(SCIP_NETWORKDECOMP * dec, spqr_member member){ - assert(SPQRmemberIsValid(member)); - assert(member < dec->memMembers); - assert(dec); - assert((getMemberType(dec,member) == SPQR_MEMBERTYPE_PARALLEL || getMemberType(dec, member) == SPQR_MEMBERTYPE_SERIES || - getMemberType(dec,member) == SPQR_MEMBERTYPE_LOOP) && getNumMemberArcs(dec, member) == 2); - assert(memberIsRepresentative(dec,member)); - dec->members[member].type = SPQR_MEMBERTYPE_SERIES; -} -static void changeLoopToParallel(SCIP_NETWORKDECOMP * dec, spqr_member member){ - assert(SPQRmemberIsValid(member)); - assert(member < dec->memMembers); - assert(dec); - assert((getMemberType(dec,member) == SPQR_MEMBERTYPE_PARALLEL || getMemberType(dec, member) == SPQR_MEMBERTYPE_SERIES || - getMemberType(dec,member) == SPQR_MEMBERTYPE_LOOP) && getNumMemberArcs(dec, member) == 2); - assert(memberIsRepresentative(dec,member)); - dec->members[member].type = SPQR_MEMBERTYPE_PARALLEL; -} -SCIP_Bool SCIPNetworkDecompositionIsMinimal(const SCIP_NETWORKDECOMP * dec){ - //Relies on parents/children etc. being set correctly in the tree - SCIP_Bool isMinimal = TRUE; - for (spqr_member member = 0; member < dec->numMembers; ++member) { - if (!memberIsRepresentative(dec, member) || getMemberType(dec,member) == SPQR_MEMBERTYPE_UNASSIGNED ){ - continue; - } - spqr_member memberParent = findMemberParentNoCompression(dec, member); - if(SPQRmemberIsValid(memberParent)){ - assert(memberIsRepresentative(dec,memberParent)); - SPQRMemberType memberType = getMemberType(dec, member); - SPQRMemberType parentType = getMemberType(dec, memberParent); - if(memberType == parentType && memberType != SPQR_MEMBERTYPE_RIGID){ - isMinimal = FALSE; - break; - } - } - - } - return isMinimal; -} - -static void decreaseNumConnectedComponents(SCIP_NETWORKDECOMP *dec, int by){ - dec->numConnectedComponents-= by; - assert(dec->numConnectedComponents >= 1); -} - -static void reorderComponent(SCIP_NETWORKDECOMP *dec, spqr_member newRoot){ - assert(dec); - assert(memberIsRepresentative(dec,newRoot)); - //If the newRoot has no parent, it is already the root, so then there's no need to reorder. - if(SPQRmemberIsValid(dec->members[newRoot].parentMember)){ - spqr_member member = findMemberParent(dec, newRoot); - spqr_member newParent = newRoot; - spqr_arc newMarkerToParent = dec->members[newRoot].markerOfParent; - spqr_arc markerOfNewParent = dec->members[newRoot].markerToParent; - - //Recursively update the parent - do{ - assert(SPQRmemberIsValid(member)); - assert(SPQRmemberIsValid(newParent)); - spqr_member oldParent = findMemberParent(dec, member); - spqr_arc oldMarkerToParent = dec->members[member].markerToParent; - spqr_arc oldMarkerOfParent = dec->members[member].markerOfParent; - - dec->members[member].markerToParent = newMarkerToParent; - dec->members[member].markerOfParent = markerOfNewParent; - dec->members[member].parentMember = newParent; - dec->arcs[markerOfNewParent].childMember = member; - dec->arcs[newMarkerToParent].childMember = SPQR_INVALID_MEMBER; - - if (SPQRmemberIsValid(oldParent)){ - newParent = member; - member = oldParent; - newMarkerToParent = oldMarkerOfParent; - markerOfNewParent = oldMarkerToParent; - }else{ - break; - } - }while(TRUE); - dec->members[newRoot].parentMember = SPQR_INVALID_MEMBER; - dec->members[newRoot].markerToParent = SPQR_INVALID_ARC; - dec->members[newRoot].markerOfParent = SPQR_INVALID_ARC; +///Merge for signed union find of the arc directions. +///Is not symmetric, in the sense that the arc directions of coponent first are guaranteed not to change but those of second may change +///Based on whether one wants the reflection or not +static spqr_arc mergeArcSigns( + SCIP_NETMATDEC* dec, + spqr_arc first, + spqr_arc second, + SCIP_Bool reflectRelative +) +{ + assert(dec); + assert(arcIsRepresentative(dec, first)); + assert(arcIsRepresentative(dec, second)); + assert(first != second);//We cannot merge a member into itself + assert(first < dec->memArcs); + assert(second < dec->memArcs); + + //The rank is stored as a negative number: we decrement it making the negative number larger. + // We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement. + spqr_member firstRank = dec->arcs[first].representative; + spqr_member secondRank = dec->arcs[second].representative; + + if( firstRank > secondRank ) + { + swap_ints(&first, &second); + } + dec->arcs[second].representative = first; + if( firstRank == secondRank ) + { + --dec->arcs[first].representative; + } + //These boolean formula's cover all 16 possible cases, such that the relative orientation of the first is not changed + SCIP_Bool equal = dec->arcs[first].reversed == dec->arcs[second].reversed; + dec->arcs[second].reversed = ( equal == reflectRelative ); + if( firstRank > secondRank ) + { + dec->arcs[first].reversed = ( dec->arcs[first].reversed != reflectRelative ); + } + return first; +} + +static SCIP_Bool arcIsReversedNonRigid( + const SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); - } + return dec->arcs[arc].reversed; } -void SCIPNetworkDecompositionRemoveComponents(SCIP_NETWORKDECOMP *dec, const int * componentRows, - int numRows, const int * componentCols, int numCols){ - //The below just removes the 'link' but not the internal datastructures. - //This is sufficient for our purposes, as long as we do not re-introduce any of the 'negated' rows/columns back into the decomposition. - - for (int i = 0; i < numRows; ++i) { - spqr_row row = componentRows[i]; - if(SPQRarcIsValid(dec->rowArcs[row])){ - dec->rowArcs[row] = SPQR_INVALID_ARC; - } - } +static spqr_element arcGetElement( + const SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); - for (int i = 0; i < numCols; ++i) { - spqr_col col = componentCols[i]; - if(SPQRarcIsValid(dec->columnArcs[col])){ - dec->columnArcs[col] = SPQR_INVALID_ARC; - } - } + return dec->arcs[arc].element; } -#ifdef SCIP_DEBUG -//Debugging functions to print the decomposition -static char typeToChar(SPQRMemberType type){ - switch (type) { - case SPQR_MEMBERTYPE_RIGID: - return 'R'; - case SPQR_MEMBERTYPE_PARALLEL: - return 'P'; - case SPQR_MEMBERTYPE_SERIES: - return 'S'; - case SPQR_MEMBERTYPE_LOOP: - return 'L'; - default: - return '?'; - } -} - -static void arcToDot(FILE * stream, const SCIP_NETWORKDECOMP * dec, - spqr_arc arc, unsigned long dot_head, unsigned long dot_tail, SCIP_Bool useElementNames){ - assert(SPQRarcIsValid(arc)); - spqr_member member = findArcMemberNoCompression(dec, arc); - SPQRMemberType member_type = getMemberType(dec, member); - char type = typeToChar(member_type); - const char* color = arcIsTree(dec,arc) ? ",color=red" :",color=blue"; - - int arc_name = arc; - - if(markerToParent(dec,member) == arc){ - if(useElementNames){ - arc_name = -1; - } - fprintf(stream, " %c_%d_%lu -> %c_p_%d [label=\"%d\",style=dashed%s];\n", type, member, dot_tail, type, member, arc_name, color); - fprintf(stream, " %c_p_%d -> %c_%d_%lu [label=\"%d\",style=dashed%s];\n", type, member, type, member, dot_head, arc_name, color); - fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_tail); - fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_head); - fprintf(stream, " %c_p_%d [style=dashed];\n", type, member); - }else if(arcIsMarker(dec,arc)){ - spqr_member child = findArcChildMemberNoCompression(dec, arc); - char childType = typeToChar(getMemberType(dec,child)); - if(useElementNames){ - arc_name = -1; - } - fprintf(stream, " %c_%d_%lu -> %c_c_%d [label=\"%d\",style=dotted%s];\n", type, member, dot_tail, type, child, arc_name, color); - fprintf(stream, " %c_c_%d -> %c_%d_%lu [label=\"%d\",style=dotted%s];\n", type, child, type, member, dot_head, arc_name, color); - fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_tail); - fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_head); - fprintf(stream, " %c_c_%d [style=dotted];\n", type, child); - fprintf(stream, " %c_p_%d -> %c_c_%d [style=dashed,dir=forward];\n", childType, child, type, child); - }else{ - if(useElementNames){ - spqr_element element = dec->arcs[arc].element; - if(SPQRelementIsRow(element)){ - arc_name = (int) SPQRelementToRow(element); - }else{ - arc_name = (int) SPQRelementToColumn(element); - } - } - - fprintf(stream, " %c_%d_%lu -> %c_%d_%lu [label=\"%d \",style=bold%s];\n", type, member, dot_tail, type, member, dot_head, - arc_name, color); - fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_tail); - fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_head); - } +SCIP_Bool SCIPnetmatdecContainsRow( + const SCIP_NETMATDEC* dec, + int row +) +{ + assert(SPQRrowIsValid(row) && (int) row < dec->memRows); + assert(dec); + return SPQRarcIsValid(dec->rowArcs[row]); } -static void decompositionToDot(FILE * stream, const SCIP_NETWORKDECOMP *dec, SCIP_Bool useElementNames ){ - fprintf(stream, "//decomposition\ndigraph decomposition{\n compound = TRUE;\n"); - for (spqr_member member = 0; member < dec->numMembers; ++member){ - if(!memberIsRepresentative(dec,member)) continue; - fprintf(stream," subgraph member_%d{\n",member); - switch(getMemberType(dec,member)){ - case SPQR_MEMBERTYPE_RIGID: - { - spqr_arc first_arc = getFirstMemberArc(dec, member); - spqr_arc arc = first_arc; - do - { - unsigned long arcHead = (unsigned long) findEffectiveArcHeadNoCompression(dec,arc); - unsigned long arcTail = (unsigned long) findEffectiveArcTailNoCompression(dec,arc); - arcToDot(stream,dec,arc,arcHead,arcTail,useElementNames); - arc = getNextMemberArc(dec,arc); - } - while(arc != first_arc); - break; - } - case SPQR_MEMBERTYPE_LOOP: - case SPQR_MEMBERTYPE_PARALLEL: - { - spqr_arc first_arc = getFirstMemberArc(dec, member); - spqr_arc arc = first_arc; - do - { - if(arcIsReversedNonRigid(dec,arc)){ - arcToDot(stream,dec,arc,1,0,useElementNames); - }else{ - arcToDot(stream,dec,arc,0,1,useElementNames); - } - arc = getNextMemberArc(dec,arc); - } - while(arc != first_arc); - break; - } - case SPQR_MEMBERTYPE_SERIES: - { - unsigned long i = 0; - unsigned long num_member_arcs = (unsigned long) getNumMemberArcs(dec, member); - spqr_arc first_arc = getFirstMemberArc(dec, member); - spqr_arc arc = first_arc; - do { - unsigned long head = i; - unsigned long tail = (i+1) % num_member_arcs; - if(arcIsReversedNonRigid(dec,arc)){ - unsigned long temp = head; - head = tail; - tail = temp; - } - arcToDot(stream, dec, arc, head,tail,useElementNames); - arc = getNextMemberArc(dec, arc); - i++; - } while (arc != first_arc); - break; - } - case SPQR_MEMBERTYPE_UNASSIGNED: - break; - } - fprintf(stream," }\n"); - } - fprintf(stream,"}\n"); +SCIP_Bool SCIPnetmatdecContainsColumn( + const SCIP_NETMATDEC* dec, + int column +) +{ + assert(SPQRcolIsValid(column) && (int) column < dec->memColumns); + assert(dec); + return SPQRarcIsValid(dec->columnArcs[column]); } -#endif -static SCIP_RETCODE mergeGivenMemberIntoParent(SCIP_NETWORKDECOMP *dec, - spqr_member member, - spqr_member parent, - spqr_arc parentToChild, - spqr_arc childToParent, - SCIP_Bool headToHead, - spqr_member * mergedMember){ - assert(dec); - assert(SPQRmemberIsValid(member)); - assert(memberIsRepresentative(dec,member)); - assert(SPQRmemberIsValid(parent)); - assert(memberIsRepresentative(dec,parent)); - assert(findMemberParentNoCompression(dec,member) == parent); - assert(markerOfParent(dec, member) == parentToChild); - assert(markerToParent(dec, member) == childToParent); - - removeArcFromMemberArcList(dec,parentToChild,parent); - removeArcFromMemberArcList(dec,childToParent,member); - - spqr_node parentArcNodes[2] = {findEffectiveArcTail(dec, parentToChild), findEffectiveArcHead(dec, parentToChild)}; - spqr_node childArcNodes[2] = {findEffectiveArcTail(dec, childToParent), findEffectiveArcHead(dec, childToParent)}; - - clearArcHeadAndTail(dec,parentToChild); - clearArcHeadAndTail(dec,childToParent); - - spqr_node first = childArcNodes[headToHead ? 0 : 1]; - spqr_node second = childArcNodes[headToHead ? 1 : 0]; - { - spqr_node newNode = mergeNodes(dec, parentArcNodes[0], first); - spqr_node toRemoveFrom = newNode == first ? parentArcNodes[0] : first; - mergeNodeArcList(dec,newNode,toRemoveFrom); - } - { - spqr_node newNode = mergeNodes(dec, parentArcNodes[1], second); - spqr_node toRemoveFrom = newNode == second ? parentArcNodes[1] : second; - mergeNodeArcList(dec,newNode,toRemoveFrom); - } - - - spqr_member newMember = mergeMembers(dec, member, parent); - spqr_member toRemoveFrom = newMember == member ? parent : member; - mergeMemberArcList(dec,newMember,toRemoveFrom); - if(toRemoveFrom == parent){ - updateMemberParentInformation(dec,newMember,toRemoveFrom); - } - updateMemberType(dec, newMember, SPQR_MEMBERTYPE_RIGID); - *mergedMember = newMember; - return SCIP_OKAY; -} - -static int max(int a, int b){ - return (a > b) ? a : b; +static void setDecompositionColumnArc( + SCIP_NETMATDEC* dec, + spqr_col col, + spqr_arc arc +) +{ + assert(SPQRcolIsValid(col) && (int) col < dec->memColumns); + assert(dec); + assert(SPQRarcIsValid(arc)); + dec->columnArcs[col] = arc; } -typedef int path_arc_id; -#define INVALID_PATH_ARC (-1) +static void setDecompositionRowArc( + SCIP_NETMATDEC* dec, + spqr_row row, + spqr_arc arc +) +{ + assert(SPQRrowIsValid(row) && (int) row < dec->memRows); + assert(dec); + assert(SPQRarcIsValid(arc)); + dec->rowArcs[row] = arc; +} -static SCIP_Bool pathArcIsInvalid(const path_arc_id arc) { - return arc < 0; +static spqr_arc getDecompositionColumnArc( + const SCIP_NETMATDEC* dec, + spqr_col col +) +{ + assert(SPQRcolIsValid(col) && (int) col < dec->memColumns); + assert(dec); + return dec->columnArcs[col]; } -static SCIP_Bool pathArcIsValid(const path_arc_id arc) { - return !pathArcIsInvalid(arc); +static spqr_arc getDecompositionRowArc( + const SCIP_NETMATDEC* dec, + spqr_row row +) +{ + assert(SPQRrowIsValid(row) && (int) row < dec->memRows); + assert(dec); + return dec->rowArcs[row]; } -typedef struct { - spqr_arc arc; - spqr_node arcHead; //These can be used in various places to prevent additional find()'s - spqr_node arcTail; - path_arc_id nextMember; - path_arc_id nextOverall; - SCIP_Bool reversed; -} PathArcListNode; +SCIP_RETCODE SCIPnetmatdecCreate( + SCIP* scip, + SCIP_NETMATDEC** pdec, + int nrows, + int ncols +) +{ + assert(scip); + assert(pdec); + assert(!*pdec); + + SCIP_CALL(SCIPallocBlockMemory(scip, pdec)); + SCIP_NETMATDEC* dec = *pdec; + dec->env = scip; + + //Initialize arc array data + int initialMemArcs = 8; + { + assert(initialMemArcs > 0); + dec->memArcs = initialMemArcs; + dec->numArcs = 0; + SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->arcs, (size_t) dec->memArcs)); + for( spqr_arc i = 0; i < dec->memArcs; ++i ) + { + dec->arcs[i].arcListNode.next = i + 1; + dec->arcs[i].member = SPQR_INVALID_MEMBER; + } + dec->arcs[dec->memArcs - 1].arcListNode.next = SPQR_INVALID_ARC; + dec->firstFreeArc = 0; + } + + //Initialize member array data + int initialMemMembers = 8; + { + assert(initialMemMembers > 0); + dec->memMembers = initialMemMembers; + dec->numMembers = 0; + SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->members, (size_t) dec->memMembers)); + } + + //Initialize node array data + int initialMemNodes = 8; + { + assert(initialMemNodes > 0); + dec->memNodes = initialMemNodes; + dec->numNodes = 0; + SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->nodes, (size_t) dec->memNodes)); + } + + //Initialize mappings for rows + { + dec->memRows = nrows; + SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->rowArcs, (size_t) dec->memRows)); + for( int i = 0; i < dec->memRows; ++i ) + { + dec->rowArcs[i] = SPQR_INVALID_ARC; + } + } + //Initialize mappings for columns + { + dec->memColumns = ncols; + dec->numColumns = 0; + SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->columnArcs, (size_t) dec->memColumns)); + for( int i = 0; i < dec->memColumns; ++i ) + { + dec->columnArcs[i] = SPQR_INVALID_ARC; + } + } + + dec->numConnectedComponents = 0; + return SCIP_OKAY; +} + +void SCIPnetmatdecFree(SCIP_NETMATDEC** pDecomposition) +{ + assert(pDecomposition); + assert(*pDecomposition); -typedef int reduced_member_id; -#define INVALID_REDUCED_MEMBER (-1) + SCIP_NETMATDEC* dec = *pDecomposition; + SCIPfreeBlockMemoryArray(dec->env, &dec->columnArcs, dec->memColumns); + SCIPfreeBlockMemoryArray(dec->env, &dec->rowArcs, dec->memRows); + SCIPfreeBlockMemoryArray(dec->env, &dec->nodes, dec->memNodes); + SCIPfreeBlockMemoryArray(dec->env, &dec->members, dec->memMembers); + SCIPfreeBlockMemoryArray(dec->env, &dec->arcs, dec->memArcs); -static SCIP_Bool reducedMemberIsInvalid(const reduced_member_id id) { - return id < 0; -} -static SCIP_Bool reducedMemberIsValid(const reduced_member_id id){ - return !reducedMemberIsInvalid(id); + SCIPfreeBlockMemory(dec->env, pDecomposition); } -typedef int children_idx; +static spqr_arc getFirstMemberArc( + const SCIP_NETMATDEC* dec, + spqr_member member +) +{ + assert(dec); + assert(SPQRmemberIsValid(member)); + assert(member < dec->memMembers); + return dec->members[member].firstArc; +} -typedef enum { - REDUCEDMEMBER_TYPE_UNASSIGNED = 0, - REDUCEDMEMBER_TYPE_CYCLE = 1, - REDUCEDMEMBER_TYPE_MERGED = 2, - REDUCEDMEMBER_TYPE_NOT_NETWORK = 3 -} ReducedMemberType; +static spqr_arc getNextMemberArc( + const SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + arc = dec->arcs[arc].arcListNode.next; + return arc; +} -typedef enum { - INTO_HEAD = 0, - INTO_TAIL = 1, - OUT_HEAD = 2, - OUT_TAIL = 3 -} MemberPathType; +static spqr_arc getPreviousMemberArc( + const SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + assert(dec); + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + arc = dec->arcs[arc].arcListNode.previous; + return arc; +} + +static void addArcToMemberArcList( + SCIP_NETMATDEC* dec, + spqr_arc arc, + spqr_member member +) +{ + spqr_arc firstMemberArc = getFirstMemberArc(dec, member); + + if( SPQRarcIsValid(firstMemberArc)) + { + spqr_arc lastMemberArc = getPreviousMemberArc(dec, firstMemberArc); + dec->arcs[arc].arcListNode.next = firstMemberArc; + dec->arcs[arc].arcListNode.previous = lastMemberArc; + dec->arcs[firstMemberArc].arcListNode.previous = arc; + dec->arcs[lastMemberArc].arcListNode.next = arc; + } else + { + assert(dec->members[member].numArcs == 0); + dec->arcs[arc].arcListNode.next = arc; + dec->arcs[arc].arcListNode.previous = arc; + } + dec->members[member].firstArc = arc; + ++( dec->members[member].numArcs ); +} + +static SCIP_RETCODE createArc( + SCIP_NETMATDEC* dec, + spqr_member member, + SCIP_Bool reversed, + spqr_arc* pArc +) +{ + assert(dec); + assert(pArc); + assert(SPQRmemberIsInvalid(member) || memberIsRepresentative(dec, member)); + + spqr_arc index = dec->firstFreeArc; + if( SPQRarcIsValid(index)) + { + dec->firstFreeArc = dec->arcs[index].arcListNode.next; + } else + { + //Enlarge array, no free nodes in arc list + int newSize = 2 * dec->memArcs; + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &dec->arcs, dec->memArcs, (size_t) newSize)); + for( int i = dec->memArcs + 1; i < newSize; ++i ) + { + dec->arcs[i].arcListNode.next = i + 1; + dec->arcs[i].member = SPQR_INVALID_MEMBER; + } + dec->arcs[newSize - 1].arcListNode.next = SPQR_INVALID_ARC; + dec->firstFreeArc = dec->memArcs + 1; + index = dec->memArcs; + dec->memArcs = newSize; + } + + dec->arcs[index].tail = SPQR_INVALID_NODE; + dec->arcs[index].head = SPQR_INVALID_NODE; + dec->arcs[index].member = member; + dec->arcs[index].childMember = SPQR_INVALID_MEMBER; + dec->arcs[index].reversed = reversed; + + dec->arcs[index].headArcListNode.next = SPQR_INVALID_ARC; + dec->arcs[index].headArcListNode.previous = SPQR_INVALID_ARC; + dec->arcs[index].tailArcListNode.next = SPQR_INVALID_ARC; + dec->arcs[index].tailArcListNode.previous = SPQR_INVALID_ARC; + + dec->numArcs++; + + *pArc = index; + + return SCIP_OKAY; +} + +static SCIP_RETCODE createRowArc( + SCIP_NETMATDEC* dec, + spqr_member member, + spqr_arc* pArc, + spqr_row row, + SCIP_Bool reversed +) +{ + SCIP_CALL(createArc(dec, member, reversed, pArc)); + setDecompositionRowArc(dec, row, *pArc); + addArcToMemberArcList(dec, *pArc, member); + dec->arcs[*pArc].element = SPQRrowToElement(row); -static SCIP_Bool isInto(MemberPathType type){ - return type < 2; -} -static SCIP_Bool isHead(MemberPathType type){ - return (type & 1) == 0; + return SCIP_OKAY; } -typedef struct { - spqr_member member; - spqr_member rootMember; - int depth; - ReducedMemberType type; - reduced_member_id parent; +static SCIP_RETCODE createColumnArc( + SCIP_NETMATDEC* dec, + spqr_member member, + spqr_arc* pArc, + spqr_col column, + SCIP_Bool reversed +) +{ + SCIP_CALL(createArc(dec, member, reversed, pArc)); + setDecompositionColumnArc(dec, column, *pArc); + addArcToMemberArcList(dec, *pArc, member); + dec->arcs[*pArc].element = SPQRcolumnToElement(column); - children_idx firstChild; - children_idx numChildren; + return SCIP_OKAY; +} - path_arc_id firstPathArc; - int numPathArcs; +static SCIP_RETCODE createMember( + SCIP_NETMATDEC* dec, + SPQRMemberType type, + spqr_member* pMember +) +{ + assert(dec); + assert(pMember); + + if( dec->numMembers == dec->memMembers ) + { + int newSize = dec->memMembers * 2; + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &dec->members, dec->memMembers, newSize)); + dec->memMembers = newSize; + } + SPQRNetworkDecompositionMember* data = &dec->members[dec->numMembers]; + data->markerOfParent = SPQR_INVALID_ARC; + data->markerToParent = SPQR_INVALID_ARC; + data->firstArc = SPQR_INVALID_ARC; + data->representativeMember = SPQR_INVALID_MEMBER; + data->numArcs = 0; + data->parentMember = SPQR_INVALID_MEMBER; + data->type = type; + + *pMember = dec->numMembers; + + dec->numMembers++; + return SCIP_OKAY; +} + +static SCIP_RETCODE createNode( + SCIP_NETMATDEC* dec, + spqr_node* pNode +) +{ - SCIP_Bool reverseArcs; - spqr_node rigidPathStart; - spqr_node rigidPathEnd; + if( dec->numNodes == dec->memNodes ) + { + int newSize = dec->memNodes * 2; + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &dec->nodes, dec->memNodes, newSize)); + dec->memNodes = newSize; + } + *pNode = dec->numNodes; + dec->nodes[dec->numNodes].representativeNode = SPQR_INVALID_NODE; + dec->nodes[dec->numNodes].firstArc = SPQR_INVALID_ARC; + dec->nodes[dec->numNodes].numArcs = 0; + dec->numNodes++; + + return SCIP_OKAY; +} + +static void removeArcFromNodeArcList( + SCIP_NETMATDEC* dec, + spqr_arc arc, + spqr_node node, + SCIP_Bool nodeIsHead +) +{ + SPQRNetworkDecompositionArcListNode* arcListNode = nodeIsHead ? &dec->arcs[arc].headArcListNode + : &dec->arcs[arc].tailArcListNode; + + if( dec->nodes[node].numArcs == 1 ) + { + dec->nodes[node].firstArc = SPQR_INVALID_ARC; + } else + { + spqr_arc next_arc = arcListNode->next; + spqr_arc prev_arc = arcListNode->previous; + SPQRNetworkDecompositionArcListNode* nextListNode = + findArcHead(dec, next_arc) == node ? &dec->arcs[next_arc].headArcListNode + : &dec->arcs[next_arc].tailArcListNode; + SPQRNetworkDecompositionArcListNode* prevListNode = + findArcHead(dec, prev_arc) == node ? &dec->arcs[prev_arc].headArcListNode + : &dec->arcs[prev_arc].tailArcListNode; + + nextListNode->previous = prev_arc; + prevListNode->next = next_arc; + + if( dec->nodes[node].firstArc == arc ) + { + dec->nodes[node].firstArc = next_arc; + } + } + --( dec->nodes[node].numArcs ); +} + +static void addArcToNodeArcList( + SCIP_NETMATDEC* dec, + spqr_arc arc, + spqr_node node, + SCIP_Bool nodeIsHead +) +{ + assert(nodeIsRepresentative(dec, node)); + + spqr_arc firstNodeArc = getFirstNodeArc(dec, node); + + SPQRNetworkDecompositionArcListNode* arcListNode = nodeIsHead ? &dec->arcs[arc].headArcListNode + : &dec->arcs[arc].tailArcListNode; + if( SPQRarcIsValid(firstNodeArc)) + { + SCIP_Bool nextIsHead = findArcHead(dec, firstNodeArc) == node; + SPQRNetworkDecompositionArcListNode* nextListNode = nextIsHead ? &dec->arcs[firstNodeArc].headArcListNode + : &dec->arcs[firstNodeArc].tailArcListNode; + spqr_arc lastNodeArc = nextListNode->previous; + + arcListNode->next = firstNodeArc; + arcListNode->previous = lastNodeArc; + + + SCIP_Bool previousIsHead = findArcHead(dec, lastNodeArc) == node; + SPQRNetworkDecompositionArcListNode* previousListNode = previousIsHead ? &dec->arcs[lastNodeArc].headArcListNode + : &dec->arcs[lastNodeArc].tailArcListNode; + previousListNode->next = arc; + nextListNode->previous = arc; + + } else + { + arcListNode->next = arc; + arcListNode->previous = arc; + } + dec->nodes[node].firstArc = arc; + ++dec->nodes[node].numArcs; + if( nodeIsHead ) + { + dec->arcs[arc].head = node; + } else + { + dec->arcs[arc].tail = node; + } +} + +static void setArcHeadAndTail( + SCIP_NETMATDEC* dec, + spqr_arc arc, + spqr_node head, + spqr_node tail +) +{ + addArcToNodeArcList(dec, arc, head, TRUE); + addArcToNodeArcList(dec, arc, tail, FALSE); +} - SCIP_Bool pathBackwards; +static void clearArcHeadAndTail( + SCIP_NETMATDEC* dec, + spqr_arc arc +) +{ + removeArcFromNodeArcList(dec, arc, findArcHead(dec, arc), TRUE); + removeArcFromNodeArcList(dec, arc, findArcTail(dec, arc), FALSE); + dec->arcs[arc].head = SPQR_INVALID_NODE; + dec->arcs[arc].tail = SPQR_INVALID_NODE; +} + +static void changeArcHead( + SCIP_NETMATDEC* dec, + spqr_arc arc, + spqr_node oldHead, + spqr_node newHead +) +{ + assert(nodeIsRepresentative(dec, oldHead)); + assert(nodeIsRepresentative(dec, newHead)); + removeArcFromNodeArcList(dec, arc, oldHead, TRUE); + addArcToNodeArcList(dec, arc, newHead, TRUE); +} + +static void changeArcTail( + SCIP_NETMATDEC* dec, + spqr_arc arc, + spqr_node oldTail, + spqr_node newTail +) +{ + assert(nodeIsRepresentative(dec, oldTail)); + assert(nodeIsRepresentative(dec, newTail)); + removeArcFromNodeArcList(dec, arc, oldTail, FALSE); + addArcToNodeArcList(dec, arc, newTail, FALSE); +} - int numPropagatedChildren; - int componentIndex; +static int nodeDegree( + SCIP_NETMATDEC* dec, + spqr_node node +) +{ + assert(dec); + assert(SPQRnodeIsValid(node)); + assert(node < dec->memNodes); + return dec->nodes[node].numArcs; +} - MemberPathType pathType; - reduced_member_id nextPathMember; - SCIP_Bool nextPathMemberIsParent; - spqr_arc pathSourceArc; - spqr_arc pathTargetArc; -} SPQRColReducedMember; +static SPQRMemberType getMemberType( + const SCIP_NETMATDEC* dec, + spqr_member member +) +{ + assert(dec); + assert(SPQRmemberIsValid(member)); + assert(member < dec->memMembers); + assert(memberIsRepresentative(dec, member)); + return dec->members[member].type; +} + +static void updateMemberType( + const SCIP_NETMATDEC* dec, + spqr_member member, + SPQRMemberType type +) +{ + assert(dec); + assert(SPQRmemberIsValid(member)); + assert(member < dec->memMembers); + assert(memberIsRepresentative(dec, member)); -typedef struct { - int rootDepth; - reduced_member_id root; + dec->members[member].type = type; +} - reduced_member_id pathEndMembers[2]; - int numPathEndMembers; -} SPQRColReducedComponent; +static spqr_arc markerToParent( + const SCIP_NETMATDEC* dec, + spqr_member member +) +{ + assert(dec); + assert(SPQRmemberIsValid(member)); + assert(member < dec->memMembers); + assert(memberIsRepresentative(dec, member)); + return dec->members[member].markerToParent; +} + +static void updateMemberParentInformation( + SCIP_NETMATDEC* dec, + const spqr_member newMember, + const spqr_member toRemove +) +{ + assert(memberIsRepresentative(dec, newMember)); + assert(findMemberNoCompression(dec, toRemove) == newMember); -typedef struct { - reduced_member_id reducedMember; - reduced_member_id rootDepthMinimizer; -} MemberInfo; + dec->members[newMember].markerOfParent = dec->members[toRemove].markerOfParent; + dec->members[newMember].markerToParent = dec->members[toRemove].markerToParent; + dec->members[newMember].parentMember = dec->members[toRemove].parentMember; -typedef struct { - spqr_member member; -} CreateReducedMembersCallstack; + dec->members[toRemove].markerOfParent = SPQR_INVALID_ARC; + dec->members[toRemove].markerToParent = SPQR_INVALID_ARC; + dec->members[toRemove].parentMember = SPQR_INVALID_MEMBER; +} -struct SCIP_NetworkColAddition { - SCIP_Bool remainsNetwork; +static spqr_arc markerOfParent( + const SCIP_NETMATDEC* dec, + spqr_member member +) +{ + assert(dec); + assert(SPQRmemberIsValid(member)); + assert(member < dec->memMembers); + assert(memberIsRepresentative(dec, member)); + return dec->members[member].markerOfParent; +} - SPQRColReducedMember *reducedMembers; - int memReducedMembers; - int numReducedMembers; - SPQRColReducedComponent *reducedComponents; - int memReducedComponents; - int numReducedComponents; +static int getNumMemberArcs( + const SCIP_NETMATDEC* dec, + spqr_member member +) +{ + assert(dec); + assert(SPQRmemberIsValid(member)); + assert(member < dec->memMembers); + assert(memberIsRepresentative(dec, member)); + return dec->members[member].numArcs; +} - MemberInfo *memberInformation; - int memMemberInformation; - int numMemberInformation; +static int getNumNodes(const SCIP_NETMATDEC* dec) +{ + assert(dec); + return dec->numNodes; +} - reduced_member_id *childrenStorage; - int memChildrenStorage; - int numChildrenStorage; +static int getNumMembers(const SCIP_NETMATDEC* dec) +{ + assert(dec); + return dec->numMembers; +} + +static SCIP_RETCODE createStandaloneParallel( + SCIP_NETMATDEC* dec, + spqr_col* columns, + SCIP_Bool* reversed, + int num_columns, + spqr_row row, + spqr_member* pMember +) +{ + spqr_member member; + SCIP_CALL(createMember(dec, num_columns <= 1 ? SPQR_MEMBERTYPE_LOOP : SPQR_MEMBERTYPE_PARALLEL, &member)); - PathArcListNode *pathArcs; - int memPathArcs; - int numPathArcs; - path_arc_id firstOverallPathArc; + spqr_arc row_arc; + SCIP_CALL(createRowArc(dec, member, &row_arc, row, num_columns <= 1)); - int *nodeInPathDegree; - int *nodeOutPathDegree; - int memNodePathDegree; + spqr_arc col_arc; + for( int i = 0; i < num_columns; ++i ) + { + SCIP_CALL(createColumnArc(dec, member, &col_arc, columns[i], reversed[i])); + } + *pMember = member; - SCIP_Bool *arcInPath; - SCIP_Bool *arcInPathReversed; - int memArcsInPath; + ++dec->numConnectedComponents; + return SCIP_OKAY; +} - CreateReducedMembersCallstack * createReducedMembersCallStack; - int memCreateReducedMembersCallStack; +//TODO: fix tracking connectivity more cleanly, should not be left up to the algorithms ideally +static SCIP_RETCODE createConnectedParallel( + SCIP_NETMATDEC* dec, + spqr_col* columns, + SCIP_Bool* reversed, + int num_columns, + spqr_row row, + spqr_member* pMember +) +{ + spqr_member member; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &member)); + + spqr_arc row_arc; + SCIP_CALL(createRowArc(dec, member, &row_arc, row, FALSE)); + + spqr_arc col_arc; + for( int i = 0; i < num_columns; ++i ) + { + SCIP_CALL(createColumnArc(dec, member, &col_arc, columns[i], reversed[i])); + } + *pMember = member; + + return SCIP_OKAY; +} + +static SCIP_RETCODE createStandaloneSeries( + SCIP_NETMATDEC* dec, + spqr_row* rows, + SCIP_Bool* reversed, + int numRows, + spqr_col col, + spqr_member* pMember +) +{ + spqr_member member; + SCIP_CALL(createMember(dec, numRows <= 1 ? SPQR_MEMBERTYPE_LOOP : SPQR_MEMBERTYPE_SERIES, &member)); + + spqr_arc colArc; + SCIP_CALL(createColumnArc(dec, member, &colArc, col, FALSE)); + + spqr_arc rowArc; + for( int i = 0; i < numRows; ++i ) + { + SCIP_CALL(createRowArc(dec, member, &rowArc, rows[i], !reversed[i])); + } + *pMember = member; + ++dec->numConnectedComponents; + return SCIP_OKAY; +} + +static SCIP_RETCODE createConnectedSeries( + SCIP_NETMATDEC* dec, + spqr_row* rows, + SCIP_Bool* reversed, + int numRows, + spqr_col col, + spqr_member* pMember +) +{ + spqr_member member; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &member)); + + spqr_arc colArc; + SCIP_CALL(createColumnArc(dec, member, &colArc, col, FALSE)); + + spqr_arc rowArc; + for( int i = 0; i < numRows; ++i ) + { + SCIP_CALL(createRowArc(dec, member, &rowArc, rows[i], !reversed[i])); + } + *pMember = member; + return SCIP_OKAY; +} + +static void removeArcFromMemberArcList( + SCIP_NETMATDEC* dec, + spqr_arc arc, + spqr_member member +) +{ + assert(findArcMemberNoCompression(dec, arc) == member); + assert(memberIsRepresentative(dec, member)); - spqr_col newColIndex; + if( dec->members[member].numArcs == 1 ) + { + dec->members[member].firstArc = SPQR_INVALID_ARC; - spqr_row *newRowArcs; - SCIP_Bool * newRowArcReversed; - int memNewRowArcs; - int numNewRowArcs; + } else + { + spqr_arc nextArc = dec->arcs[arc].arcListNode.next; + spqr_arc prevArc = dec->arcs[arc].arcListNode.previous; - spqr_arc *decompositionRowArcs; - SCIP_Bool *decompositionArcReversed; - int memDecompositionRowArcs; - int numDecompositionRowArcs; + dec->arcs[nextArc].arcListNode.previous = prevArc; + dec->arcs[prevArc].arcListNode.next = nextArc; - spqr_member * leafMembers; - int numLeafMembers; - int memLeafMembers; -}; + if( dec->members[member].firstArc == arc ) + { + dec->members[member].firstArc = nextArc; + } + } -static void cleanupPreviousIteration(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol) { - assert(dec); - assert(newCol); - - path_arc_id pathArc = newCol->firstOverallPathArc; - while (pathArcIsValid(pathArc)) { - spqr_node head = newCol->pathArcs[pathArc].arcHead; - spqr_node tail = newCol->pathArcs[pathArc].arcTail; - if(SPQRnodeIsValid(head)){ - newCol->nodeInPathDegree[head] = 0; - } - if(SPQRnodeIsValid(tail)){ - newCol->nodeOutPathDegree[tail] = 0; - } - - spqr_arc arc = newCol->pathArcs[pathArc].arc; - if(arc < newCol->memArcsInPath){ - newCol->arcInPath[arc] = FALSE; - newCol->arcInPathReversed[arc] = FALSE; - } - pathArc = newCol->pathArcs[pathArc].nextOverall; - } -#ifndef NDEBUG - for (int i = 0; i < newCol->memArcsInPath; ++i) { - assert(newCol->arcInPath[i] == FALSE); - assert(newCol->arcInPathReversed[i] == FALSE); - } - - for (int i = 0; i < newCol->memNodePathDegree; ++i) { - assert(newCol->nodeInPathDegree[i] == 0); - assert(newCol->nodeOutPathDegree[i] == 0); - } -#endif - newCol->firstOverallPathArc = INVALID_PATH_ARC; - newCol->numPathArcs = 0; + --( dec->members[member].numArcs ); } -SCIP_RETCODE SCIPNetworkColAdditionCreate(SCIP*env, SCIP_NETWORKCOLADDITION **pNewCol) { - assert(env); - - SCIP_CALL(SCIPallocBlockMemory(env, pNewCol)); - SCIP_NETWORKCOLADDITION *newCol = *pNewCol; - - newCol->remainsNetwork = FALSE; - newCol->reducedMembers = NULL; - newCol->memReducedMembers = 0; - newCol->numReducedMembers = 0; - - newCol->reducedComponents = NULL; - newCol->memReducedComponents = 0; - newCol->numReducedComponents = 0; +typedef struct +{ + spqr_arc arc; + SCIP_Bool reversed; +} FindCycleCall; - newCol->memberInformation = NULL; - newCol->memMemberInformation = 0; - newCol->numMemberInformation = 0; +static void process_arc( + spqr_row* fundamental_cycle_arcs, + int* num_cycle_arcs, + FindCycleCall* callStack, + int* callStackSize, + spqr_arc arc, + const SCIP_NETMATDEC* dec, + SCIP_Bool* fundamental_cycle_direction, + SCIP_Bool arcIsReversed +) +{ + assert(arcIsTree(dec, arc)); + if( !arcIsMarker(dec, arc)) + { + spqr_member current_member = findArcMemberNoCompression(dec, arc); + if( markerToParent(dec, current_member) == arc ) + { + spqr_arc other_arc = markerOfParent(dec, current_member); + assert(!arcIsTree(dec, other_arc)); + callStack[*callStackSize].arc = other_arc; + callStack[*callStackSize].reversed = arcIsReversed; + ++( *callStackSize ); + } else + { + spqr_element element = arcGetElement(dec, arc); + assert(SPQRelementIsRow(element)); + spqr_row row = SPQRelementToRow(element); + fundamental_cycle_arcs[*num_cycle_arcs] = row; + fundamental_cycle_direction[*num_cycle_arcs] = arcIsReversed; + ++( *num_cycle_arcs ); + } + } else + { + spqr_member child_member = findArcChildMemberNoCompression(dec, arc); + spqr_arc other_arc = markerToParent(dec, child_member); + assert(!arcIsTree(dec, other_arc)); + callStack[*callStackSize].arc = other_arc; + callStack[*callStackSize].reversed = arcIsReversed; + ++( *callStackSize ); + } +} + +static int decompositionGetFundamentalCycleRows( + const SCIP_NETMATDEC* dec, + spqr_col column, + spqr_row* output, + SCIP_Bool* computedSignStorage +) +{ + spqr_arc arc = getDecompositionColumnArc(dec, column); + if( SPQRarcIsInvalid(arc)) + { + return 0; + } + int num_rows = 0; + + FindCycleCall* callStack; + SCIP_RETCODE result = SCIPallocBlockMemoryArray(dec->env, &callStack, (size_t) dec->memRows); + if( result != SCIP_OKAY ) + { + return -1; + } + int callStackSize = 1; + callStack[0].arc = arc; + callStack[0].reversed = FALSE; + + SCIP_Bool* nodeVisited; + result = SCIPallocBlockMemoryArray(dec->env, &nodeVisited, (size_t) dec->numNodes); + if( result != SCIP_OKAY ) + { + return -1; + } + for( int i = 0; i < dec->numNodes; ++i ) + { + nodeVisited[i] = FALSE; + } + + typedef struct + { + spqr_node node; + spqr_arc nodeArc; + } DFSCallData; + DFSCallData* pathSearchCallStack; + result = SCIPallocBlockMemoryArray(dec->env, &pathSearchCallStack, (size_t) dec->numNodes); + if( result != SCIP_OKAY ) + { + return -1; + } + int pathSearchCallStackSize = 0; + + while( callStackSize > 0 ) + { + spqr_arc column_arc = callStack[callStackSize - 1].arc; + SCIP_Bool reverseEverything = callStack[callStackSize - 1].reversed; + --callStackSize; + spqr_member column_arc_member = findArcMemberNoCompression(dec, column_arc); + switch( getMemberType(dec, column_arc_member)) + { + case SPQR_MEMBERTYPE_RIGID: + { + spqr_node source = findEffectiveArcTailNoCompression(dec, column_arc); + spqr_node target = findEffectiveArcHeadNoCompression(dec, column_arc); + + assert(pathSearchCallStackSize == 0); + pathSearchCallStack[0].node = source; + pathSearchCallStack[0].nodeArc = getFirstNodeArc(dec, source); + pathSearchCallStackSize++; + while( pathSearchCallStackSize > 0 ) + { + DFSCallData* dfsData = &pathSearchCallStack[pathSearchCallStackSize - 1]; + nodeVisited[dfsData->node] = TRUE; + //cannot be a tree arc which is its parent + if( arcIsTree(dec, dfsData->nodeArc) && + ( pathSearchCallStackSize <= 1 || + dfsData->nodeArc != pathSearchCallStack[pathSearchCallStackSize - 2].nodeArc )) + { + spqr_node head = findEffectiveArcHeadNoCompression(dec, dfsData->nodeArc); + spqr_node tail = findEffectiveArcTailNoCompression(dec, dfsData->nodeArc); + spqr_node other = head == dfsData->node ? tail : head; + assert(other != dfsData->node); + assert(!nodeVisited[other]); + if( other == target ) + { + break; + } + //We go up a level: add new node to the call stack + + pathSearchCallStack[pathSearchCallStackSize].node = other; + pathSearchCallStack[pathSearchCallStackSize].nodeArc = getFirstNodeArc(dec, other); + ++pathSearchCallStackSize; + continue; + } + do + { + dfsData->nodeArc = getNextNodeArcNoCompression(dec, dfsData->nodeArc, dfsData->node); + if( dfsData->nodeArc == getFirstNodeArc(dec, dfsData->node)) + { + --pathSearchCallStackSize; + dfsData = &pathSearchCallStack[pathSearchCallStackSize - 1]; + } else + { + break; + } + } while( pathSearchCallStackSize > 0 ); + } + for( int i = 0; i < pathSearchCallStackSize; ++i ) + { + if( arcIsTree(dec, pathSearchCallStack[i].nodeArc)) + { + SCIP_Bool arcReversedInPath = + findEffectiveArcHeadNoCompression(dec, pathSearchCallStack[i].nodeArc) == + pathSearchCallStack[i].node; + process_arc(output, &num_rows, callStack, &callStackSize, pathSearchCallStack[i].nodeArc, dec, + computedSignStorage, arcReversedInPath != reverseEverything); + } + } - newCol->childrenStorage = NULL; - newCol->memChildrenStorage = 0; - newCol->numChildrenStorage = 0; + pathSearchCallStackSize = 0; + break; + } + case SPQR_MEMBERTYPE_PARALLEL: + { + SCIP_Bool columnReversed = arcIsReversedNonRigid(dec, column_arc); + + spqr_arc first_arc = getFirstMemberArc(dec, column_arc_member); + spqr_arc iter_arc = first_arc; + int tree_count = 0; + do + { + if( arcIsTree(dec, iter_arc)) + { + SCIP_Bool treeIsReversed = arcIsReversedNonRigid(dec, iter_arc); + process_arc(output, &num_rows, callStack, &callStackSize, iter_arc, dec, + computedSignStorage, ( columnReversed != treeIsReversed ) != reverseEverything); + tree_count++; + } + iter_arc = getNextMemberArc(dec, iter_arc); + } while( iter_arc != first_arc ); + if( tree_count != 1 ) + { + return -1; + } + break; + } + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_SERIES: + { + SCIP_Bool columnReversed = arcIsReversedNonRigid(dec, column_arc); + spqr_arc first_arc = getFirstMemberArc(dec, column_arc_member); + spqr_arc iter_arc = first_arc; + int nontree_count = 0; + do + { + if( arcIsTree(dec, iter_arc)) + { + SCIP_Bool treeIsReversed = arcIsReversedNonRigid(dec, iter_arc); + process_arc(output, &num_rows, callStack, &callStackSize, iter_arc, dec, + computedSignStorage, ( columnReversed == treeIsReversed ) != reverseEverything); + } else + { + nontree_count++; + } + iter_arc = getNextMemberArc(dec, iter_arc); + } while( iter_arc != first_arc ); + if( nontree_count != 1 ) + { + return -1; + } + break; + } + case SPQR_MEMBERTYPE_UNASSIGNED: + assert(FALSE); + } + } + SCIPfreeBlockMemoryArray(dec->env, &pathSearchCallStack, dec->numNodes); + SCIPfreeBlockMemoryArray(dec->env, &nodeVisited, dec->numNodes); + SCIPfreeBlockMemoryArray(dec->env, &callStack, dec->memRows); + return num_rows; +} - newCol->pathArcs = NULL; - newCol->memPathArcs = 0; - newCol->numPathArcs = 0; - newCol->firstOverallPathArc = INVALID_PATH_ARC; +typedef struct +{ + spqr_row row; + SCIP_Bool reversed; +} Nonzero; - newCol->nodeInPathDegree = NULL; - newCol->nodeOutPathDegree = NULL; - newCol->memNodePathDegree = 0; +static int qsort_comparison( + const void* a, + const void* b +) +{ + Nonzero* s1 = (Nonzero*) a; + Nonzero* s2 = (Nonzero*) b; + + if( s1->row > s2->row ) + { + return 1; + } else if( s1->row == s2->row ) + { + return 0; + } else + { + return -1; + } +} + +SCIP_Bool SCIPnetmatdecVerifyCycle( + SCIP* scip, + const SCIP_NETMATDEC* dec, + int column, + const int* nonzrowidx, + const double* nonzvals, + int num_rows, + int* pathrowstorage, + SCIP_Bool* pathsignstorage +) +{ + int num_found_rows = decompositionGetFundamentalCycleRows(dec, column, pathrowstorage, pathsignstorage); + + if( num_found_rows != num_rows ) + { + return FALSE; + } + if( num_rows == 0 ) + { + return TRUE; + } + Nonzero* array; + SCIP_RETCODE code = SCIPallocBufferArray(scip, &array, num_rows); + if( code != SCIP_OKAY ) + { + return FALSE; + } + for( int i = 0; i < num_rows; ++i ) + { + array[i].row = pathrowstorage[i]; + array[i].reversed = pathsignstorage[i]; + } + qsort(array, (size_t) num_rows, sizeof(Nonzero), qsort_comparison); + + Nonzero* secondArray; + code = SCIPallocBufferArray(scip, &secondArray, num_rows); + if( code != SCIP_OKAY ) + { + SCIPfreeBufferArray(scip, &array); + return FALSE; + } + for( int i = 0; i < num_rows; ++i ) + { + secondArray[i].row = nonzrowidx[i]; + secondArray[i].reversed = nonzvals[i] < 0.0; + } + + qsort(secondArray, (size_t) num_rows, sizeof(Nonzero), qsort_comparison); + + SCIP_Bool good = TRUE; + for( int i = 0; i < num_rows; ++i ) + { + if( array[i].row != secondArray[i].row || array[i].reversed != secondArray[i].reversed ) + { + good = FALSE; + break; + } + } + SCIPfreeBufferArray(scip, &secondArray); + SCIPfreeBufferArray(scip, &array); + return good; +} + +static spqr_member largestMemberID(const SCIP_NETMATDEC* dec) +{ + return dec->numMembers; +} - newCol->arcInPath = NULL; - newCol->arcInPathReversed = NULL; - newCol->memArcsInPath = 0; +static spqr_arc largestArcID(const SCIP_NETMATDEC* dec) +{ + return dec->numArcs; +} - newCol->createReducedMembersCallStack = NULL; - newCol->memCreateReducedMembersCallStack = 0; +static spqr_node largestNodeID(const SCIP_NETMATDEC* dec) +{ + return dec->numNodes; +} - newCol->newColIndex = SPQR_INVALID_COL; +static int numConnectedComponents(const SCIP_NETMATDEC* dec) +{ + return dec->numConnectedComponents; +} - newCol->newRowArcs = NULL; - newCol->newRowArcReversed = NULL; - newCol->memNewRowArcs = 0; - newCol->numNewRowArcs = 0; +static SCIP_RETCODE createChildMarker( + SCIP_NETMATDEC* dec, + spqr_member member, + spqr_member child, + SCIP_Bool isTree, + spqr_arc* pArc, + SCIP_Bool reversed +) +{ + SCIP_CALL(createArc(dec, member, reversed, pArc)); + dec->arcs[*pArc].element = isTree ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; + dec->arcs[*pArc].childMember = child; + + addArcToMemberArcList(dec, *pArc, member); + return SCIP_OKAY; +} + +static SCIP_RETCODE createParentMarker( + SCIP_NETMATDEC* dec, + spqr_member member, + SCIP_Bool isTree, + spqr_member parent, + spqr_arc parentMarker, + spqr_arc* arc, + SCIP_Bool reversed +) +{ - newCol->decompositionRowArcs = NULL; - newCol->decompositionArcReversed = NULL; - newCol->memDecompositionRowArcs = 0; - newCol->numDecompositionRowArcs = 0; + SCIP_CALL(createArc(dec, member, reversed, arc)); + dec->arcs[*arc].element = isTree ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; - newCol->leafMembers = NULL; - newCol->numLeafMembers = 0; - newCol->memLeafMembers = 0; + addArcToMemberArcList(dec, *arc, member); - return SCIP_OKAY; + dec->members[member].parentMember = parent; + dec->members[member].markerOfParent = parentMarker; + dec->members[member].markerToParent = *arc; + return SCIP_OKAY; } -void SCIPNetworkColAdditionFree(SCIP * env, SCIP_NETWORKCOLADDITION **pNewCol) { - assert(env); - SCIP_NETWORKCOLADDITION *newCol = *pNewCol; - SCIPfreeBlockMemoryArray(env, &newCol->decompositionRowArcs,newCol->memDecompositionRowArcs); - SCIPfreeBlockMemoryArray(env, &newCol->decompositionArcReversed,newCol->memDecompositionRowArcs); - SCIPfreeBlockMemoryArray(env, &newCol->newRowArcs,newCol->memNewRowArcs); - SCIPfreeBlockMemoryArray(env, &newCol->newRowArcReversed,newCol->memNewRowArcs); - SCIPfreeBlockMemoryArray(env, &newCol->createReducedMembersCallStack,newCol->memCreateReducedMembersCallStack); - SCIPfreeBlockMemoryArray(env, &newCol->arcInPath,newCol->memArcsInPath); - SCIPfreeBlockMemoryArray(env, &newCol->arcInPathReversed,newCol->memArcsInPath); - SCIPfreeBlockMemoryArray(env, &newCol->nodeInPathDegree,newCol->memNodePathDegree); - SCIPfreeBlockMemoryArray(env, &newCol->nodeOutPathDegree,newCol->memNodePathDegree); - SCIPfreeBlockMemoryArray(env, &newCol->pathArcs,newCol->memPathArcs); - SCIPfreeBlockMemoryArray(env, &newCol->childrenStorage,newCol->memChildrenStorage); - SCIPfreeBlockMemoryArray(env, &newCol->memberInformation,newCol->memMemberInformation); - SCIPfreeBlockMemoryArray(env, &newCol->reducedComponents,newCol->memReducedComponents); - SCIPfreeBlockMemoryArray(env, &newCol->reducedMembers,newCol->memReducedMembers); - SCIPfreeBlockMemoryArray(env, &newCol->leafMembers,newCol->memLeafMembers); +static SCIP_RETCODE createMarkerPair( + SCIP_NETMATDEC* dec, + spqr_member parentMember, + spqr_member childMember, + SCIP_Bool parentIsTree, + SCIP_Bool childMarkerReversed, + SCIP_Bool parentMarkerReversed +) +{ + spqr_arc parentToChildMarker = SPQR_INVALID_ARC; + SCIP_CALL( + createChildMarker(dec, parentMember, childMember, parentIsTree, &parentToChildMarker, childMarkerReversed)); + + spqr_arc childToParentMarker = SPQR_INVALID_ARC; + SCIP_CALL( + createParentMarker(dec, childMember, !parentIsTree, parentMember, parentToChildMarker, &childToParentMarker, + parentMarkerReversed)); + + return SCIP_OKAY; +} + +static SCIP_RETCODE createMarkerPairWithReferences( + SCIP_NETMATDEC* dec, + spqr_member parentMember, + spqr_member childMember, + SCIP_Bool parentIsTree, + SCIP_Bool childMarkerReversed, + SCIP_Bool parentMarkerReversed, + spqr_arc* parentToChild, + spqr_arc* childToParent +) +{ + SCIP_CALL(createChildMarker(dec, parentMember, childMember, parentIsTree, parentToChild, childMarkerReversed)); + SCIP_CALL(createParentMarker(dec, childMember, !parentIsTree, parentMember, *parentToChild, childToParent, + parentMarkerReversed)); - SCIPfreeBlockMemory(env, pNewCol); + return SCIP_OKAY; } +static void moveArcToNewMember( + SCIP_NETMATDEC* dec, + spqr_arc arc, + spqr_member oldMember, + spqr_member newMember +) +{ + assert(SPQRarcIsValid(arc)); + assert(arc < dec->memArcs); + assert(dec); + + assert(memberIsRepresentative(dec, oldMember)); + assert(memberIsRepresentative(dec, newMember)); + //Need to change the arc's member, remove it from the current member list and add it to the new member list + assert(findArcMemberNoCompression(dec, arc) == oldMember); + + removeArcFromMemberArcList(dec, arc, oldMember); + addArcToMemberArcList(dec, arc, newMember); + + dec->arcs[arc].member = newMember; + + //If this arc has a childMember, update the information correctly! + spqr_member childMember = dec->arcs[arc].childMember; + if( SPQRmemberIsValid(childMember)) + { + spqr_member childRepresentative = findArcChildMember(dec, arc); + dec->members[childRepresentative].parentMember = newMember; + } + //If this arc is a marker to the parent, update the child arc marker of the parent to reflect the move + if( dec->members[oldMember].markerToParent == arc ) + { + dec->members[newMember].markerToParent = arc; + dec->members[newMember].parentMember = dec->members[oldMember].parentMember; + dec->members[newMember].markerOfParent = dec->members[oldMember].markerOfParent; + + assert(findArcChildMemberNoCompression(dec, dec->members[oldMember].markerOfParent) == oldMember); + dec->arcs[dec->members[oldMember].markerOfParent].childMember = newMember; + } +} + +static void mergeMemberArcList( + SCIP_NETMATDEC* dec, + spqr_member toMergeInto, + spqr_member toRemove +) +{ + spqr_arc firstIntoArc = getFirstMemberArc(dec, toMergeInto); + spqr_arc firstFromArc = getFirstMemberArc(dec, toRemove); + assert(SPQRarcIsValid(firstIntoArc)); + assert(SPQRarcIsValid(firstFromArc)); + + spqr_arc lastIntoArc = getPreviousMemberArc(dec, firstIntoArc); + spqr_arc lastFromArc = getPreviousMemberArc(dec, firstFromArc); + + //Relink linked lists to merge them effectively + dec->arcs[firstIntoArc].arcListNode.previous = lastFromArc; + dec->arcs[lastIntoArc].arcListNode.next = firstFromArc; + dec->arcs[firstFromArc].arcListNode.previous = lastIntoArc; + dec->arcs[lastFromArc].arcListNode.next = firstIntoArc; + + //Clean up old + dec->members[toMergeInto].numArcs += dec->members[toRemove].numArcs; + dec->members[toRemove].numArcs = 0; + dec->members[toRemove].firstArc = SPQR_INVALID_ARC; +} + +static void changeLoopToSeries( + SCIP_NETMATDEC* dec, + spqr_member member +) +{ + assert(SPQRmemberIsValid(member)); + assert(member < dec->memMembers); + assert(dec); + assert(( getMemberType(dec, member) == SPQR_MEMBERTYPE_PARALLEL || + getMemberType(dec, member) == SPQR_MEMBERTYPE_SERIES || + getMemberType(dec, member) == SPQR_MEMBERTYPE_LOOP ) && + getNumMemberArcs(dec, member) == 2); + assert(memberIsRepresentative(dec, member)); + dec->members[member].type = SPQR_MEMBERTYPE_SERIES; +} + +static void changeLoopToParallel( + SCIP_NETMATDEC* dec, + spqr_member member +) +{ + assert(SPQRmemberIsValid(member)); + assert(member < dec->memMembers); + assert(dec); + assert(( getMemberType(dec, member) == SPQR_MEMBERTYPE_PARALLEL || + getMemberType(dec, member) == SPQR_MEMBERTYPE_SERIES || + getMemberType(dec, member) == SPQR_MEMBERTYPE_LOOP ) && + getNumMemberArcs(dec, member) == 2); + assert(memberIsRepresentative(dec, member)); + dec->members[member].type = SPQR_MEMBERTYPE_PARALLEL; +} + +SCIP_Bool SCIPnetmatdecIsMinimal(const SCIP_NETMATDEC* dec) +{ + //Relies on parents/children etc. being set correctly in the tree + SCIP_Bool isMinimal = TRUE; + for( spqr_member member = 0; member < dec->numMembers; ++member ) + { + if( !memberIsRepresentative(dec, member) || getMemberType(dec, member) == SPQR_MEMBERTYPE_UNASSIGNED ) + { + continue; + } + spqr_member memberParent = findMemberParentNoCompression(dec, member); + if( SPQRmemberIsValid(memberParent)) + { + assert(memberIsRepresentative(dec, memberParent)); + SPQRMemberType memberType = getMemberType(dec, member); + SPQRMemberType parentType = getMemberType(dec, memberParent); + if( memberType == parentType && memberType != SPQR_MEMBERTYPE_RIGID ) + { + isMinimal = FALSE; + break; + } + } + } + return isMinimal; +} -static reduced_member_id createReducedMembersToRoot(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, const spqr_member firstMember ){ - assert(SPQRmemberIsValid(firstMember)); - - CreateReducedMembersCallstack * callstack = newCol->createReducedMembersCallStack; - callstack[0].member = firstMember; - int callDepth = 0; - - while(callDepth >= 0){ - spqr_member member = callstack[callDepth].member; - reduced_member_id reducedMember = newCol->memberInformation[member].reducedMember; - - SCIP_Bool reducedValid = reducedMemberIsValid(reducedMember); - if(!reducedValid) { - //reduced member was not yet created; we create it - reducedMember = newCol->numReducedMembers; - - SPQRColReducedMember *reducedMemberData = &newCol->reducedMembers[reducedMember]; - ++newCol->numReducedMembers; - - reducedMemberData->member = member; - reducedMemberData->numChildren = 0; - - reducedMemberData->type = REDUCEDMEMBER_TYPE_UNASSIGNED; - reducedMemberData->numPropagatedChildren = 0; - reducedMemberData->firstPathArc = INVALID_PATH_ARC; - reducedMemberData->numPathArcs = 0; - reducedMemberData->rigidPathStart = SPQR_INVALID_NODE; - reducedMemberData->rigidPathEnd = SPQR_INVALID_NODE; - - reducedMemberData->componentIndex = -1; - //The children are set later - - newCol->memberInformation[member].reducedMember = reducedMember; - assert(memberIsRepresentative(dec, member)); - spqr_member parentMember = findMemberParent(dec, member); - - if (SPQRmemberIsValid(parentMember)) { - //recursive call to parent member - ++callDepth; - assert(callDepth < newCol->memCreateReducedMembersCallStack); - callstack[callDepth].member = parentMember; - continue; - - } else { - //we found a new reduced decomposition component - - reducedMemberData->parent = INVALID_REDUCED_MEMBER; - reducedMemberData->depth = 0; - reducedMemberData->rootMember = member; - reducedMemberData->componentIndex = newCol->numReducedComponents; - - assert(newCol->numReducedComponents < newCol->memReducedComponents); - newCol->reducedComponents[newCol->numReducedComponents].root = reducedMember; - newCol->reducedComponents[newCol->numReducedComponents].numPathEndMembers = 0; - newCol->reducedComponents[newCol->numReducedComponents].pathEndMembers[0] = INVALID_REDUCED_MEMBER; - newCol->reducedComponents[newCol->numReducedComponents].pathEndMembers[1] = INVALID_REDUCED_MEMBER; - ++newCol->numReducedComponents; - } - } - if(reducedValid){ - assert(reducedMember < newCol->numReducedMembers); - //Reduced member was already created in earlier call - //update the depth of the root if appropriate - reduced_member_id * depthMinimizer = &newCol->memberInformation[newCol->reducedMembers[reducedMember].rootMember].rootDepthMinimizer; - if(reducedMemberIsInvalid(*depthMinimizer) || - newCol->reducedMembers[reducedMember].depth < newCol->reducedMembers[*depthMinimizer].depth){ - *depthMinimizer = reducedMember; - } - } - while(TRUE){ - --callDepth; - if(callDepth < 0 ) break; - spqr_member parentMember = callstack[callDepth + 1].member; - reduced_member_id parentReducedMember = newCol->memberInformation[parentMember].reducedMember; - spqr_member currentMember = callstack[callDepth].member; - reduced_member_id currentReducedMember = newCol->memberInformation[currentMember].reducedMember; - - SPQRColReducedMember *parentReducedMemberData = &newCol->reducedMembers[parentReducedMember]; - SPQRColReducedMember *reducedMemberData = &newCol->reducedMembers[currentReducedMember]; +static void decreaseNumConnectedComponents( + SCIP_NETMATDEC* dec, + int by +) +{ + dec->numConnectedComponents -= by; + assert(dec->numConnectedComponents >= 1); +} - reducedMemberData->parent = parentReducedMember; - reducedMemberData->depth = parentReducedMemberData->depth + 1; - reducedMemberData->rootMember = parentReducedMemberData->rootMember; - //ensure that all newly created reduced members are pointing to the correct component - assert(parentReducedMemberData->componentIndex >= 0); - reducedMemberData->componentIndex = parentReducedMemberData->componentIndex; +static void reorderComponent( + SCIP_NETMATDEC* dec, + spqr_member newRoot +) +{ + assert(dec); + assert(memberIsRepresentative(dec, newRoot)); + //If the newRoot has no parent, it is already the root, so then there's no need to reorder. + if( SPQRmemberIsValid(dec->members[newRoot].parentMember)) + { + spqr_member member = findMemberParent(dec, newRoot); + spqr_member newParent = newRoot; + spqr_arc newMarkerToParent = dec->members[newRoot].markerOfParent; + spqr_arc markerOfNewParent = dec->members[newRoot].markerToParent; + + //Recursively update the parent + do + { + assert(SPQRmemberIsValid(member)); + assert(SPQRmemberIsValid(newParent)); + spqr_member oldParent = findMemberParent(dec, member); + spqr_arc oldMarkerToParent = dec->members[member].markerToParent; + spqr_arc oldMarkerOfParent = dec->members[member].markerOfParent; + + dec->members[member].markerToParent = newMarkerToParent; + dec->members[member].markerOfParent = markerOfNewParent; + dec->members[member].parentMember = newParent; + dec->arcs[markerOfNewParent].childMember = member; + dec->arcs[newMarkerToParent].childMember = SPQR_INVALID_MEMBER; + + if( SPQRmemberIsValid(oldParent)) + { + newParent = member; + member = oldParent; + newMarkerToParent = oldMarkerOfParent; + markerOfNewParent = oldMarkerToParent; + } else + { + break; + } + } while( TRUE ); + dec->members[newRoot].parentMember = SPQR_INVALID_MEMBER; + dec->members[newRoot].markerToParent = SPQR_INVALID_ARC; + dec->members[newRoot].markerOfParent = SPQR_INVALID_ARC; + } +} + + +void SCIPnetmatdecRemoveComponent( + SCIP_NETMATDEC* dec, + const int* componentRows, + int numRows, + const int* componentCols, + int numCols +) +{ + //The below just removes the 'link' but not the internal datastructures. + //This is sufficient for our purposes, as long as we do not re-introduce any of the 'negated' rows/columns back into the decomposition. - newCol->reducedMembers[parentReducedMember].numChildren++; - } + for( int i = 0; i < numRows; ++i ) + { + spqr_row row = componentRows[i]; + if( SPQRarcIsValid(dec->rowArcs[row])) + { + dec->rowArcs[row] = SPQR_INVALID_ARC; + } + } + + for( int i = 0; i < numCols; ++i ) + { + spqr_col col = componentCols[i]; + if( SPQRarcIsValid(dec->columnArcs[col])) + { + dec->columnArcs[col] = SPQR_INVALID_ARC; + } + } +} - } +#ifdef SCIP_DEBUG + //Debugging functions to print the decomposition +static char typeToChar(SPQRMemberType type) +{ + switch (type) + { + case SPQR_MEMBERTYPE_RIGID: + return 'R'; + case SPQR_MEMBERTYPE_PARALLEL: + return 'P'; + case SPQR_MEMBERTYPE_SERIES: + return 'S'; + case SPQR_MEMBERTYPE_LOOP: + return 'L'; + default: + return '?'; + } +} + +static void arcToDot(FILE *stream, const SCIP_NETMATDEC *dec, + spqr_arc arc, unsigned long dot_head, unsigned long dot_tail, SCIP_Bool useElementNames) +{ + assert(SPQRarcIsValid(arc)); + spqr_member member = findArcMemberNoCompression(dec, arc); + SPQRMemberType member_type = getMemberType(dec, member); + char type = typeToChar(member_type); + const char *color = arcIsTree(dec, arc) ? ",color=red" : ",color=blue"; + + int arc_name = arc; + + if (markerToParent(dec, member) == arc) + { + if (useElementNames) + { + arc_name = -1; + } + fprintf(stream, " %c_%d_%lu -> %c_p_%d [label=\"%d\",style=dashed%s];\n", type, member, dot_tail, type, member, arc_name, color); + fprintf(stream, " %c_p_%d -> %c_%d_%lu [label=\"%d\",style=dashed%s];\n", type, member, type, member, dot_head, arc_name, color); + fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_tail); + fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_head); + fprintf(stream, " %c_p_%d [style=dashed];\n", type, member); + } else if (arcIsMarker(dec, arc)) + { + spqr_member child = findArcChildMemberNoCompression(dec, arc); + char childType = typeToChar(getMemberType(dec, child)); + if (useElementNames) + { + arc_name = -1; + } + fprintf(stream, " %c_%d_%lu -> %c_c_%d [label=\"%d\",style=dotted%s];\n", type, member, dot_tail, type, child, arc_name, color); + fprintf(stream, " %c_c_%d -> %c_%d_%lu [label=\"%d\",style=dotted%s];\n", type, child, type, member, dot_head, arc_name, color); + fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_tail); + fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_head); + fprintf(stream, " %c_c_%d [style=dotted];\n", type, child); + fprintf(stream, " %c_p_%d -> %c_c_%d [style=dashed,dir=forward];\n", childType, child, type, child); + } else + { + if (useElementNames) + { + spqr_element element = dec->arcs[arc].element; + if (SPQRelementIsRow(element)) + { + arc_name = (int) SPQRelementToRow(element); + } else + { + arc_name = (int) SPQRelementToColumn(element); + } + } + + fprintf(stream, " %c_%d_%lu -> %c_%d_%lu [label=\"%d \",style=bold%s];\n", type, member, dot_tail, type, member, dot_head, + arc_name, color); + fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_tail); + fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_head); + } +} + +static void decompositionToDot(FILE *stream, const SCIP_NETMATDEC *dec, SCIP_Bool useElementNames) +{ + fprintf(stream, "//decomposition\ndigraph decomposition{\n compound = TRUE;\n"); + for (spqr_member member = 0; member < dec->numMembers; ++member) + { + if (!memberIsRepresentative(dec, member)) continue; + fprintf(stream, " subgraph member_%d{\n", member); + switch (getMemberType(dec, member)) + { + case SPQR_MEMBERTYPE_RIGID: { + spqr_arc first_arc = getFirstMemberArc(dec, member); + spqr_arc arc = first_arc; + do + { + unsigned long arcHead = (unsigned long) findEffectiveArcHeadNoCompression(dec, arc); + unsigned long arcTail = (unsigned long) findEffectiveArcTailNoCompression(dec, arc); + arcToDot(stream, dec, arc, arcHead, arcTail, useElementNames); + arc = getNextMemberArc(dec, arc); + } while (arc != first_arc); + break; + } + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_PARALLEL: { + spqr_arc first_arc = getFirstMemberArc(dec, member); + spqr_arc arc = first_arc; + do + { + if (arcIsReversedNonRigid(dec, arc)) + { + arcToDot(stream, dec, arc, 1, 0, useElementNames); + } else + { + arcToDot(stream, dec, arc, 0, 1, useElementNames); + } + arc = getNextMemberArc(dec, arc); + } while (arc != first_arc); + break; + } + case SPQR_MEMBERTYPE_SERIES: { + unsigned long i = 0; + unsigned long num_member_arcs = (unsigned long) getNumMemberArcs(dec, member); + spqr_arc first_arc = getFirstMemberArc(dec, member); + spqr_arc arc = first_arc; + do { + unsigned long head = i; + unsigned long tail = (i + 1) % num_member_arcs; + if (arcIsReversedNonRigid(dec, arc)) + { + unsigned long temp = head; + head = tail; + tail = temp; + } + arcToDot(stream, dec, arc, head, tail, useElementNames); + arc = getNextMemberArc(dec, arc); + i++; + } while (arc != first_arc); + break; + } + case SPQR_MEMBERTYPE_UNASSIGNED: + break; + } + fprintf(stream, " }\n"); + } + fprintf(stream, "}\n"); +} +#endif - reduced_member_id returnedMember = newCol->memberInformation[callstack[0].member].reducedMember; - return returnedMember; +static SCIP_RETCODE mergeGivenMemberIntoParent( + SCIP_NETMATDEC* dec, + spqr_member member, + spqr_member parent, + spqr_arc parentToChild, + spqr_arc childToParent, + SCIP_Bool headToHead, + spqr_member* mergedMember +) +{ + assert(dec); + assert(SPQRmemberIsValid(member)); + assert(memberIsRepresentative(dec, member)); + assert(SPQRmemberIsValid(parent)); + assert(memberIsRepresentative(dec, parent)); + assert(findMemberParentNoCompression(dec, member) == parent); + assert(markerOfParent(dec, member) == parentToChild); + assert(markerToParent(dec, member) == childToParent); + + removeArcFromMemberArcList(dec, parentToChild, parent); + removeArcFromMemberArcList(dec, childToParent, member); + + spqr_node parentArcNodes[2] = {findEffectiveArcTail(dec, parentToChild), findEffectiveArcHead(dec, parentToChild)}; + spqr_node childArcNodes[2] = {findEffectiveArcTail(dec, childToParent), findEffectiveArcHead(dec, childToParent)}; + + clearArcHeadAndTail(dec, parentToChild); + clearArcHeadAndTail(dec, childToParent); + + spqr_node first = childArcNodes[headToHead ? 0 : 1]; + spqr_node second = childArcNodes[headToHead ? 1 : 0]; + { + spqr_node newNode = mergeNodes(dec, parentArcNodes[0], first); + spqr_node toRemoveFrom = newNode == first ? parentArcNodes[0] : first; + mergeNodeArcList(dec, newNode, toRemoveFrom); + } + { + spqr_node newNode = mergeNodes(dec, parentArcNodes[1], second); + spqr_node toRemoveFrom = newNode == second ? parentArcNodes[1] : second; + mergeNodeArcList(dec, newNode, toRemoveFrom); + } + + + spqr_member newMember = mergeMembers(dec, member, parent); + spqr_member toRemoveFrom = newMember == member ? parent : member; + mergeMemberArcList(dec, newMember, toRemoveFrom); + if( toRemoveFrom == parent ) + { + updateMemberParentInformation(dec, newMember, toRemoveFrom); + } + updateMemberType(dec, newMember, SPQR_MEMBERTYPE_RIGID); + *mergedMember = newMember; + return SCIP_OKAY; +} + +static int max( + int a, + int b +) +{ + return ( a > b ) ? a : b; } -static SCIP_RETCODE constructReducedDecomposition(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol) { - assert(dec); - assert(newCol); +typedef int path_arc_id; +#define INVALID_PATH_ARC (-1) + +static SCIP_Bool pathArcIsInvalid(const path_arc_id arc) +{ + return arc < 0; +} + +static SCIP_Bool pathArcIsValid(const path_arc_id arc) +{ + return !pathArcIsInvalid(arc); +} + +typedef struct +{ + spqr_arc arc; + spqr_node arcHead;//These can be used in various places to prevent additional find()'s + spqr_node arcTail; + path_arc_id nextMember; + path_arc_id nextOverall; + SCIP_Bool reversed; +} PathArcListNode; + +typedef int reduced_member_id; +#define INVALID_REDUCED_MEMBER (-1) + +static SCIP_Bool reducedMemberIsInvalid(const reduced_member_id id) +{ + return id < 0; +} + +static SCIP_Bool reducedMemberIsValid(const reduced_member_id id) +{ + return !reducedMemberIsInvalid(id); +} + +typedef int children_idx; + +typedef enum +{ + REDUCEDMEMBER_TYPE_UNASSIGNED = 0, + REDUCEDMEMBER_TYPE_CYCLE = 1, + REDUCEDMEMBER_TYPE_MERGED = 2, + REDUCEDMEMBER_TYPE_NOT_NETWORK = 3 +} ReducedMemberType; + +typedef enum +{ + INTO_HEAD = 0, + INTO_TAIL = 1, + OUT_HEAD = 2, + OUT_TAIL = 3 +} MemberPathType; + +static SCIP_Bool isInto(MemberPathType type) +{ + return type < 2; +} + +static SCIP_Bool isHead(MemberPathType type) +{ + return ( type & 1 ) == 0; +} + +typedef struct +{ + spqr_member member; + spqr_member rootMember; + int depth; + ReducedMemberType type; + reduced_member_id parent; + + children_idx firstChild; + children_idx numChildren; + + path_arc_id firstPathArc; + int numPathArcs; + + SCIP_Bool reverseArcs; + spqr_node rigidPathStart; + spqr_node rigidPathEnd; + + SCIP_Bool pathBackwards; + + int numPropagatedChildren; + int componentIndex; + + MemberPathType pathType; + reduced_member_id nextPathMember; + SCIP_Bool nextPathMemberIsParent; + spqr_arc pathSourceArc; + spqr_arc pathTargetArc; +} SPQRColReducedMember; + +typedef struct +{ + int rootDepth; + reduced_member_id root; + + reduced_member_id pathEndMembers[2]; + int numPathEndMembers; +} SPQRColReducedComponent; + +typedef struct +{ + reduced_member_id reducedMember; + reduced_member_id rootDepthMinimizer; +} MemberInfo; + +typedef struct +{ + spqr_member member; +} CreateReducedMembersCallstack; + +struct SCIP_NetColAdd +{ + SCIP_Bool remainsNetwork; + + SPQRColReducedMember* reducedMembers; + int memReducedMembers; + int numReducedMembers; + + SPQRColReducedComponent* reducedComponents; + int memReducedComponents; + int numReducedComponents; + + MemberInfo* memberInformation; + int memMemberInformation; + int numMemberInformation; + + reduced_member_id* childrenStorage; + int memChildrenStorage; + int numChildrenStorage; + + PathArcListNode* pathArcs; + int memPathArcs; + int numPathArcs; + path_arc_id firstOverallPathArc; + + int* nodeInPathDegree; + int* nodeOutPathDegree; + int memNodePathDegree; + + SCIP_Bool* arcInPath; + SCIP_Bool* arcInPathReversed; + int memArcsInPath; + + CreateReducedMembersCallstack* createReducedMembersCallStack; + int memCreateReducedMembersCallStack; + + spqr_col newColIndex; + + spqr_row* newRowArcs; + SCIP_Bool* newRowArcReversed; + int memNewRowArcs; + int numNewRowArcs; + + spqr_arc* decompositionRowArcs; + SCIP_Bool* decompositionArcReversed; + int memDecompositionRowArcs; + int numDecompositionRowArcs; + + spqr_member* leafMembers; + int numLeafMembers; + int memLeafMembers; +}; + +static void cleanupPreviousIteration( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol +) +{ + assert(dec); + assert(newCol); + + path_arc_id pathArc = newCol->firstOverallPathArc; + while( pathArcIsValid(pathArc)) + { + spqr_node head = newCol->pathArcs[pathArc].arcHead; + spqr_node tail = newCol->pathArcs[pathArc].arcTail; + if( SPQRnodeIsValid(head)) + { + newCol->nodeInPathDegree[head] = 0; + } + if( SPQRnodeIsValid(tail)) + { + newCol->nodeOutPathDegree[tail] = 0; + } + + spqr_arc arc = newCol->pathArcs[pathArc].arc; + if( arc < newCol->memArcsInPath ) + { + newCol->arcInPath[arc] = FALSE; + newCol->arcInPathReversed[arc] = FALSE; + } + pathArc = newCol->pathArcs[pathArc].nextOverall; + } #ifndef NDEBUG - for (int i = 0; i < newCol->memMemberInformation; ++i) { - assert(reducedMemberIsInvalid(newCol->memberInformation[i].reducedMember)); - } + for( int i = 0; i < newCol->memArcsInPath; ++i ) + { + assert(newCol->arcInPath[i] == FALSE); + assert(newCol->arcInPathReversed[i] == FALSE); + } + + for( int i = 0; i < newCol->memNodePathDegree; ++i ) + { + assert(newCol->nodeInPathDegree[i] == 0); + assert(newCol->nodeOutPathDegree[i] == 0); + } #endif - newCol->numReducedComponents = 0; - newCol->numReducedMembers = 0; - if (newCol->numDecompositionRowArcs == 0) { //Early return in case the reduced decomposition will be empty - return SCIP_OKAY; - } - assert(newCol->numReducedMembers == 0); - assert(newCol->numReducedComponents == 0); - - int newSize = largestMemberID(dec); //Is this sufficient? - if (newSize > newCol->memReducedMembers) { - int newArraySize = max(2 * newCol->memReducedMembers, newSize); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->reducedMembers,newCol->memReducedMembers,newArraySize)); - newCol->memReducedMembers = newArraySize; - } - if (newSize > newCol->memMemberInformation) { - int updatedSize = max(2 * newCol->memMemberInformation, newSize); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->memberInformation,newCol->memMemberInformation,updatedSize)); - for (int i = newCol->memMemberInformation; i < updatedSize; ++i) { - newCol->memberInformation[i].reducedMember = INVALID_REDUCED_MEMBER; - newCol->memberInformation[i].rootDepthMinimizer = INVALID_REDUCED_MEMBER; - } - newCol->memMemberInformation = updatedSize; - - } - - int numComponents = numConnectedComponents(dec); - if (numComponents > newCol->memReducedComponents) { - int updatedSize = max(2 * newCol->memReducedComponents, numComponents); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->reducedComponents,newCol->memReducedComponents,updatedSize)); - newCol->memReducedComponents = updatedSize; - } - - int numMembers = getNumMembers(dec); - if (newCol->memCreateReducedMembersCallStack < numMembers) { - int updatedSize = max(2 * newCol->memCreateReducedMembersCallStack, numMembers); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->createReducedMembersCallStack, - newCol->memCreateReducedMembersCallStack, updatedSize)); - newCol->memCreateReducedMembersCallStack = updatedSize; - - } - - //Create the reduced members (recursively) - for (int i = 0; i < newCol->numDecompositionRowArcs; ++i) { - assert(i < newCol->memDecompositionRowArcs); - spqr_arc arc = newCol->decompositionRowArcs[i]; - spqr_member arcMember = findArcMember(dec, arc); - reduced_member_id reducedMember = createReducedMembersToRoot(dec, newCol, arcMember); - reduced_member_id *depthMinimizer = &newCol->memberInformation[newCol->reducedMembers[reducedMember].rootMember].rootDepthMinimizer; - if (reducedMemberIsInvalid(*depthMinimizer)) { - *depthMinimizer = reducedMember; - } - } - - //Set the reduced roots according to the root depth minimizers - for (int i = 0; i < newCol->numReducedComponents; ++i) { - SPQRColReducedComponent *component = &newCol->reducedComponents[i]; - spqr_member rootMember = newCol->reducedMembers[component->root].member; - reduced_member_id reducedMinimizer = newCol->memberInformation[rootMember].rootDepthMinimizer; - component->rootDepth = newCol->reducedMembers[reducedMinimizer].depth; - component->root = reducedMinimizer; - - //This simplifies code further down which does not need to be component-aware; just pretend that the reduced member is the new root. - newCol->reducedMembers[component->root].parent = INVALID_REDUCED_MEMBER; - assert(memberIsRepresentative(dec, rootMember)); - } - - //update the children array - int numTotalChildren = 0; - for (int i = 0; i < newCol->numReducedMembers; ++i) { - SPQRColReducedMember *reducedMember = &newCol->reducedMembers[i]; - reduced_member_id minimizer = newCol->memberInformation[reducedMember->rootMember].rootDepthMinimizer; - if (reducedMember->depth >= newCol->reducedMembers[minimizer].depth) { - reducedMember->firstChild = numTotalChildren; - numTotalChildren += reducedMember->numChildren; - reducedMember->numChildren = 0; - } - } - - if (newCol->memChildrenStorage < numTotalChildren) { - int newMemSize = max(newCol->memChildrenStorage * 2, numTotalChildren); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->childrenStorage, newCol->memChildrenStorage, newMemSize)); - newCol->memChildrenStorage = newMemSize; - } - newCol->numChildrenStorage = numTotalChildren; - - //Fill up the children array` - for (reduced_member_id reducedMember = 0; reducedMember < newCol->numReducedMembers; ++reducedMember) { - SPQRColReducedMember *reducedMemberData = &newCol->reducedMembers[reducedMember]; - if (reducedMemberData->depth <= - newCol->reducedMembers[newCol->memberInformation[reducedMemberData->rootMember].rootDepthMinimizer].depth) { + + newCol->firstOverallPathArc = INVALID_PATH_ARC; + newCol->numPathArcs = 0; +} + +SCIP_RETCODE SCIPnetcoladdCreate( + SCIP* scip, + SCIP_NETCOLADD** pcoladd +) +{ + assert(scip); + + SCIP_CALL(SCIPallocBlockMemory(scip, pcoladd)); + SCIP_NETCOLADD* newCol = *pcoladd; + + newCol->remainsNetwork = FALSE; + newCol->reducedMembers = NULL; + newCol->memReducedMembers = 0; + newCol->numReducedMembers = 0; + + newCol->reducedComponents = NULL; + newCol->memReducedComponents = 0; + newCol->numReducedComponents = 0; + + newCol->memberInformation = NULL; + newCol->memMemberInformation = 0; + newCol->numMemberInformation = 0; + + newCol->childrenStorage = NULL; + newCol->memChildrenStorage = 0; + newCol->numChildrenStorage = 0; + + newCol->pathArcs = NULL; + newCol->memPathArcs = 0; + newCol->numPathArcs = 0; + newCol->firstOverallPathArc = INVALID_PATH_ARC; + + newCol->nodeInPathDegree = NULL; + newCol->nodeOutPathDegree = NULL; + newCol->memNodePathDegree = 0; + + newCol->arcInPath = NULL; + newCol->arcInPathReversed = NULL; + newCol->memArcsInPath = 0; + + newCol->createReducedMembersCallStack = NULL; + newCol->memCreateReducedMembersCallStack = 0; + + newCol->newColIndex = SPQR_INVALID_COL; + + newCol->newRowArcs = NULL; + newCol->newRowArcReversed = NULL; + newCol->memNewRowArcs = 0; + newCol->numNewRowArcs = 0; + + newCol->decompositionRowArcs = NULL; + newCol->decompositionArcReversed = NULL; + newCol->memDecompositionRowArcs = 0; + newCol->numDecompositionRowArcs = 0; + + newCol->leafMembers = NULL; + newCol->numLeafMembers = 0; + newCol->memLeafMembers = 0; + + return SCIP_OKAY; +} + +void SCIPnetcoladdFree( + SCIP* scip, + SCIP_NETCOLADD** pcoladd +) +{ + assert(scip); + SCIP_NETCOLADD* newCol = *pcoladd; + SCIPfreeBlockMemoryArray(scip, &newCol->decompositionRowArcs, newCol->memDecompositionRowArcs); + SCIPfreeBlockMemoryArray(scip, &newCol->decompositionArcReversed, newCol->memDecompositionRowArcs); + SCIPfreeBlockMemoryArray(scip, &newCol->newRowArcs, newCol->memNewRowArcs); + SCIPfreeBlockMemoryArray(scip, &newCol->newRowArcReversed, newCol->memNewRowArcs); + SCIPfreeBlockMemoryArray(scip, &newCol->createReducedMembersCallStack, newCol->memCreateReducedMembersCallStack); + SCIPfreeBlockMemoryArray(scip, &newCol->arcInPath, newCol->memArcsInPath); + SCIPfreeBlockMemoryArray(scip, &newCol->arcInPathReversed, newCol->memArcsInPath); + SCIPfreeBlockMemoryArray(scip, &newCol->nodeInPathDegree, newCol->memNodePathDegree); + SCIPfreeBlockMemoryArray(scip, &newCol->nodeOutPathDegree, newCol->memNodePathDegree); + SCIPfreeBlockMemoryArray(scip, &newCol->pathArcs, newCol->memPathArcs); + SCIPfreeBlockMemoryArray(scip, &newCol->childrenStorage, newCol->memChildrenStorage); + SCIPfreeBlockMemoryArray(scip, &newCol->memberInformation, newCol->memMemberInformation); + SCIPfreeBlockMemoryArray(scip, &newCol->reducedComponents, newCol->memReducedComponents); + SCIPfreeBlockMemoryArray(scip, &newCol->reducedMembers, newCol->memReducedMembers); + SCIPfreeBlockMemoryArray(scip, &newCol->leafMembers, newCol->memLeafMembers); + + SCIPfreeBlockMemory(scip, pcoladd); +} + + +static reduced_member_id createReducedMembersToRoot( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + const spqr_member firstMember +) +{ + assert(SPQRmemberIsValid(firstMember)); + + CreateReducedMembersCallstack* callstack = newCol->createReducedMembersCallStack; + callstack[0].member = firstMember; + int callDepth = 0; + + while( callDepth >= 0 ) + { + spqr_member member = callstack[callDepth].member; + reduced_member_id reducedMember = newCol->memberInformation[member].reducedMember; + + SCIP_Bool reducedValid = reducedMemberIsValid(reducedMember); + if( !reducedValid ) + { + //reduced member was not yet created; we create it + reducedMember = newCol->numReducedMembers; + + SPQRColReducedMember* reducedMemberData = &newCol->reducedMembers[reducedMember]; + ++newCol->numReducedMembers; + + reducedMemberData->member = member; + reducedMemberData->numChildren = 0; + + reducedMemberData->type = REDUCEDMEMBER_TYPE_UNASSIGNED; + reducedMemberData->numPropagatedChildren = 0; + reducedMemberData->firstPathArc = INVALID_PATH_ARC; + reducedMemberData->numPathArcs = 0; + reducedMemberData->rigidPathStart = SPQR_INVALID_NODE; + reducedMemberData->rigidPathEnd = SPQR_INVALID_NODE; + + reducedMemberData->componentIndex = -1; + //The children are set later + + newCol->memberInformation[member].reducedMember = reducedMember; + assert(memberIsRepresentative(dec, member)); + spqr_member parentMember = findMemberParent(dec, member); + + if( SPQRmemberIsValid(parentMember)) + { + //recursive call to parent member + ++callDepth; + assert(callDepth < newCol->memCreateReducedMembersCallStack); + callstack[callDepth].member = parentMember; continue; - } - spqr_member parentMember = findMemberParent(dec, reducedMemberData->member); - reduced_member_id parentReducedMember = SPQRmemberIsValid(parentMember) - ? newCol->memberInformation[parentMember].reducedMember - : INVALID_REDUCED_MEMBER; - if (reducedMemberIsValid(parentReducedMember)) { - SPQRColReducedMember *parentReducedMemberData = &newCol->reducedMembers[parentReducedMember]; - newCol->childrenStorage[parentReducedMemberData->firstChild + - parentReducedMemberData->numChildren] = reducedMember; - ++parentReducedMemberData->numChildren; - } - } - - //Clean up the root depth minimizers. - for (int i = 0; i < newCol->numReducedMembers; ++i) { - SPQRColReducedMember *reducedMember = &newCol->reducedMembers[i]; - assert(reducedMember); - spqr_member rootMember = reducedMember->rootMember; - assert(rootMember >= 0); - assert(rootMember < dec->memMembers); - newCol->memberInformation[rootMember].rootDepthMinimizer = INVALID_REDUCED_MEMBER; - } - - return SCIP_OKAY; -} - -static void cleanUpMemberInformation(SCIP_NETWORKCOLADDITION * newCol){ - //This loop is at the end as memberInformation is also used to assign the cut arcs during propagation - //Clean up the memberInformation array - for (int i = 0; i < newCol->numReducedMembers; ++i) { - newCol->memberInformation[newCol->reducedMembers[i].member].reducedMember = INVALID_REDUCED_MEMBER; - } + + } else + { + //we found a new reduced decomposition component + + reducedMemberData->parent = INVALID_REDUCED_MEMBER; + reducedMemberData->depth = 0; + reducedMemberData->rootMember = member; + reducedMemberData->componentIndex = newCol->numReducedComponents; + + assert(newCol->numReducedComponents < newCol->memReducedComponents); + newCol->reducedComponents[newCol->numReducedComponents].root = reducedMember; + newCol->reducedComponents[newCol->numReducedComponents].numPathEndMembers = 0; + newCol->reducedComponents[newCol->numReducedComponents].pathEndMembers[0] = INVALID_REDUCED_MEMBER; + newCol->reducedComponents[newCol->numReducedComponents].pathEndMembers[1] = INVALID_REDUCED_MEMBER; + ++newCol->numReducedComponents; + } + } + if( reducedValid ) + { + assert(reducedMember < newCol->numReducedMembers); + //Reduced member was already created in earlier call + //update the depth of the root if appropriate + reduced_member_id* depthMinimizer = &newCol->memberInformation[newCol->reducedMembers[reducedMember].rootMember].rootDepthMinimizer; + if( reducedMemberIsInvalid(*depthMinimizer) || + newCol->reducedMembers[reducedMember].depth < newCol->reducedMembers[*depthMinimizer].depth ) + { + *depthMinimizer = reducedMember; + } + } + while( TRUE ) + { + --callDepth; + if( callDepth < 0 ) break; + spqr_member parentMember = callstack[callDepth + 1].member; + reduced_member_id parentReducedMember = newCol->memberInformation[parentMember].reducedMember; + spqr_member currentMember = callstack[callDepth].member; + reduced_member_id currentReducedMember = newCol->memberInformation[currentMember].reducedMember; + + SPQRColReducedMember* parentReducedMemberData = &newCol->reducedMembers[parentReducedMember]; + SPQRColReducedMember* reducedMemberData = &newCol->reducedMembers[currentReducedMember]; + + reducedMemberData->parent = parentReducedMember; + reducedMemberData->depth = parentReducedMemberData->depth + 1; + reducedMemberData->rootMember = parentReducedMemberData->rootMember; + //ensure that all newly created reduced members are pointing to the correct component + assert(parentReducedMemberData->componentIndex >= 0); + reducedMemberData->componentIndex = parentReducedMemberData->componentIndex; + + newCol->reducedMembers[parentReducedMember].numChildren++; + } + } + + reduced_member_id returnedMember = newCol->memberInformation[callstack[0].member].reducedMember; + return returnedMember; +} + +static SCIP_RETCODE constructReducedDecomposition( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol +) +{ + assert(dec); + assert(newCol); #ifndef NDEBUG - for (int i = 0; i < newCol->memMemberInformation; ++i) { - assert(reducedMemberIsInvalid(newCol->memberInformation[i].reducedMember)); - } + for( int i = 0; i < newCol->memMemberInformation; ++i ) + { + assert(reducedMemberIsInvalid(newCol->memberInformation[i].reducedMember)); + } +#endif + newCol->numReducedComponents = 0; + newCol->numReducedMembers = 0; + if( newCol->numDecompositionRowArcs == 0 ) + {//Early return in case the reduced decomposition will be empty + return SCIP_OKAY; + } + assert(newCol->numReducedMembers == 0); + assert(newCol->numReducedComponents == 0); + + int newSize = largestMemberID(dec);//Is this sufficient? + if( newSize > newCol->memReducedMembers ) + { + int newArraySize = max(2 * newCol->memReducedMembers, newSize); + SCIP_CALL( + SCIPreallocBlockMemoryArray(dec->env, &newCol->reducedMembers, newCol->memReducedMembers, newArraySize)); + newCol->memReducedMembers = newArraySize; + } + if( newSize > newCol->memMemberInformation ) + { + int updatedSize = max(2 * newCol->memMemberInformation, newSize); + SCIP_CALL( + SCIPreallocBlockMemoryArray(dec->env, &newCol->memberInformation, newCol->memMemberInformation, updatedSize)); + for( int i = newCol->memMemberInformation; i < updatedSize; ++i ) + { + newCol->memberInformation[i].reducedMember = INVALID_REDUCED_MEMBER; + newCol->memberInformation[i].rootDepthMinimizer = INVALID_REDUCED_MEMBER; + } + newCol->memMemberInformation = updatedSize; + } + + int numComponents = numConnectedComponents(dec); + if( numComponents > newCol->memReducedComponents ) + { + int updatedSize = max(2 * newCol->memReducedComponents, numComponents); + SCIP_CALL( + SCIPreallocBlockMemoryArray(dec->env, &newCol->reducedComponents, newCol->memReducedComponents, updatedSize)); + newCol->memReducedComponents = updatedSize; + } + + int numMembers = getNumMembers(dec); + if( newCol->memCreateReducedMembersCallStack < numMembers ) + { + int updatedSize = max(2 * newCol->memCreateReducedMembersCallStack, numMembers); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->createReducedMembersCallStack, + newCol->memCreateReducedMembersCallStack, updatedSize)); + newCol->memCreateReducedMembersCallStack = updatedSize; + } + + //Create the reduced members (recursively) + for( int i = 0; i < newCol->numDecompositionRowArcs; ++i ) + { + assert(i < newCol->memDecompositionRowArcs); + spqr_arc arc = newCol->decompositionRowArcs[i]; + spqr_member arcMember = findArcMember(dec, arc); + reduced_member_id reducedMember = createReducedMembersToRoot(dec, newCol, arcMember); + reduced_member_id* depthMinimizer = &newCol->memberInformation[newCol->reducedMembers[reducedMember].rootMember].rootDepthMinimizer; + if( reducedMemberIsInvalid(*depthMinimizer)) + { + *depthMinimizer = reducedMember; + } + } + + //Set the reduced roots according to the root depth minimizers + for( int i = 0; i < newCol->numReducedComponents; ++i ) + { + SPQRColReducedComponent* component = &newCol->reducedComponents[i]; + spqr_member rootMember = newCol->reducedMembers[component->root].member; + reduced_member_id reducedMinimizer = newCol->memberInformation[rootMember].rootDepthMinimizer; + component->rootDepth = newCol->reducedMembers[reducedMinimizer].depth; + component->root = reducedMinimizer; + + //This simplifies code further down which does not need to be component-aware; just pretend that the reduced member is the new root. + newCol->reducedMembers[component->root].parent = INVALID_REDUCED_MEMBER; + assert(memberIsRepresentative(dec, rootMember)); + } + + //update the children array + int numTotalChildren = 0; + for( int i = 0; i < newCol->numReducedMembers; ++i ) + { + SPQRColReducedMember* reducedMember = &newCol->reducedMembers[i]; + reduced_member_id minimizer = newCol->memberInformation[reducedMember->rootMember].rootDepthMinimizer; + if( reducedMember->depth >= newCol->reducedMembers[minimizer].depth ) + { + reducedMember->firstChild = numTotalChildren; + numTotalChildren += reducedMember->numChildren; + reducedMember->numChildren = 0; + } + } + + if( newCol->memChildrenStorage < numTotalChildren ) + { + int newMemSize = max(newCol->memChildrenStorage * 2, numTotalChildren); + SCIP_CALL( + SCIPreallocBlockMemoryArray(dec->env, &newCol->childrenStorage, newCol->memChildrenStorage, newMemSize)); + newCol->memChildrenStorage = newMemSize; + } + newCol->numChildrenStorage = numTotalChildren; + + //Fill up the children array` + for( reduced_member_id reducedMember = 0; reducedMember < newCol->numReducedMembers; ++reducedMember ) + { + SPQRColReducedMember* reducedMemberData = &newCol->reducedMembers[reducedMember]; + if( reducedMemberData->depth <= + newCol->reducedMembers[newCol->memberInformation[reducedMemberData->rootMember].rootDepthMinimizer].depth ) + { + continue; + } + spqr_member parentMember = findMemberParent(dec, reducedMemberData->member); + reduced_member_id parentReducedMember = SPQRmemberIsValid(parentMember) + ? newCol->memberInformation[parentMember].reducedMember + : INVALID_REDUCED_MEMBER; + if( reducedMemberIsValid(parentReducedMember)) + { + SPQRColReducedMember* parentReducedMemberData = &newCol->reducedMembers[parentReducedMember]; + newCol->childrenStorage[parentReducedMemberData->firstChild + + parentReducedMemberData->numChildren] = reducedMember; + ++parentReducedMemberData->numChildren; + } + } + + //Clean up the root depth minimizers. + for( int i = 0; i < newCol->numReducedMembers; ++i ) + { + SPQRColReducedMember* reducedMember = &newCol->reducedMembers[i]; + assert(reducedMember); + spqr_member rootMember = reducedMember->rootMember; + assert(rootMember >= 0); + assert(rootMember < dec->memMembers); + newCol->memberInformation[rootMember].rootDepthMinimizer = INVALID_REDUCED_MEMBER; + } + + return SCIP_OKAY; +} + +static void cleanUpMemberInformation(SCIP_NETCOLADD* newCol) +{ + //This loop is at the end as memberInformation is also used to assign the cut arcs during propagation + //Clean up the memberInformation array + for( int i = 0; i < newCol->numReducedMembers; ++i ) + { + newCol->memberInformation[newCol->reducedMembers[i].member].reducedMember = INVALID_REDUCED_MEMBER; + } +#ifndef NDEBUG + for( int i = 0; i < newCol->memMemberInformation; ++i ) + { + assert(reducedMemberIsInvalid(newCol->memberInformation[i].reducedMember)); + } #endif } static void createPathArc( - SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION * newCol, - const spqr_arc arc, const reduced_member_id reducedMember, SCIP_Bool reversed){ - assert(dec); - assert(newCol); - - path_arc_id path_arc = newCol->numPathArcs; - PathArcListNode * listNode = &newCol->pathArcs[path_arc]; - listNode->arc = arc; - - listNode->nextMember = newCol->reducedMembers[reducedMember].firstPathArc; - newCol->reducedMembers[reducedMember].firstPathArc = path_arc; - newCol->reducedMembers[reducedMember].numPathArcs += 1; - - listNode->nextOverall = newCol->firstOverallPathArc; - newCol->firstOverallPathArc = path_arc; - - ++newCol->numPathArcs; - assert(newCol->numPathArcs <= newCol->memPathArcs); - - assert(arc < newCol->memArcsInPath); - newCol->arcInPath[arc] = TRUE; - newCol->arcInPathReversed[arc] = reversed; - assert(memberIsRepresentative(dec,newCol->reducedMembers[reducedMember].member)); - if(getMemberType(dec,newCol->reducedMembers[reducedMember].member) == SPQR_MEMBERTYPE_RIGID){ - - listNode->arcHead = findEffectiveArcHead(dec,arc); - listNode->arcTail = findEffectiveArcTail(dec,arc); - if(reversed){ - swap_ints(&listNode->arcHead,&listNode->arcTail); - } - assert(SPQRnodeIsValid(listNode->arcHead) && SPQRnodeIsValid(listNode->arcTail)); - assert(listNode->arcHead < newCol->memNodePathDegree && listNode->arcTail < newCol->memNodePathDegree); - ++newCol->nodeInPathDegree[listNode->arcHead]; - ++newCol->nodeOutPathDegree[listNode->arcTail]; - }else{ - listNode->arcHead = SPQR_INVALID_NODE; - listNode->arcTail = SPQR_INVALID_NODE; - } - listNode->reversed = reversed; - -} - -static SCIP_RETCODE createPathArcs(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol){ - int maxNumPathArcs = newCol->numDecompositionRowArcs + getNumMembers(dec); - if(newCol->memPathArcs < maxNumPathArcs){ - int newMaxNumArcs = 2*maxNumPathArcs; //safety factor to prevent very frequent reallocations - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newCol->pathArcs, newCol->memPathArcs, newMaxNumArcs)); - newCol->memPathArcs = newMaxNumArcs; - } - int maxPathArcIndex = largestArcID(dec); - if(newCol->memArcsInPath < maxPathArcIndex){ - int newSize = 2*maxPathArcIndex; //safety factor to prevent very frequent reallocations - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newCol->arcInPath,newCol->memArcsInPath,newSize)); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newCol->arcInPathReversed,newCol->memArcsInPath,newSize)); - - for (int i = newCol->memArcsInPath; i < newSize; ++i) { - newCol->arcInPath[i] = FALSE; - newCol->arcInPathReversed[i] = FALSE; - } - newCol->memArcsInPath = newSize; - } - int maxNumNodes = largestNodeID(dec); - if(newCol->memNodePathDegree < maxNumNodes){ - int newSize = 2*maxNumNodes; //safety factor to prevent very frequent reallocations - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newCol->nodeInPathDegree,newCol->memNodePathDegree, newSize)); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newCol->nodeOutPathDegree,newCol->memNodePathDegree,newSize)); - for (int i = newCol->memNodePathDegree; i < newSize; ++i) { - newCol->nodeInPathDegree[i] = 0; - newCol->nodeOutPathDegree[i] = 0; - } - newCol->memNodePathDegree = newSize; - } - for (int i = 0; i < newCol->numDecompositionRowArcs; ++i) { - spqr_arc arc = newCol->decompositionRowArcs[i]; - spqr_member member = findArcMember(dec, arc); - reduced_member_id reducedMember = newCol->memberInformation[member].reducedMember; - createPathArc(dec,newCol,arc,reducedMember,newCol->decompositionArcReversed[i]); - } - - return SCIP_OKAY; + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + const spqr_arc arc, + const reduced_member_id reducedMember, + SCIP_Bool reversed +) +{ + assert(dec); + assert(newCol); + + path_arc_id path_arc = newCol->numPathArcs; + PathArcListNode* listNode = &newCol->pathArcs[path_arc]; + listNode->arc = arc; + + listNode->nextMember = newCol->reducedMembers[reducedMember].firstPathArc; + newCol->reducedMembers[reducedMember].firstPathArc = path_arc; + newCol->reducedMembers[reducedMember].numPathArcs += 1; + + listNode->nextOverall = newCol->firstOverallPathArc; + newCol->firstOverallPathArc = path_arc; + + ++newCol->numPathArcs; + assert(newCol->numPathArcs <= newCol->memPathArcs); + + assert(arc < newCol->memArcsInPath); + newCol->arcInPath[arc] = TRUE; + newCol->arcInPathReversed[arc] = reversed; + assert(memberIsRepresentative(dec, newCol->reducedMembers[reducedMember].member)); + if( getMemberType(dec, newCol->reducedMembers[reducedMember].member) == SPQR_MEMBERTYPE_RIGID ) + { + + listNode->arcHead = findEffectiveArcHead(dec, arc); + listNode->arcTail = findEffectiveArcTail(dec, arc); + if( reversed ) + { + swap_ints(&listNode->arcHead, &listNode->arcTail); + } + assert(SPQRnodeIsValid(listNode->arcHead) && SPQRnodeIsValid(listNode->arcTail)); + assert(listNode->arcHead < newCol->memNodePathDegree && listNode->arcTail < newCol->memNodePathDegree); + ++newCol->nodeInPathDegree[listNode->arcHead]; + ++newCol->nodeOutPathDegree[listNode->arcTail]; + } else + { + listNode->arcHead = SPQR_INVALID_NODE; + listNode->arcTail = SPQR_INVALID_NODE; + } + listNode->reversed = reversed; +} + +static SCIP_RETCODE createPathArcs( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol +) +{ + int maxNumPathArcs = newCol->numDecompositionRowArcs + getNumMembers(dec); + if( newCol->memPathArcs < maxNumPathArcs ) + { + int newMaxNumArcs = 2 * maxNumPathArcs;//safety factor to prevent very frequent reallocations + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->pathArcs, newCol->memPathArcs, newMaxNumArcs)); + newCol->memPathArcs = newMaxNumArcs; + } + int maxPathArcIndex = largestArcID(dec); + if( newCol->memArcsInPath < maxPathArcIndex ) + { + int newSize = 2 * maxPathArcIndex;//safety factor to prevent very frequent reallocations + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->arcInPath, newCol->memArcsInPath, newSize)); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->arcInPathReversed, newCol->memArcsInPath, newSize)); + + for( int i = newCol->memArcsInPath; i < newSize; ++i ) + { + newCol->arcInPath[i] = FALSE; + newCol->arcInPathReversed[i] = FALSE; + } + newCol->memArcsInPath = newSize; + } + int maxNumNodes = largestNodeID(dec); + if( newCol->memNodePathDegree < maxNumNodes ) + { + int newSize = 2 * maxNumNodes;//safety factor to prevent very frequent reallocations + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->nodeInPathDegree, newCol->memNodePathDegree, newSize)); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->nodeOutPathDegree, newCol->memNodePathDegree, newSize)); + for( int i = newCol->memNodePathDegree; i < newSize; ++i ) + { + newCol->nodeInPathDegree[i] = 0; + newCol->nodeOutPathDegree[i] = 0; + } + newCol->memNodePathDegree = newSize; + } + for( int i = 0; i < newCol->numDecompositionRowArcs; ++i ) + { + spqr_arc arc = newCol->decompositionRowArcs[i]; + spqr_member member = findArcMember(dec, arc); + reduced_member_id reducedMember = newCol->memberInformation[member].reducedMember; + createPathArc(dec, newCol, arc, reducedMember, newCol->decompositionArcReversed[i]); + } + + return SCIP_OKAY; } @@ -2476,2122 +3221,2659 @@ static SCIP_RETCODE createPathArcs(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDIT * already part of the decomposition. */ static SCIP_RETCODE -newColUpdateColInformation(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, spqr_col column, - const spqr_row * nonzeroRows, const double * nonzeroValues, size_t numNonzeros) { - newCol->newColIndex = column; - - newCol->numDecompositionRowArcs = 0; - newCol->numNewRowArcs = 0; - - for (size_t i = 0; i < numNonzeros; ++i) { - spqr_arc rowArc = getDecompositionRowArc(dec, nonzeroRows[i]); - SCIP_Bool reversed = nonzeroValues[i] < 0.0; - if (SPQRarcIsValid(rowArc)) { //If the arc is the current decomposition: save it in the array - if (newCol->numDecompositionRowArcs == newCol->memDecompositionRowArcs) { - int newNumArcs = newCol->memDecompositionRowArcs == 0 ? 8 : 2 * newCol->memDecompositionRowArcs; - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->decompositionRowArcs, - newCol->memDecompositionRowArcs,newNumArcs)); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->decompositionArcReversed, - (size_t) newCol->memDecompositionRowArcs,newNumArcs)); - newCol->memDecompositionRowArcs = newNumArcs; - - } - newCol->decompositionRowArcs[newCol->numDecompositionRowArcs] = rowArc; - newCol->decompositionArcReversed[newCol->numDecompositionRowArcs] = reversed; - ++newCol->numDecompositionRowArcs; - } else { - //Not in the decomposition: add it to the set of arcs which are newly added with this row. - if (newCol->numNewRowArcs == newCol->memNewRowArcs) { - int newNumArcs = newCol->memNewRowArcs == 0 ? 8 : 2 * newCol->memNewRowArcs; - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->newRowArcs, - (size_t) newCol->memNewRowArcs,newNumArcs)); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->newRowArcReversed, - (size_t) newCol->memNewRowArcs,newNumArcs)); - newCol->memNewRowArcs = newNumArcs; - - } - newCol->newRowArcs[newCol->numNewRowArcs] = nonzeroRows[i]; - newCol->newRowArcReversed[newCol->numNewRowArcs] = reversed; - newCol->numNewRowArcs++; - } - } - - return SCIP_OKAY; -} - -static SCIP_RETCODE computeLeafMembers(const SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol) { - if (newCol->numReducedMembers > newCol->memLeafMembers) { - int newSize = max(newCol->numReducedMembers, 2 * newCol->memLeafMembers); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->leafMembers,newCol->memLeafMembers, newSize)); - newCol->memLeafMembers = newSize; - } - newCol->numLeafMembers = 0; - - for (reduced_member_id reducedMember = 0; reducedMember < newCol->numReducedMembers; ++reducedMember) { - if (newCol->reducedMembers[reducedMember].numChildren == 0) { - newCol->leafMembers[newCol->numLeafMembers] = reducedMember; - ++newCol->numLeafMembers; - } - } - return SCIP_OKAY; -} - -static void determineRigidPath(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, SPQRColReducedMember * redMem){ - assert(dec); - assert(newCol); - assert(redMem); - - SCIP_Bool isValidPath = TRUE; - redMem->rigidPathStart = SPQR_INVALID_NODE; - redMem->rigidPathEnd = SPQR_INVALID_NODE; - for(path_arc_id pathArc = redMem->firstPathArc; pathArcIsValid(pathArc); pathArc = newCol->pathArcs[pathArc].nextMember){ - spqr_node head = newCol->pathArcs[pathArc].arcHead; - spqr_node tail = newCol->pathArcs[pathArc].arcTail; - assert(nodeIsRepresentative(dec,head) && nodeIsRepresentative(dec,tail)); - - if(newCol->nodeInPathDegree[head] > 1 || newCol->nodeOutPathDegree[tail] > 1){ - //not network -> stop +newColUpdateColInformation( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + spqr_col column, + const spqr_row* nonzeroRows, + const double* nonzeroValues, + size_t numNonzeros +) +{ + newCol->newColIndex = column; + + newCol->numDecompositionRowArcs = 0; + newCol->numNewRowArcs = 0; + + for( size_t i = 0; i < numNonzeros; ++i ) + { + spqr_arc rowArc = getDecompositionRowArc(dec, nonzeroRows[i]); + SCIP_Bool reversed = nonzeroValues[i] < 0.0; + if( SPQRarcIsValid(rowArc)) + {//If the arc is the current decomposition: save it in the array + if( newCol->numDecompositionRowArcs == newCol->memDecompositionRowArcs ) + { + int newNumArcs = newCol->memDecompositionRowArcs == 0 ? 8 : 2 * newCol->memDecompositionRowArcs; + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->decompositionRowArcs, + newCol->memDecompositionRowArcs, newNumArcs)); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->decompositionArcReversed, + (size_t) newCol->memDecompositionRowArcs, newNumArcs)); + newCol->memDecompositionRowArcs = newNumArcs; + } + newCol->decompositionRowArcs[newCol->numDecompositionRowArcs] = rowArc; + newCol->decompositionArcReversed[newCol->numDecompositionRowArcs] = reversed; + ++newCol->numDecompositionRowArcs; + } else + { + //Not in the decomposition: add it to the set of arcs which are newly added with this row. + if( newCol->numNewRowArcs == newCol->memNewRowArcs ) + { + int newNumArcs = newCol->memNewRowArcs == 0 ? 8 : 2 * newCol->memNewRowArcs; + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->newRowArcs, + (size_t) newCol->memNewRowArcs, newNumArcs)); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->newRowArcReversed, + (size_t) newCol->memNewRowArcs, newNumArcs)); + newCol->memNewRowArcs = newNumArcs; + } + newCol->newRowArcs[newCol->numNewRowArcs] = nonzeroRows[i]; + newCol->newRowArcReversed[newCol->numNewRowArcs] = reversed; + newCol->numNewRowArcs++; + } + } + + return SCIP_OKAY; +} + +static SCIP_RETCODE computeLeafMembers( + const SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol +) +{ + if( newCol->numReducedMembers > newCol->memLeafMembers ) + { + int newSize = max(newCol->numReducedMembers, 2 * newCol->memLeafMembers); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->leafMembers, newCol->memLeafMembers, newSize)); + newCol->memLeafMembers = newSize; + } + newCol->numLeafMembers = 0; + + for( reduced_member_id reducedMember = 0; reducedMember < newCol->numReducedMembers; ++reducedMember ) + { + if( newCol->reducedMembers[reducedMember].numChildren == 0 ) + { + newCol->leafMembers[newCol->numLeafMembers] = reducedMember; + ++newCol->numLeafMembers; + } + } + return SCIP_OKAY; +} + +static void determineRigidPath( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + SPQRColReducedMember* redMem +) +{ + assert(dec); + assert(newCol); + assert(redMem); + + SCIP_Bool isValidPath = TRUE; + redMem->rigidPathStart = SPQR_INVALID_NODE; + redMem->rigidPathEnd = SPQR_INVALID_NODE; + for( path_arc_id pathArc = redMem->firstPathArc; pathArcIsValid( + pathArc); pathArc = newCol->pathArcs[pathArc].nextMember ) + { + spqr_node head = newCol->pathArcs[pathArc].arcHead; + spqr_node tail = newCol->pathArcs[pathArc].arcTail; + assert(nodeIsRepresentative(dec, head) && nodeIsRepresentative(dec, tail)); + + if( newCol->nodeInPathDegree[head] > 1 || newCol->nodeOutPathDegree[tail] > 1 ) + { + //not network -> stop + isValidPath = FALSE; + break; + } + if( newCol->nodeOutPathDegree[head] == 0 ) + { + //found end node + //If this is the second, stop + if( SPQRnodeIsValid(redMem->rigidPathEnd)) + { isValidPath = FALSE; break; - } - if(newCol->nodeOutPathDegree[head] == 0){ - //found end node - //If this is the second, stop - if(SPQRnodeIsValid(redMem->rigidPathEnd)){ - isValidPath = FALSE; - break; - } - redMem->rigidPathEnd = head; - } - if(newCol->nodeInPathDegree[tail] == 0){ - //Found start node. - //If this is the second, stop. - if(SPQRnodeIsValid(redMem->rigidPathStart)){ - isValidPath = FALSE; - break; - } - redMem->rigidPathStart = tail; - } - } - //assert that both a start and end node have been found - assert(!isValidPath || (SPQRnodeIsValid(redMem->rigidPathStart) && SPQRnodeIsValid(redMem->rigidPathEnd))); - if(!isValidPath){ - redMem->rigidPathStart = SPQR_INVALID_NODE; - redMem->rigidPathEnd = SPQR_INVALID_NODE; - redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; - newCol->remainsNetwork = FALSE; - } - -} -static void determineSingleRigidType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, reduced_member_id reducedMember, - spqr_member member){ - assert(dec); - assert(newCol); - assert(reducedMemberIsValid(reducedMember)); - SPQRColReducedMember * redMem = &newCol->reducedMembers[reducedMember]; - assert(pathArcIsValid(redMem->firstPathArc)); - determineRigidPath(dec,newCol,redMem); - if(redMem->type != REDUCEDMEMBER_TYPE_NOT_NETWORK){ - redMem->type = REDUCEDMEMBER_TYPE_MERGED; - } + } + redMem->rigidPathEnd = head; + } + if( newCol->nodeInPathDegree[tail] == 0 ) + { + //Found start node. + //If this is the second, stop. + if( SPQRnodeIsValid(redMem->rigidPathStart)) + { + isValidPath = FALSE; + break; + } + redMem->rigidPathStart = tail; + } + } + //assert that both a start and end node have been found + assert(!isValidPath || ( SPQRnodeIsValid(redMem->rigidPathStart) && SPQRnodeIsValid(redMem->rigidPathEnd))); + if( !isValidPath ) + { + redMem->rigidPathStart = SPQR_INVALID_NODE; + redMem->rigidPathEnd = SPQR_INVALID_NODE; + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + } +} + +static void determineSingleRigidType( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + reduced_member_id reducedMember, + spqr_member member +) +{ + assert(dec); + assert(newCol); + assert(reducedMemberIsValid(reducedMember)); + SPQRColReducedMember* redMem = &newCol->reducedMembers[reducedMember]; + assert(pathArcIsValid(redMem->firstPathArc)); + determineRigidPath(dec, newCol, redMem); + if( redMem->type != REDUCEDMEMBER_TYPE_NOT_NETWORK ) + { + redMem->type = REDUCEDMEMBER_TYPE_MERGED; + } } + //TODO: type seems somewhat duplicate -static void determineSingleComponentType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, reduced_member_id reducedMember){ - assert(dec); - assert(newCol); - - int numNonPropagatedAdjacent = newCol->reducedMembers[reducedMember].numChildren-newCol->reducedMembers[reducedMember].numPropagatedChildren; - if(reducedMemberIsValid(newCol->reducedMembers[reducedMember].parent) && - newCol->reducedMembers[newCol->reducedMembers[reducedMember].parent].type != REDUCEDMEMBER_TYPE_CYCLE){ - ++numNonPropagatedAdjacent; - } - - if(numNonPropagatedAdjacent > 2){ - newCol->reducedMembers[reducedMember].type = REDUCEDMEMBER_TYPE_NOT_NETWORK; - newCol->remainsNetwork = FALSE; - return; - } - - - spqr_member member = findMember(dec, newCol->reducedMembers[reducedMember].member); - SPQRMemberType type = getMemberType(dec, member); - switch(type){ - case SPQR_MEMBERTYPE_RIGID: - { - determineSingleRigidType(dec,newCol,reducedMember,member); +static void determineSingleComponentType( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + reduced_member_id reducedMember +) +{ + assert(dec); + assert(newCol); + + int numNonPropagatedAdjacent = + newCol->reducedMembers[reducedMember].numChildren - newCol->reducedMembers[reducedMember].numPropagatedChildren; + if( reducedMemberIsValid(newCol->reducedMembers[reducedMember].parent) && + newCol->reducedMembers[newCol->reducedMembers[reducedMember].parent].type != REDUCEDMEMBER_TYPE_CYCLE ) + { + ++numNonPropagatedAdjacent; + } + + if( numNonPropagatedAdjacent > 2 ) + { + newCol->reducedMembers[reducedMember].type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + + + spqr_member member = findMember(dec, newCol->reducedMembers[reducedMember].member); + SPQRMemberType type = getMemberType(dec, member); + switch( type ) + { + case SPQR_MEMBERTYPE_RIGID: + { + determineSingleRigidType(dec, newCol, reducedMember, member); + break; + } + case SPQR_MEMBERTYPE_PARALLEL: + { + SPQRColReducedMember* redMem = &newCol->reducedMembers[reducedMember]; + assert(pathArcIsValid(redMem->firstPathArc)); + SCIP_Bool pathForwards = newCol->pathArcs[redMem->firstPathArc].reversed == + arcIsReversedNonRigid(dec, newCol->pathArcs[redMem->firstPathArc].arc); + redMem->pathBackwards = !pathForwards; + redMem->type = REDUCEDMEMBER_TYPE_CYCLE; + break; + } + case SPQR_MEMBERTYPE_SERIES: + case SPQR_MEMBERTYPE_LOOP: + { + SPQRColReducedMember* redMem = &newCol->reducedMembers[reducedMember]; + int countedPathArcs = 0; + SCIP_Bool good = TRUE; + SCIP_Bool passesForwards = TRUE; + for( path_arc_id pathArc = redMem->firstPathArc; pathArcIsValid(pathArc); + pathArc = newCol->pathArcs[pathArc].nextMember ) + { + if( countedPathArcs == 0 ) + { + passesForwards = + newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc); + } else if( + ( newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc)) != + passesForwards ) + { + good = FALSE; + break; + } + ++countedPathArcs; + } + if( !good ) + { + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; break; - } - case SPQR_MEMBERTYPE_PARALLEL: - { - SPQRColReducedMember *redMem =&newCol->reducedMembers[reducedMember]; - assert(pathArcIsValid(redMem->firstPathArc)); - SCIP_Bool pathForwards = newCol->pathArcs[redMem->firstPathArc].reversed == - arcIsReversedNonRigid(dec,newCol->pathArcs[redMem->firstPathArc].arc); - redMem->pathBackwards = !pathForwards; + } + + redMem->pathBackwards = !passesForwards; + if( countedPathArcs == getNumMemberArcs(dec, findMember(dec, redMem->member)) - 1 ) + { + //Type -> Cycle; + //Propagate arc redMem->type = REDUCEDMEMBER_TYPE_CYCLE; + } else + { + //Type -> single_end + redMem->type = REDUCEDMEMBER_TYPE_MERGED; + } + break; + } + case SPQR_MEMBERTYPE_UNASSIGNED: + { + assert(FALSE); + break; + } + } +} + + +static void determinePathSeriesType( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + reduced_member_id reducedMember, + spqr_member member, + MemberPathType previousType, + spqr_arc source, + spqr_arc target +) +{ + assert(dec); + assert(newCol); + assert(reducedMemberIsValid(reducedMember)); + assert(SPQRmemberIsValid(member) && memberIsRepresentative(dec, member)); + assert(getMemberType(dec, member) == SPQR_MEMBERTYPE_SERIES); + + SPQRColReducedMember* redMem = &newCol->reducedMembers[reducedMember]; + int countedPathArcs = 0; + + SCIP_Bool good = TRUE; + SCIP_Bool passesForwards = TRUE; + for( path_arc_id pathArc = redMem->firstPathArc; pathArcIsValid(pathArc); + pathArc = newCol->pathArcs[pathArc].nextMember ) + { + if( countedPathArcs == 0 ) + { + passesForwards = + newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc); + } else if(( newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc)) != + passesForwards ) + { + good = FALSE; + break; + } + ++countedPathArcs; + } + //If the internal directions of the arcs do not agree, we have no way to realize + if( !good ) + { + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + //If we are in the first skeleton processed, ignore the previous member type + if( !SPQRarcIsValid(source)) + { + assert(countedPathArcs > 0); + assert(SPQRarcIsValid(target)); + SCIP_Bool firstReversed = arcIsReversedNonRigid(dec, newCol->pathArcs[redMem->firstPathArc].arc); + SCIP_Bool targetReversed = arcIsReversedNonRigid(dec, target); + SCIP_Bool reversePath = newCol->pathArcs[redMem->firstPathArc].reversed; + + if(( firstReversed == targetReversed ) == reversePath ) + { + redMem->pathType = INTO_HEAD; + } else + { + redMem->pathType = OUT_HEAD; + } + redMem->pathBackwards = !passesForwards; + return; + } + SCIP_Bool sourceReversed = arcIsReversedNonRigid(dec, source); + if( countedPathArcs > 0 ) + { + SCIP_Bool isIntoHeadOrOutTail = isInto(previousType) == isHead(previousType); + SCIP_Bool isGood = isIntoHeadOrOutTail == ( sourceReversed == passesForwards ); + if( !isGood ) + { + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + } + redMem->pathBackwards = !passesForwards; + if( SPQRarcIsValid(target)) + { + SCIP_Bool targetReversed = arcIsReversedNonRigid(dec, target); + SCIP_Bool inSameDirection = sourceReversed == targetReversed; + + MemberPathType currentType; + switch( previousType ) + { + case INTO_HEAD: + { + currentType = inSameDirection ? INTO_TAIL : INTO_HEAD; break; - } - case SPQR_MEMBERTYPE_SERIES: - case SPQR_MEMBERTYPE_LOOP: - { - SPQRColReducedMember *redMem =&newCol->reducedMembers[reducedMember]; - int countedPathArcs = 0; - SCIP_Bool good = TRUE; - SCIP_Bool passesForwards = TRUE; - for(path_arc_id pathArc = redMem->firstPathArc; pathArcIsValid(pathArc); - pathArc = newCol->pathArcs[pathArc].nextMember){ - if(countedPathArcs == 0){ - passesForwards = newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec,newCol->pathArcs[pathArc].arc); - }else if((newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec,newCol->pathArcs[pathArc].arc)) != passesForwards){ - good = FALSE; - break; - } - ++countedPathArcs; - } - if(!good){ - redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; - newCol->remainsNetwork = FALSE; - break; - } - - redMem->pathBackwards = !passesForwards; - if (countedPathArcs == getNumMemberArcs(dec, findMember(dec,redMem->member)) -1){ - //Type -> Cycle; - //Propagate arc - redMem->type = REDUCEDMEMBER_TYPE_CYCLE; - }else{ - //Type -> single_end - redMem->type = REDUCEDMEMBER_TYPE_MERGED; - } + } + case INTO_TAIL: + { + currentType = inSameDirection ? INTO_HEAD : INTO_TAIL; break; - } - case SPQR_MEMBERTYPE_UNASSIGNED: - { - assert(FALSE); + } + case OUT_HEAD: + { + currentType = inSameDirection ? OUT_TAIL : OUT_HEAD; break; - } - } -} - - -static void determinePathSeriesType(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION *newCol, - reduced_member_id reducedMember, spqr_member member, MemberPathType previousType, - spqr_arc source, spqr_arc target){ - assert(dec); - assert(newCol); - assert(reducedMemberIsValid(reducedMember)); - assert(SPQRmemberIsValid(member) &&memberIsRepresentative(dec,member)); - assert(getMemberType(dec,member) == SPQR_MEMBERTYPE_SERIES); - - SPQRColReducedMember *redMem =&newCol->reducedMembers[reducedMember]; - int countedPathArcs = 0; - - SCIP_Bool good = TRUE; - SCIP_Bool passesForwards = TRUE; - for(path_arc_id pathArc = redMem->firstPathArc; pathArcIsValid(pathArc); - pathArc = newCol->pathArcs[pathArc].nextMember){ - if(countedPathArcs == 0){ - passesForwards = newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec,newCol->pathArcs[pathArc].arc); - }else if((newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec,newCol->pathArcs[pathArc].arc)) != passesForwards){ - good = FALSE; + } + case OUT_TAIL: + { + currentType = inSameDirection ? OUT_HEAD : OUT_TAIL; break; - } - ++countedPathArcs; - } - //If the internal directions of the arcs do not agree, we have no way to realize - if(!good){ - redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; - newCol->remainsNetwork = FALSE; - return; - } - //If we are in the first skeleton processed, ignore the previous member type - if(!SPQRarcIsValid(source)){ - assert(countedPathArcs > 0); - assert(SPQRarcIsValid(target)); - SCIP_Bool firstReversed = arcIsReversedNonRigid(dec,newCol->pathArcs[redMem->firstPathArc].arc); - SCIP_Bool targetReversed = arcIsReversedNonRigid(dec,target); - SCIP_Bool reversePath = newCol->pathArcs[redMem->firstPathArc].reversed; - - if((firstReversed == targetReversed ) == reversePath){ - redMem->pathType = INTO_HEAD; - }else{ - redMem->pathType = OUT_HEAD; - } - redMem->pathBackwards = !passesForwards; - return; - } - SCIP_Bool sourceReversed = arcIsReversedNonRigid(dec,source); - if(countedPathArcs > 0){ - SCIP_Bool isIntoHeadOrOutTail = isInto(previousType) == isHead(previousType); - SCIP_Bool isGood = isIntoHeadOrOutTail == (sourceReversed == passesForwards); - if(!isGood){ - redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; - newCol->remainsNetwork = FALSE; - return; - } - } - redMem->pathBackwards = !passesForwards; - if(SPQRarcIsValid(target)){ - SCIP_Bool targetReversed = arcIsReversedNonRigid(dec,target); - SCIP_Bool inSameDirection = sourceReversed == targetReversed; - - MemberPathType currentType; - switch(previousType){ - case INTO_HEAD:{ - currentType = inSameDirection ? INTO_TAIL : INTO_HEAD; - break; - } - case INTO_TAIL: + } + default: + { + assert(FALSE); + } + } + redMem->pathType = currentType; + return; + } + //If we are in the last skeleton, we only have a source, so nothing further to do + assert(countedPathArcs > 0); + //Strictly speaking below are no-ops within the algorithm, but help with debugging + if( isInto(previousType)) + { + redMem->pathType = INTO_HEAD; + } else + { + redMem->pathType = OUT_HEAD; + } +} + +static void determinePathParallelType( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + reduced_member_id reducedMember, + spqr_member member, + MemberPathType previousType, + spqr_arc source, + spqr_arc target +) +{ + assert(dec); + assert(newCol); + assert(reducedMemberIsValid(reducedMember)); + assert(SPQRmemberIsValid(member) && memberIsRepresentative(dec, member)); + assert(getMemberType(dec, member) == SPQR_MEMBERTYPE_PARALLEL); + + //Parallel members must always be of degree two; if they are a leaf, they must contain an edge, which could have been propagated + assert(SPQRarcIsValid(source) && SPQRarcIsValid(target)); + SCIP_Bool sourceReversed = arcIsReversedNonRigid(dec, source); + SCIP_Bool targetReversed = arcIsReversedNonRigid(dec, target); + SCIP_Bool inSameDirection = sourceReversed == targetReversed; + + SPQRColReducedMember* redMem = &newCol->reducedMembers[reducedMember]; + + path_arc_id pathArc = redMem->firstPathArc; + + SCIP_Bool arcPresent = FALSE; + if( pathArcIsValid(pathArc)) + { + SCIP_Bool pathArcReversed = + newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc); + SCIP_Bool intoHeadOrOutTail = isInto(previousType) == isHead(previousType); + SCIP_Bool good = intoHeadOrOutTail == ( pathArcReversed != sourceReversed ); + if( !good ) + { + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + arcPresent = TRUE; + } + + SCIP_Bool swapHeadTail = arcPresent != inSameDirection; + MemberPathType currentType; + switch( previousType ) + { + case INTO_HEAD: + { + currentType = swapHeadTail ? INTO_HEAD : INTO_TAIL; + break; + } + case INTO_TAIL: + { + currentType = swapHeadTail ? INTO_TAIL : INTO_HEAD; + break; + } + case OUT_HEAD: + { + currentType = swapHeadTail ? OUT_HEAD : OUT_TAIL; + break; + } + case OUT_TAIL: + { + currentType = swapHeadTail ? OUT_TAIL : OUT_HEAD; + break; + } + } + redMem->pathType = currentType; +} + +static void determinePathRigidType( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + reduced_member_id reducedMember, + spqr_member member, + MemberPathType previousType, + spqr_arc source, + spqr_arc target +) +{ + + SPQRColReducedMember* redMem = &newCol->reducedMembers[reducedMember]; + if( pathArcIsInvalid(redMem->firstPathArc)) + { + assert(SPQRarcIsValid(source)); + assert(SPQRarcIsValid(target)); + //In this case, we need to check if the source and target are adjacent in any node + + spqr_node sourceTail = findEffectiveArcTail(dec, source); + spqr_node sourceHead = findEffectiveArcHead(dec, source); + spqr_node targetTail = findEffectiveArcTail(dec, target); + spqr_node targetHead = findEffectiveArcHead(dec, target); + SCIP_Bool sourceHeadIsTargetHead = sourceHead == targetHead; + SCIP_Bool sourceTailIsTargetHead = sourceTail == targetHead; + SCIP_Bool sourceHeadIsTargetTail = sourceHead == targetTail; + SCIP_Bool sourceTailIsTargetTail = sourceTail == targetTail; + + if( !( sourceHeadIsTargetHead || sourceTailIsTargetHead || sourceHeadIsTargetTail || sourceTailIsTargetTail )) + { + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + assert( + ( sourceHeadIsTargetHead ? 1 : 0 ) + ( sourceHeadIsTargetTail ? 1 : 0 ) + ( sourceTailIsTargetHead ? 1 : 0 ) + + ( sourceTailIsTargetTail ? 1 : 0 ) == 1); + //assert simplicity; they can be adjacent in only exactly one node + SCIP_Bool isSourceHead = sourceHeadIsTargetHead || sourceHeadIsTargetTail; + SCIP_Bool isTargetTail = sourceHeadIsTargetTail || sourceTailIsTargetTail; + if( isHead(previousType) == isSourceHead ) + { + //no need to reflect + redMem->reverseArcs = FALSE; + if( isInto(previousType)) + { + if( isTargetTail ) { - currentType = inSameDirection ? INTO_HEAD : INTO_TAIL; - break; - } - case OUT_HEAD:{ - currentType = inSameDirection ? OUT_TAIL : OUT_HEAD; - break; - } - case OUT_TAIL: + redMem->pathType = INTO_TAIL; + } else { - currentType = inSameDirection ? OUT_HEAD : OUT_TAIL; - break; + redMem->pathType = INTO_HEAD; } - default:{ - assert(FALSE); + } else + { + if( isTargetTail ) + { + redMem->pathType = OUT_TAIL; + } else + { + redMem->pathType = OUT_HEAD; } - } - redMem->pathType = currentType; - return; - } - //If we are in the last skeleton, we only have a source, so nothing further to do - assert(countedPathArcs > 0); - //Strictly speaking below are no-ops within the algorithm, but help with debugging - if(isInto(previousType)){ - redMem->pathType = INTO_HEAD; - }else{ - redMem->pathType = OUT_HEAD; - } -} -static void determinePathParallelType(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION *newCol, - reduced_member_id reducedMember, spqr_member member, MemberPathType previousType, - spqr_arc source, spqr_arc target) { - assert(dec); - assert(newCol); - assert(reducedMemberIsValid(reducedMember)); - assert(SPQRmemberIsValid(member) && memberIsRepresentative(dec, member)); - assert(getMemberType(dec, member) == SPQR_MEMBERTYPE_PARALLEL); - - //Parallel members must always be of degree two; if they are a leaf, they must contain an edge, which could have been propagated - assert(SPQRarcIsValid(source) && SPQRarcIsValid(target)); - SCIP_Bool sourceReversed = arcIsReversedNonRigid(dec,source); - SCIP_Bool targetReversed = arcIsReversedNonRigid(dec,target); - SCIP_Bool inSameDirection = sourceReversed == targetReversed; - - SPQRColReducedMember *redMem =&newCol->reducedMembers[reducedMember]; - - path_arc_id pathArc = redMem->firstPathArc; - - SCIP_Bool arcPresent = FALSE; - if(pathArcIsValid(pathArc)){ - SCIP_Bool pathArcReversed = newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec,newCol->pathArcs[pathArc].arc); - SCIP_Bool intoHeadOrOutTail = isInto(previousType) == isHead(previousType); - SCIP_Bool good = intoHeadOrOutTail == (pathArcReversed != sourceReversed); - if(!good){ - redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; - newCol->remainsNetwork = FALSE; - return; - } - arcPresent = TRUE; - } - - SCIP_Bool swapHeadTail = arcPresent != inSameDirection; - MemberPathType currentType; - switch(previousType){ - case INTO_HEAD:{ - currentType = swapHeadTail ? INTO_HEAD : INTO_TAIL; - break; - } - case INTO_TAIL:{ - currentType = swapHeadTail ? INTO_TAIL : INTO_HEAD; - break; - } - case OUT_HEAD:{ - currentType = swapHeadTail ? OUT_HEAD : OUT_TAIL; - break; - } - case OUT_TAIL:{ - currentType = swapHeadTail ? OUT_TAIL : OUT_HEAD; - break; - } - } - redMem->pathType = currentType; -} -static void determinePathRigidType(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION *newCol, - reduced_member_id reducedMember, spqr_member member, MemberPathType previousType, - spqr_arc source, spqr_arc target) { - - SPQRColReducedMember *redMem = &newCol->reducedMembers[reducedMember]; - if(pathArcIsInvalid(redMem->firstPathArc)){ - assert(SPQRarcIsValid(source)); - assert(SPQRarcIsValid(target)); - //In this case, we need to check if the source and target are adjacent in any node - - spqr_node sourceTail = findEffectiveArcTail(dec, source); - spqr_node sourceHead = findEffectiveArcHead(dec, source); - spqr_node targetTail = findEffectiveArcTail(dec, target); - spqr_node targetHead = findEffectiveArcHead(dec, target); - SCIP_Bool sourceHeadIsTargetHead = sourceHead == targetHead; - SCIP_Bool sourceTailIsTargetHead = sourceTail == targetHead; - SCIP_Bool sourceHeadIsTargetTail = sourceHead == targetTail; - SCIP_Bool sourceTailIsTargetTail = sourceTail == targetTail; - - if(!(sourceHeadIsTargetHead || sourceTailIsTargetHead || sourceHeadIsTargetTail || sourceTailIsTargetTail)){ - redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; - newCol->remainsNetwork = FALSE; - return; - } - assert((sourceHeadIsTargetHead ? 1 : 0) + (sourceHeadIsTargetTail ? 1 : 0) - + (sourceTailIsTargetHead ? 1 : 0) + (sourceTailIsTargetTail ? 1 : 0) == 1 ); - //assert simplicity; they can be adjacent in only exactly one node - SCIP_Bool isSourceHead = sourceHeadIsTargetHead || sourceHeadIsTargetTail; - SCIP_Bool isTargetTail = sourceHeadIsTargetTail || sourceTailIsTargetTail; - if (isHead(previousType) == isSourceHead){ - //no need to reflect - redMem->reverseArcs = FALSE; - if(isInto(previousType)){ - if(isTargetTail){ - redMem->pathType = INTO_TAIL; - }else{ - redMem->pathType = INTO_HEAD; - } - }else{ - if(isTargetTail){ - redMem->pathType = OUT_TAIL; - }else{ - redMem->pathType = OUT_HEAD; - } + } + } else + { + redMem->reverseArcs = TRUE; + //because of the reversal, all the heads/tails are switched below + if( isInto(previousType)) + { + if( isTargetTail ) + { + redMem->pathType = INTO_HEAD; + } else + { + redMem->pathType = INTO_TAIL; } - }else{ - redMem->reverseArcs = TRUE; - //because of the reversal, all the heads/tails are switched below - if(isInto(previousType)){ - if(isTargetTail){ - redMem->pathType = INTO_HEAD; - }else{ - redMem->pathType = INTO_TAIL; - } - }else{ - if(isTargetTail){ - redMem->pathType = OUT_HEAD; - }else{ - redMem->pathType = OUT_TAIL; - } + } else + { + if( isTargetTail ) + { + redMem->pathType = OUT_HEAD; + } else + { + redMem->pathType = OUT_TAIL; } - } - assert(isInto(redMem->pathType) == isInto(previousType)); - return; - } - determineRigidPath(dec, newCol, redMem); - if (redMem->type == REDUCEDMEMBER_TYPE_NOT_NETWORK) { - return; - } - if(SPQRarcIsInvalid(source)){ - assert(SPQRarcIsValid(target)); - spqr_node targetTail = findEffectiveArcTail(dec, target); - spqr_node targetHead = findEffectiveArcHead(dec, target); - redMem->reverseArcs = FALSE; - if(redMem->rigidPathEnd == targetHead){ - redMem->pathType = INTO_HEAD; - }else if(redMem->rigidPathEnd == targetTail){ - redMem->pathType = INTO_TAIL; - }else if(redMem->rigidPathStart == targetHead){ - redMem->pathType = OUT_HEAD; - }else if(redMem->rigidPathStart == targetTail){ - redMem->pathType = OUT_TAIL; - }else { - redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; - newCol->remainsNetwork = FALSE; - } - return; - } - assert(SPQRarcIsValid(source)); - spqr_node sourceTail = findEffectiveArcTail(dec, source); - spqr_node sourceHead = findEffectiveArcHead(dec, source); - - SCIP_Bool startsAtHead = sourceHead == redMem->rigidPathStart; - SCIP_Bool endsAtTail = sourceTail == redMem->rigidPathEnd; - SCIP_Bool startsAtTail = sourceTail == redMem->rigidPathStart; - SCIP_Bool endsAtHead = sourceHead == redMem->rigidPathEnd; - - SCIP_Bool isIntoHeadOrOutTail = isInto(previousType) == isHead(previousType); - if(isIntoHeadOrOutTail){ //into head or outTail - //Check if path starts at head or ends at tail - if(!startsAtHead && !endsAtTail){ - redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; - newCol->remainsNetwork = FALSE; - return; - } - assert(startsAtHead || endsAtTail); //both can hold; they can form cycle but other components can not be reduced -// redMem->reverseArcs = isInto(previousType) != startsAtHead; //Reverse only if there is no path starting at head - }else{ //Into tail or outHead - //Check if path starts at tail or ends at head - if(!startsAtTail && !endsAtHead){ - redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; - newCol->remainsNetwork = FALSE; - return; - } - assert(startsAtTail || endsAtHead); // both can hold; they can form cycle but other components can not be reduced -// redMem->reverseArcs = isInto(previousType) != startsAtTail; //Reverse only if there is no path starting at tail - } - - if(SPQRarcIsValid(target)){ - spqr_node targetTail = findEffectiveArcTail(dec, target); - spqr_node targetHead = findEffectiveArcHead(dec, target); - - //Check if they are not parallel; (below logic relies on this fact) - assert(!((targetHead == sourceHead && targetTail == sourceTail) || (targetHead == sourceTail && targetTail == sourceHead))); - - SCIP_Bool startsAtTargetHead = redMem->rigidPathStart == targetHead; - SCIP_Bool startsAtTargetTail = redMem->rigidPathStart == targetTail; - SCIP_Bool endsAtTargetHead = redMem->rigidPathEnd == targetHead; - SCIP_Bool endsAtTargetTail = redMem->rigidPathEnd == targetTail; - - if(!(startsAtTargetHead || startsAtTargetTail || endsAtTargetHead || endsAtTargetTail)){ - redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; - newCol->remainsNetwork = FALSE; - return; - } - SCIP_Bool outReverse = FALSE; - SCIP_Bool outHead = FALSE; - - if(isInto(previousType) == isHead(previousType)) { - if (startsAtHead && endsAtTail) { - outReverse = (startsAtTargetHead || startsAtTargetTail) == isInto(previousType); - } else if(startsAtHead) { - outReverse = !isInto(previousType); - } else { - assert(endsAtTail); - outReverse = isInto(previousType); + } + } + assert(isInto(redMem->pathType) == isInto(previousType)); + return; + } + determineRigidPath(dec, newCol, redMem); + if( redMem->type == REDUCEDMEMBER_TYPE_NOT_NETWORK ) + { + return; + } + if( SPQRarcIsInvalid(source)) + { + assert(SPQRarcIsValid(target)); + spqr_node targetTail = findEffectiveArcTail(dec, target); + spqr_node targetHead = findEffectiveArcHead(dec, target); + redMem->reverseArcs = FALSE; + if( redMem->rigidPathEnd == targetHead ) + { + redMem->pathType = INTO_HEAD; + } else if( redMem->rigidPathEnd == targetTail ) + { + redMem->pathType = INTO_TAIL; + } else if( redMem->rigidPathStart == targetHead ) + { + redMem->pathType = OUT_HEAD; + } else if( redMem->rigidPathStart == targetTail ) + { + redMem->pathType = OUT_TAIL; + } else + { + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + } + return; + } + assert(SPQRarcIsValid(source)); + spqr_node sourceTail = findEffectiveArcTail(dec, source); + spqr_node sourceHead = findEffectiveArcHead(dec, source); + + SCIP_Bool startsAtHead = sourceHead == redMem->rigidPathStart; + SCIP_Bool endsAtTail = sourceTail == redMem->rigidPathEnd; + SCIP_Bool startsAtTail = sourceTail == redMem->rigidPathStart; + SCIP_Bool endsAtHead = sourceHead == redMem->rigidPathEnd; + + SCIP_Bool isIntoHeadOrOutTail = isInto(previousType) == isHead(previousType); + if( isIntoHeadOrOutTail ) + {//into head or outTail + //Check if path starts at head or ends at tail + if( !startsAtHead && !endsAtTail ) + { + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + assert(startsAtHead || endsAtTail);//both can hold; they can form cycle but other components can not be reduced + // redMem->reverseArcs = isInto(previousType) != startsAtHead; //Reverse only if there is no path starting at head + } else + {//Into tail or outHead + //Check if path starts at tail or ends at head + if( !startsAtTail && !endsAtHead ) + { + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + assert(startsAtTail || endsAtHead);// both can hold; they can form cycle but other components can not be reduced + // redMem->reverseArcs = isInto(previousType) != startsAtTail; //Reverse only if there is no path starting at tail + } + + if( SPQRarcIsValid(target)) + { + spqr_node targetTail = findEffectiveArcTail(dec, target); + spqr_node targetHead = findEffectiveArcHead(dec, target); + + //Check if they are not parallel; (below logic relies on this fact) + assert(!(( targetHead == sourceHead && targetTail == sourceTail ) || + ( targetHead == sourceTail && targetTail == sourceHead ))); + + SCIP_Bool startsAtTargetHead = redMem->rigidPathStart == targetHead; + SCIP_Bool startsAtTargetTail = redMem->rigidPathStart == targetTail; + SCIP_Bool endsAtTargetHead = redMem->rigidPathEnd == targetHead; + SCIP_Bool endsAtTargetTail = redMem->rigidPathEnd == targetTail; + + if( !( startsAtTargetHead || startsAtTargetTail || endsAtTargetHead || endsAtTargetTail )) + { + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + SCIP_Bool outReverse = FALSE; + SCIP_Bool outHead = FALSE; + + if( isInto(previousType) == isHead(previousType)) + { + if( startsAtHead && endsAtTail ) + { + outReverse = ( startsAtTargetHead || startsAtTargetTail ) == isInto(previousType); + } else if( startsAtHead ) + { + outReverse = !isInto(previousType); + } else + { + assert(endsAtTail); + outReverse = isInto(previousType); + } + } else + { + if( startsAtTail && endsAtHead ) + { + outReverse = ( startsAtTargetHead || startsAtTargetTail ) == isInto(previousType); + } else if( startsAtTail ) + { + outReverse = !isInto(previousType); + } else + { + assert(endsAtHead); + outReverse = isInto(previousType); + } + } + + //TODO: these can probably be simplified significantly, but this might pose risk of introducing incorrect assumptions + SCIP_Bool isBad = FALSE; + if( isInto(previousType) == isHead(previousType)) + { + if( startsAtHead && endsAtTail ) + { + outHead = ( startsAtTargetTail || endsAtTargetHead ) == isInto(previousType); + } else if( startsAtHead ) + { + if( endsAtTargetHead ) + { + outHead = isInto(previousType); + } else if( endsAtTargetTail ) + { + outHead = !isInto(previousType); + } else + { + isBad = TRUE; } - }else{ - if (startsAtTail && endsAtHead) { - outReverse = (startsAtTargetHead || startsAtTargetTail) == isInto(previousType); - } else if (startsAtTail) { - outReverse = !isInto(previousType); - } else { - assert(endsAtHead); - outReverse = isInto(previousType); + } else + { + assert(endsAtTail); + if( startsAtTargetTail ) + { + outHead = isInto(previousType); + } else if( startsAtTargetHead ) + { + outHead = !isInto(previousType); + } else + { + isBad = TRUE; } - } - - //TODO: these can probably be simplified significantly, but this might pose risk of introducing incorrect assumptions - SCIP_Bool isBad = FALSE; - if(isInto(previousType) == isHead(previousType)){ - if(startsAtHead && endsAtTail){ - outHead = (startsAtTargetTail || endsAtTargetHead) == isInto(previousType); - }else if(startsAtHead){ - if(endsAtTargetHead){ - outHead = isInto(previousType); - }else if (endsAtTargetTail){ - outHead = !isInto(previousType); - }else{ - isBad = TRUE; - } - }else{ - assert(endsAtTail); - if(startsAtTargetTail){ - outHead = isInto(previousType); - }else if (startsAtTargetHead){ - outHead = !isInto(previousType); - }else{ - isBad = TRUE; - } + } + } else + { + if( startsAtTail && endsAtHead ) + { + outHead = ( startsAtTargetTail || endsAtTargetHead ) == isInto(previousType); + } else if( startsAtTail ) + { + if( endsAtTargetHead ) + { + outHead = isInto(previousType); + } else if( endsAtTargetTail ) + { + outHead = !isInto(previousType); + } else + { + isBad = TRUE; } - }else{ - if(startsAtTail && endsAtHead){ - outHead = (startsAtTargetTail || endsAtTargetHead) == isInto(previousType); - }else if(startsAtTail){ - if(endsAtTargetHead){ - outHead = isInto(previousType); - }else if(endsAtTargetTail){ - outHead = !isInto(previousType); - }else{ - isBad = TRUE; - } - }else{ - assert(endsAtHead); - if(startsAtTargetTail){ - outHead = isInto(previousType); - }else if (startsAtTargetHead){ - outHead = !isInto(previousType); - }else{ - isBad = TRUE; - } + } else + { + assert(endsAtHead); + if( startsAtTargetTail ) + { + outHead = isInto(previousType); + } else if( startsAtTargetHead ) + { + outHead = !isInto(previousType); + } else + { + isBad = TRUE; } - } - if(isBad){ - redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; - newCol->remainsNetwork = FALSE; - return; - } - - redMem->reverseArcs = outReverse; - if(isInto(previousType)){ - redMem->pathType = outHead ? INTO_HEAD : INTO_TAIL; - }else{ - redMem->pathType = outHead ? OUT_HEAD : OUT_TAIL; - } - return; - } - - //TODO: is this simplifyable? - if(isInto(previousType) == isHead(previousType)){ - redMem->reverseArcs = startsAtHead != isInto(previousType); - }else{ - redMem->reverseArcs = startsAtTail != isInto(previousType); - } - //last member of the path. Since we already checked the source, - //Below is technically no op, but helps with debugging - if(isInto(previousType)){ - redMem->pathType = INTO_HEAD; - }else{ - redMem->pathType = OUT_HEAD; - } -} -static void determinePathMemberType(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION *newCol, - reduced_member_id reducedMember, spqr_member member, MemberPathType previousType, - spqr_arc source, spqr_arc target){ - newCol->reducedMembers[reducedMember].pathSourceArc = source; - newCol->reducedMembers[reducedMember].pathTargetArc = target; - //Check if the marked edges with the given signs - //form a (reverse) directed path from one of the source's end nodes to one of the target's end nodes - switch(getMemberType(dec,member)){ - case SPQR_MEMBERTYPE_RIGID:{ - determinePathRigidType(dec,newCol,reducedMember,member,previousType,source,target); - break; - } - case SPQR_MEMBERTYPE_PARALLEL:{ - determinePathParallelType(dec,newCol,reducedMember,member,previousType,source,target); - break; - } - case SPQR_MEMBERTYPE_SERIES:{ - determinePathSeriesType(dec,newCol,reducedMember,member,previousType,source,target); + } + } + if( isBad ) + { + redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; + newCol->remainsNetwork = FALSE; + return; + } + + redMem->reverseArcs = outReverse; + if( isInto(previousType)) + { + redMem->pathType = outHead ? INTO_HEAD : INTO_TAIL; + } else + { + redMem->pathType = outHead ? OUT_HEAD : OUT_TAIL; + } + return; + } + + //TODO: is this simplifyable? + if( isInto(previousType) == isHead(previousType)) + { + redMem->reverseArcs = startsAtHead != isInto(previousType); + } else + { + redMem->reverseArcs = startsAtTail != isInto(previousType); + } + //last member of the path. Since we already checked the source, + //Below is technically no op, but helps with debugging + if( isInto(previousType)) + { + redMem->pathType = INTO_HEAD; + } else + { + redMem->pathType = OUT_HEAD; + } +} + +static void determinePathMemberType( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + reduced_member_id reducedMember, + spqr_member member, + MemberPathType previousType, + spqr_arc source, + spqr_arc target +) +{ + newCol->reducedMembers[reducedMember].pathSourceArc = source; + newCol->reducedMembers[reducedMember].pathTargetArc = target; + //Check if the marked edges with the given signs + //form a (reverse) directed path from one of the source's end nodes to one of the target's end nodes + switch( getMemberType(dec, member)) + { + case SPQR_MEMBERTYPE_RIGID: + { + determinePathRigidType(dec, newCol, reducedMember, member, previousType, source, target); + break; + } + case SPQR_MEMBERTYPE_PARALLEL: + { + determinePathParallelType(dec, newCol, reducedMember, member, previousType, source, target); + break; + } + case SPQR_MEMBERTYPE_SERIES: + { + determinePathSeriesType(dec, newCol, reducedMember, member, previousType, source, target); + break; + } + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_UNASSIGNED: + { + assert(FALSE); + //In release, + + newCol->remainsNetwork = FALSE; + break; + } + } +} + +static void determinePathTypes( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + SPQRColReducedComponent* component +) +{ + assert(dec); + assert(newCol); + assert(component); + assert(component->numPathEndMembers == 2); + + assert(component->pathEndMembers[0] != component->root); + + //We check the path by going from end to end. We start at the leaf and process every component, + //walking down until we hit the root. + //Then, we walk up from the root and process each component in the same manner. + reduced_member_id reducedStart = component->pathEndMembers[0]; + spqr_arc toPrevious = SPQR_INVALID_ARC; + spqr_arc toNext = SPQR_INVALID_ARC; + MemberPathType previousType = INTO_HEAD;//Arbitrary, is ignored in the first call + + spqr_member member = newCol->reducedMembers[reducedStart].member; + reduced_member_id reducedMember = reducedStart; + reduced_member_id previousReducedMember = reducedStart; + while( reducedMember != component->root ) + { + toNext = markerToParent(dec, member); + determinePathMemberType(dec, newCol, reducedMember, member, previousType, toPrevious, toNext); + if( !newCol->remainsNetwork ) + { + return; + } + previousType = newCol->reducedMembers[reducedMember].pathType; + toPrevious = markerOfParent(dec, member); + member = findMemberParent(dec, newCol->reducedMembers[reducedMember].member); + previousReducedMember = reducedMember; + reducedMember = newCol->memberInformation[member].reducedMember; + newCol->reducedMembers[previousReducedMember].nextPathMember = reducedMember; + newCol->reducedMembers[previousReducedMember].nextPathMemberIsParent = TRUE; + } + + while( reducedMember != component->pathEndMembers[1] ) + { + //Search the (other) child node + reduced_member_id child = INVALID_REDUCED_MEMBER; + //a bit ugly linear search, but not a problem for time complexity + for( children_idx i = newCol->reducedMembers[reducedMember].firstChild; + i < + newCol->reducedMembers[reducedMember].firstChild + newCol->reducedMembers[reducedMember].numChildren; ++i ) + { + reduced_member_id childReduced = newCol->childrenStorage[i]; + if( newCol->reducedMembers[childReduced].type != REDUCEDMEMBER_TYPE_CYCLE && + childReduced != previousReducedMember ) + { + child = childReduced; break; - } - case SPQR_MEMBERTYPE_LOOP: - case SPQR_MEMBERTYPE_UNASSIGNED: - { - assert(FALSE); - //In release, - + } + } + assert(reducedMemberIsValid(child)); + + spqr_member childMember = newCol->reducedMembers[child].member; + toNext = markerOfParent(dec, childMember); + + determinePathMemberType(dec, newCol, reducedMember, member, previousType, toPrevious, toNext); + if( !newCol->remainsNetwork ) + { + return; + } + previousType = newCol->reducedMembers[reducedMember].pathType; + toPrevious = markerToParent(dec, childMember); + member = childMember; + previousReducedMember = reducedMember; + reducedMember = child; + newCol->reducedMembers[previousReducedMember].nextPathMember = reducedMember; + newCol->reducedMembers[previousReducedMember].nextPathMemberIsParent = FALSE; + } + //The last iteration is not performed by the loops above. + //We explicitly set the target arc to invalid in order to indicate that this is the last iteration. + toNext = SPQR_INVALID_ARC; + determinePathMemberType(dec, newCol, reducedMember, member, previousType, toPrevious, toNext); + newCol->reducedMembers[reducedMember].nextPathMember = INVALID_REDUCED_MEMBER; + //since we return anyways, no need to check newCol->remainsNetwork explicitly +} + +static void checkRigidLeaf( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + reduced_member_id leaf, + spqr_arc toParent, + reduced_member_id parent, + spqr_arc toChild +) +{ + SPQRColReducedMember* leafMember = &newCol->reducedMembers[leaf]; + determineRigidPath(dec, newCol, leafMember); + if( leafMember->type == REDUCEDMEMBER_TYPE_NOT_NETWORK ) + { + return; + } + spqr_node targetHead = findEffectiveArcHead(dec, toParent); + spqr_node targetTail = findEffectiveArcTail(dec, toParent); + SCIP_Bool matches = leafMember->rigidPathStart == targetTail && leafMember->rigidPathEnd == targetHead; + SCIP_Bool opposite = leafMember->rigidPathStart == targetHead && leafMember->rigidPathEnd == targetTail; + if( matches || opposite ) + { + leafMember->type = REDUCEDMEMBER_TYPE_CYCLE; + createPathArc(dec, newCol, toChild, parent, opposite); + return; + } + leafMember->type = REDUCEDMEMBER_TYPE_MERGED; +} + +static ReducedMemberType checkLeaf( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + reduced_member_id leaf, + spqr_arc toParent, + reduced_member_id parent, + spqr_arc toChild +) +{ + assert(dec); + assert(newCol); + assert(SPQRarcIsValid(toParent)); + assert(SPQRarcIsValid(toChild)); + assert(!arcIsTree(dec, toParent)); + assert(reducedMemberIsValid(leaf)); + assert(reducedMemberIsValid(parent)); + + switch( getMemberType(dec, newCol->reducedMembers[leaf].member)) + { + case SPQR_MEMBERTYPE_RIGID: + { + checkRigidLeaf(dec, newCol, leaf, toParent, parent, toChild); + break; + } + case SPQR_MEMBERTYPE_PARALLEL: + { + SPQRColReducedMember* reducedMember = &newCol->reducedMembers[leaf]; + assert(pathArcIsValid(reducedMember->firstPathArc)); + reducedMember->type = REDUCEDMEMBER_TYPE_CYCLE; + + SCIP_Bool pathArcReversed = newCol->pathArcs[reducedMember->firstPathArc].reversed; + SCIP_Bool arcPathArcIsReverse = arcIsReversedNonRigid(dec, newCol->pathArcs[reducedMember->firstPathArc].arc); + SCIP_Bool parentReversed = arcIsReversedNonRigid(dec, toParent); + createPathArc(dec, newCol, toChild, parent, ( arcPathArcIsReverse == parentReversed ) == pathArcReversed); + break; + } + case SPQR_MEMBERTYPE_SERIES: + case SPQR_MEMBERTYPE_LOOP: + { + SPQRColReducedMember* reducedMember = &newCol->reducedMembers[leaf]; + int countedPathArcs = 0; + SCIP_Bool good = TRUE; + SCIP_Bool passesForwards = TRUE; + for( path_arc_id pathArc = reducedMember->firstPathArc; pathArcIsValid(pathArc); + pathArc = newCol->pathArcs[pathArc].nextMember ) + { + if( countedPathArcs == 0 ) + { + passesForwards = + newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc); + } else if( + ( newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc)) != + passesForwards ) + { + good = FALSE; + break; + } + ++countedPathArcs; + } + if( !good ) + { + reducedMember->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; newCol->remainsNetwork = FALSE; break; - } - } - -} - -static void determinePathTypes(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, SPQRColReducedComponent * component){ - assert(dec); - assert(newCol); - assert(component); - assert(component->numPathEndMembers == 2); - - assert(component->pathEndMembers[0] != component->root); - - //We check the path by going from end to end. We start at the leaf and process every component, - //walking down until we hit the root. - //Then, we walk up from the root and process each component in the same manner. - reduced_member_id reducedStart = component->pathEndMembers[0]; - spqr_arc toPrevious = SPQR_INVALID_ARC; - spqr_arc toNext = SPQR_INVALID_ARC; - MemberPathType previousType = INTO_HEAD; //Arbitrary, is ignored in the first call - - spqr_member member = newCol->reducedMembers[reducedStart].member; - reduced_member_id reducedMember = reducedStart; - reduced_member_id previousReducedMember = reducedStart; - while(reducedMember != component->root){ - toNext = markerToParent(dec,member); - determinePathMemberType(dec,newCol,reducedMember,member,previousType,toPrevious,toNext); - if(!newCol->remainsNetwork){ - return; - } - previousType = newCol->reducedMembers[reducedMember].pathType; - toPrevious = markerOfParent(dec,member); - member = findMemberParent(dec,newCol->reducedMembers[reducedMember].member); - previousReducedMember = reducedMember; - reducedMember = newCol->memberInformation[member].reducedMember; - newCol->reducedMembers[previousReducedMember].nextPathMember = reducedMember; - newCol->reducedMembers[previousReducedMember].nextPathMemberIsParent = TRUE; - } - - while(reducedMember != component->pathEndMembers[1]){ - //Search the (other) child node - reduced_member_id child = INVALID_REDUCED_MEMBER; - //a bit ugly linear search, but not a problem for time complexity - for (children_idx i = newCol->reducedMembers[reducedMember].firstChild; - i < newCol->reducedMembers[reducedMember].firstChild + newCol->reducedMembers[reducedMember].numChildren; ++i) { - reduced_member_id childReduced = newCol->childrenStorage[i]; - if(newCol->reducedMembers[childReduced].type != REDUCEDMEMBER_TYPE_CYCLE && - childReduced != previousReducedMember){ - child = childReduced; - break; - } - } - assert(reducedMemberIsValid(child)); + } - spqr_member childMember = newCol->reducedMembers[child].member; - toNext = markerOfParent(dec,childMember); - - determinePathMemberType(dec,newCol,reducedMember,member,previousType,toPrevious,toNext); - if(!newCol->remainsNetwork){ - return; - } - previousType = newCol->reducedMembers[reducedMember].pathType; - toPrevious = markerToParent(dec,childMember); - member = childMember; - previousReducedMember = reducedMember; - reducedMember = child; - newCol->reducedMembers[previousReducedMember].nextPathMember = reducedMember; - newCol->reducedMembers[previousReducedMember].nextPathMemberIsParent = FALSE; - } - //The last iteration is not performed by the loops above. - //We explicitly set the target arc to invalid in order to indicate that this is the last iteration. - toNext = SPQR_INVALID_ARC; - determinePathMemberType(dec,newCol,reducedMember,member,previousType,toPrevious,toNext); - newCol->reducedMembers[reducedMember].nextPathMember = INVALID_REDUCED_MEMBER; - //since we return anyways, no need to check newCol->remainsNetwork explicitly -} -static void checkRigidLeaf(SCIP_NETWORKDECOMP * dec,SCIP_NETWORKCOLADDITION * newCol, reduced_member_id leaf, - spqr_arc toParent, reduced_member_id parent, spqr_arc toChild){ - SPQRColReducedMember * leafMember = &newCol->reducedMembers[leaf]; - determineRigidPath(dec,newCol,leafMember); - if(leafMember->type == REDUCEDMEMBER_TYPE_NOT_NETWORK){ - return; - } - spqr_node targetHead = findEffectiveArcHead(dec,toParent); - spqr_node targetTail = findEffectiveArcTail(dec,toParent); - SCIP_Bool matches = leafMember->rigidPathStart == targetTail && leafMember->rigidPathEnd == targetHead; - SCIP_Bool opposite = leafMember->rigidPathStart == targetHead && leafMember->rigidPathEnd == targetTail; - if(matches || opposite) { - leafMember->type = REDUCEDMEMBER_TYPE_CYCLE; - createPathArc(dec,newCol,toChild,parent, opposite); - return; - } - leafMember->type = REDUCEDMEMBER_TYPE_MERGED; -} -static ReducedMemberType checkLeaf(SCIP_NETWORKDECOMP * dec,SCIP_NETWORKCOLADDITION * newCol, reduced_member_id leaf, - spqr_arc toParent, reduced_member_id parent, spqr_arc toChild){ - assert(dec); - assert(newCol); - assert(SPQRarcIsValid(toParent)); - assert(SPQRarcIsValid(toChild)); - assert(!arcIsTree(dec,toParent)); - assert(reducedMemberIsValid(leaf)); - assert(reducedMemberIsValid(parent)); - - switch(getMemberType(dec,newCol->reducedMembers[leaf].member)){ - case SPQR_MEMBERTYPE_RIGID: - { - checkRigidLeaf(dec,newCol,leaf,toParent,parent,toChild); - break; - } - case SPQR_MEMBERTYPE_PARALLEL: - { - SPQRColReducedMember *reducedMember =&newCol->reducedMembers[leaf]; - assert(pathArcIsValid(reducedMember->firstPathArc)); + reducedMember->pathBackwards = !passesForwards; + if( countedPathArcs == getNumMemberArcs(dec, findMember(dec, reducedMember->member)) - 1 ) + { + //Type -> Cycle; + //Propagate arc reducedMember->type = REDUCEDMEMBER_TYPE_CYCLE; - SCIP_Bool pathArcReversed = newCol->pathArcs[reducedMember->firstPathArc].reversed; - SCIP_Bool arcPathArcIsReverse = arcIsReversedNonRigid(dec, newCol->pathArcs[reducedMember->firstPathArc].arc); + SCIP_Bool firstArcReversed = arcIsReversedNonRigid(dec, newCol->pathArcs[reducedMember->firstPathArc].arc); + SCIP_Bool firstArcInPathReverse = newCol->pathArcs[reducedMember->firstPathArc].reversed; SCIP_Bool parentReversed = arcIsReversedNonRigid(dec, toParent); - createPathArc(dec, newCol, toChild, parent, (arcPathArcIsReverse == parentReversed) == pathArcReversed); - break; - } - case SPQR_MEMBERTYPE_SERIES: - case SPQR_MEMBERTYPE_LOOP: - { - SPQRColReducedMember *reducedMember =&newCol->reducedMembers[leaf]; - int countedPathArcs = 0; - SCIP_Bool good = TRUE; - SCIP_Bool passesForwards = TRUE; - for(path_arc_id pathArc = reducedMember->firstPathArc; pathArcIsValid(pathArc); - pathArc = newCol->pathArcs[pathArc].nextMember){ - if(countedPathArcs == 0){ - passesForwards = newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec,newCol->pathArcs[pathArc].arc); - }else if((newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec,newCol->pathArcs[pathArc].arc)) != passesForwards){ - good = FALSE; - break; - } - ++countedPathArcs; - } - if(!good){ - reducedMember->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; - newCol->remainsNetwork = FALSE; - break; - } - - reducedMember->pathBackwards = !passesForwards; - if (countedPathArcs == getNumMemberArcs(dec, findMember(dec,reducedMember->member)) -1){ - //Type -> Cycle; - //Propagate arc - reducedMember->type = REDUCEDMEMBER_TYPE_CYCLE; - - SCIP_Bool firstArcReversed = arcIsReversedNonRigid(dec,newCol->pathArcs[reducedMember->firstPathArc].arc); - SCIP_Bool firstArcInPathReverse = newCol->pathArcs[reducedMember->firstPathArc].reversed; - SCIP_Bool parentReversed = arcIsReversedNonRigid(dec,toParent); - createPathArc(dec,newCol,toChild,parent,(parentReversed == firstArcReversed) != firstArcInPathReverse); - - }else{ - //Type -> single_end - reducedMember->type = REDUCEDMEMBER_TYPE_MERGED; - } - - break; - } - case SPQR_MEMBERTYPE_UNASSIGNED: - { - assert(FALSE); - break; - } - } - return newCol->reducedMembers[leaf].type; -} - -static void propagateCycles(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION * newCol){ - assert(dec); - assert(newCol); - int leafArrayIndex = 0; - - while(leafArrayIndex != newCol->numLeafMembers){ - reduced_member_id leaf = newCol->leafMembers[leafArrayIndex]; - //next is invalid if the member is not in the reduced decomposition. - reduced_member_id next = newCol->reducedMembers[leaf].parent; - spqr_arc parentMarker = markerToParent(dec, newCol->reducedMembers[leaf].member); - if(reducedMemberIsValid(next) && !arcIsTree(dec,parentMarker)){ - assert(reducedMemberIsValid(next)); - assert(SPQRarcIsValid(parentMarker)); - ReducedMemberType type = checkLeaf(dec,newCol,leaf,parentMarker,next,markerOfParent(dec,newCol->reducedMembers[leaf].member)); - if(type == REDUCEDMEMBER_TYPE_CYCLE){ - ++newCol->reducedMembers[next].numPropagatedChildren; - if(newCol->reducedMembers[next].numPropagatedChildren == newCol->reducedMembers[next].numChildren){ - newCol->leafMembers[leafArrayIndex] = next; - }else{ - ++leafArrayIndex; - } - }else if(type == REDUCEDMEMBER_TYPE_NOT_NETWORK){ - return; - }else{ - assert(type == REDUCEDMEMBER_TYPE_MERGED); - ++leafArrayIndex; - int component = newCol->reducedMembers[leaf].componentIndex; - if(newCol->reducedComponents[component].numPathEndMembers >= 2){ - newCol->remainsNetwork = FALSE; - return; - } - assert(newCol->reducedComponents[component].numPathEndMembers < 2); - newCol->reducedComponents[component].pathEndMembers[newCol->reducedComponents[component].numPathEndMembers] = leaf; - ++newCol->reducedComponents[component].numPathEndMembers; + createPathArc(dec, newCol, toChild, parent, + ( parentReversed == firstArcReversed ) != firstArcInPathReverse); + + } else + { + //Type -> single_end + reducedMember->type = REDUCEDMEMBER_TYPE_MERGED; + } + + break; + } + case SPQR_MEMBERTYPE_UNASSIGNED: + { + assert(FALSE); + break; + } + } + return newCol->reducedMembers[leaf].type; +} + +static void propagateCycles( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol +) +{ + assert(dec); + assert(newCol); + int leafArrayIndex = 0; + + while( leafArrayIndex != newCol->numLeafMembers ) + { + reduced_member_id leaf = newCol->leafMembers[leafArrayIndex]; + //next is invalid if the member is not in the reduced decomposition. + reduced_member_id next = newCol->reducedMembers[leaf].parent; + spqr_arc parentMarker = markerToParent(dec, newCol->reducedMembers[leaf].member); + if( reducedMemberIsValid(next) && !arcIsTree(dec, parentMarker)) + { + assert(reducedMemberIsValid(next)); + assert(SPQRarcIsValid(parentMarker)); + ReducedMemberType type = checkLeaf(dec, newCol, leaf, parentMarker, next, + markerOfParent(dec, newCol->reducedMembers[leaf].member)); + if( type == REDUCEDMEMBER_TYPE_CYCLE ) + { + ++newCol->reducedMembers[next].numPropagatedChildren; + if( newCol->reducedMembers[next].numPropagatedChildren == newCol->reducedMembers[next].numChildren ) + { + newCol->leafMembers[leafArrayIndex] = next; + } else + { + ++leafArrayIndex; } - }else{ + } else if( type == REDUCEDMEMBER_TYPE_NOT_NETWORK ) + { + return; + } else + { + assert(type == REDUCEDMEMBER_TYPE_MERGED); ++leafArrayIndex; int component = newCol->reducedMembers[leaf].componentIndex; - if(newCol->reducedComponents[component].numPathEndMembers >= 2){ - newCol->remainsNetwork = FALSE; - return; + if( newCol->reducedComponents[component].numPathEndMembers >= 2 ) + { + newCol->remainsNetwork = FALSE; + return; } assert(newCol->reducedComponents[component].numPathEndMembers < 2); newCol->reducedComponents[component].pathEndMembers[newCol->reducedComponents[component].numPathEndMembers] = leaf; ++newCol->reducedComponents[component].numPathEndMembers; - } - - } - - for (int j = 0; j < newCol->numReducedComponents; ++j) { - //The reduced root might be a leaf as well: we propagate it last - reduced_member_id root = newCol->reducedComponents[j].root; - - while(TRUE){ - if(newCol->reducedMembers[root].numPropagatedChildren != newCol->reducedMembers[root].numChildren -1) { - break; - } - //TODO: bit ugly, have to do a linear search for the child - reduced_member_id child = INVALID_REDUCED_MEMBER; - spqr_arc markerToChild = SPQR_INVALID_ARC; - for (children_idx i = newCol->reducedMembers[root].firstChild; - i < newCol->reducedMembers[root].firstChild + newCol->reducedMembers[root].numChildren; ++i) { - reduced_member_id childReduced = newCol->childrenStorage[i]; - if (newCol->reducedMembers[childReduced].type != REDUCEDMEMBER_TYPE_CYCLE) { - child = childReduced; - markerToChild = markerOfParent(dec, newCol->reducedMembers[child].member); - break; - } - } - assert(SPQRmemberIsValid(newCol->reducedMembers[child].member)); - assert(SPQRarcIsValid(markerToChild)); - if (!arcIsTree(dec, markerToChild)) { - ReducedMemberType type = checkLeaf(dec, newCol, root, markerToChild, child, - markerToParent(dec, newCol->reducedMembers[child].member)); - if (type == REDUCEDMEMBER_TYPE_CYCLE) { - root = child; - continue; - } else if (type == REDUCEDMEMBER_TYPE_NOT_NETWORK) { - return; - } + } + } else + { + ++leafArrayIndex; + int component = newCol->reducedMembers[leaf].componentIndex; + if( newCol->reducedComponents[component].numPathEndMembers >= 2 ) + { + newCol->remainsNetwork = FALSE; + return; + } + assert(newCol->reducedComponents[component].numPathEndMembers < 2); + newCol->reducedComponents[component].pathEndMembers[newCol->reducedComponents[component].numPathEndMembers] = leaf; + ++newCol->reducedComponents[component].numPathEndMembers; + } + } + + for( int j = 0; j < newCol->numReducedComponents; ++j ) + { + //The reduced root might be a leaf as well: we propagate it last + reduced_member_id root = newCol->reducedComponents[j].root; + + while( TRUE ) + { + if( newCol->reducedMembers[root].numPropagatedChildren != newCol->reducedMembers[root].numChildren - 1 ) + { + break; + } + //TODO: bit ugly, have to do a linear search for the child + reduced_member_id child = INVALID_REDUCED_MEMBER; + spqr_arc markerToChild = SPQR_INVALID_ARC; + for( children_idx i = newCol->reducedMembers[root].firstChild; + i < newCol->reducedMembers[root].firstChild + newCol->reducedMembers[root].numChildren; ++i ) + { + reduced_member_id childReduced = newCol->childrenStorage[i]; + if( newCol->reducedMembers[childReduced].type != REDUCEDMEMBER_TYPE_CYCLE ) + { + child = childReduced; + markerToChild = markerOfParent(dec, newCol->reducedMembers[child].member); + break; } - //If the root has exactly one neighbour and is not contained, it is also considered a path end member - int component = newCol->reducedMembers[root].componentIndex; - SCIP_Bool rootPresent = FALSE; - for (int i = 0; i < newCol->reducedComponents[component].numPathEndMembers; ++i) { - rootPresent = rootPresent || (newCol->reducedComponents[component].pathEndMembers[i] == root); + } + assert(SPQRmemberIsValid(newCol->reducedMembers[child].member)); + assert(SPQRarcIsValid(markerToChild)); + if( !arcIsTree(dec, markerToChild)) + { + ReducedMemberType type = checkLeaf(dec, newCol, root, markerToChild, child, + markerToParent(dec, newCol->reducedMembers[child].member)); + if( type == REDUCEDMEMBER_TYPE_CYCLE ) + { + root = child; + continue; + } else if( type == REDUCEDMEMBER_TYPE_NOT_NETWORK ) + { + return; } - if(!rootPresent){ - if(newCol->reducedComponents[component].numPathEndMembers >= 2){ - newCol->remainsNetwork = FALSE; - return; - } - newCol->reducedComponents[component].pathEndMembers[newCol->reducedComponents[component].numPathEndMembers] = root; - ++newCol->reducedComponents[component].numPathEndMembers; + } + //If the root has exactly one neighbour and is not contained, it is also considered a path end member + int component = newCol->reducedMembers[root].componentIndex; + SCIP_Bool rootPresent = FALSE; + for( int i = 0; i < newCol->reducedComponents[component].numPathEndMembers; ++i ) + { + rootPresent = rootPresent || ( newCol->reducedComponents[component].pathEndMembers[i] == root ); + } + if( !rootPresent ) + { + if( newCol->reducedComponents[component].numPathEndMembers >= 2 ) + { + newCol->remainsNetwork = FALSE; + return; } - break; - - } + newCol->reducedComponents[component].pathEndMembers[newCol->reducedComponents[component].numPathEndMembers] = root; + ++newCol->reducedComponents[component].numPathEndMembers; + } + break; + } - newCol->reducedComponents[j].root = root; - newCol->reducedMembers[root].parent = INVALID_REDUCED_MEMBER; - } + newCol->reducedComponents[j].root = root; + newCol->reducedMembers[root].parent = INVALID_REDUCED_MEMBER; + } } -static void determineComponentTypes(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION * newCol, SPQRColReducedComponent * component){ - assert(dec); - assert(newCol); - assert(component); - if(component->numPathEndMembers == 1){ - assert(component->root == component->pathEndMembers[0]); - determineSingleComponentType(dec,newCol,component->root); - }else{ - assert(component->numPathEndMembers == 2); - determinePathTypes(dec,newCol,component); - } +static void determineComponentTypes( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + SPQRColReducedComponent* component +) +{ + assert(dec); + assert(newCol); + assert(component); + + if( component->numPathEndMembers == 1 ) + { + assert(component->root == component->pathEndMembers[0]); + determineSingleComponentType(dec, newCol, component->root); + } else + { + assert(component->numPathEndMembers == 2); + determinePathTypes(dec, newCol, component); + } } SCIP_RETCODE -SCIPNetworkColAdditionCheck(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, spqr_col column, const spqr_row * nonzeroRows, - const double * nonzeroValues, size_t numNonzeros) { - assert(dec); - assert(newCol); - assert(numNonzeros == 0 || (nonzeroRows && nonzeroValues)); - - newCol->remainsNetwork = TRUE; - cleanupPreviousIteration(dec, newCol); - //assert that previous iteration was cleaned up - - //Store call data - SCIP_CALL(newColUpdateColInformation(dec, newCol, column, nonzeroRows, nonzeroValues, numNonzeros)); - - //compute reduced decomposition - SCIP_CALL(constructReducedDecomposition(dec, newCol)); - //initialize path arcs in reduced decomposition - SCIP_CALL(createPathArcs(dec,newCol)); - SCIP_CALL(computeLeafMembers(dec,newCol)); - propagateCycles(dec,newCol); - //determine types - if(newCol->remainsNetwork){ - for (int i = 0; i < newCol->numReducedComponents; ++i) { - determineComponentTypes(dec,newCol,&newCol->reducedComponents[i]); - } - } - //clean up memberInformation - cleanUpMemberInformation(newCol); - - return SCIP_OKAY; +SCIPnetcoladdCheck( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* coladd, + int column, + const int* nonzrows, + const double* nonzvals, + size_t nnonzs +) +{ + assert(dec); + assert(coladd); + assert(nnonzs == 0 || ( nonzrows && nonzvals )); + + coladd->remainsNetwork = TRUE; + cleanupPreviousIteration(dec, coladd); + //assert that previous iteration was cleaned up + + //Store call data + SCIP_CALL(newColUpdateColInformation(dec, coladd, column, nonzrows, nonzvals, nnonzs)); + + //compute reduced decomposition + SCIP_CALL(constructReducedDecomposition(dec, coladd)); + //initialize path arcs in reduced decomposition + SCIP_CALL(createPathArcs(dec, coladd)); + SCIP_CALL(computeLeafMembers(dec, coladd)); + propagateCycles(dec, coladd); + //determine types + if( coladd->remainsNetwork ) + { + for( int i = 0; i < coladd->numReducedComponents; ++i ) + { + determineComponentTypes(dec, coladd, &coladd->reducedComponents[i]); + } + } + //clean up memberInformation + cleanUpMemberInformation(coladd); + + return SCIP_OKAY; } ///Contains the data which tells us where to store the new column after the graph has been modified ///In case member is a parallel or series node, the respective new column and rows are placed in parallel (or series) with it ///Otherwise, the rigid member has a free spot between firstNode and secondNode -typedef struct { - spqr_member member; - spqr_node head; - spqr_node tail; - spqr_arc representative; - SCIP_Bool reversed; +typedef struct +{ + spqr_member member; + spqr_node head; + spqr_node tail; + spqr_arc representative; + SCIP_Bool reversed; } NewColInformation; -static NewColInformation emptyNewColInformation(void){ - NewColInformation information; - information.member = SPQR_INVALID_MEMBER; - information.head = SPQR_INVALID_NODE; - information.tail = SPQR_INVALID_NODE; - information.reversed = FALSE; - information.representative = SPQR_INVALID_ARC; - return information; -} -static void setTerminalHead(NewColInformation * info, spqr_node node){ - assert(SPQRnodeIsValid(node)); - assert(SPQRnodeIsInvalid(info->head)); - assert(info); - info->head = node; -} -static void setTerminalTail(NewColInformation * info, spqr_node node){ - assert(SPQRnodeIsValid(node)); - assert(info); - assert(SPQRnodeIsInvalid(info->tail)); - info->tail = node; -} -static void setTerminalReversed(NewColInformation *info, SCIP_Bool reversed){ - assert(info); - info->reversed = reversed; -} -static void setTerminalMember(NewColInformation * info, spqr_member member){ - assert(info); - info->member = member; -} -static void setTerminalRepresentative(NewColInformation * info, spqr_arc representative){ - assert(info); - info->representative = representative; -} - -static SCIP_RETCODE splitParallel(SCIP_NETWORKDECOMP *dec, spqr_member parallel, - spqr_arc arc1, spqr_arc arc2, - spqr_member * childParallel){ - assert(dec); - assert(SPQRarcIsValid(arc1)); - assert(SPQRarcIsValid(arc2)); - assert(SPQRmemberIsValid(parallel)); - - SCIP_Bool childContainsTree = arcIsTree(dec,arc1) || arcIsTree(dec,arc2); - spqr_arc toParent = markerToParent(dec, parallel); - SCIP_Bool parentMoved = toParent == arc1 || toParent == arc2; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, childParallel)); - - moveArcToNewMember(dec,arc1,parallel,*childParallel); - moveArcToNewMember(dec,arc2,parallel,*childParallel); - - if(parentMoved){ - SCIP_CALL(createMarkerPair(dec,*childParallel,parallel,!childContainsTree,FALSE,FALSE)); - }else{ - SCIP_CALL(createMarkerPair(dec,parallel,*childParallel,childContainsTree,FALSE,FALSE)); - } - return SCIP_OKAY; -} - -static SCIP_RETCODE splitSeries(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, - SPQRColReducedMember * reducedMember, - spqr_member member, spqr_member * loopMember, NewColInformation * newColInfo){ - assert(dec); - assert(reducedMember); - assert(SPQRmemberIsValid(member)); - assert(memberIsRepresentative(dec,member)); - - assert(reducedMember->numPathArcs != 0); - SCIP_Bool createPathSeries = reducedMember->numPathArcs > 1; - SCIP_Bool convertOriginal = reducedMember->numPathArcs == getNumMemberArcs(dec,member) -1; - - //TODO: for now very elaborate to first get logic correct. Can probably merge some branches later... - if(!createPathSeries && !convertOriginal){ - spqr_member parallel; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, ¶llel)); - path_arc_id pathArcId = reducedMember->firstPathArc; - assert(pathArcIsValid(pathArcId)); - spqr_arc marked = newCol->pathArcs[pathArcId].arc; - assert(marked != markerToParent(dec,member));//TODO: handle this case later - moveArcToNewMember(dec,marked,member,parallel); - SCIP_Bool reversed = arcIsReversedNonRigid(dec,marked) ; - SCIP_CALL(createMarkerPair(dec,member,parallel,TRUE, reversed,reversed)); - *loopMember = parallel; - - if(reversed == reducedMember->pathBackwards){ - setTerminalReversed(newColInfo,!reversed); - }else{ - setTerminalReversed(newColInfo,reversed); - } - - setTerminalMember(newColInfo,*loopMember); - return SCIP_OKAY; - } - if(!createPathSeries && convertOriginal){ - //only one path arc; we are in a loop; no need to change anything - assert(getNumMemberArcs(dec,member) == 2); - assert(reducedMember->numPathArcs == 1); - *loopMember = member; - changeLoopToParallel(dec,member); - - path_arc_id pathArcId = reducedMember->firstPathArc; - spqr_arc marked = newCol->pathArcs[pathArcId].arc; - //The 'reversed' field has different meaning for parallels, so we need to change orientation when converting to a parallel - arcFlipReversed(dec,marked); - - setTerminalReversed(newColInfo,reducedMember->pathBackwards); - setTerminalMember(newColInfo,*loopMember); - return SCIP_OKAY; - } - if(createPathSeries && !convertOriginal){ - spqr_member pathMember; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember)); - - path_arc_id pathArcId = reducedMember->firstPathArc; - SCIP_Bool parentMoved = FALSE; - while(pathArcIsValid(pathArcId)){ - spqr_arc pathArc = newCol->pathArcs[pathArcId].arc; - pathArcId = newCol->pathArcs[pathArcId].nextMember; - if(pathArc == markerToParent(dec,member)){ - parentMoved = TRUE; - } - moveArcToNewMember(dec,pathArc,member,pathMember); - } - - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, loopMember)); - - if(!parentMoved){ - SCIP_CALL(createMarkerPair(dec,member,*loopMember,TRUE,FALSE,FALSE)); - SCIP_CALL(createMarkerPair(dec,*loopMember,pathMember,TRUE,FALSE,TRUE)); - }else{ - SCIP_CALL(createMarkerPair(dec,pathMember,*loopMember,FALSE,FALSE,TRUE)); - SCIP_CALL(createMarkerPair(dec,*loopMember,member,FALSE,FALSE,FALSE)); - } - - setTerminalReversed(newColInfo,!reducedMember->pathBackwards); - setTerminalMember(newColInfo,*loopMember); - return SCIP_OKAY; - } - assert(createPathSeries && convertOriginal); - //There's one exception in this case - //if the single unmarked (column) marker is a parent or child marker to a parallel member, we add the edge there - { - spqr_member adjacentMember = SPQR_INVALID_MEMBER; - spqr_arc adjacentMarker = SPQR_INVALID_ARC; - spqr_arc memberMarker = SPQR_INVALID_ARC; - spqr_arc firstArc = getFirstMemberArc(dec, reducedMember->member); - spqr_arc arc = firstArc; - do { - if (!newCol->arcInPath[arc]) { - if (arc == markerToParent(dec, reducedMember->member)) { - adjacentMember = findMemberParent(dec, reducedMember->member); - adjacentMarker = markerOfParent(dec, reducedMember->member); - memberMarker = arc; - } else if (arcIsMarker(dec, arc)) { - adjacentMember = findArcChildMember(dec, arc); - adjacentMarker = markerToParent(dec, adjacentMember); - memberMarker = arc; - } - - break; //There is only a singular such arc - } - arc = getNextMemberArc(dec, arc); - } while (arc != firstArc); - - if (SPQRmemberIsValid(adjacentMember)) { - SPQRMemberType adjacentType = getMemberType(dec, adjacentMember); - if (adjacentType == SPQR_MEMBERTYPE_PARALLEL) { - //Figure out if the markers are the same or opposite orientations - //If they are the same, we can proceed as normal, otherwise, we need to flip the placed edge - SCIP_Bool markersHaveSameOrientation = - arcIsReversedNonRigid(dec, adjacentMarker) == arcIsReversedNonRigid(dec, memberMarker); - setTerminalReversed(newColInfo, reducedMember->pathBackwards == markersHaveSameOrientation); - setTerminalMember(newColInfo, adjacentMember); - return SCIP_OKAY; - } - } - } - - spqr_member pathMember; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember)); - - path_arc_id pathArcId = reducedMember->firstPathArc; - SCIP_Bool parentMoved = FALSE; - while(pathArcIsValid(pathArcId)){ - spqr_arc pathArc = newCol->pathArcs[pathArcId].arc; - pathArcId = newCol->pathArcs[pathArcId].nextMember; - if(pathArc == markerToParent(dec,member)){ +static NewColInformation emptyNewColInformation(void) +{ + NewColInformation information; + information.member = SPQR_INVALID_MEMBER; + information.head = SPQR_INVALID_NODE; + information.tail = SPQR_INVALID_NODE; + information.reversed = FALSE; + information.representative = SPQR_INVALID_ARC; + return information; +} + +static void setTerminalHead( + NewColInformation* info, + spqr_node node +) +{ + assert(SPQRnodeIsValid(node)); + assert(SPQRnodeIsInvalid(info->head)); + assert(info); + info->head = node; +} + +static void setTerminalTail( + NewColInformation* info, + spqr_node node +) +{ + assert(SPQRnodeIsValid(node)); + assert(info); + assert(SPQRnodeIsInvalid(info->tail)); + info->tail = node; +} + +static void setTerminalReversed( + NewColInformation* info, + SCIP_Bool reversed +) +{ + assert(info); + info->reversed = reversed; +} + +static void setTerminalMember( + NewColInformation* info, + spqr_member member +) +{ + assert(info); + info->member = member; +} + +static void setTerminalRepresentative( + NewColInformation* info, + spqr_arc representative +) +{ + assert(info); + info->representative = representative; +} + +static SCIP_RETCODE splitParallel( + SCIP_NETMATDEC* dec, + spqr_member parallel, + spqr_arc arc1, + spqr_arc arc2, + spqr_member* childParallel +) +{ + assert(dec); + assert(SPQRarcIsValid(arc1)); + assert(SPQRarcIsValid(arc2)); + assert(SPQRmemberIsValid(parallel)); + + SCIP_Bool childContainsTree = arcIsTree(dec, arc1) || arcIsTree(dec, arc2); + spqr_arc toParent = markerToParent(dec, parallel); + SCIP_Bool parentMoved = toParent == arc1 || toParent == arc2; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, childParallel)); + + moveArcToNewMember(dec, arc1, parallel, *childParallel); + moveArcToNewMember(dec, arc2, parallel, *childParallel); + + if( parentMoved ) + { + SCIP_CALL(createMarkerPair(dec, *childParallel, parallel, !childContainsTree, FALSE, FALSE)); + } else + { + SCIP_CALL(createMarkerPair(dec, parallel, *childParallel, childContainsTree, FALSE, FALSE)); + } + return SCIP_OKAY; +} + +static SCIP_RETCODE splitSeries( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + SPQRColReducedMember* reducedMember, + spqr_member member, + spqr_member* loopMember, + NewColInformation* newColInfo +) +{ + assert(dec); + assert(reducedMember); + assert(SPQRmemberIsValid(member)); + assert(memberIsRepresentative(dec, member)); + + assert(reducedMember->numPathArcs != 0); + SCIP_Bool createPathSeries = reducedMember->numPathArcs > 1; + SCIP_Bool convertOriginal = reducedMember->numPathArcs == getNumMemberArcs(dec, member) - 1; + + //TODO: for now very elaborate to first get logic correct. Can probably merge some branches later... + if( !createPathSeries && !convertOriginal ) + { + spqr_member parallel; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, ¶llel)); + path_arc_id pathArcId = reducedMember->firstPathArc; + assert(pathArcIsValid(pathArcId)); + spqr_arc marked = newCol->pathArcs[pathArcId].arc; + assert(marked != markerToParent(dec, member));//TODO: handle this case later + moveArcToNewMember(dec, marked, member, parallel); + SCIP_Bool reversed = arcIsReversedNonRigid(dec, marked); + SCIP_CALL(createMarkerPair(dec, member, parallel, TRUE, reversed, reversed)); + *loopMember = parallel; + + if( reversed == reducedMember->pathBackwards ) + { + setTerminalReversed(newColInfo, !reversed); + } else + { + setTerminalReversed(newColInfo, reversed); + } + + setTerminalMember(newColInfo, *loopMember); + return SCIP_OKAY; + } + if( !createPathSeries && convertOriginal ) + { + //only one path arc; we are in a loop; no need to change anything + assert(getNumMemberArcs(dec, member) == 2); + assert(reducedMember->numPathArcs == 1); + *loopMember = member; + changeLoopToParallel(dec, member); + + path_arc_id pathArcId = reducedMember->firstPathArc; + spqr_arc marked = newCol->pathArcs[pathArcId].arc; + //The 'reversed' field has different meaning for parallels, so we need to change orientation when converting to a parallel + arcFlipReversed(dec, marked); + + setTerminalReversed(newColInfo, reducedMember->pathBackwards); + setTerminalMember(newColInfo, *loopMember); + return SCIP_OKAY; + } + if( createPathSeries && !convertOriginal ) + { + spqr_member pathMember; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember)); + + path_arc_id pathArcId = reducedMember->firstPathArc; + SCIP_Bool parentMoved = FALSE; + while( pathArcIsValid(pathArcId)) + { + spqr_arc pathArc = newCol->pathArcs[pathArcId].arc; + pathArcId = newCol->pathArcs[pathArcId].nextMember; + if( pathArc == markerToParent(dec, member)) + { parentMoved = TRUE; - } - moveArcToNewMember(dec,pathArc,member,pathMember); - } - if(parentMoved){ - SCIP_CALL(createMarkerPair(dec,pathMember,member,FALSE,FALSE,FALSE)); - }else{ - SCIP_CALL(createMarkerPair(dec,member,pathMember,TRUE,FALSE,FALSE)); - } - - changeLoopToParallel(dec,member); - - *loopMember = member; - setTerminalReversed(newColInfo,reducedMember->pathBackwards); - setTerminalMember(newColInfo,*loopMember); - return SCIP_OKAY; -} - - -static SCIP_RETCODE splitSeriesMerging(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, - SPQRColReducedMember * reducedMember, - spqr_member member, - spqr_arc * pathRepresentative, - spqr_arc * nonPathRepresentative, - spqr_arc exceptionArc1, - spqr_arc exceptionArc2){ - assert(dec); - assert(reducedMember); - assert(SPQRmemberIsValid(member)); - assert(memberIsRepresentative(dec,member)); - - int numExceptionArcs = (exceptionArc1 == SPQR_INVALID_ARC ? 0 : 1) + (exceptionArc2 == SPQR_INVALID_ARC ? 0 : 1); - int numNonPathArcs = getNumMemberArcs(dec,member) - reducedMember->numPathArcs - numExceptionArcs; - SCIP_Bool createPathSeries = reducedMember->numPathArcs > 1; - //If this holds, there are 2 or more non-parent marker non-path arcs - SCIP_Bool createNonPathSeries = numNonPathArcs > 1; - assert(exceptionArc1 == SPQR_INVALID_ARC || !newCol->arcInPath[exceptionArc1]); - assert(exceptionArc2 == SPQR_INVALID_ARC || !newCol->arcInPath[exceptionArc2]); - - if(createPathSeries){ - spqr_member pathMember; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember)); - - path_arc_id pathArcId = reducedMember->firstPathArc; - SCIP_Bool parentMoved = FALSE; - while(pathArcIsValid(pathArcId)){ - spqr_arc pathArc = newCol->pathArcs[pathArcId].arc; - pathArcId = newCol->pathArcs[pathArcId].nextMember; - assert(pathArc != exceptionArc1 && pathArc != exceptionArc2); - parentMoved = parentMoved || markerToParent(dec,member) == pathArc; - moveArcToNewMember(dec,pathArc,member,pathMember); - } - assert(getNumMemberArcs(dec,pathMember) >= 2); - - spqr_arc ignored; - SCIP_Bool inOldReversed = TRUE; - SCIP_Bool inNewReversed = FALSE; - if(parentMoved){ - SCIP_CALL(createMarkerPairWithReferences(dec,pathMember,member,FALSE,inNewReversed,inOldReversed, - &ignored,pathRepresentative)); - }else{ - SCIP_CALL(createMarkerPairWithReferences(dec,member,pathMember,TRUE,inOldReversed,inNewReversed, - pathRepresentative,&ignored)); - } - }else{ - if(pathArcIsValid(reducedMember->firstPathArc)){ - *pathRepresentative = newCol->pathArcs[reducedMember->firstPathArc].arc; - }else{ - *pathRepresentative = SPQR_INVALID_ARC; - } - } - - if(createNonPathSeries){ - spqr_member nonPathMember; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &nonPathMember)); - - spqr_arc arc = getFirstMemberArc(dec, member); - SCIP_Bool parentMoved = FALSE; - SCIP_Bool canStop = FALSE; //hack when the first arc is moved in the below loop to prevent that we immediately terminate - do{ - spqr_arc nextArc = getNextMemberArc(dec, arc); - if(arc != *pathRepresentative && arc != exceptionArc1 && arc != exceptionArc2){ - parentMoved = parentMoved || markerToParent(dec,member) == arc; - moveArcToNewMember(dec,arc,member,nonPathMember); - }else{ - canStop = TRUE; + } + moveArcToNewMember(dec, pathArc, member, pathMember); + } + + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, loopMember)); + + if( !parentMoved ) + { + SCIP_CALL(createMarkerPair(dec, member, *loopMember, TRUE, FALSE, FALSE)); + SCIP_CALL(createMarkerPair(dec, *loopMember, pathMember, TRUE, FALSE, TRUE)); + } else + { + SCIP_CALL(createMarkerPair(dec, pathMember, *loopMember, FALSE, FALSE, TRUE)); + SCIP_CALL(createMarkerPair(dec, *loopMember, member, FALSE, FALSE, FALSE)); + } + + setTerminalReversed(newColInfo, !reducedMember->pathBackwards); + setTerminalMember(newColInfo, *loopMember); + return SCIP_OKAY; + } + assert(createPathSeries && convertOriginal); + //There's one exception in this case + //if the single unmarked (column) marker is a parent or child marker to a parallel member, we add the edge there + { + spqr_member adjacentMember = SPQR_INVALID_MEMBER; + spqr_arc adjacentMarker = SPQR_INVALID_ARC; + spqr_arc memberMarker = SPQR_INVALID_ARC; + spqr_arc firstArc = getFirstMemberArc(dec, reducedMember->member); + spqr_arc arc = firstArc; + do + { + if( !newCol->arcInPath[arc] ) + { + if( arc == markerToParent(dec, reducedMember->member)) + { + adjacentMember = findMemberParent(dec, reducedMember->member); + adjacentMarker = markerOfParent(dec, reducedMember->member); + memberMarker = arc; + } else if( arcIsMarker(dec, arc)) + { + adjacentMember = findArcChildMember(dec, arc); + adjacentMarker = markerToParent(dec, adjacentMember); + memberMarker = arc; } - arc = nextArc; - if(canStop && arc == getFirstMemberArc(dec,member)){ - break; + + break;//There is only a singular such arc + } + arc = getNextMemberArc(dec, arc); + } while( arc != firstArc ); + + if( SPQRmemberIsValid(adjacentMember)) + { + SPQRMemberType adjacentType = getMemberType(dec, adjacentMember); + if( adjacentType == SPQR_MEMBERTYPE_PARALLEL ) + { + //Figure out if the markers are the same or opposite orientations + //If they are the same, we can proceed as normal, otherwise, we need to flip the placed edge + SCIP_Bool markersHaveSameOrientation = + arcIsReversedNonRigid(dec, adjacentMarker) == arcIsReversedNonRigid(dec, memberMarker); + setTerminalReversed(newColInfo, reducedMember->pathBackwards == markersHaveSameOrientation); + setTerminalMember(newColInfo, adjacentMember); + return SCIP_OKAY; + } + } + } + + spqr_member pathMember; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember)); + + path_arc_id pathArcId = reducedMember->firstPathArc; + SCIP_Bool parentMoved = FALSE; + while( pathArcIsValid(pathArcId)) + { + spqr_arc pathArc = newCol->pathArcs[pathArcId].arc; + pathArcId = newCol->pathArcs[pathArcId].nextMember; + if( pathArc == markerToParent(dec, member)) + { + parentMoved = TRUE; + } + moveArcToNewMember(dec, pathArc, member, pathMember); + } + if( parentMoved ) + { + SCIP_CALL(createMarkerPair(dec, pathMember, member, FALSE, FALSE, FALSE)); + } else + { + SCIP_CALL(createMarkerPair(dec, member, pathMember, TRUE, FALSE, FALSE)); + } + + changeLoopToParallel(dec, member); + + *loopMember = member; + setTerminalReversed(newColInfo, reducedMember->pathBackwards); + setTerminalMember(newColInfo, *loopMember); + return SCIP_OKAY; +} + + +static SCIP_RETCODE splitSeriesMerging( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + SPQRColReducedMember* reducedMember, + spqr_member member, + spqr_arc* pathRepresentative, + spqr_arc* nonPathRepresentative, + spqr_arc exceptionArc1, + spqr_arc exceptionArc2 +) +{ + assert(dec); + assert(reducedMember); + assert(SPQRmemberIsValid(member)); + assert(memberIsRepresentative(dec, member)); + + int numExceptionArcs = ( exceptionArc1 == SPQR_INVALID_ARC ? 0 : 1 ) + ( exceptionArc2 == SPQR_INVALID_ARC ? 0 : 1 ); + int numNonPathArcs = getNumMemberArcs(dec, member) - reducedMember->numPathArcs - numExceptionArcs; + SCIP_Bool createPathSeries = reducedMember->numPathArcs > 1; + //If this holds, there are 2 or more non-parent marker non-path arcs + SCIP_Bool createNonPathSeries = numNonPathArcs > 1; + assert(exceptionArc1 == SPQR_INVALID_ARC || !newCol->arcInPath[exceptionArc1]); + assert(exceptionArc2 == SPQR_INVALID_ARC || !newCol->arcInPath[exceptionArc2]); + + if( createPathSeries ) + { + spqr_member pathMember; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember)); + + path_arc_id pathArcId = reducedMember->firstPathArc; + SCIP_Bool parentMoved = FALSE; + while( pathArcIsValid(pathArcId)) + { + spqr_arc pathArc = newCol->pathArcs[pathArcId].arc; + pathArcId = newCol->pathArcs[pathArcId].nextMember; + assert(pathArc != exceptionArc1 && pathArc != exceptionArc2); + parentMoved = parentMoved || markerToParent(dec, member) == pathArc; + moveArcToNewMember(dec, pathArc, member, pathMember); + } + assert(getNumMemberArcs(dec, pathMember) >= 2); + + spqr_arc ignored; + SCIP_Bool inOldReversed = TRUE; + SCIP_Bool inNewReversed = FALSE; + if( parentMoved ) + { + SCIP_CALL(createMarkerPairWithReferences(dec, pathMember, member, FALSE, inNewReversed, inOldReversed, + &ignored, pathRepresentative)); + } else + { + SCIP_CALL(createMarkerPairWithReferences(dec, member, pathMember, TRUE, inOldReversed, inNewReversed, + pathRepresentative, &ignored)); + } + } else + { + if( pathArcIsValid(reducedMember->firstPathArc)) + { + *pathRepresentative = newCol->pathArcs[reducedMember->firstPathArc].arc; + } else + { + *pathRepresentative = SPQR_INVALID_ARC; + } + } + + if( createNonPathSeries ) + { + spqr_member nonPathMember; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &nonPathMember)); + + spqr_arc arc = getFirstMemberArc(dec, member); + SCIP_Bool parentMoved = FALSE; + SCIP_Bool canStop = FALSE;//hack when the first arc is moved in the below loop to prevent that we immediately terminate + do + { + spqr_arc nextArc = getNextMemberArc(dec, arc); + if( arc != *pathRepresentative && arc != exceptionArc1 && arc != exceptionArc2 ) + { + parentMoved = parentMoved || markerToParent(dec, member) == arc; + moveArcToNewMember(dec, arc, member, nonPathMember); + } else + { + canStop = TRUE; + } + arc = nextArc; + if( canStop && arc == getFirstMemberArc(dec, member)) + { + break; + } + } while( TRUE ); + assert(getNumMemberArcs(dec, nonPathMember) >= 2); + SCIP_Bool representativeIsTree = !arcIsTree(dec, exceptionArc1); + if( SPQRarcIsValid(exceptionArc2)) + { + representativeIsTree = representativeIsTree || !arcIsTree(dec, exceptionArc2); + } + spqr_arc ignored; + SCIP_Bool inOldReversed = TRUE; + SCIP_Bool inNewReversed = FALSE; + if( parentMoved ) + { + SCIP_CALL(createMarkerPairWithReferences(dec, nonPathMember, member, !representativeIsTree, + inNewReversed, inOldReversed, &ignored, nonPathRepresentative)); + } else + { + SCIP_CALL(createMarkerPairWithReferences(dec, member, nonPathMember, representativeIsTree, + inOldReversed, inNewReversed, nonPathRepresentative, &ignored)); + } + } else + { + *nonPathRepresentative = SPQR_INVALID_ARC; + if( numNonPathArcs != 0 ) + { + spqr_arc firstArc = getFirstMemberArc(dec, member); + spqr_arc arc = firstArc; + do + { + if( arc != *pathRepresentative && arc != exceptionArc1 && arc != exceptionArc2 ) + { + *nonPathRepresentative = arc; + break; } - }while(TRUE); - assert(getNumMemberArcs(dec,nonPathMember) >= 2); - SCIP_Bool representativeIsTree = !arcIsTree(dec,exceptionArc1); - if(SPQRarcIsValid(exceptionArc2)){ - representativeIsTree = representativeIsTree || !arcIsTree(dec,exceptionArc2); - } - spqr_arc ignored; - SCIP_Bool inOldReversed = TRUE; - SCIP_Bool inNewReversed = FALSE; - if(parentMoved){ - SCIP_CALL(createMarkerPairWithReferences(dec,nonPathMember,member,!representativeIsTree, - inNewReversed,inOldReversed,&ignored,nonPathRepresentative)); - }else{ - SCIP_CALL(createMarkerPairWithReferences(dec,member,nonPathMember,representativeIsTree, - inOldReversed,inNewReversed,nonPathRepresentative,&ignored)); - } - }else{ - *nonPathRepresentative = SPQR_INVALID_ARC; - if(numNonPathArcs != 0) { - spqr_arc firstArc = getFirstMemberArc(dec, member); - spqr_arc arc = firstArc; - do { - if (arc != *pathRepresentative && arc != exceptionArc1 && arc != exceptionArc2) { - *nonPathRepresentative = arc; - break; - } - arc = getNextMemberArc(dec, arc); - } while (arc != firstArc); - assert(*nonPathRepresentative != SPQR_INVALID_ARC); - } - } - - return SCIP_OKAY; -} - -static SCIP_RETCODE transformFirstPathMember(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, - reduced_member_id reducedMember, NewColInformation * newColInfo, - spqr_arc * representativeArc, - spqr_member * mergedMember ){ - spqr_member member = newCol->reducedMembers[reducedMember].member; - SPQRMemberType type = getMemberType(dec,member); - if(type == SPQR_MEMBERTYPE_RIGID){ - //The nodes are already created, we only need to assign the correct start/end node - switch(newCol->reducedMembers[reducedMember].pathType){ - case INTO_HEAD: - case INTO_TAIL: - setTerminalTail(newColInfo,newCol->reducedMembers[reducedMember].rigidPathStart); - break; - case OUT_HEAD: - case OUT_TAIL: - setTerminalHead(newColInfo,newCol->reducedMembers[reducedMember].rigidPathEnd); - break; - } - *representativeArc = findArcSign(dec,newCol->reducedMembers[reducedMember].pathTargetArc).representative; - *mergedMember = member; - - return SCIP_OKAY; - } - assert(type == SPQR_MEMBERTYPE_SERIES); - //Split off sets of multiple path non-path edges so that the series has exactly 3 edges - - spqr_arc target = newCol->reducedMembers[reducedMember].pathTargetArc; - SPQRColReducedMember * redMem = &newCol->reducedMembers[reducedMember]; - spqr_arc pathRepresentative = SPQR_INVALID_ARC; - spqr_arc nonPathRepresentative = SPQR_INVALID_ARC; - SCIP_CALL(splitSeriesMerging(dec,newCol,redMem,member,&pathRepresentative,&nonPathRepresentative,target,SPQR_INVALID_ARC)); - - assert(target != pathRepresentative - && target != nonPathRepresentative && pathRepresentative != nonPathRepresentative); - assert(SPQRarcIsValid(pathRepresentative) && SPQRarcIsValid(nonPathRepresentative) && SPQRarcIsValid(target)); - assert(getNumMemberArcs(dec,member) == 3); - - //Create nodes - spqr_node a = SPQR_INVALID_NODE; - spqr_node b = SPQR_INVALID_NODE; - spqr_node c = SPQR_INVALID_NODE; - SCIP_CALL(createNode(dec, &a)); - SCIP_CALL(createNode(dec, &b)); - SCIP_CALL(createNode(dec, &c)); - - // a -- b - // \ / - // c - - //Set arc nodes - //Set target from b to c, - SCIP_Bool targetReversed = arcIsReversedNonRigid(dec,target); - setArcHeadAndTail(dec, target, c, b); - - MemberPathType pathType = newCol->reducedMembers[reducedMember].pathType; - assert(pathType == INTO_HEAD || pathType == OUT_HEAD); - if(arcIsReversedNonRigid(dec,pathRepresentative) == targetReversed){ - setArcHeadAndTail(dec,pathRepresentative,a,c); - }else{ - setArcHeadAndTail(dec,pathRepresentative,c,a); - } - if(arcIsReversedNonRigid(dec,nonPathRepresentative) == targetReversed){ - setArcHeadAndTail(dec,nonPathRepresentative,b,a); - }else{ - setArcHeadAndTail(dec,nonPathRepresentative,a,b); - } - //setup signed union find; all arcs are placed are not reversed. We pick an arbitrary arc as 'root' arc for this skeleton - arcSetReversed(dec,target,FALSE); - arcSetReversed(dec,pathRepresentative,FALSE); - arcSetReversed(dec,nonPathRepresentative,FALSE); - arcSetRepresentative(dec,target,SPQR_INVALID_ARC); - arcSetRepresentative(dec,pathRepresentative,target); - arcSetRepresentative(dec,nonPathRepresentative,target); - *representativeArc = target; - - if(pathType == INTO_HEAD){ - setTerminalTail(newColInfo,a); - }else{ - setTerminalHead(newColInfo,a); - } - - *mergedMember = member; - - return SCIP_OKAY; -} -static SCIP_RETCODE transformAndMergeParallel(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, - reduced_member_id current, reduced_member_id next, - spqr_member nextMember, SCIP_Bool nextIsParent, - spqr_arc * representativeArc, - spqr_member * mergedMember){ - //Split off edges not in current subtree to form one parallel (or one edge) - spqr_member childParallel = INVALID_REDUCED_MEMBER; - spqr_arc source = newCol->reducedMembers[next].pathSourceArc; - spqr_arc target = newCol->reducedMembers[next].pathTargetArc; - if(getNumMemberArcs(dec,nextMember) > 3){ - SCIP_CALL(splitParallel(dec, nextMember, source, target, &childParallel)); - nextMember = childParallel; - newCol->reducedMembers[next].member = childParallel; - } - assert(getNumMemberArcs(dec,nextMember) == 3); - - spqr_node sourceHead = SPQR_INVALID_NODE; - spqr_node sourceTail = SPQR_INVALID_NODE; - SCIP_CALL(createNode(dec,&sourceHead)); - SCIP_CALL(createNode(dec,&sourceTail)); - - //set edge nodes and arc union-find data - { - spqr_arc firstArc = getFirstMemberArc(dec,nextMember); - spqr_arc arc = firstArc; - - SCIP_Bool sourceReversed = arcIsReversedNonRigid(dec,source); - do{ - if(arcIsReversedNonRigid(dec,arc) == sourceReversed){ - setArcHeadAndTail(dec,arc,sourceHead,sourceTail); - }else{ - setArcHeadAndTail(dec,arc,sourceTail,sourceHead); + arc = getNextMemberArc(dec, arc); + } while( arc != firstArc ); + assert(*nonPathRepresentative != SPQR_INVALID_ARC); + } + } + + return SCIP_OKAY; +} + +static SCIP_RETCODE transformFirstPathMember( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + reduced_member_id reducedMember, + NewColInformation* newColInfo, + spqr_arc* representativeArc, + spqr_member* mergedMember +) +{ + spqr_member member = newCol->reducedMembers[reducedMember].member; + SPQRMemberType type = getMemberType(dec, member); + if( type == SPQR_MEMBERTYPE_RIGID ) + { + //The nodes are already created, we only need to assign the correct start/end node + switch( newCol->reducedMembers[reducedMember].pathType ) + { + case INTO_HEAD: + case INTO_TAIL: + setTerminalTail(newColInfo, newCol->reducedMembers[reducedMember].rigidPathStart); + break; + case OUT_HEAD: + case OUT_TAIL: + setTerminalHead(newColInfo, newCol->reducedMembers[reducedMember].rigidPathEnd); + break; + } + *representativeArc = findArcSign(dec, newCol->reducedMembers[reducedMember].pathTargetArc).representative; + *mergedMember = member; + + return SCIP_OKAY; + } + assert(type == SPQR_MEMBERTYPE_SERIES); + //Split off sets of multiple path non-path edges so that the series has exactly 3 edges + + spqr_arc target = newCol->reducedMembers[reducedMember].pathTargetArc; + SPQRColReducedMember* redMem = &newCol->reducedMembers[reducedMember]; + spqr_arc pathRepresentative = SPQR_INVALID_ARC; + spqr_arc nonPathRepresentative = SPQR_INVALID_ARC; + SCIP_CALL(splitSeriesMerging(dec, newCol, redMem, member, &pathRepresentative, &nonPathRepresentative, target, + SPQR_INVALID_ARC)); + + assert( + target != pathRepresentative && target != nonPathRepresentative && pathRepresentative != nonPathRepresentative); + assert(SPQRarcIsValid(pathRepresentative) && SPQRarcIsValid(nonPathRepresentative) && SPQRarcIsValid(target)); + assert(getNumMemberArcs(dec, member) == 3); + + //Create nodes + spqr_node a = SPQR_INVALID_NODE; + spqr_node b = SPQR_INVALID_NODE; + spqr_node c = SPQR_INVALID_NODE; + SCIP_CALL(createNode(dec, &a)); + SCIP_CALL(createNode(dec, &b)); + SCIP_CALL(createNode(dec, &c)); + + // a -- b + // \ / + // c + + //Set arc nodes + //Set target from b to c, + SCIP_Bool targetReversed = arcIsReversedNonRigid(dec, target); + setArcHeadAndTail(dec, target, c, b); + + MemberPathType pathType = newCol->reducedMembers[reducedMember].pathType; + assert(pathType == INTO_HEAD || pathType == OUT_HEAD); + if( arcIsReversedNonRigid(dec, pathRepresentative) == targetReversed ) + { + setArcHeadAndTail(dec, pathRepresentative, a, c); + } else + { + setArcHeadAndTail(dec, pathRepresentative, c, a); + } + if( arcIsReversedNonRigid(dec, nonPathRepresentative) == targetReversed ) + { + setArcHeadAndTail(dec, nonPathRepresentative, b, a); + } else + { + setArcHeadAndTail(dec, nonPathRepresentative, a, b); + } + //setup signed union find; all arcs are placed are not reversed. We pick an arbitrary arc as 'root' arc for this skeleton + arcSetReversed(dec, target, FALSE); + arcSetReversed(dec, pathRepresentative, FALSE); + arcSetReversed(dec, nonPathRepresentative, FALSE); + arcSetRepresentative(dec, target, SPQR_INVALID_ARC); + arcSetRepresentative(dec, pathRepresentative, target); + arcSetRepresentative(dec, nonPathRepresentative, target); + *representativeArc = target; + + if( pathType == INTO_HEAD ) + { + setTerminalTail(newColInfo, a); + } else + { + setTerminalHead(newColInfo, a); + } + + *mergedMember = member; + + return SCIP_OKAY; +} + +static SCIP_RETCODE transformAndMergeParallel( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + reduced_member_id current, + reduced_member_id next, + spqr_member nextMember, + SCIP_Bool nextIsParent, + spqr_arc* representativeArc, + spqr_member* mergedMember +) +{ + //Split off edges not in current subtree to form one parallel (or one edge) + spqr_member childParallel = INVALID_REDUCED_MEMBER; + spqr_arc source = newCol->reducedMembers[next].pathSourceArc; + spqr_arc target = newCol->reducedMembers[next].pathTargetArc; + if( getNumMemberArcs(dec, nextMember) > 3 ) + { + SCIP_CALL(splitParallel(dec, nextMember, source, target, &childParallel)); + nextMember = childParallel; + newCol->reducedMembers[next].member = childParallel; + } + assert(getNumMemberArcs(dec, nextMember) == 3); + + spqr_node sourceHead = SPQR_INVALID_NODE; + spqr_node sourceTail = SPQR_INVALID_NODE; + SCIP_CALL(createNode(dec, &sourceHead)); + SCIP_CALL(createNode(dec, &sourceTail)); + + //set edge nodes and arc union-find data + { + spqr_arc firstArc = getFirstMemberArc(dec, nextMember); + spqr_arc arc = firstArc; + + SCIP_Bool sourceReversed = arcIsReversedNonRigid(dec, source); + do + { + if( arcIsReversedNonRigid(dec, arc) == sourceReversed ) + { + setArcHeadAndTail(dec, arc, sourceHead, sourceTail); + } else + { + setArcHeadAndTail(dec, arc, sourceTail, sourceHead); + } + arcSetRepresentative(dec, arc, source); + arcSetReversed(dec, arc, FALSE); + + arc = getNextMemberArc(dec, arc); + } while( arc != firstArc ); + + arcSetRepresentative(dec, source, SPQR_INVALID_ARC); + } + + //fix arc orientations of members; we cannot reflect for parallels. + *representativeArc = mergeArcSigns(dec, *representativeArc, source, FALSE); + + spqr_member newMergedMember = SPQR_INVALID_MEMBER; + if( nextIsParent ) + { + mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, + source, newCol->reducedMembers[current].pathTargetArc, TRUE, &newMergedMember); + } else + { + mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, + newCol->reducedMembers[current].pathTargetArc, source, TRUE, &newMergedMember); + } + *mergedMember = newMergedMember; + + return SCIP_OKAY; +} + +static SCIP_RETCODE transformAndMergeSeries( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + reduced_member_id current, + reduced_member_id next, + spqr_member nextMember, + SCIP_Bool nextIsParent, + spqr_arc* representativeArc, + spqr_member* mergedMember, + NewColInformation* info +) +{ + + SPQRColReducedMember* redMem = &newCol->reducedMembers[next]; + spqr_arc source = redMem->pathSourceArc; + spqr_arc target = redMem->pathTargetArc; + + spqr_arc pathRepresentative = SPQR_INVALID_ARC; + spqr_arc nonPathRepresentative = SPQR_INVALID_ARC; + SCIP_CALL( + splitSeriesMerging(dec, newCol, redMem, nextMember, &pathRepresentative, &nonPathRepresentative, source, target)); + //After splitting there is the following possibilities for nodes a-d: + //(a)-source-(b)-path-(c)-target-(d)-nonpath-(a) + //(a)-source-(b)-path-(c)-target-(d==a) + //(a)-source-(b)=(c)-target-(d)-nonpath-(a) + //(a)-source-(b)-path-(c)=(d) -nonpath-(a) + //Note that the given arc is always between the same nodes + assert(getNumMemberArcs(dec, nextMember) == 3 || getNumMemberArcs(dec, nextMember) == 4); + assert(pathRepresentative != source && nonPathRepresentative != source && + ( SPQRarcIsInvalid(target) || ( target != pathRepresentative && target != nonPathRepresentative ))); + spqr_node a = SPQR_INVALID_NODE; + spqr_node b = SPQR_INVALID_NODE; + spqr_node c = SPQR_INVALID_NODE; + spqr_node d = SPQR_INVALID_NODE; + SCIP_CALL(createNode(dec, &a)); + SCIP_CALL(createNode(dec, &b)); + if( SPQRarcIsValid(pathRepresentative)) + { + SCIP_CALL(createNode(dec, &c)); + } else + { + c = b; + } + SCIP_Bool hasNonPath = SPQRarcIsValid(nonPathRepresentative); + SCIP_Bool hasTarget = SPQRarcIsValid(target); + if( hasNonPath && hasTarget ) + { + SCIP_CALL(createNode(dec, &d)); + } else + { + if( hasNonPath ) + { + d = c; + } else + { + d = a; + } + } + + SCIP_Bool sourceReversed = arcIsReversedNonRigid(dec, source); + SCIP_Bool pathStartInHead = isHead(newCol->reducedMembers[current].pathType); + if( pathStartInHead ) + { + setArcHeadAndTail(dec, source, b, a); + } else + { + setArcHeadAndTail(dec, source, a, b); + } + if( SPQRarcIsValid(pathRepresentative)) + { + if(( arcIsReversedNonRigid(dec, pathRepresentative) == sourceReversed ) == pathStartInHead ) + { + setArcHeadAndTail(dec, pathRepresentative, c, b); + } else + { + setArcHeadAndTail(dec, pathRepresentative, b, c); + } + arcSetReversed(dec, pathRepresentative, FALSE); + arcSetRepresentative(dec, pathRepresentative, source); + } + if( hasTarget ) + { + if(( arcIsReversedNonRigid(dec, target) == sourceReversed ) == pathStartInHead ) + { + setArcHeadAndTail(dec, target, d, c); + } else + { + setArcHeadAndTail(dec, target, c, d); + } + arcSetReversed(dec, target, FALSE); + arcSetRepresentative(dec, target, source); + } + if( hasNonPath ) + { + if(( arcIsReversedNonRigid(dec, nonPathRepresentative) == sourceReversed ) == pathStartInHead ) + { + setArcHeadAndTail(dec, nonPathRepresentative, a, d); + } else + { + setArcHeadAndTail(dec, nonPathRepresentative, d, a); + } + arcSetReversed(dec, nonPathRepresentative, FALSE); + arcSetRepresentative(dec, nonPathRepresentative, source); + } + + arcSetReversed(dec, source, FALSE); + arcSetRepresentative(dec, source, SPQR_INVALID_ARC); + + //fix arc orientations of members; we cannot reflect for series + + spqr_member newMergedMember = SPQR_INVALID_MEMBER; + if( nextIsParent ) + { + mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, + source, newCol->reducedMembers[current].pathTargetArc, TRUE, &newMergedMember); + } else + { + mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, + newCol->reducedMembers[current].pathTargetArc, source, TRUE, &newMergedMember); + } + *mergedMember = newMergedMember; + + *representativeArc = mergeArcSigns(dec, *representativeArc, source, FALSE); + if( !hasTarget ) + { + //We are in the last node; finish the path + setTerminalReversed(info, FALSE); + if( isInto(newCol->reducedMembers[current].pathType)) + { + setTerminalHead(info, c); + } else + { + setTerminalTail(info, c); + } + setTerminalMember(info, *mergedMember); + setTerminalRepresentative(info, *representativeArc); + } + return SCIP_OKAY; +} + +static SCIP_RETCODE transformAndMergeRigid( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + reduced_member_id current, + reduced_member_id next, + spqr_member nextMember, + SCIP_Bool nextIsParent, + spqr_arc* representativeArc, + spqr_member* mergedMember, + NewColInformation* info +) +{ + SPQRColReducedMember* redMem = &newCol->reducedMembers[next]; + spqr_arc source = redMem->pathSourceArc; + spqr_arc sourceRepresentative = findArcSign(dec, source).representative; + + + spqr_member newMergedMember = SPQR_INVALID_MEMBER; + + if( nextIsParent ) + { + mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, + source, newCol->reducedMembers[current].pathTargetArc, !redMem->reverseArcs, + &newMergedMember); + } else + { + mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, + newCol->reducedMembers[current].pathTargetArc, source, !redMem->reverseArcs, + &newMergedMember); + } + + *mergedMember = newMergedMember; + + *representativeArc = mergeArcSigns(dec, *representativeArc, sourceRepresentative, redMem->reverseArcs); + + if( SPQRarcIsInvalid(redMem->pathTargetArc)) + { + //We are in the last node; finish the path + setTerminalReversed(info, FALSE); + if( isInto(newCol->reducedMembers[current].pathType)) + { + if( redMem->reverseArcs ) + { + setTerminalHead(info, redMem->rigidPathStart); + } else + { + setTerminalHead(info, redMem->rigidPathEnd); + } + } else + { + if( redMem->reverseArcs ) + { + setTerminalTail(info, redMem->rigidPathEnd); + } else + { + setTerminalTail(info, redMem->rigidPathStart); + } + } + setTerminalMember(info, *mergedMember); + setTerminalRepresentative(info, *representativeArc); + } + + return SCIP_OKAY; +} + +static SCIP_RETCODE transformAndMerge( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + reduced_member_id current, + reduced_member_id next, + spqr_arc* representativeArc, + spqr_member* mergedMember, + SCIP_Bool nextIsParent, + NewColInformation* info +) +{ + spqr_member nextMember = newCol->reducedMembers[next].member; + switch( getMemberType(dec, nextMember)) + { + case SPQR_MEMBERTYPE_RIGID: + { + SCIP_CALL(transformAndMergeRigid(dec, newCol, current, next, nextMember, nextIsParent, + representativeArc, mergedMember, info)); + break; + } + case SPQR_MEMBERTYPE_PARALLEL: + { + SCIP_CALL(transformAndMergeParallel(dec, newCol, current, next, nextMember, nextIsParent, + representativeArc, mergedMember)); + break; + } + case SPQR_MEMBERTYPE_SERIES: + { + SCIP_CALL(transformAndMergeSeries(dec, newCol, current, next, nextMember, nextIsParent, + representativeArc, mergedMember, info)); + break; + } + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_UNASSIGNED: + { + assert(FALSE); + break; + } + } + return SCIP_OKAY; +} + +static SCIP_RETCODE transformPath( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + SPQRColReducedComponent* component, + NewColInformation* newColInfo +) +{ + //Realize first member + reduced_member_id firstPathMember = component->pathEndMembers[0]; + + spqr_arc representativeArc = SPQR_INVALID_ARC; + spqr_member mergedMember = SPQR_INVALID_MEMBER; + SCIP_CALL(transformFirstPathMember(dec, newCol, firstPathMember, newColInfo, &representativeArc, &mergedMember)); + //Iteratively call function which realizes next member and merges them together. + reduced_member_id current = firstPathMember; + reduced_member_id next = newCol->reducedMembers[current].nextPathMember; + SCIP_Bool nextIsParent = newCol->reducedMembers[current].nextPathMemberIsParent; + while( reducedMemberIsValid(next)) + { + SCIP_CALL( + transformAndMerge(dec, newCol, current, next, &representativeArc, &mergedMember, nextIsParent, newColInfo)); + current = next; + next = newCol->reducedMembers[current].nextPathMember; + nextIsParent = newCol->reducedMembers[current].nextPathMemberIsParent; + } + return SCIP_OKAY; +} + +static SCIP_RETCODE columnTransformSingleParallel( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + reduced_member_id reducedMemberId, + spqr_member member, + NewColInformation* newColInfo +) +{ + SPQRColReducedMember* reducedMember = &newCol->reducedMembers[reducedMemberId]; + assert(pathArcIsValid(reducedMember->firstPathArc) && reducedMember->numPathArcs == 1); + //The new arc can be placed in parallel; just add it to this member + setTerminalReversed(newColInfo, reducedMember->pathBackwards); + setTerminalMember(newColInfo, member); + return SCIP_OKAY; +} + +static SCIP_RETCODE columnTransformSingleSeries( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + reduced_member_id reducedMemberId, + spqr_member member, + NewColInformation* newColInfo +) +{ + + if( getNumMemberArcs(dec, member) == 1 ) + { + newColInfo->member = member; + newColInfo->reversed = newCol->arcInPathReversed[getFirstMemberArc(dec, member)]; + return SCIP_OKAY; + } + //Isolated single cycle + spqr_member loopMember; + SPQRColReducedMember* reducedMember = &newCol->reducedMembers[reducedMemberId]; + SCIP_CALL(splitSeries(dec, newCol, reducedMember, member, &loopMember, newColInfo)); + + return SCIP_OKAY; +} + +static SCIP_RETCODE columnTransformSingleRigid( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + reduced_member_id reducedMemberId, + spqr_member member, + NewColInformation* newColInfo +) +{ + assert(dec); + assert(newCol); + assert(newColInfo); + assert(reducedMemberIsValid(reducedMemberId)); + //The path is already computed, so we can simply take the start and end nodes. + //However, there is one exception, which is that an arc connecting these two nodes already exists in the member + //If so, we create a new parallel member with the new arc and this member, unless the existing arc already points + //to a parallel member + SPQRColReducedMember* reducedMember = &newCol->reducedMembers[reducedMemberId]; + assert(SPQRnodeIsValid(reducedMember->rigidPathStart) && SPQRnodeIsValid(reducedMember->rigidPathEnd)); + { + spqr_arc existingArcWithPath = SPQR_INVALID_ARC; + spqr_arc firstArc = getFirstNodeArc(dec, reducedMember->rigidPathStart); + spqr_arc arc = firstArc; + SCIP_Bool pathInSameDirection = FALSE; + do + { + spqr_node head = findArcHead(dec, arc); + spqr_node tail = findArcTail(dec, arc); + spqr_node other = head == reducedMember->rigidPathStart ? tail : head; + if( other == reducedMember->rigidPathEnd ) + { + existingArcWithPath = arc; + pathInSameDirection = ( head == other ) != findArcSign(dec, existingArcWithPath).reversed; + break; + } + arc = getNextNodeArc(dec, arc, reducedMember->rigidPathStart); + } while( arc != firstArc ); + if( SPQRarcIsValid(existingArcWithPath)) + { + SCIP_Bool isParent = FALSE; + spqr_member adjacentMember = arcIsMarker(dec, existingArcWithPath) ? findArcChildMember(dec, + existingArcWithPath) + : SPQR_INVALID_MEMBER; + if( existingArcWithPath == markerToParent(dec, member)) + { + adjacentMember = findMemberParent(dec, member); + isParent = TRUE; + } + if( SPQRmemberIsValid(adjacentMember) && getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_PARALLEL ) + { + spqr_arc parallelMarker = isParent ? markerOfParent(dec, member) : markerToParent(dec, adjacentMember); + SCIP_Bool markerReversed = arcIsReversedNonRigid(dec, parallelMarker); + setTerminalMember(newColInfo, adjacentMember); + setTerminalReversed(newColInfo, markerReversed == pathInSameDirection); + } else + { + //create a new parallel and move the edge there + //This is a bit painful, because we cannot actually remove edges because of the union-find data structure + //So what we do instead, is convert the current edge to a marker edge, and 'duplicate' + //it in the new parallel member, and add the new marker there too, manually + spqr_member adjacentParallel = SPQR_INVALID_MEMBER; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &adjacentParallel)); + //'duplicate' a new arc in the parallel to be the current arc + spqr_arc duplicate = SPQR_INVALID_ARC; + spqr_element element = arcGetElement(dec, existingArcWithPath); + if( element != MARKER_COLUMN_ELEMENT && element != MARKER_ROW_ELEMENT) + { + if( SPQRelementIsColumn(element)) + { + SCIP_CALL(createColumnArc(dec, adjacentParallel, &duplicate, SPQRelementToColumn(element), FALSE)); + } else + { + SCIP_CALL(createRowArc(dec, adjacentParallel, &duplicate, SPQRelementToRow(element), FALSE)); + } + } else if( isParent ) + { + SCIP_CALL(createParentMarker(dec, adjacentParallel, arcIsTree(dec, existingArcWithPath), adjacentMember, + markerOfParent(dec, member), &duplicate, FALSE)); + } else + { + SCIP_CALL(createChildMarker(dec, adjacentParallel, adjacentMember, arcIsTree(dec, existingArcWithPath), + &duplicate, FALSE)); + dec->members[adjacentMember].parentMember = adjacentParallel; + dec->members[adjacentMember].markerOfParent = duplicate; } - arcSetRepresentative(dec,arc,source); - arcSetReversed(dec,arc,FALSE); - - arc = getNextMemberArc(dec,arc); - }while(arc != firstArc); - - arcSetRepresentative(dec,source,SPQR_INVALID_ARC); - } - - //fix arc orientations of members; we cannot reflect for parallels. - *representativeArc = mergeArcSigns(dec,*representativeArc,source,FALSE); - - spqr_member newMergedMember = SPQR_INVALID_MEMBER; - if(nextIsParent){ - mergeGivenMemberIntoParent(dec,*mergedMember,nextMember, - source,newCol->reducedMembers[current].pathTargetArc - ,TRUE,&newMergedMember); - }else{ - mergeGivenMemberIntoParent(dec,nextMember,*mergedMember, - newCol->reducedMembers[current].pathTargetArc,source ,TRUE,&newMergedMember); - } - * mergedMember = newMergedMember; - - return SCIP_OKAY; -} -static SCIP_RETCODE transformAndMergeSeries(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, - reduced_member_id current, reduced_member_id next, - spqr_member nextMember, SCIP_Bool nextIsParent, - spqr_arc * representativeArc, - spqr_member * mergedMember, - NewColInformation * info){ - - SPQRColReducedMember * redMem = &newCol->reducedMembers[next]; - spqr_arc source = redMem->pathSourceArc; - spqr_arc target = redMem->pathTargetArc; - - spqr_arc pathRepresentative = SPQR_INVALID_ARC; - spqr_arc nonPathRepresentative = SPQR_INVALID_ARC; - SCIP_CALL(splitSeriesMerging(dec,newCol,redMem,nextMember,&pathRepresentative,&nonPathRepresentative,source,target)); - //After splitting there is the following possibilities for nodes a-d: - //(a)-source-(b)-path-(c)-target-(d)-nonpath-(a) - //(a)-source-(b)-path-(c)-target-(d==a) - //(a)-source-(b)=(c)-target-(d)-nonpath-(a) - //(a)-source-(b)-path-(c)=(d) -nonpath-(a) - //Note that the given arc is always between the same nodes - assert(getNumMemberArcs(dec,nextMember) == 3 || getNumMemberArcs(dec,nextMember) == 4); - assert(pathRepresentative != source && nonPathRepresentative != source && (SPQRarcIsInvalid(target) || ( - target != pathRepresentative && target != nonPathRepresentative - ))); - spqr_node a = SPQR_INVALID_NODE; - spqr_node b = SPQR_INVALID_NODE; - spqr_node c = SPQR_INVALID_NODE; - spqr_node d = SPQR_INVALID_NODE; - SCIP_CALL(createNode(dec, &a)); - SCIP_CALL(createNode(dec, &b)); - if (SPQRarcIsValid(pathRepresentative)) { - SCIP_CALL(createNode(dec, &c)); - } else { - c = b; - } - SCIP_Bool hasNonPath = SPQRarcIsValid(nonPathRepresentative); - SCIP_Bool hasTarget = SPQRarcIsValid(target); - if (hasNonPath && hasTarget) { - SCIP_CALL(createNode(dec, &d)); - } else { - if(hasNonPath){ - d = c; - }else{ - d = a; - } - } - - SCIP_Bool sourceReversed = arcIsReversedNonRigid(dec,source); - SCIP_Bool pathStartInHead = isHead(newCol->reducedMembers[current].pathType); - if(pathStartInHead){ - setArcHeadAndTail(dec,source,b,a); - }else{ - setArcHeadAndTail(dec,source,a,b); - } - if(SPQRarcIsValid(pathRepresentative)){ - if((arcIsReversedNonRigid(dec,pathRepresentative) == sourceReversed) == pathStartInHead){ - setArcHeadAndTail(dec,pathRepresentative,c,b); - }else{ - setArcHeadAndTail(dec,pathRepresentative,b,c); - } - arcSetReversed(dec,pathRepresentative,FALSE); - arcSetRepresentative(dec,pathRepresentative,source); - } - if(hasTarget){ - if((arcIsReversedNonRigid(dec,target) == sourceReversed) == pathStartInHead){ - setArcHeadAndTail(dec,target,d,c); - }else{ - setArcHeadAndTail(dec,target,c,d); - } - arcSetReversed(dec,target,FALSE); - arcSetRepresentative(dec,target,source); - } - if(hasNonPath){ - if((arcIsReversedNonRigid(dec,nonPathRepresentative) == sourceReversed) == pathStartInHead){ - setArcHeadAndTail(dec,nonPathRepresentative,a,d); - }else{ - setArcHeadAndTail(dec,nonPathRepresentative,d,a); - } - arcSetReversed(dec,nonPathRepresentative,FALSE); - arcSetRepresentative(dec,nonPathRepresentative,source); - } - - arcSetReversed(dec,source,FALSE); - arcSetRepresentative(dec,source,SPQR_INVALID_ARC); - - //fix arc orientations of members; we cannot reflect for series - - spqr_member newMergedMember = SPQR_INVALID_MEMBER; - if(nextIsParent){ - mergeGivenMemberIntoParent(dec,*mergedMember,nextMember, - source,newCol->reducedMembers[current].pathTargetArc - ,TRUE,&newMergedMember); - }else{ - mergeGivenMemberIntoParent(dec,nextMember,*mergedMember, - newCol->reducedMembers[current].pathTargetArc,source ,TRUE,&newMergedMember); - } - * mergedMember = newMergedMember; - - *representativeArc = mergeArcSigns(dec,*representativeArc,source,FALSE); - if(!hasTarget){ - //We are in the last node; finish the path - setTerminalReversed(info,FALSE); - if(isInto(newCol->reducedMembers[current].pathType)) { - setTerminalHead(info, c); - }else{ - setTerminalTail(info, c); - } - setTerminalMember(info,*mergedMember); - setTerminalRepresentative(info,*representativeArc); - } - return SCIP_OKAY; -} -static SCIP_RETCODE transformAndMergeRigid(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, - reduced_member_id current, reduced_member_id next, - spqr_member nextMember, SCIP_Bool nextIsParent, - spqr_arc * representativeArc, - spqr_member * mergedMember, - NewColInformation * info){ - SPQRColReducedMember * redMem = &newCol->reducedMembers[next]; - spqr_arc source = redMem->pathSourceArc; - spqr_arc sourceRepresentative = findArcSign(dec,source).representative; - - - spqr_member newMergedMember = SPQR_INVALID_MEMBER; - - if(nextIsParent){ - mergeGivenMemberIntoParent(dec,*mergedMember,nextMember, - source,newCol->reducedMembers[current].pathTargetArc - ,!redMem->reverseArcs,&newMergedMember); - }else{ - mergeGivenMemberIntoParent(dec,nextMember,*mergedMember, - newCol->reducedMembers[current].pathTargetArc,source ,!redMem->reverseArcs,&newMergedMember); - } - - * mergedMember = newMergedMember; - - *representativeArc = mergeArcSigns(dec,*representativeArc,sourceRepresentative,redMem->reverseArcs); - - if(SPQRarcIsInvalid(redMem->pathTargetArc)){ - //We are in the last node; finish the path - setTerminalReversed(info,FALSE); - if(isInto(newCol->reducedMembers[current].pathType)) { - if(redMem->reverseArcs){ - setTerminalHead(info,redMem->rigidPathStart); - }else{ - setTerminalHead(info, redMem->rigidPathEnd); + //Create the other marker edge + spqr_arc parallelMarker = SPQR_INVALID_ARC; + if( isParent ) + { + SCIP_CALL(createChildMarker(dec, adjacentParallel, member, !arcIsTree(dec, existingArcWithPath), + ¶llelMarker, FALSE)); + } else + { + SCIP_CALL(createParentMarker(dec, adjacentParallel, !arcIsTree(dec, existingArcWithPath), + member, existingArcWithPath, ¶llelMarker, FALSE)); } - }else{ - if(redMem->reverseArcs){ - setTerminalTail(info,redMem->rigidPathEnd); - }else{ - setTerminalTail(info, redMem->rigidPathStart); + + //Change the existing edge to a marker + if( isParent ) + { + assert(markerToParent(dec, member) == existingArcWithPath); + dec->arcs[markerOfParent(dec, member)].childMember = adjacentParallel; + dec->members[member].parentMember = adjacentParallel; + dec->members[member].markerToParent = existingArcWithPath; + dec->members[member].markerOfParent = parallelMarker; + dec->arcs[existingArcWithPath].element = arcIsTree(dec, existingArcWithPath) ? MARKER_ROW_ELEMENT + : MARKER_COLUMN_ELEMENT;; + dec->arcs[existingArcWithPath].childMember = adjacentParallel; + + } else + { + dec->arcs[existingArcWithPath].element = arcIsTree(dec, existingArcWithPath) ? MARKER_ROW_ELEMENT + : MARKER_COLUMN_ELEMENT; + dec->arcs[existingArcWithPath].childMember = adjacentParallel; } - } - setTerminalMember(info,*mergedMember); - setTerminalRepresentative(info,*representativeArc); - } - - return SCIP_OKAY; -} -static SCIP_RETCODE transformAndMerge(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, - reduced_member_id current, reduced_member_id next, - spqr_arc * representativeArc, - spqr_member * mergedMember, - SCIP_Bool nextIsParent, NewColInformation * info){ - spqr_member nextMember = newCol->reducedMembers[next].member; - switch(getMemberType(dec,nextMember)){ - case SPQR_MEMBERTYPE_RIGID:{ - SCIP_CALL(transformAndMergeRigid(dec,newCol,current,next,nextMember,nextIsParent, - representativeArc,mergedMember,info)); + + setTerminalMember(newColInfo, adjacentParallel); + setTerminalReversed(newColInfo, !pathInSameDirection); + } + return SCIP_OKAY; + } + } + + setTerminalMember(newColInfo, member); + setTerminalReversed(newColInfo, FALSE); + setTerminalTail(newColInfo, reducedMember->rigidPathStart); + setTerminalHead(newColInfo, reducedMember->rigidPathEnd); + setTerminalRepresentative(newColInfo, + findArcSign(dec, newCol->pathArcs[reducedMember->firstPathArc].arc).representative); + return SCIP_OKAY; +} + +static SCIP_RETCODE transformComponent( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol, + SPQRColReducedComponent* component, + NewColInformation* newColInfo +) +{ + assert(dec); + assert(newCol); + assert(component); + assert(newColInfo); + + if( newCol->reducedMembers[component->root].numChildren == + newCol->reducedMembers[component->root].numPropagatedChildren ) + { + //No merging necessary, only a single component + reduced_member_id reducedMember = component->root; + assert(reducedMemberIsValid(reducedMember)); + spqr_member member = newCol->reducedMembers[reducedMember].member; + SPQRMemberType type = getMemberType(dec, member); + + switch( type ) + { + case SPQR_MEMBERTYPE_RIGID: + { + SCIP_CALL(columnTransformSingleRigid(dec, newCol, reducedMember, member, newColInfo)); break; - } - case SPQR_MEMBERTYPE_PARALLEL:{ - SCIP_CALL(transformAndMergeParallel(dec,newCol,current,next,nextMember,nextIsParent, - representativeArc, mergedMember)); + } + case SPQR_MEMBERTYPE_PARALLEL: + { + SCIP_CALL(columnTransformSingleParallel(dec, newCol, reducedMember, member, newColInfo)); break; - } - case SPQR_MEMBERTYPE_SERIES:{ - SCIP_CALL(transformAndMergeSeries(dec,newCol,current,next,nextMember,nextIsParent, - representativeArc, mergedMember,info)); + } + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_SERIES: + { + SCIP_CALL(columnTransformSingleSeries(dec, newCol, reducedMember, member, newColInfo)); break; - } - case SPQR_MEMBERTYPE_LOOP: - case SPQR_MEMBERTYPE_UNASSIGNED: - { + } + default: + { assert(FALSE); break; - } - } - return SCIP_OKAY; -} - -static SCIP_RETCODE transformPath(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION * newCol, - SPQRColReducedComponent * component, - NewColInformation * newColInfo){ - //Realize first member - reduced_member_id firstPathMember = component->pathEndMembers[0]; - - spqr_arc representativeArc = SPQR_INVALID_ARC; - spqr_member mergedMember = SPQR_INVALID_MEMBER; - SCIP_CALL(transformFirstPathMember(dec,newCol,firstPathMember,newColInfo,&representativeArc,&mergedMember)); - //Iteratively call function which realizes next member and merges them together. - reduced_member_id current = firstPathMember; - reduced_member_id next = newCol->reducedMembers[current].nextPathMember; - SCIP_Bool nextIsParent = newCol->reducedMembers[current].nextPathMemberIsParent; - while(reducedMemberIsValid(next)){ - SCIP_CALL(transformAndMerge(dec,newCol,current,next,&representativeArc,&mergedMember,nextIsParent,newColInfo)); - current = next; - next = newCol->reducedMembers[current].nextPathMember; - nextIsParent = newCol->reducedMembers[current].nextPathMemberIsParent; - } - return SCIP_OKAY; -} - -static SCIP_RETCODE columnTransformSingleParallel(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, - reduced_member_id reducedMemberId, spqr_member member, - NewColInformation *newColInfo){ - SPQRColReducedMember * reducedMember = &newCol->reducedMembers[reducedMemberId]; - assert(pathArcIsValid(reducedMember->firstPathArc) && reducedMember->numPathArcs == 1); - //The new arc can be placed in parallel; just add it to this member - setTerminalReversed(newColInfo,reducedMember->pathBackwards); - setTerminalMember(newColInfo,member); - return SCIP_OKAY; -} -static SCIP_RETCODE columnTransformSingleSeries(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, - reduced_member_id reducedMemberId, spqr_member member, - NewColInformation *newColInfo){ - - if(getNumMemberArcs(dec,member) == 1){ - newColInfo->member = member; - newColInfo->reversed = newCol->arcInPathReversed[getFirstMemberArc(dec,member)]; - return SCIP_OKAY; - } - //Isolated single cycle - spqr_member loopMember; - SPQRColReducedMember *reducedMember = &newCol->reducedMembers[reducedMemberId]; - SCIP_CALL(splitSeries(dec,newCol,reducedMember,member,&loopMember,newColInfo)); - - return SCIP_OKAY; -} -static SCIP_RETCODE columnTransformSingleRigid(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, - reduced_member_id reducedMemberId, spqr_member member, - NewColInformation *newColInfo){ - assert(dec); - assert(newCol); - assert(newColInfo); - assert(reducedMemberIsValid(reducedMemberId)); - //The path is already computed, so we can simply take the start and end nodes. - //However, there is one exception, which is that an arc connecting these two nodes already exists in the member - //If so, we create a new parallel member with the new arc and this member, unless the existing arc already points - //to a parallel member - SPQRColReducedMember * reducedMember = &newCol->reducedMembers[reducedMemberId]; - assert(SPQRnodeIsValid(reducedMember->rigidPathStart) && SPQRnodeIsValid(reducedMember->rigidPathEnd)); - { - spqr_arc existingArcWithPath = SPQR_INVALID_ARC; - spqr_arc firstArc = getFirstNodeArc(dec,reducedMember->rigidPathStart); - spqr_arc arc = firstArc; - SCIP_Bool pathInSameDirection = FALSE; - do{ - spqr_node head = findArcHead(dec,arc); - spqr_node tail = findArcTail(dec,arc); - spqr_node other = head == reducedMember->rigidPathStart ? tail : head; - if(other == reducedMember->rigidPathEnd){ - existingArcWithPath = arc; - pathInSameDirection = (head == other) != findArcSign(dec,existingArcWithPath).reversed; - break; - } - arc = getNextNodeArc(dec,arc,reducedMember->rigidPathStart); - }while(arc != firstArc); - if(SPQRarcIsValid(existingArcWithPath)){ - SCIP_Bool isParent = FALSE; - spqr_member adjacentMember = arcIsMarker(dec,existingArcWithPath) ? findArcChildMember(dec,existingArcWithPath) : SPQR_INVALID_MEMBER; - if(existingArcWithPath == markerToParent(dec,member)){ - adjacentMember = findMemberParent(dec,member); - isParent = TRUE; - } - if(SPQRmemberIsValid(adjacentMember) && getMemberType(dec,adjacentMember) == SPQR_MEMBERTYPE_PARALLEL){ - spqr_arc parallelMarker = isParent ? markerOfParent(dec,member) : markerToParent(dec,adjacentMember); - SCIP_Bool markerReversed = arcIsReversedNonRigid(dec,parallelMarker); - setTerminalMember(newColInfo,adjacentMember); - setTerminalReversed(newColInfo,markerReversed == pathInSameDirection); - }else{ - //create a new parallel and move the edge there - //This is a bit painful, because we cannot actually remove edges because of the union-find data structure - //So what we do instead, is convert the current edge to a marker edge, and 'duplicate' - //it in the new parallel member, and add the new marker there too, manually - spqr_member adjacentParallel = SPQR_INVALID_MEMBER; - SCIP_CALL(createMember(dec,SPQR_MEMBERTYPE_PARALLEL,&adjacentParallel)); - //'duplicate' a new arc in the parallel to be the current arc - spqr_arc duplicate = SPQR_INVALID_ARC; - spqr_element element = arcGetElement(dec,existingArcWithPath); - if( element != MARKER_COLUMN_ELEMENT && element != MARKER_ROW_ELEMENT){ - if(SPQRelementIsColumn(element)){ - SCIP_CALL(createColumnArc(dec,adjacentParallel,&duplicate, SPQRelementToColumn(element),FALSE)); - }else{ - SCIP_CALL(createRowArc(dec,adjacentParallel,&duplicate, SPQRelementToRow(element),FALSE)); - } - }else if(isParent){ - SCIP_CALL(createParentMarker(dec,adjacentParallel, arcIsTree(dec,existingArcWithPath),adjacentMember, - markerOfParent(dec,member) - ,&duplicate,FALSE)); - }else{ - SCIP_CALL(createChildMarker(dec,adjacentParallel,adjacentMember,arcIsTree(dec,existingArcWithPath),&duplicate,FALSE)); - dec->members[adjacentMember].parentMember = adjacentParallel; - dec->members[adjacentMember].markerOfParent = duplicate; - } - //Create the other marker edge - spqr_arc parallelMarker = SPQR_INVALID_ARC; - if(isParent){ - SCIP_CALL(createChildMarker(dec,adjacentParallel,member,!arcIsTree(dec,existingArcWithPath), - ¶llelMarker,FALSE)); - }else{ - SCIP_CALL(createParentMarker(dec,adjacentParallel,!arcIsTree(dec,existingArcWithPath), - member,existingArcWithPath,¶llelMarker,FALSE)); - } - - //Change the existing edge to a marker - if(isParent){ - assert(markerToParent(dec,member) == existingArcWithPath); - dec->arcs[markerOfParent(dec,member)].childMember = adjacentParallel; - dec->members[member].parentMember = adjacentParallel; - dec->members[member].markerToParent = existingArcWithPath; - dec->members[member].markerOfParent = parallelMarker; - dec->arcs[existingArcWithPath].element = arcIsTree(dec,existingArcWithPath) ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT;; - dec->arcs[existingArcWithPath].childMember = adjacentParallel; - - }else{ - dec->arcs[existingArcWithPath].element = arcIsTree(dec,existingArcWithPath) ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; - dec->arcs[existingArcWithPath].childMember = adjacentParallel; - } - - setTerminalMember(newColInfo,adjacentParallel); - setTerminalReversed(newColInfo,!pathInSameDirection); - } - return SCIP_OKAY; - } - } - - setTerminalMember(newColInfo,member); - setTerminalReversed(newColInfo,FALSE); - setTerminalTail(newColInfo,reducedMember->rigidPathStart); - setTerminalHead(newColInfo,reducedMember->rigidPathEnd); - setTerminalRepresentative(newColInfo,findArcSign(dec,newCol->pathArcs[reducedMember->firstPathArc].arc).representative); - return SCIP_OKAY; -} -static SCIP_RETCODE transformComponent(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol, SPQRColReducedComponent * component, NewColInformation * newColInfo){ - assert(dec); - assert(newCol); - assert(component); - assert(newColInfo); - - if(newCol->reducedMembers[component->root].numChildren == newCol->reducedMembers[component->root].numPropagatedChildren){ - //No merging necessary, only a single component - reduced_member_id reducedMember = component->root; - assert(reducedMemberIsValid(reducedMember)); - spqr_member member = newCol->reducedMembers[reducedMember].member; - SPQRMemberType type = getMemberType(dec, member); - - switch(type) { - case SPQR_MEMBERTYPE_RIGID: { - SCIP_CALL(columnTransformSingleRigid(dec,newCol,reducedMember,member,newColInfo)); - break; - } - case SPQR_MEMBERTYPE_PARALLEL: { - SCIP_CALL(columnTransformSingleParallel(dec, newCol, reducedMember, member, newColInfo)); - break; - } - case SPQR_MEMBERTYPE_LOOP: - case SPQR_MEMBERTYPE_SERIES: { - SCIP_CALL(columnTransformSingleSeries(dec, newCol, reducedMember, member, newColInfo)); - break; - } - default: { - assert(FALSE); - break; - } - } - return SCIP_OKAY; - } - // Otherwise, the reduced members form a path which can be merged into a single component of type R - SCIP_CALL(transformPath(dec,newCol,component,newColInfo)); - - return SCIP_OKAY; -} - -SCIP_RETCODE SCIPNetworkColAdditionAdd(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol){ - assert(dec); - assert(newCol); - - if(newCol->numReducedComponents == 0){ - spqr_member member; - SCIP_CALL(createStandaloneSeries(dec,newCol->newRowArcs,newCol->newRowArcReversed, - newCol->numNewRowArcs,newCol->newColIndex,&member)); - }else if(newCol->numReducedComponents == 1){ - NewColInformation information = emptyNewColInformation(); - SCIP_CALL(transformComponent(dec,newCol,&newCol->reducedComponents[0],&information)); - assert(memberIsRepresentative(dec,information.member)); - if(newCol->numNewRowArcs == 0){ - spqr_arc colArc = SPQR_INVALID_ARC; - SCIP_CALL(createColumnArc(dec,information.member,&colArc,newCol->newColIndex,information.reversed)); - if(SPQRnodeIsValid(information.head)){ - assert(SPQRnodeIsValid(information.tail)); - assert(SPQRarcIsValid(information.representative)); - setArcHeadAndTail(dec,colArc,findNode(dec,information.head),findNode(dec,information.tail)); - arcSetRepresentative(dec,colArc,information.representative); - arcSetReversed(dec,colArc,information.reversed != arcIsReversedNonRigid(dec,information.representative)); - } - }else{ - spqr_member newSeries = SPQR_INVALID_MEMBER; - SCIP_CALL(createConnectedSeries(dec,newCol->newRowArcs,newCol->newRowArcReversed,newCol->numNewRowArcs,newCol->newColIndex,&newSeries)); - spqr_arc markerArc = SPQR_INVALID_ARC; - spqr_arc ignore = SPQR_INVALID_ARC; - SCIP_CALL(createMarkerPairWithReferences(dec,information.member,newSeries,FALSE,information.reversed,TRUE,&markerArc,&ignore)); - if(SPQRnodeIsValid(information.head)){ - assert(SPQRnodeIsValid(information.tail)); - assert(SPQRarcIsValid(information.representative)); - setArcHeadAndTail(dec,markerArc,findNode(dec,information.head),findNode(dec,information.tail)); - arcSetRepresentative(dec,markerArc,information.representative); - arcSetReversed(dec,markerArc,information.reversed != arcIsReversedNonRigid(dec,information.representative)); - } - } - if(getMemberType(dec,information.member) == SPQR_MEMBERTYPE_LOOP){ - assert(getNumMemberArcs(dec,information.member) == 2 || getNumMemberArcs(dec,information.member) == 3); - if(getNumMemberArcs(dec,information.member) == 3){ - changeLoopToParallel(dec,information.member); - } - } - }else{ + } + } + return SCIP_OKAY; + } + // Otherwise, the reduced members form a path which can be merged into a single component of type R + SCIP_CALL(transformPath(dec, newCol, component, newColInfo)); + + return SCIP_OKAY; +} + +SCIP_RETCODE SCIPnetcoladdAdd( + SCIP_NETMATDEC* dec, + SCIP_NETCOLADD* newCol +) +{ + assert(dec); + assert(newCol); + assert(SCIPnetcoladdRemainsNetwork(newCol)); + + if( newCol->numReducedComponents == 0 ) + { + spqr_member member; + SCIP_CALL(createStandaloneSeries(dec, newCol->newRowArcs, newCol->newRowArcReversed, + newCol->numNewRowArcs, newCol->newColIndex, &member)); + } else if( newCol->numReducedComponents == 1 ) + { + NewColInformation information = emptyNewColInformation(); + SCIP_CALL(transformComponent(dec, newCol, &newCol->reducedComponents[0], &information)); + assert(memberIsRepresentative(dec, information.member)); + if( newCol->numNewRowArcs == 0 ) + { + spqr_arc colArc = SPQR_INVALID_ARC; + SCIP_CALL(createColumnArc(dec, information.member, &colArc, newCol->newColIndex, information.reversed)); + if( SPQRnodeIsValid(information.head)) + { + assert(SPQRnodeIsValid(information.tail)); + assert(SPQRarcIsValid(information.representative)); + setArcHeadAndTail(dec, colArc, findNode(dec, information.head), findNode(dec, information.tail)); + arcSetRepresentative(dec, colArc, information.representative); + arcSetReversed(dec, colArc, information.reversed != arcIsReversedNonRigid(dec, information.representative)); + } + } else + { + spqr_member newSeries = SPQR_INVALID_MEMBER; + SCIP_CALL(createConnectedSeries(dec, newCol->newRowArcs, newCol->newRowArcReversed, newCol->numNewRowArcs, + newCol->newColIndex, &newSeries)); + spqr_arc markerArc = SPQR_INVALID_ARC; + spqr_arc ignore = SPQR_INVALID_ARC; + SCIP_CALL(createMarkerPairWithReferences(dec, information.member, newSeries, FALSE, information.reversed, TRUE, + &markerArc, &ignore)); + if( SPQRnodeIsValid(information.head)) + { + assert(SPQRnodeIsValid(information.tail)); + assert(SPQRarcIsValid(information.representative)); + setArcHeadAndTail(dec, markerArc, findNode(dec, information.head), findNode(dec, information.tail)); + arcSetRepresentative(dec, markerArc, information.representative); + arcSetReversed(dec, markerArc, + information.reversed != arcIsReversedNonRigid(dec, information.representative)); + } + } + if( getMemberType(dec, information.member) == SPQR_MEMBERTYPE_LOOP ) + { + assert(getNumMemberArcs(dec, information.member) == 2 || getNumMemberArcs(dec, information.member) == 3); + if( getNumMemberArcs(dec, information.member) == 3 ) + { + changeLoopToParallel(dec, information.member); + } + } + } else + { #ifndef NDEBUG - int numDecComponentsBefore = numConnectedComponents(dec); + int numDecComponentsBefore = numConnectedComponents(dec); #endif - spqr_member newSeries = SPQR_INVALID_MEMBER; - SCIP_CALL(createConnectedSeries(dec,newCol->newRowArcs,newCol->newRowArcReversed, - newCol->numNewRowArcs,newCol->newColIndex,&newSeries)); - for (int i = 0; i < newCol->numReducedComponents; ++i) { - NewColInformation information = emptyNewColInformation(); - SCIP_CALL(transformComponent(dec,newCol,&newCol->reducedComponents[i],&information)); - if(getMemberType(dec,information.member) == SPQR_MEMBERTYPE_LOOP){ - assert(getNumMemberArcs(dec,information.member) == 1); - spqr_arc arc = getFirstMemberArc(dec,information.member); - assert(newCol->arcInPath[arc]); - moveArcToNewMember(dec,arc,information.member,newSeries); - arcSetReversed(dec,arc, !newCol->arcInPathReversed[arc]); - dec->members[information.member].type = SPQR_MEMBERTYPE_UNASSIGNED; - }else { - reorderComponent(dec, - information.member); //reorder the subtree so that the newly series member is a parent - spqr_arc markerArc = SPQR_INVALID_ARC; - spqr_arc ignore = SPQR_INVALID_ARC; - SCIP_CALL(createMarkerPairWithReferences(dec, newSeries, information.member, TRUE, information.reversed, - TRUE, &ignore, &markerArc)); - if (SPQRnodeIsValid(information.head)) { - assert(SPQRnodeIsValid(information.tail)); - assert(SPQRarcIsValid(information.representative)); - setArcHeadAndTail(dec, markerArc, findNode(dec, information.head), findNode(dec, information.tail)); - arcSetRepresentative(dec, markerArc, information.representative); - arcSetReversed(dec, markerArc, information.reversed == arcIsReversedNonRigid(dec, information.representative)); - } + spqr_member newSeries = SPQR_INVALID_MEMBER; + SCIP_CALL(createConnectedSeries(dec, newCol->newRowArcs, newCol->newRowArcReversed, + newCol->numNewRowArcs, newCol->newColIndex, &newSeries)); + for( int i = 0; i < newCol->numReducedComponents; ++i ) + { + NewColInformation information = emptyNewColInformation(); + SCIP_CALL(transformComponent(dec, newCol, &newCol->reducedComponents[i], &information)); + if( getMemberType(dec, information.member) == SPQR_MEMBERTYPE_LOOP ) + { + assert(getNumMemberArcs(dec, information.member) == 1); + spqr_arc arc = getFirstMemberArc(dec, information.member); + assert(newCol->arcInPath[arc]); + moveArcToNewMember(dec, arc, information.member, newSeries); + arcSetReversed(dec, arc, !newCol->arcInPathReversed[arc]); + dec->members[information.member].type = SPQR_MEMBERTYPE_UNASSIGNED; + } else + { + reorderComponent(dec, + information.member);//reorder the subtree so that the newly series member is a parent + spqr_arc markerArc = SPQR_INVALID_ARC; + spqr_arc ignore = SPQR_INVALID_ARC; + SCIP_CALL(createMarkerPairWithReferences(dec, newSeries, information.member, TRUE, information.reversed, + TRUE, &ignore, &markerArc)); + if( SPQRnodeIsValid(information.head)) + { + assert(SPQRnodeIsValid(information.tail)); + assert(SPQRarcIsValid(information.representative)); + setArcHeadAndTail(dec, markerArc, findNode(dec, information.head), findNode(dec, information.tail)); + arcSetRepresentative(dec, markerArc, information.representative); + arcSetReversed(dec, markerArc, + information.reversed == arcIsReversedNonRigid(dec, information.representative)); } - } - decreaseNumConnectedComponents(dec,newCol->numReducedComponents-1); - assert(numConnectedComponents(dec) == (numDecComponentsBefore - newCol->numReducedComponents + 1)); - } - return SCIP_OKAY; + } + } + decreaseNumConnectedComponents(dec, newCol->numReducedComponents - 1); + assert(numConnectedComponents(dec) == ( numDecComponentsBefore - newCol->numReducedComponents + 1 )); + } + return SCIP_OKAY; } -SCIP_Bool SCIPNetworkColAdditionRemainsNetwork(SCIP_NETWORKCOLADDITION *newCol){ - return newCol->remainsNetwork; +SCIP_Bool SCIPnetcoladdRemainsNetwork(const SCIP_NETCOLADD* newCol) +{ + return newCol->remainsNetwork; } -static int min(int a, int b){ - return a < b ? a : b; +static int min( + int a, + int b +) +{ + return a < b ? a : b; } typedef int cut_arc_id; #define INVALID_CUT_ARC (-1) -static SCIP_Bool cutArcIsInvalid(const cut_arc_id arc){ - return arc < 0; +static SCIP_Bool cutArcIsInvalid(const cut_arc_id arc) +{ + return arc < 0; } -static SCIP_Bool cutArcIsValid(const cut_arc_id arc){ - return !cutArcIsInvalid(arc); + +static SCIP_Bool cutArcIsValid(const cut_arc_id arc) +{ + return !cutArcIsInvalid(arc); } -typedef struct { //TODO:test if memory overhead of pointers is worth it? - spqr_arc arc; - spqr_node arcHead; - spqr_node arcTail; - cut_arc_id nextMember; - cut_arc_id nextOverall; - SCIP_Bool arcReversed; +typedef struct +{//TODO:test if memory overhead of pointers is worth it? + spqr_arc arc; + spqr_node arcHead; + spqr_node arcTail; + cut_arc_id nextMember; + cut_arc_id nextOverall; + SCIP_Bool arcReversed; } CutArcListNode; -typedef enum{ - TYPE_UNDETERMINED = 0, - TYPE_PROPAGATED = 1, - TYPE_MERGED = 2, - TYPE_NOT_NETWORK = 3 +typedef enum +{ + TYPE_UNDETERMINED = 0, + TYPE_PROPAGATED = 1, + TYPE_MERGED = 2, + TYPE_NOT_NETWORK = 3 } RowReducedMemberType; -typedef struct{ - int low; - int discoveryTime; +typedef struct +{ + int low; + int discoveryTime; } ArticulationNodeInformation; //We allocate the callstacks of recursive algorithms (usually DFS, bounded by some linear number of calls) //If one does not do this, we overflow the stack for large matrices/graphs through the number of recursive function calls //Then, we can write the recursive algorithms as while loops and allocate the function call data on the heap, preventing //Stack overflows -typedef struct { - spqr_node node; - spqr_arc nodeArc; +typedef struct +{ + spqr_node node; + spqr_arc nodeArc; } DFSCallData; -typedef struct{ - children_idx currentChild; - reduced_member_id id; +typedef struct +{ + children_idx currentChild; + reduced_member_id id; } MergeTreeCallData; -typedef struct{ - spqr_node node; - spqr_arc arc; +typedef struct +{ + spqr_node node; + spqr_arc arc; } ColorDFSCallData; -typedef struct{ - spqr_arc arc; - spqr_node node; - spqr_node parent; - SCIP_Bool isAP; +typedef struct +{ + spqr_arc arc; + spqr_node node; + spqr_node parent; + SCIP_Bool isAP; } ArticulationPointCallStack; -typedef enum{ - UNCOLORED = 0, - COLOR_SOURCE = 1, - COLOR_SINK = 2 +typedef enum +{ + UNCOLORED = 0, + COLOR_SOURCE = 1, + COLOR_SINK = 2 } COLOR_STATUS; -typedef struct { - spqr_member member; - spqr_member rootMember; - int depth; - RowReducedMemberType type; - reduced_member_id parent; - - children_idx firstChild; - children_idx numChildren; - children_idx numPropagatedChildren; - - cut_arc_id firstCutArc; - int numCutArcs; - - //For non-rigid members - spqr_arc splitArc; - SCIP_Bool splitHead; //Otherwise the tail of this arc is split - SCIP_Bool otherIsSource; //Otherwise the other end node is part of the sink partition. - // For non-rigid members this refers to splitArc, for rigid members it refers to articulation arc - - //For rigid members - spqr_node otherNode; - spqr_node splitNode; - SCIP_Bool allHaveCommonNode; - SCIP_Bool otherNodeSplit; - SCIP_Bool willBeReversed; - spqr_arc articulationArc; - spqr_node coloredNode; //points to a colored node so that we can efficiently zero out the colors again. +typedef struct +{ + spqr_member member; + spqr_member rootMember; + int depth; + RowReducedMemberType type; + reduced_member_id parent; + + children_idx firstChild; + children_idx numChildren; + children_idx numPropagatedChildren; + + cut_arc_id firstCutArc; + int numCutArcs; + + //For non-rigid members + spqr_arc splitArc; + SCIP_Bool splitHead; //Otherwise the tail of this arc is split + SCIP_Bool otherIsSource;//Otherwise the other end node is part of the sink partition. + // For non-rigid members this refers to splitArc, for rigid members it refers to articulation arc + + //For rigid members + spqr_node otherNode; + spqr_node splitNode; + SCIP_Bool allHaveCommonNode; + SCIP_Bool otherNodeSplit; + SCIP_Bool willBeReversed; + spqr_arc articulationArc; + spqr_node coloredNode;//points to a colored node so that we can efficiently zero out the colors again. } SPQRRowReducedMember; -typedef struct { - int rootDepth; - reduced_member_id root; +typedef struct +{ + int rootDepth; + reduced_member_id root; } SPQRRowReducedComponent; -struct SCIP_NetworkRowAddition { - SCIP_Bool remainsNetwork; +struct SCIP_NetRowAdd +{ + SCIP_Bool remainsNetwork; - SPQRRowReducedMember *reducedMembers; - int memReducedMembers; - int numReducedMembers; + SPQRRowReducedMember* reducedMembers; + int memReducedMembers; + int numReducedMembers; - SPQRRowReducedComponent *reducedComponents; - int memReducedComponents; - int numReducedComponents; + SPQRRowReducedComponent* reducedComponents; + int memReducedComponents; + int numReducedComponents; - MemberInfo *memberInformation; - int memMemberInformation; - int numMemberInformation; + MemberInfo* memberInformation; + int memMemberInformation; + int numMemberInformation; - reduced_member_id *childrenStorage; - int memChildrenStorage; - int numChildrenStorage; + reduced_member_id* childrenStorage; + int memChildrenStorage; + int numChildrenStorage; - CutArcListNode *cutArcs; - int memCutArcs; - int numCutArcs; - cut_arc_id firstOverallCutArc; + CutArcListNode* cutArcs; + int memCutArcs; + int numCutArcs; + cut_arc_id firstOverallCutArc; - spqr_row newRowIndex; + spqr_row newRowIndex; - spqr_col *newColumnArcs; - SCIP_Bool *newColumnReversed; - int memColumnArcs; - int numColumnArcs; + spqr_col* newColumnArcs; + SCIP_Bool* newColumnReversed; + int memColumnArcs; + int numColumnArcs; - reduced_member_id *leafMembers; - int numLeafMembers; - int memLeafMembers; + reduced_member_id* leafMembers; + int numLeafMembers; + int memLeafMembers; - spqr_arc *decompositionColumnArcs; - SCIP_Bool *decompositionColumnArcReversed; - int memDecompositionColumnArcs; - int numDecompositionColumnArcs; + spqr_arc* decompositionColumnArcs; + SCIP_Bool* decompositionColumnArcReversed; + int memDecompositionColumnArcs; + int numDecompositionColumnArcs; - SCIP_Bool *isArcCut; - SCIP_Bool *isArcCutReversed; - int numIsArcCut; - int memIsArcCut; + SCIP_Bool* isArcCut; + SCIP_Bool* isArcCutReversed; + int numIsArcCut; + int memIsArcCut; - COLOR_STATUS *nodeColors; - int memNodeColors; + COLOR_STATUS* nodeColors; + int memNodeColors; - spqr_node *articulationNodes; - int numArticulationNodes; - int memArticulationNodes; + spqr_node* articulationNodes; + int numArticulationNodes; + int memArticulationNodes; - ArticulationNodeInformation *articulationNodeSearchInfo; - int memNodeSearchInfo; + ArticulationNodeInformation* articulationNodeSearchInfo; + int memNodeSearchInfo; - int *crossingPathCount; - int memCrossingPathCount; + int* crossingPathCount; + int memCrossingPathCount; - DFSCallData *intersectionDFSData; - int memIntersectionDFSData; + DFSCallData* intersectionDFSData; + int memIntersectionDFSData; - ColorDFSCallData *colorDFSData; - int memColorDFSData; + ColorDFSCallData* colorDFSData; + int memColorDFSData; - ArticulationPointCallStack *artDFSData; - int memArtDFSData; + ArticulationPointCallStack* artDFSData; + int memArtDFSData; - CreateReducedMembersCallstack *createReducedMembersCallstack; - int memCreateReducedMembersCallstack; + CreateReducedMembersCallstack* createReducedMembersCallstack; + int memCreateReducedMembersCallstack; - int *intersectionPathDepth; - int memIntersectionPathDepth; + int* intersectionPathDepth; + int memIntersectionPathDepth; - spqr_node *intersectionPathParent; - int memIntersectionPathParent; + spqr_node* intersectionPathParent; + int memIntersectionPathParent; - MergeTreeCallData *mergeTreeCallData; - int memMergeTreeCallData; + MergeTreeCallData* mergeTreeCallData; + int memMergeTreeCallData; }; -typedef struct { - spqr_member member; - spqr_node head; - spqr_node tail; - spqr_arc representative; - SCIP_Bool reversed; +typedef struct +{ + spqr_member member; + spqr_node head; + spqr_node tail; + spqr_arc representative; + SCIP_Bool reversed; } NewRowInformation; -static NewRowInformation emptyNewRowInformation(void){ - NewRowInformation information; - information.member = SPQR_INVALID_MEMBER; - information.head = SPQR_INVALID_NODE; - information.tail = SPQR_INVALID_NODE; - information.representative = SPQR_INVALID_ARC; - information.reversed = FALSE; - return information; +static NewRowInformation emptyNewRowInformation(void) +{ + NewRowInformation information; + information.member = SPQR_INVALID_MEMBER; + information.head = SPQR_INVALID_NODE; + information.tail = SPQR_INVALID_NODE; + information.representative = SPQR_INVALID_ARC; + information.reversed = FALSE; + return information; } /** * Saves the information of the current row and partitions it based on whether or not the given columns are * already part of the decomposition. */ -static SCIP_RETCODE newRowUpdateRowInformation(const SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - const spqr_row row, const spqr_col * columns, const double * columnValues, - const size_t numColumns) -{ - newRow->newRowIndex = row; - - newRow->numDecompositionColumnArcs = 0; - newRow->numColumnArcs = 0; - - for (size_t i = 0; i < numColumns; ++i) { - spqr_arc columnArc = getDecompositionColumnArc(dec, columns[i]); - SCIP_Bool reversed = columnValues[i] < 0.0; - if(SPQRarcIsValid(columnArc)){ //If the arc is the current decomposition: save it in the array - if(newRow->numDecompositionColumnArcs == newRow->memDecompositionColumnArcs){ - int newNumArcs = newRow->memDecompositionColumnArcs == 0 ? 8 : 2*newRow->memDecompositionColumnArcs; - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->decompositionColumnArcs, - newRow->memDecompositionColumnArcs, newNumArcs)); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->decompositionColumnArcReversed, - newRow->memDecompositionColumnArcs, newNumArcs)); - newRow->memDecompositionColumnArcs = newNumArcs; - } - newRow->decompositionColumnArcs[newRow->numDecompositionColumnArcs] = columnArc; - newRow->decompositionColumnArcReversed[newRow->numDecompositionColumnArcs] = reversed; - ++newRow->numDecompositionColumnArcs; - }else{ - //Not in the decomposition: add it to the set of arcs which are newly added with this row. - if(newRow->numColumnArcs == newRow->memColumnArcs){ - int newNumArcs = newRow->memColumnArcs == 0 ? 8 : 2*newRow->memColumnArcs; - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->newColumnArcs, - newRow->memColumnArcs,newNumArcs)); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->newColumnReversed, - newRow->memColumnArcs,newNumArcs)); - newRow->memColumnArcs = newNumArcs; - - } - newRow->newColumnArcs[newRow->numColumnArcs] = columns[i]; - newRow->newColumnReversed[newRow->numColumnArcs] = reversed; - newRow->numColumnArcs++; - } - } - - return SCIP_OKAY; +static SCIP_RETCODE newRowUpdateRowInformation( + const SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const spqr_row row, + const spqr_col* columns, + const double* columnValues, + const size_t numColumns +) +{ + newRow->newRowIndex = row; + + newRow->numDecompositionColumnArcs = 0; + newRow->numColumnArcs = 0; + + for( size_t i = 0; i < numColumns; ++i ) + { + spqr_arc columnArc = getDecompositionColumnArc(dec, columns[i]); + SCIP_Bool reversed = columnValues[i] < 0.0; + if( SPQRarcIsValid(columnArc)) + {//If the arc is the current decomposition: save it in the array + if( newRow->numDecompositionColumnArcs == newRow->memDecompositionColumnArcs ) + { + int newNumArcs = newRow->memDecompositionColumnArcs == 0 ? 8 : 2 * newRow->memDecompositionColumnArcs; + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->decompositionColumnArcs, + newRow->memDecompositionColumnArcs, newNumArcs)); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->decompositionColumnArcReversed, + newRow->memDecompositionColumnArcs, newNumArcs)); + newRow->memDecompositionColumnArcs = newNumArcs; + } + newRow->decompositionColumnArcs[newRow->numDecompositionColumnArcs] = columnArc; + newRow->decompositionColumnArcReversed[newRow->numDecompositionColumnArcs] = reversed; + ++newRow->numDecompositionColumnArcs; + } else + { + //Not in the decomposition: add it to the set of arcs which are newly added with this row. + if( newRow->numColumnArcs == newRow->memColumnArcs ) + { + int newNumArcs = newRow->memColumnArcs == 0 ? 8 : 2 * newRow->memColumnArcs; + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->newColumnArcs, + newRow->memColumnArcs, newNumArcs)); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->newColumnReversed, + newRow->memColumnArcs, newNumArcs)); + newRow->memColumnArcs = newNumArcs; + } + newRow->newColumnArcs[newRow->numColumnArcs] = columns[i]; + newRow->newColumnReversed[newRow->numColumnArcs] = reversed; + newRow->numColumnArcs++; + } + } + + return SCIP_OKAY; } /** @@ -4601,96 +5883,107 @@ static SCIP_RETCODE newRowUpdateRowInformation(const SCIP_NETWORKDECOMP *dec, SC * @param member * @return */ -static reduced_member_id createRowReducedMembersToRoot(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION * newRow, const spqr_member firstMember){ - assert(SPQRmemberIsValid(firstMember)); - - CreateReducedMembersCallstack * callstack = newRow->createReducedMembersCallstack; - callstack[0].member = firstMember; - int callDepth = 0; - - while(callDepth >= 0){ - spqr_member member = callstack[callDepth].member; - reduced_member_id reducedMember = newRow->memberInformation[member].reducedMember; - - SCIP_Bool reducedValid = reducedMemberIsValid(reducedMember); - if(!reducedValid) { - //reduced member was not yet created; we create it - reducedMember = newRow->numReducedMembers; - - SPQRRowReducedMember *reducedMemberData = &newRow->reducedMembers[reducedMember]; - ++newRow->numReducedMembers; - - reducedMemberData->member = member; - reducedMemberData->numChildren = 0; - reducedMemberData->numCutArcs = 0; - reducedMemberData->firstCutArc = INVALID_CUT_ARC; - reducedMemberData->type = TYPE_UNDETERMINED; - reducedMemberData->numPropagatedChildren = 0; - reducedMemberData->articulationArc = SPQR_INVALID_ARC; - reducedMemberData->splitNode = SPQR_INVALID_NODE; - reducedMemberData->otherNode = SPQR_INVALID_NODE; - reducedMemberData->splitArc = SPQR_INVALID_ARC; - reducedMemberData->splitHead = FALSE; - reducedMemberData->allHaveCommonNode = FALSE; - reducedMemberData->otherNodeSplit = FALSE; - reducedMemberData->willBeReversed = FALSE; - reducedMemberData->coloredNode = SPQR_INVALID_NODE; - - newRow->memberInformation[member].reducedMember = reducedMember; - assert(memberIsRepresentative(dec, member)); - spqr_member parentMember = findMemberParent(dec, member); - - if (SPQRmemberIsValid(parentMember)) { - //recursive call to parent member - ++callDepth; - assert(callDepth < newRow->memCreateReducedMembersCallstack); - callstack[callDepth].member = parentMember; - continue; - - } else { - //we found a new reduced decomposition component - - reducedMemberData->parent = INVALID_REDUCED_MEMBER; - reducedMemberData->depth = 0; - reducedMemberData->rootMember = member; - - assert(newRow->numReducedComponents < newRow->memReducedComponents); - newRow->reducedComponents[newRow->numReducedComponents].root = reducedMember; - ++newRow->numReducedComponents; - } - } - if(reducedValid){ - assert(reducedMember < newRow->numReducedMembers); - //Reduced member was already created in earlier call - //update the depth of the root if appropriate - reduced_member_id * depthMinimizer = &newRow->memberInformation[newRow->reducedMembers[reducedMember].rootMember].rootDepthMinimizer; - if(reducedMemberIsInvalid(*depthMinimizer) || - newRow->reducedMembers[reducedMember].depth < newRow->reducedMembers[*depthMinimizer].depth){ - *depthMinimizer = reducedMember; - } - } - while(TRUE){ - --callDepth; - if(callDepth < 0 ) break; - spqr_member parentMember = callstack[callDepth + 1].member; - reduced_member_id parentReducedMember = newRow->memberInformation[parentMember].reducedMember; - spqr_member currentMember = callstack[callDepth].member; - reduced_member_id currentReducedMember = newRow->memberInformation[currentMember].reducedMember; +static reduced_member_id createRowReducedMembersToRoot( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const spqr_member firstMember +) +{ + assert(SPQRmemberIsValid(firstMember)); + + CreateReducedMembersCallstack* callstack = newRow->createReducedMembersCallstack; + callstack[0].member = firstMember; + int callDepth = 0; + + while( callDepth >= 0 ) + { + spqr_member member = callstack[callDepth].member; + reduced_member_id reducedMember = newRow->memberInformation[member].reducedMember; + + SCIP_Bool reducedValid = reducedMemberIsValid(reducedMember); + if( !reducedValid ) + { + //reduced member was not yet created; we create it + reducedMember = newRow->numReducedMembers; + + SPQRRowReducedMember* reducedMemberData = &newRow->reducedMembers[reducedMember]; + ++newRow->numReducedMembers; + + reducedMemberData->member = member; + reducedMemberData->numChildren = 0; + reducedMemberData->numCutArcs = 0; + reducedMemberData->firstCutArc = INVALID_CUT_ARC; + reducedMemberData->type = TYPE_UNDETERMINED; + reducedMemberData->numPropagatedChildren = 0; + reducedMemberData->articulationArc = SPQR_INVALID_ARC; + reducedMemberData->splitNode = SPQR_INVALID_NODE; + reducedMemberData->otherNode = SPQR_INVALID_NODE; + reducedMemberData->splitArc = SPQR_INVALID_ARC; + reducedMemberData->splitHead = FALSE; + reducedMemberData->allHaveCommonNode = FALSE; + reducedMemberData->otherNodeSplit = FALSE; + reducedMemberData->willBeReversed = FALSE; + reducedMemberData->coloredNode = SPQR_INVALID_NODE; + + newRow->memberInformation[member].reducedMember = reducedMember; + assert(memberIsRepresentative(dec, member)); + spqr_member parentMember = findMemberParent(dec, member); + + if( SPQRmemberIsValid(parentMember)) + { + //recursive call to parent member + ++callDepth; + assert(callDepth < newRow->memCreateReducedMembersCallstack); + callstack[callDepth].member = parentMember; + continue; - SPQRRowReducedMember *parentReducedMemberData = &newRow->reducedMembers[parentReducedMember]; - SPQRRowReducedMember *reducedMemberData = &newRow->reducedMembers[currentReducedMember]; + } else + { + //we found a new reduced decomposition component + + reducedMemberData->parent = INVALID_REDUCED_MEMBER; + reducedMemberData->depth = 0; + reducedMemberData->rootMember = member; + + assert(newRow->numReducedComponents < newRow->memReducedComponents); + newRow->reducedComponents[newRow->numReducedComponents].root = reducedMember; + ++newRow->numReducedComponents; + } + } + if( reducedValid ) + { + assert(reducedMember < newRow->numReducedMembers); + //Reduced member was already created in earlier call + //update the depth of the root if appropriate + reduced_member_id* depthMinimizer = &newRow->memberInformation[newRow->reducedMembers[reducedMember].rootMember].rootDepthMinimizer; + if( reducedMemberIsInvalid(*depthMinimizer) || + newRow->reducedMembers[reducedMember].depth < newRow->reducedMembers[*depthMinimizer].depth ) + { + *depthMinimizer = reducedMember; + } + } + while( TRUE ) + { + --callDepth; + if( callDepth < 0 ) break; + spqr_member parentMember = callstack[callDepth + 1].member; + reduced_member_id parentReducedMember = newRow->memberInformation[parentMember].reducedMember; + spqr_member currentMember = callstack[callDepth].member; + reduced_member_id currentReducedMember = newRow->memberInformation[currentMember].reducedMember; - reducedMemberData->parent = parentReducedMember; - reducedMemberData->depth = parentReducedMemberData->depth + 1; - reducedMemberData->rootMember = parentReducedMemberData->rootMember; + SPQRRowReducedMember* parentReducedMemberData = &newRow->reducedMembers[parentReducedMember]; + SPQRRowReducedMember* reducedMemberData = &newRow->reducedMembers[currentReducedMember]; - newRow->reducedMembers[parentReducedMember].numChildren++; - } + reducedMemberData->parent = parentReducedMember; + reducedMemberData->depth = parentReducedMemberData->depth + 1; + reducedMemberData->rootMember = parentReducedMemberData->rootMember; - } + newRow->reducedMembers[parentReducedMember].numChildren++; + } + } - reduced_member_id returnedMember = newRow->memberInformation[callstack[0].member].reducedMember; - return returnedMember; + reduced_member_id returnedMember = newRow->memberInformation[callstack[0].member].reducedMember; + return returnedMember; } @@ -4698,122 +5991,150 @@ static reduced_member_id createRowReducedMembersToRoot(SCIP_NETWORKDECOMP *dec, * Construct a smaller sub tree of the decomposition on which the cut arcs lie. * @return */ -static SCIP_RETCODE constructRowReducedDecomposition(SCIP_NETWORKDECOMP* dec, SCIP_NETWORKROWADDITION* newRow){ +static SCIP_RETCODE constructRowReducedDecomposition( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow +) +{ #ifndef NDEBUG - for (int i = 0; i < newRow->memMemberInformation; ++i) { - assert(reducedMemberIsInvalid(newRow->memberInformation[i].reducedMember)); - } + for( int i = 0; i < newRow->memMemberInformation; ++i ) + { + assert(reducedMemberIsInvalid(newRow->memberInformation[i].reducedMember)); + } #endif - newRow->numReducedComponents = 0; - newRow->numReducedMembers = 0; - if(newRow->numDecompositionColumnArcs == 0){ //Early return in case the reduced decomposition will be empty - return SCIP_OKAY; - } - assert(newRow->numReducedMembers == 0); - assert(newRow->numReducedComponents == 0); - - int newSize = largestMemberID(dec); //Is this sufficient? - if(newSize > newRow->memReducedMembers){ - int updatedSize = max(2*newRow->memReducedMembers,newSize); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->reducedMembers, newRow->memReducedMembers, updatedSize)); - newRow->memReducedMembers = updatedSize; - } - if(newSize > newRow->memMemberInformation){ - int updatedSize = max(2*newRow->memMemberInformation,newSize); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->memberInformation,newRow->memMemberInformation, updatedSize)); - for (int i = newRow->memMemberInformation; i < updatedSize; ++i) { - newRow->memberInformation[i].reducedMember = INVALID_REDUCED_MEMBER; - newRow->memberInformation[i].rootDepthMinimizer = INVALID_REDUCED_MEMBER; - } - newRow->memMemberInformation = updatedSize; - - } - - int numComponents = numConnectedComponents(dec); - if(numComponents > newRow->memReducedComponents){ - int updatedSize = max(2*newRow->memReducedComponents,numComponents); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->reducedComponents,newRow->memReducedComponents,updatedSize)); - newRow->memReducedComponents = updatedSize; - } - - int numMembers = getNumMembers(dec); - if(newRow->memCreateReducedMembersCallstack < numMembers){ - int updatedSize = max(2*newRow->memCreateReducedMembersCallstack,numMembers); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->createReducedMembersCallstack, newRow->memCreateReducedMembersCallstack,updatedSize)); - newRow->memCreateReducedMembersCallstack = updatedSize; - } - - //Create the reduced members (recursively) - for (int i = 0; i < newRow->numDecompositionColumnArcs; ++i) { - assert(i < newRow->memDecompositionColumnArcs); - spqr_arc arc = newRow->decompositionColumnArcs[i]; - spqr_member arcMember = findArcMember(dec, arc); - reduced_member_id reducedMember = createRowReducedMembersToRoot(dec,newRow,arcMember); - reduced_member_id* depthMinimizer = &newRow->memberInformation[newRow->reducedMembers[reducedMember].rootMember].rootDepthMinimizer; - if(reducedMemberIsInvalid(*depthMinimizer)){ - *depthMinimizer = reducedMember; - } - } - - //Set the reduced roots according to the root depth minimizers - for (int i = 0; i < newRow->numReducedComponents; ++i) { - SPQRRowReducedComponent * component = &newRow->reducedComponents[i]; - spqr_member rootMember = newRow->reducedMembers[component->root].member; - reduced_member_id reducedMinimizer = newRow->memberInformation[rootMember].rootDepthMinimizer; - component->rootDepth = newRow->reducedMembers[reducedMinimizer].depth; - component->root = reducedMinimizer; - - //This simplifies code further down which does not need to be component-aware; just pretend that the reduced member is the new root. - newRow->reducedMembers[component->root].parent = INVALID_REDUCED_MEMBER; - assert(memberIsRepresentative(dec,rootMember)); - } - - //update the children array - int numTotalChildren = 0; - for (int i = 0; i < newRow->numReducedMembers; ++i) { - SPQRRowReducedMember * reducedMember = &newRow->reducedMembers[i]; - reduced_member_id minimizer = newRow->memberInformation[reducedMember->rootMember].rootDepthMinimizer; - if(reducedMember->depth >= newRow->reducedMembers[minimizer].depth){ - reducedMember->firstChild = numTotalChildren; - numTotalChildren += reducedMember->numChildren; - reducedMember->numChildren = 0; - } - } - - if(newRow->memChildrenStorage < numTotalChildren){ - int newMemSize = max(newRow->memChildrenStorage*2, numTotalChildren); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->childrenStorage,(size_t) newRow->memChildrenStorage,newMemSize)); - newRow->memChildrenStorage = newMemSize; - } - newRow->numChildrenStorage = numTotalChildren; - - //Fill up the children array` - for(reduced_member_id reducedMember = 0; reducedMember < newRow->numReducedMembers; ++reducedMember){ - SPQRRowReducedMember * reducedMemberData = &newRow->reducedMembers[reducedMember]; - if(reducedMemberData->depth <= newRow->reducedMembers[newRow->memberInformation[reducedMemberData->rootMember].rootDepthMinimizer].depth){ - continue; - } - spqr_member parentMember = findMemberParent(dec, reducedMemberData->member); - reduced_member_id parentReducedMember = SPQRmemberIsValid(parentMember) ? newRow->memberInformation[parentMember].reducedMember : INVALID_REDUCED_MEMBER; - if(reducedMemberIsValid(parentReducedMember)){ - SPQRRowReducedMember * parentReducedMemberData = &newRow->reducedMembers[parentReducedMember]; - newRow->childrenStorage[parentReducedMemberData->firstChild + parentReducedMemberData->numChildren] = reducedMember; - ++parentReducedMemberData->numChildren; - } - } - - //Clean up the root depth minimizers. - for (int i = 0; i < newRow->numReducedMembers; ++i) { - SPQRRowReducedMember * reducedMember = &newRow->reducedMembers[i]; - assert(reducedMember); - spqr_member rootMember = reducedMember->rootMember; - assert(rootMember >= 0); - assert(rootMember < dec->memMembers); - newRow->memberInformation[rootMember].rootDepthMinimizer = INVALID_REDUCED_MEMBER; - } - - return SCIP_OKAY; + newRow->numReducedComponents = 0; + newRow->numReducedMembers = 0; + if( newRow->numDecompositionColumnArcs == 0 ) + {//Early return in case the reduced decomposition will be empty + return SCIP_OKAY; + } + assert(newRow->numReducedMembers == 0); + assert(newRow->numReducedComponents == 0); + + int newSize = largestMemberID(dec);//Is this sufficient? + if( newSize > newRow->memReducedMembers ) + { + int updatedSize = max(2 * newRow->memReducedMembers, newSize); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->reducedMembers, newRow->memReducedMembers, updatedSize)); + newRow->memReducedMembers = updatedSize; + } + if( newSize > newRow->memMemberInformation ) + { + int updatedSize = max(2 * newRow->memMemberInformation, newSize); + SCIP_CALL( + SCIPreallocBlockMemoryArray(dec->env, &newRow->memberInformation, newRow->memMemberInformation, updatedSize)); + for( int i = newRow->memMemberInformation; i < updatedSize; ++i ) + { + newRow->memberInformation[i].reducedMember = INVALID_REDUCED_MEMBER; + newRow->memberInformation[i].rootDepthMinimizer = INVALID_REDUCED_MEMBER; + } + newRow->memMemberInformation = updatedSize; + } + + int numComponents = numConnectedComponents(dec); + if( numComponents > newRow->memReducedComponents ) + { + int updatedSize = max(2 * newRow->memReducedComponents, numComponents); + SCIP_CALL( + SCIPreallocBlockMemoryArray(dec->env, &newRow->reducedComponents, newRow->memReducedComponents, updatedSize)); + newRow->memReducedComponents = updatedSize; + } + + int numMembers = getNumMembers(dec); + if( newRow->memCreateReducedMembersCallstack < numMembers ) + { + int updatedSize = max(2 * newRow->memCreateReducedMembersCallstack, numMembers); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->createReducedMembersCallstack, + newRow->memCreateReducedMembersCallstack, updatedSize)); + newRow->memCreateReducedMembersCallstack = updatedSize; + } + + //Create the reduced members (recursively) + for( int i = 0; i < newRow->numDecompositionColumnArcs; ++i ) + { + assert(i < newRow->memDecompositionColumnArcs); + spqr_arc arc = newRow->decompositionColumnArcs[i]; + spqr_member arcMember = findArcMember(dec, arc); + reduced_member_id reducedMember = createRowReducedMembersToRoot(dec, newRow, arcMember); + reduced_member_id* depthMinimizer = &newRow->memberInformation[newRow->reducedMembers[reducedMember].rootMember].rootDepthMinimizer; + if( reducedMemberIsInvalid(*depthMinimizer)) + { + *depthMinimizer = reducedMember; + } + } + + //Set the reduced roots according to the root depth minimizers + for( int i = 0; i < newRow->numReducedComponents; ++i ) + { + SPQRRowReducedComponent* component = &newRow->reducedComponents[i]; + spqr_member rootMember = newRow->reducedMembers[component->root].member; + reduced_member_id reducedMinimizer = newRow->memberInformation[rootMember].rootDepthMinimizer; + component->rootDepth = newRow->reducedMembers[reducedMinimizer].depth; + component->root = reducedMinimizer; + + //This simplifies code further down which does not need to be component-aware; just pretend that the reduced member is the new root. + newRow->reducedMembers[component->root].parent = INVALID_REDUCED_MEMBER; + assert(memberIsRepresentative(dec, rootMember)); + } + + //update the children array + int numTotalChildren = 0; + for( int i = 0; i < newRow->numReducedMembers; ++i ) + { + SPQRRowReducedMember* reducedMember = &newRow->reducedMembers[i]; + reduced_member_id minimizer = newRow->memberInformation[reducedMember->rootMember].rootDepthMinimizer; + if( reducedMember->depth >= newRow->reducedMembers[minimizer].depth ) + { + reducedMember->firstChild = numTotalChildren; + numTotalChildren += reducedMember->numChildren; + reducedMember->numChildren = 0; + } + } + + if( newRow->memChildrenStorage < numTotalChildren ) + { + int newMemSize = max(newRow->memChildrenStorage * 2, numTotalChildren); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->childrenStorage, (size_t) newRow->memChildrenStorage, + newMemSize)); + newRow->memChildrenStorage = newMemSize; + } + newRow->numChildrenStorage = numTotalChildren; + + //Fill up the children array` + for( reduced_member_id reducedMember = 0; reducedMember < newRow->numReducedMembers; ++reducedMember ) + { + SPQRRowReducedMember* reducedMemberData = &newRow->reducedMembers[reducedMember]; + if( reducedMemberData->depth <= + newRow->reducedMembers[newRow->memberInformation[reducedMemberData->rootMember].rootDepthMinimizer].depth ) + { + continue; + } + spqr_member parentMember = findMemberParent(dec, reducedMemberData->member); + reduced_member_id parentReducedMember = SPQRmemberIsValid(parentMember) + ? newRow->memberInformation[parentMember].reducedMember + : INVALID_REDUCED_MEMBER; + if( reducedMemberIsValid(parentReducedMember)) + { + SPQRRowReducedMember* parentReducedMemberData = &newRow->reducedMembers[parentReducedMember]; + newRow->childrenStorage[parentReducedMemberData->firstChild + + parentReducedMemberData->numChildren] = reducedMember; + ++parentReducedMemberData->numChildren; + } + } + + //Clean up the root depth minimizers. + for( int i = 0; i < newRow->numReducedMembers; ++i ) + { + SPQRRowReducedMember* reducedMember = &newRow->reducedMembers[i]; + assert(reducedMember); + spqr_member rootMember = reducedMember->rootMember; + assert(rootMember >= 0); + assert(rootMember < dec->memMembers); + newRow->memberInformation[rootMember].rootDepthMinimizer = INVALID_REDUCED_MEMBER; + } + + return SCIP_OKAY; } @@ -4823,86 +6144,103 @@ static SCIP_RETCODE constructRowReducedDecomposition(SCIP_NETWORKDECOMP* dec, SC * @param arc * @param reducedMember */ -static void createCutArc(SCIP_NETWORKDECOMP * dec, - SCIP_NETWORKROWADDITION* newRow, const spqr_arc arc, const reduced_member_id reducedMember, - SCIP_Bool reversed){ - cut_arc_id cut_arc = newRow->numCutArcs; +static void createCutArc( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const spqr_arc arc, + const reduced_member_id reducedMember, + SCIP_Bool reversed +) +{ + cut_arc_id cut_arc = newRow->numCutArcs; - CutArcListNode * listNode = &newRow->cutArcs[cut_arc]; - listNode->arc = arc; + CutArcListNode* listNode = &newRow->cutArcs[cut_arc]; + listNode->arc = arc; - listNode->nextMember = newRow->reducedMembers[reducedMember].firstCutArc; - newRow->reducedMembers[reducedMember].firstCutArc = cut_arc; + listNode->nextMember = newRow->reducedMembers[reducedMember].firstCutArc; + newRow->reducedMembers[reducedMember].firstCutArc = cut_arc; - listNode->nextOverall = newRow->firstOverallCutArc; - newRow->firstOverallCutArc = cut_arc; + listNode->nextOverall = newRow->firstOverallCutArc; + newRow->firstOverallCutArc = cut_arc; - newRow->numCutArcs++; - newRow->reducedMembers[reducedMember].numCutArcs++; - assert(newRow->numCutArcs <= newRow->memCutArcs); + newRow->numCutArcs++; + newRow->reducedMembers[reducedMember].numCutArcs++; + assert(newRow->numCutArcs <= newRow->memCutArcs); - assert(arc < newRow->memIsArcCut); - newRow->isArcCut[arc] = TRUE; - newRow->isArcCutReversed[arc] = reversed; + assert(arc < newRow->memIsArcCut); + newRow->isArcCut[arc] = TRUE; + newRow->isArcCutReversed[arc] = reversed; - assert(memberIsRepresentative(dec,newRow->reducedMembers[reducedMember].member)); - if(getMemberType(dec,newRow->reducedMembers[reducedMember].member) == SPQR_MEMBERTYPE_RIGID){ + assert(memberIsRepresentative(dec, newRow->reducedMembers[reducedMember].member)); + if( getMemberType(dec, newRow->reducedMembers[reducedMember].member) == SPQR_MEMBERTYPE_RIGID ) + { - listNode->arcHead = findEffectiveArcHead(dec,arc); - listNode->arcTail = findEffectiveArcTail(dec,arc); - if(reversed){ - swap_ints(&listNode->arcHead,&listNode->arcTail); - } - assert(SPQRnodeIsValid(listNode->arcHead) && SPQRnodeIsValid(listNode->arcTail)); - }else{ - listNode->arcHead = SPQR_INVALID_NODE; - listNode->arcTail = SPQR_INVALID_NODE; - } + listNode->arcHead = findEffectiveArcHead(dec, arc); + listNode->arcTail = findEffectiveArcTail(dec, arc); + if( reversed ) + { + swap_ints(&listNode->arcHead, &listNode->arcTail); + } + assert(SPQRnodeIsValid(listNode->arcHead) && SPQRnodeIsValid(listNode->arcTail)); + } else + { + listNode->arcHead = SPQR_INVALID_NODE; + listNode->arcTail = SPQR_INVALID_NODE; + } - listNode->arcReversed = reversed; + listNode->arcReversed = reversed; } /** * Creates all cut arcs within the decomposition for the new row. * Note this preallocates memory for cut arcs which may be created by propagation. */ -static SCIP_RETCODE createReducedDecompositionCutArcs(SCIP_NETWORKDECOMP* dec, SCIP_NETWORKROWADDITION* newRow){ - //Allocate memory for cut arcs - spqr_arc maxArcID = largestArcID(dec); - if(maxArcID > newRow->memIsArcCut){ - int newSize = max(maxArcID,2*newRow->memIsArcCut); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->isArcCut,newRow->memIsArcCut, newSize)); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->isArcCutReversed,newRow->memIsArcCut,newSize)); - for (int i = newRow->memIsArcCut; i < newSize ; ++i) { - newRow->isArcCut[i] = FALSE; - newRow->isArcCutReversed[i] = FALSE; - } - newRow->memIsArcCut = newSize; - } +static SCIP_RETCODE createReducedDecompositionCutArcs( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow +) +{ + //Allocate memory for cut arcs + spqr_arc maxArcID = largestArcID(dec); + if( maxArcID > newRow->memIsArcCut ) + { + int newSize = max(maxArcID, 2 * newRow->memIsArcCut); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->isArcCut, newRow->memIsArcCut, newSize)); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->isArcCutReversed, newRow->memIsArcCut, newSize)); + for( int i = newRow->memIsArcCut; i < newSize; ++i ) + { + newRow->isArcCut[i] = FALSE; + newRow->isArcCutReversed[i] = FALSE; + } + newRow->memIsArcCut = newSize; + } #ifndef NDEBUG - for (int i = 0; i < newRow->memIsArcCut; ++i) { - assert(!newRow->isArcCut[i]); - assert(!newRow->isArcCutReversed[i]); - } + for( int i = 0; i < newRow->memIsArcCut; ++i ) + { + assert(!newRow->isArcCut[i]); + assert(!newRow->isArcCutReversed[i]); + } #endif - int numNeededArcs = newRow->numDecompositionColumnArcs*4; - if(numNeededArcs > newRow->memCutArcs){ - int newSize = max(newRow->memCutArcs*2, numNeededArcs); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->cutArcs,newRow->memCutArcs, newSize)); - newRow->memCutArcs = newSize; - } - newRow->numCutArcs = 0; - newRow->firstOverallCutArc = INVALID_CUT_ARC; - for (int i = 0; i < newRow->numDecompositionColumnArcs; ++i) { - spqr_arc arc = newRow->decompositionColumnArcs[i]; - spqr_member member = findArcMember(dec, arc); - reduced_member_id reduced_member = newRow->memberInformation[member].reducedMember; - assert(reducedMemberIsValid(reduced_member)); - createCutArc(dec,newRow,arc,reduced_member,newRow->decompositionColumnArcReversed[i]); - } - - return SCIP_OKAY; + int numNeededArcs = newRow->numDecompositionColumnArcs * 4; + if( numNeededArcs > newRow->memCutArcs ) + { + int newSize = max(newRow->memCutArcs * 2, numNeededArcs); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->cutArcs, newRow->memCutArcs, newSize)); + newRow->memCutArcs = newSize; + } + newRow->numCutArcs = 0; + newRow->firstOverallCutArc = INVALID_CUT_ARC; + for( int i = 0; i < newRow->numDecompositionColumnArcs; ++i ) + { + spqr_arc arc = newRow->decompositionColumnArcs[i]; + spqr_member member = findArcMember(dec, arc); + reduced_member_id reduced_member = newRow->memberInformation[member].reducedMember; + assert(reducedMemberIsValid(reduced_member)); + createCutArc(dec, newRow, arc, reduced_member, newRow->decompositionColumnArcReversed[i]); + } + + return SCIP_OKAY; } /** @@ -4910,3093 +6248,3870 @@ static SCIP_RETCODE createReducedDecompositionCutArcs(SCIP_NETWORKDECOMP* dec, S * This is used in propagation to ensure propagation is only checked for components which have at most one neighbour * which is not propagated. */ -static SCIP_RETCODE determineLeafReducedMembers(const SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow) { - if (newRow->numDecompositionColumnArcs > newRow->memLeafMembers) { - int updatedSize = max(newRow->numDecompositionColumnArcs, 2 * newRow->memLeafMembers); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->leafMembers,newRow->memLeafMembers,updatedSize)); - newRow->memLeafMembers = updatedSize; - } - newRow->numLeafMembers = 0; - - for (reduced_member_id reducedMember = 0; reducedMember < newRow->numReducedMembers; ++reducedMember) { - if (newRow->reducedMembers[reducedMember].numChildren == 0) { - newRow->leafMembers[newRow->numLeafMembers] = reducedMember; - ++newRow->numLeafMembers; - } - } - return SCIP_OKAY; +static SCIP_RETCODE determineLeafReducedMembers( + const SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow +) +{ + if( newRow->numDecompositionColumnArcs > newRow->memLeafMembers ) + { + int updatedSize = max(newRow->numDecompositionColumnArcs, 2 * newRow->memLeafMembers); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->leafMembers, newRow->memLeafMembers, updatedSize)); + newRow->memLeafMembers = updatedSize; + } + newRow->numLeafMembers = 0; + + for( reduced_member_id reducedMember = 0; reducedMember < newRow->numReducedMembers; ++reducedMember ) + { + if( newRow->reducedMembers[reducedMember].numChildren == 0 ) + { + newRow->leafMembers[newRow->numLeafMembers] = reducedMember; + ++newRow->numLeafMembers; + } + } + return SCIP_OKAY; } /** * Preallocates memory arrays necessary for searching rigid components. */ -static SCIP_RETCODE allocateRigidSearchMemory(const SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow){ - int totalNumNodes = getNumNodes(dec); - int maxNumNodes = 2*dec->numArcs; - if(maxNumNodes > newRow->memNodeColors){ - int newSize = max(2*newRow->memNodeColors,maxNumNodes); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->nodeColors,newRow->memNodeColors, newSize)); - for (int i = newRow->memNodeColors; i < newSize; ++i) { - newRow->nodeColors[i] = UNCOLORED; - } - newRow->memNodeColors = newSize; - } - - if(totalNumNodes > newRow->memArticulationNodes){ - int newSize = max(2*newRow->memArticulationNodes,totalNumNodes); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->articulationNodes,newRow->memArticulationNodes, newSize)); - newRow->memArticulationNodes = newSize; - } - if(totalNumNodes > newRow->memNodeSearchInfo){ - int newSize = max(2*newRow->memNodeSearchInfo,totalNumNodes); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->articulationNodeSearchInfo,newRow->memNodeSearchInfo, newSize)); - newRow->memNodeSearchInfo = newSize; - } - if(totalNumNodes > newRow->memCrossingPathCount){ - int newSize = max(2*newRow->memCrossingPathCount,totalNumNodes); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->crossingPathCount,newRow->memCrossingPathCount, newSize)); - newRow->memCrossingPathCount = newSize; - } - - //TODO: see if tradeoff for performance bound by checking max # of nodes of rigid is worth it to reduce size - //of the following allocations - int largestID = largestNodeID(dec); //TODO: only update the stack sizes of the following when needed? The preallocation might be causing performance problems - if(largestID > newRow->memIntersectionDFSData){ - int newSize = max(2*newRow->memIntersectionDFSData,largestID); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->intersectionDFSData,newRow->memIntersectionDFSData, newSize)); - newRow->memIntersectionDFSData = newSize; - } - if(largestID > newRow->memColorDFSData){ - int newSize = max(2*newRow->memColorDFSData, largestID); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->colorDFSData,newRow->memColorDFSData, newSize)); - newRow->memColorDFSData = newSize; - } - if(largestID > newRow->memArtDFSData){ - int newSize = max(2*newRow->memArtDFSData,largestID); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->artDFSData,newRow->memArtDFSData, newSize)); - newRow->memArtDFSData = newSize; - } - - for (int i = 0; i < newRow->memIntersectionPathDepth; ++i) { - newRow->intersectionPathDepth[i] = -1; - } - - if(largestID > newRow->memIntersectionPathDepth){ - int newSize = max(2*newRow->memIntersectionPathDepth,largestID); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->intersectionPathDepth,newRow->memIntersectionPathDepth, newSize)); - for (int i = newRow->memIntersectionPathDepth; i < newSize; ++i) { - newRow->intersectionPathDepth[i] = -1; - } - newRow->memIntersectionPathDepth = newSize; - } - for (int i = 0; i < newRow->memIntersectionPathParent; ++i) { - newRow->intersectionPathParent[i] = SPQR_INVALID_NODE; - } - if(largestID > newRow->memIntersectionPathParent){ - int newSize = max(2*newRow->memIntersectionPathParent,largestID); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->intersectionPathParent,newRow->memIntersectionPathParent, newSize)); - for (int i = newRow->memIntersectionPathParent; i intersectionPathParent[i] = SPQR_INVALID_NODE; - } - newRow->memIntersectionPathParent = newSize; - } - - return SCIP_OKAY; -} -static void zeroOutColors(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, const spqr_node firstRemoveNode){ - assert(firstRemoveNode < newRow->memNodeColors); - - newRow->nodeColors[firstRemoveNode] = UNCOLORED; - ColorDFSCallData * data = newRow->colorDFSData; - if(newRow->colorDFSData == NULL){ - return; - } - - data[0].node = firstRemoveNode; - data[0].arc = getFirstNodeArc(dec,firstRemoveNode); - if(SPQRarcIsInvalid(data[0].arc)){ - return; - } - - int depth = 0; - - while(depth >= 0){ - assert(depth < newRow->memColorDFSData); - ColorDFSCallData * callData = &data[depth]; - spqr_node head = findArcHead(dec, callData->arc); - spqr_node tail = findArcTail(dec, callData->arc); - spqr_node otherNode = callData->node == head ? tail : head; - assert(otherNode < newRow->memNodeColors); - if(newRow->nodeColors[otherNode] != UNCOLORED){ - callData->arc = getNextNodeArc(dec,callData->arc,callData->node); - - newRow->nodeColors[otherNode] = UNCOLORED; - ++depth; - data[depth].node = otherNode; - data[depth].arc = getFirstNodeArc(dec,otherNode); - continue; - } - - callData->arc = getNextNodeArc(dec,callData->arc,callData->node); - while(depth >= 0 && data[depth].arc == getFirstNodeArc(dec,data[depth].node)){ - --depth; - } - } - - -} -static void cleanUpPreviousIteration(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKROWADDITION * newRow){ - //zero out coloring information from previous check - for (int i = 0; i < newRow->numReducedMembers; ++i) { - if (SPQRnodeIsValid(newRow->reducedMembers[i].coloredNode)) { - zeroOutColors(dec, newRow, newRow->reducedMembers[i].coloredNode); - } - } +static SCIP_RETCODE allocateRigidSearchMemory( + const SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow +) +{ + int totalNumNodes = getNumNodes(dec); + int maxNumNodes = 2 * dec->numArcs; + if( maxNumNodes > newRow->memNodeColors ) + { + int newSize = max(2 * newRow->memNodeColors, maxNumNodes); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->nodeColors, newRow->memNodeColors, newSize)); + for( int i = newRow->memNodeColors; i < newSize; ++i ) + { + newRow->nodeColors[i] = UNCOLORED; + } + newRow->memNodeColors = newSize; + } + + if( totalNumNodes > newRow->memArticulationNodes ) + { + int newSize = max(2 * newRow->memArticulationNodes, totalNumNodes); + SCIP_CALL( + SCIPreallocBlockMemoryArray(dec->env, &newRow->articulationNodes, newRow->memArticulationNodes, newSize)); + newRow->memArticulationNodes = newSize; + } + if( totalNumNodes > newRow->memNodeSearchInfo ) + { + int newSize = max(2 * newRow->memNodeSearchInfo, totalNumNodes); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->articulationNodeSearchInfo, newRow->memNodeSearchInfo, + newSize)); + newRow->memNodeSearchInfo = newSize; + } + if( totalNumNodes > newRow->memCrossingPathCount ) + { + int newSize = max(2 * newRow->memCrossingPathCount, totalNumNodes); + SCIP_CALL( + SCIPreallocBlockMemoryArray(dec->env, &newRow->crossingPathCount, newRow->memCrossingPathCount, newSize)); + newRow->memCrossingPathCount = newSize; + } + + //TODO: see if tradeoff for performance bound by checking max # of nodes of rigid is worth it to reduce size + //of the following allocations + int largestID = largestNodeID( + dec);//TODO: only update the stack sizes of the following when needed? The preallocation might be causing performance problems + if( largestID > newRow->memIntersectionDFSData ) + { + int newSize = max(2 * newRow->memIntersectionDFSData, largestID); + SCIP_CALL( + SCIPreallocBlockMemoryArray(dec->env, &newRow->intersectionDFSData, newRow->memIntersectionDFSData, newSize)); + newRow->memIntersectionDFSData = newSize; + } + if( largestID > newRow->memColorDFSData ) + { + int newSize = max(2 * newRow->memColorDFSData, largestID); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->colorDFSData, newRow->memColorDFSData, newSize)); + newRow->memColorDFSData = newSize; + } + if( largestID > newRow->memArtDFSData ) + { + int newSize = max(2 * newRow->memArtDFSData, largestID); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->artDFSData, newRow->memArtDFSData, newSize)); + newRow->memArtDFSData = newSize; + } + + for( int i = 0; i < newRow->memIntersectionPathDepth; ++i ) + { + newRow->intersectionPathDepth[i] = -1; + } + + if( largestID > newRow->memIntersectionPathDepth ) + { + int newSize = max(2 * newRow->memIntersectionPathDepth, largestID); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->intersectionPathDepth, newRow->memIntersectionPathDepth, + newSize)); + for( int i = newRow->memIntersectionPathDepth; i < newSize; ++i ) + { + newRow->intersectionPathDepth[i] = -1; + } + newRow->memIntersectionPathDepth = newSize; + } + for( int i = 0; i < newRow->memIntersectionPathParent; ++i ) + { + newRow->intersectionPathParent[i] = SPQR_INVALID_NODE; + } + if( largestID > newRow->memIntersectionPathParent ) + { + int newSize = max(2 * newRow->memIntersectionPathParent, largestID); + SCIP_CALL( + SCIPreallocBlockMemoryArray(dec->env, &newRow->intersectionPathParent, newRow->memIntersectionPathParent, + newSize)); + for( int i = newRow->memIntersectionPathParent; i < newSize; ++i ) + { + newRow->intersectionPathParent[i] = SPQR_INVALID_NODE; + } + newRow->memIntersectionPathParent = newSize; + } + + return SCIP_OKAY; +} + +static void zeroOutColors( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const spqr_node firstRemoveNode +) +{ + assert(firstRemoveNode < newRow->memNodeColors); + + newRow->nodeColors[firstRemoveNode] = UNCOLORED; + ColorDFSCallData* data = newRow->colorDFSData; + if( newRow->colorDFSData == NULL) + { + return; + } + + data[0].node = firstRemoveNode; + data[0].arc = getFirstNodeArc(dec, firstRemoveNode); + if( SPQRarcIsInvalid(data[0].arc)) + { + return; + } + + int depth = 0; + + while( depth >= 0 ) + { + assert(depth < newRow->memColorDFSData); + ColorDFSCallData* callData = &data[depth]; + spqr_node head = findArcHead(dec, callData->arc); + spqr_node tail = findArcTail(dec, callData->arc); + spqr_node otherNode = callData->node == head ? tail : head; + assert(otherNode < newRow->memNodeColors); + if( newRow->nodeColors[otherNode] != UNCOLORED ) + { + callData->arc = getNextNodeArc(dec, callData->arc, callData->node); + + newRow->nodeColors[otherNode] = UNCOLORED; + ++depth; + data[depth].node = otherNode; + data[depth].arc = getFirstNodeArc(dec, otherNode); + continue; + } + + callData->arc = getNextNodeArc(dec, callData->arc, callData->node); + while( depth >= 0 && data[depth].arc == getFirstNodeArc(dec, data[depth].node)) + { + --depth; + } + } +} + +static void cleanUpPreviousIteration( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow +) +{ + //zero out coloring information from previous check + for( int i = 0; i < newRow->numReducedMembers; ++i ) + { + if( SPQRnodeIsValid(newRow->reducedMembers[i].coloredNode)) + { + zeroOutColors(dec, newRow, newRow->reducedMembers[i].coloredNode); + } + } #ifndef NDEBUG - for (int i = 0; i < newRow->memNodeColors; ++i) { - assert(newRow->nodeColors[i] == UNCOLORED); - } + for( int i = 0; i < newRow->memNodeColors; ++i ) + { + assert(newRow->nodeColors[i] == UNCOLORED); + } #endif - //For cut arcs: clear them from the array from previous iteration - cut_arc_id cutArcIdx = newRow->firstOverallCutArc; - while(cutArcIsValid(cutArcIdx)){ - spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; - cutArcIdx = newRow->cutArcs[cutArcIdx].nextOverall; - newRow->isArcCut[cutArc] = FALSE; - newRow->isArcCutReversed[cutArc] = FALSE; - } + //For cut arcs: clear them from the array from previous iteration + cut_arc_id cutArcIdx = newRow->firstOverallCutArc; + while( cutArcIsValid(cutArcIdx)) + { + spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; + cutArcIdx = newRow->cutArcs[cutArcIdx].nextOverall; + newRow->isArcCut[cutArc] = FALSE; + newRow->isArcCutReversed[cutArc] = FALSE; + } #ifndef NDEBUG - for (int i = 0; i < newRow->memIsArcCut; ++i) { - assert(!newRow->isArcCut[i]); - assert(!newRow->isArcCutReversed[i]); - } + for( int i = 0; i < newRow->memIsArcCut; ++i ) + { + assert(!newRow->isArcCut[i]); + assert(!newRow->isArcCutReversed[i]); + } #endif } -static void rigidFindStarNodes(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKROWADDITION * newRow, - const reduced_member_id toCheck){ - //4 cases: - //Only a single edge; both head/tail are okay => network - //All are adjacent to a single node, but do not have it as head or tail => not network - //All are adjacent to a single node, and have it as head or tail => network - //Not all are adjacent to a single node => check articulation nodes - assert(newRow->reducedMembers[toCheck].numCutArcs > 0);//calling this function otherwise is nonsensical - - cut_arc_id cutArcIdx = newRow->reducedMembers[toCheck].firstCutArc; - spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; - spqr_node head = findArcHead(dec, cutArc); - spqr_node tail = findArcTail(dec, cutArc); - - SCIP_Bool reverse = findArcSign(dec,cutArc).reversed != newRow->cutArcs[cutArcIdx].arcReversed; - spqr_node cutArcsHead = reverse ? tail : head; - spqr_node cutArcsTail = reverse ? head : tail; - - if(newRow->reducedMembers[toCheck].numCutArcs == 1){ - newRow->reducedMembers[toCheck].articulationArc = cutArc; - newRow->reducedMembers[toCheck].splitNode = cutArcsHead; - newRow->reducedMembers[toCheck].otherNode = cutArcsTail; - newRow->reducedMembers[toCheck].otherIsSource = TRUE; - newRow->reducedMembers[toCheck].allHaveCommonNode = TRUE; - return;// Only a single cut arc - } - - spqr_node intersectionNodes[2] = {head,tail}; - - while (cutArcIsValid(newRow->cutArcs[cutArcIdx].nextMember)) { - cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; - cutArc = newRow->cutArcs[cutArcIdx].arc; - head = findArcHead(dec, cutArc); - tail = findArcTail(dec, cutArc); - reverse = findArcSign(dec,cutArc).reversed != newRow->cutArcs[cutArcIdx].arcReversed; - spqr_node effectiveHead = reverse ? tail : head; - spqr_node effectiveTail = reverse ? head : tail; - if(effectiveHead != cutArcsHead){ - cutArcsHead = SPQR_INVALID_NODE; - } - if(effectiveTail != cutArcsTail){ - cutArcsTail = SPQR_INVALID_NODE; - } - - //intersection between intersectionNodes and head and tail - for (int i = 0; i < 2; ++i) { - if(intersectionNodes[i] != head && intersectionNodes[i] != tail){ - intersectionNodes[i] = SPQR_INVALID_NODE; - } - } - if(SPQRnodeIsInvalid(intersectionNodes[0]) && SPQRnodeIsInvalid(intersectionNodes[1])){ - newRow->reducedMembers[toCheck].splitNode = SPQR_INVALID_NODE; - newRow->reducedMembers[toCheck].allHaveCommonNode = FALSE; - return; //not all arcs are adjacent to a single node, need to check articulation nodes - } - } - if(SPQRnodeIsInvalid(cutArcsHead) && SPQRnodeIsInvalid(cutArcsTail)){ - //All arcs adjacent to a single node, but not in same direction; not network - newRow->remainsNetwork = FALSE; - newRow->reducedMembers[toCheck].type = TYPE_NOT_NETWORK; - return; - } - SCIP_Bool headSplittable = SPQRnodeIsValid(cutArcsHead); - //Check if the n arcs are in a n+1 degree node; if so, the other endpoint of this non split arc is also splittable - //By virtue of the spanning tree, this arc must be a tree arc. - spqr_node splitNode = headSplittable ? cutArcsHead : cutArcsTail; - newRow->reducedMembers[toCheck].splitNode = splitNode; - newRow->reducedMembers[toCheck].otherIsSource = headSplittable; - newRow->reducedMembers[toCheck].allHaveCommonNode = TRUE; - if(newRow->reducedMembers[toCheck].numCutArcs == nodeDegree(dec,splitNode) - 1){ - spqr_arc firstNodeArc = getFirstNodeArc(dec,splitNode); - spqr_arc neighbourArc = firstNodeArc; - do{ - if(arcIsTree(dec,neighbourArc)){ - break; - } - neighbourArc = getNextNodeArc(dec,neighbourArc,splitNode); - }while(neighbourArc != firstNodeArc); - - newRow->reducedMembers[toCheck].articulationArc = neighbourArc; - spqr_arc arcHead = findArcHead(dec,neighbourArc); - newRow->reducedMembers[toCheck].otherNode = arcHead == splitNode ? findArcTail(dec,neighbourArc) : arcHead; +static void rigidFindStarNodes( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const reduced_member_id toCheck +) +{ + //4 cases: + //Only a single edge; both head/tail are okay => network + //All are adjacent to a single node, but do not have it as head or tail => not network + //All are adjacent to a single node, and have it as head or tail => network + //Not all are adjacent to a single node => check articulation nodes + assert(newRow->reducedMembers[toCheck].numCutArcs > 0);//calling this function otherwise is nonsensical + + cut_arc_id cutArcIdx = newRow->reducedMembers[toCheck].firstCutArc; + spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; + spqr_node head = findArcHead(dec, cutArc); + spqr_node tail = findArcTail(dec, cutArc); + + SCIP_Bool reverse = findArcSign(dec, cutArc).reversed != newRow->cutArcs[cutArcIdx].arcReversed; + spqr_node cutArcsHead = reverse ? tail : head; + spqr_node cutArcsTail = reverse ? head : tail; + + if( newRow->reducedMembers[toCheck].numCutArcs == 1 ) + { + newRow->reducedMembers[toCheck].articulationArc = cutArc; + newRow->reducedMembers[toCheck].splitNode = cutArcsHead; + newRow->reducedMembers[toCheck].otherNode = cutArcsTail; + newRow->reducedMembers[toCheck].otherIsSource = TRUE; + newRow->reducedMembers[toCheck].allHaveCommonNode = TRUE; + return;// Only a single cut arc + } + + spqr_node intersectionNodes[2] = {head, tail}; + + while( cutArcIsValid(newRow->cutArcs[cutArcIdx].nextMember)) + { + cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; + cutArc = newRow->cutArcs[cutArcIdx].arc; + head = findArcHead(dec, cutArc); + tail = findArcTail(dec, cutArc); + reverse = findArcSign(dec, cutArc).reversed != newRow->cutArcs[cutArcIdx].arcReversed; + spqr_node effectiveHead = reverse ? tail : head; + spqr_node effectiveTail = reverse ? head : tail; + if( effectiveHead != cutArcsHead ) + { + cutArcsHead = SPQR_INVALID_NODE; + } + if( effectiveTail != cutArcsTail ) + { + cutArcsTail = SPQR_INVALID_NODE; + } + + //intersection between intersectionNodes and head and tail + for( int i = 0; i < 2; ++i ) + { + if( intersectionNodes[i] != head && intersectionNodes[i] != tail ) + { + intersectionNodes[i] = SPQR_INVALID_NODE; + } + } + if( SPQRnodeIsInvalid(intersectionNodes[0]) && SPQRnodeIsInvalid(intersectionNodes[1])) + { + newRow->reducedMembers[toCheck].splitNode = SPQR_INVALID_NODE; + newRow->reducedMembers[toCheck].allHaveCommonNode = FALSE; + return;//not all arcs are adjacent to a single node, need to check articulation nodes + } + } + if( SPQRnodeIsInvalid(cutArcsHead) && SPQRnodeIsInvalid(cutArcsTail)) + { + //All arcs adjacent to a single node, but not in same direction; not network + newRow->remainsNetwork = FALSE; + newRow->reducedMembers[toCheck].type = TYPE_NOT_NETWORK; + return; + } + SCIP_Bool headSplittable = SPQRnodeIsValid(cutArcsHead); + //Check if the n arcs are in a n+1 degree node; if so, the other endpoint of this non split arc is also splittable + //By virtue of the spanning tree, this arc must be a tree arc. + spqr_node splitNode = headSplittable ? cutArcsHead : cutArcsTail; + newRow->reducedMembers[toCheck].splitNode = splitNode; + newRow->reducedMembers[toCheck].otherIsSource = headSplittable; + newRow->reducedMembers[toCheck].allHaveCommonNode = TRUE; + if( newRow->reducedMembers[toCheck].numCutArcs == nodeDegree(dec, splitNode) - 1 ) + { + spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode); + spqr_arc neighbourArc = firstNodeArc; + do + { + if( arcIsTree(dec, neighbourArc)) + { + break; + } + neighbourArc = getNextNodeArc(dec, neighbourArc, splitNode); + } while( neighbourArc != firstNodeArc ); - } + newRow->reducedMembers[toCheck].articulationArc = neighbourArc; + spqr_arc arcHead = findArcHead(dec, neighbourArc); + newRow->reducedMembers[toCheck].otherNode = arcHead == splitNode ? findArcTail(dec, neighbourArc) : arcHead; + } } //TODO: remove SCIP_RETCODE from below functions (until propagation function, basically) and refactor memory allocation -static SCIP_RETCODE zeroOutColorsExceptNeighbourhood(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - const spqr_node articulationNode, const spqr_node startRemoveNode){ - COLOR_STATUS * neighbourColors; - int degree = nodeDegree(dec,articulationNode); - SCIP_CALL(SCIPallocBlockMemoryArray(dec->env,&neighbourColors,(size_t) degree)); - - { - int i = 0; - spqr_arc artFirstArc = getFirstNodeArc(dec, articulationNode); - spqr_arc artItArc = artFirstArc; - do{ - spqr_node head = findArcHead(dec, artItArc); - spqr_node tail = findArcTail(dec, artItArc); - spqr_node otherNode = articulationNode == head ? tail : head; - neighbourColors[i] = newRow->nodeColors[otherNode]; - i++; - assert(i <= degree); - artItArc = getNextNodeArc(dec,artItArc,articulationNode); - }while(artItArc != artFirstArc); - } - zeroOutColors(dec,newRow,startRemoveNode); - - { - int i = 0; - spqr_arc artFirstArc = getFirstNodeArc(dec, articulationNode); - spqr_arc artItArc = artFirstArc; - do{ - spqr_node head = findArcHead(dec, artItArc); - spqr_node tail = findArcTail(dec, artItArc); - spqr_node otherNode = articulationNode == head ? tail : head; - newRow->nodeColors[otherNode] = neighbourColors[i]; - i++; - assert(i <= degree); - artItArc = getNextNodeArc(dec,artItArc,articulationNode); - }while(artItArc != artFirstArc); - } - - SCIPfreeBlockMemoryArray(dec->env,&neighbourColors,degree); - return SCIP_OKAY; +static SCIP_RETCODE zeroOutColorsExceptNeighbourhood( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const spqr_node articulationNode, + const spqr_node startRemoveNode +) +{ + COLOR_STATUS* neighbourColors; + int degree = nodeDegree(dec, articulationNode); + SCIP_CALL(SCIPallocBlockMemoryArray(dec->env, &neighbourColors, (size_t) degree)); + + { + int i = 0; + spqr_arc artFirstArc = getFirstNodeArc(dec, articulationNode); + spqr_arc artItArc = artFirstArc; + do + { + spqr_node head = findArcHead(dec, artItArc); + spqr_node tail = findArcTail(dec, artItArc); + spqr_node otherNode = articulationNode == head ? tail : head; + neighbourColors[i] = newRow->nodeColors[otherNode]; + i++; + assert(i <= degree); + artItArc = getNextNodeArc(dec, artItArc, articulationNode); + } while( artItArc != artFirstArc ); + } + zeroOutColors(dec, newRow, startRemoveNode); + + { + int i = 0; + spqr_arc artFirstArc = getFirstNodeArc(dec, articulationNode); + spqr_arc artItArc = artFirstArc; + do + { + spqr_node head = findArcHead(dec, artItArc); + spqr_node tail = findArcTail(dec, artItArc); + spqr_node otherNode = articulationNode == head ? tail : head; + newRow->nodeColors[otherNode] = neighbourColors[i]; + i++; + assert(i <= degree); + artItArc = getNextNodeArc(dec, artItArc, articulationNode); + } while( artItArc != artFirstArc ); + } + + SCIPfreeBlockMemoryArray(dec->env, &neighbourColors, degree); + return SCIP_OKAY; } //Theoretically, there is a faster algorithm, but it is quite complicated to implement. //Thus, we stick with the 'simple' version below, which is easily fast enough in practice. -static void intersectionOfAllPaths(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKROWADDITION *newRow, - const reduced_member_id toCheck, int * const nodeNumPaths){ - int * intersectionPathDepth = newRow->intersectionPathDepth; - spqr_node * intersectionPathParent = newRow->intersectionPathParent; - - //First do a dfs over the tree, storing all the tree-parents and depths for each node - //TODO: maybe cache this tree and also update it so we can prevent this DFS call? - - //pick an arbitrary node as root; we just use the first cutArc here - { - spqr_node root = findArcHead(dec, newRow->cutArcs[newRow->reducedMembers[toCheck].firstCutArc].arc); - DFSCallData *pathSearchCallStack = newRow->intersectionDFSData; - - assert(intersectionPathDepth[root] == -1); - assert(intersectionPathParent[root] == SPQR_INVALID_NODE); - - int pathSearchCallStackSize = 0; - - intersectionPathDepth[root] = 0; - intersectionPathParent[root] = SPQR_INVALID_NODE; - - pathSearchCallStack[0].node = root; - pathSearchCallStack[0].nodeArc = getFirstNodeArc(dec, root); - pathSearchCallStackSize++; - while (pathSearchCallStackSize > 0) { - assert(pathSearchCallStackSize <= newRow->memIntersectionDFSData); - DFSCallData *dfsData = &pathSearchCallStack[pathSearchCallStackSize - 1]; - //cannot be a tree arc which is its parent - if (arcIsTree(dec, dfsData->nodeArc) && - (pathSearchCallStackSize <= 1 || - dfsData->nodeArc != pathSearchCallStack[pathSearchCallStackSize - 2].nodeArc)) { - spqr_node head = findArcHeadNoCompression(dec, dfsData->nodeArc); - spqr_node tail = findArcTailNoCompression(dec, dfsData->nodeArc); - spqr_node other = head == dfsData->node ? tail : head; - assert(other != dfsData->node); - - //We go up a level: add new node to the call stack - pathSearchCallStack[pathSearchCallStackSize].node = other; - pathSearchCallStack[pathSearchCallStackSize].nodeArc = getFirstNodeArc(dec, other); - //Every time a new node is discovered/added, we update its parent and depth information - assert(intersectionPathDepth[other] == -1); - assert(intersectionPathParent[other] == SPQR_INVALID_NODE); - intersectionPathParent[other] = dfsData->node; - intersectionPathDepth[other] = pathSearchCallStackSize; - ++pathSearchCallStackSize; - continue; +static void intersectionOfAllPaths( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const reduced_member_id toCheck, + int* const nodeNumPaths +) +{ + int* intersectionPathDepth = newRow->intersectionPathDepth; + spqr_node* intersectionPathParent = newRow->intersectionPathParent; + + //First do a dfs over the tree, storing all the tree-parents and depths for each node + //TODO: maybe cache this tree and also update it so we can prevent this DFS call? + + //pick an arbitrary node as root; we just use the first cutArc here + { + spqr_node root = findArcHead(dec, newRow->cutArcs[newRow->reducedMembers[toCheck].firstCutArc].arc); + DFSCallData* pathSearchCallStack = newRow->intersectionDFSData; + + assert(intersectionPathDepth[root] == -1); + assert(intersectionPathParent[root] == SPQR_INVALID_NODE); + + int pathSearchCallStackSize = 0; + + intersectionPathDepth[root] = 0; + intersectionPathParent[root] = SPQR_INVALID_NODE; + + pathSearchCallStack[0].node = root; + pathSearchCallStack[0].nodeArc = getFirstNodeArc(dec, root); + pathSearchCallStackSize++; + while( pathSearchCallStackSize > 0 ) + { + assert(pathSearchCallStackSize <= newRow->memIntersectionDFSData); + DFSCallData* dfsData = &pathSearchCallStack[pathSearchCallStackSize - 1]; + //cannot be a tree arc which is its parent + if( arcIsTree(dec, dfsData->nodeArc) && + ( pathSearchCallStackSize <= 1 || + dfsData->nodeArc != pathSearchCallStack[pathSearchCallStackSize - 2].nodeArc )) + { + spqr_node head = findArcHeadNoCompression(dec, dfsData->nodeArc); + spqr_node tail = findArcTailNoCompression(dec, dfsData->nodeArc); + spqr_node other = head == dfsData->node ? tail : head; + assert(other != dfsData->node); + + //We go up a level: add new node to the call stack + pathSearchCallStack[pathSearchCallStackSize].node = other; + pathSearchCallStack[pathSearchCallStackSize].nodeArc = getFirstNodeArc(dec, other); + //Every time a new node is discovered/added, we update its parent and depth information + assert(intersectionPathDepth[other] == -1); + assert(intersectionPathParent[other] == SPQR_INVALID_NODE); + intersectionPathParent[other] = dfsData->node; + intersectionPathDepth[other] = pathSearchCallStackSize; + ++pathSearchCallStackSize; + continue; + } + do + { + dfsData->nodeArc = getNextNodeArc(dec, dfsData->nodeArc, dfsData->node); + if( dfsData->nodeArc == getFirstNodeArc(dec, dfsData->node)) + { + --pathSearchCallStackSize; + dfsData = &pathSearchCallStack[pathSearchCallStackSize - 1]; + } else + { + break; } - do { - dfsData->nodeArc = getNextNodeArc(dec, dfsData->nodeArc, dfsData->node); - if (dfsData->nodeArc == getFirstNodeArc(dec, dfsData->node)) { - --pathSearchCallStackSize; - dfsData = &pathSearchCallStack[pathSearchCallStackSize - 1]; - } else { - break; - } - } while (pathSearchCallStackSize > 0); - } - } - - //For each cut arc, trace back both ends until they meet - cut_arc_id cutArc = newRow->reducedMembers[toCheck].firstCutArc; - do{ - spqr_arc arc = newRow->cutArcs[cutArc].arc; - cutArc = newRow->cutArcs[cutArc].nextMember; - - //Iteratively jump up to the parents until they reach a common parent - spqr_node source = findArcHead(dec, arc); - spqr_node target = findArcTail(dec, arc); - int sourceDepth = intersectionPathDepth[source]; - int targetDepth = intersectionPathDepth[target]; - nodeNumPaths[source]++; - nodeNumPaths[target]++; - - while (sourceDepth > targetDepth){ - assert(source != target); - source = intersectionPathParent[source]; - nodeNumPaths[source]++; - --sourceDepth; - } - while(targetDepth > sourceDepth){ - assert(source != target); - target = intersectionPathParent[target]; - nodeNumPaths[target]++; - --targetDepth; - } - while(source != target && targetDepth >= 0){ - source = intersectionPathParent[source]; - target = intersectionPathParent[target]; - nodeNumPaths[source]++; - nodeNumPaths[target]++; - --targetDepth; - } - //In all the above, the lowest common ancestor is increased twice, so we correct for it ad-hoc - nodeNumPaths[source]--; - assert(SPQRnodeIsValid(source) && SPQRnodeIsValid(target)); - assert(source == target); - - }while (cutArcIsValid(cutArc)); - -} - -static void addArticulationNode(SCIP_NETWORKROWADDITION *newRow, spqr_node articulationNode){ + } while( pathSearchCallStackSize > 0 ); + } + } + + //For each cut arc, trace back both ends until they meet + cut_arc_id cutArc = newRow->reducedMembers[toCheck].firstCutArc; + do + { + spqr_arc arc = newRow->cutArcs[cutArc].arc; + cutArc = newRow->cutArcs[cutArc].nextMember; + + //Iteratively jump up to the parents until they reach a common parent + spqr_node source = findArcHead(dec, arc); + spqr_node target = findArcTail(dec, arc); + int sourceDepth = intersectionPathDepth[source]; + int targetDepth = intersectionPathDepth[target]; + nodeNumPaths[source]++; + nodeNumPaths[target]++; + + while( sourceDepth > targetDepth ) + { + assert(source != target); + source = intersectionPathParent[source]; + nodeNumPaths[source]++; + --sourceDepth; + } + while( targetDepth > sourceDepth ) + { + assert(source != target); + target = intersectionPathParent[target]; + nodeNumPaths[target]++; + --targetDepth; + } + while( source != target && targetDepth >= 0 ) + { + source = intersectionPathParent[source]; + target = intersectionPathParent[target]; + nodeNumPaths[source]++; + nodeNumPaths[target]++; + --targetDepth; + } + //In all the above, the lowest common ancestor is increased twice, so we correct for it ad-hoc + nodeNumPaths[source]--; + assert(SPQRnodeIsValid(source) && SPQRnodeIsValid(target)); + assert(source == target); + + } while( cutArcIsValid(cutArc)); +} + +static void addArticulationNode( + SCIP_NETROWADD* newRow, + spqr_node articulationNode +) +{ #ifndef NDEBUG - for (int i = 0; i < newRow->numArticulationNodes; ++i) { - assert(newRow->articulationNodes[i] != articulationNode); - } + for( int i = 0; i < newRow->numArticulationNodes; ++i ) + { + assert(newRow->articulationNodes[i] != articulationNode); + } #endif - newRow->articulationNodes[newRow->numArticulationNodes] = articulationNode; - ++newRow->numArticulationNodes; -} -static void articulationPoints(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION * newRow, ArticulationNodeInformation *nodeInfo, reduced_member_id reducedMember){ - const SCIP_Bool * arcRemoved = newRow->isArcCut; - - int rootChildren = 0; - spqr_node root_node = findArcHead(dec, getFirstMemberArc(dec, newRow->reducedMembers[reducedMember].member));; - - ArticulationPointCallStack * callStack = newRow->artDFSData; - - int depth = 0; - int time = 1; - - callStack[depth].arc = getFirstNodeArc(dec,root_node); - callStack[depth].node = root_node; - callStack[depth].parent = SPQR_INVALID_NODE; - callStack[depth].isAP = FALSE; - - nodeInfo[root_node].low = time; - nodeInfo[root_node].discoveryTime = time; - - while(depth >= 0){ - if(!arcRemoved[callStack[depth].arc]){ - spqr_node node = callStack[depth].node; - spqr_node head = findArcHead(dec, callStack[depth].arc); - spqr_node tail = findArcTail(dec, callStack[depth].arc); - spqr_node otherNode = node == head ? tail : head; - if(otherNode != callStack[depth].parent){ - if(nodeInfo[otherNode].discoveryTime == 0){ - if(depth == 0){ - rootChildren++; - } - //recursive call - ++depth; - assert(depth < newRow->memArtDFSData); - callStack[depth].parent = node; - callStack[depth].node = otherNode; - callStack[depth].arc = getFirstNodeArc(dec,otherNode); - callStack[depth].isAP = FALSE; - - ++time; - nodeInfo[otherNode].low = time; - nodeInfo[otherNode].discoveryTime = time; - continue; - - }else{ - nodeInfo[node].low = min(nodeInfo[node].low, nodeInfo[otherNode].discoveryTime); - } - } - } - - while(TRUE){ - callStack[depth].arc = getNextNodeArc(dec,callStack[depth].arc,callStack[depth].node); - if(callStack[depth].arc != getFirstNodeArc(dec,callStack[depth].node)) break; - --depth; - if (depth < 0) break; - - spqr_node current_node = callStack[depth].node; - spqr_node other_node = callStack[depth + 1].node; - nodeInfo[current_node].low = min(nodeInfo[current_node].low, - nodeInfo[other_node].low); - if (depth != 0 && - !callStack[depth].isAP && - nodeInfo[current_node].discoveryTime <= nodeInfo[other_node].low) { - addArticulationNode(newRow, current_node); - callStack[depth].isAP = TRUE; - } - } - - } - if(rootChildren > 1 ){ - addArticulationNode(newRow,root_node); - } -} - -static void rigidConnectedColoringRecursive(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION * newRow, spqr_node articulationNode, - spqr_node firstProcessNode, COLOR_STATUS firstColor, - SCIP_Bool *isGood){ - const SCIP_Bool * isArcCut = newRow->isArcCut; - COLOR_STATUS * nodeColors = newRow->nodeColors; - ColorDFSCallData * data = newRow->colorDFSData; - - data[0].node = firstProcessNode; - data[0].arc = getFirstNodeArc(dec,firstProcessNode); - newRow->nodeColors[firstProcessNode] = firstColor; - - int depth = 0; - while(depth >= 0){ - assert(depth < newRow->memColorDFSData); - assert(newRow->nodeColors[articulationNode] == UNCOLORED); - - ColorDFSCallData * callData = &data[depth]; - spqr_node head = findArcHead(dec, callData->arc); - spqr_node tail = findArcTail(dec, callData->arc); - spqr_node otherNode = callData->node == head ? tail : head; - COLOR_STATUS currentColor = nodeColors[callData->node]; - COLOR_STATUS otherColor = nodeColors[otherNode]; - //Checks the direction of the arc; in the rest of the algorithm, we just need to check partition - if(isArcCut[callData->arc] && currentColor != otherColor){ - SCIP_Bool otherIsTail = callData->node == head; - SCIP_Bool arcReversed = findArcSign(dec,callData->arc).reversed != newRow->isArcCutReversed[callData->arc]; - SCIP_Bool good = (currentColor == COLOR_SOURCE) == (otherIsTail == arcReversed); - if(!good){ - *isGood = FALSE; - break; - } - } - if(otherNode != articulationNode){ - if(otherColor == UNCOLORED){ - if(isArcCut[callData->arc]){ - nodeColors[otherNode] = currentColor == COLOR_SOURCE ? COLOR_SINK : COLOR_SOURCE; //reverse the colors - }else{ - nodeColors[otherNode] = currentColor; - } - callData->arc = getNextNodeArc(dec,callData->arc,callData->node); - - depth++; - assert(depth < newRow->memColorDFSData); - data[depth].node = otherNode; - data[depth].arc = getFirstNodeArc(dec,otherNode); - continue; + newRow->articulationNodes[newRow->numArticulationNodes] = articulationNode; + ++newRow->numArticulationNodes; +} + +static void articulationPoints( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + ArticulationNodeInformation* nodeInfo, + reduced_member_id reducedMember +) +{ + const SCIP_Bool* arcRemoved = newRow->isArcCut; + + int rootChildren = 0; + spqr_node root_node = findArcHead(dec, getFirstMemberArc(dec, newRow->reducedMembers[reducedMember].member));; + + ArticulationPointCallStack* callStack = newRow->artDFSData; + + int depth = 0; + int time = 1; + + callStack[depth].arc = getFirstNodeArc(dec, root_node); + callStack[depth].node = root_node; + callStack[depth].parent = SPQR_INVALID_NODE; + callStack[depth].isAP = FALSE; + + nodeInfo[root_node].low = time; + nodeInfo[root_node].discoveryTime = time; + + while( depth >= 0 ) + { + if( !arcRemoved[callStack[depth].arc] ) + { + spqr_node node = callStack[depth].node; + spqr_node head = findArcHead(dec, callStack[depth].arc); + spqr_node tail = findArcTail(dec, callStack[depth].arc); + spqr_node otherNode = node == head ? tail : head; + if( otherNode != callStack[depth].parent ) + { + if( nodeInfo[otherNode].discoveryTime == 0 ) + { + if( depth == 0 ) + { + rootChildren++; + } + //recursive call + ++depth; + assert(depth < newRow->memArtDFSData); + callStack[depth].parent = node; + callStack[depth].node = otherNode; + callStack[depth].arc = getFirstNodeArc(dec, otherNode); + callStack[depth].isAP = FALSE; + + ++time; + nodeInfo[otherNode].low = time; + nodeInfo[otherNode].discoveryTime = time; + continue; + + } else + { + nodeInfo[node].low = min(nodeInfo[node].low, nodeInfo[otherNode].discoveryTime); } - if(isArcCut[callData->arc] != (currentColor != otherColor)){ - *isGood = FALSE; - break; + } + } + + while( TRUE ) + { + callStack[depth].arc = getNextNodeArc(dec, callStack[depth].arc, callStack[depth].node); + if( callStack[depth].arc != getFirstNodeArc(dec, callStack[depth].node)) break; + --depth; + if( depth < 0 ) break; + + spqr_node current_node = callStack[depth].node; + spqr_node other_node = callStack[depth + 1].node; + nodeInfo[current_node].low = min(nodeInfo[current_node].low, + nodeInfo[other_node].low); + if( depth != 0 && + !callStack[depth].isAP && + nodeInfo[current_node].discoveryTime <= nodeInfo[other_node].low ) + { + addArticulationNode(newRow, current_node); + callStack[depth].isAP = TRUE; + } + } + } + if( rootChildren > 1 ) + { + addArticulationNode(newRow, root_node); + } +} + +static void rigidConnectedColoringRecursive( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + spqr_node articulationNode, + spqr_node firstProcessNode, + COLOR_STATUS firstColor, + SCIP_Bool* isGood +) +{ + const SCIP_Bool* isArcCut = newRow->isArcCut; + COLOR_STATUS* nodeColors = newRow->nodeColors; + ColorDFSCallData* data = newRow->colorDFSData; + + data[0].node = firstProcessNode; + data[0].arc = getFirstNodeArc(dec, firstProcessNode); + newRow->nodeColors[firstProcessNode] = firstColor; + + int depth = 0; + while( depth >= 0 ) + { + assert(depth < newRow->memColorDFSData); + assert(newRow->nodeColors[articulationNode] == UNCOLORED); + + ColorDFSCallData* callData = &data[depth]; + spqr_node head = findArcHead(dec, callData->arc); + spqr_node tail = findArcTail(dec, callData->arc); + spqr_node otherNode = callData->node == head ? tail : head; + COLOR_STATUS currentColor = nodeColors[callData->node]; + COLOR_STATUS otherColor = nodeColors[otherNode]; + //Checks the direction of the arc; in the rest of the algorithm, we just need to check partition + if( isArcCut[callData->arc] && currentColor != otherColor ) + { + SCIP_Bool otherIsTail = callData->node == head; + SCIP_Bool arcReversed = findArcSign(dec, callData->arc).reversed != newRow->isArcCutReversed[callData->arc]; + SCIP_Bool good = ( currentColor == COLOR_SOURCE ) == ( otherIsTail == arcReversed ); + if( !good ) + { + *isGood = FALSE; + break; + } + } + if( otherNode != articulationNode ) + { + if( otherColor == UNCOLORED ) + { + if( isArcCut[callData->arc] ) + { + nodeColors[otherNode] = currentColor == COLOR_SOURCE ? COLOR_SINK : COLOR_SOURCE;//reverse the colors + } else + { + nodeColors[otherNode] = currentColor; } - } - callData->arc = getNextNodeArc(dec,callData->arc,callData->node); - while(depth >= 0 && data[depth].arc == getFirstNodeArc(dec,data[depth].node)){ - --depth; - } - } -} + callData->arc = getNextNodeArc(dec, callData->arc, callData->node); -static void rigidConnectedColoring(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - const reduced_member_id reducedMember, const spqr_node node, SCIP_Bool * const isGood){ + depth++; + assert(depth < newRow->memColorDFSData); + data[depth].node = otherNode; + data[depth].arc = getFirstNodeArc(dec, otherNode); + continue; + } + if( isArcCut[callData->arc] != ( currentColor != otherColor )) + { + *isGood = FALSE; + break; + } + } + callData->arc = getNextNodeArc(dec, callData->arc, callData->node); + while( depth >= 0 && data[depth].arc == getFirstNodeArc(dec, data[depth].node)) + { + --depth; + } + } +} + +static void rigidConnectedColoring( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const reduced_member_id reducedMember, + const spqr_node node, + SCIP_Bool* const isGood +) +{ - //we should only perform this function if there's more than one cut arc - assert(newRow->reducedMembers[reducedMember].numCutArcs > 1); + //we should only perform this function if there's more than one cut arc + assert(newRow->reducedMembers[reducedMember].numCutArcs > 1); #ifndef NDEBUG - { - - spqr_member member = newRow->reducedMembers[reducedMember].member; - spqr_arc firstArc = getFirstMemberArc(dec, member); - spqr_arc memberArc = firstArc; - do{ - assert(newRow->nodeColors[findArcHeadNoCompression(dec,memberArc)] == UNCOLORED); - assert(newRow->nodeColors[findArcTailNoCompression(dec,memberArc)] == UNCOLORED); - memberArc = getNextMemberArc(dec,memberArc); - }while(firstArc != memberArc); - } + { + + spqr_member member = newRow->reducedMembers[reducedMember].member; + spqr_arc firstArc = getFirstMemberArc(dec, member); + spqr_arc memberArc = firstArc; + do + { + assert(newRow->nodeColors[findArcHeadNoCompression(dec, memberArc)] == UNCOLORED); + assert(newRow->nodeColors[findArcTailNoCompression(dec, memberArc)] == UNCOLORED); + memberArc = getNextMemberArc(dec, memberArc); + } while( firstArc != memberArc ); + } #endif - spqr_node firstProcessNode; - COLOR_STATUS firstColor; - { - cut_arc_id cutArc = newRow->reducedMembers[reducedMember].firstCutArc; - spqr_arc arc = newRow->cutArcs[cutArc].arc; - assert(SPQRarcIsValid(arc)); - spqr_node head = findArcHead(dec, arc); - spqr_node tail = findArcTail(dec, arc); - if(findArcSign(dec,arc).reversed != newRow->cutArcs[cutArc].arcReversed){ - spqr_node temp = head; - head = tail; - tail = temp; - } - if(tail != node){ - firstProcessNode = tail; - firstColor = COLOR_SOURCE; - }else{ - assert(head != node); - firstProcessNode = head; - firstColor = COLOR_SINK; - } - } - assert(SPQRnodeIsValid(firstProcessNode) && firstProcessNode != node); - *isGood = TRUE; - newRow->reducedMembers[reducedMember].coloredNode = firstProcessNode; - rigidConnectedColoringRecursive(dec,newRow,node,firstProcessNode,firstColor,isGood); - - // Need to zero all colors for next attempts if we failed - if(!(*isGood)){ - zeroOutColors(dec,newRow,firstProcessNode); - newRow->reducedMembers[reducedMember].coloredNode = SPQR_INVALID_NODE; - }else{ - //Otherwise, we zero out all colors but the ones which we need - zeroOutColorsExceptNeighbourhood(dec,newRow,node, firstProcessNode); - newRow->reducedMembers[reducedMember].coloredNode = node; - } -} - -static spqr_node checkNeighbourColoringArticulationNode(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKROWADDITION *newRow, - const spqr_node articulationNode, spqr_arc * const adjacentSplittingArc){ - spqr_node firstSideCandidate = SPQR_INVALID_NODE; - spqr_node secondSideCandidate = SPQR_INVALID_NODE; - spqr_arc firstSideArc = SPQR_INVALID_ARC; - spqr_arc secondSideArc = SPQR_INVALID_ARC; - int numFirstSide = 0; - int numSecondSide = 0; - - spqr_arc firstArc = getFirstNodeArc(dec, articulationNode); - spqr_arc moveArc = firstArc; - do{ - spqr_node head = findArcHead(dec, moveArc); - spqr_node tail = findArcTail(dec, moveArc); - spqr_node otherNode = articulationNode == head ? tail : head; - assert(newRow->nodeColors[otherNode] != UNCOLORED); - if((newRow->nodeColors[otherNode] == COLOR_SOURCE) != newRow->isArcCut[moveArc] ){ - if(numFirstSide == 0 && arcIsTree(dec,moveArc)){ - firstSideCandidate = otherNode; - firstSideArc = moveArc; - }else if (numFirstSide > 0){ - firstSideCandidate = SPQR_INVALID_NODE; - } - ++numFirstSide; - }else{ - if(numSecondSide == 0 && arcIsTree(dec,moveArc)){ - secondSideCandidate = otherNode; - secondSideArc = moveArc; - }else if (numSecondSide > 0){ - secondSideCandidate = SPQR_INVALID_NODE; - } - ++numSecondSide; - } - moveArc = getNextNodeArc(dec,moveArc,articulationNode); - }while(moveArc != firstArc); - - if(numFirstSide == 1){ - *adjacentSplittingArc = firstSideArc; - return firstSideCandidate; - }else if (numSecondSide == 1){ - *adjacentSplittingArc = secondSideArc; - return secondSideCandidate; - } - return SPQR_INVALID_NODE; -} - - -static void rigidGetSplittableArticulationPointsOnPath(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - const reduced_member_id toCheck, - spqr_node firstNode, spqr_node secondNode){ - assert(newRow->reducedMembers[toCheck].numCutArcs > 1); - - int totalNumNodes = getNumNodes(dec); - int * nodeNumPaths = newRow->crossingPathCount; - - for (int i = 0; i < totalNumNodes; ++i) { - nodeNumPaths[i] = 0; - } - - intersectionOfAllPaths(dec,newRow,toCheck,nodeNumPaths); - - newRow->numArticulationNodes = 0; - - ArticulationNodeInformation * artNodeInfo = newRow->articulationNodeSearchInfo; - //TODO: ugly; we clean up over all decomposition nodes for every component - //clean up can not easily be done in the search, unfortunately - for (int i = 0; i < totalNumNodes; ++i) { - artNodeInfo[i].low = 0 ; - artNodeInfo[i].discoveryTime = 0; - } - - articulationPoints(dec,newRow,artNodeInfo,toCheck); - - int numCutArcs = newRow->reducedMembers[toCheck].numCutArcs; - for (int i = 0; i < newRow->numArticulationNodes; i++) { - spqr_node articulationNode = newRow->articulationNodes[i]; - assert(nodeIsRepresentative(dec, articulationNode)); - SCIP_Bool isOnPath = nodeNumPaths[articulationNode] == numCutArcs; - if (isOnPath && ( - (SPQRnodeIsInvalid(firstNode) &&SPQRnodeIsInvalid(secondNode)) - || articulationNode == firstNode || articulationNode == secondNode )){ - SCIP_Bool isGood = TRUE; - rigidConnectedColoring(dec, newRow, toCheck, articulationNode, &isGood); - if(!isGood){ - continue; + spqr_node firstProcessNode; + COLOR_STATUS firstColor; + { + cut_arc_id cutArc = newRow->reducedMembers[reducedMember].firstCutArc; + spqr_arc arc = newRow->cutArcs[cutArc].arc; + assert(SPQRarcIsValid(arc)); + spqr_node head = findArcHead(dec, arc); + spqr_node tail = findArcTail(dec, arc); + if( findArcSign(dec, arc).reversed != newRow->cutArcs[cutArc].arcReversed ) + { + spqr_node temp = head; + head = tail; + tail = temp; + } + if( tail != node ) + { + firstProcessNode = tail; + firstColor = COLOR_SOURCE; + } else + { + assert(head != node); + firstProcessNode = head; + firstColor = COLOR_SINK; + } + } + assert(SPQRnodeIsValid(firstProcessNode) && firstProcessNode != node); + *isGood = TRUE; + newRow->reducedMembers[reducedMember].coloredNode = firstProcessNode; + rigidConnectedColoringRecursive(dec, newRow, node, firstProcessNode, firstColor, isGood); + + // Need to zero all colors for next attempts if we failed + if( !( *isGood )) + { + zeroOutColors(dec, newRow, firstProcessNode); + newRow->reducedMembers[reducedMember].coloredNode = SPQR_INVALID_NODE; + } else + { + //Otherwise, we zero out all colors but the ones which we need + zeroOutColorsExceptNeighbourhood(dec, newRow, node, firstProcessNode); + newRow->reducedMembers[reducedMember].coloredNode = node; + } +} + +static spqr_node checkNeighbourColoringArticulationNode( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const spqr_node articulationNode, + spqr_arc* const adjacentSplittingArc +) +{ + spqr_node firstSideCandidate = SPQR_INVALID_NODE; + spqr_node secondSideCandidate = SPQR_INVALID_NODE; + spqr_arc firstSideArc = SPQR_INVALID_ARC; + spqr_arc secondSideArc = SPQR_INVALID_ARC; + int numFirstSide = 0; + int numSecondSide = 0; + + spqr_arc firstArc = getFirstNodeArc(dec, articulationNode); + spqr_arc moveArc = firstArc; + do + { + spqr_node head = findArcHead(dec, moveArc); + spqr_node tail = findArcTail(dec, moveArc); + spqr_node otherNode = articulationNode == head ? tail : head; + assert(newRow->nodeColors[otherNode] != UNCOLORED); + if(( newRow->nodeColors[otherNode] == COLOR_SOURCE ) != newRow->isArcCut[moveArc] ) + { + if( numFirstSide == 0 && arcIsTree(dec, moveArc)) + { + firstSideCandidate = otherNode; + firstSideArc = moveArc; + } else if( numFirstSide > 0 ) + { + firstSideCandidate = SPQR_INVALID_NODE; + } + ++numFirstSide; + } else + { + if( numSecondSide == 0 && arcIsTree(dec, moveArc)) + { + secondSideCandidate = otherNode; + secondSideArc = moveArc; + } else if( numSecondSide > 0 ) + { + secondSideCandidate = SPQR_INVALID_NODE; + } + ++numSecondSide; + } + moveArc = getNextNodeArc(dec, moveArc, articulationNode); + } while( moveArc != firstArc ); + + if( numFirstSide == 1 ) + { + *adjacentSplittingArc = firstSideArc; + return firstSideCandidate; + } else if( numSecondSide == 1 ) + { + *adjacentSplittingArc = secondSideArc; + return secondSideCandidate; + } + return SPQR_INVALID_NODE; +} + + +static void rigidGetSplittableArticulationPointsOnPath( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const reduced_member_id toCheck, + spqr_node firstNode, + spqr_node secondNode +) +{ + assert(newRow->reducedMembers[toCheck].numCutArcs > 1); + + int totalNumNodes = getNumNodes(dec); + int* nodeNumPaths = newRow->crossingPathCount; + + for( int i = 0; i < totalNumNodes; ++i ) + { + nodeNumPaths[i] = 0; + } + + intersectionOfAllPaths(dec, newRow, toCheck, nodeNumPaths); + + newRow->numArticulationNodes = 0; + + ArticulationNodeInformation* artNodeInfo = newRow->articulationNodeSearchInfo; + //TODO: ugly; we clean up over all decomposition nodes for every component + //clean up can not easily be done in the search, unfortunately + for( int i = 0; i < totalNumNodes; ++i ) + { + artNodeInfo[i].low = 0; + artNodeInfo[i].discoveryTime = 0; + } + + articulationPoints(dec, newRow, artNodeInfo, toCheck); + + int numCutArcs = newRow->reducedMembers[toCheck].numCutArcs; + for( int i = 0; i < newRow->numArticulationNodes; i++ ) + { + spqr_node articulationNode = newRow->articulationNodes[i]; + assert(nodeIsRepresentative(dec, articulationNode)); + SCIP_Bool isOnPath = nodeNumPaths[articulationNode] == numCutArcs; + if( isOnPath && + (( SPQRnodeIsInvalid(firstNode) && SPQRnodeIsInvalid(secondNode)) || articulationNode == firstNode || + articulationNode == secondNode )) + { + SCIP_Bool isGood = TRUE; + rigidConnectedColoring(dec, newRow, toCheck, articulationNode, &isGood); + if( !isGood ) + { + continue; + } + newRow->reducedMembers[toCheck].splitNode = articulationNode; + + //Once we have found one node, we can (possibly) find another by looking at the coloring of the neighbourhood of it. + + spqr_arc adjacentSplittingArc = SPQR_INVALID_ARC; + spqr_node adjacentSplittingNode = checkNeighbourColoringArticulationNode(dec, newRow, articulationNode, + &adjacentSplittingArc); + + //If the neighbour-coloring works, we still need to check that the adjacent node + //is also an articulation node + if( SPQRnodeIsValid(adjacentSplittingNode) && + (( SPQRnodeIsInvalid(firstNode) && SPQRnodeIsInvalid(secondNode)) || adjacentSplittingNode == firstNode || + adjacentSplittingNode == secondNode )) + { + SCIP_Bool isArticulationNode = FALSE; + for( int j = 0; j < newRow->numArticulationNodes; ++j ) + { + if( newRow->articulationNodes[j] == adjacentSplittingNode ) + { + isArticulationNode = TRUE; + break; + } } - newRow->reducedMembers[toCheck].splitNode = articulationNode; - - //Once we have found one node, we can (possibly) find another by looking at the coloring of the neighbourhood of it. - - spqr_arc adjacentSplittingArc = SPQR_INVALID_ARC; - spqr_node adjacentSplittingNode = checkNeighbourColoringArticulationNode(dec, newRow, articulationNode, - &adjacentSplittingArc); - - //If the neighbour-coloring works, we still need to check that the adjacent node - //is also an articulation node - if(SPQRnodeIsValid(adjacentSplittingNode) && ((SPQRnodeIsInvalid(firstNode) && SPQRnodeIsInvalid(secondNode)) - || adjacentSplittingNode == firstNode || adjacentSplittingNode == secondNode)){ - SCIP_Bool isArticulationNode = FALSE; - for (int j = 0; j < newRow->numArticulationNodes; ++j) { - if (newRow->articulationNodes[j] == adjacentSplittingNode) { - isArticulationNode = TRUE; - break; - } - } - if (isArticulationNode) { - newRow->reducedMembers[toCheck].articulationArc = adjacentSplittingArc; - newRow->reducedMembers[toCheck].otherNode = adjacentSplittingNode; - newRow->reducedMembers[toCheck].otherIsSource = newRow->nodeColors[adjacentSplittingNode] == COLOR_SOURCE; - - //Cleaning up the colors - { - spqr_arc firstNodeArc = getFirstNodeArc(dec, articulationNode); - spqr_arc itArc = firstNodeArc; - do { - spqr_node head = findArcHead(dec, itArc); - spqr_node tail = findArcTail(dec, itArc); - spqr_node otherNode = articulationNode == head ? tail : head; - newRow->nodeColors[otherNode] = UNCOLORED; - itArc = getNextNodeArc(dec, itArc, articulationNode); - } while (itArc != firstNodeArc); - - } - } + if( isArticulationNode ) + { + newRow->reducedMembers[toCheck].articulationArc = adjacentSplittingArc; + newRow->reducedMembers[toCheck].otherNode = adjacentSplittingNode; + newRow->reducedMembers[toCheck].otherIsSource = + newRow->nodeColors[adjacentSplittingNode] == COLOR_SOURCE; + + //Cleaning up the colors + { + spqr_arc firstNodeArc = getFirstNodeArc(dec, articulationNode); + spqr_arc itArc = firstNodeArc; + do + { + spqr_node head = findArcHead(dec, itArc); + spqr_node tail = findArcTail(dec, itArc); + spqr_node otherNode = articulationNode == head ? tail : head; + newRow->nodeColors[otherNode] = UNCOLORED; + itArc = getNextNodeArc(dec, itArc, articulationNode); + } while( itArc != firstNodeArc ); + } } - return; - } - } - - newRow->reducedMembers[toCheck].type = TYPE_NOT_NETWORK; - newRow->remainsNetwork = FALSE; -} - -static void determineParallelType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - const reduced_member_id toCheckMember, const spqr_arc markerToOther, - const reduced_member_id otherMember, const spqr_arc markerToCheck){ - - SPQRRowReducedMember * member = &newRow->reducedMembers[toCheckMember]; - assert(cutArcIsValid(member->firstCutArc)); - - SCIP_Bool good = TRUE; - SCIP_Bool isReversed = TRUE; - int countedCutArcs = 0; - for (cut_arc_id cutArc = member->firstCutArc; cutArcIsValid(cutArc); - cutArc = newRow->cutArcs[cutArc].nextMember){ - spqr_arc arc = newRow->cutArcs[cutArc].arc; - SCIP_Bool arcIsReversed = arcIsReversedNonRigid(dec,arc) != newRow->cutArcs[cutArc].arcReversed; - if(countedCutArcs == 0){ - isReversed = arcIsReversed; - }else if(arcIsReversed != isReversed){ - good = FALSE; - break; - } - ++countedCutArcs; - } - if(!good){ - member->type = TYPE_NOT_NETWORK; - newRow->remainsNetwork = FALSE; - return; - } - //we can only propagate if the marker arc is a tree arc and all other arcs are cut - if(!arcIsTree(dec,markerToOther) || - countedCutArcs != (getNumMemberArcs(dec,newRow->reducedMembers[toCheckMember].member) - 1)){ - //In all other cases, the bond can be split so that the result will be okay! - newRow->reducedMembers[toCheckMember].type = TYPE_MERGED; - }else{ - SCIP_Bool markerIsReversed = arcIsReversedNonRigid(dec,markerToOther); - createCutArc(dec,newRow,markerToCheck,otherMember, markerIsReversed != isReversed); - newRow->reducedMembers[toCheckMember].type = TYPE_PROPAGATED; - } - -} - -static void determineSeriesType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - const reduced_member_id toCheckMember, const spqr_arc markerToOther, - const reduced_member_id otherMember, const spqr_arc markerToCheck){ - //Propagation only calls this function if the arc is tree already, so we do not check it here. - assert(arcIsTree(dec,markerToOther)); - assert(newRow->reducedMembers[toCheckMember].numCutArcs == 1); - assert(cutArcIsValid(newRow->reducedMembers[toCheckMember].firstCutArc)); - spqr_arc cutArc = newRow->reducedMembers[toCheckMember].firstCutArc; - spqr_arc arc = newRow->cutArcs[cutArc].arc; - newRow->reducedMembers[toCheckMember].type = TYPE_PROPAGATED; - createCutArc(dec,newRow,markerToCheck,otherMember, - (arcIsReversedNonRigid(dec,arc) == arcIsReversedNonRigid(dec,markerToOther)) != newRow->cutArcs[cutArc].arcReversed); -} - -static void determineRigidType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - const reduced_member_id toCheckMember, const spqr_arc markerToOther, - const reduced_member_id otherMember, const spqr_arc markerToCheck){ - assert(newRow->reducedMembers[toCheckMember].numCutArcs > 0);//Checking for propagation only makes sense if there is at least one cut arc - - rigidFindStarNodes(dec,newRow,toCheckMember); - if(!newRow->remainsNetwork){ - return; - } - spqr_node markerHead = findArcHead(dec,markerToOther); - spqr_node markerTail = findArcTail(dec,markerToOther); - if(findArcSign(dec,markerToOther).reversed){ - spqr_node temp = markerHead; - markerHead = markerTail; - markerTail = temp; - } - if(SPQRnodeIsInvalid(newRow->reducedMembers[toCheckMember].splitNode)){ - //not a star => attempt to find splittable nodes using articulation node algorithms - //We save some work by telling the methods that only the marker nodes should be checked - rigidGetSplittableArticulationPointsOnPath(dec,newRow,toCheckMember,markerHead,markerTail); - } - if(!newRow->remainsNetwork){ - return; - } - - - if(SPQRarcIsValid(newRow->reducedMembers[toCheckMember].articulationArc) && - newRow->reducedMembers[toCheckMember].articulationArc == markerToOther){ - newRow->reducedMembers[toCheckMember].type = TYPE_PROPAGATED; - SCIP_Bool reverse =(markerHead == newRow->reducedMembers[toCheckMember].splitNode) != - newRow->reducedMembers[toCheckMember].otherIsSource; - - createCutArc(dec,newRow,markerToCheck,otherMember,reverse); - }else if(newRow->reducedMembers[toCheckMember].splitNode == markerHead || - newRow->reducedMembers[toCheckMember].splitNode == markerTail){ - newRow->reducedMembers[toCheckMember].type = TYPE_MERGED; - }else if(SPQRarcIsValid(newRow->reducedMembers[toCheckMember].articulationArc) && - (newRow->reducedMembers[toCheckMember].otherNode == markerHead || - newRow->reducedMembers[toCheckMember].otherNode == markerTail) - ){ - newRow->reducedMembers[toCheckMember].type = TYPE_MERGED; - }else{ - //Found source or sinks, but not adjacent to the marker - newRow->reducedMembers[toCheckMember].type = TYPE_NOT_NETWORK; - newRow->remainsNetwork = FALSE; - } - -} -static RowReducedMemberType determineType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - const reduced_member_id toCheckMember, const spqr_arc markerToOther, - const reduced_member_id otherMember, const spqr_arc markerToCheck){ - assert(newRow->reducedMembers[toCheckMember].type == TYPE_UNDETERMINED); - switch (getMemberType(dec,newRow->reducedMembers[toCheckMember].member)) { - case SPQR_MEMBERTYPE_RIGID: - { - determineRigidType(dec,newRow,toCheckMember,markerToOther,otherMember,markerToCheck); - break; + } + return; + } + } + + newRow->reducedMembers[toCheck].type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; +} + +static void determineParallelType( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const reduced_member_id toCheckMember, + const spqr_arc markerToOther, + const reduced_member_id otherMember, + const spqr_arc markerToCheck +) +{ - } - case SPQR_MEMBERTYPE_PARALLEL: - { - determineParallelType(dec,newRow,toCheckMember,markerToOther,otherMember,markerToCheck); - break; - } - case SPQR_MEMBERTYPE_SERIES: - { - determineSeriesType(dec,newRow,toCheckMember,markerToOther,otherMember,markerToCheck); - break; - } - default: - assert(FALSE); - newRow->reducedMembers[toCheckMember].type = TYPE_NOT_NETWORK; - } - - return newRow->reducedMembers[toCheckMember].type; -} - -static void propagateComponents(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow){ - int leafArrayIndex = 0; - - reduced_member_id leaf; - reduced_member_id next; - - while(leafArrayIndex != newRow->numLeafMembers){ - leaf = newRow->leafMembers[leafArrayIndex]; - //next is invalid if the member is not in the reduced decomposition. - next = newRow->reducedMembers[leaf].parent; - spqr_arc parentMarker = markerToParent(dec, newRow->reducedMembers[leaf].member); - if(next != INVALID_REDUCED_MEMBER && arcIsTree(dec,parentMarker)){ - assert(reducedMemberIsValid(next)); - assert(SPQRarcIsValid(parentMarker)); - RowReducedMemberType type = determineType(dec,newRow,leaf,parentMarker,next,markerOfParent(dec,newRow->reducedMembers[leaf].member)); - if(type == TYPE_PROPAGATED){ - ++newRow->reducedMembers[next].numPropagatedChildren; - if(newRow->reducedMembers[next].numPropagatedChildren == newRow->reducedMembers[next].numChildren){ - newRow->leafMembers[leafArrayIndex] = next; - }else{ - ++leafArrayIndex; - } - }else if(type == TYPE_NOT_NETWORK){ - return; - }else{ - assert(type == TYPE_MERGED); - ++leafArrayIndex; + SPQRRowReducedMember* member = &newRow->reducedMembers[toCheckMember]; + assert(cutArcIsValid(member->firstCutArc)); + + SCIP_Bool good = TRUE; + SCIP_Bool isReversed = TRUE; + int countedCutArcs = 0; + for( cut_arc_id cutArc = member->firstCutArc; cutArcIsValid(cutArc); + cutArc = newRow->cutArcs[cutArc].nextMember ) + { + spqr_arc arc = newRow->cutArcs[cutArc].arc; + SCIP_Bool arcIsReversed = arcIsReversedNonRigid(dec, arc) != newRow->cutArcs[cutArc].arcReversed; + if( countedCutArcs == 0 ) + { + isReversed = arcIsReversed; + } else if( arcIsReversed != isReversed ) + { + good = FALSE; + break; + } + ++countedCutArcs; + } + if( !good ) + { + member->type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + return; + } + //we can only propagate if the marker arc is a tree arc and all other arcs are cut + if( !arcIsTree(dec, markerToOther) || + countedCutArcs != ( getNumMemberArcs(dec, newRow->reducedMembers[toCheckMember].member) - 1 )) + { + //In all other cases, the bond can be split so that the result will be okay! + newRow->reducedMembers[toCheckMember].type = TYPE_MERGED; + } else + { + SCIP_Bool markerIsReversed = arcIsReversedNonRigid(dec, markerToOther); + createCutArc(dec, newRow, markerToCheck, otherMember, markerIsReversed != isReversed); + newRow->reducedMembers[toCheckMember].type = TYPE_PROPAGATED; + } +} + +static void determineSeriesType( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const reduced_member_id toCheckMember, + const spqr_arc markerToOther, + const reduced_member_id otherMember, + const spqr_arc markerToCheck +) +{ + //Propagation only calls this function if the arc is tree already, so we do not check it here. + assert(arcIsTree(dec, markerToOther)); + assert(newRow->reducedMembers[toCheckMember].numCutArcs == 1); + assert(cutArcIsValid(newRow->reducedMembers[toCheckMember].firstCutArc)); + spqr_arc cutArc = newRow->reducedMembers[toCheckMember].firstCutArc; + spqr_arc arc = newRow->cutArcs[cutArc].arc; + newRow->reducedMembers[toCheckMember].type = TYPE_PROPAGATED; + createCutArc(dec, newRow, markerToCheck, otherMember, + ( arcIsReversedNonRigid(dec, arc) == arcIsReversedNonRigid(dec, markerToOther)) != + newRow->cutArcs[cutArc].arcReversed); +} + +static void determineRigidType( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const reduced_member_id toCheckMember, + const spqr_arc markerToOther, + const reduced_member_id otherMember, + const spqr_arc markerToCheck +) +{ + assert(newRow->reducedMembers[toCheckMember].numCutArcs > + 0);//Checking for propagation only makes sense if there is at least one cut arc + + rigidFindStarNodes(dec, newRow, toCheckMember); + if( !newRow->remainsNetwork ) + { + return; + } + spqr_node markerHead = findArcHead(dec, markerToOther); + spqr_node markerTail = findArcTail(dec, markerToOther); + if( findArcSign(dec, markerToOther).reversed ) + { + spqr_node temp = markerHead; + markerHead = markerTail; + markerTail = temp; + } + if( SPQRnodeIsInvalid(newRow->reducedMembers[toCheckMember].splitNode)) + { + //not a star => attempt to find splittable nodes using articulation node algorithms + //We save some work by telling the methods that only the marker nodes should be checked + rigidGetSplittableArticulationPointsOnPath(dec, newRow, toCheckMember, markerHead, markerTail); + } + if( !newRow->remainsNetwork ) + { + return; + } + + + if( SPQRarcIsValid(newRow->reducedMembers[toCheckMember].articulationArc) && + newRow->reducedMembers[toCheckMember].articulationArc == markerToOther ) + { + newRow->reducedMembers[toCheckMember].type = TYPE_PROPAGATED; + SCIP_Bool reverse = ( markerHead == newRow->reducedMembers[toCheckMember].splitNode ) != + newRow->reducedMembers[toCheckMember].otherIsSource; + + createCutArc(dec, newRow, markerToCheck, otherMember, reverse); + } else if( newRow->reducedMembers[toCheckMember].splitNode == markerHead || + newRow->reducedMembers[toCheckMember].splitNode == markerTail ) + { + newRow->reducedMembers[toCheckMember].type = TYPE_MERGED; + } else if( SPQRarcIsValid(newRow->reducedMembers[toCheckMember].articulationArc) && + ( newRow->reducedMembers[toCheckMember].otherNode == markerHead || + newRow->reducedMembers[toCheckMember].otherNode == markerTail )) + { + newRow->reducedMembers[toCheckMember].type = TYPE_MERGED; + } else + { + //Found source or sinks, but not adjacent to the marker + newRow->reducedMembers[toCheckMember].type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + } +} + +static RowReducedMemberType determineType( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const reduced_member_id toCheckMember, + const spqr_arc markerToOther, + const reduced_member_id otherMember, + const spqr_arc markerToCheck +) +{ + assert(newRow->reducedMembers[toCheckMember].type == TYPE_UNDETERMINED); + switch( getMemberType(dec, newRow->reducedMembers[toCheckMember].member)) + { + case SPQR_MEMBERTYPE_RIGID: + { + determineRigidType(dec, newRow, toCheckMember, markerToOther, otherMember, markerToCheck); + break; + } + case SPQR_MEMBERTYPE_PARALLEL: + { + determineParallelType(dec, newRow, toCheckMember, markerToOther, otherMember, markerToCheck); + break; + } + case SPQR_MEMBERTYPE_SERIES: + { + determineSeriesType(dec, newRow, toCheckMember, markerToOther, otherMember, markerToCheck); + break; + } + default: + assert(FALSE); + newRow->reducedMembers[toCheckMember].type = TYPE_NOT_NETWORK; + } + + return newRow->reducedMembers[toCheckMember].type; +} + +static void propagateComponents( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow +) +{ + int leafArrayIndex = 0; + + reduced_member_id leaf; + reduced_member_id next; + + while( leafArrayIndex != newRow->numLeafMembers ) + { + leaf = newRow->leafMembers[leafArrayIndex]; + //next is invalid if the member is not in the reduced decomposition. + next = newRow->reducedMembers[leaf].parent; + spqr_arc parentMarker = markerToParent(dec, newRow->reducedMembers[leaf].member); + if( next != INVALID_REDUCED_MEMBER && arcIsTree(dec, parentMarker)) + { + assert(reducedMemberIsValid(next)); + assert(SPQRarcIsValid(parentMarker)); + RowReducedMemberType type = determineType(dec, newRow, leaf, parentMarker, next, + markerOfParent(dec, newRow->reducedMembers[leaf].member)); + if( type == TYPE_PROPAGATED ) + { + ++newRow->reducedMembers[next].numPropagatedChildren; + if( newRow->reducedMembers[next].numPropagatedChildren == newRow->reducedMembers[next].numChildren ) + { + newRow->leafMembers[leafArrayIndex] = next; + } else + { + ++leafArrayIndex; } - }else{ + } else if( type == TYPE_NOT_NETWORK ) + { + return; + } else + { + assert(type == TYPE_MERGED); ++leafArrayIndex; - } - - } - - for (int j = 0; j < newRow->numReducedComponents; ++j) { - //The reduced root might be a leaf as well: we propagate it last - reduced_member_id root = newRow->reducedComponents[j].root; - - while(TRUE){ - if(newRow->reducedMembers[root].numPropagatedChildren == newRow->reducedMembers[root].numChildren -1){ - //TODO: bit ugly, have to do a linear search for the child - reduced_member_id child = INVALID_REDUCED_MEMBER; - spqr_arc markerToChild = SPQR_INVALID_ARC; - for (children_idx i = newRow->reducedMembers[root].firstChild; i < newRow->reducedMembers[root].firstChild + newRow->reducedMembers[root].numChildren; ++i) { - reduced_member_id childReduced = newRow->childrenStorage[i]; - if(newRow->reducedMembers[childReduced].type != TYPE_PROPAGATED){ - child = childReduced; - markerToChild = markerOfParent(dec,newRow->reducedMembers[child].member); - break; - } - } - assert(SPQRmemberIsValid(newRow->reducedMembers[child].member)); - assert(SPQRarcIsValid(markerToChild)); - if(!arcIsTree(dec,markerToChild)){ - break; - } - RowReducedMemberType type = determineType(dec,newRow,root,markerToChild,child,markerToParent(dec,newRow->reducedMembers[child].member)); - if(type == TYPE_PROPAGATED){ - root = child; - }else if(type == TYPE_NOT_NETWORK){ - return; - }else{ - break; - } - }else{ - break; + } + } else + { + ++leafArrayIndex; + } + } + + for( int j = 0; j < newRow->numReducedComponents; ++j ) + { + //The reduced root might be a leaf as well: we propagate it last + reduced_member_id root = newRow->reducedComponents[j].root; + + while( TRUE ) + { + if( newRow->reducedMembers[root].numPropagatedChildren == newRow->reducedMembers[root].numChildren - 1 ) + { + //TODO: bit ugly, have to do a linear search for the child + reduced_member_id child = INVALID_REDUCED_MEMBER; + spqr_arc markerToChild = SPQR_INVALID_ARC; + for( children_idx i = newRow->reducedMembers[root].firstChild; + i < newRow->reducedMembers[root].firstChild + newRow->reducedMembers[root].numChildren; ++i ) + { + reduced_member_id childReduced = newRow->childrenStorage[i]; + if( newRow->reducedMembers[childReduced].type != TYPE_PROPAGATED ) + { + child = childReduced; + markerToChild = markerOfParent(dec, newRow->reducedMembers[child].member); + break; + } + } + assert(SPQRmemberIsValid(newRow->reducedMembers[child].member)); + assert(SPQRarcIsValid(markerToChild)); + if( !arcIsTree(dec, markerToChild)) + { + break; + } + RowReducedMemberType type = determineType(dec, newRow, root, markerToChild, child, + markerToParent(dec, newRow->reducedMembers[child].member)); + if( type == TYPE_PROPAGATED ) + { + root = child; + } else if( type == TYPE_NOT_NETWORK ) + { + return; + } else + { + break; } - } - newRow->reducedComponents[j].root = root; - newRow->reducedMembers[root].parent = INVALID_REDUCED_MEMBER; - } + } else + { + break; + } + } + newRow->reducedComponents[j].root = root; + newRow->reducedMembers[root].parent = INVALID_REDUCED_MEMBER; + } } -typedef struct { - spqr_node first; - spqr_node second; +typedef struct +{ + spqr_node first; + spqr_node second; } Nodes; + static Nodes -rigidDetermineCandidateNodesFromAdjacentComponents(SCIP_NETWORKDECOMP *dec, - SCIP_NETWORKROWADDITION *newRow, const reduced_member_id toCheck) { - - Nodes pair; - pair.first = SPQR_INVALID_NODE; - pair.second = SPQR_INVALID_NODE; - - //take union of children's arcs nodes to find one or two candidates - for (children_idx i = newRow->reducedMembers[toCheck].firstChild; - i < newRow->reducedMembers[toCheck].firstChild + newRow->reducedMembers[toCheck].numChildren; ++i) { - reduced_member_id reducedChild = newRow->childrenStorage[i]; - if (newRow->reducedMembers[reducedChild].type != TYPE_PROPAGATED) { - spqr_arc arc = markerOfParent(dec, newRow->reducedMembers[reducedChild].member); - spqr_node head = findArcHead(dec, arc); - spqr_node tail = findArcTail(dec, arc); - if(SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second)){ - pair.first = head; - pair.second = tail; - }else{ - if(pair.first != head && pair.first != tail){ - pair.first = SPQR_INVALID_NODE; - } - if(pair.second != head && pair.second != tail){ - pair.second = SPQR_INVALID_NODE; - } - } - if (SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second)) { - return pair; - } - } - } - if (reducedMemberIsValid(newRow->reducedMembers[toCheck].parent) && - newRow->reducedMembers[newRow->reducedMembers[toCheck].parent].type != TYPE_PROPAGATED) { - - spqr_arc arc = markerToParent(dec, newRow->reducedMembers[toCheck].member); - spqr_node head = findArcHead(dec, arc); - spqr_node tail = findArcTail(dec, arc); - if(SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second)){ +rigidDetermineCandidateNodesFromAdjacentComponents( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const reduced_member_id toCheck +) +{ + + Nodes pair; + pair.first = SPQR_INVALID_NODE; + pair.second = SPQR_INVALID_NODE; + + //take union of children's arcs nodes to find one or two candidates + for( children_idx i = newRow->reducedMembers[toCheck].firstChild; + i < newRow->reducedMembers[toCheck].firstChild + newRow->reducedMembers[toCheck].numChildren; ++i ) + { + reduced_member_id reducedChild = newRow->childrenStorage[i]; + if( newRow->reducedMembers[reducedChild].type != TYPE_PROPAGATED ) + { + spqr_arc arc = markerOfParent(dec, newRow->reducedMembers[reducedChild].member); + spqr_node head = findArcHead(dec, arc); + spqr_node tail = findArcTail(dec, arc); + if( SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second)) + { pair.first = head; pair.second = tail; - }else{ - if(pair.first != head && pair.first != tail){ - pair.first = SPQR_INVALID_NODE; + } else + { + if( pair.first != head && pair.first != tail ) + { + pair.first = SPQR_INVALID_NODE; } - if(pair.second != head && pair.second != tail){ - pair.second = SPQR_INVALID_NODE; + if( pair.second != head && pair.second != tail ) + { + pair.second = SPQR_INVALID_NODE; } - } - } - return pair; -} - -static SCIP_RETCODE allocateTreeSearchMemory(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow){ - int necessarySpace = newRow->numReducedMembers; - if( necessarySpace > newRow->memMergeTreeCallData ){ - int updatedSize = max(2*newRow->memMergeTreeCallData,necessarySpace); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env,&newRow->mergeTreeCallData,newRow->memMergeTreeCallData, - updatedSize)); - newRow->memMergeTreeCallData = updatedSize; - } - return SCIP_OKAY; -} - -static void determineSingleRowRigidType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id reducedMember, spqr_member member){ - assert(newRow->reducedMembers[reducedMember].numCutArcs > 0);//Checking for propagation only makes sense if there is at least one cut arc - rigidFindStarNodes(dec,newRow,reducedMember); - if(!newRow->remainsNetwork){ - return; - } - - if(SPQRnodeIsInvalid(newRow->reducedMembers[reducedMember].splitNode)){ - //not a star => attempt to find splittable nodes using articulation node algorithms - rigidGetSplittableArticulationPointsOnPath(dec,newRow,reducedMember,SPQR_INVALID_NODE,SPQR_INVALID_NODE); - } - if(SPQRnodeIsValid(newRow->reducedMembers[reducedMember].splitNode)){ - newRow->reducedMembers[reducedMember].type = TYPE_MERGED; - }else{ - newRow->reducedMembers[reducedMember].type = TYPE_NOT_NETWORK; - newRow->remainsNetwork = FALSE; - } - -} -static void determineSingleParallelType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id reducedMember, spqr_member member){ - SPQRRowReducedMember * redMember = &newRow->reducedMembers[reducedMember]; - assert(cutArcIsValid(redMember->firstCutArc)); - - SCIP_Bool good = TRUE; - SCIP_Bool isReversed = TRUE; - int countedCutArcs = 0; - for (cut_arc_id cutArc = redMember->firstCutArc; cutArcIsValid(cutArc); - cutArc = newRow->cutArcs[cutArc].nextMember){ - spqr_arc arc = newRow->cutArcs[cutArc].arc; - SCIP_Bool arcIsReversed = arcIsReversedNonRigid(dec,arc) != newRow->cutArcs[cutArc].arcReversed; - if(countedCutArcs == 0){ + } + if( SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second)) + { + return pair; + } + } + } + if( reducedMemberIsValid(newRow->reducedMembers[toCheck].parent) && + newRow->reducedMembers[newRow->reducedMembers[toCheck].parent].type != TYPE_PROPAGATED ) + { + + spqr_arc arc = markerToParent(dec, newRow->reducedMembers[toCheck].member); + spqr_node head = findArcHead(dec, arc); + spqr_node tail = findArcTail(dec, arc); + if( SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second)) + { + pair.first = head; + pair.second = tail; + } else + { + if( pair.first != head && pair.first != tail ) + { + pair.first = SPQR_INVALID_NODE; + } + if( pair.second != head && pair.second != tail ) + { + pair.second = SPQR_INVALID_NODE; + } + } + } + return pair; +} + +static SCIP_RETCODE allocateTreeSearchMemory( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow +) +{ + int necessarySpace = newRow->numReducedMembers; + if( necessarySpace > newRow->memMergeTreeCallData ) + { + int updatedSize = max(2 * newRow->memMergeTreeCallData, necessarySpace); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->mergeTreeCallData, newRow->memMergeTreeCallData, + updatedSize)); + newRow->memMergeTreeCallData = updatedSize; + } + return SCIP_OKAY; +} + +static void determineSingleRowRigidType( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id reducedMember, + spqr_member member +) +{ + assert(newRow->reducedMembers[reducedMember].numCutArcs > + 0);//Checking for propagation only makes sense if there is at least one cut arc + rigidFindStarNodes(dec, newRow, reducedMember); + if( !newRow->remainsNetwork ) + { + return; + } + + if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedMember].splitNode)) + { + //not a star => attempt to find splittable nodes using articulation node algorithms + rigidGetSplittableArticulationPointsOnPath(dec, newRow, reducedMember, SPQR_INVALID_NODE, SPQR_INVALID_NODE); + } + if( SPQRnodeIsValid(newRow->reducedMembers[reducedMember].splitNode)) + { + newRow->reducedMembers[reducedMember].type = TYPE_MERGED; + } else + { + newRow->reducedMembers[reducedMember].type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + } +} + +static void determineSingleParallelType( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id reducedMember, + spqr_member member +) +{ + SPQRRowReducedMember* redMember = &newRow->reducedMembers[reducedMember]; + assert(cutArcIsValid(redMember->firstCutArc)); + + SCIP_Bool good = TRUE; + SCIP_Bool isReversed = TRUE; + int countedCutArcs = 0; + for( cut_arc_id cutArc = redMember->firstCutArc; cutArcIsValid(cutArc); + cutArc = newRow->cutArcs[cutArc].nextMember ) + { + spqr_arc arc = newRow->cutArcs[cutArc].arc; + SCIP_Bool arcIsReversed = arcIsReversedNonRigid(dec, arc) != newRow->cutArcs[cutArc].arcReversed; + if( countedCutArcs == 0 ) + { + isReversed = arcIsReversed; + } else if( arcIsReversed != isReversed ) + { + good = FALSE; + break; + } + ++countedCutArcs; + } + if( !good ) + { + redMember->type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + } else + { + redMember->type = TYPE_MERGED; + } +} + +static void determineSingleSeriesType( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id reducedMember, + spqr_member member +) +{ + assert(newRow->reducedMembers[reducedMember].numCutArcs == 1); + assert(cutArcIsValid(newRow->reducedMembers[reducedMember].firstCutArc)); + newRow->reducedMembers[reducedMember].type = TYPE_MERGED; +} + +static spqr_node determineAndColorSplitNode( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id id, + spqr_node candidateOne, + spqr_node candidateTwo +) +{ + if( SPQRnodeIsInvalid(newRow->reducedMembers[id].splitNode)) + { + return SPQR_INVALID_NODE; + } + spqr_node splitNode = newRow->reducedMembers[id].splitNode; + if( splitNode == candidateOne || splitNode == candidateTwo ) + { + if( newRow->reducedMembers[id].allHaveCommonNode ) + { + spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode); + spqr_arc iterArc = firstNodeArc; + COLOR_STATUS color = newRow->reducedMembers[id].otherIsSource ? COLOR_SOURCE : COLOR_SINK; + do + { + spqr_node head = findArcHead(dec, iterArc); + spqr_node other = head == splitNode ? findArcTail(dec, iterArc) : head; + newRow->nodeColors[other] = color; + iterArc = getNextNodeArc(dec, iterArc, splitNode); + } while( iterArc != firstNodeArc ); + newRow->reducedMembers[id].coloredNode = splitNode; + } + return splitNode; + } + splitNode = newRow->reducedMembers[id].otherNode; + if( SPQRnodeIsInvalid(splitNode) || ( splitNode != candidateOne && splitNode != candidateTwo )) + { + return SPQR_INVALID_NODE; + } + assert(SPQRarcIsValid(newRow->reducedMembers[id].articulationArc)); + if( newRow->reducedMembers[id].allHaveCommonNode ) + { + spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode); + spqr_arc iterArc = firstNodeArc; + COLOR_STATUS color; + if( newRow->reducedMembers[id].numCutArcs == 1 ) + { + color = newRow->reducedMembers[id].otherIsSource ? COLOR_SINK : COLOR_SOURCE; + } else + { + color = newRow->reducedMembers[id].otherIsSource ? COLOR_SOURCE : COLOR_SINK; + } + do + { + spqr_node head = findArcHead(dec, iterArc); + spqr_node other = head == splitNode ? findArcTail(dec, iterArc) : head; + newRow->nodeColors[other] = color; + iterArc = getNextNodeArc(dec, iterArc, splitNode); + } while( iterArc != firstNodeArc ); + newRow->nodeColors[newRow->reducedMembers[id].splitNode] = newRow->reducedMembers[id].otherIsSource ? COLOR_SINK + : COLOR_SOURCE; + + } else + { + COLOR_STATUS splitColor = newRow->nodeColors[splitNode]; + + spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode); + spqr_arc iterArc = firstNodeArc; + do + { + spqr_node head = findArcHead(dec, iterArc); + spqr_node other = head == splitNode ? findArcTail(dec, iterArc) : head; + newRow->nodeColors[other] = splitColor; + iterArc = getNextNodeArc(dec, iterArc, splitNode); + } while( iterArc != firstNodeArc ); + newRow->nodeColors[newRow->reducedMembers[id].splitNode] = splitColor == COLOR_SOURCE ? COLOR_SINK : COLOR_SOURCE; + newRow->nodeColors[splitNode] = UNCOLORED; + } + + newRow->reducedMembers[id].coloredNode = splitNode; + return splitNode; +} + +static void determineSplitTypeFirstLeaf( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id reducedId +) +{ + spqr_member member = newRow->reducedMembers[reducedId].member; + SPQRMemberType type = getMemberType(dec, member); + assert(type == SPQR_MEMBERTYPE_PARALLEL || type == SPQR_MEMBERTYPE_RIGID); + assert(cutArcIsValid(newRow->reducedMembers[reducedId].firstCutArc)); + SPQRRowReducedMember* redMember = &newRow->reducedMembers[reducedId]; + + if( type == SPQR_MEMBERTYPE_PARALLEL ) + { + //TODO: duplicate-ish + + SCIP_Bool good = TRUE; + SCIP_Bool isReversed = TRUE; + int countedCutArcs = 0; + for( cut_arc_id cutArc = redMember->firstCutArc; cutArcIsValid(cutArc); + cutArc = newRow->cutArcs[cutArc].nextMember ) + { + spqr_arc arc = newRow->cutArcs[cutArc].arc; + SCIP_Bool arcIsReversed = arcIsReversedNonRigid(dec, arc) != newRow->cutArcs[cutArc].arcReversed; + if( countedCutArcs == 0 ) + { isReversed = arcIsReversed; - }else if(arcIsReversed != isReversed){ + } else if( arcIsReversed != isReversed ) + { good = FALSE; break; - } - ++countedCutArcs; - } - if(!good){ - redMember->type = TYPE_NOT_NETWORK; - newRow->remainsNetwork = FALSE; - }else{ - redMember->type = TYPE_MERGED; - } -} -static void determineSingleSeriesType(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id reducedMember, spqr_member member){ - assert(newRow->reducedMembers[reducedMember].numCutArcs == 1); - assert(cutArcIsValid(newRow->reducedMembers[reducedMember].firstCutArc)); - newRow->reducedMembers[reducedMember].type = TYPE_MERGED; -} - -static spqr_node determineAndColorSplitNode(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKROWADDITION * newRow, - reduced_member_id id, spqr_node candidateOne, spqr_node candidateTwo){ - if(SPQRnodeIsInvalid(newRow->reducedMembers[id].splitNode)){ - return SPQR_INVALID_NODE; - } - spqr_node splitNode = newRow->reducedMembers[id].splitNode; - if(splitNode == candidateOne || splitNode == candidateTwo){ - if(newRow->reducedMembers[id].allHaveCommonNode){ - spqr_arc firstNodeArc = getFirstNodeArc(dec,splitNode); - spqr_arc iterArc = firstNodeArc; - COLOR_STATUS color = newRow->reducedMembers[id].otherIsSource ? COLOR_SOURCE : COLOR_SINK; - do{ - spqr_node head = findArcHead(dec,iterArc); - spqr_node other = head == splitNode ? findArcTail(dec,iterArc) : head; - newRow->nodeColors[other] = color; - iterArc = getNextNodeArc(dec,iterArc,splitNode); - }while(iterArc != firstNodeArc); - newRow->reducedMembers[id].coloredNode = splitNode; - } - return splitNode; - } - splitNode = newRow->reducedMembers[id].otherNode; - if(SPQRnodeIsInvalid(splitNode) || (splitNode != candidateOne && splitNode != candidateTwo)){ - return SPQR_INVALID_NODE; - } - assert(SPQRarcIsValid(newRow->reducedMembers[id].articulationArc)); - if(newRow->reducedMembers[id].allHaveCommonNode){ - spqr_arc firstNodeArc = getFirstNodeArc(dec,splitNode); - spqr_arc iterArc = firstNodeArc; - COLOR_STATUS color; - if(newRow->reducedMembers[id].numCutArcs == 1){ - color = newRow->reducedMembers[id].otherIsSource ? COLOR_SINK : COLOR_SOURCE; - }else{ - color = newRow->reducedMembers[id].otherIsSource ? COLOR_SOURCE : COLOR_SINK; - } - do{ - spqr_node head = findArcHead(dec,iterArc); - spqr_node other = head == splitNode ? findArcTail(dec,iterArc) : head; - newRow->nodeColors[other] = color; - iterArc = getNextNodeArc(dec,iterArc,splitNode); - }while(iterArc != firstNodeArc); - newRow->nodeColors[newRow->reducedMembers[id].splitNode] = newRow->reducedMembers[id].otherIsSource ? COLOR_SINK : COLOR_SOURCE; - - }else{ - COLOR_STATUS splitColor = newRow->nodeColors[splitNode]; - - spqr_arc firstNodeArc = getFirstNodeArc(dec,splitNode); - spqr_arc iterArc = firstNodeArc; - do{ - spqr_node head = findArcHead(dec,iterArc); - spqr_node other = head == splitNode ? findArcTail(dec,iterArc) : head; - newRow->nodeColors[other] = splitColor; - iterArc = getNextNodeArc(dec,iterArc,splitNode); - }while(iterArc != firstNodeArc); - newRow->nodeColors[newRow->reducedMembers[id].splitNode] = splitColor == COLOR_SOURCE ? COLOR_SINK : COLOR_SOURCE; - newRow->nodeColors[splitNode] = UNCOLORED; - } - - newRow->reducedMembers[id].coloredNode = splitNode; - return splitNode; -} -static void determineSplitTypeFirstLeaf(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id reducedId){ - spqr_member member = newRow->reducedMembers[reducedId].member; - SPQRMemberType type = getMemberType(dec,member); - assert(type == SPQR_MEMBERTYPE_PARALLEL || type == SPQR_MEMBERTYPE_RIGID); - assert(cutArcIsValid(newRow->reducedMembers[reducedId].firstCutArc)); - SPQRRowReducedMember * redMember = &newRow->reducedMembers[reducedId]; - - if(type == SPQR_MEMBERTYPE_PARALLEL){ - //TODO: duplicate-ish - - SCIP_Bool good = TRUE; - SCIP_Bool isReversed = TRUE; - int countedCutArcs = 0; - for (cut_arc_id cutArc = redMember->firstCutArc; cutArcIsValid(cutArc); - cutArc = newRow->cutArcs[cutArc].nextMember){ - spqr_arc arc = newRow->cutArcs[cutArc].arc; - SCIP_Bool arcIsReversed = arcIsReversedNonRigid(dec,arc) != newRow->cutArcs[cutArc].arcReversed; - if(countedCutArcs == 0){ - isReversed = arcIsReversed; - }else if(arcIsReversed != isReversed){ - good = FALSE; - break; - } - ++countedCutArcs; - } - if(!good){ - redMember->type = TYPE_NOT_NETWORK; - newRow->remainsNetwork = FALSE; - }else{ - spqr_arc marker = markerToParent(dec,member); - redMember->type = TYPE_MERGED; - redMember->splitArc = marker; - redMember->splitHead = TRUE; - redMember->otherIsSource = arcIsReversedNonRigid(dec,marker) == isReversed; - } - return; - } - assert(type == SPQR_MEMBERTYPE_RIGID); - - spqr_arc marker = markerToParent(dec,member); - spqr_node markerHead = findArcHead(dec,marker); - spqr_node markerTail = findArcTail(dec,marker); - if(findArcSign(dec,marker).reversed){ - spqr_node temp = markerHead; - markerHead = markerTail; - markerTail = temp; - } - - if(!SPQRnodeIsValid(newRow->reducedMembers[reducedId].splitNode)){ - assert(newRow->reducedMembers[reducedId].numCutArcs > 0);//Checking for propagation only makes sense if there is at least one cut arc - - rigidFindStarNodes(dec,newRow,reducedId); - if(!newRow->remainsNetwork){ - return; - } - - if(SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode)){ - //not a star => attempt to find splittable nodes using articulation node algorithms - //We save some work by telling the methods that only the marker nodes should be checked - rigidGetSplittableArticulationPointsOnPath(dec,newRow,reducedId,markerHead,markerTail); - } - if(!newRow->remainsNetwork){ - return; - } - if(SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode)){ - redMember->type = TYPE_NOT_NETWORK; - newRow->remainsNetwork = FALSE; - return; - } - - } - - spqr_node splitNode = newRow->reducedMembers[reducedId].splitNode; + } + ++countedCutArcs; + } + if( !good ) + { + redMember->type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + } else + { + spqr_arc marker = markerToParent(dec, member); + redMember->type = TYPE_MERGED; + redMember->splitArc = marker; + redMember->splitHead = TRUE; + redMember->otherIsSource = arcIsReversedNonRigid(dec, marker) == isReversed; + } + return; + } + assert(type == SPQR_MEMBERTYPE_RIGID); + + spqr_arc marker = markerToParent(dec, member); + spqr_node markerHead = findArcHead(dec, marker); + spqr_node markerTail = findArcTail(dec, marker); + if( findArcSign(dec, marker).reversed ) + { + spqr_node temp = markerHead; + markerHead = markerTail; + markerTail = temp; + } + + if( !SPQRnodeIsValid(newRow->reducedMembers[reducedId].splitNode)) + { + assert(newRow->reducedMembers[reducedId].numCutArcs > + 0);//Checking for propagation only makes sense if there is at least one cut arc + + rigidFindStarNodes(dec, newRow, reducedId); + if( !newRow->remainsNetwork ) + { + return; + } + + if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode)) + { + //not a star => attempt to find splittable nodes using articulation node algorithms + //We save some work by telling the methods that only the marker nodes should be checked + rigidGetSplittableArticulationPointsOnPath(dec, newRow, reducedId, markerHead, markerTail); + } + if( !newRow->remainsNetwork ) + { + return; + } + if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode)) + { + redMember->type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + return; + } + } + + spqr_node splitNode = newRow->reducedMembers[reducedId].splitNode; #ifndef NDEBUG - spqr_node otherNode = newRow->reducedMembers[reducedId].otherNode; + spqr_node otherNode = newRow->reducedMembers[reducedId].otherNode; #endif - //We cannot have both splittable (should have been propagated) - assert(!((splitNode == markerTail || splitNode == markerHead) - && (otherNode == markerTail || otherNode == markerHead))); - - splitNode = determineAndColorSplitNode(dec,newRow,reducedId,markerHead,markerTail); - if(SPQRnodeIsInvalid(splitNode)){ - redMember->type = TYPE_NOT_NETWORK; - newRow->remainsNetwork = FALSE; - return; - } - assert(splitNode == markerHead || splitNode == markerTail); - - newRow->reducedMembers[reducedId].splitNode = splitNode; - newRow->reducedMembers[reducedId].willBeReversed = FALSE; - redMember->type = TYPE_MERGED; - -} -typedef struct{ - SCIP_Bool headSplit; - SCIP_Bool otherIsSource; + //We cannot have both splittable (should have been propagated) + assert(!(( splitNode == markerTail || splitNode == markerHead ) && + ( otherNode == markerTail || otherNode == markerHead ))); + + splitNode = determineAndColorSplitNode(dec, newRow, reducedId, markerHead, markerTail); + if( SPQRnodeIsInvalid(splitNode)) + { + redMember->type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + return; + } + assert(splitNode == markerHead || splitNode == markerTail); + + newRow->reducedMembers[reducedId].splitNode = splitNode; + newRow->reducedMembers[reducedId].willBeReversed = FALSE; + redMember->type = TYPE_MERGED; +} + +typedef struct +{ + SCIP_Bool headSplit; + SCIP_Bool otherIsSource; } SplitOrientation; -static SplitOrientation getRelativeOrientationRigid(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id reducedId, spqr_arc arcToNext){ - assert(findArcMemberNoCompression(dec,arcToNext) == newRow->reducedMembers[reducedId].member); - assert(SPQRnodeIsValid(newRow->reducedMembers[reducedId].splitNode)); - - SplitOrientation orientation; - if(newRow->reducedMembers[reducedId].numCutArcs == 0){ - spqr_node splitNode = newRow->reducedMembers[reducedId].splitNode; - spqr_node head = findEffectiveArcHead(dec,arcToNext); - - assert(head == splitNode || splitNode == findEffectiveArcTailNoCompression(dec,arcToNext)); - - orientation.headSplit = newRow->reducedMembers[reducedId].willBeReversed == (head != splitNode); - orientation.otherIsSource = newRow->reducedMembers[reducedId].otherIsSource; - return orientation; - } - spqr_node splitNode = newRow->reducedMembers[reducedId].splitNode; - spqr_node arcHead = findArcHead(dec,arcToNext); - spqr_node arcTail = findArcTail(dec,arcToNext); - if(findArcSign(dec,arcToNext).reversed){ - spqr_node temp = arcHead; - arcHead = arcTail; - arcTail = temp; - } - assert(arcHead == splitNode || arcTail == splitNode); - spqr_node other = arcHead == splitNode ? arcTail : arcHead; - - assert(newRow->nodeColors[other] == COLOR_SOURCE || newRow->nodeColors[other] == COLOR_SINK); - - if(newRow->reducedMembers[reducedId].willBeReversed){ - orientation.headSplit = arcHead != splitNode; - orientation.otherIsSource = newRow->nodeColors[other] != COLOR_SOURCE; - }else{ - orientation.headSplit = arcHead == splitNode; - orientation.otherIsSource = newRow->nodeColors[other] == COLOR_SOURCE; - } - return orientation; -} - -static SplitOrientation getRelativeOrientationParallel(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id reducedId, spqr_arc arcToNext){ - assert(findArcMemberNoCompression(dec,arcToNext) == newRow->reducedMembers[reducedId].member); - assert(SPQRarcIsValid(newRow->reducedMembers[reducedId].splitArc) && SPQRarcIsValid(arcToNext)); - SplitOrientation orientation; - orientation.otherIsSource = newRow->reducedMembers[reducedId].otherIsSource; - if(arcIsReversedNonRigid(dec,arcToNext) == arcIsReversedNonRigid(dec,newRow->reducedMembers[reducedId].splitArc)){ - orientation.headSplit = newRow->reducedMembers[reducedId].splitHead; - }else{ - orientation.headSplit = !newRow->reducedMembers[reducedId].splitHead; - } - return orientation; -} -static SplitOrientation getRelativeOrientationSeries(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id reducedId, spqr_arc arcToNext){ - assert(findArcMemberNoCompression(dec,arcToNext) == newRow->reducedMembers[reducedId].member); - assert(SPQRarcIsValid(newRow->reducedMembers[reducedId].splitArc) && SPQRarcIsValid(arcToNext)); - SplitOrientation orientation; - - orientation.otherIsSource = newRow->reducedMembers[reducedId].otherIsSource; - if(arcIsReversedNonRigid(dec,arcToNext) == arcIsReversedNonRigid(dec,newRow->reducedMembers[reducedId].splitArc)){ - orientation.headSplit = !newRow->reducedMembers[reducedId].splitHead; - }else{ - orientation.headSplit = newRow->reducedMembers[reducedId].splitHead; - } - return orientation; -} - -static SplitOrientation getRelativeOrientation(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id reducedId, spqr_arc arcToNext){ - switch(getMemberType(dec,newRow->reducedMembers[reducedId].member)){ - case SPQR_MEMBERTYPE_RIGID: - return getRelativeOrientationRigid(dec,newRow,reducedId,arcToNext); - case SPQR_MEMBERTYPE_PARALLEL: - return getRelativeOrientationParallel(dec,newRow,reducedId,arcToNext); - case SPQR_MEMBERTYPE_SERIES: - return getRelativeOrientationSeries(dec,newRow,reducedId,arcToNext); - default: - assert(FALSE); - } - SplitOrientation orientation; - orientation.headSplit = FALSE; - orientation.otherIsSource = FALSE; - return orientation; -} -static void determineSplitTypeSeries(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id reducedId, spqr_member member, spqr_arc marker, - SplitOrientation previousOrientation){ - int numAdjacentMembers = newRow->reducedMembers[reducedId].numChildren-newRow->reducedMembers[reducedId].numPropagatedChildren; - if(reducedMemberIsValid(newRow->reducedMembers[reducedId].parent) && - newRow->reducedMembers[newRow->reducedMembers[reducedId].parent].type != TYPE_PROPAGATED){ - ++numAdjacentMembers; - } - assert(numAdjacentMembers > 1); - if(numAdjacentMembers > 2) { - newRow->remainsNetwork = FALSE; - newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; - return; - } - cut_arc_id cutArc = newRow->reducedMembers[reducedId].firstCutArc; - if(cutArcIsValid(cutArc)){ - spqr_arc arc = newRow->cutArcs[cutArc].arc; - SCIP_Bool good = (((arcIsReversedNonRigid(dec,arc) == arcIsReversedNonRigid(dec,marker)) - == newRow->cutArcs[cutArc].arcReversed ) == previousOrientation.headSplit) - == previousOrientation.otherIsSource; - if(!good){ - newRow->remainsNetwork = FALSE; - newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; - return; - } - newRow->reducedMembers[reducedId].splitArc = marker; - newRow->reducedMembers[reducedId].splitHead = previousOrientation.headSplit; - newRow->reducedMembers[reducedId].otherIsSource = !previousOrientation.otherIsSource; - newRow->reducedMembers[reducedId].type = TYPE_MERGED; - return; - } - - newRow->reducedMembers[reducedId].splitArc = marker; - newRow->reducedMembers[reducedId].splitHead = previousOrientation.headSplit; - newRow->reducedMembers[reducedId].otherIsSource = previousOrientation.otherIsSource; - newRow->reducedMembers[reducedId].type = TYPE_MERGED; -} -static void determineSplitTypeParallel(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id reducedId, spqr_member member, spqr_arc marker, - SplitOrientation previousOrientation){ - SPQRRowReducedMember * redMember = &newRow->reducedMembers[reducedId]; - - SCIP_Bool good = TRUE; - SCIP_Bool isReversed = TRUE; - int countedCutArcs = 0; - for (cut_arc_id cutArc = redMember->firstCutArc; cutArcIsValid(cutArc); - cutArc = newRow->cutArcs[cutArc].nextMember){ - spqr_arc arc = newRow->cutArcs[cutArc].arc; - SCIP_Bool arcIsReversed = arcIsReversedNonRigid(dec,arc) != newRow->cutArcs[cutArc].arcReversed; - if(countedCutArcs == 0){ - isReversed = arcIsReversed; - }else if(arcIsReversed != isReversed){ - good = FALSE; - break; - } - ++countedCutArcs; - } - if(!good){ - redMember->type = TYPE_NOT_NETWORK; - newRow->remainsNetwork = FALSE; - return; - } - if(countedCutArcs == 0){ - redMember->splitArc = marker; - redMember->splitHead = previousOrientation.headSplit; - redMember->otherIsSource = previousOrientation.otherIsSource; - redMember->type = TYPE_MERGED; - return; - } - SCIP_Bool isHeadSourceOrTailTarget = previousOrientation.headSplit == previousOrientation.otherIsSource; - SCIP_Bool isOkay = isHeadSourceOrTailTarget == (isReversed == arcIsReversedNonRigid(dec,marker)); - if(!isOkay){ - redMember->type = TYPE_NOT_NETWORK; - newRow->remainsNetwork = FALSE; - return; - } - redMember->splitArc = marker; - redMember->splitHead = previousOrientation.headSplit; - redMember->otherIsSource = previousOrientation.otherIsSource; - redMember->type = TYPE_MERGED; -} -static void determineSplitTypeRigid(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id reducedId, spqr_member member, spqr_arc marker, - SplitOrientation previousOrientation){ - assert(dec); - assert(newRow); - assert(getMemberType(dec,member) == SPQR_MEMBERTYPE_RIGID); - - Nodes nodes = rigidDetermineCandidateNodesFromAdjacentComponents(dec,newRow,reducedId); - if(SPQRnodeIsInvalid(nodes.first) && SPQRnodeIsInvalid(nodes.second)){ - newRow->remainsNetwork = FALSE; - newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; - return; - } - if(SPQRnodeIsInvalid(nodes.first) && SPQRnodeIsValid(nodes.second)){ - nodes.first = nodes.second; - nodes.second = SPQR_INVALID_NODE; - } - - spqr_node markerHead = findArcHead(dec,marker); - spqr_node markerTail = findArcTail(dec,marker); - if(findArcSign(dec,marker).reversed){ - spqr_node temp = markerHead; - markerHead = markerTail; - markerTail = temp; - } - - if(newRow->reducedMembers[reducedId].numCutArcs == 0){ - assert(SPQRnodeIsInvalid(nodes.second)); //There must be at least two adjacent components - if(nodes.first != markerHead && nodes.first != markerTail){ - newRow->remainsNetwork = FALSE; - newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; - return; - } - SCIP_Bool reverse = previousOrientation.headSplit == (nodes.first == markerTail); - newRow->reducedMembers[reducedId].splitNode = nodes.first; - newRow->reducedMembers[reducedId].otherIsSource = previousOrientation.otherIsSource; - newRow->reducedMembers[reducedId].willBeReversed = reverse; - newRow->reducedMembers[reducedId].type = TYPE_MERGED; - return; - } - if(!SPQRnodeIsValid(newRow->reducedMembers[reducedId].splitNode)){ - assert(newRow->reducedMembers[reducedId].numCutArcs > 0);//Checking for propagation only makes sense if there is at least one cut arc - - rigidFindStarNodes(dec,newRow,reducedId); - if(!newRow->remainsNetwork){ - return; - } - - if(SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode)){ - //not a star => attempt to find splittable nodes using articulation node algorithms - //We save some work by telling the methods that only the marker nodes should be checked - rigidGetSplittableArticulationPointsOnPath(dec,newRow,reducedId,nodes.first,nodes.second); - } - if(!newRow->remainsNetwork){ - return; - } - if(SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode)){ - newRow->remainsNetwork = FALSE; - newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; - return; - } - } - spqr_node splitNode = determineAndColorSplitNode(dec,newRow,reducedId,nodes.first,nodes.second); - - if(SPQRnodeIsInvalid(splitNode)){ - newRow->remainsNetwork = FALSE; - newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; - return; - } - assert(splitNode == nodes.first || splitNode == nodes.second); - assert(splitNode == markerHead || splitNode == markerTail); - - spqr_node otherNode = splitNode == markerHead ? markerTail : markerHead; - SCIP_Bool headsMatch = previousOrientation.headSplit == (splitNode == markerHead); - - COLOR_STATUS otherColor = newRow->nodeColors[otherNode]; - assert(otherColor == COLOR_SOURCE || otherColor == COLOR_SINK); - SCIP_Bool otherIsSource = otherColor == COLOR_SOURCE; - - SCIP_Bool good = headsMatch == (previousOrientation.otherIsSource == otherIsSource); - - if(!good){ - newRow->remainsNetwork = FALSE; - newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; - return; - } - - newRow->reducedMembers[reducedId].splitNode = splitNode; - newRow->reducedMembers[reducedId].willBeReversed = !headsMatch; - newRow->reducedMembers[reducedId].type = TYPE_MERGED; -} -static void determineSplitTypeNext(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id current, reduced_member_id next, SCIP_Bool currentIsParent){ - spqr_member member = newRow->reducedMembers[next].member; - SPQRMemberType type = getMemberType(dec,member); - spqr_arc nextArc = currentIsParent ? markerToParent(dec,member) : markerOfParent(dec,newRow->reducedMembers[current].member); - spqr_arc currentArc = currentIsParent ? markerOfParent(dec,member) : markerToParent(dec,newRow->reducedMembers[current].member); - SplitOrientation orientation = getRelativeOrientation(dec,newRow,current,currentArc); - assert(type == SPQR_MEMBERTYPE_PARALLEL || type == SPQR_MEMBERTYPE_RIGID || type == SPQR_MEMBERTYPE_SERIES); - switch(type){ - case SPQR_MEMBERTYPE_RIGID:{ - determineSplitTypeRigid(dec,newRow,next,member,nextArc,orientation); - break; - } - case SPQR_MEMBERTYPE_PARALLEL:{ - determineSplitTypeParallel(dec,newRow,next,member,nextArc,orientation); - break; - } - case SPQR_MEMBERTYPE_SERIES:{ - determineSplitTypeSeries(dec,newRow,next,member,nextArc,orientation); +static SplitOrientation getRelativeOrientationRigid( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id reducedId, + spqr_arc arcToNext +) +{ + assert(findArcMemberNoCompression(dec, arcToNext) == newRow->reducedMembers[reducedId].member); + assert(SPQRnodeIsValid(newRow->reducedMembers[reducedId].splitNode)); + + SplitOrientation orientation; + if( newRow->reducedMembers[reducedId].numCutArcs == 0 ) + { + spqr_node splitNode = newRow->reducedMembers[reducedId].splitNode; + spqr_node head = findEffectiveArcHead(dec, arcToNext); + + assert(head == splitNode || splitNode == findEffectiveArcTailNoCompression(dec, arcToNext)); + + orientation.headSplit = newRow->reducedMembers[reducedId].willBeReversed == ( head != splitNode ); + orientation.otherIsSource = newRow->reducedMembers[reducedId].otherIsSource; + return orientation; + } + spqr_node splitNode = newRow->reducedMembers[reducedId].splitNode; + spqr_node arcHead = findArcHead(dec, arcToNext); + spqr_node arcTail = findArcTail(dec, arcToNext); + if( findArcSign(dec, arcToNext).reversed ) + { + spqr_node temp = arcHead; + arcHead = arcTail; + arcTail = temp; + } + assert(arcHead == splitNode || arcTail == splitNode); + spqr_node other = arcHead == splitNode ? arcTail : arcHead; + + assert(newRow->nodeColors[other] == COLOR_SOURCE || newRow->nodeColors[other] == COLOR_SINK); + + if( newRow->reducedMembers[reducedId].willBeReversed ) + { + orientation.headSplit = arcHead != splitNode; + orientation.otherIsSource = newRow->nodeColors[other] != COLOR_SOURCE; + } else + { + orientation.headSplit = arcHead == splitNode; + orientation.otherIsSource = newRow->nodeColors[other] == COLOR_SOURCE; + } + return orientation; +} + +static SplitOrientation getRelativeOrientationParallel( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id reducedId, + spqr_arc arcToNext +) +{ + assert(findArcMemberNoCompression(dec, arcToNext) == newRow->reducedMembers[reducedId].member); + assert(SPQRarcIsValid(newRow->reducedMembers[reducedId].splitArc) && SPQRarcIsValid(arcToNext)); + SplitOrientation orientation; + orientation.otherIsSource = newRow->reducedMembers[reducedId].otherIsSource; + if( arcIsReversedNonRigid(dec, arcToNext) == arcIsReversedNonRigid(dec, newRow->reducedMembers[reducedId].splitArc)) + { + orientation.headSplit = newRow->reducedMembers[reducedId].splitHead; + } else + { + orientation.headSplit = !newRow->reducedMembers[reducedId].splitHead; + } + return orientation; +} + +static SplitOrientation getRelativeOrientationSeries( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id reducedId, + spqr_arc arcToNext +) +{ + assert(findArcMemberNoCompression(dec, arcToNext) == newRow->reducedMembers[reducedId].member); + assert(SPQRarcIsValid(newRow->reducedMembers[reducedId].splitArc) && SPQRarcIsValid(arcToNext)); + SplitOrientation orientation; + + orientation.otherIsSource = newRow->reducedMembers[reducedId].otherIsSource; + if( arcIsReversedNonRigid(dec, arcToNext) == arcIsReversedNonRigid(dec, newRow->reducedMembers[reducedId].splitArc)) + { + orientation.headSplit = !newRow->reducedMembers[reducedId].splitHead; + } else + { + orientation.headSplit = newRow->reducedMembers[reducedId].splitHead; + } + return orientation; +} + +static SplitOrientation getRelativeOrientation( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id reducedId, + spqr_arc arcToNext +) +{ + switch( getMemberType(dec, newRow->reducedMembers[reducedId].member)) + { + case SPQR_MEMBERTYPE_RIGID: + return getRelativeOrientationRigid(dec, newRow, reducedId, arcToNext); + case SPQR_MEMBERTYPE_PARALLEL: + return getRelativeOrientationParallel(dec, newRow, reducedId, arcToNext); + case SPQR_MEMBERTYPE_SERIES: + return getRelativeOrientationSeries(dec, newRow, reducedId, arcToNext); + default: + assert(FALSE); + } + SplitOrientation orientation; + orientation.headSplit = FALSE; + orientation.otherIsSource = FALSE; + return orientation; +} + +static void determineSplitTypeSeries( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id reducedId, + spqr_member member, + spqr_arc marker, + SplitOrientation previousOrientation +) +{ + int numAdjacentMembers = + newRow->reducedMembers[reducedId].numChildren - newRow->reducedMembers[reducedId].numPropagatedChildren; + if( reducedMemberIsValid(newRow->reducedMembers[reducedId].parent) && + newRow->reducedMembers[newRow->reducedMembers[reducedId].parent].type != TYPE_PROPAGATED ) + { + ++numAdjacentMembers; + } + assert(numAdjacentMembers > 1); + if( numAdjacentMembers > 2 ) + { + newRow->remainsNetwork = FALSE; + newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; + return; + } + cut_arc_id cutArc = newRow->reducedMembers[reducedId].firstCutArc; + if( cutArcIsValid(cutArc)) + { + spqr_arc arc = newRow->cutArcs[cutArc].arc; + SCIP_Bool good = ((( arcIsReversedNonRigid(dec, arc) == arcIsReversedNonRigid(dec, marker)) == + newRow->cutArcs[cutArc].arcReversed ) == previousOrientation.headSplit ) == + previousOrientation.otherIsSource; + if( !good ) + { + newRow->remainsNetwork = FALSE; + newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; + return; + } + newRow->reducedMembers[reducedId].splitArc = marker; + newRow->reducedMembers[reducedId].splitHead = previousOrientation.headSplit; + newRow->reducedMembers[reducedId].otherIsSource = !previousOrientation.otherIsSource; + newRow->reducedMembers[reducedId].type = TYPE_MERGED; + return; + } + + newRow->reducedMembers[reducedId].splitArc = marker; + newRow->reducedMembers[reducedId].splitHead = previousOrientation.headSplit; + newRow->reducedMembers[reducedId].otherIsSource = previousOrientation.otherIsSource; + newRow->reducedMembers[reducedId].type = TYPE_MERGED; +} + +static void determineSplitTypeParallel( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id reducedId, + spqr_member member, + spqr_arc marker, + SplitOrientation previousOrientation +) +{ + SPQRRowReducedMember* redMember = &newRow->reducedMembers[reducedId]; + + SCIP_Bool good = TRUE; + SCIP_Bool isReversed = TRUE; + int countedCutArcs = 0; + for( cut_arc_id cutArc = redMember->firstCutArc; cutArcIsValid(cutArc); + cutArc = newRow->cutArcs[cutArc].nextMember ) + { + spqr_arc arc = newRow->cutArcs[cutArc].arc; + SCIP_Bool arcIsReversed = arcIsReversedNonRigid(dec, arc) != newRow->cutArcs[cutArc].arcReversed; + if( countedCutArcs == 0 ) + { + isReversed = arcIsReversed; + } else if( arcIsReversed != isReversed ) + { + good = FALSE; + break; + } + ++countedCutArcs; + } + if( !good ) + { + redMember->type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + return; + } + if( countedCutArcs == 0 ) + { + redMember->splitArc = marker; + redMember->splitHead = previousOrientation.headSplit; + redMember->otherIsSource = previousOrientation.otherIsSource; + redMember->type = TYPE_MERGED; + return; + } + SCIP_Bool isHeadSourceOrTailTarget = previousOrientation.headSplit == previousOrientation.otherIsSource; + SCIP_Bool isOkay = isHeadSourceOrTailTarget == ( isReversed == arcIsReversedNonRigid(dec, marker)); + if( !isOkay ) + { + redMember->type = TYPE_NOT_NETWORK; + newRow->remainsNetwork = FALSE; + return; + } + redMember->splitArc = marker; + redMember->splitHead = previousOrientation.headSplit; + redMember->otherIsSource = previousOrientation.otherIsSource; + redMember->type = TYPE_MERGED; +} + +static void determineSplitTypeRigid( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id reducedId, + spqr_member member, + spqr_arc marker, + SplitOrientation previousOrientation +) +{ + assert(dec); + assert(newRow); + assert(getMemberType(dec, member) == SPQR_MEMBERTYPE_RIGID); + + Nodes nodes = rigidDetermineCandidateNodesFromAdjacentComponents(dec, newRow, reducedId); + if( SPQRnodeIsInvalid(nodes.first) && SPQRnodeIsInvalid(nodes.second)) + { + newRow->remainsNetwork = FALSE; + newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; + return; + } + if( SPQRnodeIsInvalid(nodes.first) && SPQRnodeIsValid(nodes.second)) + { + nodes.first = nodes.second; + nodes.second = SPQR_INVALID_NODE; + } + + spqr_node markerHead = findArcHead(dec, marker); + spqr_node markerTail = findArcTail(dec, marker); + if( findArcSign(dec, marker).reversed ) + { + spqr_node temp = markerHead; + markerHead = markerTail; + markerTail = temp; + } + + if( newRow->reducedMembers[reducedId].numCutArcs == 0 ) + { + assert(SPQRnodeIsInvalid(nodes.second));//There must be at least two adjacent components + if( nodes.first != markerHead && nodes.first != markerTail ) + { + newRow->remainsNetwork = FALSE; + newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; + return; + } + SCIP_Bool reverse = previousOrientation.headSplit == ( nodes.first == markerTail ); + newRow->reducedMembers[reducedId].splitNode = nodes.first; + newRow->reducedMembers[reducedId].otherIsSource = previousOrientation.otherIsSource; + newRow->reducedMembers[reducedId].willBeReversed = reverse; + newRow->reducedMembers[reducedId].type = TYPE_MERGED; + return; + } + if( !SPQRnodeIsValid(newRow->reducedMembers[reducedId].splitNode)) + { + assert(newRow->reducedMembers[reducedId].numCutArcs > + 0);//Checking for propagation only makes sense if there is at least one cut arc + + rigidFindStarNodes(dec, newRow, reducedId); + if( !newRow->remainsNetwork ) + { + return; + } + + if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode)) + { + //not a star => attempt to find splittable nodes using articulation node algorithms + //We save some work by telling the methods that only the marker nodes should be checked + rigidGetSplittableArticulationPointsOnPath(dec, newRow, reducedId, nodes.first, nodes.second); + } + if( !newRow->remainsNetwork ) + { + return; + } + if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode)) + { + newRow->remainsNetwork = FALSE; + newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; + return; + } + } + spqr_node splitNode = determineAndColorSplitNode(dec, newRow, reducedId, nodes.first, nodes.second); + + if( SPQRnodeIsInvalid(splitNode)) + { + newRow->remainsNetwork = FALSE; + newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; + return; + } + assert(splitNode == nodes.first || splitNode == nodes.second); + assert(splitNode == markerHead || splitNode == markerTail); + + spqr_node otherNode = splitNode == markerHead ? markerTail : markerHead; + SCIP_Bool headsMatch = previousOrientation.headSplit == ( splitNode == markerHead ); + + COLOR_STATUS otherColor = newRow->nodeColors[otherNode]; + assert(otherColor == COLOR_SOURCE || otherColor == COLOR_SINK); + SCIP_Bool otherIsSource = otherColor == COLOR_SOURCE; + + SCIP_Bool good = headsMatch == ( previousOrientation.otherIsSource == otherIsSource ); + + if( !good ) + { + newRow->remainsNetwork = FALSE; + newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; + return; + } + + newRow->reducedMembers[reducedId].splitNode = splitNode; + newRow->reducedMembers[reducedId].willBeReversed = !headsMatch; + newRow->reducedMembers[reducedId].type = TYPE_MERGED; +} + +static void determineSplitTypeNext( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id current, + reduced_member_id next, + SCIP_Bool currentIsParent +) +{ + spqr_member member = newRow->reducedMembers[next].member; + SPQRMemberType type = getMemberType(dec, member); + spqr_arc nextArc = currentIsParent ? markerToParent(dec, member) : markerOfParent(dec, + newRow->reducedMembers[current].member); + spqr_arc currentArc = currentIsParent ? markerOfParent(dec, member) : markerToParent(dec, + newRow->reducedMembers[current].member); + SplitOrientation orientation = getRelativeOrientation(dec, newRow, current, currentArc); + assert(type == SPQR_MEMBERTYPE_PARALLEL || type == SPQR_MEMBERTYPE_RIGID || type == SPQR_MEMBERTYPE_SERIES); + switch( type ) + { + case SPQR_MEMBERTYPE_RIGID: + { + determineSplitTypeRigid(dec, newRow, next, member, nextArc, orientation); + break; + } + case SPQR_MEMBERTYPE_PARALLEL: + { + determineSplitTypeParallel(dec, newRow, next, member, nextArc, orientation); + break; + } + case SPQR_MEMBERTYPE_SERIES: + { + determineSplitTypeSeries(dec, newRow, next, member, nextArc, orientation); + break; + } + default: + assert(FALSE); + newRow->remainsNetwork = FALSE; + } +} + +static void determineTypesChildrenNodes( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id parent, + reduced_member_id node, + reduced_member_id skipNode +) +{ + if( node == skipNode || newRow->reducedMembers[node].type == TYPE_PROPAGATED ) + { + return; + } + //check merging + determineSplitTypeNext(dec, newRow, parent, node, TRUE); + if( !newRow->remainsNetwork ) + { + return; + } + //merge all children + for( int i = 0; i < newRow->reducedMembers[node].numChildren; ++i ) + { + children_idx idx = newRow->reducedMembers[node].firstChild + i; + reduced_member_id child = newRow->childrenStorage[idx]; + determineTypesChildrenNodes(dec, newRow, node, child, skipNode); + } +} + +static void determineMergeableTypes( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id root +) +{ + assert(newRow->numReducedMembers <= newRow->memMergeTreeCallData); + if( newRow->reducedMembers[root].numPropagatedChildren == newRow->reducedMembers[root].numChildren ) + { + //Determine single component; + if( newRow->reducedMembers[root].type == TYPE_UNDETERMINED ) + { + spqr_member member = newRow->reducedMembers[root].member; + switch( getMemberType(dec, member)) + { + case SPQR_MEMBERTYPE_RIGID: + determineSingleRowRigidType(dec, newRow, root, member); + break; + case SPQR_MEMBERTYPE_PARALLEL: + determineSingleParallelType(dec, newRow, root, member); + break; + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_SERIES: + determineSingleSeriesType(dec, newRow, root, member); + break; + default: + assert(FALSE); + newRow->remainsNetwork = FALSE; + } + } + return; + } + + //go to a leaf. We need to start in a leaf to avoid the ambiguity of choosing an orientation + //in members which have no cut arcs; otherwise, we might choose the wrong one + reduced_member_id leaf = root; + while( newRow->reducedMembers[leaf].numChildren != newRow->reducedMembers[leaf].numPropagatedChildren ) + { + for( int i = 0; i < newRow->reducedMembers[leaf].numChildren; ++i ) + { + children_idx idx = newRow->reducedMembers[leaf].firstChild + i; + reduced_member_id child = newRow->childrenStorage[idx]; + if( newRow->reducedMembers[child].type != TYPE_PROPAGATED ) + { + leaf = child; break; - } - default: - assert(FALSE); - newRow->remainsNetwork = FALSE; - } -} -static void determineTypesChildrenNodes(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id parent, reduced_member_id node, - reduced_member_id skipNode){ - if(node == skipNode || newRow->reducedMembers[node].type == TYPE_PROPAGATED){ - return; - } - //check merging - determineSplitTypeNext(dec,newRow,parent,node,TRUE); - if(!newRow->remainsNetwork){ - return; - } - //merge all children - for (int i = 0; i < newRow->reducedMembers[node].numChildren; ++i) { - children_idx idx = newRow->reducedMembers[node].firstChild + i; - reduced_member_id child = newRow->childrenStorage[idx]; - determineTypesChildrenNodes(dec,newRow,node,child,skipNode); - } -} - -static void determineMergeableTypes(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, reduced_member_id root){ - assert(newRow->numReducedMembers <= newRow->memMergeTreeCallData); - if(newRow->reducedMembers[root].numPropagatedChildren == newRow->reducedMembers[root].numChildren){ - //Determine single component; - if(newRow->reducedMembers[root].type == TYPE_UNDETERMINED){ - spqr_member member = newRow->reducedMembers[root].member; - switch(getMemberType(dec,member)){ - case SPQR_MEMBERTYPE_RIGID: - determineSingleRowRigidType(dec,newRow,root,member); - break; - case SPQR_MEMBERTYPE_PARALLEL: - determineSingleParallelType(dec,newRow,root,member); - break; - case SPQR_MEMBERTYPE_LOOP: - case SPQR_MEMBERTYPE_SERIES: - determineSingleSeriesType(dec,newRow,root,member); - break; - default: - assert(FALSE); - newRow->remainsNetwork = FALSE; - } - } - return; - } - - //go to a leaf. We need to start in a leaf to avoid the ambiguity of choosing an orientation - //in members which have no cut arcs; otherwise, we might choose the wrong one - reduced_member_id leaf = root; - while(newRow->reducedMembers[leaf].numChildren != newRow->reducedMembers[leaf].numPropagatedChildren){ - for(int i = 0; i < newRow->reducedMembers[leaf].numChildren;++i){ - children_idx idx = newRow->reducedMembers[leaf].firstChild + i; - reduced_member_id child = newRow->childrenStorage[idx]; - if(newRow->reducedMembers[child].type != TYPE_PROPAGATED){ - leaf = child; - break; - } - } - } - determineSplitTypeFirstLeaf(dec,newRow,leaf); - - if(!newRow->remainsNetwork){ - return; - } - reduced_member_id baseNode = leaf; - reduced_member_id nextNode = newRow->reducedMembers[baseNode].parent; - - while(reducedMemberIsValid(nextNode)){ - //check this node - determineSplitTypeNext(dec,newRow,baseNode,nextNode,FALSE); - if(!newRow->remainsNetwork){ + } + } + } + determineSplitTypeFirstLeaf(dec, newRow, leaf); + + if( !newRow->remainsNetwork ) + { + return; + } + reduced_member_id baseNode = leaf; + reduced_member_id nextNode = newRow->reducedMembers[baseNode].parent; + + while( reducedMemberIsValid(nextNode)) + { + //check this node + determineSplitTypeNext(dec, newRow, baseNode, nextNode, FALSE); + if( !newRow->remainsNetwork ) + { + return; + } + //Add other nodes in the subtree + for( int i = 0; i < newRow->reducedMembers[nextNode].numChildren; ++i ) + { + children_idx idx = newRow->reducedMembers[nextNode].firstChild + i; + reduced_member_id child = newRow->childrenStorage[idx]; + determineTypesChildrenNodes(dec, newRow, nextNode, child, baseNode); + if( !newRow->remainsNetwork ) + { return; - } - //Add other nodes in the subtree - for (int i = 0; i < newRow->reducedMembers[nextNode].numChildren; ++i) { - children_idx idx = newRow->reducedMembers[nextNode].firstChild + i; - reduced_member_id child = newRow->childrenStorage[idx]; - determineTypesChildrenNodes(dec,newRow,nextNode,child,baseNode); - if(!newRow->remainsNetwork){ - return; - } - } + } + } - //Move up one layer - baseNode = nextNode; - nextNode = newRow->reducedMembers[nextNode].parent; - } + //Move up one layer + baseNode = nextNode; + nextNode = newRow->reducedMembers[nextNode].parent; + } } -static void cleanUpRowMemberInformation(SCIP_NETWORKROWADDITION * newRow){ - //This loop is at the end as memberInformation is also used to assign the cut arcs during propagation - //Clean up the memberInformation array - for (int i = 0; i < newRow->numReducedMembers; ++i) { - newRow->memberInformation[newRow->reducedMembers[i].member].reducedMember = INVALID_REDUCED_MEMBER; - } +static void cleanUpRowMemberInformation(SCIP_NETROWADD* newRow) +{ + //This loop is at the end as memberInformation is also used to assign the cut arcs during propagation + //Clean up the memberInformation array + for( int i = 0; i < newRow->numReducedMembers; ++i ) + { + newRow->memberInformation[newRow->reducedMembers[i].member].reducedMember = INVALID_REDUCED_MEMBER; + } #ifndef NDEBUG - for (int i = 0; i < newRow->memMemberInformation; ++i) { - assert(reducedMemberIsInvalid(newRow->memberInformation[i].reducedMember)); - } + for( int i = 0; i < newRow->memMemberInformation; ++i ) + { + assert(reducedMemberIsInvalid(newRow->memberInformation[i].reducedMember)); + } #endif } -static SCIP_RETCODE rigidTransformArcIntoCycle(SCIP_NETWORKDECOMP *dec, - const spqr_member member, - const spqr_arc arc, - const SCIP_Bool reverseArcDirection, - NewRowInformation * const newRowInformation){ - //If a cycle already exists, just expand it with the new arc. - spqr_member markerCycleMember = SPQR_INVALID_MEMBER; - spqr_arc markerCycleArc = SPQR_INVALID_ARC; - SCIP_Bool isParent = arc == markerToParent(dec,member); - spqr_member adjacentMember = SPQR_INVALID_MEMBER; - if (isParent) { - adjacentMember = findMemberParent(dec, member); - if (getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_SERIES) { - markerCycleMember = adjacentMember; - markerCycleArc = markerOfParent(dec,member); - } - } else if (arcIsMarker(dec, arc)) { - adjacentMember = findArcChildMember(dec, arc); - if (getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_SERIES) { - markerCycleMember = adjacentMember; - markerCycleArc = markerToParent(dec,adjacentMember); - } - } - if (markerCycleMember != SPQR_INVALID_MEMBER) { - newRowInformation->member = markerCycleMember; - if(arcIsReversedNonRigid(dec,markerCycleArc)){ - newRowInformation->reversed = reverseArcDirection; - }else{ - newRowInformation->reversed = !reverseArcDirection; - } - - return SCIP_OKAY; - } - - //Otherwise, we create a new cycle - spqr_member newCycle; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &newCycle)); - //We would like to move the edge but unfortunately cannot do so without destroying the arc union-find datastructure. - //Thus, we 'convert' the arc into a marker and add the new series - - spqr_arc duplicate = SPQR_INVALID_ARC; - spqr_element element = arcGetElement(dec,arc); - if(element != MARKER_COLUMN_ELEMENT && element != MARKER_ROW_ELEMENT){ - if(SPQRelementIsColumn(element)){ - SCIP_CALL(createColumnArc(dec,newCycle,&duplicate, SPQRelementToColumn(element),TRUE)); - }else{ - SCIP_CALL(createRowArc(dec,newCycle,&duplicate, SPQRelementToRow(element),TRUE)); - } - }else if(isParent){ - //create parent marker - SCIP_CALL(createParentMarker(dec, newCycle, arcIsTree(dec, arc), adjacentMember, - markerOfParent(dec, member),&duplicate,TRUE)); - }else{ - //create child marker - SCIP_CALL(createChildMarker(dec,newCycle,adjacentMember,arcIsTree(dec,arc),&duplicate,TRUE)); - dec->members[adjacentMember].parentMember = newCycle; - dec->members[adjacentMember].markerOfParent = duplicate; - } - //Create the other marker edge - spqr_arc cycleMarker = SPQR_INVALID_ARC; - if(isParent){ - SCIP_CALL(createChildMarker(dec,newCycle,member,!arcIsTree(dec,arc), - &cycleMarker,FALSE)); - }else{ - SCIP_CALL(createParentMarker(dec,newCycle,!arcIsTree(dec,arc), - member,arc,&cycleMarker,FALSE)); - } - //Change the existing edge to a marker - if(isParent){ - assert(markerToParent(dec,member) == arc); - dec->arcs[markerOfParent(dec,member)].childMember = newCycle; - dec->members[member].parentMember = newCycle; - dec->members[member].markerToParent = arc; - dec->members[member].markerOfParent = cycleMarker; - dec->arcs[arc].element = arcIsTree(dec,arc) ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT;; - dec->arcs[arc].childMember = SPQR_INVALID_MEMBER; - - }else{ - dec->arcs[arc].element = arcIsTree(dec,arc) ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; - dec->arcs[arc].childMember = newCycle; - } - newRowInformation->member = newCycle; - newRowInformation->reversed = !reverseArcDirection; - - return SCIP_OKAY; -} - -static SCIP_RETCODE transformSingleRigid(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - const reduced_member_id reducedMember, - const spqr_member member, - NewRowInformation * const newRowInformation){ - if(SPQRarcIsValid(newRow->reducedMembers[reducedMember].articulationArc)){ - spqr_arc arc = newRow->reducedMembers[reducedMember].articulationArc; - //Cut arc is propagated into a cycle with new arc - assert(newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcHeadNoCompression(dec,arc) || - newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcTailNoCompression(dec,arc)); - - - SCIP_Bool reversed; - - if(newRow->isArcCut[arc]){ - reversed = (newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcHead(dec,arc)) == - newRow->reducedMembers[reducedMember].otherIsSource; - }else{ - reversed = (newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcHead(dec,arc)) != - newRow->reducedMembers[reducedMember].otherIsSource; - } - - SCIP_CALL(rigidTransformArcIntoCycle(dec,member,newRow->reducedMembers[reducedMember].articulationArc, - reversed, newRowInformation)); - - return SCIP_OKAY; - } - //Single splittable node - assert(SPQRnodeIsValid(newRow->reducedMembers[reducedMember].splitNode)); - - spqr_node splitNode = newRow->reducedMembers[reducedMember].splitNode; - if(newRow->reducedMembers[reducedMember].allHaveCommonNode){ - //Create a new node; move all cut arcs end of split node to it and add new arc between new node and split node - spqr_node newNode = SPQR_INVALID_NODE; - SCIP_CALL(createNode(dec, &newNode)); - - cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; - do { - spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; - spqr_node arcHead = findArcHead(dec, cutArc); - if (arcHead == splitNode) { - changeArcHead(dec, cutArc, arcHead, newNode); - } else { - changeArcTail(dec, cutArc, findArcTail(dec, cutArc), newNode); - } - - cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; - } while (cutArcIsValid(cutArcIdx)); - - newRowInformation->member = member; - if(newRow->reducedMembers[reducedMember].otherIsSource){ - newRowInformation->head = newNode; - newRowInformation->tail = splitNode; - }else{ - newRowInformation->head = splitNode; - newRowInformation->tail = newNode; - } - newRowInformation->representative = findArcSign(dec,newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc).representative; - newRowInformation->reversed = FALSE; - - return SCIP_OKAY; - } - //Articulation point was split (based on coloring) - - spqr_node newNode = SPQR_INVALID_NODE; - SCIP_CALL(createNode(dec, &newNode)); - - spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode); - spqr_arc iterArc = firstNodeArc; - - do{ - SCIP_Bool isCut = newRow->isArcCut[iterArc]; - spqr_node otherHead = findArcHead(dec, iterArc); - spqr_node otherTail = findArcTail(dec, iterArc); - spqr_node otherEnd = otherHead == splitNode ? otherTail : otherHead; - SCIP_Bool isMoveColor = newRow->nodeColors[otherEnd] == COLOR_SOURCE; - spqr_arc nextArc = getNextNodeArc(dec, iterArc, splitNode); //Need to do this before we modify the arc :) - - SCIP_Bool changeArcEnd = isCut == isMoveColor; - if(changeArcEnd){ - if(otherHead == splitNode){ - changeArcHead(dec,iterArc,otherHead,newNode); - }else{ - changeArcTail(dec,iterArc,otherTail,newNode); +static SCIP_RETCODE rigidTransformArcIntoCycle( + SCIP_NETMATDEC* dec, + const spqr_member member, + const spqr_arc arc, + const SCIP_Bool reverseArcDirection, + NewRowInformation* const newRowInformation +) +{ + //If a cycle already exists, just expand it with the new arc. + spqr_member markerCycleMember = SPQR_INVALID_MEMBER; + spqr_arc markerCycleArc = SPQR_INVALID_ARC; + SCIP_Bool isParent = arc == markerToParent(dec, member); + spqr_member adjacentMember = SPQR_INVALID_MEMBER; + if( isParent ) + { + adjacentMember = findMemberParent(dec, member); + if( getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_SERIES ) + { + markerCycleMember = adjacentMember; + markerCycleArc = markerOfParent(dec, member); + } + } else if( arcIsMarker(dec, arc)) + { + adjacentMember = findArcChildMember(dec, arc); + if( getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_SERIES ) + { + markerCycleMember = adjacentMember; + markerCycleArc = markerToParent(dec, adjacentMember); + } + } + if( markerCycleMember != SPQR_INVALID_MEMBER) + { + newRowInformation->member = markerCycleMember; + if( arcIsReversedNonRigid(dec, markerCycleArc)) + { + newRowInformation->reversed = reverseArcDirection; + } else + { + newRowInformation->reversed = !reverseArcDirection; + } + + return SCIP_OKAY; + } + + //Otherwise, we create a new cycle + spqr_member newCycle; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &newCycle)); + //We would like to move the edge but unfortunately cannot do so without destroying the arc union-find datastructure. + //Thus, we 'convert' the arc into a marker and add the new series + + spqr_arc duplicate = SPQR_INVALID_ARC; + spqr_element element = arcGetElement(dec, arc); + if( element != MARKER_COLUMN_ELEMENT && element != MARKER_ROW_ELEMENT) + { + if( SPQRelementIsColumn(element)) + { + SCIP_CALL(createColumnArc(dec, newCycle, &duplicate, SPQRelementToColumn(element), TRUE)); + } else + { + SCIP_CALL(createRowArc(dec, newCycle, &duplicate, SPQRelementToRow(element), TRUE)); + } + } else if( isParent ) + { + //create parent marker + SCIP_CALL(createParentMarker(dec, newCycle, arcIsTree(dec, arc), adjacentMember, + markerOfParent(dec, member), &duplicate, TRUE)); + } else + { + //create child marker + SCIP_CALL(createChildMarker(dec, newCycle, adjacentMember, arcIsTree(dec, arc), &duplicate, TRUE)); + dec->members[adjacentMember].parentMember = newCycle; + dec->members[adjacentMember].markerOfParent = duplicate; + } + //Create the other marker edge + spqr_arc cycleMarker = SPQR_INVALID_ARC; + if( isParent ) + { + SCIP_CALL(createChildMarker(dec, newCycle, member, !arcIsTree(dec, arc), + &cycleMarker, FALSE)); + } else + { + SCIP_CALL(createParentMarker(dec, newCycle, !arcIsTree(dec, arc), + member, arc, &cycleMarker, FALSE)); + } + //Change the existing edge to a marker + if( isParent ) + { + assert(markerToParent(dec, member) == arc); + dec->arcs[markerOfParent(dec, member)].childMember = newCycle; + dec->members[member].parentMember = newCycle; + dec->members[member].markerToParent = arc; + dec->members[member].markerOfParent = cycleMarker; + dec->arcs[arc].element = arcIsTree(dec, arc) ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT;; + dec->arcs[arc].childMember = SPQR_INVALID_MEMBER; + + } else + { + dec->arcs[arc].element = arcIsTree(dec, arc) ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; + dec->arcs[arc].childMember = newCycle; + } + newRowInformation->member = newCycle; + newRowInformation->reversed = !reverseArcDirection; + + return SCIP_OKAY; +} + +static SCIP_RETCODE transformSingleRigid( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const reduced_member_id reducedMember, + const spqr_member member, + NewRowInformation* const newRowInformation +) +{ + if( SPQRarcIsValid(newRow->reducedMembers[reducedMember].articulationArc)) + { + spqr_arc arc = newRow->reducedMembers[reducedMember].articulationArc; + //Cut arc is propagated into a cycle with new arc + assert(newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcHeadNoCompression(dec, arc) || + newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcTailNoCompression(dec, arc)); + + + SCIP_Bool reversed; + + if( newRow->isArcCut[arc] ) + { + reversed = ( newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcHead(dec, arc)) == + newRow->reducedMembers[reducedMember].otherIsSource; + } else + { + reversed = ( newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcHead(dec, arc)) != + newRow->reducedMembers[reducedMember].otherIsSource; + } + + SCIP_CALL(rigidTransformArcIntoCycle(dec, member, newRow->reducedMembers[reducedMember].articulationArc, + reversed, newRowInformation)); + + return SCIP_OKAY; + } + //Single splittable node + assert(SPQRnodeIsValid(newRow->reducedMembers[reducedMember].splitNode)); + + spqr_node splitNode = newRow->reducedMembers[reducedMember].splitNode; + if( newRow->reducedMembers[reducedMember].allHaveCommonNode ) + { + //Create a new node; move all cut arcs end of split node to it and add new arc between new node and split node + spqr_node newNode = SPQR_INVALID_NODE; + SCIP_CALL(createNode(dec, &newNode)); + + cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; + do + { + spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; + spqr_node arcHead = findArcHead(dec, cutArc); + if( arcHead == splitNode ) + { + changeArcHead(dec, cutArc, arcHead, newNode); + } else + { + changeArcTail(dec, cutArc, findArcTail(dec, cutArc), newNode); + } + + cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; + } while( cutArcIsValid(cutArcIdx)); + + newRowInformation->member = member; + if( newRow->reducedMembers[reducedMember].otherIsSource ) + { + newRowInformation->head = newNode; + newRowInformation->tail = splitNode; + } else + { + newRowInformation->head = splitNode; + newRowInformation->tail = newNode; + } + newRowInformation->representative = findArcSign(dec, + newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc).representative; + newRowInformation->reversed = FALSE; + + return SCIP_OKAY; + } + //Articulation point was split (based on coloring) + + spqr_node newNode = SPQR_INVALID_NODE; + SCIP_CALL(createNode(dec, &newNode)); + + spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode); + spqr_arc iterArc = firstNodeArc; + + do + { + SCIP_Bool isCut = newRow->isArcCut[iterArc]; + spqr_node otherHead = findArcHead(dec, iterArc); + spqr_node otherTail = findArcTail(dec, iterArc); + spqr_node otherEnd = otherHead == splitNode ? otherTail : otherHead; + SCIP_Bool isMoveColor = newRow->nodeColors[otherEnd] == COLOR_SOURCE; + spqr_arc nextArc = getNextNodeArc(dec, iterArc, splitNode);//Need to do this before we modify the arc :) + + SCIP_Bool changeArcEnd = isCut == isMoveColor; + if( changeArcEnd ) + { + if( otherHead == splitNode ) + { + changeArcHead(dec, iterArc, otherHead, newNode); + } else + { + changeArcTail(dec, iterArc, otherTail, newNode); + } + } + newRow->nodeColors[otherEnd] = UNCOLORED;//Clean up + + //Ugly hack to make sure we can iterate neighbourhood whilst changing arc ends. + spqr_arc previousArc = iterArc; + iterArc = nextArc; + if( iterArc == firstNodeArc ) + { + break; + } + if( changeArcEnd && previousArc == firstNodeArc ) + { + firstNodeArc = iterArc; + } + } while( TRUE ); + newRow->reducedMembers[reducedMember].coloredNode = SPQR_INVALID_NODE; + + newRowInformation->member = member; + newRowInformation->head = newNode; + newRowInformation->tail = splitNode; + newRowInformation->representative = findArcSign(dec, + newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc).representative; + newRowInformation->reversed = FALSE; + + return SCIP_OKAY; +} + + +static SCIP_RETCODE splitParallelRowAddition( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const reduced_member_id reducedMember, + const spqr_member member, + NewRowInformation* newRowInfo +) +{ + assert(newRow->reducedMembers[reducedMember].numCutArcs > 0); + + int numCutArcs = newRow->reducedMembers[reducedMember].numCutArcs; + int numParallelArcs = getNumMemberArcs(dec, member); + + SCIP_Bool createCutParallel = numCutArcs > 1; + SCIP_Bool convertOriginalParallel = ( numCutArcs + 1 ) == numParallelArcs; + + //Do linear search to find non-marked arc + spqr_arc treeArc = getFirstMemberArc(dec, member); + do + { + if( arcIsTree(dec, treeArc)) + { + break; + } + treeArc = getNextMemberArc(dec, treeArc); + } while( treeArc != getFirstMemberArc(dec, member)); + assert(arcIsTree(dec, treeArc)); + + SCIP_Bool treeReversed = arcIsReversedNonRigid(dec, treeArc); + + assert(!( !createCutParallel && + convertOriginalParallel ));//This can only happen if the parallel member is actually a loop, which means it is mislabeled + if( createCutParallel ) + { + if( convertOriginalParallel ) + { + spqr_member adjacentMember = SPQR_INVALID_MEMBER; + spqr_arc adjacentArc = SPQR_INVALID_ARC; + if( treeArc == markerToParent(dec, member)) + { + adjacentMember = findMemberParent(dec, member); + adjacentArc = markerOfParent(dec, member); + } else if( arcIsMarker(dec, treeArc)) + { + adjacentMember = findArcChildMember(dec, treeArc); + adjacentArc = markerToParent(dec, adjacentMember); + assert(markerOfParent(dec, adjacentMember) == treeArc); + } + cut_arc_id firstCut = newRow->reducedMembers[reducedMember].firstCutArc; + SCIP_Bool firstReversed = + newRow->cutArcs[firstCut].arcReversed != arcIsReversedNonRigid(dec, newRow->cutArcs[firstCut].arc); + + if( SPQRmemberIsValid(adjacentMember) && getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_SERIES ) + { + newRowInfo->member = adjacentMember; + if( arcIsReversedNonRigid(dec, treeArc) == arcIsReversedNonRigid(dec, adjacentArc)) + { + if( treeReversed ) + { + newRowInfo->reversed = !firstReversed; + } else + { + newRowInfo->reversed = !firstReversed; + } + } else + { + if( treeReversed ) + { + newRowInfo->reversed = firstReversed; + } else + { + newRowInfo->reversed = firstReversed; + } } - } - newRow->nodeColors[otherEnd] = UNCOLORED; //Clean up - - //Ugly hack to make sure we can iterate neighbourhood whilst changing arc ends. - spqr_arc previousArc = iterArc; - iterArc = nextArc; - if(iterArc == firstNodeArc){ - break; - } - if(changeArcEnd && previousArc == firstNodeArc){ - firstNodeArc = iterArc; - } - }while(TRUE); - newRow->reducedMembers[reducedMember].coloredNode = SPQR_INVALID_NODE; - - newRowInformation->member = member; - newRowInformation->head = newNode; - newRowInformation->tail = splitNode; - newRowInformation->representative = findArcSign(dec,newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc).representative; - newRowInformation->reversed = FALSE; - - return SCIP_OKAY; -} - - -static SCIP_RETCODE splitParallelRowAddition(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - const reduced_member_id reducedMember, - const spqr_member member, - NewRowInformation * newRowInfo){ - assert(newRow->reducedMembers[reducedMember].numCutArcs > 0); - - int numCutArcs = newRow->reducedMembers[reducedMember].numCutArcs; - int numParallelArcs = getNumMemberArcs(dec,member); + return SCIP_OKAY; + } + spqr_member cutMember = SPQR_INVALID_MEMBER; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember)); - SCIP_Bool createCutParallel = numCutArcs > 1; - SCIP_Bool convertOriginalParallel = (numCutArcs + 1) == numParallelArcs; + cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; + assert(cutArcIsValid(cutArcIdx)); + SCIP_Bool parentCut = FALSE; - //Do linear search to find non-marked arc - spqr_arc treeArc = getFirstMemberArc(dec,member); - do{ - if(arcIsTree(dec,treeArc)){ - break; - } - treeArc = getNextMemberArc(dec,treeArc); - }while(treeArc != getFirstMemberArc(dec,member)); - assert(arcIsTree(dec,treeArc)); - - SCIP_Bool treeReversed = arcIsReversedNonRigid(dec,treeArc); - - assert(!(!createCutParallel && convertOriginalParallel));//This can only happen if the parallel member is actually a loop, which means it is mislabeled - if(createCutParallel) { - if(convertOriginalParallel){ - spqr_member adjacentMember = SPQR_INVALID_MEMBER; - spqr_arc adjacentArc = SPQR_INVALID_ARC; - if(treeArc == markerToParent(dec,member)){ - adjacentMember = findMemberParent(dec,member); - adjacentArc = markerOfParent(dec,member); - }else if(arcIsMarker(dec,treeArc)){ - adjacentMember = findArcChildMember(dec,treeArc); - adjacentArc = markerToParent(dec,adjacentMember); - assert(markerOfParent(dec,adjacentMember) == treeArc); - } - cut_arc_id firstCut = newRow->reducedMembers[reducedMember].firstCutArc; - SCIP_Bool firstReversed = newRow->cutArcs[firstCut].arcReversed != arcIsReversedNonRigid(dec,newRow->cutArcs[firstCut].arc); - - if(SPQRmemberIsValid(adjacentMember) && getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_SERIES){ - newRowInfo->member = adjacentMember; - if(arcIsReversedNonRigid(dec,treeArc) == arcIsReversedNonRigid(dec,adjacentArc)){ - if(treeReversed){ - newRowInfo->reversed = !firstReversed; - }else{ - newRowInfo->reversed = !firstReversed; - } - }else{ - if(treeReversed) { - newRowInfo->reversed = firstReversed; - }else{ - newRowInfo->reversed = firstReversed; - } - } - return SCIP_OKAY; - } - spqr_member cutMember = SPQR_INVALID_MEMBER; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember)); - - cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; - assert(cutArcIsValid(cutArcIdx)); - SCIP_Bool parentCut = FALSE; - - while(cutArcIsValid(cutArcIdx)){ - spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; - cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; - moveArcToNewMember(dec,cutArc,member,cutMember); - if (cutArc == markerToParent(dec,member)){ - parentCut = TRUE; - } - } - if(parentCut){ - SCIP_CALL(createMarkerPair(dec,cutMember,member,TRUE,FALSE,TRUE)); - }else{ - SCIP_CALL(createMarkerPair(dec,member,cutMember,FALSE,TRUE,FALSE)); - } - changeLoopToSeries(dec,member); - newRowInfo->member = member; - if(treeReversed){ - newRowInfo->reversed = firstReversed == arcIsReversedNonRigid(dec,treeArc); - }else{ - newRowInfo->reversed = firstReversed != arcIsReversedNonRigid(dec,treeArc); - } - }else{ - spqr_member cutMember = SPQR_INVALID_MEMBER; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember)); - - cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; - assert(cutArcIsValid(cutArcIdx)); - SCIP_Bool parentCut = FALSE; - - while(cutArcIsValid(cutArcIdx)){ - spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; - cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; - moveArcToNewMember(dec,cutArc,member,cutMember); - if (cutArc == markerToParent(dec,member)){ - parentCut = TRUE; - } - } - spqr_member newSeries; - SCIP_CALL(createMember(dec,SPQR_MEMBERTYPE_SERIES,&newSeries)); - if(parentCut){ - SCIP_CALL(createMarkerPair(dec,newSeries,member,TRUE,FALSE,FALSE)); - SCIP_CALL(createMarkerPair(dec,cutMember,newSeries,TRUE,FALSE,TRUE)); - }else{ - SCIP_CALL(createMarkerPair(dec,member,newSeries,FALSE,FALSE,FALSE)); - SCIP_CALL(createMarkerPair(dec,newSeries,cutMember,FALSE,TRUE,FALSE)); + while( cutArcIsValid(cutArcIdx)) + { + spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; + cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; + moveArcToNewMember(dec, cutArc, member, cutMember); + if( cutArc == markerToParent(dec, member)) + { + parentCut = TRUE; } - newRowInfo->member = newSeries; - cut_arc_id firstCut = newRow->reducedMembers[reducedMember].firstCutArc; - SCIP_Bool firstReversed = newRow->cutArcs[firstCut].arcReversed != arcIsReversedNonRigid(dec,newRow->cutArcs[firstCut].arc); - if(treeReversed){ - newRowInfo->reversed = firstReversed; - }else{ - newRowInfo->reversed = firstReversed; + } + if( parentCut ) + { + SCIP_CALL(createMarkerPair(dec, cutMember, member, TRUE, FALSE, TRUE)); + } else + { + SCIP_CALL(createMarkerPair(dec, member, cutMember, FALSE, TRUE, FALSE)); + } + changeLoopToSeries(dec, member); + newRowInfo->member = member; + if( treeReversed ) + { + newRowInfo->reversed = firstReversed == arcIsReversedNonRigid(dec, treeArc); + } else + { + newRowInfo->reversed = firstReversed != arcIsReversedNonRigid(dec, treeArc); + } + } else + { + spqr_member cutMember = SPQR_INVALID_MEMBER; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember)); + + cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; + assert(cutArcIsValid(cutArcIdx)); + SCIP_Bool parentCut = FALSE; + + while( cutArcIsValid(cutArcIdx)) + { + spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; + cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; + moveArcToNewMember(dec, cutArc, member, cutMember); + if( cutArc == markerToParent(dec, member)) + { + parentCut = TRUE; } - } - - return SCIP_OKAY; - } - - assert(!createCutParallel && !convertOriginalParallel); - assert(numCutArcs == 1); + } + spqr_member newSeries; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &newSeries)); + if( parentCut ) + { + SCIP_CALL(createMarkerPair(dec, newSeries, member, TRUE, FALSE, FALSE)); + SCIP_CALL(createMarkerPair(dec, cutMember, newSeries, TRUE, FALSE, TRUE)); + } else + { + SCIP_CALL(createMarkerPair(dec, member, newSeries, FALSE, FALSE, FALSE)); + SCIP_CALL(createMarkerPair(dec, newSeries, cutMember, FALSE, TRUE, FALSE)); + } + newRowInfo->member = newSeries; + cut_arc_id firstCut = newRow->reducedMembers[reducedMember].firstCutArc; + SCIP_Bool firstReversed = + newRow->cutArcs[firstCut].arcReversed != arcIsReversedNonRigid(dec, newRow->cutArcs[firstCut].arc); + if( treeReversed ) + { + newRowInfo->reversed = firstReversed; + } else + { + newRowInfo->reversed = firstReversed; + } + } + + return SCIP_OKAY; + } + + assert(!createCutParallel && !convertOriginalParallel); + assert(numCutArcs == 1); #ifndef NDEBUG - spqr_arc arc = newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc; - spqr_member adjacentMember = SPQR_INVALID_MEMBER; - if(arc == markerToParent(dec,member)){ - adjacentMember = findMemberParent(dec,member); - }else if(arcIsMarker(dec,arc)){ - adjacentMember = findArcChildMember(dec,arc); - } - if(SPQRmemberIsValid(adjacentMember)){ - assert(getMemberType(dec,adjacentMember) != SPQR_MEMBERTYPE_SERIES); - } + spqr_arc arc = newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc; + spqr_member adjacentMember = SPQR_INVALID_MEMBER; + if( arc == markerToParent(dec, member)) + { + adjacentMember = findMemberParent(dec, member); + } else if( arcIsMarker(dec, arc)) + { + adjacentMember = findArcChildMember(dec, arc); + } + if( SPQRmemberIsValid(adjacentMember)) + { + assert(getMemberType(dec, adjacentMember) != SPQR_MEMBERTYPE_SERIES); + } #endif - spqr_member newSeries; - SCIP_CALL(createMember(dec,SPQR_MEMBERTYPE_SERIES,&newSeries)); - cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; - assert(cutArcIsValid(cutArcIdx)); - SCIP_Bool parentCut = FALSE; - - while(cutArcIsValid(cutArcIdx)){ - spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; - cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; - moveArcToNewMember(dec,cutArc,member,newSeries); - if (cutArc == markerToParent(dec,member)){ - parentCut = TRUE; - } - } - if(parentCut){ - SCIP_CALL(createMarkerPair(dec,newSeries,member,TRUE,TRUE,FALSE)); - }else{ - SCIP_CALL(createMarkerPair(dec,member,newSeries,FALSE,FALSE,TRUE)); - } - newRowInfo->member = newSeries; - cut_arc_id cutArcId = newRow->reducedMembers[reducedMember].firstCutArc; - if(treeReversed){ - newRowInfo->reversed = newRow->cutArcs[cutArcId].arcReversed == arcIsReversedNonRigid(dec,newRow->cutArcs[cutArcId].arc); - }else{ - newRowInfo->reversed = newRow->cutArcs[cutArcId].arcReversed == arcIsReversedNonRigid(dec,newRow->cutArcs[cutArcId].arc); - } - return SCIP_OKAY; -} - -static SCIP_RETCODE transformSingleParallel(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - const reduced_member_id reducedMember, - const spqr_member member, - NewRowInformation * info){ - SCIP_CALL(splitParallelRowAddition(dec,newRow,reducedMember,member,info)); - return SCIP_OKAY; -} - -static SCIP_RETCODE splitSeriesMergingRowAddition(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - const reduced_member_id reducedMember, - const spqr_member member, - spqr_member * const mergingMember, SCIP_Bool * const isCut, - spqr_arc * representativeEdge){ - assert(getNumMemberArcs(dec,member) >= 3); - * isCut = newRow->reducedMembers[reducedMember].numCutArcs > 0; - - if(getNumMemberArcs(dec,member) == 3){ - spqr_arc splitArc = newRow->reducedMembers[reducedMember].splitArc; - spqr_arc otherArc = SPQR_INVALID_ARC; - for (children_idx i = newRow->reducedMembers[reducedMember].firstChild; - i < newRow->reducedMembers[reducedMember].firstChild + newRow->reducedMembers[reducedMember].numChildren; ++i) { - reduced_member_id child = newRow->childrenStorage[i]; - if(newRow->reducedMembers[child].type == TYPE_MERGED){ - spqr_arc testArc = markerOfParent(dec, findMember(dec,newRow->reducedMembers[child].member)); - if(testArc != splitArc){ - otherArc = testArc; - break; - } + spqr_member newSeries; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &newSeries)); + cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; + assert(cutArcIsValid(cutArcIdx)); + SCIP_Bool parentCut = FALSE; + + while( cutArcIsValid(cutArcIdx)) + { + spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; + cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; + moveArcToNewMember(dec, cutArc, member, newSeries); + if( cutArc == markerToParent(dec, member)) + { + parentCut = TRUE; + } + } + if( parentCut ) + { + SCIP_CALL(createMarkerPair(dec, newSeries, member, TRUE, TRUE, FALSE)); + } else + { + SCIP_CALL(createMarkerPair(dec, member, newSeries, FALSE, FALSE, TRUE)); + } + newRowInfo->member = newSeries; + cut_arc_id cutArcId = newRow->reducedMembers[reducedMember].firstCutArc; + if( treeReversed ) + { + newRowInfo->reversed = + newRow->cutArcs[cutArcId].arcReversed == arcIsReversedNonRigid(dec, newRow->cutArcs[cutArcId].arc); + } else + { + newRowInfo->reversed = + newRow->cutArcs[cutArcId].arcReversed == arcIsReversedNonRigid(dec, newRow->cutArcs[cutArcId].arc); + } + return SCIP_OKAY; +} + +static SCIP_RETCODE transformSingleParallel( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const reduced_member_id reducedMember, + const spqr_member member, + NewRowInformation* info +) +{ + SCIP_CALL(splitParallelRowAddition(dec, newRow, reducedMember, member, info)); + return SCIP_OKAY; +} + +static SCIP_RETCODE splitSeriesMergingRowAddition( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + const reduced_member_id reducedMember, + const spqr_member member, + spqr_member* const mergingMember, + SCIP_Bool* const isCut, + spqr_arc* representativeEdge +) +{ + assert(getNumMemberArcs(dec, member) >= 3); + *isCut = newRow->reducedMembers[reducedMember].numCutArcs > 0; + + if( getNumMemberArcs(dec, member) == 3 ) + { + spqr_arc splitArc = newRow->reducedMembers[reducedMember].splitArc; + spqr_arc otherArc = SPQR_INVALID_ARC; + for( children_idx i = newRow->reducedMembers[reducedMember].firstChild; + i < + newRow->reducedMembers[reducedMember].firstChild + newRow->reducedMembers[reducedMember].numChildren; ++i ) + { + reduced_member_id child = newRow->childrenStorage[i]; + if( newRow->reducedMembers[child].type == TYPE_MERGED ) + { + spqr_arc testArc = markerOfParent(dec, findMember(dec, newRow->reducedMembers[child].member)); + if( testArc != splitArc ) + { + otherArc = testArc; + break; } - } - if(SPQRarcIsInvalid(otherArc)){ + } + } + if( SPQRarcIsInvalid(otherArc)) + { #ifndef NDEBUG - reduced_member_id parent = newRow->reducedMembers[reducedMember].parent; + reduced_member_id parent = newRow->reducedMembers[reducedMember].parent; #endif - assert(newRow->reducedMembers[parent].type == TYPE_MERGED || newRow->reducedMembers[parent].type == TYPE_PROPAGATED); - assert(reducedMemberIsValid(parent) && newRow->reducedMembers[parent].type == TYPE_MERGED); - otherArc = markerToParent(dec, member); - - } - spqr_arc firstArc = getFirstMemberArc(dec,member); - spqr_arc arc = firstArc; - do{ - if(arc != splitArc && arc != otherArc){ - *representativeEdge = arc; - break; - } - arc = getNextMemberArc(dec,arc); - }while(arc != firstArc); - *mergingMember = member; - return SCIP_OKAY; - } - //Split off the relevant part of the series member - spqr_member mergingSeries = SPQR_INVALID_MEMBER; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &mergingSeries)); - //Move all marker arcs which point to another component in the reduced decomposition to the new member - //This should be exactly 2, as with 3 the result is not network anymore - //move all mergeable children and parent arcs to the mergingMember - - SCIP_Bool coTreeToMergingMember = FALSE; - SCIP_Bool parentToMergingMember = FALSE; - for (children_idx i = newRow->reducedMembers[reducedMember].firstChild; - i < newRow->reducedMembers[reducedMember].firstChild + newRow->reducedMembers[reducedMember].numChildren; ++i) { - reduced_member_id child = newRow->childrenStorage[i]; - assert(newRow->reducedMembers[child].type == TYPE_MERGED || newRow->reducedMembers[child].type == TYPE_PROPAGATED); - if(newRow->reducedMembers[child].type == TYPE_MERGED){ - spqr_arc moveArc = markerOfParent(dec, findMember(dec,newRow->reducedMembers[child].member)); - moveArcToNewMember(dec,moveArc,member,mergingSeries); - if(!arcIsTree(dec,moveArc)){ - coTreeToMergingMember = TRUE; - } - } - } - - reduced_member_id parent = newRow->reducedMembers[reducedMember].parent; - assert(reducedMemberIsInvalid(parent) || (newRow->reducedMembers[parent].type == TYPE_MERGED || newRow->reducedMembers[parent].type == TYPE_PROPAGATED)); - - if(reducedMemberIsValid(parent) && - newRow->reducedMembers[parent].type == TYPE_MERGED ){ - spqr_arc moveArc = markerToParent(dec, member); - moveArcToNewMember(dec,moveArc,member,mergingSeries); - parentToMergingMember = TRUE; - if(!arcIsTree(dec,moveArc)){ + assert(newRow->reducedMembers[parent].type == TYPE_MERGED || + newRow->reducedMembers[parent].type == TYPE_PROPAGATED); + assert(reducedMemberIsValid(parent) && newRow->reducedMembers[parent].type == TYPE_MERGED); + otherArc = markerToParent(dec, member); + } + spqr_arc firstArc = getFirstMemberArc(dec, member); + spqr_arc arc = firstArc; + do + { + if( arc != splitArc && arc != otherArc ) + { + *representativeEdge = arc; + break; + } + arc = getNextMemberArc(dec, arc); + } while( arc != firstArc ); + *mergingMember = member; + return SCIP_OKAY; + } + //Split off the relevant part of the series member + spqr_member mergingSeries = SPQR_INVALID_MEMBER; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &mergingSeries)); + //Move all marker arcs which point to another component in the reduced decomposition to the new member + //This should be exactly 2, as with 3 the result is not network anymore + //move all mergeable children and parent arcs to the mergingMember + + SCIP_Bool coTreeToMergingMember = FALSE; + SCIP_Bool parentToMergingMember = FALSE; + for( children_idx i = newRow->reducedMembers[reducedMember].firstChild; + i < newRow->reducedMembers[reducedMember].firstChild + newRow->reducedMembers[reducedMember].numChildren; ++i ) + { + reduced_member_id child = newRow->childrenStorage[i]; + assert( + newRow->reducedMembers[child].type == TYPE_MERGED || newRow->reducedMembers[child].type == TYPE_PROPAGATED); + if( newRow->reducedMembers[child].type == TYPE_MERGED ) + { + spqr_arc moveArc = markerOfParent(dec, findMember(dec, newRow->reducedMembers[child].member)); + moveArcToNewMember(dec, moveArc, member, mergingSeries); + if( !arcIsTree(dec, moveArc)) + { coTreeToMergingMember = TRUE; - } - } - spqr_arc ignoreArc = SPQR_INVALID_ARC; - if(parentToMergingMember){ - SCIP_CALL(createMarkerPairWithReferences(dec,mergingSeries,member,coTreeToMergingMember,TRUE,FALSE,representativeEdge,&ignoreArc)); - }else{ - SCIP_CALL(createMarkerPairWithReferences(dec,member,mergingSeries,!coTreeToMergingMember,FALSE,TRUE,&ignoreArc,representativeEdge)); - } - - *mergingMember = mergingSeries; - assert(getNumMemberArcs(dec,mergingSeries) == 3 ); - assert(getNumMemberArcs(dec,member) >= 3); - return SCIP_OKAY; -} - -static SCIP_RETCODE splitParallelMerging(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id reducedMember, spqr_member member,spqr_member * const pMergeMember, - spqr_arc * const cutRepresentative){ - //When merging, we cannot have propagated members; - assert(newRow->reducedMembers[reducedMember].numCutArcs < (getNumMemberArcs(dec,member)-1)); - - int numMergeableAdjacent = newRow->reducedMembers[reducedMember].numChildren - newRow->reducedMembers[reducedMember].numPropagatedChildren; - if(reducedMemberIsValid(newRow->reducedMembers[reducedMember].parent) && - newRow->reducedMembers[newRow->reducedMembers[reducedMember].parent].type == TYPE_MERGED){ - numMergeableAdjacent++; - } - - int numCutArcs = newRow->reducedMembers[reducedMember].numCutArcs; - //All arcs which are not in the mergeable decomposition or cut - int numBaseSplitAwayArcs = getNumMemberArcs(dec,member) - numMergeableAdjacent - numCutArcs ; - - SCIP_Bool createCutParallel = numCutArcs > 1; - SCIP_Bool keepOriginalParallel = numBaseSplitAwayArcs <= 1; - - spqr_member cutMember = SPQR_INVALID_MEMBER; - //The below cases can probably be aggregated in some way, but for now we first focus on the correct logic - if(createCutParallel && keepOriginalParallel){ - SCIP_Bool parentCut = FALSE; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember)); - - cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; - assert(cutArcIsValid(cutArcIdx)); - - while(cutArcIsValid(cutArcIdx)){ - spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; - cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; - moveArcToNewMember(dec,cutArc,member,cutMember); - if (cutArc == markerToParent(dec,member)){ - parentCut = TRUE; - } - } - spqr_arc ignoreArc = SPQR_INVALID_ARC; - if(parentCut){ - SCIP_CALL(createMarkerPairWithReferences(dec,cutMember,member,TRUE,FALSE,FALSE,&ignoreArc,cutRepresentative)); - }else{ - SCIP_CALL(createMarkerPairWithReferences(dec,member,cutMember,FALSE,FALSE,FALSE,cutRepresentative,&ignoreArc)); - } - - *pMergeMember = member; - }else if(createCutParallel){ - assert(!keepOriginalParallel); - - SCIP_Bool parentCut = FALSE; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember)); - - cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; - assert(cutArcIsValid(cutArcIdx)); - - while(cutArcIsValid(cutArcIdx)){ - spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; - cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; - moveArcToNewMember(dec,cutArc,member,cutMember); - if (cutArc == markerToParent(dec,member)){ - parentCut = TRUE; - } - } - spqr_arc ignoreArc = SPQR_INVALID_ARC; - if(parentCut){ - SCIP_CALL(createMarkerPairWithReferences(dec,cutMember,member,TRUE,FALSE,FALSE,&ignoreArc,cutRepresentative)); - }else{ - SCIP_CALL(createMarkerPairWithReferences(dec,member,cutMember,FALSE,FALSE,FALSE,cutRepresentative,&ignoreArc)); - } - - - spqr_arc noCutRepresentative = SPQR_INVALID_ARC; - spqr_member mergingMember = member; - SCIP_Bool parentToMergingMember = FALSE; - SCIP_Bool treeToMergingMember = FALSE; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &mergingMember)); - //move all mergeable children and parent arcs to the mergingMember - for (children_idx i = newRow->reducedMembers[reducedMember].firstChild; - i < newRow->reducedMembers[reducedMember].firstChild + - newRow->reducedMembers[reducedMember].numChildren; ++i) { - reduced_member_id child = newRow->childrenStorage[i]; - assert(newRow->reducedMembers[child].type == TYPE_MERGED || newRow->reducedMembers[child].type == TYPE_PROPAGATED); - if (newRow->reducedMembers[child].type == TYPE_MERGED) { - spqr_arc moveArc = markerOfParent(dec, findMember(dec,newRow->reducedMembers[child].member)); - moveArcToNewMember(dec, moveArc, member, mergingMember); - if (arcIsTree(dec, moveArc)) { - treeToMergingMember = TRUE; - } - } - } - reduced_member_id parent = newRow->reducedMembers[reducedMember].parent; - if (reducedMemberIsValid(parent) && - newRow->reducedMembers[parent].type == TYPE_MERGED) { - spqr_arc moveArc = markerToParent(dec, member); + } + } + } + + reduced_member_id parent = newRow->reducedMembers[reducedMember].parent; + assert(reducedMemberIsInvalid(parent) || ( newRow->reducedMembers[parent].type == TYPE_MERGED || + newRow->reducedMembers[parent].type == TYPE_PROPAGATED )); + + if( reducedMemberIsValid(parent) && + newRow->reducedMembers[parent].type == TYPE_MERGED ) + { + spqr_arc moveArc = markerToParent(dec, member); + moveArcToNewMember(dec, moveArc, member, mergingSeries); + parentToMergingMember = TRUE; + if( !arcIsTree(dec, moveArc)) + { + coTreeToMergingMember = TRUE; + } + } + spqr_arc ignoreArc = SPQR_INVALID_ARC; + if( parentToMergingMember ) + { + SCIP_CALL(createMarkerPairWithReferences(dec, mergingSeries, member, coTreeToMergingMember, TRUE, FALSE, + representativeEdge, &ignoreArc)); + } else + { + SCIP_CALL( + createMarkerPairWithReferences(dec, member, mergingSeries, !coTreeToMergingMember, FALSE, TRUE, &ignoreArc, + representativeEdge)); + } + + *mergingMember = mergingSeries; + assert(getNumMemberArcs(dec, mergingSeries) == 3); + assert(getNumMemberArcs(dec, member) >= 3); + return SCIP_OKAY; +} + +static SCIP_RETCODE splitParallelMerging( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id reducedMember, + spqr_member member, + spqr_member* const pMergeMember, + spqr_arc* const cutRepresentative +) +{ + //When merging, we cannot have propagated members; + assert(newRow->reducedMembers[reducedMember].numCutArcs < ( getNumMemberArcs(dec, member) - 1 )); + + int numMergeableAdjacent = + newRow->reducedMembers[reducedMember].numChildren - newRow->reducedMembers[reducedMember].numPropagatedChildren; + if( reducedMemberIsValid(newRow->reducedMembers[reducedMember].parent) && + newRow->reducedMembers[newRow->reducedMembers[reducedMember].parent].type == TYPE_MERGED ) + { + numMergeableAdjacent++; + } + + int numCutArcs = newRow->reducedMembers[reducedMember].numCutArcs; + //All arcs which are not in the mergeable decomposition or cut + int numBaseSplitAwayArcs = getNumMemberArcs(dec, member) - numMergeableAdjacent - numCutArcs; + + SCIP_Bool createCutParallel = numCutArcs > 1; + SCIP_Bool keepOriginalParallel = numBaseSplitAwayArcs <= 1; + + spqr_member cutMember = SPQR_INVALID_MEMBER; + //The below cases can probably be aggregated in some way, but for now we first focus on the correct logic + if( createCutParallel && keepOriginalParallel ) + { + SCIP_Bool parentCut = FALSE; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember)); + + cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; + assert(cutArcIsValid(cutArcIdx)); + + while( cutArcIsValid(cutArcIdx)) + { + spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; + cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; + moveArcToNewMember(dec, cutArc, member, cutMember); + if( cutArc == markerToParent(dec, member)) + { + parentCut = TRUE; + } + } + spqr_arc ignoreArc = SPQR_INVALID_ARC; + if( parentCut ) + { + SCIP_CALL( + createMarkerPairWithReferences(dec, cutMember, member, TRUE, FALSE, FALSE, &ignoreArc, cutRepresentative)); + } else + { + SCIP_CALL( + createMarkerPairWithReferences(dec, member, cutMember, FALSE, FALSE, FALSE, cutRepresentative, &ignoreArc)); + } + + *pMergeMember = member; + } else if( createCutParallel ) + { + assert(!keepOriginalParallel); + + SCIP_Bool parentCut = FALSE; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember)); + + cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; + assert(cutArcIsValid(cutArcIdx)); + + while( cutArcIsValid(cutArcIdx)) + { + spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; + cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; + moveArcToNewMember(dec, cutArc, member, cutMember); + if( cutArc == markerToParent(dec, member)) + { + parentCut = TRUE; + } + } + spqr_arc ignoreArc = SPQR_INVALID_ARC; + if( parentCut ) + { + SCIP_CALL( + createMarkerPairWithReferences(dec, cutMember, member, TRUE, FALSE, FALSE, &ignoreArc, cutRepresentative)); + } else + { + SCIP_CALL( + createMarkerPairWithReferences(dec, member, cutMember, FALSE, FALSE, FALSE, cutRepresentative, &ignoreArc)); + } + + + spqr_arc noCutRepresentative = SPQR_INVALID_ARC; + spqr_member mergingMember = member; + SCIP_Bool parentToMergingMember = FALSE; + SCIP_Bool treeToMergingMember = FALSE; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &mergingMember)); + //move all mergeable children and parent arcs to the mergingMember + for( children_idx i = newRow->reducedMembers[reducedMember].firstChild; + i < newRow->reducedMembers[reducedMember].firstChild + + newRow->reducedMembers[reducedMember].numChildren; + ++i ) + { + reduced_member_id child = newRow->childrenStorage[i]; + assert( + newRow->reducedMembers[child].type == TYPE_MERGED || newRow->reducedMembers[child].type == TYPE_PROPAGATED); + if( newRow->reducedMembers[child].type == TYPE_MERGED ) + { + spqr_arc moveArc = markerOfParent(dec, findMember(dec, newRow->reducedMembers[child].member)); moveArcToNewMember(dec, moveArc, member, mergingMember); + if( arcIsTree(dec, moveArc)) + { + treeToMergingMember = TRUE; + } + } + } + reduced_member_id parent = newRow->reducedMembers[reducedMember].parent; + if( reducedMemberIsValid(parent) && + newRow->reducedMembers[parent].type == TYPE_MERGED ) + { + spqr_arc moveArc = markerToParent(dec, member); + moveArcToNewMember(dec, moveArc, member, mergingMember); + parentToMergingMember = TRUE; + if( arcIsTree(dec, moveArc)) + { + treeToMergingMember = TRUE; + } + } + //If there is only one cut arc, we also move it. + if( SPQRarcIsValid(*cutRepresentative)) + { + if( *cutRepresentative == markerToParent(dec, member)) + { parentToMergingMember = TRUE; - if (arcIsTree(dec, moveArc)) { - treeToMergingMember = TRUE; + } + moveArcToNewMember(dec, *cutRepresentative, member, mergingMember); + } + spqr_arc ignoreArgument = SPQR_INVALID_ARC; + if( parentToMergingMember || parentCut ) + { + SCIP_CALL(createMarkerPairWithReferences(dec, mergingMember, member, !treeToMergingMember, FALSE, FALSE, + &ignoreArgument, &noCutRepresentative)); + } else + { + SCIP_CALL(createMarkerPairWithReferences(dec, member, mergingMember, treeToMergingMember, FALSE, FALSE, + &noCutRepresentative, &ignoreArgument)); + } + *pMergeMember = mergingMember; + } else if( keepOriginalParallel ) + { + assert(!createCutParallel); + if( cutArcIsValid(newRow->reducedMembers[reducedMember].firstCutArc)) + { + *cutRepresentative = newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc; + } + *pMergeMember = member; + + } else + { + assert(!keepOriginalParallel && !createCutParallel); + + if( cutArcIsValid(newRow->reducedMembers[reducedMember].firstCutArc)) + { + *cutRepresentative = newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc; + } + + spqr_arc noCutRepresentative = SPQR_INVALID_ARC; + spqr_member mergingMember = member; + SCIP_Bool parentToMergingMember = FALSE; + SCIP_Bool treeToMergingMember = FALSE; + SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &mergingMember)); + //move all mergeable children and parent arcs to the mergingMember + for( children_idx i = newRow->reducedMembers[reducedMember].firstChild; + i < newRow->reducedMembers[reducedMember].firstChild + + newRow->reducedMembers[reducedMember].numChildren; + ++i ) + { + reduced_member_id child = newRow->childrenStorage[i]; + assert( + newRow->reducedMembers[child].type == TYPE_MERGED || newRow->reducedMembers[child].type == TYPE_PROPAGATED); + if( newRow->reducedMembers[child].type == TYPE_MERGED ) + { + spqr_arc moveArc = markerOfParent(dec, findMember(dec, newRow->reducedMembers[child].member)); + moveArcToNewMember(dec, moveArc, member, mergingMember); + if( arcIsTree(dec, moveArc)) + { + treeToMergingMember = TRUE; } - } - //If there is only one cut arc, we also move it. - if (SPQRarcIsValid(*cutRepresentative)) { - if (*cutRepresentative == markerToParent(dec, member)) { - parentToMergingMember = TRUE; + } + } + reduced_member_id parent = newRow->reducedMembers[reducedMember].parent; + if( reducedMemberIsValid(parent) && + newRow->reducedMembers[parent].type == TYPE_MERGED ) + { + spqr_arc moveArc = markerToParent(dec, member); + moveArcToNewMember(dec, moveArc, member, mergingMember); + parentToMergingMember = TRUE; + if( arcIsTree(dec, moveArc)) + { + treeToMergingMember = TRUE; + } + } + //If there is only one cut arc, we also move it. + if( SPQRarcIsValid(*cutRepresentative)) + { + if( *cutRepresentative == markerToParent(dec, member)) + { + parentToMergingMember = TRUE; + } + moveArcToNewMember(dec, *cutRepresentative, member, mergingMember); + } + spqr_arc ignoreArgument = SPQR_INVALID_ARC; + if( parentToMergingMember ) + { + SCIP_CALL(createMarkerPairWithReferences(dec, mergingMember, member, !treeToMergingMember, FALSE, FALSE, + &ignoreArgument, &noCutRepresentative)); + } else + { + SCIP_CALL(createMarkerPairWithReferences(dec, member, mergingMember, treeToMergingMember, FALSE, FALSE, + &noCutRepresentative, &ignoreArgument)); + } + *pMergeMember = mergingMember; + } + return SCIP_OKAY; +} + +static SCIP_RETCODE splitFirstLeaf( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id leaf, + NewRowInformation* const newRowInformation +) +{ + assert(cutArcIsValid(newRow->reducedMembers[leaf].firstCutArc)); + assert(newRow->reducedMembers[leaf].numCutArcs > 0); + spqr_member member = newRow->reducedMembers[leaf].member; + if( getMemberType(dec, member) == SPQR_MEMBERTYPE_PARALLEL ) + { + spqr_member mergeMember = SPQR_INVALID_MEMBER; + spqr_arc cutRepresentative = SPQR_INVALID_ARC; + SCIP_CALL(splitParallelMerging(dec, newRow, leaf, member, &mergeMember, &cutRepresentative)); + newRow->reducedMembers[leaf].member = mergeMember; + + spqr_node firstNode = SPQR_INVALID_NODE; + spqr_node secondNode = SPQR_INVALID_NODE; + spqr_node thirdNode = SPQR_INVALID_NODE; + SCIP_CALL(createNode(dec, &firstNode)); + SCIP_CALL(createNode(dec, &secondNode)); + SCIP_CALL(createNode(dec, &thirdNode)); + + spqr_arc splitArc = newRow->reducedMembers[leaf].splitArc; + assert(findArcMemberNoCompression(dec, splitArc) == mergeMember); + SCIP_Bool splitArcReversed = arcIsReversedNonRigid(dec, splitArc); + SCIP_Bool splitHead = newRow->reducedMembers[leaf].splitHead; + spqr_node splitArcHead = splitArcReversed ? secondNode : firstNode; + spqr_node splitArcTail = splitArcReversed ? firstNode : secondNode; + spqr_node splitNode = splitHead ? splitArcHead : splitArcTail; + spqr_node otherNode = splitHead ? splitArcTail : splitArcHead; + + spqr_arc first_arc = getFirstMemberArc(dec, mergeMember); + spqr_arc arc = first_arc; + + do + { + if( arc != cutRepresentative ) + { + if( arcIsReversedNonRigid(dec, arc)) + { + setArcHeadAndTail(dec, arc, secondNode, firstNode); + } else + { + setArcHeadAndTail(dec, arc, firstNode, secondNode); } - moveArcToNewMember(dec, *cutRepresentative, member, mergingMember); - } - spqr_arc ignoreArgument = SPQR_INVALID_ARC; - if(parentToMergingMember || parentCut){ - SCIP_CALL(createMarkerPairWithReferences(dec,mergingMember,member,!treeToMergingMember,FALSE,FALSE,&ignoreArgument,&noCutRepresentative)); - }else{ - SCIP_CALL(createMarkerPairWithReferences(dec,member,mergingMember,treeToMergingMember,FALSE,FALSE,&noCutRepresentative,&ignoreArgument)); - } - *pMergeMember = mergingMember; - }else if(keepOriginalParallel){ - assert(!createCutParallel); - if(cutArcIsValid( newRow->reducedMembers[reducedMember].firstCutArc)){ - *cutRepresentative = newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc; - } - *pMergeMember = member; - - }else{ - assert(!keepOriginalParallel && ! createCutParallel); - - if(cutArcIsValid( newRow->reducedMembers[reducedMember].firstCutArc)){ - *cutRepresentative = newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc; - } - - spqr_arc noCutRepresentative = SPQR_INVALID_ARC; - spqr_member mergingMember = member; - SCIP_Bool parentToMergingMember = FALSE; - SCIP_Bool treeToMergingMember = FALSE; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &mergingMember)); - //move all mergeable children and parent arcs to the mergingMember - for (children_idx i = newRow->reducedMembers[reducedMember].firstChild; - i < newRow->reducedMembers[reducedMember].firstChild + - newRow->reducedMembers[reducedMember].numChildren; ++i) { - reduced_member_id child = newRow->childrenStorage[i]; - assert(newRow->reducedMembers[child].type == TYPE_MERGED || newRow->reducedMembers[child].type == TYPE_PROPAGATED); - if (newRow->reducedMembers[child].type == TYPE_MERGED) { - spqr_arc moveArc = markerOfParent(dec, findMember(dec,newRow->reducedMembers[child].member)); - moveArcToNewMember(dec, moveArc, member, mergingMember); - if (arcIsTree(dec, moveArc)) { - treeToMergingMember = TRUE; - } + } else + { + if(( arcIsReversedNonRigid(dec, arc) == splitArcReversed ) == splitHead ) + { + setArcHeadAndTail(dec, arc, thirdNode, otherNode); + } else + { + setArcHeadAndTail(dec, arc, otherNode, thirdNode); } - } - reduced_member_id parent = newRow->reducedMembers[reducedMember].parent; - if (reducedMemberIsValid(parent) && - newRow->reducedMembers[parent].type == TYPE_MERGED) { - spqr_arc moveArc = markerToParent(dec, member); - moveArcToNewMember(dec, moveArc, member, mergingMember); - parentToMergingMember = TRUE; - if (arcIsTree(dec, moveArc)) { - treeToMergingMember = TRUE; + } + arcSetReversed(dec, arc, FALSE); + if( arc == splitArc ) + { + arcSetRepresentative(dec, arc, SPQR_INVALID_ARC); + } else + { + arcSetRepresentative(dec, arc, splitArc); + } + arc = getNextMemberArc(dec, arc); + } while( arc != first_arc ); + + + updateMemberType(dec, mergeMember, SPQR_MEMBERTYPE_RIGID); + newRowInformation->member = mergeMember; + if( newRow->reducedMembers[leaf].otherIsSource ) + { + newRowInformation->head = thirdNode; + newRowInformation->tail = splitNode; + } else + { + newRowInformation->head = splitNode; + newRowInformation->tail = thirdNode; + } + + newRowInformation->reversed = FALSE; + newRowInformation->representative = splitArc; + + return SCIP_OKAY; + } + assert(getMemberType(dec, member) == SPQR_MEMBERTYPE_RIGID); + + spqr_node newNode = SPQR_INVALID_NODE;//Sink node + SCIP_CALL(createNode(dec, &newNode)); + + spqr_node splitNode = newRow->reducedMembers[leaf].splitNode; + + spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode); + spqr_arc iterArc = firstNodeArc; + do + { + spqr_node otherHead = findArcHead(dec, iterArc); + spqr_node otherTail = findArcTail(dec, iterArc); + spqr_node otherEnd = otherHead == splitNode ? otherTail : otherHead; + spqr_arc nextArc = getNextNodeArc(dec, iterArc, splitNode);//Need to do this before we modify the arc + + SCIP_Bool isCut = newRow->isArcCut[iterArc]; + SCIP_Bool isMoveColor = newRow->nodeColors[otherEnd] == COLOR_SOURCE; + SCIP_Bool changeArcEnd = isCut == isMoveColor; + if( changeArcEnd ) + { + if( otherHead == splitNode ) + { + changeArcHead(dec, iterArc, otherHead, newNode); + } else + { + changeArcTail(dec, iterArc, otherTail, newNode); + } + } + //Ugly hack to make sure we can iterate neighbourhood whilst changing arc ends. + newRow->nodeColors[otherEnd] = UNCOLORED; + spqr_arc previousArc = iterArc; + iterArc = nextArc; + if( iterArc == firstNodeArc ) + { + break; + } + if( changeArcEnd && previousArc == firstNodeArc ) + { + firstNodeArc = iterArc; + } + } while( TRUE ); + newRowInformation->head = newNode; + newRowInformation->tail = splitNode; + newRowInformation->member = member; + newRowInformation->reversed = FALSE; + newRowInformation->representative = findArcSign(dec, iterArc).representative; + + return SCIP_OKAY; +} + +static SCIP_RETCODE mergeSplitMemberIntoParent( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + spqr_member member, + spqr_member parent, + spqr_arc parentToChild, + spqr_arc childToParent, + SCIP_Bool headToHead, + spqr_node parentNode, + spqr_node childNode, + spqr_member* mergedMember, + spqr_node* arcNodeOne, + spqr_node* arcNodeTwo, + spqr_node* thirdNode +) +{ + assert(dec); + assert(SPQRmemberIsValid(member)); + assert(memberIsRepresentative(dec, member)); + assert(SPQRmemberIsValid(parent)); + assert(memberIsRepresentative(dec, parent)); + assert(findMemberParentNoCompression(dec, member) == parent); + assert(markerOfParent(dec, member) == parentToChild); + assert(markerToParent(dec, member) == childToParent); + + removeArcFromMemberArcList(dec, parentToChild, parent); + removeArcFromMemberArcList(dec, childToParent, member); + + spqr_node parentArcNodes[2] = {findEffectiveArcTail(dec, parentToChild), findEffectiveArcHead(dec, parentToChild)}; + spqr_node childArcNodes[2] = {findEffectiveArcTail(dec, childToParent), findEffectiveArcHead(dec, childToParent)}; + + clearArcHeadAndTail(dec, parentToChild); + clearArcHeadAndTail(dec, childToParent); + + spqr_node first = childArcNodes[headToHead ? 0 : 1]; + spqr_node second = childArcNodes[headToHead ? 1 : 0]; + { + spqr_node newNode = mergeNodes(dec, parentArcNodes[0], first); + spqr_node toRemoveFrom = newNode == first ? parentArcNodes[0] : first; + mergeNodeArcList(dec, newNode, toRemoveFrom); + *arcNodeOne = newNode; + newRow->nodeColors[toRemoveFrom] = UNCOLORED; + } + { + spqr_node newNode = mergeNodes(dec, parentArcNodes[1], second); + spqr_node toRemoveFrom = newNode == second ? parentArcNodes[1] : second; + mergeNodeArcList(dec, newNode, toRemoveFrom); + *arcNodeTwo = newNode; + newRow->nodeColors[toRemoveFrom] = UNCOLORED; + } + { + spqr_node newNode = mergeNodes(dec, parentNode, childNode); + spqr_node toRemoveFrom = newNode == childNode ? parentNode : childNode; + mergeNodeArcList(dec, newNode, toRemoveFrom); + *thirdNode = newNode; + newRow->nodeColors[toRemoveFrom] = UNCOLORED; + } + + spqr_member newMember = mergeMembers(dec, member, parent); + spqr_member toRemoveFrom = newMember == member ? parent : member; + mergeMemberArcList(dec, newMember, toRemoveFrom); + if( toRemoveFrom == parent ) + { + updateMemberParentInformation(dec, newMember, toRemoveFrom); + } + updateMemberType(dec, newMember, SPQR_MEMBERTYPE_RIGID); + *mergedMember = newMember; + return SCIP_OKAY; +} + +static SCIP_RETCODE splitAndMergeSeries( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id largeMember, + reduced_member_id smallMember, + SCIP_Bool largeIsParent, + NewRowInformation* const newRowInformation, + spqr_member member +) +{ + SCIP_Bool isCut = FALSE; + spqr_member mergingMember = SPQR_INVALID_MEMBER; + spqr_arc nonVirtualArc = SPQR_INVALID_ARC; + SCIP_CALL(splitSeriesMergingRowAddition(dec, newRow, smallMember, member, &mergingMember, &isCut, &nonVirtualArc)); + assert(getNumMemberArcs(dec, mergingMember) == 3); + + //create the split series. There's two possible configurations, based on whether it contains a cut edge or not + spqr_node a = SPQR_INVALID_NODE; + spqr_node b = SPQR_INVALID_NODE; + spqr_node c = SPQR_INVALID_NODE; + spqr_node d = SPQR_INVALID_NODE; + SCIP_CALL(createNode(dec, &a)); + SCIP_CALL(createNode(dec, &b)); + SCIP_CALL(createNode(dec, &c)); + SCIP_CALL(createNode(dec, &d)); + + spqr_arc splitArc = newRow->reducedMembers[smallMember].splitArc; + + { + SCIP_Bool splitHead = newRow->reducedMembers[smallMember].splitHead; + spqr_arc firstArc = getFirstMemberArc(dec, mergingMember); + spqr_arc arc = firstArc; + SCIP_Bool splitReversed = arcIsReversedNonRigid(dec, splitArc); + do + { + if( arc == splitArc ) + { + if( splitHead ) + { + setArcHeadAndTail(dec, splitArc, b, a); + } else + { + setArcHeadAndTail(dec, splitArc, a, b); } - } - //If there is only one cut arc, we also move it. - if (SPQRarcIsValid(*cutRepresentative)) { - if (*cutRepresentative == markerToParent(dec, member)) { - parentToMergingMember = TRUE; + } else if( arc == nonVirtualArc ) + { + if(( arcIsReversedNonRigid(dec, arc) == splitReversed ) == splitHead ) + { + setArcHeadAndTail(dec, arc, a, d); + } else + { + setArcHeadAndTail(dec, arc, d, a); } - moveArcToNewMember(dec, *cutRepresentative, member, mergingMember); - } - spqr_arc ignoreArgument = SPQR_INVALID_ARC; - if(parentToMergingMember){ - SCIP_CALL(createMarkerPairWithReferences(dec,mergingMember,member,!treeToMergingMember,FALSE,FALSE,&ignoreArgument,&noCutRepresentative)); - }else{ - SCIP_CALL(createMarkerPairWithReferences(dec,member,mergingMember,treeToMergingMember,FALSE,FALSE,&noCutRepresentative,&ignoreArgument)); - } - *pMergeMember = mergingMember; - } - return SCIP_OKAY; -} -static SCIP_RETCODE splitFirstLeaf(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id leaf, NewRowInformation * const newRowInformation){ - assert(cutArcIsValid(newRow->reducedMembers[leaf].firstCutArc)); - assert(newRow->reducedMembers[leaf].numCutArcs > 0); - spqr_member member = newRow->reducedMembers[leaf].member; - if(getMemberType(dec,member) == SPQR_MEMBERTYPE_PARALLEL){ - spqr_member mergeMember = SPQR_INVALID_MEMBER; - spqr_arc cutRepresentative = SPQR_INVALID_ARC; - SCIP_CALL(splitParallelMerging(dec,newRow,leaf,member,&mergeMember,&cutRepresentative)); - newRow->reducedMembers[leaf].member = mergeMember; - - spqr_node firstNode = SPQR_INVALID_NODE; - spqr_node secondNode = SPQR_INVALID_NODE; - spqr_node thirdNode = SPQR_INVALID_NODE; - SCIP_CALL(createNode(dec,&firstNode)); - SCIP_CALL(createNode(dec,&secondNode)); - SCIP_CALL(createNode(dec,&thirdNode)); - - spqr_arc splitArc = newRow->reducedMembers[leaf].splitArc; - assert(findArcMemberNoCompression(dec,splitArc) == mergeMember); - SCIP_Bool splitArcReversed = arcIsReversedNonRigid(dec,splitArc); - SCIP_Bool splitHead = newRow->reducedMembers[leaf].splitHead; - spqr_node splitArcHead = splitArcReversed ? secondNode : firstNode; - spqr_node splitArcTail = splitArcReversed ? firstNode : secondNode; - spqr_node splitNode = splitHead ? splitArcHead : splitArcTail; - spqr_node otherNode = splitHead ? splitArcTail : splitArcHead; - - spqr_arc first_arc = getFirstMemberArc(dec, mergeMember); - spqr_arc arc = first_arc; - - do { - if(arc != cutRepresentative){ - if(arcIsReversedNonRigid(dec,arc)){ - setArcHeadAndTail(dec,arc,secondNode,firstNode); - }else{ - setArcHeadAndTail(dec,arc,firstNode,secondNode); - } - }else{ - if ((arcIsReversedNonRigid(dec, arc) == splitArcReversed) == splitHead) { - setArcHeadAndTail(dec, arc, thirdNode, otherNode); - } else { - setArcHeadAndTail(dec, arc, otherNode, thirdNode); - } + } else + { + spqr_node otherNode = cutArcIsValid(newRow->reducedMembers[smallMember].firstCutArc) ? c : b; + if(( arcIsReversedNonRigid(dec, arc) == splitReversed ) == splitHead ) + { + setArcHeadAndTail(dec, arc, d, otherNode); + } else + { + setArcHeadAndTail(dec, arc, otherNode, d); } - arcSetReversed(dec,arc,FALSE); - if(arc == splitArc){ - arcSetRepresentative(dec,arc,SPQR_INVALID_ARC); - }else{ - arcSetRepresentative(dec,arc,splitArc); + } + arcSetReversed(dec, arc, FALSE); + arcSetRepresentative(dec, arc, splitArc); + arc = getNextMemberArc(dec, arc); + } while( arc != firstArc ); + arcSetRepresentative(dec, splitArc, SPQR_INVALID_ARC); + } + + spqr_member otherMember = newRowInformation->member; + spqr_arc otherMarker = largeIsParent ? markerOfParent(dec, mergingMember) : markerToParent(dec, otherMember); + + assert(nodeIsRepresentative(dec, newRowInformation->tail)); + assert(nodeIsRepresentative(dec, newRowInformation->head)); + spqr_node splitNode = newRow->reducedMembers[smallMember].splitHead ? findEffectiveArcHead(dec, otherMarker) + : findEffectiveArcTail(dec, otherMarker); + + spqr_node otherNode = splitNode == newRowInformation->head ? newRowInformation->tail : newRowInformation->head; + assert(splitNode == newRowInformation->head || splitNode == newRowInformation->tail); + newRowInformation->representative = mergeArcSigns(dec, newRowInformation->representative, splitArc, FALSE); + + spqr_member mergedMember = SPQR_INVALID_MEMBER; + spqr_node arcNodeOne; + spqr_node arcNodeTwo; + spqr_node thirdNode; + if( largeIsParent ) + { + SCIP_CALL(mergeSplitMemberIntoParent(dec, newRow, mergingMember, otherMember, otherMarker, splitArc, TRUE, + otherNode, c, &mergedMember, + &arcNodeOne, + &arcNodeTwo, + &thirdNode)); + } else + { + SCIP_CALL(mergeSplitMemberIntoParent(dec, newRow, otherMember, mergingMember, splitArc, otherMarker, TRUE, + c, otherNode, &mergedMember, + &arcNodeOne, + &arcNodeTwo, + &thirdNode)); + } + newRow->reducedMembers[smallMember].member = mergedMember; + + newRowInformation->member = mergedMember; + SCIP_Bool splitIsReferenceHead = newRow->reducedMembers[smallMember].splitHead; + SCIP_Bool splitIsNewRowHead = splitNode == newRowInformation->head; + if( !splitIsReferenceHead && !splitIsNewRowHead ) + { + newRowInformation->head = thirdNode; + newRowInformation->tail = arcNodeOne; + } else if( !splitIsReferenceHead && splitIsNewRowHead ) + { + newRowInformation->head = arcNodeOne; + newRowInformation->tail = thirdNode; + } else if( splitIsReferenceHead && !splitIsNewRowHead ) + { + newRowInformation->head = thirdNode; + newRowInformation->tail = arcNodeTwo; + } else if( splitIsReferenceHead && splitIsNewRowHead ) + { + newRowInformation->head = arcNodeTwo; + newRowInformation->tail = thirdNode; + } + + return SCIP_OKAY; +} + +static SCIP_RETCODE splitAndMergeParallel( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id largeMember, + reduced_member_id smallMember, + SCIP_Bool largeIsParent, + NewRowInformation* const newRowInformation, + spqr_member member +) +{ + spqr_member mergeMember = SPQR_INVALID_MEMBER; + spqr_arc cutRepresentative = SPQR_INVALID_ARC; + SCIP_CALL(splitParallelMerging(dec, newRow, smallMember, member, &mergeMember, &cutRepresentative)); + newRow->reducedMembers[smallMember].member = mergeMember; + + spqr_node firstNode = SPQR_INVALID_NODE; + spqr_node secondNode = SPQR_INVALID_NODE; + spqr_node thirdNode = SPQR_INVALID_NODE; + SCIP_CALL(createNode(dec, &firstNode)); + SCIP_CALL(createNode(dec, &secondNode)); + SCIP_CALL(createNode(dec, &thirdNode)); + + spqr_arc splitArc = newRow->reducedMembers[smallMember].splitArc; + assert(findArcMemberNoCompression(dec, splitArc) == mergeMember); + SCIP_Bool splitArcReversed = arcIsReversedNonRigid(dec, splitArc); + SCIP_Bool splitHead = newRow->reducedMembers[smallMember].splitHead; + + spqr_node splitArcHead = splitArcReversed ? secondNode : firstNode; + spqr_node splitArcTail = splitArcReversed ? firstNode : secondNode; + spqr_node otherNode = splitHead ? splitArcTail : splitArcHead; + + spqr_arc first_arc = getFirstMemberArc(dec, mergeMember); + spqr_arc arc = first_arc; + + do + { + if( arc != cutRepresentative ) + { + if( arcIsReversedNonRigid(dec, arc)) + { + setArcHeadAndTail(dec, arc, secondNode, firstNode); + } else + { + setArcHeadAndTail(dec, arc, firstNode, secondNode); + } + } else + { + if(( arcIsReversedNonRigid(dec, arc) == splitArcReversed ) == splitHead ) + { + setArcHeadAndTail(dec, arc, thirdNode, otherNode); + } else + { + setArcHeadAndTail(dec, arc, otherNode, thirdNode); + } + } + arcSetReversed(dec, arc, FALSE); + arcSetRepresentative(dec, arc, splitArc); + arc = getNextMemberArc(dec, arc); + } while( arc != first_arc ); + arcSetRepresentative(dec, splitArc, SPQR_INVALID_ARC); + + spqr_member otherMember = newRowInformation->member; + spqr_arc otherMarker = largeIsParent ? markerOfParent(dec, mergeMember) : markerToParent(dec, otherMember); + + assert(nodeIsRepresentative(dec, newRowInformation->tail)); + assert(nodeIsRepresentative(dec, newRowInformation->head)); + spqr_node largeSplitNode = newRow->reducedMembers[smallMember].splitHead ? findEffectiveArcHead(dec, otherMarker) + : findEffectiveArcTail(dec, otherMarker); + + spqr_node largeOtherNode = + largeSplitNode == newRowInformation->head ? newRowInformation->tail : newRowInformation->head; + assert(largeSplitNode == newRowInformation->head || largeSplitNode == newRowInformation->tail); + + newRowInformation->representative = mergeArcSigns(dec, newRowInformation->representative, splitArc, FALSE); + + spqr_member mergedMember = SPQR_INVALID_MEMBER; + spqr_node arcNodeOne; + spqr_node arcNodeTwo; + spqr_node mergeNodeThree; + if( largeIsParent ) + { + SCIP_CALL(mergeSplitMemberIntoParent(dec, newRow, mergeMember, otherMember, otherMarker, splitArc, TRUE, + largeOtherNode, thirdNode, &mergedMember, + &arcNodeOne, + &arcNodeTwo, + &mergeNodeThree)); + } else + { + SCIP_CALL(mergeSplitMemberIntoParent(dec, newRow, otherMember, mergeMember, splitArc, otherMarker, TRUE, + thirdNode, largeOtherNode, &mergedMember, + &arcNodeOne, + &arcNodeTwo, + &mergeNodeThree)); + } + + + newRowInformation->member = mergedMember; + + SCIP_Bool splitIsReferenceHead = newRow->reducedMembers[smallMember].splitHead; + SCIP_Bool splitIsNewRowHead = largeSplitNode == newRowInformation->head; + if( !splitIsReferenceHead && !splitIsNewRowHead ) + { + newRowInformation->head = mergeNodeThree; + newRowInformation->tail = arcNodeOne; + } else if( !splitIsReferenceHead && splitIsNewRowHead ) + { + newRowInformation->head = arcNodeOne; + newRowInformation->tail = mergeNodeThree; + } else if( splitIsReferenceHead && !splitIsNewRowHead ) + { + newRowInformation->head = mergeNodeThree; + newRowInformation->tail = arcNodeTwo; + } else if( splitIsReferenceHead && splitIsNewRowHead ) + { + newRowInformation->head = arcNodeTwo; + newRowInformation->tail = mergeNodeThree; + } + + return SCIP_OKAY; +} + +static SCIP_RETCODE splitAndMergeRigid( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id largeMember, + reduced_member_id smallMember, + SCIP_Bool largeIsParent, + NewRowInformation* const newRowInformation, + spqr_member member +) +{ + + spqr_node newNode = SPQR_INVALID_NODE;//Sink node + SCIP_CALL(createNode(dec, &newNode)); + + spqr_member smallMemberMember = member; + spqr_member largeMemberMember = newRowInformation->member; + + spqr_arc smallMarker = largeIsParent ? markerToParent(dec, smallMemberMember) : markerOfParent(dec, + largeMemberMember); + spqr_arc largeMarker = largeIsParent ? markerOfParent(dec, smallMemberMember) : markerToParent(dec, + largeMemberMember); + + spqr_node splitNode = newRow->reducedMembers[smallMember].splitNode; + spqr_node smallOtherNode = newNode; + + if( newRow->reducedMembers[smallMember].numCutArcs != 0 ) + { + spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode); + spqr_arc iterArc = firstNodeArc; + do + { + spqr_node otherHead = findArcHead(dec, iterArc); + spqr_node otherTail = findArcTail(dec, iterArc); + spqr_node otherEnd = otherHead == splitNode ? otherTail : otherHead; + spqr_arc nextArc = getNextNodeArc(dec, iterArc, splitNode);//Need to do this before we modify the arc + + SCIP_Bool isCut = newRow->isArcCut[iterArc]; + SCIP_Bool isMoveColor = newRow->nodeColors[otherEnd] == COLOR_SOURCE; + SCIP_Bool changeArcEnd = isCut == isMoveColor; + if( changeArcEnd ) + { + if( otherHead == splitNode ) + { + changeArcHead(dec, iterArc, otherHead, newNode); + } else + { + changeArcTail(dec, iterArc, otherTail, newNode); } - arc = getNextMemberArc(dec, arc); - } while (arc != first_arc); - - - updateMemberType(dec, mergeMember, SPQR_MEMBERTYPE_RIGID); - newRowInformation->member = mergeMember; - if(newRow->reducedMembers[leaf].otherIsSource){ - newRowInformation->head = thirdNode; - newRowInformation->tail = splitNode; - }else{ - newRowInformation->head = splitNode; - newRowInformation->tail = thirdNode; - } - - newRowInformation->reversed = FALSE; - newRowInformation->representative = splitArc; - - return SCIP_OKAY; - } - assert(getMemberType(dec,member) == SPQR_MEMBERTYPE_RIGID); - - spqr_node newNode = SPQR_INVALID_NODE; //Sink node - SCIP_CALL(createNode(dec,&newNode)); - - spqr_node splitNode = newRow->reducedMembers[leaf].splitNode; - - spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode); - spqr_arc iterArc = firstNodeArc; - do { - spqr_node otherHead = findArcHead(dec, iterArc); - spqr_node otherTail = findArcTail(dec, iterArc); - spqr_node otherEnd = otherHead == splitNode ? otherTail : otherHead; - spqr_arc nextArc = getNextNodeArc(dec, iterArc, splitNode); //Need to do this before we modify the arc - - SCIP_Bool isCut = newRow->isArcCut[iterArc]; - SCIP_Bool isMoveColor = newRow->nodeColors[otherEnd] == COLOR_SOURCE; - SCIP_Bool changeArcEnd = isCut == isMoveColor; - if (changeArcEnd) { - if (otherHead == splitNode) { - changeArcHead(dec, iterArc, otherHead, newNode); - } else { - changeArcTail(dec, iterArc, otherTail, newNode); + if( iterArc == smallMarker ) + { + smallOtherNode = splitNode; } - } - //Ugly hack to make sure we can iterate neighbourhood whilst changing arc ends. - newRow->nodeColors[otherEnd] = UNCOLORED; - spqr_arc previousArc = iterArc; - iterArc = nextArc; - if (iterArc == firstNodeArc) { + } + newRow->nodeColors[otherEnd] = UNCOLORED; + //Ugly hack to make sure we can iterate neighbourhood whilst changing arc ends. + spqr_arc previousArc = iterArc; + iterArc = nextArc; + if( iterArc == firstNodeArc ) + { break; - } - if (changeArcEnd && previousArc == firstNodeArc) { + } + if( changeArcEnd && previousArc == firstNodeArc ) + { firstNodeArc = iterArc; - } - } while (TRUE); - newRowInformation->head = newNode; - newRowInformation->tail = splitNode; - newRowInformation->member = member; - newRowInformation->reversed = FALSE; - newRowInformation->representative = findArcSign(dec,iterArc).representative; - - return SCIP_OKAY; -} - -static SCIP_RETCODE mergeSplitMemberIntoParent(SCIP_NETWORKDECOMP *dec, - SCIP_NETWORKROWADDITION * newRow, - spqr_member member, - spqr_member parent, - spqr_arc parentToChild, - spqr_arc childToParent, - SCIP_Bool headToHead, - spqr_node parentNode, - spqr_node childNode, - spqr_member * mergedMember, - spqr_node * arcNodeOne, - spqr_node * arcNodeTwo, - spqr_node * thirdNode) { - assert(dec); - assert(SPQRmemberIsValid(member)); - assert(memberIsRepresentative(dec, member)); - assert(SPQRmemberIsValid(parent)); - assert(memberIsRepresentative(dec, parent)); - assert(findMemberParentNoCompression(dec, member) == parent); - assert(markerOfParent(dec, member) == parentToChild); - assert(markerToParent(dec, member) == childToParent); - - removeArcFromMemberArcList(dec, parentToChild, parent); - removeArcFromMemberArcList(dec, childToParent, member); - - spqr_node parentArcNodes[2] = {findEffectiveArcTail(dec, parentToChild), findEffectiveArcHead(dec, parentToChild)}; - spqr_node childArcNodes[2] = {findEffectiveArcTail(dec, childToParent), findEffectiveArcHead(dec, childToParent)}; - - clearArcHeadAndTail(dec, parentToChild); - clearArcHeadAndTail(dec, childToParent); - - spqr_node first = childArcNodes[headToHead ? 0 : 1]; - spqr_node second = childArcNodes[headToHead ? 1 : 0]; - { - spqr_node newNode = mergeNodes(dec, parentArcNodes[0], first); - spqr_node toRemoveFrom = newNode == first ? parentArcNodes[0] : first; - mergeNodeArcList(dec, newNode, toRemoveFrom); - *arcNodeOne = newNode; - newRow->nodeColors[toRemoveFrom] = UNCOLORED; - } - { - spqr_node newNode = mergeNodes(dec, parentArcNodes[1], second); - spqr_node toRemoveFrom = newNode == second ? parentArcNodes[1] : second; - mergeNodeArcList(dec, newNode, toRemoveFrom); - *arcNodeTwo = newNode; - newRow->nodeColors[toRemoveFrom] = UNCOLORED; - } - { - spqr_node newNode = mergeNodes(dec, parentNode, childNode); - spqr_node toRemoveFrom = newNode == childNode ? parentNode : childNode; - mergeNodeArcList(dec,newNode,toRemoveFrom); - *thirdNode = newNode; - newRow->nodeColors[toRemoveFrom] = UNCOLORED; - } - - spqr_member newMember = mergeMembers(dec, member, parent); - spqr_member toRemoveFrom = newMember == member ? parent : member; - mergeMemberArcList(dec, newMember, toRemoveFrom); - if (toRemoveFrom == parent) { - updateMemberParentInformation(dec, newMember, toRemoveFrom); - } - updateMemberType(dec, newMember, SPQR_MEMBERTYPE_RIGID); - *mergedMember = newMember; - return SCIP_OKAY; -} -static SCIP_RETCODE splitAndMergeSeries(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id largeMember, reduced_member_id smallMember, - SCIP_Bool largeIsParent, NewRowInformation * const newRowInformation, - spqr_member member){ - SCIP_Bool isCut = FALSE; - spqr_member mergingMember = SPQR_INVALID_MEMBER; - spqr_arc nonVirtualArc = SPQR_INVALID_ARC; - SCIP_CALL(splitSeriesMergingRowAddition(dec,newRow,smallMember,member,&mergingMember,&isCut,&nonVirtualArc)); - assert(getNumMemberArcs(dec,mergingMember) == 3); - - //create the split series. There's two possible configurations, based on whether it contains a cut edge or not - spqr_node a = SPQR_INVALID_NODE; - spqr_node b = SPQR_INVALID_NODE; - spqr_node c = SPQR_INVALID_NODE; - spqr_node d = SPQR_INVALID_NODE; - SCIP_CALL(createNode(dec,&a)); - SCIP_CALL(createNode(dec,&b)); - SCIP_CALL(createNode(dec,&c)); - SCIP_CALL(createNode(dec,&d)); - - spqr_arc splitArc = newRow->reducedMembers[smallMember].splitArc; - - { - SCIP_Bool splitHead = newRow->reducedMembers[smallMember].splitHead; - spqr_arc firstArc = getFirstMemberArc(dec,mergingMember); - spqr_arc arc = firstArc; - SCIP_Bool splitReversed = arcIsReversedNonRigid(dec,splitArc); - do{ - if(arc == splitArc){ - if(splitHead){ - setArcHeadAndTail(dec,splitArc,b,a); - }else{ - setArcHeadAndTail(dec,splitArc,a,b); - } - }else if (arc == nonVirtualArc){ - if ((arcIsReversedNonRigid(dec, arc) == splitReversed) == splitHead) { - setArcHeadAndTail(dec, arc, a, d); - } else { - setArcHeadAndTail(dec, arc, d, a); - } - }else{ - spqr_node otherNode = cutArcIsValid(newRow->reducedMembers[smallMember].firstCutArc) ? c : b; - if((arcIsReversedNonRigid(dec,arc) == splitReversed) == splitHead){ - setArcHeadAndTail(dec, arc, d, otherNode); - }else{ - setArcHeadAndTail(dec, arc, otherNode, d); - } - } - arcSetReversed(dec,arc,FALSE); - arcSetRepresentative(dec,arc,splitArc); - arc = getNextMemberArc(dec,arc); - }while(arc != firstArc); - arcSetRepresentative(dec,splitArc,SPQR_INVALID_ARC); - } - - spqr_member otherMember = newRowInformation->member; - spqr_arc otherMarker = largeIsParent ? markerOfParent(dec,mergingMember) : markerToParent(dec,otherMember); - - assert(nodeIsRepresentative(dec,newRowInformation->tail)); - assert(nodeIsRepresentative(dec,newRowInformation->head)); - spqr_node splitNode = newRow->reducedMembers[smallMember].splitHead ? findEffectiveArcHead(dec,otherMarker) : - findEffectiveArcTail(dec,otherMarker); - - spqr_node otherNode = splitNode == newRowInformation->head ? newRowInformation->tail : newRowInformation->head; - assert(splitNode == newRowInformation->head || splitNode == newRowInformation->tail); - newRowInformation->representative = mergeArcSigns(dec,newRowInformation->representative,splitArc,FALSE); - - spqr_member mergedMember = SPQR_INVALID_MEMBER; - spqr_node arcNodeOne; - spqr_node arcNodeTwo; - spqr_node thirdNode; - if(largeIsParent){ - SCIP_CALL(mergeSplitMemberIntoParent(dec,newRow,mergingMember,otherMember,otherMarker,splitArc,TRUE, - otherNode,c,&mergedMember, - &arcNodeOne, - &arcNodeTwo, - &thirdNode)); - }else{ - SCIP_CALL(mergeSplitMemberIntoParent(dec,newRow,otherMember,mergingMember, splitArc, otherMarker,TRUE, - c,otherNode,&mergedMember, - &arcNodeOne, - &arcNodeTwo, - &thirdNode)); - } - newRow->reducedMembers[smallMember].member = mergedMember; - - newRowInformation->member = mergedMember; - SCIP_Bool splitIsReferenceHead = newRow->reducedMembers[smallMember].splitHead; - SCIP_Bool splitIsNewRowHead = splitNode == newRowInformation->head; - if(!splitIsReferenceHead && !splitIsNewRowHead){ - newRowInformation->head = thirdNode; - newRowInformation->tail = arcNodeOne; - }else if (!splitIsReferenceHead && splitIsNewRowHead){ - newRowInformation->head = arcNodeOne; - newRowInformation->tail = thirdNode; - }else if(splitIsReferenceHead && !splitIsNewRowHead){ - newRowInformation->head = thirdNode; - newRowInformation->tail = arcNodeTwo; - }else if (splitIsReferenceHead && splitIsNewRowHead){ - newRowInformation->head = arcNodeTwo; - newRowInformation->tail = thirdNode; - } - - return SCIP_OKAY; -} - -static SCIP_RETCODE splitAndMergeParallel(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id largeMember, reduced_member_id smallMember, - SCIP_Bool largeIsParent, NewRowInformation * const newRowInformation, - spqr_member member){ - spqr_member mergeMember = SPQR_INVALID_MEMBER; - spqr_arc cutRepresentative = SPQR_INVALID_ARC; - SCIP_CALL(splitParallelMerging(dec,newRow,smallMember,member,&mergeMember,&cutRepresentative)); - newRow->reducedMembers[smallMember].member = mergeMember; - - spqr_node firstNode = SPQR_INVALID_NODE; - spqr_node secondNode = SPQR_INVALID_NODE; - spqr_node thirdNode = SPQR_INVALID_NODE; - SCIP_CALL(createNode(dec,&firstNode)); - SCIP_CALL(createNode(dec,&secondNode)); - SCIP_CALL(createNode(dec,&thirdNode)); - - spqr_arc splitArc = newRow->reducedMembers[smallMember].splitArc; - assert(findArcMemberNoCompression(dec,splitArc) == mergeMember); - SCIP_Bool splitArcReversed = arcIsReversedNonRigid(dec,splitArc); - SCIP_Bool splitHead = newRow->reducedMembers[smallMember].splitHead; - - spqr_node splitArcHead = splitArcReversed ? secondNode : firstNode; - spqr_node splitArcTail = splitArcReversed ? firstNode : secondNode; - spqr_node otherNode = splitHead ? splitArcTail : splitArcHead; - - spqr_arc first_arc = getFirstMemberArc(dec, mergeMember); - spqr_arc arc = first_arc; - - do { - if(arc != cutRepresentative){ - if(arcIsReversedNonRigid(dec,arc)){ - setArcHeadAndTail(dec,arc,secondNode,firstNode); - }else{ - setArcHeadAndTail(dec,arc,firstNode,secondNode); - } - }else{ - if ((arcIsReversedNonRigid(dec, arc) == splitArcReversed) == splitHead) { - setArcHeadAndTail(dec, arc, thirdNode, otherNode); - } else { - setArcHeadAndTail(dec, arc, otherNode, thirdNode); - } - } - arcSetReversed(dec,arc,FALSE); - arcSetRepresentative(dec,arc,splitArc); - arc = getNextMemberArc(dec, arc); - } while (arc != first_arc); - arcSetRepresentative(dec,splitArc,SPQR_INVALID_ARC); - - spqr_member otherMember = newRowInformation->member; - spqr_arc otherMarker = largeIsParent ? markerOfParent(dec,mergeMember) : markerToParent(dec,otherMember); - - assert(nodeIsRepresentative(dec,newRowInformation->tail)); - assert(nodeIsRepresentative(dec,newRowInformation->head)); - spqr_node largeSplitNode = newRow->reducedMembers[smallMember].splitHead ? findEffectiveArcHead(dec,otherMarker) : - findEffectiveArcTail(dec,otherMarker); - - spqr_node largeOtherNode = largeSplitNode == newRowInformation->head ? newRowInformation->tail : newRowInformation->head; - assert(largeSplitNode == newRowInformation->head || largeSplitNode == newRowInformation->tail); - - newRowInformation->representative = mergeArcSigns(dec,newRowInformation->representative,splitArc,FALSE); - - spqr_member mergedMember = SPQR_INVALID_MEMBER; - spqr_node arcNodeOne; - spqr_node arcNodeTwo; - spqr_node mergeNodeThree; - if(largeIsParent){ - SCIP_CALL(mergeSplitMemberIntoParent(dec,newRow,mergeMember,otherMember,otherMarker,splitArc,TRUE, - largeOtherNode,thirdNode,&mergedMember, - &arcNodeOne, - &arcNodeTwo, - &mergeNodeThree)); - }else{ - SCIP_CALL(mergeSplitMemberIntoParent(dec,newRow,otherMember,mergeMember, splitArc, otherMarker,TRUE, - thirdNode,largeOtherNode,&mergedMember, - &arcNodeOne, - &arcNodeTwo, - &mergeNodeThree)); - } - - - newRowInformation->member = mergedMember; - - SCIP_Bool splitIsReferenceHead = newRow->reducedMembers[smallMember].splitHead; - SCIP_Bool splitIsNewRowHead = largeSplitNode == newRowInformation->head; - if(!splitIsReferenceHead && !splitIsNewRowHead){ - newRowInformation->head = mergeNodeThree; - newRowInformation->tail = arcNodeOne; - }else if (!splitIsReferenceHead && splitIsNewRowHead){ - newRowInformation->head = arcNodeOne; - newRowInformation->tail = mergeNodeThree; - }else if(splitIsReferenceHead && !splitIsNewRowHead){ - newRowInformation->head = mergeNodeThree; - newRowInformation->tail = arcNodeTwo; - }else if (splitIsReferenceHead && splitIsNewRowHead){ - newRowInformation->head = arcNodeTwo; - newRowInformation->tail = mergeNodeThree; - } - - return SCIP_OKAY; -} -static SCIP_RETCODE splitAndMergeRigid(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id largeMember, reduced_member_id smallMember, - SCIP_Bool largeIsParent, NewRowInformation * const newRowInformation, - spqr_member member){ - - spqr_node newNode = SPQR_INVALID_NODE; //Sink node - SCIP_CALL(createNode(dec,&newNode)); - - spqr_member smallMemberMember = member; - spqr_member largeMemberMember = newRowInformation->member; - - spqr_arc smallMarker = largeIsParent ? markerToParent(dec,smallMemberMember) : markerOfParent(dec,largeMemberMember); - spqr_arc largeMarker = largeIsParent ? markerOfParent(dec,smallMemberMember) : markerToParent(dec,largeMemberMember); - - spqr_node splitNode = newRow->reducedMembers[smallMember].splitNode; - spqr_node smallOtherNode = newNode; - - if(newRow->reducedMembers[smallMember].numCutArcs != 0) { - spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode); - spqr_arc iterArc = firstNodeArc; - do { - spqr_node otherHead = findArcHead(dec, iterArc); - spqr_node otherTail = findArcTail(dec, iterArc); - spqr_node otherEnd = otherHead == splitNode ? otherTail : otherHead; - spqr_arc nextArc = getNextNodeArc(dec, iterArc, splitNode); //Need to do this before we modify the arc - - SCIP_Bool isCut = newRow->isArcCut[iterArc]; - SCIP_Bool isMoveColor = newRow->nodeColors[otherEnd] == COLOR_SOURCE; - SCIP_Bool changeArcEnd = isCut == isMoveColor; - if (changeArcEnd) { - if (otherHead == splitNode) { - changeArcHead(dec, iterArc, otherHead, newNode); - } else { - changeArcTail(dec, iterArc, otherTail, newNode); - } - if(iterArc == smallMarker){ - smallOtherNode = splitNode; - } - } - newRow->nodeColors[otherEnd] = UNCOLORED; - //Ugly hack to make sure we can iterate neighbourhood whilst changing arc ends. - spqr_arc previousArc = iterArc; - iterArc = nextArc; - if (iterArc == firstNodeArc) { - break; - } - if (changeArcEnd && previousArc == firstNodeArc) { - firstNodeArc = iterArc; - } - } while (TRUE); - } - - spqr_arc representative = findArcSign(dec,smallMarker).representative; - - newRowInformation->representative = mergeArcSigns(dec,newRowInformation->representative,representative, - newRow->reducedMembers[smallMember].willBeReversed); - - spqr_node largeMarkerHead = findArcHead(dec,largeMarker); - spqr_node largeMarkerTail = findArcTail(dec,largeMarker); - if(findArcSign(dec,largeMarker).reversed){ - spqr_node temp = largeMarkerHead; - largeMarkerHead = largeMarkerTail; - largeMarkerTail = temp; - } - assert(newRowInformation->head == largeMarkerHead || newRowInformation->head == largeMarkerTail || - newRowInformation->tail == largeMarkerHead || newRowInformation->tail == largeMarkerTail); - spqr_node largeOtherNode = (newRowInformation->head == largeMarkerHead || newRowInformation->head == largeMarkerTail) - ? newRowInformation->tail : newRowInformation->head; - - spqr_member mergedMember = SPQR_INVALID_MEMBER; - spqr_node arcNodeOne; - spqr_node arcNodeTwo; - spqr_node mergeNodeThree; - if(largeIsParent){ - SCIP_CALL(mergeSplitMemberIntoParent(dec,newRow,smallMemberMember,largeMemberMember,largeMarker,smallMarker,TRUE, - largeOtherNode,smallOtherNode,&mergedMember, - &arcNodeOne, - &arcNodeTwo, - &mergeNodeThree)); - }else{ - SCIP_CALL(mergeSplitMemberIntoParent(dec,newRow,largeMemberMember,smallMemberMember,smallMarker,largeMarker,TRUE, - smallOtherNode,largeOtherNode,&mergedMember, - &arcNodeOne, - &arcNodeTwo, - &mergeNodeThree)); - } - newRowInformation->member = mergedMember; - - SCIP_Bool otherIsHead = largeOtherNode == newRowInformation->head; - SCIP_Bool adjacentToMarkerHead = (newRowInformation->tail == largeMarkerHead || newRowInformation->head == largeMarkerHead); - if(adjacentToMarkerHead){ - if(otherIsHead){ - newRowInformation->head = mergeNodeThree; - newRowInformation->tail = arcNodeTwo; - }else{ - newRowInformation->head = arcNodeTwo; - newRowInformation->tail = mergeNodeThree; - } - }else{ - if(otherIsHead){ - newRowInformation->head = mergeNodeThree; - newRowInformation->tail = arcNodeOne; - }else{ - newRowInformation->head = arcNodeOne; - newRowInformation->tail = mergeNodeThree; - } - } - - return SCIP_OKAY; -} -static SCIP_RETCODE splitAndMerge(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id largeMember, reduced_member_id smallMember, - SCIP_Bool largeIsParent, NewRowInformation * const newRowInformation){ - spqr_member member = newRow->reducedMembers[smallMember].member; - switch(getMemberType(dec,member)){ - case SPQR_MEMBERTYPE_RIGID:{ - SCIP_CALL(splitAndMergeRigid(dec,newRow,largeMember,smallMember,largeIsParent,newRowInformation,member)); + } + } while( TRUE ); + } + + spqr_arc representative = findArcSign(dec, smallMarker).representative; + + newRowInformation->representative = mergeArcSigns(dec, newRowInformation->representative, representative, + newRow->reducedMembers[smallMember].willBeReversed); + + spqr_node largeMarkerHead = findArcHead(dec, largeMarker); + spqr_node largeMarkerTail = findArcTail(dec, largeMarker); + if( findArcSign(dec, largeMarker).reversed ) + { + spqr_node temp = largeMarkerHead; + largeMarkerHead = largeMarkerTail; + largeMarkerTail = temp; + } + assert(newRowInformation->head == largeMarkerHead || newRowInformation->head == largeMarkerTail || + newRowInformation->tail == largeMarkerHead || newRowInformation->tail == largeMarkerTail); + spqr_node largeOtherNode = ( newRowInformation->head == largeMarkerHead || + newRowInformation->head == largeMarkerTail ) + ? newRowInformation->tail + : newRowInformation->head; + + spqr_member mergedMember = SPQR_INVALID_MEMBER; + spqr_node arcNodeOne; + spqr_node arcNodeTwo; + spqr_node mergeNodeThree; + if( largeIsParent ) + { + SCIP_CALL( + mergeSplitMemberIntoParent(dec, newRow, smallMemberMember, largeMemberMember, largeMarker, smallMarker, TRUE, + largeOtherNode, smallOtherNode, &mergedMember, + &arcNodeOne, + &arcNodeTwo, + &mergeNodeThree)); + } else + { + SCIP_CALL( + mergeSplitMemberIntoParent(dec, newRow, largeMemberMember, smallMemberMember, smallMarker, largeMarker, TRUE, + smallOtherNode, largeOtherNode, &mergedMember, + &arcNodeOne, + &arcNodeTwo, + &mergeNodeThree)); + } + newRowInformation->member = mergedMember; + + SCIP_Bool otherIsHead = largeOtherNode == newRowInformation->head; + SCIP_Bool adjacentToMarkerHead = ( newRowInformation->tail == largeMarkerHead || + newRowInformation->head == largeMarkerHead ); + if( adjacentToMarkerHead ) + { + if( otherIsHead ) + { + newRowInformation->head = mergeNodeThree; + newRowInformation->tail = arcNodeTwo; + } else + { + newRowInformation->head = arcNodeTwo; + newRowInformation->tail = mergeNodeThree; + } + } else + { + if( otherIsHead ) + { + newRowInformation->head = mergeNodeThree; + newRowInformation->tail = arcNodeOne; + } else + { + newRowInformation->head = arcNodeOne; + newRowInformation->tail = mergeNodeThree; + } + } + + return SCIP_OKAY; +} + +static SCIP_RETCODE splitAndMerge( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id largeMember, + reduced_member_id smallMember, + SCIP_Bool largeIsParent, + NewRowInformation* const newRowInformation +) +{ + spqr_member member = newRow->reducedMembers[smallMember].member; + switch( getMemberType(dec, member)) + { + case SPQR_MEMBERTYPE_RIGID: + { + SCIP_CALL(splitAndMergeRigid(dec, newRow, largeMember, smallMember, largeIsParent, newRowInformation, member)); + break; + } + case SPQR_MEMBERTYPE_PARALLEL: + { + SCIP_CALL( + splitAndMergeParallel(dec, newRow, largeMember, smallMember, largeIsParent, newRowInformation, member)); + break; + } + case SPQR_MEMBERTYPE_SERIES: + { + SCIP_CALL( + splitAndMergeSeries(dec, newRow, largeMember, smallMember, largeIsParent, newRowInformation, member)); + break; + } + default: + assert(FALSE); + newRow->remainsNetwork = FALSE; + } + return SCIP_OKAY; +} + +static SCIP_RETCODE mergeChildrenNodes( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id parent, + reduced_member_id node, + reduced_member_id skipNode, + NewRowInformation* const newRowInformation +) +{ + if( node == skipNode || newRow->reducedMembers[node].type == TYPE_PROPAGATED ) + { + return SCIP_OKAY; + } + //check merging + splitAndMerge(dec, newRow, parent, node, TRUE, newRowInformation); + + //merge all children + for( int i = 0; i < newRow->reducedMembers[node].numChildren; ++i ) + { + children_idx idx = newRow->reducedMembers[node].firstChild + i; + reduced_member_id child = newRow->childrenStorage[idx]; + SCIP_CALL(mergeChildrenNodes(dec, newRow, node, child, skipNode, newRowInformation)); + } + return SCIP_OKAY; +} + +static SCIP_RETCODE mergeTree( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + reduced_member_id root, + NewRowInformation* const newRowInformation +) +{ + + //We use the same ordering as when finding + //go to a leaf. We need to start in a leaf to avoid the ambiguity of choosing an orientation + //in members which have no cut arcs; otherwise, we might choose the wrong one + reduced_member_id leaf = root; + while( newRow->reducedMembers[leaf].numChildren != newRow->reducedMembers[leaf].numPropagatedChildren ) + { + for( int i = 0; i < newRow->reducedMembers[leaf].numChildren; ++i ) + { + children_idx idx = newRow->reducedMembers[leaf].firstChild + i; + reduced_member_id child = newRow->childrenStorage[idx]; + if( newRow->reducedMembers[child].type != TYPE_PROPAGATED ) + { + leaf = child; break; - } - case SPQR_MEMBERTYPE_PARALLEL:{ - SCIP_CALL(splitAndMergeParallel(dec,newRow,largeMember,smallMember,largeIsParent,newRowInformation,member)); + } + } + } + SCIP_CALL(splitFirstLeaf(dec, newRow, leaf, newRowInformation)); + + reduced_member_id baseNode = leaf; + reduced_member_id nextNode = newRow->reducedMembers[baseNode].parent; + + while( reducedMemberIsValid(nextNode)) + { + //check this node + SCIP_CALL(splitAndMerge(dec, newRow, baseNode, nextNode, FALSE, newRowInformation)); + + //Add other nodes in the subtree + for( int i = 0; i < newRow->reducedMembers[nextNode].numChildren; ++i ) + { + children_idx idx = newRow->reducedMembers[nextNode].firstChild + i; + reduced_member_id child = newRow->childrenStorage[idx]; + SCIP_CALL(mergeChildrenNodes(dec, newRow, nextNode, child, baseNode, newRowInformation)); + } + + //Move up one layer + baseNode = nextNode; + nextNode = newRow->reducedMembers[nextNode].parent; + } + + return SCIP_OKAY; +} + +static SCIP_RETCODE transformComponentRowAddition( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* newRow, + SPQRRowReducedComponent* component, + NewRowInformation* const newRowInformation +) +{ + assert(component); + if( newRow->reducedMembers[component->root].numChildren == + newRow->reducedMembers[component->root].numPropagatedChildren ) + { + //No merging necessary, only a single component + reduced_member_id reducedMember = component->root; + assert(reducedMemberIsValid(reducedMember)); + spqr_member member = newRow->reducedMembers[reducedMember].member; + SPQRMemberType type = getMemberType(dec, member); + + switch( type ) + { + case SPQR_MEMBERTYPE_RIGID: + SCIP_CALL(transformSingleRigid(dec, newRow, reducedMember, member, newRowInformation)); break; - } - case SPQR_MEMBERTYPE_SERIES:{ - SCIP_CALL(splitAndMergeSeries(dec,newRow,largeMember,smallMember,largeIsParent,newRowInformation,member)); + case SPQR_MEMBERTYPE_PARALLEL: + { + SCIP_CALL(transformSingleParallel(dec, newRow, reducedMember, member, newRowInformation)); break; - } - default: - assert(FALSE); - newRow->remainsNetwork = FALSE; - } - return SCIP_OKAY; -} -static SCIP_RETCODE mergeChildrenNodes(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - reduced_member_id parent, reduced_member_id node, - reduced_member_id skipNode, NewRowInformation * const newRowInformation -){ - if(node == skipNode || newRow->reducedMembers[node].type == TYPE_PROPAGATED){ - return SCIP_OKAY; - } - //check merging - splitAndMerge(dec,newRow,parent,node,TRUE,newRowInformation); - - //merge all children - for (int i = 0; i < newRow->reducedMembers[node].numChildren; ++i) { - children_idx idx = newRow->reducedMembers[node].firstChild + i; - reduced_member_id child = newRow->childrenStorage[idx]; - SCIP_CALL(mergeChildrenNodes(dec,newRow,node,child,skipNode,newRowInformation)); - } - return SCIP_OKAY; -} -static SCIP_RETCODE mergeTree(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, reduced_member_id root, - NewRowInformation * const newRowInformation){ - - //We use the same ordering as when finding - //go to a leaf. We need to start in a leaf to avoid the ambiguity of choosing an orientation - //in members which have no cut arcs; otherwise, we might choose the wrong one - reduced_member_id leaf = root; - while(newRow->reducedMembers[leaf].numChildren != newRow->reducedMembers[leaf].numPropagatedChildren){ - for(int i = 0; i < newRow->reducedMembers[leaf].numChildren;++i){ - children_idx idx = newRow->reducedMembers[leaf].firstChild + i; - reduced_member_id child = newRow->childrenStorage[idx]; - if(newRow->reducedMembers[child].type != TYPE_PROPAGATED){ - leaf = child; - break; - } - } - } - SCIP_CALL(splitFirstLeaf(dec,newRow,leaf,newRowInformation)); - - reduced_member_id baseNode = leaf; - reduced_member_id nextNode = newRow->reducedMembers[baseNode].parent; - - while(reducedMemberIsValid(nextNode)){ - //check this node - SCIP_CALL(splitAndMerge(dec,newRow,baseNode,nextNode,FALSE,newRowInformation)); - - //Add other nodes in the subtree - for (int i = 0; i < newRow->reducedMembers[nextNode].numChildren; ++i) { - children_idx idx = newRow->reducedMembers[nextNode].firstChild + i; - reduced_member_id child = newRow->childrenStorage[idx]; - SCIP_CALL(mergeChildrenNodes(dec,newRow,nextNode,child,baseNode,newRowInformation)); - } - - //Move up one layer - baseNode = nextNode; - nextNode = newRow->reducedMembers[nextNode].parent; - } - - return SCIP_OKAY; -} -static SCIP_RETCODE transformComponentRowAddition(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow, - SPQRRowReducedComponent* component, NewRowInformation * const newRowInformation){ - assert(component); - if(newRow->reducedMembers[component->root].numChildren == newRow->reducedMembers[component->root].numPropagatedChildren){ - //No merging necessary, only a single component - reduced_member_id reducedMember = component->root; - assert(reducedMemberIsValid(reducedMember)); - spqr_member member = newRow->reducedMembers[reducedMember].member; - SPQRMemberType type = getMemberType(dec, member); - - switch(type){ - case SPQR_MEMBERTYPE_RIGID: - SCIP_CALL(transformSingleRigid(dec,newRow,reducedMember,member,newRowInformation)); - break; - case SPQR_MEMBERTYPE_PARALLEL: { - SCIP_CALL(transformSingleParallel(dec,newRow,reducedMember,member,newRowInformation)); - break; - } - case SPQR_MEMBERTYPE_LOOP: - case SPQR_MEMBERTYPE_SERIES: { - - newRowInformation->member = member; - cut_arc_id cutArc = newRow->reducedMembers[reducedMember].firstCutArc; - spqr_arc arc = newRow->cutArcs[cutArc].arc; - newRowInformation->reversed = arcIsReversedNonRigid(dec,arc) == newRow->cutArcs[cutArc].arcReversed; - if(type == SPQR_MEMBERTYPE_LOOP ){ - if(getNumMemberArcs(dec,member) == 2){ - changeLoopToSeries(dec,member); - }else{ - - } - } - break; + } + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_SERIES: + { + + newRowInformation->member = member; + cut_arc_id cutArc = newRow->reducedMembers[reducedMember].firstCutArc; + spqr_arc arc = newRow->cutArcs[cutArc].arc; + newRowInformation->reversed = arcIsReversedNonRigid(dec, arc) == newRow->cutArcs[cutArc].arcReversed; + if( type == SPQR_MEMBERTYPE_LOOP ) + { + if( getNumMemberArcs(dec, member) == 2 ) + { + changeLoopToSeries(dec, member); + } else + { + } } - default: - assert(FALSE); - break; - } + break; + } + default: + assert(FALSE); + break; + } - return SCIP_OKAY; - } + return SCIP_OKAY; + } - SCIP_CALL(mergeTree(dec,newRow,component->root,newRowInformation)); + SCIP_CALL(mergeTree(dec, newRow, component->root, newRowInformation)); - return SCIP_OKAY; + return SCIP_OKAY; } -SCIP_RETCODE SCIPNetworkRowAdditionCreate(SCIP* env, SCIP_NETWORKROWADDITION** pNewRow ){ - assert(env); - SCIP_CALL(SCIPallocBlockMemory(env,pNewRow)); - SCIP_NETWORKROWADDITION * newRow = *pNewRow; +SCIP_RETCODE SCIPnetrowaddCreate( + SCIP* scip, + SCIP_NETROWADD** prowadd +) +{ + assert(scip); + SCIP_CALL(SCIPallocBlockMemory(scip, prowadd)); + SCIP_NETROWADD* newRow = *prowadd; + + newRow->remainsNetwork = TRUE; - newRow->remainsNetwork = TRUE; + newRow->reducedMembers = NULL; + newRow->memReducedMembers = 0; + newRow->numReducedMembers = 0; - newRow->reducedMembers = NULL; - newRow->memReducedMembers = 0; - newRow->numReducedMembers = 0; + newRow->reducedComponents = NULL; + newRow->memReducedComponents = 0; + newRow->numReducedComponents = 0; - newRow->reducedComponents = NULL; - newRow->memReducedComponents = 0; - newRow->numReducedComponents = 0; + newRow->memberInformation = NULL; + newRow->memMemberInformation = 0; + newRow->numMemberInformation = 0; - newRow->memberInformation = NULL; - newRow->memMemberInformation = 0; - newRow->numMemberInformation = 0; + newRow->cutArcs = NULL; + newRow->memCutArcs = 0; + newRow->numCutArcs = 0; + newRow->firstOverallCutArc = INVALID_CUT_ARC; - newRow->cutArcs = NULL; - newRow->memCutArcs = 0; - newRow->numCutArcs = 0; - newRow->firstOverallCutArc = INVALID_CUT_ARC; + newRow->childrenStorage = NULL; + newRow->memChildrenStorage = 0; + newRow->numChildrenStorage = 0; - newRow->childrenStorage = NULL; - newRow->memChildrenStorage = 0; - newRow->numChildrenStorage = 0; + newRow->newRowIndex = SPQR_INVALID_ROW; - newRow->newRowIndex = SPQR_INVALID_ROW; + newRow->newColumnArcs = NULL; + newRow->newColumnReversed = NULL; + newRow->memColumnArcs = 0; + newRow->numColumnArcs = 0; - newRow->newColumnArcs = NULL; - newRow->newColumnReversed = NULL; - newRow->memColumnArcs = 0; - newRow->numColumnArcs = 0; + newRow->leafMembers = NULL; + newRow->numLeafMembers = 0; + newRow->memLeafMembers = 0; - newRow->leafMembers = NULL; - newRow->numLeafMembers = 0; - newRow->memLeafMembers = 0; + newRow->decompositionColumnArcs = NULL; + newRow->decompositionColumnArcReversed = NULL; + newRow->memDecompositionColumnArcs = 0; + newRow->numDecompositionColumnArcs = 0; - newRow->decompositionColumnArcs = NULL; - newRow->decompositionColumnArcReversed = NULL; - newRow->memDecompositionColumnArcs = 0; - newRow->numDecompositionColumnArcs = 0; - - newRow->isArcCut = NULL; - newRow->isArcCutReversed = NULL; - newRow->memIsArcCut = 0; - newRow->numIsArcCut = 0; - - newRow->nodeColors = NULL; - newRow->memNodeColors = 0; - - newRow->articulationNodes = NULL; - newRow->memArticulationNodes = 0; - newRow->numArticulationNodes = 0; - - newRow->articulationNodeSearchInfo = NULL; - newRow->memNodeSearchInfo = 0; - - newRow->crossingPathCount = NULL; - newRow->memCrossingPathCount = 0; - - newRow->intersectionDFSData = NULL; - newRow->memIntersectionDFSData = 0; - - newRow->colorDFSData = NULL; - newRow->memColorDFSData = 0; - - newRow->artDFSData = NULL; - newRow->memArtDFSData = 0; - - newRow->createReducedMembersCallstack = NULL; - newRow->memCreateReducedMembersCallstack = 0; - - newRow->intersectionPathDepth = NULL; - newRow->memIntersectionPathDepth = 0; - - newRow->intersectionPathParent = NULL; - newRow->memIntersectionPathParent = 0; - - newRow->mergeTreeCallData = NULL; - newRow->memMergeTreeCallData = 0; - - return SCIP_OKAY; -} - -void SCIPNetworkRowAdditionFree(SCIP* env, SCIP_NETWORKROWADDITION ** pNewRow){ - assert(*pNewRow); - - SCIP_NETWORKROWADDITION * newRow = *pNewRow; + newRow->isArcCut = NULL; + newRow->isArcCutReversed = NULL; + newRow->memIsArcCut = 0; + newRow->numIsArcCut = 0; - SCIPfreeBlockMemoryArray(env,&newRow->createReducedMembersCallstack,newRow->memCreateReducedMembersCallstack); - SCIPfreeBlockMemoryArray(env,&newRow->artDFSData,newRow->memArtDFSData); - SCIPfreeBlockMemoryArray(env,&newRow->colorDFSData,newRow->memColorDFSData); - SCIPfreeBlockMemoryArray(env,&newRow->mergeTreeCallData,newRow->memMergeTreeCallData); - SCIPfreeBlockMemoryArray(env,&newRow->intersectionDFSData,newRow->memIntersectionDFSData); - SCIPfreeBlockMemoryArray(env,&newRow->intersectionPathParent,newRow->memIntersectionPathParent); - SCIPfreeBlockMemoryArray(env,&newRow->intersectionPathDepth,newRow->memIntersectionPathDepth); - SCIPfreeBlockMemoryArray(env,&newRow->crossingPathCount,newRow->memCrossingPathCount); - SCIPfreeBlockMemoryArray(env,&newRow->articulationNodeSearchInfo,newRow->memNodeSearchInfo); - SCIPfreeBlockMemoryArray(env,&newRow->articulationNodes,newRow->memArticulationNodes); - SCIPfreeBlockMemoryArray(env,&newRow->nodeColors,newRow->memNodeColors); - SCIPfreeBlockMemoryArray(env,&newRow->isArcCut,newRow->memIsArcCut); - SCIPfreeBlockMemoryArray(env,&newRow->isArcCutReversed,newRow->memIsArcCut); - SCIPfreeBlockMemoryArray(env,&newRow->decompositionColumnArcs,newRow->memDecompositionColumnArcs); - SCIPfreeBlockMemoryArray(env,&newRow->decompositionColumnArcReversed,newRow->memDecompositionColumnArcs); - SCIPfreeBlockMemoryArray(env,&newRow->newColumnArcs,newRow->memColumnArcs); - SCIPfreeBlockMemoryArray(env,&newRow->newColumnReversed,newRow->memColumnArcs); - SCIPfreeBlockMemoryArray(env,&newRow->childrenStorage,newRow->memChildrenStorage); - SCIPfreeBlockMemoryArray(env,&newRow->cutArcs,newRow->memCutArcs); - SCIPfreeBlockMemoryArray(env,&newRow->memberInformation,newRow->memMemberInformation); - SCIPfreeBlockMemoryArray(env,&newRow->reducedComponents,newRow->memReducedComponents); - SCIPfreeBlockMemoryArray(env,&newRow->reducedMembers,newRow->memReducedMembers); - SCIPfreeBlockMemoryArray(env,&newRow->leafMembers, newRow->memLeafMembers); - SCIPfreeBlockMemory(env,pNewRow); -} - -SCIP_RETCODE SCIPNetworkRowAdditionCheck(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKROWADDITION * newRow, - const spqr_row row, const spqr_col * columns, const double * columnValues, - size_t numColumns){ - assert(dec); - assert(newRow); - assert(numColumns == 0 || columns ); - - newRow->remainsNetwork = TRUE; - cleanUpPreviousIteration(dec,newRow); - - SCIP_CALL(newRowUpdateRowInformation(dec,newRow,row,columns,columnValues,numColumns)); - SCIP_CALL(constructRowReducedDecomposition(dec,newRow)); - SCIP_CALL(createReducedDecompositionCutArcs(dec,newRow)); - - SCIP_CALL(determineLeafReducedMembers(dec,newRow)); - SCIP_CALL(allocateRigidSearchMemory(dec,newRow)); - SCIP_CALL(allocateTreeSearchMemory(dec,newRow)); - //Check for each component if the cut arcs propagate through a row tree marker to a cut arc in another component - //From the leafs inward. - propagateComponents(dec,newRow); - //It can happen that we are not graphic by some of the checked components. - //In that case, further checking may lead to errors as some invariants that the code assumes will be broken. - if(newRow->remainsNetwork){ - for (int i = 0; i < newRow->numReducedComponents; ++i) { - determineMergeableTypes(dec,newRow,newRow->reducedComponents[i].root); - //exit early if one is not graphic - if(!newRow->remainsNetwork){ - break; - } - } - } - - cleanUpRowMemberInformation(newRow); - - return SCIP_OKAY; -} - -SCIP_RETCODE SCIPNetworkRowAdditionAdd(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow){ - assert(newRow->remainsNetwork); - if(newRow->numReducedComponents == 0){ - spqr_member newMember = SPQR_INVALID_MEMBER; - SCIP_CALL(createStandaloneParallel(dec,newRow->newColumnArcs, newRow->newColumnReversed, - newRow->numColumnArcs,newRow->newRowIndex,&newMember)); - }else if (newRow->numReducedComponents == 1){ - NewRowInformation information = emptyNewRowInformation(); - SCIP_CALL(transformComponentRowAddition(dec,newRow,&newRow->reducedComponents[0],&information)); - - if(newRow->numColumnArcs == 0){ - spqr_arc rowArc = SPQR_INVALID_ARC; - SCIP_CALL(createRowArc(dec,information.member,&rowArc,newRow->newRowIndex,information.reversed)); - if(SPQRnodeIsValid(information.head)){ - assert(SPQRnodeIsValid(information.tail)); - assert(SPQRarcIsValid(information.representative)); - setArcHeadAndTail(dec,rowArc,findNode(dec,information.head),findNode(dec,information.tail)); - arcSetRepresentative(dec,rowArc,information.representative); - arcSetReversed(dec,rowArc,information.reversed != arcIsReversedNonRigid(dec,information.representative)); - } - }else{ - spqr_member new_row_parallel = SPQR_INVALID_MEMBER; - SCIP_CALL(createConnectedParallel(dec,newRow->newColumnArcs,newRow->newColumnReversed,newRow->numColumnArcs, - newRow->newRowIndex,&new_row_parallel)); - spqr_arc markerArc = SPQR_INVALID_ARC; - spqr_arc ignore = SPQR_INVALID_ARC; - SCIP_CALL(createMarkerPairWithReferences(dec,information.member,new_row_parallel,TRUE, - information.reversed,FALSE, - &markerArc,&ignore)); - if(SPQRnodeIsValid(information.head)){ - assert(SPQRnodeIsValid(information.tail)); - assert(SPQRarcIsValid(information.representative)); - setArcHeadAndTail(dec,markerArc,findNode(dec,information.head),findNode(dec,information.tail)); - arcSetRepresentative(dec,markerArc,information.representative); - arcSetReversed(dec,markerArc,information.reversed != arcIsReversedNonRigid(dec,information.representative)); - } - } - if(getMemberType(dec,information.member) == SPQR_MEMBERTYPE_LOOP){ - assert(getNumMemberArcs(dec,information.member) == 2 || getNumMemberArcs(dec,information.member) == 3); - if(getNumMemberArcs(dec,information.member) == 3){ - changeLoopToSeries(dec,information.member); - } - } - }else{ + newRow->nodeColors = NULL; + newRow->memNodeColors = 0; + + newRow->articulationNodes = NULL; + newRow->memArticulationNodes = 0; + newRow->numArticulationNodes = 0; + + newRow->articulationNodeSearchInfo = NULL; + newRow->memNodeSearchInfo = 0; + + newRow->crossingPathCount = NULL; + newRow->memCrossingPathCount = 0; + + newRow->intersectionDFSData = NULL; + newRow->memIntersectionDFSData = 0; + + newRow->colorDFSData = NULL; + newRow->memColorDFSData = 0; + + newRow->artDFSData = NULL; + newRow->memArtDFSData = 0; + + newRow->createReducedMembersCallstack = NULL; + newRow->memCreateReducedMembersCallstack = 0; + + newRow->intersectionPathDepth = NULL; + newRow->memIntersectionPathDepth = 0; + + newRow->intersectionPathParent = NULL; + newRow->memIntersectionPathParent = 0; + + newRow->mergeTreeCallData = NULL; + newRow->memMergeTreeCallData = 0; + + return SCIP_OKAY; +} + +void SCIPnetrowaddFree( + SCIP* scip, + SCIP_NETROWADD** prowadd +) +{ + assert(*prowadd); + + SCIP_NETROWADD* newRow = *prowadd; + + SCIPfreeBlockMemoryArray(scip, &newRow->createReducedMembersCallstack, newRow->memCreateReducedMembersCallstack); + SCIPfreeBlockMemoryArray(scip, &newRow->artDFSData, newRow->memArtDFSData); + SCIPfreeBlockMemoryArray(scip, &newRow->colorDFSData, newRow->memColorDFSData); + SCIPfreeBlockMemoryArray(scip, &newRow->mergeTreeCallData, newRow->memMergeTreeCallData); + SCIPfreeBlockMemoryArray(scip, &newRow->intersectionDFSData, newRow->memIntersectionDFSData); + SCIPfreeBlockMemoryArray(scip, &newRow->intersectionPathParent, newRow->memIntersectionPathParent); + SCIPfreeBlockMemoryArray(scip, &newRow->intersectionPathDepth, newRow->memIntersectionPathDepth); + SCIPfreeBlockMemoryArray(scip, &newRow->crossingPathCount, newRow->memCrossingPathCount); + SCIPfreeBlockMemoryArray(scip, &newRow->articulationNodeSearchInfo, newRow->memNodeSearchInfo); + SCIPfreeBlockMemoryArray(scip, &newRow->articulationNodes, newRow->memArticulationNodes); + SCIPfreeBlockMemoryArray(scip, &newRow->nodeColors, newRow->memNodeColors); + SCIPfreeBlockMemoryArray(scip, &newRow->isArcCut, newRow->memIsArcCut); + SCIPfreeBlockMemoryArray(scip, &newRow->isArcCutReversed, newRow->memIsArcCut); + SCIPfreeBlockMemoryArray(scip, &newRow->decompositionColumnArcs, newRow->memDecompositionColumnArcs); + SCIPfreeBlockMemoryArray(scip, &newRow->decompositionColumnArcReversed, newRow->memDecompositionColumnArcs); + SCIPfreeBlockMemoryArray(scip, &newRow->newColumnArcs, newRow->memColumnArcs); + SCIPfreeBlockMemoryArray(scip, &newRow->newColumnReversed, newRow->memColumnArcs); + SCIPfreeBlockMemoryArray(scip, &newRow->childrenStorage, newRow->memChildrenStorage); + SCIPfreeBlockMemoryArray(scip, &newRow->cutArcs, newRow->memCutArcs); + SCIPfreeBlockMemoryArray(scip, &newRow->memberInformation, newRow->memMemberInformation); + SCIPfreeBlockMemoryArray(scip, &newRow->reducedComponents, newRow->memReducedComponents); + SCIPfreeBlockMemoryArray(scip, &newRow->reducedMembers, newRow->memReducedMembers); + SCIPfreeBlockMemoryArray(scip, &newRow->leafMembers, newRow->memLeafMembers); + SCIPfreeBlockMemory(scip, prowadd); +} + +SCIP_RETCODE SCIPnetrowaddCheck( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* rowadd, + int row, + const int* nonzcols, + const double* nonzvals, + size_t nnonzs +) +{ + assert(dec); + assert(rowadd); + assert(nnonzs == 0 || nonzcols); + + rowadd->remainsNetwork = TRUE; + cleanUpPreviousIteration(dec, rowadd); + + SCIP_CALL(newRowUpdateRowInformation(dec, rowadd, row, nonzcols, nonzvals, nnonzs)); + SCIP_CALL(constructRowReducedDecomposition(dec, rowadd)); + SCIP_CALL(createReducedDecompositionCutArcs(dec, rowadd)); + + SCIP_CALL(determineLeafReducedMembers(dec, rowadd)); + SCIP_CALL(allocateRigidSearchMemory(dec, rowadd)); + SCIP_CALL(allocateTreeSearchMemory(dec, rowadd)); + //Check for each component if the cut arcs propagate through a row tree marker to a cut arc in another component + //From the leafs inward. + propagateComponents(dec, rowadd); + //It can happen that we are not graphic by some of the checked components. + //In that case, further checking may lead to errors as some invariants that the code assumes will be broken. + if( rowadd->remainsNetwork ) + { + for( int i = 0; i < rowadd->numReducedComponents; ++i ) + { + determineMergeableTypes(dec, rowadd, rowadd->reducedComponents[i].root); + //exit early if one is not graphic + if( !rowadd->remainsNetwork ) + { + break; + } + } + } + + cleanUpRowMemberInformation(rowadd); + + return SCIP_OKAY; +} + +SCIP_RETCODE SCIPnetrowaddAdd( + SCIP_NETMATDEC* dec, + SCIP_NETROWADD* rowadd +) +{ + assert(rowadd->remainsNetwork); + if( rowadd->numReducedComponents == 0 ) + { + spqr_member newMember = SPQR_INVALID_MEMBER; + SCIP_CALL(createStandaloneParallel(dec, rowadd->newColumnArcs, rowadd->newColumnReversed, + rowadd->numColumnArcs, rowadd->newRowIndex, &newMember)); + } else if( rowadd->numReducedComponents == 1 ) + { + NewRowInformation information = emptyNewRowInformation(); + SCIP_CALL(transformComponentRowAddition(dec, rowadd, &rowadd->reducedComponents[0], &information)); + + if( rowadd->numColumnArcs == 0 ) + { + spqr_arc rowArc = SPQR_INVALID_ARC; + SCIP_CALL(createRowArc(dec, information.member, &rowArc, rowadd->newRowIndex, information.reversed)); + if( SPQRnodeIsValid(information.head)) + { + assert(SPQRnodeIsValid(information.tail)); + assert(SPQRarcIsValid(information.representative)); + setArcHeadAndTail(dec, rowArc, findNode(dec, information.head), findNode(dec, information.tail)); + arcSetRepresentative(dec, rowArc, information.representative); + arcSetReversed(dec, rowArc, information.reversed != arcIsReversedNonRigid(dec, information.representative)); + } + } else + { + spqr_member new_row_parallel = SPQR_INVALID_MEMBER; + SCIP_CALL(createConnectedParallel(dec, rowadd->newColumnArcs, rowadd->newColumnReversed, rowadd->numColumnArcs, + rowadd->newRowIndex, &new_row_parallel)); + spqr_arc markerArc = SPQR_INVALID_ARC; + spqr_arc ignore = SPQR_INVALID_ARC; + SCIP_CALL(createMarkerPairWithReferences(dec, information.member, new_row_parallel, TRUE, + information.reversed, FALSE, + &markerArc, &ignore)); + if( SPQRnodeIsValid(information.head)) + { + assert(SPQRnodeIsValid(information.tail)); + assert(SPQRarcIsValid(information.representative)); + setArcHeadAndTail(dec, markerArc, findNode(dec, information.head), findNode(dec, information.tail)); + arcSetRepresentative(dec, markerArc, information.representative); + arcSetReversed(dec, markerArc, + information.reversed != arcIsReversedNonRigid(dec, information.representative)); + } + } + if( getMemberType(dec, information.member) == SPQR_MEMBERTYPE_LOOP ) + { + assert(getNumMemberArcs(dec, information.member) == 2 || getNumMemberArcs(dec, information.member) == 3); + if( getNumMemberArcs(dec, information.member) == 3 ) + { + changeLoopToSeries(dec, information.member); + } + } + } else + { #ifndef NDEBUG - int numDecComponentsBefore = numConnectedComponents(dec); + int numDecComponentsBefore = numConnectedComponents(dec); #endif - spqr_member new_row_parallel = SPQR_INVALID_MEMBER; - SCIP_CALL(createConnectedParallel(dec,newRow->newColumnArcs,newRow->newColumnReversed,newRow->numColumnArcs, - newRow->newRowIndex,&new_row_parallel)); - for (int i = 0; i < newRow->numReducedComponents; ++i) { - NewRowInformation information = emptyNewRowInformation(); - - SCIP_CALL(transformComponentRowAddition(dec,newRow,&newRow->reducedComponents[i],&information)); - if(getMemberType(dec,information.member) == SPQR_MEMBERTYPE_LOOP){ - assert(getNumMemberArcs(dec,information.member) == 1); - spqr_arc arc = getFirstMemberArc(dec,information.member); - assert(newRow->isArcCut[arc]); - moveArcToNewMember(dec, arc,information.member,new_row_parallel); - arcSetReversed(dec,arc,newRow->isArcCutReversed[arc]); - dec->members[information.member].type = SPQR_MEMBERTYPE_UNASSIGNED; - }else{ - reorderComponent(dec,information.member); //Make sure the new component is the root of the local decomposition tree - spqr_arc markerArc = SPQR_INVALID_ARC; - spqr_arc ignore = SPQR_INVALID_ARC; - SCIP_CALL(createMarkerPairWithReferences(dec,new_row_parallel,information.member,FALSE, - FALSE,information.reversed, - &ignore,&markerArc)); - if(SPQRnodeIsValid(information.head)){ - assert(SPQRnodeIsValid(information.tail)); - assert(SPQRarcIsValid(information.representative)); - setArcHeadAndTail(dec,markerArc,findNode(dec,information.head),findNode(dec,information.tail)); - arcSetRepresentative(dec,markerArc,information.representative); - arcSetReversed(dec,markerArc,information.reversed != arcIsReversedNonRigid(dec,information.representative)); - } + spqr_member new_row_parallel = SPQR_INVALID_MEMBER; + SCIP_CALL(createConnectedParallel(dec, rowadd->newColumnArcs, rowadd->newColumnReversed, rowadd->numColumnArcs, + rowadd->newRowIndex, &new_row_parallel)); + for( int i = 0; i < rowadd->numReducedComponents; ++i ) + { + NewRowInformation information = emptyNewRowInformation(); + + SCIP_CALL(transformComponentRowAddition(dec, rowadd, &rowadd->reducedComponents[i], &information)); + if( getMemberType(dec, information.member) == SPQR_MEMBERTYPE_LOOP ) + { + assert(getNumMemberArcs(dec, information.member) == 1); + spqr_arc arc = getFirstMemberArc(dec, information.member); + assert(rowadd->isArcCut[arc]); + moveArcToNewMember(dec, arc, information.member, new_row_parallel); + arcSetReversed(dec, arc, rowadd->isArcCutReversed[arc]); + dec->members[information.member].type = SPQR_MEMBERTYPE_UNASSIGNED; + } else + { + reorderComponent(dec, + information.member);//Make sure the new component is the root of the local decomposition tree + spqr_arc markerArc = SPQR_INVALID_ARC; + spqr_arc ignore = SPQR_INVALID_ARC; + SCIP_CALL(createMarkerPairWithReferences(dec, new_row_parallel, information.member, FALSE, + FALSE, information.reversed, + &ignore, &markerArc)); + if( SPQRnodeIsValid(information.head)) + { + assert(SPQRnodeIsValid(information.tail)); + assert(SPQRarcIsValid(information.representative)); + setArcHeadAndTail(dec, markerArc, findNode(dec, information.head), findNode(dec, information.tail)); + arcSetRepresentative(dec, markerArc, information.representative); + arcSetReversed(dec, markerArc, + information.reversed != arcIsReversedNonRigid(dec, information.representative)); } - } - decreaseNumConnectedComponents(dec,newRow->numReducedComponents-1); - assert(numConnectedComponents(dec) == (numDecComponentsBefore - newRow->numReducedComponents + 1)); - } - return SCIP_OKAY; + } + } + decreaseNumConnectedComponents(dec, rowadd->numReducedComponents - 1); + assert(numConnectedComponents(dec) == ( numDecComponentsBefore - rowadd->numReducedComponents + 1 )); + } + return SCIP_OKAY; } -SCIP_Bool SCIPNetworkRowAdditionRemainsNetwork(const SCIP_NETWORKROWADDITION *newRow){ - return newRow->remainsNetwork; +SCIP_Bool SCIPnetrowaddRemainsNetwork(const SCIP_NETROWADD* rowadd) +{ + return rowadd->remainsNetwork; } diff --git a/src/scip/network.h b/src/scip/network.h index d4c03c972c..137ba99427 100644 --- a/src/scip/network.h +++ b/src/scip/network.h @@ -41,11 +41,12 @@ * one +1 entry and one -1 entry in every column, which correspond to the in and out-node of an arc. * This file contains algorithms to detect reflected node-arc incidence matrices, where rows can be optionally negated. * Although network matrices are a strictly larger class of totally unimodular matrices, reflected node-arc incidence - * matrices can be detected more quickly and are commonly used. + * matrices can be detected more quickly and are commonly used within MIP formulations * * Note that all addition algorithms expect that each nonzero is given exactly once and not more often; in particular, * it is up to the user to ensure this when using both column and row addition steps. * + * TODO * The column addition for network matrices is based on: * * The row addition for network matrices is based on: @@ -68,131 +69,156 @@ extern "C" { #endif -/** - * This class stores the Network decomposition using an SPQR tree - */ -typedef struct SCIP_NetworkDecomposition SCIP_NETWORKDECOMP; - -SCIP_EXPORT SCIP_RETCODE SCIPNetworkDecompositionCreate(SCIP * env, SCIP_NETWORKDECOMP **pDecomposition, int numRows, int numColumns); - -SCIP_EXPORT void SCIPNetworkDecompositionFree(SCIP_NETWORKDECOMP **pDecomposition); - -/** - * Returns if the Network decomposition contains the given row - */ -SCIP_EXPORT SCIP_Bool SCIPNetworkDecompositionContainsRow(const SCIP_NETWORKDECOMP * decomposition, int row); - -/** - * Returns if the Network decomposition contains the given column - */ -SCIP_EXPORT SCIP_Bool SCIPNetworkDecompositionContainsColumn(const SCIP_NETWORKDECOMP *decomposition, int column); - -/** - * Checks if the Network decomposition of the graph is minimal. This method should only be used in tests. - */ -SCIP_EXPORT SCIP_Bool SCIPNetworkDecompositionIsMinimal(const SCIP_NETWORKDECOMP * decomposition); - -//TODO: method to convert decomposition into a graph -//TODO: method to remove complete components of the SPQR tree - -/** - * Removes. Removed rows and columns from the SPQR tree. - * Note that removed rows/columns can not be re-introduced into the SPQR tree; this is possible, but much more expensive - * to compute and typically not needed. - */ -SCIP_EXPORT void SCIPNetworkDecompositionRemoveComponents(SCIP_NETWORKDECOMP *dec, const int * componentRows, - int numRows, const int * componentCols, int numCols); - -/** - * A method to check if the cycle stored in the Decomposition matches the given array. This method should only be used in tests. - */ -SCIP_EXPORT SCIP_Bool SCIPNetworkDecompositionVerifyCycle(SCIP * scip, const SCIP_NETWORKDECOMP * dec, - int column, const int * column_rows, const double * column_values, - int num_rows, int * computed_column_storage, - SCIP_Bool * computedSignStorage); - -/** - * This class stores all data for performing sequential column additions to a matrix and checking if it is network or not. - */ -typedef struct SCIP_NetworkColAddition SCIP_NETWORKCOLADDITION; - -/** - * @brief Creates the data structure for managing column-addition for an SPQR decomposition - */ -SCIP_EXPORT SCIP_RETCODE SCIPNetworkColAdditionCreate(SCIP* scip, SCIP_NETWORKCOLADDITION** pNewCol ); - -/** - * @brief Destroys the data structure for managing column-addition for SPQR decomposition - */ -SCIP_EXPORT void SCIPNetworkColAdditionFree(SCIP* scip, SCIP_NETWORKCOLADDITION ** pNewCol); - -/** - * Checks if adding a column of the given matrix creates a network SPQR decomposition. - * Adding a column which is already in the decomposition is undefined behavior and not checked for. - * @param dec Current SPQR-decomposition - * @param newRow Data structure to store information on how to add the new column (if applicable). - * @param column The index of the column to be added - * @param rows An array with the row indices of the nonzero entries of the column. - * @param numRows The number of nonzero entries of the column - */ -SCIP_EXPORT SCIP_RETCODE SCIPNetworkColAdditionCheck(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKCOLADDITION * newCol, int column, - const int * nonzeroRows, const double * nonzeroValues, size_t numNonzeros); -/** - * @brief Adds the most recently checked column from checkNewRow() to the Decomposition. - * In Debug mode, adding a column for which SPQRNetworkColumnAdditionRemainsNetwork() returns false will exit the program. - * In Release mode, adding a column for which SPQRNetworkColumnAdditionRemainsNetwork() return false is undefined behavior - * @param dec Current SPQR-decomposition - * @param newRow Data structure containing information on how to add the new column. - */ -SCIP_EXPORT SCIP_RETCODE SCIPNetworkColAdditionAdd(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKCOLADDITION *newCol); - -/** - * @param newColumn - * @return True if the most recently checked column is addable to the SPQR decomposition passed to it, i.e. the submatrix - * given by both remains network. - */ -SCIP_EXPORT SCIP_Bool SCIPNetworkColAdditionRemainsNetwork(SCIP_NETWORKCOLADDITION *newCol); - -/** - * This class stores all data for performing sequential row-additions to a matrix and checking if it is network or not. - */ -typedef struct SCIP_NetworkRowAddition SCIP_NETWORKROWADDITION; - -/** - * @brief Creates the data structure for managing row-addition for an SPQR decomposition - */ -SCIP_EXPORT SCIP_RETCODE SCIPNetworkRowAdditionCreate(SCIP* scip, SCIP_NETWORKROWADDITION** pNewRow ); -/** - * @brief Destroys the data structure for managing row-addition for SPQR decomposition - */ -SCIP_EXPORT void SCIPNetworkRowAdditionFree(SCIP* scip, SCIP_NETWORKROWADDITION ** pNewRow); - -/** - * Checks if adding a row of the given matrix creates a network SPQR decomposition. - * Adding a row which is already in the decomposition is undefined behavior and not checked for. - * @param dec Current SPQR-decomposition - * @param newRow Data structure to store information on how to add the new row (if applicable). - * @param row The index of the row to be added - * @param columns An array with the column indices of the nonzero entries of the row. - * @param numColumns The number of nonzero entries of the row - */ -SCIP_EXPORT SCIP_RETCODE SCIPNetworkRowAdditionCheck(SCIP_NETWORKDECOMP * dec, SCIP_NETWORKROWADDITION * newRow, int row, - const int * nonzeroCols, const double * nonzeroValues, size_t numNonzeros); -/** - * @brief Adds the most recently checked column from checkNewRow() to the Decomposition. - * In Debug mode, adding a column for which rowAdditionRemainsNetwork() returns false will fail an assertion. - * In Release mode, adding a column for which rowAdditionRemainsNetwork() return false is undefined behavior - * @param dec Current SPQR-decomposition - * @param newRow Data structure containing information on how to add the new row. - */ -SCIP_EXPORT SCIP_RETCODE SCIPNetworkRowAdditionAdd(SCIP_NETWORKDECOMP *dec, SCIP_NETWORKROWADDITION *newRow); - -/** - * @param newRow Data structure containing information on how to add the new row - * @return True if the most recently checked row is addable to the SPQR decomposition passed to it, i.e. the submatrix - * given by both remains network. - */ -SCIP_EXPORT SCIP_Bool SCIPNetworkRowAdditionRemainsNetwork(const SCIP_NETWORKROWADDITION *newRow); +/** this class stores a decomposition of the network matrix using SPQR trees */ +typedef struct SCIP_Netmatdec SCIP_NETMATDEC; + +/** create an empty network matrix decomposition that can store a matrix with at most the given dimensions */ +SCIP_EXPORT +SCIP_RETCODE SCIPnetmatdecCreate( + SCIP* scip, /**< SCIP data structure */ + SCIP_NETMATDEC** pdec, /**< buffer to store pointer to created decomposition */ + int nrows, /**< The maximal number of rows that the decomposition can expect */ + int ncols /**< The maximal number of columns that the decomposition can expect */ +); + +/** frees a network matrix decomposition */ +SCIP_EXPORT +void SCIPnetmatdecFree( + SCIP_NETMATDEC** pdec /**< pointer to the network matrix decomposition to freed */ +); + +/** checks if the network matrix decomposition contains the given row */ +SCIP_EXPORT +SCIP_Bool SCIPnetmatdecContainsRow( + const SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ + int row /**< The row index that is checked */ +); + +/** checks if the network matrix decomposition contains the given column */ +SCIP_EXPORT +SCIP_Bool SCIPnetmatdecContainsColumn( + const SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ + int column /**< The column index that is checked */ +); + +/** checks if the network matrix decomposition is minimal; it is minimal if it does not contain adjacent parallel or series skeletons; should only be used in tests or asserts*/ +SCIP_EXPORT +SCIP_Bool SCIPnetmatdecIsMinimal( + const SCIP_NETMATDEC* dec /**< The network matrix decomposition */ +); + +//TODO: add method that realizes a SCIP digraph from the decomposition +//TODO: add method that *cleanly* removes complete components of the SPQR tree + +/** removes a connected component of the matrix from the network decomposition; note that this method is 'stupid', + * and does not delete the associated graph data structure; moreover, it does not explicitly check if the rows/columns + * that the user provides are a connected component of the matrix given by the decomposition. If this is not the case, + * then calling this function is considered a bug. */ +SCIP_EXPORT +void SCIPnetmatdecRemoveComponent( + SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ + const int* componentrows, /**< Pointer to the array of rows to delete */ + int nrows, /**< The number of rows to delete */ + const int* componentcols, /**< Pointer to the array of columns to delete */ + int ncols /**< The number of columns to delete */ +); + +/** checks if the cycle stored in the Decomposition matches the given array; should only be used in tests */ +SCIP_EXPORT +SCIP_Bool SCIPnetmatdecVerifyCycle( + SCIP* scip, /**< SCIP data structure */ + const SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ + int column, /**< The column to check */ + const int* nonzrowidx, /**< Pointer to the array with the column's nonzero row indices */ + const double* nonzvals, /**< Pointer to the array with the column's nonzero values */ + int nnonzs, /**< Number of nonzeros in the column */ + int* pathrowstorage, /**< Pointer to a buffer to hold the computed path's rows */ + SCIP_Bool* pathsignstorage /**< Pointer to a buffer to store the computed path's row signs */ +); + +/** this class stores all data for performing a column addition to the network matrix decomposition */ +typedef struct SCIP_NetColAdd SCIP_NETCOLADD; + +/** creates the data structure for managing column addition of a network matrix decomposition */ +SCIP_EXPORT +SCIP_RETCODE SCIPnetcoladdCreate( + SCIP* scip, /**< SCIP data structure */ + SCIP_NETCOLADD** pcoladd /**< buffer to store pointer to column addition data structure */ +); + +/** frees the data structure for managing column addition of a network matrix decomposition */ +SCIP_EXPORT +void SCIPnetcoladdFree( + SCIP* scip, /**< SCIP data structure */ + SCIP_NETCOLADD** pcoladd /**< pointer to the column addition data structure to be freed */ +); + +/** Checks if we can add the given column to the network matrix decomposition. + * The result of the latest query can be checked through SCIPnetcoladdRemainsNetwork(). + * Users can add the latest checked column through SCIPnetcoladdAdd()*/ +SCIP_EXPORT +SCIP_RETCODE SCIPnetcoladdCheck( + SCIP_NETMATDEC* dec, /**< Network matrix decomposition */ + SCIP_NETCOLADD* coladd, /**< Network matrix column addition data structure */ + int column, /**< The column to check */ + const int* nonzrows, /**< The column's nonzero row indices */ + const double* nonzvals, /**< The column's nonzero entries */ + size_t nnonzs /**< The number of nonzeros in the column */ +); +/** Adds the most recently checked column to the network matrix decomposition. */ +SCIP_EXPORT +SCIP_RETCODE SCIPnetcoladdAdd( + SCIP_NETMATDEC* dec, /**< Network matrix decomposition */ + SCIP_NETCOLADD* coladd /**< Network matrix column addition data structure */ +); + +/** Returns whether the most recently checked column can be added to the network */ +SCIP_EXPORT +SCIP_Bool SCIPnetcoladdRemainsNetwork( + const SCIP_NETCOLADD* coladd /**< Network matrix column addition data structure */ +); + +/** this class stores all data for performing a row addition to the network matrix decomposition */ +typedef struct SCIP_NetRowAdd SCIP_NETROWADD; + +/** creates the data structure for managing row addition of a network matrix decomposition */ +SCIP_EXPORT +SCIP_RETCODE SCIPnetrowaddCreate( + SCIP* scip, /**< SCIP data structure */ + SCIP_NETROWADD** prowadd /**< buffer to store pointer to row addition data structure */ +); + +/** frees the data structure for managing row addition of a network matrix decomposition */ +SCIP_EXPORT +void SCIPnetrowaddFree( + SCIP* scip, /**< SCIP data structure */ + SCIP_NETROWADD** prowadd /**< pointer to the row addition data structure to be freed */ +); + +/** Checks if we can add the given row to the network matrix decomposition. + * The result of the latest query can be checked through SCIPnetrowaddRemainsNetwork(). + * Users can add the latest checked column through SCIPnetrowaddAdd()*/ +SCIP_EXPORT +SCIP_RETCODE SCIPnetrowaddCheck( + SCIP_NETMATDEC* dec, /**< Network matrix decomposition */ + SCIP_NETROWADD* rowadd, /**< Network matrix row addition data structure */ + int row, /**< The row to check */ + const int* nonzcols, /**< The row's nonzero row indices */ + const double* nonzvals, /**< The row's nonzero entries */ + size_t nnonzs /**< The number of nonzeros in the row */ +); +/** Adds the most recently checked row to the network matrix decomposition. */ +SCIP_EXPORT +SCIP_RETCODE SCIPnetrowaddAdd( + SCIP_NETMATDEC* dec, /**< Network matrix decomposition */ + SCIP_NETROWADD* rowadd /**< Network matrix row addition data structure */ +); + +/** Returns whether the most recently checked row can be added to the network */ +SCIP_EXPORT +SCIP_Bool SCIPnetrowaddRemainsNetwork( + const SCIP_NETROWADD* rowadd /**< Network matrix row addition data structure */ + ); #ifdef cplusplus } diff --git a/tests/src/network/network.c b/tests/src/network/network.c index ea2e04492b..7dedbbed46 100644 --- a/tests/src/network/network.c +++ b/tests/src/network/network.c @@ -48,1809 +48,1870 @@ static SCIP* scip; static void setup(void) { - /* create scip */ - SCIP_CALL( SCIPcreate(&scip) ); + /* create scip */ + SCIP_CALL(SCIPcreate(&scip)); } static void teardown(void) { - /* free scip */ - SCIP_CALL( SCIPfree(&scip) ); + /* free scip */ + SCIP_CALL(SCIPfree(&scip)); } /** CSR/CSC matrix type to encode testing matrices **/ -typedef struct{ - int nrows; - int ncols; - int nnonzs; - - bool isRowWise; //True -> CSR matrix, False -> CSC matrix - - int * primaryIndexStart; // - int * entrySecondaryIndex; // column with CSR and row with CSC matrix - double * entryValue; -} DirectedTestCase; - -static DirectedTestCase stringToTestCase(const char * string, int rows, int cols){ - int row = 0; - int column = 0; - - DirectedTestCase testCase; - testCase.nrows = rows; - testCase.ncols = cols; - testCase.nnonzs = 0; - testCase.isRowWise = true; - - - testCase.primaryIndexStart = malloc(sizeof(int) * (rows + 1)); - - int nonzeroArraySize = 8; - testCase.entrySecondaryIndex = malloc(sizeof(int) * nonzeroArraySize); - testCase.entryValue = malloc(sizeof(double) * nonzeroArraySize); - - - const char * current = &string[0]; - int i = 0; - - while(*current != '\0'){ - char * next = NULL; - double num = strtod(current, &next); - if(i%cols == 0){ - testCase.primaryIndexStart[i/cols] = testCase.nnonzs; - } - if(num != 0.0){ - if(testCase.nnonzs == nonzeroArraySize){ - int newSize = nonzeroArraySize * 2; - testCase.entryValue = realloc(testCase.entryValue,sizeof(double) * newSize); - testCase.entrySecondaryIndex = realloc(testCase.entrySecondaryIndex,sizeof(int) * newSize); - nonzeroArraySize = newSize; - } - testCase.entryValue[testCase.nnonzs] = num; - testCase.entrySecondaryIndex[testCase.nnonzs] = i % cols; - ++testCase.nnonzs; - } - current = next; - ++i; - if(i == rows * cols){ - break; - } - } - testCase.primaryIndexStart[testCase.nrows] = testCase.nnonzs; - - return testCase; -} -static void transposeMatrixStorage(DirectedTestCase * testCase){ - int numPrimaryDimension = testCase->isRowWise ? testCase->nrows : testCase->ncols; - int numSecondaryDimension = testCase->isRowWise ? testCase->ncols : testCase->nrows; - - int * transposedFirstIndex = malloc(sizeof(int) * (numSecondaryDimension + 1)); - int * transposedEntryIndex = malloc(sizeof(int) * testCase->nnonzs); - double * transposedEntryValue = malloc(sizeof(double) * testCase->nnonzs); - - for (int i = 0; i <= numSecondaryDimension; ++i) { - transposedFirstIndex[i] = 0; - } - for (int i = 0; i < testCase->nnonzs; ++i) { - ++ (transposedFirstIndex[testCase->entrySecondaryIndex[i] + 1]); - } - - for (int i = 1; i < numSecondaryDimension; ++i) { - transposedFirstIndex[i] += transposedFirstIndex[i-1]; - } - - for (int i = 0; i < numPrimaryDimension; ++i) { - int first = testCase->primaryIndexStart[i]; - int beyond = testCase->primaryIndexStart[i+1]; - for (int entry = first; entry < beyond; ++entry) { - int index = testCase->entrySecondaryIndex[entry]; - int transIndex = transposedFirstIndex[index]; - transposedEntryIndex[transIndex] = i; - transposedEntryValue[transIndex] = testCase->entryValue[entry]; - ++ (transposedFirstIndex[index]); - } - } - for (int i = numSecondaryDimension; i > 0; --i) { - transposedFirstIndex[i] = transposedFirstIndex[i - 1]; - } - transposedFirstIndex[0] = 0; - - free(testCase->entrySecondaryIndex); - free(testCase->entryValue); - free(testCase->primaryIndexStart); - - testCase->primaryIndexStart = transposedFirstIndex; - testCase->entrySecondaryIndex = transposedEntryIndex; - testCase->entryValue = transposedEntryValue; - - testCase->isRowWise = !testCase->isRowWise; -} - -static DirectedTestCase copyTestCase(DirectedTestCase * testCase){ - DirectedTestCase copy; - copy.nrows = testCase->nrows; - copy.ncols = testCase->ncols; - copy.nnonzs = testCase->nnonzs; - copy.isRowWise = testCase->isRowWise; - - int size = (testCase->isRowWise ? testCase->nrows : testCase->ncols) + 1; - copy.primaryIndexStart = malloc(sizeof(int) * size); - for (int i = 0; i < size; ++i) { - copy.primaryIndexStart[i] = testCase->primaryIndexStart[i]; - } - copy.entrySecondaryIndex = malloc(sizeof(int) * testCase->nnonzs); - copy.entryValue = malloc(sizeof(double) * testCase->nnonzs); - - for (int i = 0; i < testCase->nnonzs; ++i) { - copy.entrySecondaryIndex[i] = testCase->entrySecondaryIndex[i]; - copy.entryValue[i] = testCase->entryValue[i]; - } - return copy; -} -static void freeTestCase(DirectedTestCase * testCase){ - free(testCase->primaryIndexStart); - free(testCase->entrySecondaryIndex); - free(testCase->entryValue); - -} - -static SCIP_RETCODE runColumnTestCase(DirectedTestCase * testCase, bool isExpectedNetwork, bool isExpectedNotNetwork){ - if(testCase->isRowWise){ - transposeMatrixStorage(testCase); - } - cr_expect(!testCase->isRowWise); - SCIP_NETWORKDECOMP * dec = NULL; - SCIP_CALL(SCIPNetworkDecompositionCreate(scip,&dec,testCase->nrows,testCase->ncols)); - - SCIP_NETWORKCOLADDITION * coladd = NULL; - SCIP_CALL(SCIPNetworkColAdditionCreate(scip,&coladd)); - bool isNetwork = true; - - int * tempColumnStorage; - SCIP_Bool * tempSignStorage; - - SCIP_CALL(SCIPallocBufferArray(scip,&tempColumnStorage,testCase->nrows)); - SCIP_CALL(SCIPallocBufferArray(scip,&tempSignStorage,testCase->nrows)); - - for (int i = 0; i < testCase->ncols; ++i) { - int colEntryStart = testCase->primaryIndexStart[i]; - int colEntryEnd = testCase->primaryIndexStart[i+1]; - const int* nonzeroRows = &testCase->entrySecondaryIndex[colEntryStart]; - const double * nonzeroValues = &testCase->entryValue[colEntryStart]; - int nonzeros = colEntryEnd-colEntryStart; - cr_assert(nonzeros >= 0); - //Check if adding the column preserves the network matrix - SCIP_CALL(SCIPNetworkColAdditionCheck(dec,coladd,i,nonzeroRows,nonzeroValues,nonzeros)); - if(SCIPNetworkColAdditionRemainsNetwork(coladd)){ - //If so, we add it. - SCIP_CALL(SCIPNetworkColAdditionAdd(dec,coladd)); - }else{ - isNetwork = false; - break; - } - cr_expect(SCIPNetworkDecompositionIsMinimal(dec)); - //Check if the computed network matrix indeed reflects the network matrix, - //by checking if the fundamental cycles are all correct - for (int j = 0; j <= i; ++j) { - int jColEntryStart = testCase->primaryIndexStart[j]; - int jColEntryEnd = testCase->primaryIndexStart[j+1]; - const int* jNonzeroRows = &testCase->entrySecondaryIndex[jColEntryStart]; - const double * jNonzeroValues = &testCase->entryValue[jColEntryStart]; - int jNonzeros = jColEntryEnd-jColEntryStart; - SCIP_Bool cycleIsCorrect = SCIPNetworkDecompositionVerifyCycle(scip,dec,j, - jNonzeroRows,jNonzeroValues, - jNonzeros,tempColumnStorage, - tempSignStorage); - - cr_expect(cycleIsCorrect); - } - } - - - if(isExpectedNetwork){ - //We expect that the given matrix is a network matrix. If not, something went wrong. - cr_expect(isNetwork); - } - if(isExpectedNotNetwork){ - //We expect that the given matrix is not a network matrix. If not, something went wrong. - cr_expect(!isNetwork); - } - SCIPfreeBufferArray(scip,&tempColumnStorage); - SCIPfreeBufferArray(scip,&tempSignStorage); - - SCIPNetworkColAdditionFree(scip,&coladd); - SCIPNetworkDecompositionFree(&dec); - return SCIP_OKAY; -} - -static SCIP_RETCODE runRowTestCase(DirectedTestCase * testCase, bool isExpectedNetwork, bool isExpectedNotNetwork){ - if(!testCase->isRowWise){ - transposeMatrixStorage(testCase); - } - cr_expect(testCase->isRowWise); - - //We keep a column-wise copy to check the columns easily - DirectedTestCase colWiseCase = copyTestCase(testCase); - transposeMatrixStorage(&colWiseCase); - - SCIP_NETWORKDECOMP * dec = NULL; - SCIP_CALL(SCIPNetworkDecompositionCreate(scip,&dec,testCase->nrows,testCase->ncols)); - - SCIP_NETWORKROWADDITION * rowadd = NULL; - SCIP_CALL(SCIPNetworkRowAdditionCreate(scip,&rowadd)); - bool isNetwork = true; - - int * tempColumnStorage; - SCIP_Bool * tempSignStorage; - - SCIP_CALL(SCIPallocBufferArray(scip,&tempColumnStorage,testCase->nrows)); - SCIP_CALL(SCIPallocBufferArray(scip,&tempSignStorage,testCase->nrows)); - - for (int i = 0; i < testCase->nrows; ++i) { - int rowEntryStart = testCase->primaryIndexStart[i]; - int rowEntryEnd = testCase->primaryIndexStart[i+1]; - const int* nonzeroCols = &testCase->entrySecondaryIndex[rowEntryStart]; - const double * nonzeroValues = &testCase->entryValue[rowEntryStart]; - int nonzeros = rowEntryEnd-rowEntryStart; - cr_assert(nonzeros >= 0); - //Check if adding the row preserves the network matrix - SCIP_CALL(SCIPNetworkRowAdditionCheck(dec,rowadd,i,nonzeroCols,nonzeroValues,nonzeros)); - if(SCIPNetworkRowAdditionRemainsNetwork(rowadd)){ - //If so, we add it. - SCIP_CALL(SCIPNetworkRowAdditionAdd(dec,rowadd)); - }else{ - isNetwork = false; - break; - } - cr_expect(SCIPNetworkDecompositionIsMinimal(dec)); - //Check if the computed network matrix indeed reflects the network matrix, - //by checking if the fundamental cycles are all correct - for (int j = 0; j < colWiseCase.ncols; ++j) { - int jColEntryStart = colWiseCase.primaryIndexStart[j]; - int jColEntryEnd = colWiseCase.primaryIndexStart[j+1]; - - //Count the number of rows in the column that should be in the current decomposition - int finalEntryIndex = jColEntryStart; - for (int testEntry = jColEntryStart; testEntry < jColEntryEnd; ++testEntry) { - if(colWiseCase.entrySecondaryIndex[testEntry] <= i){ - ++finalEntryIndex; - }else{ - break; - } +typedef struct +{ + int nrows; + int ncols; + int nnonzs; + + bool isRowWise; //True -> CSR matrix, False -> CSC matrix + + int* primaryIndexStart; // + int* entrySecondaryIndex; // column with CSR and row with CSC matrix + double* entryValue; +} DirectedTestCase; + +static DirectedTestCase stringToTestCase( + const char* string, + int rows, + int cols +) +{ + + DirectedTestCase testCase; + testCase.nrows = rows; + testCase.ncols = cols; + testCase.nnonzs = 0; + testCase.isRowWise = true; + + + testCase.primaryIndexStart = malloc(sizeof(int) * ( rows + 1 )); + + int nonzeroArraySize = 8; + testCase.entrySecondaryIndex = malloc(sizeof(int) * nonzeroArraySize); + testCase.entryValue = malloc(sizeof(double) * nonzeroArraySize); + + + const char* current = &string[0]; + int i = 0; + + while( *current != '\0' ) + { + char* next = NULL; + double num = strtod(current, &next); + if( i % cols == 0 ) + { + testCase.primaryIndexStart[i / cols] = testCase.nnonzs; + } + if( num != 0.0 ) + { + if( testCase.nnonzs == nonzeroArraySize ) + { + int newSize = nonzeroArraySize * 2; + testCase.entryValue = realloc(testCase.entryValue, sizeof(double) * newSize); + testCase.entrySecondaryIndex = realloc(testCase.entrySecondaryIndex, sizeof(int) * newSize); + nonzeroArraySize = newSize; + } + testCase.entryValue[testCase.nnonzs] = num; + testCase.entrySecondaryIndex[testCase.nnonzs] = i % cols; + ++testCase.nnonzs; + } + current = next; + ++i; + if( i == rows * cols ) + { + break; + } + } + testCase.primaryIndexStart[testCase.nrows] = testCase.nnonzs; + + return testCase; +} + +static void transposeMatrixStorage(DirectedTestCase* testCase) +{ + int numPrimaryDimension = testCase->isRowWise ? testCase->nrows : testCase->ncols; + int numSecondaryDimension = testCase->isRowWise ? testCase->ncols : testCase->nrows; + + int* transposedFirstIndex = malloc(sizeof(int) * ( numSecondaryDimension + 1 )); + int* transposedEntryIndex = malloc(sizeof(int) * testCase->nnonzs); + double* transposedEntryValue = malloc(sizeof(double) * testCase->nnonzs); + + for( int i = 0; i <= numSecondaryDimension; ++i ) + { + transposedFirstIndex[i] = 0; + } + for( int i = 0; i < testCase->nnonzs; ++i ) + { + ++( transposedFirstIndex[testCase->entrySecondaryIndex[i] + 1] ); + } + + for( int i = 1; i < numSecondaryDimension; ++i ) + { + transposedFirstIndex[i] += transposedFirstIndex[i - 1]; + } + + for( int i = 0; i < numPrimaryDimension; ++i ) + { + int first = testCase->primaryIndexStart[i]; + int beyond = testCase->primaryIndexStart[i + 1]; + for( int entry = first; entry < beyond; ++entry ) + { + int index = testCase->entrySecondaryIndex[entry]; + int transIndex = transposedFirstIndex[index]; + transposedEntryIndex[transIndex] = i; + transposedEntryValue[transIndex] = testCase->entryValue[entry]; + ++( transposedFirstIndex[index] ); + } + } + for( int i = numSecondaryDimension; i > 0; --i ) + { + transposedFirstIndex[i] = transposedFirstIndex[i - 1]; + } + transposedFirstIndex[0] = 0; + + free(testCase->entrySecondaryIndex); + free(testCase->entryValue); + free(testCase->primaryIndexStart); + + testCase->primaryIndexStart = transposedFirstIndex; + testCase->entrySecondaryIndex = transposedEntryIndex; + testCase->entryValue = transposedEntryValue; + + testCase->isRowWise = !testCase->isRowWise; +} + +static DirectedTestCase copyTestCase(DirectedTestCase* testCase) +{ + DirectedTestCase copy; + copy.nrows = testCase->nrows; + copy.ncols = testCase->ncols; + copy.nnonzs = testCase->nnonzs; + copy.isRowWise = testCase->isRowWise; + + int size = ( testCase->isRowWise ? testCase->nrows : testCase->ncols ) + 1; + copy.primaryIndexStart = malloc(sizeof(int) * size); + for( int i = 0; i < size; ++i ) + { + copy.primaryIndexStart[i] = testCase->primaryIndexStart[i]; + } + copy.entrySecondaryIndex = malloc(sizeof(int) * testCase->nnonzs); + copy.entryValue = malloc(sizeof(double) * testCase->nnonzs); + + for( int i = 0; i < testCase->nnonzs; ++i ) + { + copy.entrySecondaryIndex[i] = testCase->entrySecondaryIndex[i]; + copy.entryValue[i] = testCase->entryValue[i]; + } + return copy; +} + +static void freeTestCase(DirectedTestCase* testCase) +{ + free(testCase->primaryIndexStart); + free(testCase->entrySecondaryIndex); + free(testCase->entryValue); + +} + +static SCIP_RETCODE runColumnTestCase( + DirectedTestCase* testCase, + bool isExpectedNetwork, + bool isExpectedNotNetwork +) +{ + if( testCase->isRowWise ) + { + transposeMatrixStorage(testCase); + } + cr_expect(!testCase->isRowWise); + SCIP_NETMATDEC* dec = NULL; + SCIP_CALL(SCIPnetmatdecCreate(scip, &dec, testCase->nrows, testCase->ncols)); + + SCIP_NETCOLADD* coladd = NULL; + SCIP_CALL(SCIPnetcoladdCreate(scip, &coladd)); + bool isNetwork = true; + + int* tempColumnStorage; + SCIP_Bool* tempSignStorage; + + SCIP_CALL(SCIPallocBufferArray(scip, &tempColumnStorage, testCase->nrows)); + SCIP_CALL(SCIPallocBufferArray(scip, &tempSignStorage, testCase->nrows)); + + for( int i = 0; i < testCase->ncols; ++i ) + { + int colEntryStart = testCase->primaryIndexStart[i]; + int colEntryEnd = testCase->primaryIndexStart[i + 1]; + const int* nonzeroRows = &testCase->entrySecondaryIndex[colEntryStart]; + const double* nonzeroValues = &testCase->entryValue[colEntryStart]; + int nonzeros = colEntryEnd - colEntryStart; + cr_assert(nonzeros >= 0); + //Check if adding the column preserves the network matrix + SCIP_CALL(SCIPnetcoladdCheck(dec, coladd, i, nonzeroRows, nonzeroValues, nonzeros)); + if( SCIPnetcoladdRemainsNetwork(coladd)) + { + //If so, we add it. + SCIP_CALL(SCIPnetcoladdAdd(dec, coladd)); + } else + { + isNetwork = false; + break; + } + cr_expect(SCIPnetmatdecIsMinimal(dec)); + //Check if the computed network matrix indeed reflects the network matrix, + //by checking if the fundamental cycles are all correct + for( int j = 0; j <= i; ++j ) + { + int jColEntryStart = testCase->primaryIndexStart[j]; + int jColEntryEnd = testCase->primaryIndexStart[j + 1]; + const int* jNonzeroRows = &testCase->entrySecondaryIndex[jColEntryStart]; + const double* jNonzeroValues = &testCase->entryValue[jColEntryStart]; + int jNonzeros = jColEntryEnd - jColEntryStart; + SCIP_Bool cycleIsCorrect = SCIPnetmatdecVerifyCycle(scip, dec, j, + jNonzeroRows, jNonzeroValues, + jNonzeros, tempColumnStorage, + tempSignStorage); + + cr_expect(cycleIsCorrect); + } + } + + + if( isExpectedNetwork ) + { + //We expect that the given matrix is a network matrix. If not, something went wrong. + cr_expect(isNetwork); + } + if( isExpectedNotNetwork ) + { + //We expect that the given matrix is not a network matrix. If not, something went wrong. + cr_expect(!isNetwork); + } + SCIPfreeBufferArray(scip, &tempColumnStorage); + SCIPfreeBufferArray(scip, &tempSignStorage); + + SCIPnetcoladdFree(scip, &coladd); + SCIPnetmatdecFree(&dec); + return SCIP_OKAY; +} + +static SCIP_RETCODE runRowTestCase( + DirectedTestCase* testCase, + bool isExpectedNetwork, + bool isExpectedNotNetwork +) +{ + if( !testCase->isRowWise ) + { + transposeMatrixStorage(testCase); + } + cr_expect(testCase->isRowWise); + + //We keep a column-wise copy to check the columns easily + DirectedTestCase colWiseCase = copyTestCase(testCase); + transposeMatrixStorage(&colWiseCase); + + SCIP_NETMATDEC* dec = NULL; + SCIP_CALL(SCIPnetmatdecCreate(scip, &dec, testCase->nrows, testCase->ncols)); + + SCIP_NETROWADD* rowadd = NULL; + SCIP_CALL(SCIPnetrowaddCreate(scip, &rowadd)); + bool isNetwork = true; + + int* tempColumnStorage; + SCIP_Bool* tempSignStorage; + + SCIP_CALL(SCIPallocBufferArray(scip, &tempColumnStorage, testCase->nrows)); + SCIP_CALL(SCIPallocBufferArray(scip, &tempSignStorage, testCase->nrows)); + + for( int i = 0; i < testCase->nrows; ++i ) + { + int rowEntryStart = testCase->primaryIndexStart[i]; + int rowEntryEnd = testCase->primaryIndexStart[i + 1]; + const int* nonzeroCols = &testCase->entrySecondaryIndex[rowEntryStart]; + const double* nonzeroValues = &testCase->entryValue[rowEntryStart]; + int nonzeros = rowEntryEnd - rowEntryStart; + cr_assert(nonzeros >= 0); + //Check if adding the row preserves the network matrix + SCIP_CALL(SCIPnetrowaddCheck(dec, rowadd, i, nonzeroCols, nonzeroValues, nonzeros)); + if( SCIPnetrowaddRemainsNetwork(rowadd)) + { + //If so, we add it. + SCIP_CALL(SCIPnetrowaddAdd(dec, rowadd)); + } else + { + isNetwork = false; + break; + } + cr_expect(SCIPnetmatdecIsMinimal(dec)); + //Check if the computed network matrix indeed reflects the network matrix, + //by checking if the fundamental cycles are all correct + for( int j = 0; j < colWiseCase.ncols; ++j ) + { + int jColEntryStart = colWiseCase.primaryIndexStart[j]; + int jColEntryEnd = colWiseCase.primaryIndexStart[j + 1]; + + //Count the number of rows in the column that should be in the current decomposition + int finalEntryIndex = jColEntryStart; + for( int testEntry = jColEntryStart; testEntry < jColEntryEnd; ++testEntry ) + { + if( colWiseCase.entrySecondaryIndex[testEntry] <= i ) + { + ++finalEntryIndex; + } else + { + break; } + } - const int* jNonzeroRows = &colWiseCase.entrySecondaryIndex[jColEntryStart]; - const double * jNonzeroValues = &colWiseCase.entryValue[jColEntryStart]; + const int* jNonzeroRows = &colWiseCase.entrySecondaryIndex[jColEntryStart]; + const double* jNonzeroValues = &colWiseCase.entryValue[jColEntryStart]; - int jNonzeros = finalEntryIndex-jColEntryStart; - SCIP_Bool cycleIsCorrect = SCIPNetworkDecompositionVerifyCycle(scip,dec,j, - jNonzeroRows,jNonzeroValues, - jNonzeros,tempColumnStorage, - tempSignStorage); + int jNonzeros = finalEntryIndex - jColEntryStart; + SCIP_Bool cycleIsCorrect = SCIPnetmatdecVerifyCycle(scip, dec, j, + jNonzeroRows, jNonzeroValues, + jNonzeros, tempColumnStorage, + tempSignStorage); - cr_expect(cycleIsCorrect); - } - } + cr_expect(cycleIsCorrect); + } + } - if(isExpectedNetwork){ - //We expect that the given matrix is a network matrix. If not, something went wrong. - cr_expect(isNetwork); - } - if(isExpectedNotNetwork){ - //We expect that the given matrix is not a network matrix. If not, something went wrong. - cr_expect(!isNetwork); - } + if( isExpectedNetwork ) + { + //We expect that the given matrix is a network matrix. If not, something went wrong. + cr_expect(isNetwork); + } + if( isExpectedNotNetwork ) + { + //We expect that the given matrix is not a network matrix. If not, something went wrong. + cr_expect(!isNetwork); + } - freeTestCase(&colWiseCase); + freeTestCase(&colWiseCase); - SCIPfreeBufferArray(scip,&tempColumnStorage); - SCIPfreeBufferArray(scip,&tempSignStorage); + SCIPfreeBufferArray(scip, &tempColumnStorage); + SCIPfreeBufferArray(scip, &tempSignStorage); - SCIPNetworkRowAdditionFree(scip,&rowadd); - SCIPNetworkDecompositionFree(&dec); - return SCIP_OKAY; + SCIPnetrowaddFree(scip, &rowadd); + SCIPnetmatdecFree(&dec); + return SCIP_OKAY; } TestSuite(network, .init = setup, .fini = teardown); Test(network, coladd_single_column, .description = "Try adding a single column") { - DirectedTestCase testCase = stringToTestCase( - "+1 " - "+1 " - "-1 ", - 3,1); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 " + "+1 " + "-1 ", + 3, 1); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } + Test(network, coladd_doublecolumn_invalid_sign, .description = "Try adding a second column that has invalid signing") { - DirectedTestCase testCase = stringToTestCase( - "+1 +1 " - "+1 0 " - "-1 +1 ", - 3,2); - runColumnTestCase(&testCase,false,true); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 +1 " + "+1 0 " + "-1 +1 ", + 3, 2); + runColumnTestCase(&testCase, false, true); + freeTestCase(&testCase); } Test(network, coladd_doublecolumn_invalid_sign_2, .description = "Try adding a second column that has invalid signing") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 " - "+1 0 " - "-1 -1 ", - 3,2); - runColumnTestCase(&testCase,false,true); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "+1 0 " + "-1 -1 ", + 3, 2); + runColumnTestCase(&testCase, false, true); + freeTestCase(&testCase); } Test(network, coladd_splitseries_1, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 " - "+1 0 " - " 0 0 ", - 3,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "+1 0 " + " 0 0 ", + 3, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_2, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 " - "+1 0 " - " 0 0 ", - 3,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "+1 0 " + " 0 0 ", + 3, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_1r, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "-1 -1 " - "+1 0 " - " 0 0 ", - 3,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 -1 " + "+1 0 " + " 0 0 ", + 3, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_2r, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "-1 +1 " - "+1 0 " - " 0 0 ", - 3,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 +1 " + "+1 0 " + " 0 0 ", + 3, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_3, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 " - "+1 0 " - " 0 +1 ", - 3,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "+1 0 " + " 0 +1 ", + 3, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_4, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "+1 +1 " - "+1 0 " - " 0 +1 ", - 3,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 +1 " + "+1 0 " + " 0 +1 ", + 3, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_3r, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "-1 -1 " - "+1 0 " - " 0 +1 ", - 3,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 -1 " + "+1 0 " + " 0 +1 ", + 3, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_4r, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "-1 +1 " - "+1 0 " - " 0 +1 ", - 3,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 +1 " + "+1 0 " + " 0 +1 ", + 3, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_5, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "+1 +1 " - " 0 0 " - " 0 0 ", - 3,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 +1 " + " 0 0 " + " 0 0 ", + 3, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_6, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 " - " 0 0 " - " 0 0 ", - 3,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + " 0 0 " + " 0 0 ", + 3, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_7, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "+1 +1 " - " 0 0 " - " 0 +1 ", - 3,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 +1 " + " 0 0 " + " 0 +1 ", + 3, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_8, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 " - " 0 0 " - " 0 +1 ", - 3,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + " 0 0 " + " 0 +1 ", + 3, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_5r, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "-1 +1 " - " 0 0 " - " 0 0 ", - 3,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 +1 " + " 0 0 " + " 0 0 ", + 3, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_6r, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "-1 -1 " - " 0 0 " - " 0 0 ", - 3,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 -1 " + " 0 0 " + " 0 0 ", + 3, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_7r, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "-1 +1 " - " 0 0 " - " 0 +1 ", - 3,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 +1 " + " 0 0 " + " 0 +1 ", + 3, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_8r, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "-1 -1 " - " 0 0 " - " 0 +1 ", - 3,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 -1 " + " 0 0 " + " 0 +1 ", + 3, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_9, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 " - "+1 0 " - "-1 +1 " - " 0 0 ", - 4,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "+1 0 " + "-1 +1 " + " 0 0 ", + 4, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_10, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "+1 +1 " - "+1 0 " - "-1 -1 " - " 0 0 ", - 4,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 +1 " + "+1 0 " + "-1 -1 " + " 0 0 ", + 4, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_11, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "+1 +1 " - "+1 0 " - "-1 -1 " - " 0 +1 ", - 4,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 +1 " + "+1 0 " + "-1 -1 " + " 0 +1 ", + 4, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_12, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 " - "+1 0 " - "-1 +1 " - " 0 +1 ", - 4,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "+1 0 " + "-1 +1 " + " 0 +1 ", + 4, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_9r, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "-1 -1 " - "-1 0 " - "+1 +1 " - " 0 0 ", - 4,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 -1 " + "-1 0 " + "+1 +1 " + " 0 0 ", + 4, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_10r, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "-1 +1 " - "-1 0 " - "+1 -1 " - " 0 0 ", - 4,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 +1 " + "-1 0 " + "+1 -1 " + " 0 0 ", + 4, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_11r, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "-1 +1 " - "-1 0 " - "+1 -1 " - " 0 +1 ", - 4,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 +1 " + "-1 0 " + "+1 -1 " + " 0 +1 ", + 4, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_12r, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "-1 -1 " - "-1 0 " - "+1 +1 " - " 0 +1 ", - 4,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 -1 " + "-1 0 " + "+1 +1 " + " 0 +1 ", + 4, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_13, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "+1 +1 " - "+1 +1 " - "-1 -1 " - " 0 0 ", - 4,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 +1 " + "+1 +1 " + "-1 -1 " + " 0 0 ", + 4, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_14, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 " - "+1 -1 " - "-1 +1 " - " 0 0 ", - 4,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "+1 -1 " + "-1 +1 " + " 0 0 ", + 4, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_15, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "+1 +1 " - "+1 +1 " - "-1 -1 " - " 0 +1 ", - 4,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 +1 " + "+1 +1 " + "-1 -1 " + " 0 +1 ", + 4, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_16, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 " - "+1 -1 " - "-1 +1 " - " 0 +1 ", - 4,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "+1 -1 " + "-1 +1 " + " 0 +1 ", + 4, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_13r, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "-1 +1 " - "-1 +1 " - "+1 -1 " - " 0 0 ", - 4,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 +1 " + "-1 +1 " + "+1 -1 " + " 0 0 ", + 4, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_14r, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "-1 -1 " - "-1 -1 " - "+1 +1 " - " 0 0 ", - 4,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 -1 " + "-1 -1 " + "+1 +1 " + " 0 0 ", + 4, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_15r, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "-1 +1 " - "-1 +1 " - "+1 -1 " - " 0 +1 ", - 4,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 +1 " + "-1 +1 " + "+1 -1 " + " 0 +1 ", + 4, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_splitseries_16r, .description = "Split a series component") { - DirectedTestCase testCase = stringToTestCase( - "-1 -1 " - "-1 -1 " - "+1 +1 " - " 0 +1 ", - 4,2); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 -1 " + "-1 -1 " + "+1 +1 " + " 0 +1 ", + 4, 2); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_parallelsimple_1, .description = "Extending a parallel component") { - DirectedTestCase testCase = stringToTestCase( - "1 1 1 -1 ", - 1,4); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 1 1 -1 ", + 1, 4); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_parallelsimple_2, .description = "Extending a parallel component") { - DirectedTestCase testCase = stringToTestCase( - "1 1 1 -1 " - "0 0 -1 0 ", - 2,4); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 1 1 -1 " + "0 0 -1 0 ", + 2, 4); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_parallelsimple_3, .description = "Extending a parallel component") { - DirectedTestCase testCase = stringToTestCase( - "1 1 1 1 " - "0 0 1 0 ", - 2,4); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 1 1 1 " + "0 0 1 0 ", + 2, 4); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_components_1, .description = "Merging multiple components into one") { - DirectedTestCase testCase = stringToTestCase( - "0 -1 -1 " - "1 0 1 ", - 2,3); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 -1 -1 " + "1 0 1 ", + 2, 3); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_components_2, .description = "Merging multiple components into one") { - DirectedTestCase testCase = stringToTestCase( - "0 1 -1 " - "1 0 1 ", - 2,3); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 1 -1 " + "1 0 1 ", + 2, 3); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_components_3, .description = "Merging multiple components into one") { - DirectedTestCase testCase = stringToTestCase( - "0 1 1 " - "-1 0 1 ", - 2,3); - runColumnTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 1 1 " + "-1 0 1 ", + 2, 3); + runColumnTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, coladd_3by3_1, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "0 1 1 " - "1 -1 -1 " - "-1 1 -1 ", - 3,3); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 1 1 " + "1 -1 -1 " + "-1 1 -1 ", + 3, 3); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_3by3_2, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "0 -1 -1 " - "1 0 1 " - "0 0 0 ", - 3,3); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 -1 -1 " + "1 0 1 " + "0 0 0 ", + 3, 3); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_3by3_3, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "0 -1 1 " - "1 -1 -1 " - "0 0 1 ", - 3,3); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 -1 1 " + "1 -1 -1 " + "0 0 1 ", + 3, 3); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_3by3_4, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "1 -1 1 " - "-1 1 0 " - "0 1 -1 ", - 3,3); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 -1 1 " + "-1 1 0 " + "0 1 -1 ", + 3, 3); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_3by3_5, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "0 1 1 " - "0 1 0 " - "-1 -1 -1 ", - 3,3); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 1 1 " + "0 1 0 " + "-1 -1 -1 ", + 3, 3); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_3by3_6, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "-1 1 -1 " - "-1 1 -1 " - "-1 0 -1 ", - 3,3); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 1 -1 " + "-1 1 -1 " + "-1 0 -1 ", + 3, 3); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } + Test(network, coladd_3by3_7, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "1 1 -1 " - "0 1 1 " - "-1 0 0 ", - 3,3); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 1 -1 " + "0 1 1 " + "-1 0 0 ", + 3, 3); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } + Test(network, coladd_3by3_8, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "1 1 -1 " - "0 -1 1 " - "1 0 0 ", - 3,3); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 1 -1 " + "0 -1 1 " + "1 0 0 ", + 3, 3); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } + Test(network, coladd_3by3_9, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "-1 0 0 " - "-1 -1 -1 " - "-1 -1 -1 ", - 3,3); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 0 0 " + "-1 -1 -1 " + "-1 -1 -1 ", + 3, 3); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } + Test(network, coladd_3by3_10, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "-1 1 -1 " - "-1 1 -1 " - "1 0 -1 ", - 3,3); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 1 -1 " + "-1 1 -1 " + "1 0 -1 ", + 3, 3); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_3by3_11, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "-1 1 0 " - "-1 0 1 " - "0 1 1 ", - 3,3); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 1 0 " + "-1 0 1 " + "0 1 1 ", + 3, 3); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_6by3_1, .description = "A six by three case") { - DirectedTestCase testCase = stringToTestCase( - "1 -1 1 " - "-1 1 -1 " - "-1 1 0 " - "0 1 1 " - "1 0 -1 " - "0 -1 -1 ", - 6,3); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 -1 1 " + "-1 1 -1 " + "-1 1 0 " + "0 1 1 " + "1 0 -1 " + "0 -1 -1 ", + 6, 3); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_6by3_2, .description = "A six by three case") { - DirectedTestCase testCase = stringToTestCase( - "1 -1 1 " - "-1 1 0 " - "0 1 -1 " - "0 -1 0 " - "0 1 0 " - "0 0 1 ", - 6,3); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 -1 1 " + "-1 1 0 " + "0 1 -1 " + "0 -1 0 " + "0 1 0 " + "0 0 1 ", + 6, 3); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_3by4_1, .description = "A three by four case") { - DirectedTestCase testCase = stringToTestCase( - "0 -1 1 1 " - "-1 -1 0 0 " - "1 0 1 -1 ", - 3,4); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 -1 1 1 " + "-1 -1 0 0 " + "1 0 1 -1 ", + 3, 4); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_3by5_1, .description = "A three by five case") { - DirectedTestCase testCase = stringToTestCase( - "0 1 1 0 1 " - "0 -1 -1 -1 -1 " - "1 -1 0 -1 -1 ", - 3,5); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 1 1 0 1 " + "0 -1 -1 -1 -1 " + "1 -1 0 -1 -1 ", + 3, 5); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_4by8_1, .description = "A four by eight case") { - DirectedTestCase testCase = stringToTestCase( - "0 0 1 1 0 1 0 1 " - "0 0 0 0 0 -1 1 -1 " - "1 -1 0 0 1 1 0 0 " - "0 0 1 1 -1 0 -1 0 ", - 4,8); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 0 1 1 0 1 0 1 " + "0 0 0 0 0 -1 1 -1 " + "1 -1 0 0 1 1 0 0 " + "0 0 1 1 -1 0 -1 0 ", + 4, 8); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_4by8_2, .description = "A four by eight case") { - DirectedTestCase testCase = stringToTestCase( - "0 1 0 -1 0 0 -1 0 " - "1 1 0 0 1 1 -1 1 " - "0 -1 0 0 -1 -1 0 -1 " - "0 1 1 -1 1 0 -1 -1 ", - 4,8); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 1 0 -1 0 0 -1 0 " + "1 1 0 0 1 1 -1 1 " + "0 -1 0 0 -1 -1 0 -1 " + "0 1 1 -1 1 0 -1 -1 ", + 4, 8); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_4by8_3, .description = "A four by eight case") { - DirectedTestCase testCase = stringToTestCase( - "-1 1 -1 0 -1 0 -1 0 " - "-1 0 -1 0 1 -1 1 1 " - "0 0 -1 1 -1 -1 0 0 " - "-1 1 0 -1 0 -1 1 -1 ", - 4,8); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 1 -1 0 -1 0 -1 0 " + "-1 0 -1 0 1 -1 1 1 " + "0 0 -1 1 -1 -1 0 0 " + "-1 1 0 -1 0 -1 1 -1 ", + 4, 8); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } + Test(network, coladd_4by8_4, .description = "A four by eight case") { - DirectedTestCase testCase = stringToTestCase( - "-1 0 1 1 0 1 0 0 " - "0 -1 0 -1 1 0 -1 -1 " - "-1 -1 1 0 1 1 -1 -1 " - "0 0 0 -1 0 -1 1 -1 ", - 4,8); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 0 1 1 0 1 0 0 " + "0 -1 0 -1 1 0 -1 -1 " + "-1 -1 1 0 1 1 -1 -1 " + "0 0 0 -1 0 -1 1 -1 ", + 4, 8); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } + Test(network, coladd_4by8_5, .description = "A four by eight case") { - DirectedTestCase testCase = stringToTestCase( - "0 0 0 -1 -1 -1 0 -1 " - "-1 -1 0 0 1 1 -1 0 " - "0 0 1 0 -1 -1 0 -1 " - "0 0 1 -1 -1 0 0 -1 ", - 4,8); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 0 0 -1 -1 -1 0 -1 " + "-1 -1 0 0 1 1 -1 0 " + "0 0 1 0 -1 -1 0 -1 " + "0 0 1 -1 -1 0 0 -1 ", + 4, 8); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } + Test(network, coladd_4by8_6, .description = "A four by eight case") { - DirectedTestCase testCase = stringToTestCase( - "-1 0 0 1 1 1 0 -1 " - "0 -1 -1 0 0 1 0 -1 " - "0 0 1 -1 0 0 1 1 " - "0 -1 -1 1 1 1 -1 0 ", - 4,8); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 0 0 1 1 1 0 -1 " + "0 -1 -1 0 0 1 0 -1 " + "0 0 1 -1 0 0 1 1 " + "0 -1 -1 1 1 1 -1 0 ", + 4, 8); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } + Test(network, coladd_4by8_7, .description = "A four by eight case") { - DirectedTestCase testCase = stringToTestCase( - "0 0 1 1 1 0 1 0 " - "0 -1 1 1 1 1 -1 0 " - "1 0 0 0 1 0 1 1 " - "1 -1 1 0 -1 -1 -1 -1 ", - 4,8); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 0 1 1 1 0 1 0 " + "0 -1 1 1 1 1 -1 0 " + "1 0 0 0 1 0 1 1 " + "1 -1 1 0 -1 -1 -1 -1 ", + 4, 8); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } + Test(network, coladd_4by4_1, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "-1 +1 -1 0 " - "-1 0 -1 0 " - "0 0 -1 +1 " - "-1 +1 0 -1", - 4,4); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 +1 -1 0 " + "-1 0 -1 0 " + "0 0 -1 +1 " + "-1 +1 0 -1", + 4, 4); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } + Test(network, coladd_5by5_1, .description = "A five by five case") { - DirectedTestCase testCase = stringToTestCase( - "0 1 1 0 0 " - "0 -1 -1 1 0 " - "0 0 -1 1 0 " - "-1 -1 0 0 -1 " - "-1 -1 -1 1 0 ", - 5,5); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 1 1 0 0 " + "0 -1 -1 1 0 " + "0 0 -1 1 0 " + "-1 -1 0 0 -1 " + "-1 -1 -1 1 0 ", + 5, 5); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_5by5_2, .description = "A five by five case") { - DirectedTestCase testCase = stringToTestCase( - "-1 0 1 1 -1 " - "-1 1 1 1 0 " - "0 0 1 1 1 " - "-1 1 0 -1 0 " - "-1 1 0 0 -1 ", - 5,5); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 0 1 1 -1 " + "-1 1 1 1 0 " + "0 0 1 1 1 " + "-1 1 0 -1 0 " + "-1 1 0 0 -1 ", + 5, 5); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_5by5_3, .description = "A five by five case") { - DirectedTestCase testCase = stringToTestCase( - "0 1 1 0 -1 " - "0 -1 0 1 -1 " - "1 1 1 0 1 " - "0 0 1 1 0 " - "-1 0 -1 0 1 ", - 5,5); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 1 1 0 -1 " + "0 -1 0 1 -1 " + "1 1 1 0 1 " + "0 0 1 1 0 " + "-1 0 -1 0 1 ", + 5, 5); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_5by5_4, .description = "A five by five case") { - DirectedTestCase testCase = stringToTestCase( - "1 -1 -1 1 0 " - "0 0 -1 1 -1 " - "1 -1 0 0 1 " - "0 1 1 -1 0 " - "0 -1 0 1 0 ", - 5,5); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 -1 -1 1 0 " + "0 0 -1 1 -1 " + "1 -1 0 0 1 " + "0 1 1 -1 0 " + "0 -1 0 1 0 ", + 5, 5); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_5by5_5, .description = "A five by five case") { - DirectedTestCase testCase = stringToTestCase( - "0 1 0 1 1 " - "-1 1 1 0 0 " - "0 0 0 -1 -1 " - "1 -1 0 0 -1 " - "-1 1 0 1 1 ", - 5,5); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 1 0 1 1 " + "-1 1 1 0 0 " + "0 0 0 -1 -1 " + "1 -1 0 0 -1 " + "-1 1 0 1 1 ", + 5, 5); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_5by5_6, .description = "A five by five case") { - DirectedTestCase testCase = stringToTestCase( - "0 -1 1 0 0 " - "-1 0 -1 0 0 " - "0 -1 1 1 -1 " - "-1 1 -1 -1 0 " - "1 0 0 -1 0 ", - 5,5); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 -1 1 0 0 " + "-1 0 -1 0 0 " + "0 -1 1 1 -1 " + "-1 1 -1 -1 0 " + "1 0 0 -1 0 ", + 5, 5); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_5by5_7, .description = "A five by five case") { - DirectedTestCase testCase = stringToTestCase( - "1 0 1 0 0 " - "0 -1 0 1 0 " - "0 1 -1 -1 0 " - "-1 0 -1 -1 -1 " - "-1 -1 0 1 -1 ", - 5,5); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 0 1 0 0 " + "0 -1 0 1 0 " + "0 1 -1 -1 0 " + "-1 0 -1 -1 -1 " + "-1 -1 0 1 -1 ", + 5, 5); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_5by5_8, .description = "A five by five case") { - DirectedTestCase testCase = stringToTestCase( - "1 1 1 -1 0 " - "1 0 0 0 -1 " - "-1 0 -1 1 1 " - "0 0 0 -1 -1 " - "1 1 0 -1 0 ", - 5,5); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 1 1 -1 0 " + "1 0 0 0 -1 " + "-1 0 -1 1 1 " + "0 0 0 -1 -1 " + "1 1 0 -1 0 ", + 5, 5); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, coladd_5by5_9, .description = "A five by five case") { - DirectedTestCase testCase = stringToTestCase( - "1 -1 0 -1 1 " - "1 -1 -1 -1 1 " - "0 0 -1 0 0 " - "0 -1 -1 0 0 " - "1 0 0 0 1 ", - 5,5); - runColumnTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 -1 0 -1 1 " + "1 -1 -1 -1 1 " + "0 0 -1 0 0 " + "0 -1 -1 0 0 " + "1 0 0 0 1 ", + 5, 5); + runColumnTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_1by2_1, .description = "A one by two case") { - DirectedTestCase testCase = stringToTestCase( - "+1 0 ", - 1,2); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 0 ", + 1, 2); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_1by2_2, .description = "A one by two case") { - DirectedTestCase testCase = stringToTestCase( - "1 1 ", - 1,2); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 1 ", + 1, 2); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_1by2_3, .description = "A one by two case") { - DirectedTestCase testCase = stringToTestCase( - "1 -1 ", - 1,2); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 -1 ", + 1, 2); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_2by3_1, .description = "A two by three case") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 +1 " - "-1 +1 -1 ", - 2,3); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 +1 " + "-1 +1 -1 ", + 2, 3); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_2by3_2, .description = "A two by three case") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 +1 " - "-1 +1 +1 ", - 2,3); - runRowTestCase(&testCase,false,true); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 +1 " + "-1 +1 +1 ", + 2, 3); + runRowTestCase(&testCase, false, true); + freeTestCase(&testCase); } Test(network, rowadd_2by3_3, .description = "A two by three case") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 +1 " - "+1 0 +1 ", - 2,3); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 +1 " + "+1 0 +1 ", + 2, 3); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_2by3_4, .description = "A two by three case") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 0 " - "+1 0 0 ", - 2,3); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 0 " + "+1 0 0 ", + 2, 3); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_2by3_5, .description = "A two by three case") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 0 " - "+0 +1 0 ", - 2,3); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 0 " + "+0 +1 0 ", + 2, 3); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_2by3_6, .description = "A two by three case") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 0 " - "+0 -1 0 ", - 2,3); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 0 " + "+0 -1 0 ", + 2, 3); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_2by3_7, .description = "A two by three case") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 0 " - "+0 +1 +1 ", - 2,3); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 0 " + "+0 +1 +1 ", + 2, 3); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_2by3_8, .description = "A two by three case") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 0 " - "+0 -1 +1 ", - 2,3); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 0 " + "+0 -1 +1 ", + 2, 3); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_3by6_1, .description = "A three by six case") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 0 0 0 0 " - "0 0 +1 -1 0 0 " - "-1 +1 -1 0 0 0 ", - 3,6); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 0 0 0 0 " + "0 0 +1 -1 0 0 " + "-1 +1 -1 0 0 0 ", + 3, 6); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_3by6_2, .description = "A three by six case") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 0 0 0 0 " - "0 0 +1 -1 0 0 " - "-1 +1 -1 0 0 +1 ", - 3,6); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 0 0 0 0 " + "0 0 +1 -1 0 0 " + "-1 +1 -1 0 0 +1 ", + 3, 6); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_3by2_1, .description = "A three by two case") { - DirectedTestCase testCase = stringToTestCase( - "+1 -1 " - "-1 +1 " - "+1 -1 ", - 3,2); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 -1 " + "-1 +1 " + "+1 -1 ", + 3, 2); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_3by1_1, .description = "A three by one case") { - DirectedTestCase testCase = stringToTestCase( - "+1 " - "-1 " - "+1 ", - 3,1); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 " + "-1 " + "+1 ", + 3, 1); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_3by3_1, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "1 -1 1 " - "-1 1 0 " - "0 1 -1 ", - 3,3); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 -1 1 " + "-1 1 0 " + "0 1 -1 ", + 3, 3); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_3by3_2, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "0 1 1 " - "0 1 0 " - "-1 -1 -1 ", - 3,3); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 1 1 " + "0 1 0 " + "-1 -1 -1 ", + 3, 3); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_3by3_3, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "-1 -1 0 " - "-1 0 -1 " - "-1 -1 0 ", - 3,3); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 -1 0 " + "-1 0 -1 " + "-1 -1 0 ", + 3, 3); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_3by3_4, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "-1 0 1 " - "0 -1 1 " - "-1 -1 -1 ", - 3,3); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 0 1 " + "0 -1 1 " + "-1 -1 -1 ", + 3, 3); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_3by3_5, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "1 1 -1 " - "-1 -1 0 " - "0 1 -1 ", - 3,3); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 1 -1 " + "-1 -1 0 " + "0 1 -1 ", + 3, 3); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_3by3_6, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "0 1 1 " - "1 1 0 " - "-1 1 -1 ", - 3,3); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 1 1 " + "1 1 0 " + "-1 1 -1 ", + 3, 3); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_3by3_7, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "1 -1 0 " - "0 1 -1 " - "-1 1 -1 ", - 3,3); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 -1 0 " + "0 1 -1 " + "-1 1 -1 ", + 3, 3); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_3by3_8, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "0 1 1 " - "-1 1 0 " - "-1 0 -1 ", - 3,3); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 1 1 " + "-1 1 0 " + "-1 0 -1 ", + 3, 3); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_3by3_9, .description = "A three by three case") { - DirectedTestCase testCase = stringToTestCase( - "0 -1 -1 " - "-1 -1 -1 " - "1 0 1 ", - 3,3); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 -1 -1 " + "-1 -1 -1 " + "1 0 1 ", + 3, 3); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_3by6_3, .description = "A three by six case") { - DirectedTestCase testCase = stringToTestCase( - "0 -1 0 0 0 -1 " - "0 0 0 -1 -1 -1 " - "-1 1 0 0 1 1 ", - 3,3); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 -1 0 0 0 -1 " + "0 0 0 -1 -1 -1 " + "-1 1 0 0 1 1 ", + 3, 3); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_3by6_4, .description = "A three by six case") { - DirectedTestCase testCase = stringToTestCase( - "1 1 -1 -1 0 0 " - "0 0 0 1 -1 -1 " - "0 -1 1 1 -1 0 ", - 3,3); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 1 -1 -1 0 0 " + "0 0 0 1 -1 -1 " + "0 -1 1 1 -1 0 ", + 3, 3); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_1, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "1 1 0 0 " - "-1 -1 -1 -1 " - "0 -1 -1 -1 " - "1 0 0 -1 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 1 0 0 " + "-1 -1 -1 -1 " + "0 -1 -1 -1 " + "1 0 0 -1 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_2, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "0 0 -1 -1 " - "1 1 -1 -1 " - "0 0 -1 -1 " - "0 -1 0 1 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 0 -1 -1 " + "1 1 -1 -1 " + "0 0 -1 -1 " + "0 -1 0 1 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_3, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "0 1 0 1 " - "-1 0 -1 0 " - "0 0 1 1 " - "0 -1 -1 1 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 1 0 1 " + "-1 0 -1 0 " + "0 0 1 1 " + "0 -1 -1 1 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_4, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "1 0 -1 0 " - "0 1 0 0 " - "-1 -1 1 1 " - "0 -1 -1 1 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 0 -1 0 " + "0 1 0 0 " + "-1 -1 1 1 " + "0 -1 -1 1 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_5, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "-1 0 -1 1 " - "1 0 0 -1 " - "-1 0 -1 0 " - "1 1 1 0 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 0 -1 1 " + "1 0 0 -1 " + "-1 0 -1 0 " + "1 1 1 0 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_6, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "1 0 -1 1 " - "0 1 -1 0 " - "0 -1 1 -1 " - "-1 -1 0 1 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 0 -1 1 " + "0 1 -1 0 " + "0 -1 1 -1 " + "-1 -1 0 1 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_7, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "1 0 -1 1 " - "0 1 -1 0 " - "0 -1 1 -1 " - "-1 -1 0 1 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 0 -1 1 " + "0 1 -1 0 " + "0 -1 1 -1 " + "-1 -1 0 1 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_8, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "-1 0 1 1 " - "1 -1 -1 0 " - "-1 1 1 1 " - "1 0 0 -1 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 0 1 1 " + "1 -1 -1 0 " + "-1 1 1 1 " + "1 0 0 -1 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_9, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "1 -1 0 0 " - "-1 1 0 -1 " - "0 1 1 -1 " - "-1 0 1 0 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 -1 0 0 " + "-1 1 0 -1 " + "0 1 1 -1 " + "-1 0 1 0 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_10, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "-1 -1 1 -1 " - "-1 0 0 -1 " - "0 1 0 1 " - "0 -1 0 0 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 -1 1 -1 " + "-1 0 0 -1 " + "0 1 0 1 " + "0 -1 0 0 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_11, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "1 1 1 0 " - "-1 -1 0 1 " - "-1 0 0 1 " - "0 0 1 1 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 1 1 0 " + "-1 -1 0 1 " + "-1 0 0 1 " + "0 0 1 1 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_12, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "-1 -1 0 -1 " - "0 1 0 1 " - "-1 0 1 0 " - "0 0 -1 -1 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 -1 0 -1 " + "0 1 0 1 " + "-1 0 1 0 " + "0 0 -1 -1 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_13, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "0 -1 -1 1 " - "-1 0 0 1 " - "1 1 1 -1 " - "1 0 -1 -1 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 -1 -1 1 " + "-1 0 0 1 " + "1 1 1 -1 " + "1 0 -1 -1 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_14, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "1 1 1 1 " - "1 1 0 1 " - "0 1 1 0 " - "0 1 1 1 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 1 1 1 " + "1 1 0 1 " + "0 1 1 0 " + "0 1 1 1 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_15, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "0 -1 -1 0 " - "1 1 0 1 " - "1 0 -1 0 " - "0 1 0 1 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 -1 -1 0 " + "1 1 0 1 " + "1 0 -1 0 " + "0 1 0 1 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_16, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "-1 -1 1 0 " - "-1 0 0 -1 " - "0 -1 0 1 " - "-1 -1 0 0 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 -1 1 0 " + "-1 0 0 -1 " + "0 -1 0 1 " + "-1 -1 0 0 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_17, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "-1 -1 -1 0 " - "-1 -1 0 0 " - "0 -1 0 1 " - "0 1 1 -1 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 -1 -1 0 " + "-1 -1 0 0 " + "0 -1 0 1 " + "0 1 1 -1 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_4by4_18, .description = "A four by four case") { - DirectedTestCase testCase = stringToTestCase( - "0 -1 0 1 " - "1 0 -1 0 " - "-1 0 1 1 " - "-1 1 0 -1 ", - 4,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 -1 0 1 " + "1 0 -1 0 " + "-1 0 1 1 " + "-1 1 0 -1 ", + 4, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_5by5_1, .description = "A five by five case") { - DirectedTestCase testCase = stringToTestCase( - "1 -1 1 -1 1 " - "0 0 1 -1 0 " - "-1 0 0 1 0 " - "0 0 1 0 1 " - "0 -1 0 1 1 ", - 5,5); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 -1 1 -1 1 " + "0 0 1 -1 0 " + "-1 0 0 1 0 " + "0 0 1 0 1 " + "0 -1 0 1 1 ", + 5, 5); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_5by5_2, .description = "A five by five case") { - DirectedTestCase testCase = stringToTestCase( - "1 -1 0 -1 -1 " - "0 -1 0 -1 -1 " - "0 0 1 -1 0 " - "0 -1 0 -1 -1 " - "-1 1 1 0 0 ", - 5,5); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "1 -1 0 -1 -1 " + "0 -1 0 -1 -1 " + "0 0 1 -1 0 " + "0 -1 0 -1 -1 " + "-1 1 1 0 0 ", + 5, 5); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_5by5_3, .description = "A five by five case") { - DirectedTestCase testCase = stringToTestCase( - "-1 0 1 0 -1 " - "0 1 1 0 -1 " - "1 0 -1 0 1 " - "-1 0 1 0 0 " - "1 0 -1 0 1 ", - 5,5); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "-1 0 1 0 -1 " + "0 1 1 0 -1 " + "1 0 -1 0 1 " + "-1 0 1 0 0 " + "1 0 -1 0 1 ", + 5, 5); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_5by5_4, .description = "A five by five case") { - DirectedTestCase testCase = stringToTestCase( - "0 -1 1 0 0 " - "0 1 -1 1 0 " - "0 -1 1 0 0 " - "1 -1 1 0 0 " - "0 1 0 1 1 ", - 5,5); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 -1 1 0 0 " + "0 1 -1 1 0 " + "0 -1 1 0 0 " + "1 -1 1 0 0 " + "0 1 0 1 1 ", + 5, 5); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_5by5_5, .description = "A five by five case") { - DirectedTestCase testCase = stringToTestCase( - "0 0 1 0 1 " - "1 0 0 1 -1 " - "1 -1 1 1 0 " - "0 0 -1 0 -1 " - "0 0 1 0 1 ", - 5,5); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 0 1 0 1 " + "1 0 0 1 -1 " + "1 -1 1 1 0 " + "0 0 -1 0 -1 " + "0 0 1 0 1 ", + 5, 5); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } Test(network, rowadd_8by4, .description = "A eight by four case") { - DirectedTestCase testCase = stringToTestCase( - "0 0 0 0 " - "1 0 1 0 " - "-1 1 -1 -1 " - "1 0 1 1 " - "1 -1 1 0 " - "1 -1 0 0 " - "1 1 -1 1 " - "0 0 1 0 ", - 8,4); - runRowTestCase(&testCase,false,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "0 0 0 0 " + "1 0 1 0 " + "-1 1 -1 -1 " + "1 0 1 1 " + "1 -1 1 0 " + "1 -1 0 0 " + "1 1 -1 1 " + "0 0 1 0 ", + 8, 4); + runRowTestCase(&testCase, false, false); + freeTestCase(&testCase); } + Test(network, rowadd_singlerigid_1, .description = "Updating a single rigid member") { - DirectedTestCase testCase = stringToTestCase( - "+1 0 +1 " - "+1 +1 0 " - "0 -1 +1 " - "+1 +1 0 ", - 4,3); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 0 +1 " + "+1 +1 0 " + "0 -1 +1 " + "+1 +1 0 ", + 4, 3); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_singlerigid_2, .description = "Updating a single rigid member") { - DirectedTestCase testCase = stringToTestCase( - "+1 0 +1 " - "+1 +1 0 " - "0 -1 +1 " - "-1 -1 0 ", - 4,3); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 0 +1 " + "+1 +1 0 " + "0 -1 +1 " + "-1 -1 0 ", + 4, 3); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_singlerigid_3, .description = "Updating a single rigid member") { - DirectedTestCase testCase = stringToTestCase( - "+1 0 +1 " - "+1 +1 0 " - "0 -1 +1 " - "-1 +1 0 ", - 4,3); - runRowTestCase(&testCase,false,true); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 0 +1 " + "+1 +1 0 " + "0 -1 +1 " + "-1 +1 0 ", + 4, 3); + runRowTestCase(&testCase, false, true); + freeTestCase(&testCase); } Test(network, rowadd_singlerigid_4, .description = "Updating a single rigid member") { - DirectedTestCase testCase = stringToTestCase( - "+1 0 +1 " - "-1 -1 -1 " - "0 +1 +1 " - "+1 +1 +1 ", - 4,3); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 0 +1 " + "-1 -1 -1 " + "0 +1 +1 " + "+1 +1 +1 ", + 4, 3); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_singlerigid_5, .description = "Updating a single rigid member") { - DirectedTestCase testCase = stringToTestCase( - "+1 0 +1 " - "-1 -1 -1 " - "0 +1 +1 " - "-1 -1 -1 ", - 4,3); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 0 +1 " + "-1 -1 -1 " + "0 +1 +1 " + "-1 -1 -1 ", + 4, 3); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_singlerigid_6, .description = "Updating a single rigid member") { - DirectedTestCase testCase = stringToTestCase( - "+1 0 +1 " - "-1 -1 -1 " - "0 +1 +1 " - "-1 +1 -1 ", - 4,3); - runRowTestCase(&testCase,false,true); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 0 +1 " + "-1 -1 -1 " + "0 +1 +1 " + "-1 +1 -1 ", + 4, 3); + runRowTestCase(&testCase, false, true); + freeTestCase(&testCase); } Test(network, rowadd_singlerigid_7, .description = "Updating a single rigid member") { - DirectedTestCase testCase = stringToTestCase( - "+1 +1 0 0 +1 " - "+1 0 +1 0 0 " - "0 -1 +1 +1 -1 " - "0 0 0 -1 +1 " - "+1 +1 0 0 0 ", - 5,5); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 +1 0 0 +1 " + "+1 0 +1 0 0 " + "0 -1 +1 +1 -1 " + "0 0 0 -1 +1 " + "+1 +1 0 0 0 ", + 5, 5); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } Test(network, rowadd_singlerigid_8, .description = "Updating a single rigid member") { - DirectedTestCase testCase = stringToTestCase( - "+1 +1 0 0 +1 " - "+1 0 +1 0 0 " - "0 -1 +1 +1 -1 " - "0 0 0 -1 +1 " - "+1 +1 0 0 0 " - "+1 0 +1 +1 0 ", - 6,5); - runRowTestCase(&testCase,true,false); - freeTestCase(&testCase); + DirectedTestCase testCase = stringToTestCase( + "+1 +1 0 0 +1 " + "+1 0 +1 0 0 " + "0 -1 +1 +1 -1 " + "0 0 0 -1 +1 " + "+1 +1 0 0 0 " + "+1 0 +1 +1 0 ", + 6, 5); + runRowTestCase(&testCase, true, false); + freeTestCase(&testCase); } //TODO: test interleaved addition, test using random sampling + test erdos-renyi generated graphs \ No newline at end of file From 5621e7fae1de65418f795b6ad5cd8ed201ac4149 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Wed, 15 May 2024 10:40:33 +0200 Subject: [PATCH 06/63] Add network.c and network.h to Makefile --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index bf00461dea..3de2155e5d 100644 --- a/Makefile +++ b/Makefile @@ -860,6 +860,7 @@ SCIPLIBOBJ = scip/boundstore.o \ scip/misc.o \ scip/misc_linear.o \ scip/misc_rowprep.o \ + scip/network.o \ scip/nlhdlr.o \ scip/nlp.o \ scip/nlpi.o \ From a73d7a3c6aa36b864efd4c3318dab59e217fe835 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Wed, 15 May 2024 13:40:49 +0200 Subject: [PATCH 07/63] Fix min() and max() that are already defined on windows --- src/scip/network.c | 56 +++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 69dcbdbb9d..f523b7d2eb 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -2532,7 +2532,7 @@ static SCIP_RETCODE mergeGivenMemberIntoParent( return SCIP_OKAY; } -static int max( +static int maxValue( int a, int b ) @@ -2976,14 +2976,14 @@ static SCIP_RETCODE constructReducedDecomposition( int newSize = largestMemberID(dec);//Is this sufficient? if( newSize > newCol->memReducedMembers ) { - int newArraySize = max(2 * newCol->memReducedMembers, newSize); + int newArraySize = maxValue(2 * newCol->memReducedMembers, newSize); SCIP_CALL( SCIPreallocBlockMemoryArray(dec->env, &newCol->reducedMembers, newCol->memReducedMembers, newArraySize)); newCol->memReducedMembers = newArraySize; } if( newSize > newCol->memMemberInformation ) { - int updatedSize = max(2 * newCol->memMemberInformation, newSize); + int updatedSize = maxValue(2 * newCol->memMemberInformation, newSize); SCIP_CALL( SCIPreallocBlockMemoryArray(dec->env, &newCol->memberInformation, newCol->memMemberInformation, updatedSize)); for( int i = newCol->memMemberInformation; i < updatedSize; ++i ) @@ -2997,7 +2997,7 @@ static SCIP_RETCODE constructReducedDecomposition( int numComponents = numConnectedComponents(dec); if( numComponents > newCol->memReducedComponents ) { - int updatedSize = max(2 * newCol->memReducedComponents, numComponents); + int updatedSize = maxValue(2 * newCol->memReducedComponents, numComponents); SCIP_CALL( SCIPreallocBlockMemoryArray(dec->env, &newCol->reducedComponents, newCol->memReducedComponents, updatedSize)); newCol->memReducedComponents = updatedSize; @@ -3006,7 +3006,7 @@ static SCIP_RETCODE constructReducedDecomposition( int numMembers = getNumMembers(dec); if( newCol->memCreateReducedMembersCallStack < numMembers ) { - int updatedSize = max(2 * newCol->memCreateReducedMembersCallStack, numMembers); + int updatedSize = maxValue(2 * newCol->memCreateReducedMembersCallStack, numMembers); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->createReducedMembersCallStack, newCol->memCreateReducedMembersCallStack, updatedSize)); newCol->memCreateReducedMembersCallStack = updatedSize; @@ -3056,7 +3056,7 @@ static SCIP_RETCODE constructReducedDecomposition( if( newCol->memChildrenStorage < numTotalChildren ) { - int newMemSize = max(newCol->memChildrenStorage * 2, numTotalChildren); + int newMemSize = maxValue(newCol->memChildrenStorage * 2, numTotalChildren); SCIP_CALL( SCIPreallocBlockMemoryArray(dec->env, &newCol->childrenStorage, newCol->memChildrenStorage, newMemSize)); newCol->memChildrenStorage = newMemSize; @@ -3281,7 +3281,7 @@ static SCIP_RETCODE computeLeafMembers( { if( newCol->numReducedMembers > newCol->memLeafMembers ) { - int newSize = max(newCol->numReducedMembers, 2 * newCol->memLeafMembers); + int newSize = maxValue(newCol->numReducedMembers, 2 * newCol->memLeafMembers); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->leafMembers, newCol->memLeafMembers, newSize)); newCol->memLeafMembers = newSize; } @@ -5596,7 +5596,7 @@ SCIP_Bool SCIPnetcoladdRemainsNetwork(const SCIP_NETCOLADD* newCol) } -static int min( +static int minValue( int a, int b ) @@ -6015,13 +6015,13 @@ static SCIP_RETCODE constructRowReducedDecomposition( int newSize = largestMemberID(dec);//Is this sufficient? if( newSize > newRow->memReducedMembers ) { - int updatedSize = max(2 * newRow->memReducedMembers, newSize); + int updatedSize = maxValue(2 * newRow->memReducedMembers, newSize); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->reducedMembers, newRow->memReducedMembers, updatedSize)); newRow->memReducedMembers = updatedSize; } if( newSize > newRow->memMemberInformation ) { - int updatedSize = max(2 * newRow->memMemberInformation, newSize); + int updatedSize = maxValue(2 * newRow->memMemberInformation, newSize); SCIP_CALL( SCIPreallocBlockMemoryArray(dec->env, &newRow->memberInformation, newRow->memMemberInformation, updatedSize)); for( int i = newRow->memMemberInformation; i < updatedSize; ++i ) @@ -6035,7 +6035,7 @@ static SCIP_RETCODE constructRowReducedDecomposition( int numComponents = numConnectedComponents(dec); if( numComponents > newRow->memReducedComponents ) { - int updatedSize = max(2 * newRow->memReducedComponents, numComponents); + int updatedSize = maxValue(2 * newRow->memReducedComponents, numComponents); SCIP_CALL( SCIPreallocBlockMemoryArray(dec->env, &newRow->reducedComponents, newRow->memReducedComponents, updatedSize)); newRow->memReducedComponents = updatedSize; @@ -6044,7 +6044,7 @@ static SCIP_RETCODE constructRowReducedDecomposition( int numMembers = getNumMembers(dec); if( newRow->memCreateReducedMembersCallstack < numMembers ) { - int updatedSize = max(2 * newRow->memCreateReducedMembersCallstack, numMembers); + int updatedSize = maxValue(2 * newRow->memCreateReducedMembersCallstack, numMembers); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->createReducedMembersCallstack, newRow->memCreateReducedMembersCallstack, updatedSize)); newRow->memCreateReducedMembersCallstack = updatedSize; @@ -6094,7 +6094,7 @@ static SCIP_RETCODE constructRowReducedDecomposition( if( newRow->memChildrenStorage < numTotalChildren ) { - int newMemSize = max(newRow->memChildrenStorage * 2, numTotalChildren); + int newMemSize = maxValue(newRow->memChildrenStorage * 2, numTotalChildren); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->childrenStorage, (size_t) newRow->memChildrenStorage, newMemSize)); newRow->memChildrenStorage = newMemSize; @@ -6204,7 +6204,7 @@ static SCIP_RETCODE createReducedDecompositionCutArcs( spqr_arc maxArcID = largestArcID(dec); if( maxArcID > newRow->memIsArcCut ) { - int newSize = max(maxArcID, 2 * newRow->memIsArcCut); + int newSize = maxValue(maxArcID, 2 * newRow->memIsArcCut); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->isArcCut, newRow->memIsArcCut, newSize)); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->isArcCutReversed, newRow->memIsArcCut, newSize)); for( int i = newRow->memIsArcCut; i < newSize; ++i ) @@ -6225,7 +6225,7 @@ static SCIP_RETCODE createReducedDecompositionCutArcs( int numNeededArcs = newRow->numDecompositionColumnArcs * 4; if( numNeededArcs > newRow->memCutArcs ) { - int newSize = max(newRow->memCutArcs * 2, numNeededArcs); + int newSize = maxValue(newRow->memCutArcs * 2, numNeededArcs); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->cutArcs, newRow->memCutArcs, newSize)); newRow->memCutArcs = newSize; } @@ -6255,7 +6255,7 @@ static SCIP_RETCODE determineLeafReducedMembers( { if( newRow->numDecompositionColumnArcs > newRow->memLeafMembers ) { - int updatedSize = max(newRow->numDecompositionColumnArcs, 2 * newRow->memLeafMembers); + int updatedSize = maxValue(newRow->numDecompositionColumnArcs, 2 * newRow->memLeafMembers); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->leafMembers, newRow->memLeafMembers, updatedSize)); newRow->memLeafMembers = updatedSize; } @@ -6284,7 +6284,7 @@ static SCIP_RETCODE allocateRigidSearchMemory( int maxNumNodes = 2 * dec->numArcs; if( maxNumNodes > newRow->memNodeColors ) { - int newSize = max(2 * newRow->memNodeColors, maxNumNodes); + int newSize = maxValue(2 * newRow->memNodeColors, maxNumNodes); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->nodeColors, newRow->memNodeColors, newSize)); for( int i = newRow->memNodeColors; i < newSize; ++i ) { @@ -6295,21 +6295,21 @@ static SCIP_RETCODE allocateRigidSearchMemory( if( totalNumNodes > newRow->memArticulationNodes ) { - int newSize = max(2 * newRow->memArticulationNodes, totalNumNodes); + int newSize = maxValue(2 * newRow->memArticulationNodes, totalNumNodes); SCIP_CALL( SCIPreallocBlockMemoryArray(dec->env, &newRow->articulationNodes, newRow->memArticulationNodes, newSize)); newRow->memArticulationNodes = newSize; } if( totalNumNodes > newRow->memNodeSearchInfo ) { - int newSize = max(2 * newRow->memNodeSearchInfo, totalNumNodes); + int newSize = maxValue(2 * newRow->memNodeSearchInfo, totalNumNodes); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->articulationNodeSearchInfo, newRow->memNodeSearchInfo, newSize)); newRow->memNodeSearchInfo = newSize; } if( totalNumNodes > newRow->memCrossingPathCount ) { - int newSize = max(2 * newRow->memCrossingPathCount, totalNumNodes); + int newSize = maxValue(2 * newRow->memCrossingPathCount, totalNumNodes); SCIP_CALL( SCIPreallocBlockMemoryArray(dec->env, &newRow->crossingPathCount, newRow->memCrossingPathCount, newSize)); newRow->memCrossingPathCount = newSize; @@ -6321,20 +6321,20 @@ static SCIP_RETCODE allocateRigidSearchMemory( dec);//TODO: only update the stack sizes of the following when needed? The preallocation might be causing performance problems if( largestID > newRow->memIntersectionDFSData ) { - int newSize = max(2 * newRow->memIntersectionDFSData, largestID); + int newSize = maxValue(2 * newRow->memIntersectionDFSData, largestID); SCIP_CALL( SCIPreallocBlockMemoryArray(dec->env, &newRow->intersectionDFSData, newRow->memIntersectionDFSData, newSize)); newRow->memIntersectionDFSData = newSize; } if( largestID > newRow->memColorDFSData ) { - int newSize = max(2 * newRow->memColorDFSData, largestID); + int newSize = maxValue(2 * newRow->memColorDFSData, largestID); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->colorDFSData, newRow->memColorDFSData, newSize)); newRow->memColorDFSData = newSize; } if( largestID > newRow->memArtDFSData ) { - int newSize = max(2 * newRow->memArtDFSData, largestID); + int newSize = maxValue(2 * newRow->memArtDFSData, largestID); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->artDFSData, newRow->memArtDFSData, newSize)); newRow->memArtDFSData = newSize; } @@ -6346,7 +6346,7 @@ static SCIP_RETCODE allocateRigidSearchMemory( if( largestID > newRow->memIntersectionPathDepth ) { - int newSize = max(2 * newRow->memIntersectionPathDepth, largestID); + int newSize = maxValue(2 * newRow->memIntersectionPathDepth, largestID); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->intersectionPathDepth, newRow->memIntersectionPathDepth, newSize)); for( int i = newRow->memIntersectionPathDepth; i < newSize; ++i ) @@ -6361,7 +6361,7 @@ static SCIP_RETCODE allocateRigidSearchMemory( } if( largestID > newRow->memIntersectionPathParent ) { - int newSize = max(2 * newRow->memIntersectionPathParent, largestID); + int newSize = maxValue(2 * newRow->memIntersectionPathParent, largestID); SCIP_CALL( SCIPreallocBlockMemoryArray(dec->env, &newRow->intersectionPathParent, newRow->memIntersectionPathParent, newSize)); @@ -6801,7 +6801,7 @@ static void articulationPoints( } else { - nodeInfo[node].low = min(nodeInfo[node].low, nodeInfo[otherNode].discoveryTime); + nodeInfo[node].low = minValue(nodeInfo[node].low, nodeInfo[otherNode].discoveryTime); } } } @@ -6815,7 +6815,7 @@ static void articulationPoints( spqr_node current_node = callStack[depth].node; spqr_node other_node = callStack[depth + 1].node; - nodeInfo[current_node].low = min(nodeInfo[current_node].low, + nodeInfo[current_node].low = minValue(nodeInfo[current_node].low, nodeInfo[other_node].low); if( depth != 0 && !callStack[depth].isAP && @@ -7487,7 +7487,7 @@ static SCIP_RETCODE allocateTreeSearchMemory( int necessarySpace = newRow->numReducedMembers; if( necessarySpace > newRow->memMergeTreeCallData ) { - int updatedSize = max(2 * newRow->memMergeTreeCallData, necessarySpace); + int updatedSize = maxValue(2 * newRow->memMergeTreeCallData, necessarySpace); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->mergeTreeCallData, newRow->memMergeTreeCallData, updatedSize)); newRow->memMergeTreeCallData = updatedSize; From 0276506432dcac03c2f069cfe2d840ddc2610c25 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Wed, 15 May 2024 14:43:19 +0200 Subject: [PATCH 08/63] Fix lints --- src/scip/network.c | 263 ++++++++++++++++++++------------------------- 1 file changed, 118 insertions(+), 145 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index f523b7d2eb..f50e1eab4b 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -216,11 +216,9 @@ struct SCIP_Netmatdec SPQRNetworkDecompositionNode* nodes; int memRows; - int numRows; spqr_arc* rowArcs; int memColumns; - int numColumns; spqr_arc* columnArcs; SCIP* env; @@ -1087,7 +1085,7 @@ SCIP_RETCODE SCIPnetmatdecCreate( assert(initialMemArcs > 0); dec->memArcs = initialMemArcs; dec->numArcs = 0; - SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->arcs, (size_t) dec->memArcs)); + SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->arcs, dec->memArcs)); for( spqr_arc i = 0; i < dec->memArcs; ++i ) { dec->arcs[i].arcListNode.next = i + 1; @@ -1103,7 +1101,7 @@ SCIP_RETCODE SCIPnetmatdecCreate( assert(initialMemMembers > 0); dec->memMembers = initialMemMembers; dec->numMembers = 0; - SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->members, (size_t) dec->memMembers)); + SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->members, dec->memMembers)); } //Initialize node array data @@ -1112,13 +1110,13 @@ SCIP_RETCODE SCIPnetmatdecCreate( assert(initialMemNodes > 0); dec->memNodes = initialMemNodes; dec->numNodes = 0; - SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->nodes, (size_t) dec->memNodes)); + SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->nodes, dec->memNodes)); } //Initialize mappings for rows { dec->memRows = nrows; - SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->rowArcs, (size_t) dec->memRows)); + SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->rowArcs, dec->memRows)); for( int i = 0; i < dec->memRows; ++i ) { dec->rowArcs[i] = SPQR_INVALID_ARC; @@ -1127,8 +1125,7 @@ SCIP_RETCODE SCIPnetmatdecCreate( //Initialize mappings for columns { dec->memColumns = ncols; - dec->numColumns = 0; - SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->columnArcs, (size_t) dec->memColumns)); + SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->columnArcs, dec->memColumns)); for( int i = 0; i < dec->memColumns; ++i ) { dec->columnArcs[i] = SPQR_INVALID_ARC; @@ -1225,15 +1222,15 @@ static SCIP_RETCODE createArc( assert(pArc); assert(SPQRmemberIsInvalid(member) || memberIsRepresentative(dec, member)); - spqr_arc index = dec->firstFreeArc; - if( SPQRarcIsValid(index)) + spqr_arc idx = dec->firstFreeArc; + if( SPQRarcIsValid(idx)) { - dec->firstFreeArc = dec->arcs[index].arcListNode.next; + dec->firstFreeArc = dec->arcs[idx].arcListNode.next; } else { //Enlarge array, no free nodes in arc list int newSize = 2 * dec->memArcs; - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &dec->arcs, dec->memArcs, (size_t) newSize)); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &dec->arcs, dec->memArcs, newSize)); for( int i = dec->memArcs + 1; i < newSize; ++i ) { dec->arcs[i].arcListNode.next = i + 1; @@ -1241,24 +1238,24 @@ static SCIP_RETCODE createArc( } dec->arcs[newSize - 1].arcListNode.next = SPQR_INVALID_ARC; dec->firstFreeArc = dec->memArcs + 1; - index = dec->memArcs; + idx = dec->memArcs; dec->memArcs = newSize; } - dec->arcs[index].tail = SPQR_INVALID_NODE; - dec->arcs[index].head = SPQR_INVALID_NODE; - dec->arcs[index].member = member; - dec->arcs[index].childMember = SPQR_INVALID_MEMBER; - dec->arcs[index].reversed = reversed; + dec->arcs[idx].tail = SPQR_INVALID_NODE; + dec->arcs[idx].head = SPQR_INVALID_NODE; + dec->arcs[idx].member = member; + dec->arcs[idx].childMember = SPQR_INVALID_MEMBER; + dec->arcs[idx].reversed = reversed; - dec->arcs[index].headArcListNode.next = SPQR_INVALID_ARC; - dec->arcs[index].headArcListNode.previous = SPQR_INVALID_ARC; - dec->arcs[index].tailArcListNode.next = SPQR_INVALID_ARC; - dec->arcs[index].tailArcListNode.previous = SPQR_INVALID_ARC; + dec->arcs[idx].headArcListNode.next = SPQR_INVALID_ARC; + dec->arcs[idx].headArcListNode.previous = SPQR_INVALID_ARC; + dec->arcs[idx].tailArcListNode.next = SPQR_INVALID_ARC; + dec->arcs[idx].tailArcListNode.previous = SPQR_INVALID_ARC; dec->numArcs++; - *pArc = index; + *pArc = idx; return SCIP_OKAY; } @@ -1774,7 +1771,7 @@ static int decompositionGetFundamentalCycleRows( int num_rows = 0; FindCycleCall* callStack; - SCIP_RETCODE result = SCIPallocBlockMemoryArray(dec->env, &callStack, (size_t) dec->memRows); + SCIP_RETCODE result = SCIPallocBlockMemoryArray(dec->env, &callStack, dec->memRows); if( result != SCIP_OKAY ) { return -1; @@ -1784,7 +1781,7 @@ static int decompositionGetFundamentalCycleRows( callStack[0].reversed = FALSE; SCIP_Bool* nodeVisited; - result = SCIPallocBlockMemoryArray(dec->env, &nodeVisited, (size_t) dec->numNodes); + result = SCIPallocBlockMemoryArray(dec->env, &nodeVisited, dec->numNodes); if( result != SCIP_OKAY ) { return -1; @@ -1800,7 +1797,7 @@ static int decompositionGetFundamentalCycleRows( spqr_arc nodeArc; } DFSCallData; DFSCallData* pathSearchCallStack; - result = SCIPallocBlockMemoryArray(dec->env, &pathSearchCallStack, (size_t) dec->numNodes); + result = SCIPallocBlockMemoryArray(dec->env, &pathSearchCallStack, dec->numNodes); if( result != SCIP_OKAY ) { return -1; @@ -1995,7 +1992,7 @@ SCIP_Bool SCIPnetmatdecVerifyCycle( array[i].row = pathrowstorage[i]; array[i].reversed = pathsignstorage[i]; } - qsort(array, (size_t) num_rows, sizeof(Nonzero), qsort_comparison); + qsort(array, num_rows, sizeof(Nonzero), qsort_comparison); Nonzero* secondArray; code = SCIPallocBufferArray(scip, &secondArray, num_rows); @@ -2010,7 +2007,7 @@ SCIP_Bool SCIPnetmatdecVerifyCycle( secondArray[i].reversed = nonzvals[i] < 0.0; } - qsort(secondArray, (size_t) num_rows, sizeof(Nonzero), qsort_comparison); + qsort(secondArray, num_rows, sizeof(Nonzero), qsort_comparison); SCIP_Bool good = TRUE; for( int i = 0; i < num_rows; ++i ) @@ -2297,7 +2294,7 @@ static void reorderComponent( { break; } - } while( TRUE ); + } while( TRUE ); /*lint !e506*/ dec->members[newRoot].parentMember = SPQR_INVALID_MEMBER; dec->members[newRoot].markerToParent = SPQR_INVALID_ARC; dec->members[newRoot].markerOfParent = SPQR_INVALID_ARC; @@ -2500,8 +2497,12 @@ static SCIP_RETCODE mergeGivenMemberIntoParent( removeArcFromMemberArcList(dec, parentToChild, parent); removeArcFromMemberArcList(dec, childToParent, member); - spqr_node parentArcNodes[2] = {findEffectiveArcTail(dec, parentToChild), findEffectiveArcHead(dec, parentToChild)}; - spqr_node childArcNodes[2] = {findEffectiveArcTail(dec, childToParent), findEffectiveArcHead(dec, childToParent)}; + spqr_node parentTail = findEffectiveArcTail(dec, parentToChild) ; + spqr_node parentHead = findEffectiveArcHead(dec, parentToChild); + spqr_node childTail = findEffectiveArcTail(dec, childToParent); + spqr_node childHead = findEffectiveArcHead(dec, childToParent); + spqr_node parentArcNodes[2] = { parentTail, parentHead }; + spqr_node childArcNodes[2] = { childTail, childHead }; clearArcHeadAndTail(dec, parentToChild); clearArcHeadAndTail(dec, childToParent); @@ -2596,12 +2597,12 @@ typedef enum static SCIP_Bool isInto(MemberPathType type) { - return type < 2; + return type == INTO_HEAD || type == INTO_TAIL; } static SCIP_Bool isHead(MemberPathType type) { - return ( type & 1 ) == 0; + return type == INTO_HEAD || type == OUT_HEAD; } typedef struct @@ -2924,7 +2925,7 @@ static reduced_member_id createReducedMembersToRoot( *depthMinimizer = reducedMember; } } - while( TRUE ) + while( TRUE ) /*lint !e716*/ { --callDepth; if( callDepth < 0 ) break; @@ -3247,7 +3248,7 @@ newColUpdateColInformation( SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->decompositionRowArcs, newCol->memDecompositionRowArcs, newNumArcs)); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->decompositionArcReversed, - (size_t) newCol->memDecompositionRowArcs, newNumArcs)); + newCol->memDecompositionRowArcs, newNumArcs)); newCol->memDecompositionRowArcs = newNumArcs; } newCol->decompositionRowArcs[newCol->numDecompositionRowArcs] = rowArc; @@ -3260,9 +3261,9 @@ newColUpdateColInformation( { int newNumArcs = newCol->memNewRowArcs == 0 ? 8 : 2 * newCol->memNewRowArcs; SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->newRowArcs, - (size_t) newCol->memNewRowArcs, newNumArcs)); + newCol->memNewRowArcs, newNumArcs)); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->newRowArcReversed, - (size_t) newCol->memNewRowArcs, newNumArcs)); + newCol->memNewRowArcs, newNumArcs)); newCol->memNewRowArcs = newNumArcs; } newCol->newRowArcs[newCol->numNewRowArcs] = nonzeroRows[i]; @@ -3361,9 +3362,8 @@ static void determineRigidPath( static void determineSingleRigidType( SCIP_NETMATDEC* dec, SCIP_NETCOLADD* newCol, - reduced_member_id reducedMember, - spqr_member member -) + reduced_member_id reducedMember + ) { assert(dec); assert(newCol); @@ -3409,7 +3409,7 @@ static void determineSingleComponentType( { case SPQR_MEMBERTYPE_RIGID: { - determineSingleRigidType(dec, newCol, reducedMember, member); + determineSingleRigidType(dec, newCol, reducedMember); break; } case SPQR_MEMBERTYPE_PARALLEL: @@ -3640,38 +3640,35 @@ static void determinePathParallelType( } SCIP_Bool swapHeadTail = arcPresent != inSameDirection; - MemberPathType currentType; switch( previousType ) { case INTO_HEAD: { - currentType = swapHeadTail ? INTO_HEAD : INTO_TAIL; + redMem->pathType = swapHeadTail ? INTO_HEAD : INTO_TAIL; break; } case INTO_TAIL: { - currentType = swapHeadTail ? INTO_TAIL : INTO_HEAD; + redMem->pathType = swapHeadTail ? INTO_TAIL : INTO_HEAD; break; } case OUT_HEAD: { - currentType = swapHeadTail ? OUT_HEAD : OUT_TAIL; + redMem->pathType = swapHeadTail ? OUT_HEAD : OUT_TAIL; break; } case OUT_TAIL: { - currentType = swapHeadTail ? OUT_TAIL : OUT_HEAD; + redMem->pathType = swapHeadTail ? OUT_TAIL : OUT_HEAD; break; } } - redMem->pathType = currentType; } static void determinePathRigidType( SCIP_NETMATDEC* dec, SCIP_NETCOLADD* newCol, reduced_member_id reducedMember, - spqr_member member, MemberPathType previousType, spqr_arc source, spqr_arc target @@ -3991,7 +3988,7 @@ static void determinePathMemberType( { case SPQR_MEMBERTYPE_RIGID: { - determinePathRigidType(dec, newCol, reducedMember, member, previousType, source, target); + determinePathRigidType(dec, newCol, reducedMember, previousType, source, target); break; } case SPQR_MEMBERTYPE_PARALLEL: @@ -4007,10 +4004,9 @@ static void determinePathMemberType( case SPQR_MEMBERTYPE_LOOP: case SPQR_MEMBERTYPE_UNASSIGNED: { - assert(FALSE); - //In release, - + //In release newCol->remainsNetwork = FALSE; + assert(FALSE); break; } } @@ -4292,7 +4288,7 @@ static void propagateCycles( //The reduced root might be a leaf as well: we propagate it last reduced_member_id root = newCol->reducedComponents[j].root; - while( TRUE ) + while( TRUE ) /*lint !e716*/ { if( newCol->reducedMembers[root].numPropagatedChildren != newCol->reducedMembers[root].numChildren - 1 ) { @@ -4777,7 +4773,7 @@ static SCIP_RETCODE splitSeriesMerging( { break; } - } while( TRUE ); + } while( TRUE ); /*lint !e506*/ assert(getNumMemberArcs(dec, nonPathMember) >= 2); SCIP_Bool representativeIsTree = !arcIsTree(dec, exceptionArc1); if( SPQRarcIsValid(exceptionArc2)) @@ -4977,12 +4973,12 @@ static SCIP_RETCODE transformAndMergeParallel( spqr_member newMergedMember = SPQR_INVALID_MEMBER; if( nextIsParent ) { - mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, - source, newCol->reducedMembers[current].pathTargetArc, TRUE, &newMergedMember); + SCIP_CALL( mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, + source, newCol->reducedMembers[current].pathTargetArc, TRUE, &newMergedMember) ); } else { - mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, - newCol->reducedMembers[current].pathTargetArc, source, TRUE, &newMergedMember); + SCIP_CALL( mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, + newCol->reducedMembers[current].pathTargetArc, source, TRUE, &newMergedMember) ); } *mergedMember = newMergedMember; @@ -5102,12 +5098,12 @@ static SCIP_RETCODE transformAndMergeSeries( spqr_member newMergedMember = SPQR_INVALID_MEMBER; if( nextIsParent ) { - mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, - source, newCol->reducedMembers[current].pathTargetArc, TRUE, &newMergedMember); + SCIP_CALL( mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, + source, newCol->reducedMembers[current].pathTargetArc, TRUE, &newMergedMember) ); } else { - mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, - newCol->reducedMembers[current].pathTargetArc, source, TRUE, &newMergedMember); + SCIP_CALL( mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, + newCol->reducedMembers[current].pathTargetArc, source, TRUE, &newMergedMember) ); } *mergedMember = newMergedMember; @@ -5150,14 +5146,14 @@ static SCIP_RETCODE transformAndMergeRigid( if( nextIsParent ) { - mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, + SCIP_CALL( mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, source, newCol->reducedMembers[current].pathTargetArc, !redMem->reverseArcs, - &newMergedMember); + &newMergedMember) ); } else { - mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, + SCIP_CALL( mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, newCol->reducedMembers[current].pathTargetArc, source, !redMem->reverseArcs, - &newMergedMember); + &newMergedMember) ); } *mergedMember = newMergedMember; @@ -5265,7 +5261,6 @@ static SCIP_RETCODE transformPath( } static SCIP_RETCODE columnTransformSingleParallel( - SCIP_NETMATDEC* dec, SCIP_NETCOLADD* newCol, reduced_member_id reducedMemberId, spqr_member member, @@ -5342,9 +5337,8 @@ static SCIP_RETCODE columnTransformSingleRigid( if( SPQRarcIsValid(existingArcWithPath)) { SCIP_Bool isParent = FALSE; - spqr_member adjacentMember = arcIsMarker(dec, existingArcWithPath) ? findArcChildMember(dec, - existingArcWithPath) - : SPQR_INVALID_MEMBER; + spqr_member adjacentMember = arcIsMarker(dec, existingArcWithPath) ? + findArcChildMember(dec,existingArcWithPath) : SPQR_INVALID_MEMBER; if( existingArcWithPath == markerToParent(dec, member)) { adjacentMember = findMemberParent(dec, member); @@ -5408,7 +5402,7 @@ static SCIP_RETCODE columnTransformSingleRigid( dec->members[member].markerToParent = existingArcWithPath; dec->members[member].markerOfParent = parallelMarker; dec->arcs[existingArcWithPath].element = arcIsTree(dec, existingArcWithPath) ? MARKER_ROW_ELEMENT - : MARKER_COLUMN_ELEMENT;; + : MARKER_COLUMN_ELEMENT; dec->arcs[existingArcWithPath].childMember = adjacentParallel; } else @@ -5464,7 +5458,7 @@ static SCIP_RETCODE transformComponent( } case SPQR_MEMBERTYPE_PARALLEL: { - SCIP_CALL(columnTransformSingleParallel(dec, newCol, reducedMember, member, newColInfo)); + SCIP_CALL(columnTransformSingleParallel(newCol, reducedMember, member, newColInfo)); break; } case SPQR_MEMBERTYPE_LOOP: @@ -5473,6 +5467,7 @@ static SCIP_RETCODE transformComponent( SCIP_CALL(columnTransformSingleSeries(dec, newCol, reducedMember, member, newColInfo)); break; } + case SPQR_MEMBERTYPE_UNASSIGNED: default: { assert(FALSE); @@ -5962,7 +5957,7 @@ static reduced_member_id createRowReducedMembersToRoot( *depthMinimizer = reducedMember; } } - while( TRUE ) + while( TRUE ) /*lint !e716*/ { --callDepth; if( callDepth < 0 ) break; @@ -6575,7 +6570,7 @@ static SCIP_RETCODE zeroOutColorsExceptNeighbourhood( { COLOR_STATUS* neighbourColors; int degree = nodeDegree(dec, articulationNode); - SCIP_CALL(SCIPallocBlockMemoryArray(dec->env, &neighbourColors, (size_t) degree)); + SCIP_CALL(SCIPallocBlockMemoryArray(dec->env, &neighbourColors, degree)); { int i = 0; @@ -6755,7 +6750,7 @@ static void articulationPoints( const SCIP_Bool* arcRemoved = newRow->isArcCut; int rootChildren = 0; - spqr_node root_node = findArcHead(dec, getFirstMemberArc(dec, newRow->reducedMembers[reducedMember].member));; + spqr_node root_node = findArcHead(dec, getFirstMemberArc(dec, newRow->reducedMembers[reducedMember].member)); ArticulationPointCallStack* callStack = newRow->artDFSData; @@ -6806,7 +6801,7 @@ static void articulationPoints( } } - while( TRUE ) + while( TRUE ) /*lint !e716*/ { callStack[depth].arc = getNextNodeArc(dec, callStack[depth].arc, callStack[depth].node); if( callStack[depth].arc != getFirstNodeArc(dec, callStack[depth].node)) break; @@ -7289,6 +7284,7 @@ static RowReducedMemberType determineType( determineRigidType(dec, newRow, toCheckMember, markerToOther, otherMember, markerToCheck); break; } + case SPQR_MEMBERTYPE_LOOP: case SPQR_MEMBERTYPE_PARALLEL: { determineParallelType(dec, newRow, toCheckMember, markerToOther, otherMember, markerToCheck); @@ -7299,9 +7295,10 @@ static RowReducedMemberType determineType( determineSeriesType(dec, newRow, toCheckMember, markerToOther, otherMember, markerToCheck); break; } + case SPQR_MEMBERTYPE_UNASSIGNED: default: - assert(FALSE); newRow->reducedMembers[toCheckMember].type = TYPE_NOT_NETWORK; + assert(FALSE); } return newRow->reducedMembers[toCheckMember].type; @@ -7358,7 +7355,7 @@ static void propagateComponents( //The reduced root might be a leaf as well: we propagate it last reduced_member_id root = newRow->reducedComponents[j].root; - while( TRUE ) + while( TRUE ) /*lint !e716*/ { if( newRow->reducedMembers[root].numPropagatedChildren == newRow->reducedMembers[root].numChildren - 1 ) { @@ -7498,8 +7495,7 @@ static SCIP_RETCODE allocateTreeSearchMemory( static void determineSingleRowRigidType( SCIP_NETMATDEC* dec, SCIP_NETROWADD* newRow, - reduced_member_id reducedMember, - spqr_member member + reduced_member_id reducedMember ) { assert(newRow->reducedMembers[reducedMember].numCutArcs > @@ -7528,9 +7524,8 @@ static void determineSingleRowRigidType( static void determineSingleParallelType( SCIP_NETMATDEC* dec, SCIP_NETROWADD* newRow, - reduced_member_id reducedMember, - spqr_member member -) + reduced_member_id reducedMember + ) { SPQRRowReducedMember* redMember = &newRow->reducedMembers[reducedMember]; assert(cutArcIsValid(redMember->firstCutArc)); @@ -7564,10 +7559,8 @@ static void determineSingleParallelType( } static void determineSingleSeriesType( - SCIP_NETMATDEC* dec, SCIP_NETROWADD* newRow, - reduced_member_id reducedMember, - spqr_member member + reduced_member_id reducedMember ) { assert(newRow->reducedMembers[reducedMember].numCutArcs == 1); @@ -7870,7 +7863,7 @@ static SplitOrientation getRelativeOrientation( spqr_arc arcToNext ) { - switch( getMemberType(dec, newRow->reducedMembers[reducedId].member)) + switch( getMemberType(dec, newRow->reducedMembers[reducedId].member) ) { case SPQR_MEMBERTYPE_RIGID: return getRelativeOrientationRigid(dec, newRow, reducedId, arcToNext); @@ -7878,20 +7871,17 @@ static SplitOrientation getRelativeOrientation( return getRelativeOrientationParallel(dec, newRow, reducedId, arcToNext); case SPQR_MEMBERTYPE_SERIES: return getRelativeOrientationSeries(dec, newRow, reducedId, arcToNext); + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_UNASSIGNED: default: assert(FALSE); } - SplitOrientation orientation; - orientation.headSplit = FALSE; - orientation.otherIsSource = FALSE; - return orientation; } static void determineSplitTypeSeries( SCIP_NETMATDEC* dec, SCIP_NETROWADD* newRow, reduced_member_id reducedId, - spqr_member member, spqr_arc marker, SplitOrientation previousOrientation ) @@ -7940,7 +7930,6 @@ static void determineSplitTypeParallel( SCIP_NETMATDEC* dec, SCIP_NETROWADD* newRow, reduced_member_id reducedId, - spqr_member member, spqr_arc marker, SplitOrientation previousOrientation ) @@ -8129,17 +8118,20 @@ static void determineSplitTypeNext( } case SPQR_MEMBERTYPE_PARALLEL: { - determineSplitTypeParallel(dec, newRow, next, member, nextArc, orientation); + determineSplitTypeParallel(dec, newRow, next, nextArc, orientation); break; } case SPQR_MEMBERTYPE_SERIES: { - determineSplitTypeSeries(dec, newRow, next, member, nextArc, orientation); + determineSplitTypeSeries(dec, newRow, next, nextArc, orientation); break; } + + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_UNASSIGNED: default: - assert(FALSE); newRow->remainsNetwork = FALSE; + assert(FALSE); } } @@ -8186,15 +8178,16 @@ static void determineMergeableTypes( switch( getMemberType(dec, member)) { case SPQR_MEMBERTYPE_RIGID: - determineSingleRowRigidType(dec, newRow, root, member); + determineSingleRowRigidType(dec, newRow, root); break; case SPQR_MEMBERTYPE_PARALLEL: - determineSingleParallelType(dec, newRow, root, member); + determineSingleParallelType(dec, newRow, root); break; case SPQR_MEMBERTYPE_LOOP: case SPQR_MEMBERTYPE_SERIES: - determineSingleSeriesType(dec, newRow, root, member); + determineSingleSeriesType( newRow, root); break; + case SPQR_MEMBERTYPE_UNASSIGNED: default: assert(FALSE); newRow->remainsNetwork = FALSE; @@ -8362,7 +8355,7 @@ static SCIP_RETCODE rigidTransformArcIntoCycle( dec->members[member].parentMember = newCycle; dec->members[member].markerToParent = arc; dec->members[member].markerOfParent = cycleMarker; - dec->arcs[arc].element = arcIsTree(dec, arc) ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT;; + dec->arcs[arc].element = arcIsTree(dec, arc) ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; dec->arcs[arc].childMember = SPQR_INVALID_MEMBER; } else @@ -8492,7 +8485,7 @@ static SCIP_RETCODE transformSingleRigid( { firstNodeArc = iterArc; } - } while( TRUE ); + } while( TRUE ); /*lint !e506*/ newRow->reducedMembers[reducedMember].coloredNode = SPQR_INVALID_NODE; newRowInformation->member = member; @@ -8563,22 +8556,10 @@ static SCIP_RETCODE splitParallelRowAddition( newRowInfo->member = adjacentMember; if( arcIsReversedNonRigid(dec, treeArc) == arcIsReversedNonRigid(dec, adjacentArc)) { - if( treeReversed ) - { - newRowInfo->reversed = !firstReversed; - } else - { - newRowInfo->reversed = !firstReversed; - } + newRowInfo->reversed = !firstReversed; } else { - if( treeReversed ) - { - newRowInfo->reversed = firstReversed; - } else - { - newRowInfo->reversed = firstReversed; - } + newRowInfo->reversed = firstReversed; } return SCIP_OKAY; } @@ -8649,13 +8630,8 @@ static SCIP_RETCODE splitParallelRowAddition( cut_arc_id firstCut = newRow->reducedMembers[reducedMember].firstCutArc; SCIP_Bool firstReversed = newRow->cutArcs[firstCut].arcReversed != arcIsReversedNonRigid(dec, newRow->cutArcs[firstCut].arc); - if( treeReversed ) - { - newRowInfo->reversed = firstReversed; - } else - { - newRowInfo->reversed = firstReversed; - } + + newRowInfo->reversed = firstReversed; } return SCIP_OKAY; @@ -8703,15 +8679,9 @@ static SCIP_RETCODE splitParallelRowAddition( } newRowInfo->member = newSeries; cut_arc_id cutArcId = newRow->reducedMembers[reducedMember].firstCutArc; - if( treeReversed ) - { - newRowInfo->reversed = - newRow->cutArcs[cutArcId].arcReversed == arcIsReversedNonRigid(dec, newRow->cutArcs[cutArcId].arc); - } else - { - newRowInfo->reversed = - newRow->cutArcs[cutArcId].arcReversed == arcIsReversedNonRigid(dec, newRow->cutArcs[cutArcId].arc); - } + newRowInfo->reversed = + newRow->cutArcs[cutArcId].arcReversed == arcIsReversedNonRigid(dec, newRow->cutArcs[cutArcId].arc); + return SCIP_OKAY; } @@ -9194,7 +9164,7 @@ static SCIP_RETCODE splitFirstLeaf( { firstNodeArc = iterArc; } - } while( TRUE ); + } while( TRUE ); /*lint !e506*/ newRowInformation->head = newNode; newRowInformation->tail = splitNode; newRowInformation->member = member; @@ -9232,8 +9202,12 @@ static SCIP_RETCODE mergeSplitMemberIntoParent( removeArcFromMemberArcList(dec, parentToChild, parent); removeArcFromMemberArcList(dec, childToParent, member); - spqr_node parentArcNodes[2] = {findEffectiveArcTail(dec, parentToChild), findEffectiveArcHead(dec, parentToChild)}; - spqr_node childArcNodes[2] = {findEffectiveArcTail(dec, childToParent), findEffectiveArcHead(dec, childToParent)}; + int parentTail = findEffectiveArcTail(dec, parentToChild); + int parentHead = findEffectiveArcHead(dec, parentToChild); + int childTail = findEffectiveArcTail(dec, childToParent); + int childHead = findEffectiveArcHead(dec, childToParent); + spqr_node parentArcNodes[2] = { parentTail, parentHead }; + spqr_node childArcNodes[2] = { childTail, childHead }; clearArcHeadAndTail(dec, parentToChild); clearArcHeadAndTail(dec, childToParent); @@ -9277,7 +9251,6 @@ static SCIP_RETCODE mergeSplitMemberIntoParent( static SCIP_RETCODE splitAndMergeSeries( SCIP_NETMATDEC* dec, SCIP_NETROWADD* newRow, - reduced_member_id largeMember, reduced_member_id smallMember, SCIP_Bool largeIsParent, NewRowInformation* const newRowInformation, @@ -9405,7 +9378,6 @@ static SCIP_RETCODE splitAndMergeSeries( static SCIP_RETCODE splitAndMergeParallel( SCIP_NETMATDEC* dec, SCIP_NETROWADD* newRow, - reduced_member_id largeMember, reduced_member_id smallMember, SCIP_Bool largeIsParent, NewRowInformation* const newRowInformation, @@ -9526,7 +9498,6 @@ static SCIP_RETCODE splitAndMergeParallel( static SCIP_RETCODE splitAndMergeRigid( SCIP_NETMATDEC* dec, SCIP_NETROWADD* newRow, - reduced_member_id largeMember, reduced_member_id smallMember, SCIP_Bool largeIsParent, NewRowInformation* const newRowInformation, @@ -9588,7 +9559,7 @@ static SCIP_RETCODE splitAndMergeRigid( { firstNodeArc = iterArc; } - } while( TRUE ); + } while( TRUE ); /*lint !e506*/ } spqr_arc representative = findArcSign(dec, smallMarker).representative; @@ -9678,28 +9649,29 @@ static SCIP_RETCODE splitAndMerge( { case SPQR_MEMBERTYPE_RIGID: { - SCIP_CALL(splitAndMergeRigid(dec, newRow, largeMember, smallMember, largeIsParent, newRowInformation, member)); + SCIP_CALL(splitAndMergeRigid(dec, newRow, smallMember, largeIsParent, newRowInformation, member)); break; } case SPQR_MEMBERTYPE_PARALLEL: { - SCIP_CALL( - splitAndMergeParallel(dec, newRow, largeMember, smallMember, largeIsParent, newRowInformation, member)); + SCIP_CALL(splitAndMergeParallel(dec, newRow, smallMember, largeIsParent, newRowInformation, member)); break; } case SPQR_MEMBERTYPE_SERIES: { - SCIP_CALL( - splitAndMergeSeries(dec, newRow, largeMember, smallMember, largeIsParent, newRowInformation, member)); + SCIP_CALL(splitAndMergeSeries(dec, newRow, smallMember, largeIsParent, newRowInformation, member)); break; } + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_UNASSIGNED: default: - assert(FALSE); newRow->remainsNetwork = FALSE; + assert(FALSE); } return SCIP_OKAY; } +//TODO; avoid recursion static SCIP_RETCODE mergeChildrenNodes( SCIP_NETMATDEC* dec, SCIP_NETROWADD* newRow, @@ -9714,7 +9686,7 @@ static SCIP_RETCODE mergeChildrenNodes( return SCIP_OKAY; } //check merging - splitAndMerge(dec, newRow, parent, node, TRUE, newRowInformation); + SCIP_CALL(splitAndMerge(dec, newRow, parent, node, TRUE, newRowInformation)); //merge all children for( int i = 0; i < newRow->reducedMembers[node].numChildren; ++i ) @@ -9823,6 +9795,7 @@ static SCIP_RETCODE transformComponentRowAddition( } break; } + case SPQR_MEMBERTYPE_UNASSIGNED: default: assert(FALSE); break; From a490e706c9f0ff0495d98c65cdd676a6ed82c256 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Fri, 17 May 2024 05:59:13 +0200 Subject: [PATCH 09/63] Refactor recursion to avoid stack overflows --- src/scip/network.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index f50e1eab4b..9e2f7645b2 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -7875,6 +7875,8 @@ static SplitOrientation getRelativeOrientation( case SPQR_MEMBERTYPE_UNASSIGNED: default: assert(FALSE); + SplitOrientation orientation; + return orientation; } } @@ -8230,17 +8232,34 @@ static void determineMergeableTypes( return; } //Add other nodes in the subtree - for( int i = 0; i < newRow->reducedMembers[nextNode].numChildren; ++i ) - { - children_idx idx = newRow->reducedMembers[nextNode].firstChild + i; - reduced_member_id child = newRow->childrenStorage[idx]; - determineTypesChildrenNodes(dec, newRow, nextNode, child, baseNode); - if( !newRow->remainsNetwork ) - { + //use a while loop to avoid recursion; we may get stack overflows for large graphs + MergeTreeCallData * data = newRow->mergeTreeCallData; + + data[0].id = nextNode; + data[0].currentChild = newRow->reducedMembers[nextNode].firstChild ; + int depth = 0; + while(depth >= 0){ + reduced_member_id id = data[depth].id; + children_idx childidx = data[depth].currentChild; + if(childidx == newRow->reducedMembers[id].firstChild + newRow->reducedMembers[id].numChildren){ + --depth; + continue; + } + reduced_member_id currentchild = newRow->childrenStorage[childidx]; + data[depth].currentChild += 1; + //skip this child if we already processed it or it is not merged + if( currentchild == baseNode || newRow->reducedMembers[currentchild].type == TYPE_PROPAGATED){ + continue; + } + determineSplitTypeNext(dec,newRow,id,currentchild,TRUE); + if(!newRow->remainsNetwork){ return; } + //recursively process the child + depth += 1; + data[depth].id = currentchild; + data[depth].currentChild = newRow->reducedMembers[currentchild].firstChild; } - //Move up one layer baseNode = nextNode; nextNode = newRow->reducedMembers[nextNode].parent; From 3335a42ac2a2e156608b5e76d8fb364db836d042 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Fri, 17 May 2024 06:06:25 +0200 Subject: [PATCH 10/63] Fix more lints, compiler warnings, and remove unused function --- src/scip/network.c | 205 ++++++++++++++++-------------------- tests/src/network/network.c | 2 +- 2 files changed, 89 insertions(+), 118 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 9e2f7645b2..2339f5ebf2 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -1934,32 +1934,6 @@ static int decompositionGetFundamentalCycleRows( return num_rows; } -typedef struct -{ - spqr_row row; - SCIP_Bool reversed; -} Nonzero; - -static int qsort_comparison( - const void* a, - const void* b -) -{ - Nonzero* s1 = (Nonzero*) a; - Nonzero* s2 = (Nonzero*) b; - - if( s1->row > s2->row ) - { - return 1; - } else if( s1->row == s2->row ) - { - return 0; - } else - { - return -1; - } -} - SCIP_Bool SCIPnetmatdecVerifyCycle( SCIP* scip, const SCIP_NETMATDEC* dec, @@ -1981,45 +1955,65 @@ SCIP_Bool SCIPnetmatdecVerifyCycle( { return TRUE; } - Nonzero* array; - SCIP_RETCODE code = SCIPallocBufferArray(scip, &array, num_rows); + spqr_row * pathRow; + int * pathRowReversed; + + SCIP_RETCODE code = SCIPallocBufferArray(scip, &pathRow, num_rows); + if( code != SCIP_OKAY ) + { + return FALSE; + } + code = SCIPallocBufferArray(scip, &pathRowReversed, num_rows); if( code != SCIP_OKAY ) { + SCIPfreeBufferArray(scip,&pathRow); return FALSE; } for( int i = 0; i < num_rows; ++i ) { - array[i].row = pathrowstorage[i]; - array[i].reversed = pathsignstorage[i]; + pathRow[i] = pathrowstorage[i]; + pathRowReversed[i] = pathsignstorage[i] ? 1 : 0; } - qsort(array, num_rows, sizeof(Nonzero), qsort_comparison); + SCIPsortIntInt(pathRow,pathRowReversed,num_rows); - Nonzero* secondArray; - code = SCIPallocBufferArray(scip, &secondArray, num_rows); + spqr_row * secondPathRow; + int * secondPathRowReversed; + code = SCIPallocBufferArray(scip, &secondPathRow, num_rows); if( code != SCIP_OKAY ) { - SCIPfreeBufferArray(scip, &array); + SCIPfreeBufferArray(scip,&pathRow); + SCIPfreeBufferArray(scip,&pathRowReversed); + return FALSE; + } + code = SCIPallocBufferArray(scip, &secondPathRowReversed, num_rows); + if( code != SCIP_OKAY ) + { + SCIPfreeBufferArray(scip,&pathRow); + SCIPfreeBufferArray(scip,&pathRowReversed); + SCIPfreeBufferArray(scip,&secondPathRow); return FALSE; } for( int i = 0; i < num_rows; ++i ) { - secondArray[i].row = nonzrowidx[i]; - secondArray[i].reversed = nonzvals[i] < 0.0; + secondPathRow[i] = nonzrowidx[i]; + secondPathRowReversed[i] = nonzvals[i] < 0.0 ? 1 : 0; } - qsort(secondArray, num_rows, sizeof(Nonzero), qsort_comparison); + SCIPsortIntInt(secondPathRow,secondPathRowReversed,num_rows); SCIP_Bool good = TRUE; for( int i = 0; i < num_rows; ++i ) { - if( array[i].row != secondArray[i].row || array[i].reversed != secondArray[i].reversed ) + if( pathRow[i] != secondPathRow[i] || pathRowReversed[i] != secondPathRowReversed[i] ) { good = FALSE; break; } } - SCIPfreeBufferArray(scip, &secondArray); - SCIPfreeBufferArray(scip, &array); + SCIPfreeBufferArray(scip, &secondPathRowReversed); + SCIPfreeBufferArray(scip, &secondPathRow); + SCIPfreeBufferArray(scip, &pathRowReversed); + SCIPfreeBufferArray(scip, &pathRow); return good; } @@ -5792,6 +5786,9 @@ struct SCIP_NetRowAdd MergeTreeCallData* mergeTreeCallData; int memMergeTreeCallData; + + COLOR_STATUS * temporaryColorArray; + int memTemporaryColorArray; }; typedef struct @@ -6090,7 +6087,7 @@ static SCIP_RETCODE constructRowReducedDecomposition( if( newRow->memChildrenStorage < numTotalChildren ) { int newMemSize = maxValue(newRow->memChildrenStorage * 2, numTotalChildren); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->childrenStorage, (size_t) newRow->memChildrenStorage, + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->childrenStorage, newRow->memChildrenStorage, newMemSize)); newRow->memChildrenStorage = newMemSize; } @@ -6309,6 +6306,12 @@ static SCIP_RETCODE allocateRigidSearchMemory( SCIPreallocBlockMemoryArray(dec->env, &newRow->crossingPathCount, newRow->memCrossingPathCount, newSize)); newRow->memCrossingPathCount = newSize; } + if( totalNumNodes > newRow->memTemporaryColorArray ){ + int newSize = maxValue(2 * newRow->memTemporaryColorArray, totalNumNodes); + SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->temporaryColorArray, + newRow->memTemporaryColorArray, newSize)); + newRow->memTemporaryColorArray = newSize; + } //TODO: see if tradeoff for performance bound by checking max # of nodes of rigid is worth it to reduce size //of the following allocations @@ -6560,18 +6563,15 @@ static void rigidFindStarNodes( } } -//TODO: remove SCIP_RETCODE from below functions (until propagation function, basically) and refactor memory allocation -static SCIP_RETCODE zeroOutColorsExceptNeighbourhood( +static void zeroOutColorsExceptNeighbourhood( SCIP_NETMATDEC* dec, SCIP_NETROWADD* newRow, const spqr_node articulationNode, const spqr_node startRemoveNode ) { - COLOR_STATUS* neighbourColors; - int degree = nodeDegree(dec, articulationNode); - SCIP_CALL(SCIPallocBlockMemoryArray(dec->env, &neighbourColors, degree)); - + COLOR_STATUS* neighbourColors = newRow->temporaryColorArray; + assert(nodeDegree(dec, articulationNode) <= newRow->memTemporaryColorArray); { int i = 0; spqr_arc artFirstArc = getFirstNodeArc(dec, articulationNode); @@ -6583,7 +6583,7 @@ static SCIP_RETCODE zeroOutColorsExceptNeighbourhood( spqr_node otherNode = articulationNode == head ? tail : head; neighbourColors[i] = newRow->nodeColors[otherNode]; i++; - assert(i <= degree); + assert(i <= nodeDegree(dec, articulationNode)); artItArc = getNextNodeArc(dec, artItArc, articulationNode); } while( artItArc != artFirstArc ); } @@ -6600,13 +6600,11 @@ static SCIP_RETCODE zeroOutColorsExceptNeighbourhood( spqr_node otherNode = articulationNode == head ? tail : head; newRow->nodeColors[otherNode] = neighbourColors[i]; i++; - assert(i <= degree); + assert(i <= nodeDegree(dec, articulationNode)); artItArc = getNextNodeArc(dec, artItArc, articulationNode); } while( artItArc != artFirstArc ); } - SCIPfreeBlockMemoryArray(dec->env, &neighbourColors, degree); - return SCIP_OKAY; } //Theoretically, there is a faster algorithm, but it is quite complicated to implement. @@ -7874,9 +7872,13 @@ static SplitOrientation getRelativeOrientation( case SPQR_MEMBERTYPE_LOOP: case SPQR_MEMBERTYPE_UNASSIGNED: default: + { assert(FALSE); - SplitOrientation orientation; + SplitOrientation orientation; /*lint !e527*/ + orientation.headSplit = FALSE; /*lint !e527*/ + orientation.otherIsSource = FALSE; return orientation; + } } } @@ -8137,33 +8139,6 @@ static void determineSplitTypeNext( } } -static void determineTypesChildrenNodes( - SCIP_NETMATDEC* dec, - SCIP_NETROWADD* newRow, - reduced_member_id parent, - reduced_member_id node, - reduced_member_id skipNode -) -{ - if( node == skipNode || newRow->reducedMembers[node].type == TYPE_PROPAGATED ) - { - return; - } - //check merging - determineSplitTypeNext(dec, newRow, parent, node, TRUE); - if( !newRow->remainsNetwork ) - { - return; - } - //merge all children - for( int i = 0; i < newRow->reducedMembers[node].numChildren; ++i ) - { - children_idx idx = newRow->reducedMembers[node].firstChild + i; - reduced_member_id child = newRow->childrenStorage[idx]; - determineTypesChildrenNodes(dec, newRow, node, child, skipNode); - } -} - static void determineMergeableTypes( SCIP_NETMATDEC* dec, SCIP_NETROWADD* newRow, @@ -8191,8 +8166,8 @@ static void determineMergeableTypes( break; case SPQR_MEMBERTYPE_UNASSIGNED: default: - assert(FALSE); newRow->remainsNetwork = FALSE; + assert(FALSE); } } return; @@ -8257,6 +8232,7 @@ static void determineMergeableTypes( } //recursively process the child depth += 1; + assert(depth < newRow->memMergeTreeCallData); data[depth].id = currentchild; data[depth].currentChild = newRow->reducedMembers[currentchild].firstChild; } @@ -9657,7 +9633,6 @@ static SCIP_RETCODE splitAndMergeRigid( static SCIP_RETCODE splitAndMerge( SCIP_NETMATDEC* dec, SCIP_NETROWADD* newRow, - reduced_member_id largeMember, reduced_member_id smallMember, SCIP_Bool largeIsParent, NewRowInformation* const newRowInformation @@ -9690,33 +9665,6 @@ static SCIP_RETCODE splitAndMerge( return SCIP_OKAY; } -//TODO; avoid recursion -static SCIP_RETCODE mergeChildrenNodes( - SCIP_NETMATDEC* dec, - SCIP_NETROWADD* newRow, - reduced_member_id parent, - reduced_member_id node, - reduced_member_id skipNode, - NewRowInformation* const newRowInformation -) -{ - if( node == skipNode || newRow->reducedMembers[node].type == TYPE_PROPAGATED ) - { - return SCIP_OKAY; - } - //check merging - SCIP_CALL(splitAndMerge(dec, newRow, parent, node, TRUE, newRowInformation)); - - //merge all children - for( int i = 0; i < newRow->reducedMembers[node].numChildren; ++i ) - { - children_idx idx = newRow->reducedMembers[node].firstChild + i; - reduced_member_id child = newRow->childrenStorage[idx]; - SCIP_CALL(mergeChildrenNodes(dec, newRow, node, child, skipNode, newRowInformation)); - } - return SCIP_OKAY; -} - static SCIP_RETCODE mergeTree( SCIP_NETMATDEC* dec, SCIP_NETROWADD* newRow, @@ -9724,7 +9672,6 @@ static SCIP_RETCODE mergeTree( NewRowInformation* const newRowInformation ) { - //We use the same ordering as when finding //go to a leaf. We need to start in a leaf to avoid the ambiguity of choosing an orientation //in members which have no cut arcs; otherwise, we might choose the wrong one @@ -9750,16 +9697,36 @@ static SCIP_RETCODE mergeTree( while( reducedMemberIsValid(nextNode)) { //check this node - SCIP_CALL(splitAndMerge(dec, newRow, baseNode, nextNode, FALSE, newRowInformation)); + SCIP_CALL(splitAndMerge(dec, newRow, nextNode, FALSE, newRowInformation)); - //Add other nodes in the subtree - for( int i = 0; i < newRow->reducedMembers[nextNode].numChildren; ++i ) - { - children_idx idx = newRow->reducedMembers[nextNode].firstChild + i; - reduced_member_id child = newRow->childrenStorage[idx]; - SCIP_CALL(mergeChildrenNodes(dec, newRow, nextNode, child, baseNode, newRowInformation)); - } + //Recursively merge the children + //use a while loop to avoid recursion; we may get stack overflows for large graphs + MergeTreeCallData * data = newRow->mergeTreeCallData; + data[0].id = nextNode; + data[0].currentChild = newRow->reducedMembers[nextNode].firstChild ; + int depth = 0; + while(depth >= 0){ + reduced_member_id id = data[depth].id; + children_idx childidx = data[depth].currentChild; + if(childidx == newRow->reducedMembers[id].firstChild + newRow->reducedMembers[id].numChildren){ + --depth; + continue; + } + reduced_member_id currentchild = newRow->childrenStorage[childidx]; + data[depth].currentChild += 1; + //skip this child if we already processed it or it is not merged + if( currentchild == baseNode || newRow->reducedMembers[currentchild].type == TYPE_PROPAGATED){ + continue; + } + SCIP_CALL(splitAndMerge(dec, newRow, currentchild, TRUE, newRowInformation)); + + //recursively process the child + depth += 1; + assert(depth < newRow->memMergeTreeCallData); + data[depth].id = currentchild; + data[depth].currentChild = newRow->reducedMembers[currentchild].firstChild; + } //Move up one layer baseNode = nextNode; nextNode = newRow->reducedMembers[nextNode].parent; @@ -9916,6 +9883,9 @@ SCIP_RETCODE SCIPnetrowaddCreate( newRow->mergeTreeCallData = NULL; newRow->memMergeTreeCallData = 0; + newRow->temporaryColorArray = NULL; + newRow->memTemporaryColorArray = 0; + return SCIP_OKAY; } @@ -9928,6 +9898,7 @@ void SCIPnetrowaddFree( SCIP_NETROWADD* newRow = *prowadd; + SCIPfreeBlockMemoryArray(scip, &newRow->temporaryColorArray, newRow->memTemporaryColorArray); SCIPfreeBlockMemoryArray(scip, &newRow->createReducedMembersCallstack, newRow->memCreateReducedMembersCallstack); SCIPfreeBlockMemoryArray(scip, &newRow->artDFSData, newRow->memArtDFSData); SCIPfreeBlockMemoryArray(scip, &newRow->colorDFSData, newRow->memColorDFSData); diff --git a/tests/src/network/network.c b/tests/src/network/network.c index 7dedbbed46..d7356e4830 100644 --- a/tests/src/network/network.c +++ b/tests/src/network/network.c @@ -1914,4 +1914,4 @@ Test(network, rowadd_singlerigid_8, .description = "Updating a single rigid memb runRowTestCase(&testCase, true, false); freeTestCase(&testCase); } -//TODO: test interleaved addition, test using random sampling + test erdos-renyi generated graphs \ No newline at end of file +//TODO: test interleaved addition, test using random sampling + test erdos-renyi generated graphs From 315298e9524f753d723beb7ef7bc6c79503267bb Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 21 May 2024 12:42:09 +0200 Subject: [PATCH 11/63] Refactor network matrix addition interface --- src/scip/network.c | 585 ++++++++++++++++++++++-------------- src/scip/network.h | 202 +++++-------- tests/src/network/network.c | 38 +-- 3 files changed, 451 insertions(+), 374 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 2339f5ebf2..b5e5369a24 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -31,6 +31,7 @@ #include "scip/network.h" #include "scip/scip.h" #include +#include "blockmemshell/memory.h" ///Types which define matrix sizes typedef int spqr_matrix_size; @@ -200,7 +201,7 @@ typedef struct int numArcs; } SPQRNetworkDecompositionMember; -struct SCIP_Netmatdec +typedef struct { int numArcs; int memArcs; @@ -224,7 +225,7 @@ struct SCIP_Netmatdec SCIP* env; int numConnectedComponents; -}; +} SCIP_NETMATDECDATA; static void swap_ints( int* a, @@ -239,7 +240,7 @@ static void swap_ints( #ifndef NDEBUG static SCIP_Bool nodeIsRepresentative( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_node node ) { @@ -253,7 +254,7 @@ static SCIP_Bool nodeIsRepresentative( #endif static spqr_node findNode( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_node node ) { @@ -285,7 +286,7 @@ static spqr_node findNode( } static spqr_node findNodeNoCompression( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_node node ) { @@ -307,7 +308,7 @@ static spqr_node findNodeNoCompression( } static spqr_node findArcTail( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -322,7 +323,7 @@ static spqr_node findArcTail( } static spqr_node findArcHead( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -337,7 +338,7 @@ static spqr_node findArcHead( } static spqr_node findArcHeadNoCompression( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -350,7 +351,7 @@ static spqr_node findArcHeadNoCompression( } static spqr_node findArcTailNoCompression( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -364,7 +365,7 @@ static spqr_node findArcTailNoCompression( static spqr_arc getFirstNodeArc( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_node node ) { @@ -375,7 +376,7 @@ static spqr_arc getFirstNodeArc( } static spqr_arc getNextNodeArcNoCompression( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_arc arc, spqr_node node ) @@ -397,7 +398,7 @@ static spqr_arc getNextNodeArcNoCompression( } static spqr_arc getNextNodeArc( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc, spqr_node node ) @@ -420,7 +421,7 @@ static spqr_arc getNextNodeArc( } static spqr_arc getPreviousNodeArc( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc, spqr_node node ) @@ -443,7 +444,7 @@ static spqr_arc getPreviousNodeArc( } static void mergeNodeArcList( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_node toMergeInto, spqr_node toRemove ) @@ -498,7 +499,7 @@ static void mergeNodeArcList( } static void arcFlipReversed( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -509,7 +510,7 @@ static void arcFlipReversed( } static void arcSetReversed( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc, SCIP_Bool reversed ) @@ -521,7 +522,7 @@ static void arcSetReversed( } static void arcSetRepresentative( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc, spqr_arc representative ) @@ -534,7 +535,7 @@ static void arcSetRepresentative( } static spqr_node mergeNodes( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_node first, spqr_node second ) @@ -565,7 +566,7 @@ static spqr_node mergeNodes( } static SCIP_Bool memberIsRepresentative( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_member member ) { @@ -577,7 +578,7 @@ static SCIP_Bool memberIsRepresentative( } static spqr_member findMember( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_member member ) { @@ -609,7 +610,7 @@ static spqr_member findMember( } static spqr_member findMemberNoCompression( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_member member ) { @@ -632,7 +633,7 @@ static spqr_member findMemberNoCompression( } static spqr_member mergeMembers( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_member first, spqr_member second ) @@ -661,7 +662,7 @@ static spqr_member mergeMembers( } static spqr_member findArcMember( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -675,7 +676,7 @@ static spqr_member findArcMember( } static spqr_member findArcMemberNoCompression( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -688,7 +689,7 @@ static spqr_member findArcMemberNoCompression( } static spqr_member findMemberParent( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_member member ) { @@ -709,7 +710,7 @@ static spqr_member findMemberParent( } static spqr_member findMemberParentNoCompression( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_member member ) { @@ -727,7 +728,7 @@ static spqr_member findMemberParentNoCompression( } static spqr_member findArcChildMember( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -741,7 +742,7 @@ static spqr_member findArcChildMember( } static spqr_member findArcChildMemberNoCompression( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -755,7 +756,7 @@ static spqr_member findArcChildMemberNoCompression( // Only accounts for CHILD markers, not parent markers! static SCIP_Bool arcIsMarker( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -767,7 +768,7 @@ static SCIP_Bool arcIsMarker( } static SCIP_Bool arcIsTree( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -787,7 +788,7 @@ typedef struct #ifndef NDEBUG static SCIP_Bool arcIsRepresentative( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -801,7 +802,7 @@ static SCIP_Bool arcIsRepresentative( #endif static ArcSign findArcSign( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -847,7 +848,7 @@ static ArcSign findArcSign( } static ArcSign findArcSignNoCompression( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -875,7 +876,7 @@ static ArcSign findArcSignNoCompression( //Find the arc tail/head, but accounting for reflection static spqr_node findEffectiveArcHead( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -890,7 +891,7 @@ static spqr_node findEffectiveArcHead( } static spqr_node findEffectiveArcTail( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -905,7 +906,7 @@ static spqr_node findEffectiveArcTail( } static spqr_node findEffectiveArcHeadNoCompression( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -920,7 +921,7 @@ static spqr_node findEffectiveArcHeadNoCompression( } static spqr_node findEffectiveArcTailNoCompression( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -938,7 +939,7 @@ static spqr_node findEffectiveArcTailNoCompression( ///Is not symmetric, in the sense that the arc directions of coponent first are guaranteed not to change but those of second may change ///Based on whether one wants the reflection or not static spqr_arc mergeArcSigns( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc first, spqr_arc second, SCIP_Bool reflectRelative @@ -976,7 +977,7 @@ static spqr_arc mergeArcSigns( } static SCIP_Bool arcIsReversedNonRigid( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -989,7 +990,7 @@ static SCIP_Bool arcIsReversedNonRigid( static spqr_element arcGetElement( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -1000,75 +1001,75 @@ static spqr_element arcGetElement( return dec->arcs[arc].element; } -SCIP_Bool SCIPnetmatdecContainsRow( - const SCIP_NETMATDEC* dec, - int row +static SCIP_Bool netMatDecDataContainsRow( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + int row /**< The row index that is checked */ ) { - assert(SPQRrowIsValid(row) && (int) row < dec->memRows); + assert(SPQRrowIsValid(row) && row < dec->memRows); assert(dec); return SPQRarcIsValid(dec->rowArcs[row]); } -SCIP_Bool SCIPnetmatdecContainsColumn( - const SCIP_NETMATDEC* dec, - int column +static SCIP_Bool netMatDecDataContainsColumn( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + int column /**< The column index that is checked */ ) { - assert(SPQRcolIsValid(column) && (int) column < dec->memColumns); + assert(SPQRcolIsValid(column) && column < dec->memColumns); assert(dec); return SPQRarcIsValid(dec->columnArcs[column]); } static void setDecompositionColumnArc( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_col col, spqr_arc arc ) { - assert(SPQRcolIsValid(col) && (int) col < dec->memColumns); + assert(SPQRcolIsValid(col) && col < dec->memColumns); assert(dec); assert(SPQRarcIsValid(arc)); dec->columnArcs[col] = arc; } static void setDecompositionRowArc( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_row row, spqr_arc arc ) { - assert(SPQRrowIsValid(row) && (int) row < dec->memRows); + assert(SPQRrowIsValid(row) && row < dec->memRows); assert(dec); assert(SPQRarcIsValid(arc)); dec->rowArcs[row] = arc; } static spqr_arc getDecompositionColumnArc( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_col col ) { - assert(SPQRcolIsValid(col) && (int) col < dec->memColumns); + assert(SPQRcolIsValid(col) && col < dec->memColumns); assert(dec); return dec->columnArcs[col]; } static spqr_arc getDecompositionRowArc( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_row row ) { - assert(SPQRrowIsValid(row) && (int) row < dec->memRows); + assert(SPQRrowIsValid(row) && row < dec->memRows); assert(dec); return dec->rowArcs[row]; } -SCIP_RETCODE SCIPnetmatdecCreate( - SCIP* scip, - SCIP_NETMATDEC** pdec, - int nrows, - int ncols +static SCIP_RETCODE netMatDecDataCreate( + SCIP* scip, /**< SCIP data structure */ + SCIP_NETMATDECDATA** pdec, /**< buffer to store pointer to created decomposition */ + int nrows, /**< The maximal number of rows that the decomposition can expect */ + int ncols /**< The maximal number of columns that the decomposition can expect */ ) { assert(scip); @@ -1076,7 +1077,7 @@ SCIP_RETCODE SCIPnetmatdecCreate( assert(!*pdec); SCIP_CALL(SCIPallocBlockMemory(scip, pdec)); - SCIP_NETMATDEC* dec = *pdec; + SCIP_NETMATDECDATA* dec = *pdec; dec->env = scip; //Initialize arc array data @@ -1136,23 +1137,24 @@ SCIP_RETCODE SCIPnetmatdecCreate( return SCIP_OKAY; } -void SCIPnetmatdecFree(SCIP_NETMATDEC** pDecomposition) -{ - assert(pDecomposition); - assert(*pDecomposition); +static void netMatDecDataFree( + SCIP_NETMATDECDATA** pdec /**< pointer to the network matrix decomposition to freed */ +){ + assert(pdec); + assert(*pdec); - SCIP_NETMATDEC* dec = *pDecomposition; + SCIP_NETMATDECDATA* dec = *pdec; SCIPfreeBlockMemoryArray(dec->env, &dec->columnArcs, dec->memColumns); SCIPfreeBlockMemoryArray(dec->env, &dec->rowArcs, dec->memRows); SCIPfreeBlockMemoryArray(dec->env, &dec->nodes, dec->memNodes); SCIPfreeBlockMemoryArray(dec->env, &dec->members, dec->memMembers); SCIPfreeBlockMemoryArray(dec->env, &dec->arcs, dec->memArcs); - SCIPfreeBlockMemory(dec->env, pDecomposition); + SCIPfreeBlockMemory(dec->env, pdec); } static spqr_arc getFirstMemberArc( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_member member ) { @@ -1163,7 +1165,7 @@ static spqr_arc getFirstMemberArc( } static spqr_arc getNextMemberArc( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -1175,7 +1177,7 @@ static spqr_arc getNextMemberArc( } static spqr_arc getPreviousMemberArc( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -1187,7 +1189,7 @@ static spqr_arc getPreviousMemberArc( } static void addArcToMemberArcList( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc, spqr_member member ) @@ -1212,7 +1214,7 @@ static void addArcToMemberArcList( } static SCIP_RETCODE createArc( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_member member, SCIP_Bool reversed, spqr_arc* pArc @@ -1261,7 +1263,7 @@ static SCIP_RETCODE createArc( } static SCIP_RETCODE createRowArc( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_member member, spqr_arc* pArc, spqr_row row, @@ -1277,7 +1279,7 @@ static SCIP_RETCODE createRowArc( } static SCIP_RETCODE createColumnArc( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_member member, spqr_arc* pArc, spqr_col column, @@ -1293,7 +1295,7 @@ static SCIP_RETCODE createColumnArc( } static SCIP_RETCODE createMember( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SPQRMemberType type, spqr_member* pMember ) @@ -1323,7 +1325,7 @@ static SCIP_RETCODE createMember( } static SCIP_RETCODE createNode( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_node* pNode ) { @@ -1344,7 +1346,7 @@ static SCIP_RETCODE createNode( } static void removeArcFromNodeArcList( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc, spqr_node node, SCIP_Bool nodeIsHead @@ -1379,7 +1381,7 @@ static void removeArcFromNodeArcList( } static void addArcToNodeArcList( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc, spqr_node node, SCIP_Bool nodeIsHead @@ -1425,7 +1427,7 @@ static void addArcToNodeArcList( } static void setArcHeadAndTail( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc, spqr_node head, spqr_node tail @@ -1436,7 +1438,7 @@ static void setArcHeadAndTail( } static void clearArcHeadAndTail( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc ) { @@ -1447,7 +1449,7 @@ static void clearArcHeadAndTail( } static void changeArcHead( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc, spqr_node oldHead, spqr_node newHead @@ -1460,7 +1462,7 @@ static void changeArcHead( } static void changeArcTail( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc, spqr_node oldTail, spqr_node newTail @@ -1473,7 +1475,7 @@ static void changeArcTail( } static int nodeDegree( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_node node ) { @@ -1484,7 +1486,7 @@ static int nodeDegree( } static SPQRMemberType getMemberType( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_member member ) { @@ -1496,7 +1498,7 @@ static SPQRMemberType getMemberType( } static void updateMemberType( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_member member, SPQRMemberType type ) @@ -1510,7 +1512,7 @@ static void updateMemberType( } static spqr_arc markerToParent( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_member member ) { @@ -1522,7 +1524,7 @@ static spqr_arc markerToParent( } static void updateMemberParentInformation( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, const spqr_member newMember, const spqr_member toRemove ) @@ -1540,7 +1542,7 @@ static void updateMemberParentInformation( } static spqr_arc markerOfParent( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_member member ) { @@ -1553,7 +1555,7 @@ static spqr_arc markerOfParent( static int getNumMemberArcs( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_member member ) { @@ -1564,20 +1566,20 @@ static int getNumMemberArcs( return dec->members[member].numArcs; } -static int getNumNodes(const SCIP_NETMATDEC* dec) +static int getNumNodes(const SCIP_NETMATDECDATA* dec) { assert(dec); return dec->numNodes; } -static int getNumMembers(const SCIP_NETMATDEC* dec) +static int getNumMembers(const SCIP_NETMATDECDATA* dec) { assert(dec); return dec->numMembers; } static SCIP_RETCODE createStandaloneParallel( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_col* columns, SCIP_Bool* reversed, int num_columns, @@ -1604,7 +1606,7 @@ static SCIP_RETCODE createStandaloneParallel( //TODO: fix tracking connectivity more cleanly, should not be left up to the algorithms ideally static SCIP_RETCODE createConnectedParallel( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_col* columns, SCIP_Bool* reversed, int num_columns, @@ -1629,7 +1631,7 @@ static SCIP_RETCODE createConnectedParallel( } static SCIP_RETCODE createStandaloneSeries( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_row* rows, SCIP_Bool* reversed, int numRows, @@ -1654,7 +1656,7 @@ static SCIP_RETCODE createStandaloneSeries( } static SCIP_RETCODE createConnectedSeries( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_row* rows, SCIP_Bool* reversed, int numRows, @@ -1678,7 +1680,7 @@ static SCIP_RETCODE createConnectedSeries( } static void removeArcFromMemberArcList( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc, spqr_member member ) @@ -1720,7 +1722,7 @@ static void process_arc( FindCycleCall* callStack, int* callStackSize, spqr_arc arc, - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, SCIP_Bool* fundamental_cycle_direction, SCIP_Bool arcIsReversed ) @@ -1757,7 +1759,7 @@ static void process_arc( } static int decompositionGetFundamentalCycleRows( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, spqr_col column, spqr_row* output, SCIP_Bool* computedSignStorage @@ -1934,9 +1936,9 @@ static int decompositionGetFundamentalCycleRows( return num_rows; } -SCIP_Bool SCIPnetmatdecVerifyCycle( +static SCIP_Bool netMatDecDataVerifyCycle( SCIP* scip, - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, int column, const int* nonzrowidx, const double* nonzvals, @@ -2017,28 +2019,28 @@ SCIP_Bool SCIPnetmatdecVerifyCycle( return good; } -static spqr_member largestMemberID(const SCIP_NETMATDEC* dec) +static spqr_member largestMemberID(const SCIP_NETMATDECDATA* dec) { return dec->numMembers; } -static spqr_arc largestArcID(const SCIP_NETMATDEC* dec) +static spqr_arc largestArcID(const SCIP_NETMATDECDATA* dec) { return dec->numArcs; } -static spqr_node largestNodeID(const SCIP_NETMATDEC* dec) +static spqr_node largestNodeID(const SCIP_NETMATDECDATA* dec) { return dec->numNodes; } -static int numConnectedComponents(const SCIP_NETMATDEC* dec) +static int numConnectedComponents(const SCIP_NETMATDECDATA* dec) { return dec->numConnectedComponents; } static SCIP_RETCODE createChildMarker( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_member member, spqr_member child, SCIP_Bool isTree, @@ -2055,7 +2057,7 @@ static SCIP_RETCODE createChildMarker( } static SCIP_RETCODE createParentMarker( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_member member, SCIP_Bool isTree, spqr_member parent, @@ -2077,7 +2079,7 @@ static SCIP_RETCODE createParentMarker( } static SCIP_RETCODE createMarkerPair( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_member parentMember, spqr_member childMember, SCIP_Bool parentIsTree, @@ -2098,7 +2100,7 @@ static SCIP_RETCODE createMarkerPair( } static SCIP_RETCODE createMarkerPairWithReferences( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_member parentMember, spqr_member childMember, SCIP_Bool parentIsTree, @@ -2116,7 +2118,7 @@ static SCIP_RETCODE createMarkerPairWithReferences( } static void moveArcToNewMember( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_arc arc, spqr_member oldMember, spqr_member newMember @@ -2156,7 +2158,7 @@ static void moveArcToNewMember( } static void mergeMemberArcList( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_member toMergeInto, spqr_member toRemove ) @@ -2182,7 +2184,7 @@ static void mergeMemberArcList( } static void changeLoopToSeries( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_member member ) { @@ -2198,7 +2200,7 @@ static void changeLoopToSeries( } static void changeLoopToParallel( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_member member ) { @@ -2213,7 +2215,7 @@ static void changeLoopToParallel( dec->members[member].type = SPQR_MEMBERTYPE_PARALLEL; } -SCIP_Bool SCIPnetmatdecIsMinimal(const SCIP_NETMATDEC* dec) +static SCIP_Bool netMatDecDataIsMinimal(const SCIP_NETMATDECDATA* dec) { //Relies on parents/children etc. being set correctly in the tree SCIP_Bool isMinimal = TRUE; @@ -2240,7 +2242,7 @@ SCIP_Bool SCIPnetmatdecIsMinimal(const SCIP_NETMATDEC* dec) } static void decreaseNumConnectedComponents( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, int by ) { @@ -2249,7 +2251,7 @@ static void decreaseNumConnectedComponents( } static void reorderComponent( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_member newRoot ) { @@ -2296,8 +2298,8 @@ static void reorderComponent( } -void SCIPnetmatdecRemoveComponent( - SCIP_NETMATDEC* dec, +static void netMatDecDataRemoveComponent( + SCIP_NETMATDECDATA* dec, const int* componentRows, int numRows, const int* componentCols, @@ -2470,7 +2472,7 @@ static void decompositionToDot(FILE *stream, const SCIP_NETMATDEC *dec, SCIP_Boo #endif static SCIP_RETCODE mergeGivenMemberIntoParent( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_member member, spqr_member parent, spqr_arc parentToChild, @@ -2649,7 +2651,7 @@ typedef struct spqr_member member; } CreateReducedMembersCallstack; -struct SCIP_NetColAdd +typedef struct { SCIP_Bool remainsNetwork; @@ -2700,10 +2702,10 @@ struct SCIP_NetColAdd spqr_member* leafMembers; int numLeafMembers; int memLeafMembers; -}; +} SCIP_NETCOLADD; static void cleanupPreviousIteration( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol ) { @@ -2750,7 +2752,7 @@ static void cleanupPreviousIteration( newCol->numPathArcs = 0; } -SCIP_RETCODE SCIPnetcoladdCreate( +static SCIP_RETCODE SCIPnetcoladdCreate( SCIP* scip, SCIP_NETCOLADD** pcoladd ) @@ -2812,7 +2814,7 @@ SCIP_RETCODE SCIPnetcoladdCreate( return SCIP_OKAY; } -void SCIPnetcoladdFree( +static void SCIPnetcoladdFree( SCIP* scip, SCIP_NETCOLADD** pcoladd ) @@ -2840,7 +2842,7 @@ void SCIPnetcoladdFree( static reduced_member_id createReducedMembersToRoot( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, const spqr_member firstMember ) @@ -2947,7 +2949,7 @@ static reduced_member_id createReducedMembersToRoot( } static SCIP_RETCODE constructReducedDecomposition( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol ) { @@ -3111,7 +3113,7 @@ static void cleanUpMemberInformation(SCIP_NETCOLADD* newCol) } static void createPathArc( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, const spqr_arc arc, const reduced_member_id reducedMember, @@ -3161,7 +3163,7 @@ static void createPathArc( } static SCIP_RETCODE createPathArcs( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol ) { @@ -3217,7 +3219,7 @@ static SCIP_RETCODE createPathArcs( */ static SCIP_RETCODE newColUpdateColInformation( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, spqr_col column, const spqr_row* nonzeroRows, @@ -3270,7 +3272,7 @@ newColUpdateColInformation( } static SCIP_RETCODE computeLeafMembers( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol ) { @@ -3294,7 +3296,7 @@ static SCIP_RETCODE computeLeafMembers( } static void determineRigidPath( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, SPQRColReducedMember* redMem ) @@ -3354,7 +3356,7 @@ static void determineRigidPath( } static void determineSingleRigidType( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, reduced_member_id reducedMember ) @@ -3373,7 +3375,7 @@ static void determineSingleRigidType( //TODO: type seems somewhat duplicate static void determineSingleComponentType( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, reduced_member_id reducedMember ) @@ -3469,7 +3471,7 @@ static void determineSingleComponentType( static void determinePathSeriesType( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, reduced_member_id reducedMember, spqr_member member, @@ -3592,7 +3594,7 @@ static void determinePathSeriesType( } static void determinePathParallelType( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, reduced_member_id reducedMember, spqr_member member, @@ -3660,7 +3662,7 @@ static void determinePathParallelType( } static void determinePathRigidType( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, reduced_member_id reducedMember, MemberPathType previousType, @@ -3965,7 +3967,7 @@ static void determinePathRigidType( } static void determinePathMemberType( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, reduced_member_id reducedMember, spqr_member member, @@ -4007,7 +4009,7 @@ static void determinePathMemberType( } static void determinePathTypes( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, SPQRColReducedComponent* component ) @@ -4091,7 +4093,7 @@ static void determinePathTypes( } static void checkRigidLeaf( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, reduced_member_id leaf, spqr_arc toParent, @@ -4119,7 +4121,7 @@ static void checkRigidLeaf( } static ReducedMemberType checkLeaf( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, reduced_member_id leaf, spqr_arc toParent, @@ -4215,7 +4217,7 @@ static ReducedMemberType checkLeaf( } static void propagateCycles( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol ) { @@ -4343,7 +4345,7 @@ static void propagateCycles( } static void determineComponentTypes( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, SPQRColReducedComponent* component ) @@ -4363,9 +4365,8 @@ static void determineComponentTypes( } } -SCIP_RETCODE -SCIPnetcoladdCheck( - SCIP_NETMATDEC* dec, +static SCIP_RETCODE SCIPnetcoladdCheck( + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* coladd, int column, const int* nonzrows, @@ -4477,7 +4478,7 @@ static void setTerminalRepresentative( } static SCIP_RETCODE splitParallel( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, spqr_member parallel, spqr_arc arc1, spqr_arc arc2, @@ -4508,7 +4509,7 @@ static SCIP_RETCODE splitParallel( } static SCIP_RETCODE splitSeries( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, SPQRColReducedMember* reducedMember, spqr_member member, @@ -4680,7 +4681,7 @@ static SCIP_RETCODE splitSeries( static SCIP_RETCODE splitSeriesMerging( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, SPQRColReducedMember* reducedMember, spqr_member member, @@ -4810,7 +4811,7 @@ static SCIP_RETCODE splitSeriesMerging( } static SCIP_RETCODE transformFirstPathMember( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, reduced_member_id reducedMember, NewColInformation* newColInfo, @@ -4910,7 +4911,7 @@ static SCIP_RETCODE transformFirstPathMember( } static SCIP_RETCODE transformAndMergeParallel( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, reduced_member_id current, reduced_member_id next, @@ -4980,7 +4981,7 @@ static SCIP_RETCODE transformAndMergeParallel( } static SCIP_RETCODE transformAndMergeSeries( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, reduced_member_id current, reduced_member_id next, @@ -5120,7 +5121,7 @@ static SCIP_RETCODE transformAndMergeSeries( } static SCIP_RETCODE transformAndMergeRigid( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, reduced_member_id current, reduced_member_id next, @@ -5185,7 +5186,7 @@ static SCIP_RETCODE transformAndMergeRigid( } static SCIP_RETCODE transformAndMerge( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, reduced_member_id current, reduced_member_id next, @@ -5227,7 +5228,7 @@ static SCIP_RETCODE transformAndMerge( } static SCIP_RETCODE transformPath( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, SPQRColReducedComponent* component, NewColInformation* newColInfo @@ -5270,7 +5271,7 @@ static SCIP_RETCODE columnTransformSingleParallel( } static SCIP_RETCODE columnTransformSingleSeries( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, reduced_member_id reducedMemberId, spqr_member member, @@ -5293,7 +5294,7 @@ static SCIP_RETCODE columnTransformSingleSeries( } static SCIP_RETCODE columnTransformSingleRigid( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, reduced_member_id reducedMemberId, spqr_member member, @@ -5423,7 +5424,7 @@ static SCIP_RETCODE columnTransformSingleRigid( } static SCIP_RETCODE transformComponent( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, SPQRColReducedComponent* component, NewColInformation* newColInfo @@ -5475,9 +5476,13 @@ static SCIP_RETCODE transformComponent( return SCIP_OKAY; } +static SCIP_Bool SCIPnetcoladdRemainsNetwork(const SCIP_NETCOLADD* newCol) +{ + return newCol->remainsNetwork; +} -SCIP_RETCODE SCIPnetcoladdAdd( - SCIP_NETMATDEC* dec, +static SCIP_RETCODE SCIPnetcoladdAdd( + SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol ) { @@ -5579,10 +5584,7 @@ SCIP_RETCODE SCIPnetcoladdAdd( return SCIP_OKAY; } -SCIP_Bool SCIPnetcoladdRemainsNetwork(const SCIP_NETCOLADD* newCol) -{ - return newCol->remainsNetwork; -} + static int minValue( @@ -5707,7 +5709,7 @@ typedef struct reduced_member_id root; } SPQRRowReducedComponent; -struct SCIP_NetRowAdd +typedef struct { SCIP_Bool remainsNetwork; @@ -5789,7 +5791,7 @@ struct SCIP_NetRowAdd COLOR_STATUS * temporaryColorArray; int memTemporaryColorArray; -}; +} SCIP_NETROWADD; typedef struct { @@ -5816,7 +5818,7 @@ static NewRowInformation emptyNewRowInformation(void) * already part of the decomposition. */ static SCIP_RETCODE newRowUpdateRowInformation( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const spqr_row row, const spqr_col* columns, @@ -5876,7 +5878,7 @@ static SCIP_RETCODE newRowUpdateRowInformation( * @return */ static reduced_member_id createRowReducedMembersToRoot( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const spqr_member firstMember ) @@ -5984,7 +5986,7 @@ static reduced_member_id createRowReducedMembersToRoot( * @return */ static SCIP_RETCODE constructRowReducedDecomposition( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow ) { @@ -6137,7 +6139,7 @@ static SCIP_RETCODE constructRowReducedDecomposition( * @param reducedMember */ static void createCutArc( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const spqr_arc arc, const reduced_member_id reducedMember, @@ -6188,7 +6190,7 @@ static void createCutArc( * Note this preallocates memory for cut arcs which may be created by propagation. */ static SCIP_RETCODE createReducedDecompositionCutArcs( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow ) { @@ -6241,7 +6243,7 @@ static SCIP_RETCODE createReducedDecompositionCutArcs( * which is not propagated. */ static SCIP_RETCODE determineLeafReducedMembers( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow ) { @@ -6268,7 +6270,7 @@ static SCIP_RETCODE determineLeafReducedMembers( * Preallocates memory arrays necessary for searching rigid components. */ static SCIP_RETCODE allocateRigidSearchMemory( - const SCIP_NETMATDEC* dec, + const SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow ) { @@ -6374,7 +6376,7 @@ static SCIP_RETCODE allocateRigidSearchMemory( } static void zeroOutColors( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const spqr_node firstRemoveNode ) @@ -6425,7 +6427,7 @@ static void zeroOutColors( } static void cleanUpPreviousIteration( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow ) { @@ -6464,7 +6466,7 @@ static void cleanUpPreviousIteration( } static void rigidFindStarNodes( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const reduced_member_id toCheck ) @@ -6564,7 +6566,7 @@ static void rigidFindStarNodes( } static void zeroOutColorsExceptNeighbourhood( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const spqr_node articulationNode, const spqr_node startRemoveNode @@ -6610,7 +6612,7 @@ static void zeroOutColorsExceptNeighbourhood( //Theoretically, there is a faster algorithm, but it is quite complicated to implement. //Thus, we stick with the 'simple' version below, which is easily fast enough in practice. static void intersectionOfAllPaths( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const reduced_member_id toCheck, int* const nodeNumPaths @@ -6739,7 +6741,7 @@ static void addArticulationNode( } static void articulationPoints( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, ArticulationNodeInformation* nodeInfo, reduced_member_id reducedMember @@ -6826,7 +6828,7 @@ static void articulationPoints( } static void rigidConnectedColoringRecursive( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, spqr_node articulationNode, spqr_node firstProcessNode, @@ -6900,7 +6902,7 @@ static void rigidConnectedColoringRecursive( } static void rigidConnectedColoring( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const reduced_member_id reducedMember, const spqr_node node, @@ -6969,7 +6971,7 @@ static void rigidConnectedColoring( } static spqr_node checkNeighbourColoringArticulationNode( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const spqr_node articulationNode, spqr_arc* const adjacentSplittingArc @@ -7030,7 +7032,7 @@ static spqr_node checkNeighbourColoringArticulationNode( static void rigidGetSplittableArticulationPointsOnPath( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const reduced_member_id toCheck, spqr_node firstNode, @@ -7132,7 +7134,7 @@ static void rigidGetSplittableArticulationPointsOnPath( } static void determineParallelType( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const reduced_member_id toCheckMember, const spqr_arc markerToOther, @@ -7183,7 +7185,7 @@ static void determineParallelType( } static void determineSeriesType( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const reduced_member_id toCheckMember, const spqr_arc markerToOther, @@ -7204,7 +7206,7 @@ static void determineSeriesType( } static void determineRigidType( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const reduced_member_id toCheckMember, const spqr_arc markerToOther, @@ -7266,7 +7268,7 @@ static void determineRigidType( } static RowReducedMemberType determineType( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const reduced_member_id toCheckMember, const spqr_arc markerToOther, @@ -7303,7 +7305,7 @@ static RowReducedMemberType determineType( } static void propagateComponents( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow ) { @@ -7407,7 +7409,7 @@ typedef struct static Nodes rigidDetermineCandidateNodesFromAdjacentComponents( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const reduced_member_id toCheck ) @@ -7475,7 +7477,7 @@ rigidDetermineCandidateNodesFromAdjacentComponents( } static SCIP_RETCODE allocateTreeSearchMemory( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow ) { @@ -7491,7 +7493,7 @@ static SCIP_RETCODE allocateTreeSearchMemory( } static void determineSingleRowRigidType( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id reducedMember ) @@ -7520,7 +7522,7 @@ static void determineSingleRowRigidType( } static void determineSingleParallelType( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id reducedMember ) @@ -7567,7 +7569,7 @@ static void determineSingleSeriesType( } static spqr_node determineAndColorSplitNode( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id id, spqr_node candidateOne, @@ -7647,7 +7649,7 @@ static spqr_node determineAndColorSplitNode( } static void determineSplitTypeFirstLeaf( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id reducedId ) @@ -7764,7 +7766,7 @@ typedef struct } SplitOrientation; static SplitOrientation getRelativeOrientationRigid( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id reducedId, spqr_arc arcToNext @@ -7812,7 +7814,7 @@ static SplitOrientation getRelativeOrientationRigid( } static SplitOrientation getRelativeOrientationParallel( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id reducedId, spqr_arc arcToNext @@ -7833,7 +7835,7 @@ static SplitOrientation getRelativeOrientationParallel( } static SplitOrientation getRelativeOrientationSeries( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id reducedId, spqr_arc arcToNext @@ -7855,7 +7857,7 @@ static SplitOrientation getRelativeOrientationSeries( } static SplitOrientation getRelativeOrientation( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id reducedId, spqr_arc arcToNext @@ -7883,7 +7885,7 @@ static SplitOrientation getRelativeOrientation( } static void determineSplitTypeSeries( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id reducedId, spqr_arc marker, @@ -7931,7 +7933,7 @@ static void determineSplitTypeSeries( } static void determineSplitTypeParallel( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id reducedId, spqr_arc marker, @@ -7987,7 +7989,7 @@ static void determineSplitTypeParallel( } static void determineSplitTypeRigid( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id reducedId, spqr_member member, @@ -8098,7 +8100,7 @@ static void determineSplitTypeRigid( } static void determineSplitTypeNext( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id current, reduced_member_id next, @@ -8140,7 +8142,7 @@ static void determineSplitTypeNext( } static void determineMergeableTypes( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id root ) @@ -8259,7 +8261,7 @@ static void cleanUpRowMemberInformation(SCIP_NETROWADD* newRow) } static SCIP_RETCODE rigidTransformArcIntoCycle( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, const spqr_member member, const spqr_arc arc, const SCIP_Bool reverseArcDirection, @@ -8365,7 +8367,7 @@ static SCIP_RETCODE rigidTransformArcIntoCycle( } static SCIP_RETCODE transformSingleRigid( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const reduced_member_id reducedMember, const spqr_member member, @@ -8495,7 +8497,7 @@ static SCIP_RETCODE transformSingleRigid( static SCIP_RETCODE splitParallelRowAddition( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const reduced_member_id reducedMember, const spqr_member member, @@ -8681,7 +8683,7 @@ static SCIP_RETCODE splitParallelRowAddition( } static SCIP_RETCODE transformSingleParallel( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const reduced_member_id reducedMember, const spqr_member member, @@ -8693,7 +8695,7 @@ static SCIP_RETCODE transformSingleParallel( } static SCIP_RETCODE splitSeriesMergingRowAddition( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, const reduced_member_id reducedMember, const spqr_member member, @@ -8808,7 +8810,7 @@ static SCIP_RETCODE splitSeriesMergingRowAddition( } static SCIP_RETCODE splitParallelMerging( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id reducedMember, spqr_member member, @@ -9033,7 +9035,7 @@ static SCIP_RETCODE splitParallelMerging( } static SCIP_RETCODE splitFirstLeaf( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id leaf, NewRowInformation* const newRowInformation @@ -9170,7 +9172,7 @@ static SCIP_RETCODE splitFirstLeaf( } static SCIP_RETCODE mergeSplitMemberIntoParent( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, spqr_member member, spqr_member parent, @@ -9244,7 +9246,7 @@ static SCIP_RETCODE mergeSplitMemberIntoParent( } static SCIP_RETCODE splitAndMergeSeries( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id smallMember, SCIP_Bool largeIsParent, @@ -9371,7 +9373,7 @@ static SCIP_RETCODE splitAndMergeSeries( } static SCIP_RETCODE splitAndMergeParallel( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id smallMember, SCIP_Bool largeIsParent, @@ -9491,7 +9493,7 @@ static SCIP_RETCODE splitAndMergeParallel( } static SCIP_RETCODE splitAndMergeRigid( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id smallMember, SCIP_Bool largeIsParent, @@ -9631,7 +9633,7 @@ static SCIP_RETCODE splitAndMergeRigid( } static SCIP_RETCODE splitAndMerge( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id smallMember, SCIP_Bool largeIsParent, @@ -9666,7 +9668,7 @@ static SCIP_RETCODE splitAndMerge( } static SCIP_RETCODE mergeTree( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, reduced_member_id root, NewRowInformation* const newRowInformation @@ -9736,7 +9738,7 @@ static SCIP_RETCODE mergeTree( } static SCIP_RETCODE transformComponentRowAddition( - SCIP_NETMATDEC* dec, + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* newRow, SPQRRowReducedComponent* component, NewRowInformation* const newRowInformation @@ -9796,7 +9798,7 @@ static SCIP_RETCODE transformComponentRowAddition( } -SCIP_RETCODE SCIPnetrowaddCreate( +static SCIP_RETCODE SCIPnetrowaddCreate( SCIP* scip, SCIP_NETROWADD** prowadd ) @@ -9889,7 +9891,7 @@ SCIP_RETCODE SCIPnetrowaddCreate( return SCIP_OKAY; } -void SCIPnetrowaddFree( +static void SCIPnetrowaddFree( SCIP* scip, SCIP_NETROWADD** prowadd ) @@ -9925,8 +9927,8 @@ void SCIPnetrowaddFree( SCIPfreeBlockMemory(scip, prowadd); } -SCIP_RETCODE SCIPnetrowaddCheck( - SCIP_NETMATDEC* dec, +static SCIP_RETCODE SCIPnetrowaddCheck( + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* rowadd, int row, const int* nonzcols, @@ -9971,8 +9973,8 @@ SCIP_RETCODE SCIPnetrowaddCheck( return SCIP_OKAY; } -SCIP_RETCODE SCIPnetrowaddAdd( - SCIP_NETMATDEC* dec, +static SCIP_RETCODE SCIPnetrowaddAdd( + SCIP_NETMATDECDATA* dec, SCIP_NETROWADD* rowadd ) { @@ -10074,7 +10076,144 @@ SCIP_RETCODE SCIPnetrowaddAdd( return SCIP_OKAY; } -SCIP_Bool SCIPnetrowaddRemainsNetwork(const SCIP_NETROWADD* rowadd) +static SCIP_Bool SCIPnetrowaddRemainsNetwork(const SCIP_NETROWADD* rowadd) { return rowadd->remainsNetwork; } + +struct SCIP_Netmatdec{ + SCIP_NETMATDECDATA * dec; + SCIP_NETROWADD * rowadd; + SCIP_NETCOLADD * coladd; +}; + +SCIP_RETCODE SCIPnetmatdecCreate( + SCIP* scip, /**< SCIP data structure */ + SCIP_NETMATDEC** pdec, /**< buffer to store pointer to created decomposition */ + int nrows, /**< The maximal number of rows that the decomposition can expect */ + int ncols /**< The maximal number of columns that the decomposition can expect */ +) +{ + SCIP_CALL(SCIPallocBlockMemory(scip, pdec)); + SCIP_NETMATDEC* dec = *pdec; + dec->dec = NULL; + SCIP_CALL(netMatDecDataCreate(scip, &dec->dec, nrows, ncols)); + dec->rowadd = NULL; + dec->coladd = NULL; + return SCIP_OKAY; +} + +void SCIPnetmatdecFree( + SCIP_NETMATDEC** pdec /**< pointer to the network matrix decomposition to freed */ +) +{ + SCIP_NETMATDEC* dec = *pdec; + SCIP* scip = dec->dec->env; + if( dec->coladd != NULL) + { + SCIPnetcoladdFree(scip, &dec->coladd); + } + if( dec->rowadd != NULL) + { + SCIPnetrowaddFree(scip, &dec->rowadd); + } + netMatDecDataFree(&dec->dec); + SCIPfreeBlockMemory(scip,pdec); +} + +SCIP_RETCODE SCIPnetmatdecTryAddCol( + SCIP_NETMATDEC* dec, /**< Network matrix decomposition */ + int column, /**< The column to add */ + int* nonzrows, /**< The column's nonzero row indices */ + double* nonzvals, /**< The column's nonzero entries */ + int nnonzs, /**< The number of nonzeros in the column */ + SCIP_Bool* success /**< Buffer to store whether the column was added */ +) +{ + if( dec->coladd == NULL) + { + SCIP_CALL(SCIPnetcoladdCreate(dec->dec->env, &dec->coladd)); + } + + SCIP_CALL(SCIPnetcoladdCheck(dec->dec, dec->coladd, column, nonzrows, nonzvals, nnonzs)); + *success = SCIPnetcoladdRemainsNetwork(dec->coladd); + if( *success ) + { + SCIP_CALL(SCIPnetcoladdAdd(dec->dec, dec->coladd)); + } + return SCIP_OKAY; +} + +SCIP_RETCODE SCIPnetmatdecTryAddRow( + SCIP_NETMATDEC* dec, /**< Network matrix decomposition */ + int row, /**< The row to add */ + int* nonzcols, /**< The row's nonzero column indices */ + double* nonzvals, /**< The row's nonzero entries */ + int nnonzs, /**< The number of nonzeros in the row */ + SCIP_Bool* success /**< Buffer to store whether the row was added */ +) +{ + if( dec->rowadd == NULL) + { + SCIP_CALL(SCIPnetrowaddCreate(dec->dec->env, &dec->rowadd)); + } + + SCIP_CALL(SCIPnetrowaddCheck(dec->dec, dec->rowadd, row, nonzcols, nonzvals, nnonzs)); + *success = SCIPnetrowaddRemainsNetwork(dec->rowadd); + if( *success ) + { + SCIP_CALL(SCIPnetrowaddAdd(dec->dec, dec->rowadd)); + } + return SCIP_OKAY; +} + +SCIP_Bool SCIPnetmatdecContainsRow( + SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ + int row /**< The row index that is checked */ +) +{ + return netMatDecDataContainsRow(dec->dec, row); +} + +SCIP_Bool SCIPnetmatdecContainsColumn( + SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ + int column /**< The column index that is checked */ +) +{ + return netMatDecDataContainsColumn(dec->dec, column); +} + +void SCIPnetmatdecRemoveComponent( + SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ + int* componentrows, /**< Pointer to the array of rows to delete */ + int nrows, /**< The number of rows to delete */ + int* componentcols, /**< Pointer to the array of columns to delete */ + int ncols /**< The number of columns to delete */ +) +{ + netMatDecDataRemoveComponent(dec->dec,componentrows,nrows,componentcols,ncols); +} + +SCIP_Bool SCIPnetmatdecIsMinimal( + SCIP_NETMATDEC* dec /**< The network matrix decomposition */ +) +{ + return netMatDecDataIsMinimal(dec->dec); +} + + +SCIP_Bool SCIPnetmatdecVerifyCycle( + SCIP* scip, /**< SCIP data structure */ + SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ + int column, /**< The column to check */ + int* nonzrowidx, /**< Array with the column's nonzero row indices */ + double* nonzvals, /**< Array with the column's nonzero values */ + int nnonzs, /**< Number of nonzeros in the column */ + int* pathrowstorage, /**< A buffer to hold the computed path's rows. Should have size equal or + * greater than the number of rows in the decomposition. */ + SCIP_Bool* pathsignstorage /**< A buffer to store the computed path's row signs. Should have size + * equal or greater than the number of rows in the decomposition. */ +) +{ + return netMatDecDataVerifyCycle(scip,dec->dec,column,nonzrowidx,nonzvals,nnonzs,pathrowstorage,pathsignstorage); +} diff --git a/src/scip/network.h b/src/scip/network.h index 137ba99427..d6d7a8932d 100644 --- a/src/scip/network.h +++ b/src/scip/network.h @@ -30,39 +30,40 @@ * This file contains algorithms for incrementally growing (augmenting) network matrices, * which are a large subclass of totally unimodular matrices. * - * A $\pm 1$ matrix is a network matrix if there exists a directed graph G with a (not necessarily rooted) tree T such that - * for each arc \f$ a \in A\setminus T\f$ the column \f$ A_{:,a} \f$ corresponds to the oriented path between the tail - * and the head of \f$ a \f$. The main difficulty with detecting network matrices is that there may exist many graphs - * that realize a certain matrix. The algorithms in this file maintain and update an SPQR tree, - * which is a graph decomposition that represents all graphs that correspond to the current network matrix. + * A matrix \f$M \in \{-1,0,1\}^{m\times n} \f$ is a network matrix if there exists a directed graph \f$G\f$ + * with \f$m+n\f$ arcs and a spanning forest \f$T\f$ with \f$|T| = m\f$ such that + * for each arc \f$ a = (u,v) \in A\setminus T\f$ and each arc \f$t\in T\f$, + * \f[ + * M_{t,a} = \begin{cases} + * +1 & \textrm{if the unique } u-v \textrm{ path in } T \textrm{ passes through } a \textrm{ forwardly} \\ + * -1 & \textrm{if the unique } u-v \textrm{ path in } T \textrm{ passes through } a \textrm{ backwardly} \\ + * 0 & \textrm{if the unique } u-v \textrm{ path in } T \textrm{ does not pass through } a + * \end{cases} + * \f] * - * TODO: add incidence matrix methods - * A large subclass of network matrices are node-arc incidence matrices, which are matrices that have at most - * one +1 entry and one -1 entry in every column, which correspond to the in and out-node of an arc. - * This file contains algorithms to detect reflected node-arc incidence matrices, where rows can be optionally negated. - * Although network matrices are a strictly larger class of totally unimodular matrices, reflected node-arc incidence - * matrices can be detected more quickly and are commonly used within MIP formulations + * The main difficulty with detecting network matrices is that there may exist many graph-spanning forest pairs + * that realize a matrix. The algorithms in this file maintain and update an SPQR forest, which is a graph decomposition + * that represents all graphs that correspond to a given network matrix. * * Note that all addition algorithms expect that each nonzero is given exactly once and not more often; in particular, - * it is up to the user to ensure this when using both column and row addition steps. + * it is up to the user to ensure this when interleaving column and row addition steps. * * TODO * The column addition for network matrices is based on: * * The row addition for network matrices is based on: * - * The row addition for incidence matrices is based on an adaptation from: - * - * */ +/** TODO: add method that realizes a SCIP digraph from the decomposition */ +/** TODO: add method that *cleanly* removes complete components of the SPQR tree */ +/** TODO: add node-arc incidence matrix methods */ /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/ #ifndef __SCIP_NETWORK_H__ #define __SCIP_NETWORK_H__ #include "scip/def.h" -#include "blockmemshell/memory.h" #include "scip/type_retcode.h" #include "scip/type_scip.h" #ifdef cplusplus @@ -72,7 +73,10 @@ extern "C" { /** this class stores a decomposition of the network matrix using SPQR trees */ typedef struct SCIP_Netmatdec SCIP_NETMATDEC; -/** create an empty network matrix decomposition that can store a matrix with at most the given dimensions */ +/** create an empty network matrix decomposition that can store a matrix with at most the given dimensions + * + * Initially, the network matrix decomposition stores an empty submatrix + */ SCIP_EXPORT SCIP_RETCODE SCIPnetmatdecCreate( SCIP* scip, /**< SCIP data structure */ @@ -84,141 +88,93 @@ SCIP_RETCODE SCIPnetmatdecCreate( /** frees a network matrix decomposition */ SCIP_EXPORT void SCIPnetmatdecFree( - SCIP_NETMATDEC** pdec /**< pointer to the network matrix decomposition to freed */ + SCIP_NETMATDEC** pdec /**< pointer to the network matrix decomposition to free */ +); + +/** Tries to add a given column to the network network matrix + * + * Note that the first call to this method for a given decomposition may be a bit slower, + * due to memory initialization. + */ +SCIP_EXPORT +SCIP_RETCODE SCIPnetmatdecTryAddCol( + SCIP_NETMATDEC* dec, /**< Network matrix decomposition */ + int column, /**< The column to add */ + int* nonzrows, /**< The column's nonzero row indices */ + double* nonzvals, /**< The column's nonzero entries */ + int nnonzs, /**< The number of nonzeros in the column */ + SCIP_Bool* success /**< Buffer to store whether the column was added */ +); + +//TODO: note that first call may take longer due to memory initialization +SCIP_EXPORT +SCIP_RETCODE SCIPnetmatdecTryAddRow( + SCIP_NETMATDEC* dec, /**< Network matrix decomposition */ + int row, /**< The row to add */ + int* nonzcols, /**< The row's nonzero column indices */ + double* nonzvals, /**< The row's nonzero entries */ + int nnonzs, /**< The number of nonzeros in the row */ + SCIP_Bool* success /**< Buffer to store whether the row was added */ ); /** checks if the network matrix decomposition contains the given row */ SCIP_EXPORT SCIP_Bool SCIPnetmatdecContainsRow( - const SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ + SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ int row /**< The row index that is checked */ ); /** checks if the network matrix decomposition contains the given column */ SCIP_EXPORT SCIP_Bool SCIPnetmatdecContainsColumn( - const SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ + SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ int column /**< The column index that is checked */ ); -/** checks if the network matrix decomposition is minimal; it is minimal if it does not contain adjacent parallel or series skeletons; should only be used in tests or asserts*/ -SCIP_EXPORT -SCIP_Bool SCIPnetmatdecIsMinimal( - const SCIP_NETMATDEC* dec /**< The network matrix decomposition */ -); - -//TODO: add method that realizes a SCIP digraph from the decomposition -//TODO: add method that *cleanly* removes complete components of the SPQR tree - -/** removes a connected component of the matrix from the network decomposition; note that this method is 'stupid', - * and does not delete the associated graph data structure; moreover, it does not explicitly check if the rows/columns - * that the user provides are a connected component of the matrix given by the decomposition. If this is not the case, - * then calling this function is considered a bug. */ +/** removes a connected component of the matrix from the network decomposition + * + * Note that this method is 'stupid', and does not delete the associated graph data structure. + * Moreover, it does not explicitly check if the rows/columns that the user provides are a connected + * component of the submatrix given by the decomposition. + * If this is not the case, then calling this function is considered a bug. + */ SCIP_EXPORT void SCIPnetmatdecRemoveComponent( SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ - const int* componentrows, /**< Pointer to the array of rows to delete */ + int* componentrows, /**< Array of rows to delete */ int nrows, /**< The number of rows to delete */ - const int* componentcols, /**< Pointer to the array of columns to delete */ + int* componentcols, /**< Array of columns to delete */ int ncols /**< The number of columns to delete */ ); -/** checks if the cycle stored in the Decomposition matches the given array; should only be used in tests */ -SCIP_EXPORT -SCIP_Bool SCIPnetmatdecVerifyCycle( - SCIP* scip, /**< SCIP data structure */ - const SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ - int column, /**< The column to check */ - const int* nonzrowidx, /**< Pointer to the array with the column's nonzero row indices */ - const double* nonzvals, /**< Pointer to the array with the column's nonzero values */ - int nnonzs, /**< Number of nonzeros in the column */ - int* pathrowstorage, /**< Pointer to a buffer to hold the computed path's rows */ - SCIP_Bool* pathsignstorage /**< Pointer to a buffer to store the computed path's row signs */ -); - -/** this class stores all data for performing a column addition to the network matrix decomposition */ -typedef struct SCIP_NetColAdd SCIP_NETCOLADD; - -/** creates the data structure for managing column addition of a network matrix decomposition */ +/** checks if the network matrix decomposition is minimal. + * + * It is minimal if it does not contain adjacent parallel or series skeletons. + * The network matrix decomposition should always be minimal. This method should only be used in tests or asserts. + */ SCIP_EXPORT -SCIP_RETCODE SCIPnetcoladdCreate( - SCIP* scip, /**< SCIP data structure */ - SCIP_NETCOLADD** pcoladd /**< buffer to store pointer to column addition data structure */ +SCIP_Bool SCIPnetmatdecIsMinimal( + SCIP_NETMATDEC* dec /**< The network matrix decomposition */ ); -/** frees the data structure for managing column addition of a network matrix decomposition */ +/** checks if the cycle stored in the Decomposition matches the given array + * + * This method should only be used in tests + */ SCIP_EXPORT -void SCIPnetcoladdFree( +SCIP_Bool SCIPnetmatdecVerifyCycle( SCIP* scip, /**< SCIP data structure */ - SCIP_NETCOLADD** pcoladd /**< pointer to the column addition data structure to be freed */ -); - -/** Checks if we can add the given column to the network matrix decomposition. - * The result of the latest query can be checked through SCIPnetcoladdRemainsNetwork(). - * Users can add the latest checked column through SCIPnetcoladdAdd()*/ -SCIP_EXPORT -SCIP_RETCODE SCIPnetcoladdCheck( - SCIP_NETMATDEC* dec, /**< Network matrix decomposition */ - SCIP_NETCOLADD* coladd, /**< Network matrix column addition data structure */ + SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ int column, /**< The column to check */ - const int* nonzrows, /**< The column's nonzero row indices */ - const double* nonzvals, /**< The column's nonzero entries */ - size_t nnonzs /**< The number of nonzeros in the column */ -); -/** Adds the most recently checked column to the network matrix decomposition. */ -SCIP_EXPORT -SCIP_RETCODE SCIPnetcoladdAdd( - SCIP_NETMATDEC* dec, /**< Network matrix decomposition */ - SCIP_NETCOLADD* coladd /**< Network matrix column addition data structure */ -); - -/** Returns whether the most recently checked column can be added to the network */ -SCIP_EXPORT -SCIP_Bool SCIPnetcoladdRemainsNetwork( - const SCIP_NETCOLADD* coladd /**< Network matrix column addition data structure */ -); - -/** this class stores all data for performing a row addition to the network matrix decomposition */ -typedef struct SCIP_NetRowAdd SCIP_NETROWADD; - -/** creates the data structure for managing row addition of a network matrix decomposition */ -SCIP_EXPORT -SCIP_RETCODE SCIPnetrowaddCreate( - SCIP* scip, /**< SCIP data structure */ - SCIP_NETROWADD** prowadd /**< buffer to store pointer to row addition data structure */ -); - -/** frees the data structure for managing row addition of a network matrix decomposition */ -SCIP_EXPORT -void SCIPnetrowaddFree( - SCIP* scip, /**< SCIP data structure */ - SCIP_NETROWADD** prowadd /**< pointer to the row addition data structure to be freed */ -); - -/** Checks if we can add the given row to the network matrix decomposition. - * The result of the latest query can be checked through SCIPnetrowaddRemainsNetwork(). - * Users can add the latest checked column through SCIPnetrowaddAdd()*/ -SCIP_EXPORT -SCIP_RETCODE SCIPnetrowaddCheck( - SCIP_NETMATDEC* dec, /**< Network matrix decomposition */ - SCIP_NETROWADD* rowadd, /**< Network matrix row addition data structure */ - int row, /**< The row to check */ - const int* nonzcols, /**< The row's nonzero row indices */ - const double* nonzvals, /**< The row's nonzero entries */ - size_t nnonzs /**< The number of nonzeros in the row */ -); -/** Adds the most recently checked row to the network matrix decomposition. */ -SCIP_EXPORT -SCIP_RETCODE SCIPnetrowaddAdd( - SCIP_NETMATDEC* dec, /**< Network matrix decomposition */ - SCIP_NETROWADD* rowadd /**< Network matrix row addition data structure */ + int* nonzrowidx, /**< Array with the column's nonzero row indices */ + double* nonzvals, /**< Array with the column's nonzero values */ + int nnonzs, /**< Number of nonzeros in the column */ + int* pathrowstorage, /**< A buffer to hold the computed path's rows. Should have size equal or + * greater than the number of rows in the decomposition. */ + SCIP_Bool* pathsignstorage /**< A buffer to store the computed path's row signs. Should have size + * equal or greater than the number of rows in the decomposition. */ ); -/** Returns whether the most recently checked row can be added to the network */ -SCIP_EXPORT -SCIP_Bool SCIPnetrowaddRemainsNetwork( - const SCIP_NETROWADD* rowadd /**< Network matrix row addition data structure */ - ); #ifdef cplusplus } diff --git a/tests/src/network/network.c b/tests/src/network/network.c index d7356e4830..d8096974f9 100644 --- a/tests/src/network/network.c +++ b/tests/src/network/network.c @@ -231,9 +231,7 @@ static SCIP_RETCODE runColumnTestCase( SCIP_NETMATDEC* dec = NULL; SCIP_CALL(SCIPnetmatdecCreate(scip, &dec, testCase->nrows, testCase->ncols)); - SCIP_NETCOLADD* coladd = NULL; - SCIP_CALL(SCIPnetcoladdCreate(scip, &coladd)); - bool isNetwork = true; + SCIP_Bool isNetwork = TRUE; int* tempColumnStorage; SCIP_Bool* tempSignStorage; @@ -245,19 +243,13 @@ static SCIP_RETCODE runColumnTestCase( { int colEntryStart = testCase->primaryIndexStart[i]; int colEntryEnd = testCase->primaryIndexStart[i + 1]; - const int* nonzeroRows = &testCase->entrySecondaryIndex[colEntryStart]; - const double* nonzeroValues = &testCase->entryValue[colEntryStart]; + int* nonzeroRows = &testCase->entrySecondaryIndex[colEntryStart]; + double* nonzeroValues = &testCase->entryValue[colEntryStart]; int nonzeros = colEntryEnd - colEntryStart; cr_assert(nonzeros >= 0); //Check if adding the column preserves the network matrix - SCIP_CALL(SCIPnetcoladdCheck(dec, coladd, i, nonzeroRows, nonzeroValues, nonzeros)); - if( SCIPnetcoladdRemainsNetwork(coladd)) - { - //If so, we add it. - SCIP_CALL(SCIPnetcoladdAdd(dec, coladd)); - } else - { - isNetwork = false; + SCIP_CALL(SCIPnetmatdecTryAddCol(dec,i,nonzeroRows,nonzeroValues,nonzeros,&isNetwork)); + if(!isNetwork){ break; } cr_expect(SCIPnetmatdecIsMinimal(dec)); @@ -293,7 +285,6 @@ static SCIP_RETCODE runColumnTestCase( SCIPfreeBufferArray(scip, &tempColumnStorage); SCIPfreeBufferArray(scip, &tempSignStorage); - SCIPnetcoladdFree(scip, &coladd); SCIPnetmatdecFree(&dec); return SCIP_OKAY; } @@ -317,9 +308,7 @@ static SCIP_RETCODE runRowTestCase( SCIP_NETMATDEC* dec = NULL; SCIP_CALL(SCIPnetmatdecCreate(scip, &dec, testCase->nrows, testCase->ncols)); - SCIP_NETROWADD* rowadd = NULL; - SCIP_CALL(SCIPnetrowaddCreate(scip, &rowadd)); - bool isNetwork = true; + SCIP_Bool isNetwork = TRUE; int* tempColumnStorage; SCIP_Bool* tempSignStorage; @@ -331,19 +320,13 @@ static SCIP_RETCODE runRowTestCase( { int rowEntryStart = testCase->primaryIndexStart[i]; int rowEntryEnd = testCase->primaryIndexStart[i + 1]; - const int* nonzeroCols = &testCase->entrySecondaryIndex[rowEntryStart]; - const double* nonzeroValues = &testCase->entryValue[rowEntryStart]; + int* nonzeroCols = &testCase->entrySecondaryIndex[rowEntryStart]; + double* nonzeroValues = &testCase->entryValue[rowEntryStart]; int nonzeros = rowEntryEnd - rowEntryStart; cr_assert(nonzeros >= 0); //Check if adding the row preserves the network matrix - SCIP_CALL(SCIPnetrowaddCheck(dec, rowadd, i, nonzeroCols, nonzeroValues, nonzeros)); - if( SCIPnetrowaddRemainsNetwork(rowadd)) - { - //If so, we add it. - SCIP_CALL(SCIPnetrowaddAdd(dec, rowadd)); - } else - { - isNetwork = false; + SCIP_CALL(SCIPnetmatdecTryAddRow(dec,i,nonzeroCols,nonzeroValues,nonzeros,&isNetwork)); + if(!isNetwork){ break; } cr_expect(SCIPnetmatdecIsMinimal(dec)); @@ -396,7 +379,6 @@ static SCIP_RETCODE runRowTestCase( SCIPfreeBufferArray(scip, &tempColumnStorage); SCIPfreeBufferArray(scip, &tempSignStorage); - SCIPnetrowaddFree(scip, &rowadd); SCIPnetmatdecFree(&dec); return SCIP_OKAY; } From 1acd551882c32c25e5696c77ec6627d4aafd1fae Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 21 May 2024 12:56:37 +0200 Subject: [PATCH 12/63] Update documentation for SCIPnetmatdecTryAddRow --- src/scip/network.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/scip/network.h b/src/scip/network.h index d6d7a8932d..c5e8fefd24 100644 --- a/src/scip/network.h +++ b/src/scip/network.h @@ -106,7 +106,13 @@ SCIP_RETCODE SCIPnetmatdecTryAddCol( SCIP_Bool* success /**< Buffer to store whether the column was added */ ); -//TODO: note that first call may take longer due to memory initialization +/** Tries to add a given column to the network network matrix + * + * Note that the first call to this method for a given decomposition may be a bit slower, + * due to memory initialization. + * If the user is only interested in determining if a certain (sub)matrix is network or not, using + * SCIPnetmatdecTryAddCol() will generally be faster, unless the (sub)matrix has many more columns than rows. + */ SCIP_EXPORT SCIP_RETCODE SCIPnetmatdecTryAddRow( SCIP_NETMATDEC* dec, /**< Network matrix decomposition */ From 06085d0161412f60f3d3b6f2dbf4736650da1b78 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Wed, 22 May 2024 09:25:59 +0200 Subject: [PATCH 13/63] Fix conversion warning and clarify comments --- src/scip/network.c | 12 ++++++------ src/scip/network.h | 20 +++++++++++--------- tests/src/network/network.c | 11 ++++------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index b5e5369a24..110f63331b 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -3224,7 +3224,7 @@ newColUpdateColInformation( spqr_col column, const spqr_row* nonzeroRows, const double* nonzeroValues, - size_t numNonzeros + int numNonzeros ) { newCol->newColIndex = column; @@ -3232,7 +3232,7 @@ newColUpdateColInformation( newCol->numDecompositionRowArcs = 0; newCol->numNewRowArcs = 0; - for( size_t i = 0; i < numNonzeros; ++i ) + for( int i = 0; i < numNonzeros; ++i ) { spqr_arc rowArc = getDecompositionRowArc(dec, nonzeroRows[i]); SCIP_Bool reversed = nonzeroValues[i] < 0.0; @@ -4371,7 +4371,7 @@ static SCIP_RETCODE SCIPnetcoladdCheck( int column, const int* nonzrows, const double* nonzvals, - size_t nnonzs + int nnonzs ) { assert(dec); @@ -5823,7 +5823,7 @@ static SCIP_RETCODE newRowUpdateRowInformation( const spqr_row row, const spqr_col* columns, const double* columnValues, - const size_t numColumns + const int numColumns ) { newRow->newRowIndex = row; @@ -5831,7 +5831,7 @@ static SCIP_RETCODE newRowUpdateRowInformation( newRow->numDecompositionColumnArcs = 0; newRow->numColumnArcs = 0; - for( size_t i = 0; i < numColumns; ++i ) + for( int i = 0; i < numColumns; ++i ) { spqr_arc columnArc = getDecompositionColumnArc(dec, columns[i]); SCIP_Bool reversed = columnValues[i] < 0.0; @@ -9933,7 +9933,7 @@ static SCIP_RETCODE SCIPnetrowaddCheck( int row, const int* nonzcols, const double* nonzvals, - size_t nnonzs + int nnonzs ) { assert(dec); diff --git a/src/scip/network.h b/src/scip/network.h index c5e8fefd24..777b988bca 100644 --- a/src/scip/network.h +++ b/src/scip/network.h @@ -30,9 +30,10 @@ * This file contains algorithms for incrementally growing (augmenting) network matrices, * which are a large subclass of totally unimodular matrices. * - * A matrix \f$M \in \{-1,0,1\}^{m\times n} \f$ is a network matrix if there exists a directed graph \f$G\f$ - * with \f$m+n\f$ arcs and a spanning forest \f$T\f$ with \f$|T| = m\f$ such that - * for each arc \f$ a = (u,v) \in A\setminus T\f$ and each arc \f$t\in T\f$, + * A matrix \f$M \in \{-1,0,1\}^{m\times n} \f$ is a network matrix if there exists a directed graph \f$G = (V,A)\f$ + * with \f$|A| = m+n\f$ arcs and a spanning forest \f$T\f$ with \f$|T| = m\f$ such that \f$M\f$'s rows index \f$T\f$ and + * \f$M\f$'s columns index \f$A\setminus T\f$, + * and for each arc \f$ a = (u,v) \in A\setminus T\f$ and each arc \f$t\in T\f$ * \f[ * M_{t,a} = \begin{cases} * +1 & \textrm{if the unique } u-v \textrm{ path in } T \textrm{ passes through } a \textrm{ forwardly} \\ @@ -40,19 +41,20 @@ * 0 & \textrm{if the unique } u-v \textrm{ path in } T \textrm{ does not pass through } a * \end{cases} * \f] + * holds. * - * The main difficulty with detecting network matrices is that there may exist many graph-spanning forest pairs + * The main difficulty with detecting network matrices is that there may exist many pairs of a graph and a spanning tree * that realize a matrix. The algorithms in this file maintain and update an SPQR forest, which is a graph decomposition * that represents all graphs that correspond to a given network matrix. * * Note that all addition algorithms expect that each nonzero is given exactly once and not more often; in particular, * it is up to the user to ensure this when interleaving column and row addition steps. * - * TODO - * The column addition for network matrices is based on: - * - * The row addition for network matrices is based on: - * + * More details can be found in; + * - R.P. van der Hulst and M.Walter "A row-wise algorithm for graph realization" + * - R.E. Bixby and D.K. Wagner "An almost linear-time algorithm for graph realization" + * Note that although these publications contain the methods for undirected graphs (and binary matrices), + * their ideas are relatively easily extended to directed graphs and ternary matrices. */ /** TODO: add method that realizes a SCIP digraph from the decomposition */ diff --git a/tests/src/network/network.c b/tests/src/network/network.c index d8096974f9..a542babca5 100644 --- a/tests/src/network/network.c +++ b/tests/src/network/network.c @@ -29,9 +29,6 @@ /*--+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/ -#include - -#include "scip/scip.h" #include "scip/network.h" #include "include/scip_test.h" @@ -259,8 +256,8 @@ static SCIP_RETCODE runColumnTestCase( { int jColEntryStart = testCase->primaryIndexStart[j]; int jColEntryEnd = testCase->primaryIndexStart[j + 1]; - const int* jNonzeroRows = &testCase->entrySecondaryIndex[jColEntryStart]; - const double* jNonzeroValues = &testCase->entryValue[jColEntryStart]; + int* jNonzeroRows = &testCase->entrySecondaryIndex[jColEntryStart]; + double* jNonzeroValues = &testCase->entryValue[jColEntryStart]; int jNonzeros = jColEntryEnd - jColEntryStart; SCIP_Bool cycleIsCorrect = SCIPnetmatdecVerifyCycle(scip, dec, j, jNonzeroRows, jNonzeroValues, @@ -350,8 +347,8 @@ static SCIP_RETCODE runRowTestCase( } } - const int* jNonzeroRows = &colWiseCase.entrySecondaryIndex[jColEntryStart]; - const double* jNonzeroValues = &colWiseCase.entryValue[jColEntryStart]; + int* jNonzeroRows = &colWiseCase.entrySecondaryIndex[jColEntryStart]; + double* jNonzeroValues = &colWiseCase.entryValue[jColEntryStart]; int jNonzeros = finalEntryIndex - jColEntryStart; SCIP_Bool cycleIsCorrect = SCIPnetmatdecVerifyCycle(scip, dec, j, From 6f46dd771b899abbf872e90aec14260821d369f4 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Wed, 22 May 2024 11:58:51 +0200 Subject: [PATCH 14/63] Add documentation to data structures --- src/scip/network.c | 239 +++++++++++++++++++++++++++++++-------------- 1 file changed, 165 insertions(+), 74 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 110f63331b..22bb1fd2ea 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -42,189 +42,280 @@ typedef spqr_matrix_size spqr_col; #define SPQR_INVALID_ROW SPQR_INVALID #define SPQR_INVALID_COL SPQR_INVALID + +/* Only check in debug mode if the used indices are valid */ #ifndef NDEBUG -static SCIP_Bool SPQRrowIsInvalid(spqr_row row) +/** Determine if the row index is invalid */ +static +SCIP_Bool SPQRrowIsInvalid( + spqr_row row /**< The row to check */ + ) { return row == SPQR_INVALID_ROW; } -static SCIP_Bool SPQRcolIsInvalid(spqr_col col) +/** Determine if the column index is invalid */ +static +SCIP_Bool SPQRcolIsInvalid( + spqr_col col /**< The column to check */ + ) { return col == SPQR_INVALID_COL; } -static SCIP_Bool SPQRrowIsValid(spqr_row row) +/** Determine if the row index is valid */ +static +SCIP_Bool SPQRrowIsValid( + spqr_row row /**< The row to check */ + ) { return !SPQRrowIsInvalid(row); } -static SCIP_Bool SPQRcolIsValid(spqr_col col) +/** Determine if the column index is valid */ +static +SCIP_Bool SPQRcolIsValid( + spqr_col col /**< The column to check */ + ) { return !SPQRcolIsInvalid(col); } #endif -//Columns 0..x correspond to elements 0..x -//Rows 0..y correspond to elements -1.. -y-1 +/** Columns 0..x correspond to elements 0..x and rows 0..y correspond to elements -1.. -y-1 */ #define MARKER_ROW_ELEMENT (INT_MIN) #define MARKER_COLUMN_ELEMENT (INT_MAX) typedef int spqr_element; -static SCIP_Bool SPQRelementIsRow(spqr_element element) +/** Checks if an element is a row */ +static +SCIP_Bool SPQRelementIsRow( + spqr_element element /**< The element to check */ + ) { return element < 0; } -static SCIP_Bool SPQRelementIsColumn(spqr_element element) +/** Checks if an element is a column */ +static +SCIP_Bool SPQRelementIsColumn( + spqr_element element /**< The element to check */ +) { return !SPQRelementIsRow(element); } -static spqr_row SPQRelementToRow(spqr_element element) +/** Convert an element to the corresponding row index */ +static +spqr_row SPQRelementToRow( + spqr_element element /**< The element to convert */ +) { assert(SPQRelementIsRow(element)); return (spqr_row) ( -element - 1 ); } -static spqr_element SPQRrowToElement(spqr_row row) +/** Convert a row to the corresponding element */ +static +spqr_element SPQRrowToElement( + spqr_row row /**< The row to convert */ + ) { assert(SPQRrowIsValid(row)); return (spqr_element) -row - 1; } -static spqr_col SPQRelementToColumn(spqr_element element) +/** Convert an element to the corresponding column index */ +static +spqr_col SPQRelementToColumn( + spqr_element element /**< The element to convert */ + ) { assert(SPQRelementIsColumn(element)); return (spqr_col) element; } -static spqr_element SPQRcolumnToElement(spqr_col column) +/** Convert a column to the corresponding element */ +static +spqr_element SPQRcolumnToElement( + spqr_col column /**< The column to convert */ + ) { assert(SPQRcolIsValid(column)); return (spqr_element) column; } -typedef int spqr_node; -#define SPQR_INVALID_NODE (-1) +/** spqr_member is an index for the members of the SPQR decomposition. The members are the nodes of the SPQR tree. + * Each member has an associated subgraph, sometimes called a skeleton. + * If two members are adjacent in the SPQR tree, the two corresponding subgraphs are connected by a 2-separation in any + * graph represented by the matrix. + * For members, we reserve all negative values as invalid. We use these negative values in union-find datastructure, + * where we store the rank of the representative as a negative number. + */ +typedef int spqr_member; +#define SPQR_INVALID_MEMBER (-1) -static SCIP_Bool SPQRnodeIsInvalid(spqr_node node) +/** Check if a member is invalid */ +static +SCIP_Bool SPQRmemberIsInvalid( + spqr_member member /**< The member to check */ + ) { - return node < 0; + return member < 0; } -static SCIP_Bool SPQRnodeIsValid(spqr_node node) +/** Check if a member is valid */ +static SCIP_Bool SPQRmemberIsValid( + spqr_member member /**< The member to check */ + ) { - return !SPQRnodeIsInvalid(node); + return !SPQRmemberIsInvalid(member); } -typedef int spqr_member; -#define SPQR_INVALID_MEMBER (-1) +/** spqr_node is an index for the nodes stored in the decomposition. The nodes are part of each member's skeleton. + * Similar to spqr_member, we reserve all negative values as invalid and use these in union-find. + */ +typedef int spqr_node; +#define SPQR_INVALID_NODE (-1) -static SCIP_Bool SPQRmemberIsInvalid(spqr_member member) +/** Check if a node is invalid */ +static +SCIP_Bool SPQRnodeIsInvalid( + spqr_node node /**< The node to check */ +) { - return member < 0; + return node < 0; } -static SCIP_Bool SPQRmemberIsValid(spqr_member member) +/** Check if a node is valid */ +static +SCIP_Bool SPQRnodeIsValid( + spqr_node node /**< The node to check */ +) { - return !SPQRmemberIsInvalid(member); + return !SPQRnodeIsInvalid(node); } +/** spqr_arc is an index for the arcs stored in the decomposition. The arcs are part of each member's skeleton. + * Similar to spqr_node and spqr_member, we reserve all negative values as invalid and use these in some union-find. + * However, in contrast to spqr_node and spqr_member, the union-find data structure does not represent the arcs, + * but rather if all arcs that have the same representative we know they are in the same member. + */ typedef int spqr_arc; #define SPQR_INVALID_ARC (-1) -static SCIP_Bool SPQRarcIsInvalid(spqr_arc arc) +/** Check if an arc is invalid */ +static +SCIP_Bool SPQRarcIsInvalid( + spqr_arc arc /**< The arc to check */ + ) { return arc < 0; } -static SCIP_Bool SPQRarcIsValid(spqr_arc arc) +/** Check if an arc is valid */ +static +SCIP_Bool SPQRarcIsValid( + spqr_arc arc /**< The arc to check */ + ) { return !SPQRarcIsInvalid(arc); } +/** The type of the member */ typedef enum { - SPQR_MEMBERTYPE_RIGID = 0, //Also known as triconnected components - SPQR_MEMBERTYPE_PARALLEL = 1,//Also known as a 'bond' - SPQR_MEMBERTYPE_SERIES = 2, //Also known as 'polygon' or 'cycle' - SPQR_MEMBERTYPE_LOOP = 3, - SPQR_MEMBERTYPE_UNASSIGNED = 4// To indicate that the member has been merged/is not representative + SPQR_MEMBERTYPE_RIGID = 0, /** The member's skeleton is 3-connected and has at least 4 edges */ + SPQR_MEMBERTYPE_PARALLEL = 1, /** The member's skeleton consists of 2 nodes with at least 3 edges */ + SPQR_MEMBERTYPE_SERIES = 2, /** The member's skeleton is a cycle with at least 3 edges */ + SPQR_MEMBERTYPE_LOOP = 3, /** The member's skeleton consists of 2 nodes connected by 1 or 2 edges */ + SPQR_MEMBERTYPE_UNASSIGNED = 4 /** To indicate that the member is not a representative member anymore */ } SPQRMemberType; - +/** Represents a single node of cyclic doubly-linked list of arc edges*/ typedef struct { spqr_arc previous; spqr_arc next; } SPQRNetworkDecompositionArcListNode; +/** This structure stores the relevant data for a single node. */ typedef struct { - spqr_node representativeNode; - spqr_arc firstArc;//first arc of the neighbouring arcs - int numArcs; + spqr_node representativeNode; /**< Points to the next node in the union-find data structure + * Stores the rank as a negative number if this node represents itself */ + spqr_arc firstArc; /**< Points to the head node of the cyclic doubly linked list containing + * the arcs that are adjacent to this node.*/ + int numArcs; /**< The number of arcs adjacent to this node */ } SPQRNetworkDecompositionNode; +/** Structure that stores the relevant data for a single arc. */ typedef struct { - spqr_node head; - spqr_node tail; - spqr_member member; - spqr_member childMember; - SPQRNetworkDecompositionArcListNode headArcListNode; - SPQRNetworkDecompositionArcListNode tailArcListNode; - SPQRNetworkDecompositionArcListNode arcListNode;//Linked-list node of the array of arcs of the member which this arc is in - - spqr_element element; - - //Signed union-find for arc directions - //For non-rigid members every arc is it's own representative, and the direction is simply given by the boolean - //For rigid members, every arc is represented by another arc in the member, - //and the direction can be found by multiplying the signs along the union-find path - spqr_arc representative; - SCIP_Bool reversed; + spqr_node head; /**< The head node of the arc */ + spqr_node tail; /**< The tail node of the arc */ + spqr_member member; /**< The member that contains the arc */ + spqr_member childMember; /**< Stores the child member, if this arc points to one */ + SPQRNetworkDecompositionArcListNode headArcListNode; /**< Linked-list node for iterating over the head's arcs */ + SPQRNetworkDecompositionArcListNode tailArcListNode; /**< Linked-list node for iterating over the tail's arcs */ + SPQRNetworkDecompositionArcListNode arcListNode; /**< Linked-list node for iterating over the member's arcs */ + + spqr_element element; /**< The element associated to this arc */ + + /**Signed union-find for arc directions. If an arc is reversed, it head becomes its tail and vice-versa. + * For non-rigid members every arc is it's own representative, and the direction is simply given by the boolean. + * For rigid members, every arc is represented by another arc in the member, + * and the direction can be found by multiplying the signs along the union-find path + * We use this data structure to efficiently reverse all arcs in a skeleton. + */ + spqr_arc representative; /**< The representative of the arc */ + SCIP_Bool reversed; /**< Whether the arc's head and tail are reversed, or not */ } SPQRNetworkDecompositionArc; +/** Structure that stores the relevant data for a single member */ typedef struct { - spqr_member representativeMember; - SPQRMemberType type; + spqr_member representativeMember; /** The representative of this member (union-find) */ + SPQRMemberType type; /** The type of this member */ - spqr_member parentMember; - spqr_arc markerToParent; - spqr_arc markerOfParent; + /**The SPQR tree is stored as an arborescence. Each member stores its parents, and each edge of the member + * pointing to a child member stores the associated member in childMember + */ + spqr_member parentMember; /**< The parent of this member in the arborescence */ + spqr_arc markerToParent; /**< The arc pointing to the parent */ + spqr_arc markerOfParent; /**< The arc of the parent pointing to this member */ - spqr_arc firstArc;//First of the members' linked-list arc array - int numArcs; + spqr_arc firstArc; /** First arc of the linked list containing the member's arcs */ + int numArcs; /** The number of arcs associated to the member */ } SPQRNetworkDecompositionMember; +/** Stores the SPQR forest data structure and its relevant data */ typedef struct { - int numArcs; - int memArcs; - SPQRNetworkDecompositionArc* arcs; - spqr_arc firstFreeArc; + int numArcs; //TODO: check if this allows reclaiming of arcs + int memArcs; /**< The amount of space allocated in the arc data array */ + SPQRNetworkDecompositionArc* arcs; /**< Array of arcs of the SPQR forest, indexed by spqr_arc */ + spqr_arc firstFreeArc; /**< Points to the first unused slot in the arcs array */ - int memMembers; - int numMembers; - SPQRNetworkDecompositionMember* members; + int memMembers; /**< The amount of space allocated in the member data array */ + int numMembers; /**< The number of slots used in the member data array */ + SPQRNetworkDecompositionMember* members; /**< Array of members of the SPQR forest. Indexed by spqr_member */ - int memNodes; - int numNodes; - SPQRNetworkDecompositionNode* nodes; + int memNodes; /**< The amount of space allocated in the node data array */ + int numNodes; /**< The number of slots used in the node data array */ + SPQRNetworkDecompositionNode* nodes; /**< Array of nodes of the SPQR forest. Indexed by spqr_node */ - int memRows; - spqr_arc* rowArcs; + int memRows; /**< The (maximal) number of rows of the matrix */ + spqr_arc* rowArcs; /**< Maps the rows of the matrix to arcs in the decomposition */ - int memColumns; - spqr_arc* columnArcs; + int memColumns; /**< The (maximal) number of columns of the matrix */ + spqr_arc* columnArcs; /**< Maps the columns of the matrix to arcs in the decomposition */ - SCIP* env; + SCIP* env; /**< SCIP pointer stored for later use */ - int numConnectedComponents; + int numConnectedComponents; /** The number of disjoint SPQR trees in the SPQR forest */ } SCIP_NETMATDECDATA; static void swap_ints( From 6017f60291d8d5d5d47fda48e678803d7ceb5476 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Wed, 22 May 2024 12:02:12 +0200 Subject: [PATCH 15/63] replace swap_ints with SCIPswapInts --- src/scip/network.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 22bb1fd2ea..ad24b14b1d 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -294,7 +294,7 @@ typedef struct /** Stores the SPQR forest data structure and its relevant data */ typedef struct { - int numArcs; //TODO: check if this allows reclaiming of arcs + int numArcs; /**< The number of slots used in the arc data array */ int memArcs; /**< The amount of space allocated in the arc data array */ SPQRNetworkDecompositionArc* arcs; /**< Array of arcs of the SPQR forest, indexed by spqr_arc */ spqr_arc firstFreeArc; /**< Points to the first unused slot in the arcs array */ @@ -318,15 +318,6 @@ typedef struct int numConnectedComponents; /** The number of disjoint SPQR trees in the SPQR forest */ } SCIP_NETMATDECDATA; -static void swap_ints( - int* a, - int* b -) -{ - int temp = *a; - *a = *b; - *b = temp; -} #ifndef NDEBUG @@ -644,7 +635,7 @@ static spqr_node mergeNodes( spqr_node secondRank = dec->nodes[second].representativeNode; if( firstRank > secondRank ) { - swap_ints(&first, &second); + SCIPswapInts(&first, &second); } //first becomes representative; we merge all of the arcs of second into first mergeNodeArcList(dec, first, second); @@ -742,7 +733,7 @@ static spqr_member mergeMembers( spqr_member secondRank = dec->members[second].representativeMember; if( firstRank > secondRank ) { - swap_ints(&first, &second); + SCIPswapInts(&first, &second); } dec->members[second].representativeMember = first; if( firstRank == secondRank ) @@ -1050,7 +1041,7 @@ static spqr_arc mergeArcSigns( if( firstRank > secondRank ) { - swap_ints(&first, &second); + SCIPswapInts(&first, &second); } dec->arcs[second].representative = first; if( firstRank == secondRank ) @@ -3239,7 +3230,7 @@ static void createPathArc( listNode->arcTail = findEffectiveArcTail(dec, arc); if( reversed ) { - swap_ints(&listNode->arcHead, &listNode->arcTail); + SCIPswapInts(&listNode->arcHead, &listNode->arcTail); } assert(SPQRnodeIsValid(listNode->arcHead) && SPQRnodeIsValid(listNode->arcTail)); assert(listNode->arcHead < newCol->memNodePathDegree && listNode->arcTail < newCol->memNodePathDegree); @@ -6264,7 +6255,7 @@ static void createCutArc( listNode->arcTail = findEffectiveArcTail(dec, arc); if( reversed ) { - swap_ints(&listNode->arcHead, &listNode->arcTail); + SCIPswapInts(&listNode->arcHead, &listNode->arcTail); } assert(SPQRnodeIsValid(listNode->arcHead) && SPQRnodeIsValid(listNode->arcTail)); } else From 244e492271331e3a7309676d7c0a55a2fd9a9cf6 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Wed, 12 Jun 2024 16:50:34 +0200 Subject: [PATCH 16/63] Add documentation for network matrix methods --- src/scip/network.c | 128 ++++++++++++++++++++++++++++++++++++++------- src/scip/network.h | 3 +- 2 files changed, 112 insertions(+), 19 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index ad24b14b1d..a72504bca9 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -26,6 +26,88 @@ * @ingroup OTHER_CFILES * @brief Methods for detecting network (sub)matrices * @author Rolf van der Hulst + * Detecting if a matrix is a network matrix can be quite complex. Below is an introductory text, which may help with + * navigating the functions and datastructures in this file by giving a general overview. + * More details can be found in; + * R.P. van der Hulst and M.Walter "A row-wise algorithm for graph realization" + * and + * R.E. Bixby and D.K. Wagner "An almost linear-time algorithm for graph realization" + * for the column-wise algorithm. + * + * The main difficulty with detecting network matrices is that there may exist many pairs of a graph and a spanning tree + * that realize a matrix. The ambiguity of these graphs may be characterized in terms of \f$k\f$-separations on the + * graph associated to the network matrix. An edge partition \f$(E_1,E_2)\f$ is considered a \f$k\f$-separation if + * \f$|E_i|\geq k \f$ holds for \f$i\in\{1,2\}\f$. The ambiguity of network matrices is completely given by all + * the 1-separations and 2-separations of the associated graph(s). In particular, if the graph realizing a + * network matrix is 3-connected, then it is unique, up to inverting all edges of the graph. + * + * A 1-separation given by edge sets \f$(E_1,E_2)\f$, which is given by a singular node that is referred to as an + * articulation node, implies that no column of the network matrix contains edges in both \f$E_1\f$ and \f$E_2\f$. + * Remember that each edge in a realizing graph is associated with a row or column of the network matrix. Then, we have + * a 1-separation exactly when the network matrix contains two (or more) disconnected blocks, where the rows and columns + * of each block are contained in either \f$E_1\f$ or \f$E_2\f$. To obtain a graph realizing our network matrix, we can + * attach the graph realizing \f$E_2\f$ at any node of the graph realizing \f$E_1\f$. + * Thus, we store the graphs corresponding to the connected blocks of the network matrix separately. + * Each block has a 2-connected realization graph. + * + * If a graph \f$G\f$ realizing the network matrix has a 2-separation \f$(E_1,E_2)\f$ at vertices u and v, then we can + * obtain an another graph representing the network matrix, by inverting the direction of all edges in \f$E_2\f$ and + * replacing u by v and vice-versa, to obtain a different graph that realizes the network matrix. One can also imagine + * adding a virtual edge \f$e'=\{u,v\}\f$ to both E_1 and E_2. In the trivial realization, we simply map the head of + * \f$e'\f$ in \f$E_1\f$ to the head of \f$e'\f$ in \f$E_2\f$ and remove \f$e'\f$ from both graphs. In the second + * realization we do the same thing, but first invert all the edges in \f$E_2\f$, including \f$e'\f$. + * An SPQR tree \f$\mathcal{T}=(\mathcal{V},\mathcal{E})\f$ is a tree data structure that represents the structure of + * all 2-separations in a 2-connected graph. Each member \f$\nu\in\mathcal{V}\f$ has an associated skeleton graph that + * has one of four different types; + * (S) - The member's skeleton graph is a cycle with at least 3 edges (also referred to as series or polygon) + * (P) - The member's skeleton graph consists of at least 3 parallel edges and 2 nodes (also referred to as bond) + * (Q) - The member's skeleton graph consists of at most 2 edges connecting two nodes (also referred to as loop) + * (R) - The member's skeleton graph is 3-connected and consists of at least 4 edges. + * + * An SPQR tree is considered minimal if it has no P-P or S-S connections. Each connected matrix has a unique minimal + * SPQR tree. Each edge \f$\{\nu,\mu\}\in\mathcal{E}\f$ defines a 2-separation of the underlying graph. In particular, + * each edge has one virtual edge in the member graph that it connects to the other member graph in the edge. + * + * We can obtain a realization of the graph underlying the network matrix by doing the following operations; + * 1. Permute the edges of each (S)-member arbitrarily + * 2. For each edge in $\mathcal{E}$, pick one of the two orientations of the virtual edges and merge the adjacent + * member graphs accordingly. + * In this way, all the graphs given by the network matrix are represented. In order to efficiently perform the merge + * of two member graphs, the member and node labels are given by union-find datastructures. Additionally, we also + * introduce a signed-union find datastructure on the arcs of the graphs, so that we can efficiently invert the arcs + * of one side of a 2-separation. + * The 1-separations can be handled by storing an SPQR forest, with a (minimal) SPQR tree for every connected block + * of the network matrix. + * + * For adding a column to the network matrix, one can show that one can add a column only if the nonzeros of the column + * Form a path with the correct signs in some graph represented by the network matrix. We solve the problem for each + * graph represented by the network matrix simultaneously by decomposing over the SPQR tree. First, we compute the path + * in each member. Then, we attempt to combine the paths by orienting the 2-separations so that the different member + * paths form a path in the represented graph. + * If some member has a set of edges that do not form a path, we can terminate. + * An important step is 'propagation'; when we are in a leaf node of the sub-SPQR tree containing path edges and the + * path in our leaf node forms a cycle with the virtual arc e connecting to the rest of the sub-tree, then we can + * remove the leaf node from the SPQR tree and mark the virtual arc f that is paired with e. + * After performing all such propagations, the sub-SPQR tree should form a path. Then, we merge these members into one, + * forming a single path. By adding the new column edge to the end nodes of this path, we form a new member of type R. + * Finally, we can easily join the paths of multiple SPQR trees using a series node to obtain the final path. + * + * The ideas for the row-wise algorithm have many parallels with the column-wise algorithm. One can add a row to a + * network matrix if and only if a node is 'splittable' with respect to a certain auxilliary graph formed by the nonzero + * columns indices of the row, for a graph represented by the network matrix. In particular, this auxilliary graph must + * be a directed bipartite graph; then, the arcs incident to the given node can be reassigned to two new nodes, so that + * the paths of the columns corresponding to the nonzeros of the row can be elongated to contain the new row, which is + * placed between the two new nodes. + * Similarly to the column-case, splittability of each graph represented by the network matrix can be computed at once + * by computing the splittability (and the corresponding bipartition) of every member graph. + * Similarly to the column algorithm, we can propagate; If a member is a leaf of the SPQR tree and both nodes of the + * 2-separation connecting it to the rest of graph are splittable, then we can and update our datastructures to. + * Finally, we are left with some minimal subtree with splittable vertices for each member graph. If we can merge all + * splittable vertices of the member graphs in the subtree into a single splittable vertex, then we perform this merge, + * and split this vertex. This yields us a new, larger node of type R. + * + * + * TODO: fix tracking connectivity more cleanly, should not be left up to the algorithms ideally */ #include "scip/network.h" @@ -861,12 +943,13 @@ static SCIP_Bool arcIsTree( return SPQRelementIsRow(dec->arcs[arc].element); } +/** Output data structure that stores both the arc's sign and representative*/ typedef struct { spqr_arc representative; SCIP_Bool reversed; } ArcSign; -//find + #ifndef NDEBUG static SCIP_Bool arcIsRepresentative( @@ -1017,14 +1100,15 @@ static spqr_node findEffectiveArcTailNoCompression( } } -///Merge for signed union find of the arc directions. -///Is not symmetric, in the sense that the arc directions of coponent first are guaranteed not to change but those of second may change -///Based on whether one wants the reflection or not +/** Merges the sign union-find structures for two arc sets. If reflectRelative is set to true then all arcs of the + * represented by the second arc are reversed w.r.t. their current orientation. Otherwise, all arcs keep the same + * reversed status with respect to the root node of the union find tree. + */ static spqr_arc mergeArcSigns( - SCIP_NETMATDECDATA* dec, - spqr_arc first, - spqr_arc second, - SCIP_Bool reflectRelative + SCIP_NETMATDECDATA* dec, /**< The decomposition data structure */ + spqr_arc first, /**< Representative arc of the first arc set */ + spqr_arc second, /**< Representative arc of the second arc set */ + SCIP_Bool reflectRelative /**< Should all arcs in the second arc set be reversed?*/ ) { assert(dec); @@ -1035,7 +1119,7 @@ static spqr_arc mergeArcSigns( assert(second < dec->memArcs); //The rank is stored as a negative number: we decrement it making the negative number larger. - // We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement. + //We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement. spqr_member firstRank = dec->arcs[first].representative; spqr_member secondRank = dec->arcs[second].representative; @@ -1049,6 +1133,7 @@ static spqr_arc mergeArcSigns( --dec->arcs[first].representative; } //These boolean formula's cover all 16 possible cases, such that the relative orientation of the first is not changed + //but the relative orientation of the second is changed based on whether we want to reflect. SCIP_Bool equal = dec->arcs[first].reversed == dec->arcs[second].reversed; dec->arcs[second].reversed = ( equal == reflectRelative ); if( firstRank > secondRank ) @@ -1220,7 +1305,7 @@ static SCIP_RETCODE netMatDecDataCreate( } static void netMatDecDataFree( - SCIP_NETMATDECDATA** pdec /**< pointer to the network matrix decomposition to freed */ + SCIP_NETMATDECDATA** pdec /**< pointer to the network matrix decomposition to freed */ ){ assert(pdec); assert(*pdec); @@ -1686,7 +1771,6 @@ static SCIP_RETCODE createStandaloneParallel( return SCIP_OKAY; } -//TODO: fix tracking connectivity more cleanly, should not be left up to the algorithms ideally static SCIP_RETCODE createConnectedParallel( SCIP_NETMATDECDATA* dec, spqr_col* columns, @@ -2619,6 +2703,9 @@ static int maxValue( return ( a > b ) ? a : b; } +/** ---------- START functions for column addition -------------------------------------------------------------------*/ + +/**< Path arcs are all those arcs that correspond to nonzeros of the column to be added */ typedef int path_arc_id; #define INVALID_PATH_ARC (-1) @@ -2632,14 +2719,18 @@ static SCIP_Bool pathArcIsValid(const path_arc_id arc) return !pathArcIsInvalid(arc); } +/** A forward linked list-node for the path arcs. Contains a pointer to the next path arc in the member graph and the + * next path arc in the SPQR tree. We additionally store copies of the corresponding arc's node indices. + */ typedef struct { - spqr_arc arc; - spqr_node arcHead;//These can be used in various places to prevent additional find()'s - spqr_node arcTail; - path_arc_id nextMember; - path_arc_id nextOverall; - SCIP_Bool reversed; + spqr_arc arc; /**< The arc corresponding to the path arc*/ + spqr_node arcHead; /**< A copy of the arc's head node index */ + spqr_node arcTail; /**< A copy of the arc's tail node index */ + path_arc_id nextMember; /**< Array index of the next path arc in the member path arc linked list*/ + path_arc_id nextOverall; /**< Array index of the next path arc in the total path arc linked list */ + SCIP_Bool reversed; /**< Is the path arc occuring forwards or backwards in the path? + * Corresponds to the sign of the nonzero */ } PathArcListNode; typedef int reduced_member_id; @@ -5666,8 +5757,9 @@ static SCIP_RETCODE SCIPnetcoladdAdd( return SCIP_OKAY; } +/** ---------- END functions for column addition ---------------------------------------------------------------------*/ - +/** ---------- START functions for row addition ----------------------------------------------------------------------*/ static int minValue( int a, diff --git a/src/scip/network.h b/src/scip/network.h index 777b988bca..bbeca2b622 100644 --- a/src/scip/network.h +++ b/src/scip/network.h @@ -50,11 +50,12 @@ * Note that all addition algorithms expect that each nonzero is given exactly once and not more often; in particular, * it is up to the user to ensure this when interleaving column and row addition steps. * - * More details can be found in; + * More details can be found in: * - R.P. van der Hulst and M.Walter "A row-wise algorithm for graph realization" * - R.E. Bixby and D.K. Wagner "An almost linear-time algorithm for graph realization" * Note that although these publications contain the methods for undirected graphs (and binary matrices), * their ideas are relatively easily extended to directed graphs and ternary matrices. + * Implementation details are described in further detail in network.c */ /** TODO: add method that realizes a SCIP digraph from the decomposition */ From 0c8a0cd2862759724b320004d073580c180a867d Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Wed, 19 Jun 2024 11:27:56 +0200 Subject: [PATCH 17/63] Add documentation for column addition --- src/scip/network.c | 233 ++++++++++++++++++++++++++------------------- 1 file changed, 136 insertions(+), 97 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index a72504bca9..a70eae0bed 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -106,6 +106,14 @@ * splittable vertices of the member graphs in the subtree into a single splittable vertex, then we perform this merge, * and split this vertex. This yields us a new, larger node of type R. * + * Implementation notes: + * 1. Quite a few algorithms used for network matrix detection are recursive in nature. However, recursive calls can + * cause stack overflows, particularly with large graphs. Quite frequently in the code, we need to allocate the + * call-data of these algorithms on the heap, instead, and use while loops to simulate the recursion. + * + * 2. In order to make the code fast in practice, a lot of emphasis is put on reusing allocated memory and avoiding + * allocations. In particular for the column-wise algorithm, even allocating and zeroing an array of size m+n for an + * m x n matrix can significantly slow down the code! * * TODO: fix tracking connectivity more cleanly, should not be left up to the algorithms ideally */ @@ -2733,6 +2741,8 @@ typedef struct * Corresponds to the sign of the nonzero */ } PathArcListNode; +/**< Index for the reduced members, which are the members of the sub-SPQR tree containing all path arcs. + * Note that these are indexed separately from the members of the SPQR tree! */ typedef int reduced_member_id; #define INVALID_REDUCED_MEMBER (-1) @@ -2746,22 +2756,29 @@ static SCIP_Bool reducedMemberIsValid(const reduced_member_id id) return !reducedMemberIsInvalid(id); } +/**< Array index for the children of a reduced member */ typedef int children_idx; +/**< Type of the member, signifies to what degree we processed the member and how to treat with it when updating the + * graph */ typedef enum { - REDUCEDMEMBER_TYPE_UNASSIGNED = 0, - REDUCEDMEMBER_TYPE_CYCLE = 1, - REDUCEDMEMBER_TYPE_MERGED = 2, - REDUCEDMEMBER_TYPE_NOT_NETWORK = 3 + REDUCEDMEMBER_TYPE_UNASSIGNED = 0, /**< We have not yet decided if the reduced member contains a valid path */ + REDUCEDMEMBER_TYPE_CYCLE = 1, /**< The reduced member is a leaf node of the SPQR tree and contains a path + * that forms a cycle with the corresponding virtual edge, i.e. + * it is propagated */ + REDUCEDMEMBER_TYPE_MERGED = 2, /**< The reduced member contains a path and is not propagated */ + REDUCEDMEMBER_TYPE_NOT_NETWORK = 3 /**< The reduced member does not have a valid path or can not be oriented + * to form a valid path with the other members. */ } ReducedMemberType; +/**< Defines the structure of the path in the reduced member */ typedef enum { - INTO_HEAD = 0, - INTO_TAIL = 1, - OUT_HEAD = 2, - OUT_TAIL = 3 + INTO_HEAD = 0, /**< The directed path goes into the head of the virtual arc */ + INTO_TAIL = 1, /**< The directed path goes into the tail of the virtual arc */ + OUT_HEAD = 2, /**< The directed path goes out of the head of the virtual arc */ + OUT_TAIL = 3 /**< The directed path goes out of the tail of the virtual arc */ } MemberPathType; static SCIP_Bool isInto(MemberPathType type) @@ -2774,107 +2791,130 @@ static SCIP_Bool isHead(MemberPathType type) return type == INTO_HEAD || type == OUT_HEAD; } +/**< A struct that keeps track of the relevant data for the members that are in the subtree given by the specified row + * arcs. We typically call these 'reduced' members (members of the reduced tree). */ typedef struct { - spqr_member member; - spqr_member rootMember; - int depth; - ReducedMemberType type; - reduced_member_id parent; + spqr_member member; /**< The id of the member */ + spqr_member rootMember; /**< The root member of the arborescence that contains this member */ + int depth; /**< The depth of this member in the arborescence */ + ReducedMemberType type; /**< The type of the member */ + reduced_member_id parent; /**< The reduced member id of the parent of this reduced member */ - children_idx firstChild; - children_idx numChildren; + children_idx firstChild; /**< The index of the first child in the children array. */ + children_idx numChildren; /**< The number of children in the arborescence of this reduced member */ - path_arc_id firstPathArc; - int numPathArcs; + path_arc_id firstPathArc; /**< Head of the linked list containing the path arcs */ + int numPathArcs; /**< The number of path arcs in the linked list */ SCIP_Bool reverseArcs; - spqr_node rigidPathStart; - spqr_node rigidPathEnd; - - SCIP_Bool pathBackwards; - - int numPropagatedChildren; - int componentIndex; - - MemberPathType pathType; - reduced_member_id nextPathMember; - SCIP_Bool nextPathMemberIsParent; - spqr_arc pathSourceArc; - spqr_arc pathTargetArc; + spqr_node rigidPathStart; /**< The start node of the path. Only used for Rigid/3-connected nodes */ + spqr_node rigidPathEnd; /**< The end node of the path. Only used for Rigid/3-connected nodes */ + + SCIP_Bool pathBackwards; /**< Indicates if the path direction is reversed with respect to the + * default orientation within series and parallel members. */ + + int numPropagatedChildren; /**< Counts the number of children that are cycles that propagated to this + * reduced member */ + int componentIndex; /**< Stores the index of the component of the SPQR forest that this reduced + * member is contained in. */ + + MemberPathType pathType; /**< The type of the path with respect to the virtual arc*/ + reduced_member_id nextPathMember; /**< Indicates the id of the next reduced member in the path during merging. + * During merging, the SPQR tree must be a path itself. */ + SCIP_Bool nextPathMemberIsParent; /**< Indicates if the next reduced member in the path is a parent of + * this member */ + spqr_arc pathSourceArc; /**< The virtual arc from where the path originates */ + spqr_arc pathTargetArc; /**< The virtual arc where the path has to go */ } SPQRColReducedMember; +/**< Keeps track of the data relevant for each SPQR tree in the SPQR forest. */ typedef struct { - int rootDepth; - reduced_member_id root; + int rootDepth; /**< The depth of the root node of the subtree in the arborescence */ + reduced_member_id root; /**< The reduced member id of the root */ - reduced_member_id pathEndMembers[2]; - int numPathEndMembers; + reduced_member_id pathEndMembers[2]; /**< The reduced members that contain the ends of the path */ + int numPathEndMembers; /**< The number of reduced members that contain an end of the path */ } SPQRColReducedComponent; +/**< Keeps track of the reduced member and the reduced member that is the root of the arborescence for a single member*/ typedef struct { - reduced_member_id reducedMember; - reduced_member_id rootDepthMinimizer; + reduced_member_id reducedMember; /**< The ID of the associated reduced member */ + reduced_member_id rootDepthMinimizer; /**< The ID of the reduced member that is the root of the arborescence this + * reduced member is contained in. */ } MemberInfo; +/**< Data to be used for the recursive algorithms that creates the sub-SPQR trees that contain the path edges */ typedef struct { - spqr_member member; + spqr_member member; /**< The current member */ } CreateReducedMembersCallstack; +/**< The main datastructure that manages all the data for column-addition in network matrices */ typedef struct { - SCIP_Bool remainsNetwork; - - SPQRColReducedMember* reducedMembers; - int memReducedMembers; - int numReducedMembers; - - SPQRColReducedComponent* reducedComponents; - int memReducedComponents; - int numReducedComponents; - - MemberInfo* memberInformation; - int memMemberInformation; - int numMemberInformation; - - reduced_member_id* childrenStorage; - int memChildrenStorage; - int numChildrenStorage; - - PathArcListNode* pathArcs; - int memPathArcs; - int numPathArcs; - path_arc_id firstOverallPathArc; - - int* nodeInPathDegree; - int* nodeOutPathDegree; - int memNodePathDegree; - - SCIP_Bool* arcInPath; - SCIP_Bool* arcInPathReversed; - int memArcsInPath; - - CreateReducedMembersCallstack* createReducedMembersCallStack; - int memCreateReducedMembersCallStack; - - spqr_col newColIndex; - - spqr_row* newRowArcs; - SCIP_Bool* newRowArcReversed; - int memNewRowArcs; - int numNewRowArcs; - - spqr_arc* decompositionRowArcs; - SCIP_Bool* decompositionArcReversed; - int memDecompositionRowArcs; - int numDecompositionRowArcs; - - spqr_member* leafMembers; - int numLeafMembers; - int memLeafMembers; + SCIP_Bool remainsNetwork; /**< Does the addition of the current column give a network matrix? */ + + SPQRColReducedMember* reducedMembers; /**< The array of reduced members, that form the subtree containing the + * rows of the current column.*/ + int memReducedMembers; /**< Number of allocated slots in the reduced member array */ + int numReducedMembers; /**< Number of used slots in the reduced member array */ + + SPQRColReducedComponent* reducedComponents;/**< The array of reduced components, + * that represent the SPQR trees in the SPQR forest */ + int memReducedComponents; /**< Number of allocated slots in the reduced component array */ + int numReducedComponents; /**< Number of used slots in the reduced component array */ + + MemberInfo* memberInformation; /**< Array with member information; tracks the reduced member id that + * corresponds to every member in the decomposition. */ + int memMemberInformation; /**< Number of allocated slots in the member information array */ + int numMemberInformation; /**< Number of used slots in the member information array */ + + reduced_member_id* childrenStorage; /**< Array that stores the children of the reduced member arborescences. + * Each reduced member has a 'firstChild' field and a length, that points + * to the subarray within this array with its children. This array is + * shared here in order to minimize allocations across iterations. */ + int memChildrenStorage; /**< Number of allocated slots for the children storage array */ + int numChildrenStorage; /**< Number of used slots for the children storage array */ + + PathArcListNode* pathArcs; /**< Array that contains the linked-list nodes of the path arcs, that + * correspond to the rows of the current column. */ + int memPathArcs; /**< Number of allocated slots for the path arc array */ + int numPathArcs; /**< Number of used slots for the path arc array */ + path_arc_id firstOverallPathArc; /**< Head node of the linked list containing all path arcs */ + + int* nodeInPathDegree; /**< Array that contains the in degree of all nodes */ + int* nodeOutPathDegree; /**< Array that contains the out degree of all nodes */ + int memNodePathDegree; /**< The number of allocated slots for the node-degree arrays */ + + SCIP_Bool* arcInPath; /**< Is the given arc in the path? */ + SCIP_Bool* arcInPathReversed; /**< Is the given arc's direction reversed in the path? */ + int memArcsInPath; /**< The number of allocated slots for the arcInPath(Reversed) arrays */ + + CreateReducedMembersCallstack* createReducedMembersCallStack; /**< Callstack for createReducedMembers() */ + int memCreateReducedMembersCallStack; /**< Allocated memory for callstack for createReducedMembers() */ + + spqr_col newColIndex; /**< The index of the new column to be added */ + + spqr_row* newRowArcs; /**< The row indices of the nonzeros of the column to be added, that are + * not yet in the decomposition.*/ + SCIP_Bool* newRowArcReversed; /**< True if the nonzero corresponding to the row index is -1, + * false otherwise */ + int memNewRowArcs; /**< Number of allocated slots in newRowArcs(Reversed) */ + int numNewRowArcs; /**< Number of new rows in the column to be added */ + + spqr_arc* decompositionRowArcs; /**< For each row nonzero that is in the decomposition, + * stores the corresponding decomposition arc */ + SCIP_Bool* decompositionArcReversed; /**< For each row nonzero that is in the decomposition, + * stores whether the corresponding decomposition arc is reversed */ + int memDecompositionRowArcs; /**< Number of allocated slots in decompositionRowArcs(Reversed) */ + int numDecompositionRowArcs; /**< Number of used slots in decompositionRowArcs(Reversed)*/ + + spqr_member* leafMembers; /**< Array that stores the leaf members of the SPQR forest */ + int numLeafMembers; /**< Number of used slots in leafMembers array*/ + int memLeafMembers; /**< Number of allocated slots in leafMembers array*/ } SCIP_NETCOLADD; static void cleanupPreviousIteration( @@ -3386,8 +3426,7 @@ static SCIP_RETCODE createPathArcs( } -/** - * Saves the information of the current row and partitions it based on whether or not the given columns are +/** Saves the information of the current row and partitions it based on whether or not the given columns are * already part of the decomposition. */ static SCIP_RETCODE @@ -3546,7 +3585,6 @@ static void determineSingleRigidType( } } -//TODO: type seems somewhat duplicate static void determineSingleComponentType( SCIP_NETMATDECDATA* dec, SCIP_NETCOLADD* newCol, @@ -4578,16 +4616,16 @@ static SCIP_RETCODE SCIPnetcoladdCheck( return SCIP_OKAY; } -///Contains the data which tells us where to store the new column after the graph has been modified -///In case member is a parallel or series node, the respective new column and rows are placed in parallel (or series) with it -///Otherwise, the rigid member has a free spot between firstNode and secondNode +/**< Struct that contains the data that tells us how to add the new column after the graph has been modified + * In the case the member is a series or parallel node, the new column and rows are placed in series or parallel, + * respectively. Otherwise, the edge can be added between head and tail in a rigid member*/ typedef struct { - spqr_member member; - spqr_node head; - spqr_node tail; - spqr_arc representative; - SCIP_Bool reversed; + spqr_member member; /**< The member where the new column should be added */ + spqr_node head; /**< The head node of the new column (rigid members only)*/ + spqr_node tail; /**< The tail node of the new column (rigid members only)*/ + spqr_arc representative; /**< The representative arc of the new column */ + SCIP_Bool reversed; /**< Is the new column reversed? */ } NewColInformation; static NewColInformation emptyNewColInformation(void) @@ -5649,6 +5687,7 @@ static SCIP_RETCODE transformComponent( return SCIP_OKAY; } + static SCIP_Bool SCIPnetcoladdRemainsNetwork(const SCIP_NETCOLADD* newCol) { return newCol->remainsNetwork; From 358b400bc5131432e097dc75cc7f74f458c35d0b Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Wed, 19 Jun 2024 13:53:43 +0200 Subject: [PATCH 18/63] Add documentation for network row addition --- src/scip/network.c | 297 ++++++++++++++++++++++++--------------------- 1 file changed, 159 insertions(+), 138 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index a70eae0bed..6219b55913 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -104,7 +104,7 @@ * 2-separation connecting it to the rest of graph are splittable, then we can and update our datastructures to. * Finally, we are left with some minimal subtree with splittable vertices for each member graph. If we can merge all * splittable vertices of the member graphs in the subtree into a single splittable vertex, then we perform this merge, - * and split this vertex. This yields us a new, larger node of type R. + * and split this vertex. This yields us a new, larger node of type R (rigid). * * Implementation notes: * 1. Quite a few algorithms used for network matrix detection are recursive in nature. However, recursive calls can @@ -2807,7 +2807,7 @@ typedef struct path_arc_id firstPathArc; /**< Head of the linked list containing the path arcs */ int numPathArcs; /**< The number of path arcs in the linked list */ - SCIP_Bool reverseArcs; + SCIP_Bool reverseArcs; /**< Will the arcs in this component be reversed? */ spqr_node rigidPathStart; /**< The start node of the path. Only used for Rigid/3-connected nodes */ spqr_node rigidPathEnd; /**< The end node of the path. Only used for Rigid/3-connected nodes */ @@ -4075,7 +4075,7 @@ static void determinePathRigidType( } } - //TODO: these can probably be simplified significantly, but this might pose risk of introducing incorrect assumptions + //TODO: this if-else tree to compute two booleans can probably be simplified significantly SCIP_Bool isBad = FALSE; if( isInto(previousType) == isHead(previousType)) { @@ -5808,6 +5808,7 @@ static int minValue( return a < b ? a : b; } +/**< Index type for the nonzeros of the new row, e.g. the columns whose tree path must be elongated with the new row */ typedef int cut_arc_id; #define INVALID_CUT_ARC (-1) @@ -5821,198 +5822,222 @@ static SCIP_Bool cutArcIsValid(const cut_arc_id arc) return !cutArcIsInvalid(arc); } +/**< Linked list node for the cut arcs. Contains links to the next cut arc in the whole decomposition and the next cut + * arc in the current member. */ typedef struct -{//TODO:test if memory overhead of pointers is worth it? - spqr_arc arc; - spqr_node arcHead; - spqr_node arcTail; - cut_arc_id nextMember; - cut_arc_id nextOverall; - SCIP_Bool arcReversed; +{ + spqr_arc arc; /**< The arc id */ + spqr_node arcHead; /**< The arc's head node */ + spqr_node arcTail; /**< The arc's tail node */ + cut_arc_id nextMember; /**< Index to next linked list node of the linked list containing the cut arcs + * of the arcs member.*/ + cut_arc_id nextOverall; /**< Index to next linked list node of the linked list containing the cut arcs + * of the complete decomposition.*/ + SCIP_Bool arcReversed; /**< Should the new row have reverse direction in the cut arcs path? */ } CutArcListNode; - +/**< Type of the reduced member; indicates whether the member is processed, and whether it should be merged or left + * the same. */ typedef enum { - TYPE_UNDETERMINED = 0, - TYPE_PROPAGATED = 1, - TYPE_MERGED = 2, - TYPE_NOT_NETWORK = 3 + TYPE_UNDETERMINED = 0, /**< The type of this member has not yet been determined */ + TYPE_PROPAGATED = 1, /**< The member contains cut arcs, but should not be merged */ + TYPE_MERGED = 2, /**< This member should be merged into a rigid member */ + TYPE_NOT_NETWORK = 3 /**< This member implies that the addition of the new row + * does not create a network matrix */ } RowReducedMemberType; - +/**< Stores for every vertex information needed for computing articulation nodes in rigid members. */ typedef struct { - int low; - int discoveryTime; + int low; /**< What is the lowest discovery time vertex that can be reached from this + * subtree?*/ + int discoveryTime; /**< When was the vertex first discovered? */ } ArticulationNodeInformation; -//We allocate the callstacks of recursive algorithms (usually DFS, bounded by some linear number of calls) -//If one does not do this, we overflow the stack for large matrices/graphs through the number of recursive function calls -//Then, we can write the recursive algorithms as while loops and allocate the function call data on the heap, preventing -//Stack overflows +/**< Call stack data structure for performing DFS on the rigid member graphs */ typedef struct { - spqr_node node; - spqr_arc nodeArc; + spqr_node node; /**< The current node of the DFS call */ + spqr_arc nodeArc; /**< The current arc of the node that is being processed */ } DFSCallData; +/**< Call stack data structure for merging the SPQR tree into a single rigid member */ typedef struct { - children_idx currentChild; - reduced_member_id id; + children_idx currentChild; /**< The index of the current child that is being merged */ + reduced_member_id id; /**< The index of the current member */ } MergeTreeCallData; +/**< Call stack data structure that determines whether a bipartition of the nodes exists that satisfies the requirements*/ typedef struct { - spqr_node node; - spqr_arc arc; + spqr_node node; /**< The current node of the call */ + spqr_arc arc; /**< The current arc that is being processed */ } ColorDFSCallData; +/**< Call stack data structure for recursive algorithm to find articulation point in rigid member graphs (Tarjan) */ typedef struct { - spqr_arc arc; - spqr_node node; - spqr_node parent; - SCIP_Bool isAP; + spqr_arc arc; /**< The arc that is currently being processed */ + spqr_node node; /**< The node that is currently being processed */ + spqr_node parent; /**< The node's parent */ + SCIP_Bool isAP; /**< Is the current node an articulation point? */ } ArticulationPointCallStack; +/**< Colors assigned to the node in the partitioning algorithm. It must either be in the source or the sink partition.*/ typedef enum { - UNCOLORED = 0, - COLOR_SOURCE = 1, - COLOR_SINK = 2 + UNCOLORED = 0, /**< The current node has not yet been processed */ + COLOR_SOURCE = 1, /**< The current node belongs to the source partition */ + COLOR_SINK = 2 /**< The current node belongs to the sink partition */ } COLOR_STATUS; +/**< Struct that stores the data of a single reduced member. */ typedef struct { - spqr_member member; - spqr_member rootMember; - int depth; - RowReducedMemberType type; - reduced_member_id parent; - - children_idx firstChild; - children_idx numChildren; - children_idx numPropagatedChildren; - - cut_arc_id firstCutArc; - int numCutArcs; - - //For non-rigid members - spqr_arc splitArc; - SCIP_Bool splitHead; //Otherwise the tail of this arc is split - SCIP_Bool otherIsSource;//Otherwise the other end node is part of the sink partition. - // For non-rigid members this refers to splitArc, for rigid members it refers to articulation arc - - //For rigid members - spqr_node otherNode; - spqr_node splitNode; - SCIP_Bool allHaveCommonNode; - SCIP_Bool otherNodeSplit; - SCIP_Bool willBeReversed; - spqr_arc articulationArc; - spqr_node coloredNode;//points to a colored node so that we can efficiently zero out the colors again. + spqr_member member; /**< The id of the decomposition member */ + spqr_member rootMember; /**< The decomposition member that is the root node of the arborescence + * containing this member */ + int depth; /**< The depth of this member in the arborescence */ + RowReducedMemberType type; /**< The type of the member */ + reduced_member_id parent; /**< The reduced member id of the parent of this reduced member */ + children_idx firstChild; /**< The index of the first child in the children array. */ + children_idx numChildren; /**< The number of children in the arborescence of this reduced member */ + children_idx numPropagatedChildren; /**< Counts the number of children that are propagated to this reduced + * member */ + + cut_arc_id firstCutArc; /**< Head of the linked list containing the cut arcs */ + int numCutArcs; /**< The number of cut arcs in the linked list */ + + spqr_arc splitArc; /**< An arc adjacent to the split node. */ + SCIP_Bool splitHead; /**< Is the head or the tail of the split arc split in the realization? */ + SCIP_Bool otherIsSource; /**< Is the nonsplit node of the split arc in the source or the sink + * partition? In rigid members this refers to the articulation arc. */ + + /* Rigid member fields */ + spqr_node otherNode; /**< The other nonsplit node adjacent to the virtual edge */ + spqr_node splitNode; /**< The node to be split */ + SCIP_Bool allHaveCommonNode; /**< Do all cut edges share a common node? */ + SCIP_Bool otherNodeSplit; /**< Is the other node a split node, too? */ + SCIP_Bool willBeReversed; /**< Will all the arcs in this component be reversed? */ + spqr_arc articulationArc; /**< Indicates the arc between the split node and the other ndoe, if both + * are splittable. */ + spqr_node coloredNode; /**< Points to a colored node so that we can efficiently zero out colors + * by backtracking our DFS */ } SPQRRowReducedMember; +/**< Keeps track of the data relevant for each SPQR tree in the SPQR forest. */ typedef struct { - int rootDepth; - reduced_member_id root; + int rootDepth; /**< The depth of the root node of the subtree in the arborescence */ + reduced_member_id root; /**< The reduced member id of the root */ } SPQRRowReducedComponent; +/**< The main datastructure that manages all the data for row-addition in network matrices */ typedef struct { - SCIP_Bool remainsNetwork; + SCIP_Bool remainsNetwork; /**< Does the addition of the current row give a network matrix? */ - SPQRRowReducedMember* reducedMembers; - int memReducedMembers; - int numReducedMembers; + SPQRRowReducedMember* reducedMembers; /**< The array of reduced members, that form the subtree containing the + * rows of the current column.*/ + int memReducedMembers; /**< Number of allocated slots in the reduced member array */ + int numReducedMembers; /**< Number of used slots in the reduced member array */ - SPQRRowReducedComponent* reducedComponents; - int memReducedComponents; - int numReducedComponents; + SPQRRowReducedComponent* reducedComponents;/**< The array of reduced components, + * that represent the SPQR trees in the SPQR forest */ + int memReducedComponents; /**< Number of allocated slots in the reduced component array */ + int numReducedComponents; /**< Number of used slots in the reduced component array */ - MemberInfo* memberInformation; - int memMemberInformation; - int numMemberInformation; + MemberInfo* memberInformation; /**< Array with member information; tracks the reduced member id that + * corresponds to every member in the decomposition. */ + int memMemberInformation; /**< Number of allocated slots in the member information array */ + int numMemberInformation; /**< Number of used slots in the member information array */ - reduced_member_id* childrenStorage; - int memChildrenStorage; - int numChildrenStorage; + reduced_member_id* childrenStorage; /**< Array that stores the children of the reduced member arborescences. + * Each reduced member has a 'firstChild' field and a length, that points + * to the subarray within this array with its children. This array is + * shared here in order to minimize allocations across iterations. */ + int memChildrenStorage; /**< Number of allocated slots for the children storage array */ + int numChildrenStorage; /**< Number of used slots for the children storage array */ - CutArcListNode* cutArcs; - int memCutArcs; - int numCutArcs; - cut_arc_id firstOverallCutArc; + CutArcListNode* cutArcs; /**< Array containing the linked list nodes of the cut arcs */ + int memCutArcs; /**< Number of allocated entries in cutArcs */ + int numCutArcs; /**< Number of used entries in cutArcs */ + cut_arc_id firstOverallCutArc; /**< Index of the head node of the linked list containing all cut arcs */ - spqr_row newRowIndex; + spqr_row newRowIndex; /**< The index of the new row to be added */ - spqr_col* newColumnArcs; - SCIP_Bool* newColumnReversed; - int memColumnArcs; - int numColumnArcs; + spqr_col* newColumnArcs; /**< The nonzero columns in the new row that do not yet occur in the + * decomposition */ + SCIP_Bool* newColumnReversed; /**< True if the nonzero entry of the new column is -1, False otherwise */ + int memColumnArcs; /**< Number of allocated slots in newColumnArcs/newColumnReversed */ + int numColumnArcs; /**< Number of new columns in the row to be added */ - reduced_member_id* leafMembers; - int numLeafMembers; - int memLeafMembers; + reduced_member_id* leafMembers; /**< Array that stores the leaf members of the SPQR forest */ + int numLeafMembers; /**< Number of used slots in leafMembers array*/ + int memLeafMembers; /**< Number of allocated slots in leafMembers array*/ - spqr_arc* decompositionColumnArcs; - SCIP_Bool* decompositionColumnArcReversed; - int memDecompositionColumnArcs; - int numDecompositionColumnArcs; + spqr_arc* decompositionColumnArcs; /**< For each nonzero column of the new row that is in the decomposition, + * stores the corresponding decomposition arc */ + SCIP_Bool* decompositionColumnArcReversed;/**< For each nonzero column of the new row that is in the decomposition, + * stores whether the corresponding decomposition arc is reversed */ + int memDecompositionColumnArcs; /**< Number of allocated slots in decompositionColumnArcs(Reversed) */ + int numDecompositionColumnArcs; /**< Number of used slots in decompositionColumnArcs(Reversed) */ - SCIP_Bool* isArcCut; - SCIP_Bool* isArcCutReversed; - int numIsArcCut; - int memIsArcCut; + SCIP_Bool* isArcCut; /**< Stores for each arc, if the arc a cut arc?*/ + SCIP_Bool* isArcCutReversed; /**< Is the new row in reverse direction on the arcs cycle? */ + int memIsArcCut; /**< The allocated size of the isArcCut(Reversed) arrays */ - COLOR_STATUS* nodeColors; - int memNodeColors; + COLOR_STATUS* nodeColors; /**< Stores the color of each node */ + int memNodeColors; /**< The allocated size of the nodeColors array */ - spqr_node* articulationNodes; - int numArticulationNodes; - int memArticulationNodes; + spqr_node* articulationNodes; /**< Temp. array for storing articulation nodes of member graph-cut arcs*/ + int numArticulationNodes; /**< Number of used slots in articulation nodes array */ + int memArticulationNodes; /**< Number of allocated slots in articulation nodes array */ - ArticulationNodeInformation* articulationNodeSearchInfo; - int memNodeSearchInfo; + ArticulationNodeInformation* articulationNodeSearchInfo; /**type = TYPE_MERGED; } +/**< A data structure that tells us if the head or tail of a virtual arc is split, and if the other node is in the + * source or the sink partition. */ typedef struct { - SCIP_Bool headSplit; - SCIP_Bool otherIsSource; + SCIP_Bool headSplit; /**< Is the head or tail of the virtual arc split?*/ + SCIP_Bool otherIsSource; /**< Is the non-split node in the source or sink partition? */ } SplitOrientation; static SplitOrientation getRelativeOrientationRigid( @@ -10062,7 +10083,6 @@ static SCIP_RETCODE SCIPnetrowaddCreate( newRow->isArcCut = NULL; newRow->isArcCutReversed = NULL; newRow->memIsArcCut = 0; - newRow->numIsArcCut = 0; newRow->nodeColors = NULL; newRow->memNodeColors = 0; @@ -10294,6 +10314,7 @@ static SCIP_Bool SCIPnetrowaddRemainsNetwork(const SCIP_NETROWADD* rowadd) return rowadd->remainsNetwork; } +/**< A generic data structure that stores a decomposition and can perform both column and row additions */ struct SCIP_Netmatdec{ SCIP_NETMATDECDATA * dec; SCIP_NETROWADD * rowadd; From 3c62ad715c4b66bdac8d872210e5083345f82261 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 23 Jul 2024 10:07:57 +0200 Subject: [PATCH 19/63] Add documentation for network matrix decomposition --- src/scip/network.c | 1064 ++++++++++++++++++++++++++++---------------- 1 file changed, 669 insertions(+), 395 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 6219b55913..10651d5155 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -115,6 +115,9 @@ * allocations. In particular for the column-wise algorithm, even allocating and zeroing an array of size m+n for an * m x n matrix can significantly slow down the code! * + * 3. The graphs of the S,P and Q members do not need to be stored explicitly, as they always have the same structure. + * This also makes it easier to permute edges of S nodes on the fly. + * * TODO: fix tracking connectivity more cleanly, should not be left up to the algorithms ideally */ @@ -411,9 +414,11 @@ typedef struct #ifndef NDEBUG -static SCIP_Bool nodeIsRepresentative( - const SCIP_NETMATDECDATA* dec, - spqr_node node +/**< Check if a node is a representative in the union-find data structure for nodes */ +static +SCIP_Bool nodeIsRepresentative( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_node node /**< The node to check if it is representative */ ) { assert(dec); @@ -425,9 +430,11 @@ static SCIP_Bool nodeIsRepresentative( #endif -static spqr_node findNode( - SCIP_NETMATDECDATA* dec, - spqr_node node +/**< Find the node its representative node in the union-find data structure */ +static +spqr_node findNode( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_node node /**< The node to find the representative for */ ) { assert(dec); @@ -457,9 +464,12 @@ static spqr_node findNode( return root; } -static spqr_node findNodeNoCompression( - const SCIP_NETMATDECDATA* dec, - spqr_node node +/**< Find the node its representative node in the union-find data structure, without compressing the union-find tree. + * Should only be used for debugging or asserts. */ +static +spqr_node findNodeNoCompression( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_node node /**< The node to find the representative for */ ) { assert(dec); @@ -479,9 +489,12 @@ static spqr_node findNodeNoCompression( return root; } -static spqr_node findArcTail( - SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Find the arc's tail node in the union find data structure of the nodes. + * Updates the arc's tail to point to the representative for faster future queries. */ +static +spqr_node findArcTail( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc whose tail we want to find */ ) { assert(dec); @@ -494,9 +507,12 @@ static spqr_node findArcTail( return representative; } -static spqr_node findArcHead( - SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Find the arc's head node in the union find data structure of the nodes. + * Updates the arc's head to point to the representative for faster future queries. */ +static +spqr_node findArcHead( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc whose head we want to find */ ) { assert(dec); @@ -509,9 +525,12 @@ static spqr_node findArcHead( return representative; } -static spqr_node findArcHeadNoCompression( - const SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Find the arc's head node in the union find data structure of the nodes, without compressing the union-find tree. + * Should only be used in debugging statements or asserts. */ +static +spqr_node findArcHeadNoCompression( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc whose head we want to find */ ) { assert(dec); @@ -522,9 +541,12 @@ static spqr_node findArcHeadNoCompression( return representative; } -static spqr_node findArcTailNoCompression( - const SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Find the arc's tail node in the union find data structure of the nodes, without compressing the union-find tree. + * Should only be used in debugging statements or asserts. */ +static +spqr_node findArcTailNoCompression( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc whose tail we want to find */ ) { assert(dec); @@ -535,10 +557,12 @@ static spqr_node findArcTailNoCompression( return representative; } - -static spqr_arc getFirstNodeArc( - const SCIP_NETMATDECDATA* dec, - spqr_node node +/**< Find the first arc in the list of arcs that are adjacent to the given node. + * These arcs form a cyclic linked-list. */ +static +spqr_arc getFirstNodeArc( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_node node /**< The node to find the arc for */ ) { assert(dec); @@ -547,10 +571,13 @@ static spqr_arc getFirstNodeArc( return dec->nodes[node].firstArc; } -static spqr_arc getNextNodeArcNoCompression( - const SCIP_NETMATDECDATA* dec, - spqr_arc arc, - spqr_node node +/**< Given the current arc adjacent to this node, find the next arc in the cyclic linked list of adjacent arcs to the + * given node. This function does not compress the union-find tree, and should only be used in debugging or asserts.*/ +static +spqr_arc getNextNodeArcNoCompression( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc, /**< The current arc that is adjacent to this node */ + spqr_node node /**< The node to which the arc is adjacent */ ) { assert(dec); @@ -561,7 +588,8 @@ static spqr_arc getNextNodeArcNoCompression( if( findArcHeadNoCompression(dec, arc) == node ) { arc = dec->arcs[arc].headArcListNode.next; - } else + } + else { assert(findArcTailNoCompression(dec, arc) == node); arc = dec->arcs[arc].tailArcListNode.next; @@ -569,10 +597,13 @@ static spqr_arc getNextNodeArcNoCompression( return arc; } -static spqr_arc getNextNodeArc( - SCIP_NETMATDECDATA* dec, - spqr_arc arc, - spqr_node node +/**< Given the current arc adjacent to this node, find the next arc in the cyclic linked list of adjacent arcs to the + * given node. */ +static +spqr_arc getNextNodeArc( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc, /**< The current arc that is adjacent to this node */ + spqr_node node /**< The node to which the arc is adjacent */ ) { assert(dec); @@ -583,19 +614,23 @@ static spqr_arc getNextNodeArc( if( findArcHead(dec, arc) == node ) { arc = dec->arcs[arc].headArcListNode.next; - } else + } + else { assert(findArcTailNoCompression(dec, arc) == node); - dec->arcs[arc].tail = node;//This assignment is not necessary but speeds up future queries. + dec->arcs[arc].tail = node; //This assignment is not necessary but speeds up future queries. arc = dec->arcs[arc].tailArcListNode.next; } return arc; } -static spqr_arc getPreviousNodeArc( - SCIP_NETMATDECDATA* dec, - spqr_arc arc, - spqr_node node +/**< Given the current arc adjacent to this node, find the previous arc in the cyclic linked list of adjacent arcs + * to the given node. */ +static +spqr_arc getPreviousNodeArc( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc, /**< The current arc that is adjacent to this node */ + spqr_node node /**< The node to which the arc is adjacent */ ) { assert(dec); @@ -606,7 +641,8 @@ static spqr_arc getPreviousNodeArc( if( findArcHead(dec, arc) == node ) { arc = dec->arcs[arc].headArcListNode.previous; - } else + } + else { assert(findArcTailNoCompression(dec, arc) == node); dec->arcs[arc].tail = node;//This assignment is not necessary but speeds up future queries. @@ -615,13 +651,15 @@ static spqr_arc getPreviousNodeArc( return arc; } -static void mergeNodeArcList( - SCIP_NETMATDECDATA* dec, - spqr_node toMergeInto, - spqr_node toRemove +/**< Update the cyclic node-arc incidence data structure to move all arcs adjacent to one node to another node. + * We typically call this when two nodes are identified with one another, and we need to merge their adjacent arcs. */ +static +void mergeNodeArcList( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_node toMergeInto, /**< The node that we want to give all the arcs of both nodes */ + spqr_node toRemove /**< The node whose arcs we want to remove */ ) { - spqr_arc firstIntoArc = getFirstNodeArc(dec, toMergeInto); spqr_arc firstFromArc = getFirstNodeArc(dec, toRemove); if( SPQRarcIsInvalid(firstIntoArc)) @@ -634,7 +672,8 @@ static void mergeNodeArcList( dec->nodes[toRemove].firstArc = SPQR_INVALID_ARC; return; - } else if( SPQRarcIsInvalid(firstFromArc)) + } + else if( SPQRarcIsInvalid(firstFromArc)) { //Old node has no arcs; we can just return return; @@ -670,9 +709,11 @@ static void mergeNodeArcList( dec->nodes[toRemove].firstArc = SPQR_INVALID_ARC; } -static void arcFlipReversed( - SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Flips the direction a given arc. */ +static +void arcFlipReversed( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc that we want to flip */ ) { assert(dec); @@ -681,10 +722,12 @@ static void arcFlipReversed( dec->arcs[arc].reversed = !dec->arcs[arc].reversed; } -static void arcSetReversed( - SCIP_NETMATDECDATA* dec, - spqr_arc arc, - SCIP_Bool reversed +/**< Sets the direction of a given arc */ +static +void arcSetReversed( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc, /**< The given arc */ + SCIP_Bool reversed /**< Are the head and tail reversed?*/ ) { assert(dec); @@ -693,10 +736,15 @@ static void arcSetReversed( dec->arcs[arc].reversed = reversed; } -static void arcSetRepresentative( - SCIP_NETMATDECDATA* dec, - spqr_arc arc, - spqr_arc representative + +/**< Sets the representative of a given arc. The arcs reversed field is given with respect to the representative. + * In particular, whether an arc is reversed or not is determined by the sign of the path in the signed union-find + * data structure for arcs, which can be computed by multiplying the signs of the individual edges (xor over bools) */ +static +void arcSetRepresentative( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc, /**< The given arc */ + spqr_arc representative /**< The representative to set the arc to */ ) { assert(dec); @@ -706,10 +754,13 @@ static void arcSetRepresentative( dec->arcs[arc].representative = representative; } -static spqr_node mergeNodes( - SCIP_NETMATDECDATA* dec, - spqr_node first, - spqr_node second +/**< Merge two representative nodes (Union operation) in the union-find data structure for nodes. + * Returns the id of the node that becomes representative for both.*/ +static +spqr_node mergeNodes( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_node first, /**< A node to merge */ + spqr_node second /**< A second node to merge */ ) { assert(dec); @@ -720,7 +771,7 @@ static spqr_node mergeNodes( assert(second < dec->memNodes); //The rank is stored as a negative number: we decrement it making the negative number larger. - // We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement. + //We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement. spqr_node firstRank = dec->nodes[first].representativeNode; spqr_node secondRank = dec->nodes[second].representativeNode; if( firstRank > secondRank ) @@ -737,9 +788,12 @@ static spqr_node mergeNodes( return first; } -static SCIP_Bool memberIsRepresentative( - const SCIP_NETMATDECDATA* dec, - spqr_member member + +/**< Check if a member is a representative in the union-find data structure for members*/ +static +SCIP_Bool memberIsRepresentative( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_member member /**< The member to check */ ) { assert(dec); @@ -749,9 +803,11 @@ static SCIP_Bool memberIsRepresentative( return SPQRmemberIsInvalid(dec->members[member].representativeMember); } -static spqr_member findMember( - SCIP_NETMATDECDATA* dec, - spqr_member member +/**< Find the member its representative member in the union-find data structure */ +static +spqr_member findMember( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_member member /**< The member to find the representative for */ ) { assert(dec); @@ -781,9 +837,12 @@ static spqr_member findMember( return root; } -static spqr_member findMemberNoCompression( - const SCIP_NETMATDECDATA* dec, - spqr_member member +/**< Find the member's representative member in the union-find data structure, without compressing the union-find tree. + * Should only be used for debugging or asserts. */ +static +spqr_member findMemberNoCompression( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_member member /**< The member to find the representative for */ ) { assert(dec); @@ -804,10 +863,13 @@ static spqr_member findMemberNoCompression( return root; } -static spqr_member mergeMembers( - SCIP_NETMATDECDATA* dec, - spqr_member first, - spqr_member second +/**< Merge two representative members (Union operation) in the union-find data structure. + * Returns the id of the member that becomes representative for both.*/ +static +spqr_member mergeMembers( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_member first, /**< The first member to merge */ + spqr_member second /**< The second member to merge */ ) { assert(dec); @@ -833,9 +895,11 @@ static spqr_member mergeMembers( return first; } -static spqr_member findArcMember( - SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Finds the member in which the arc is located */ +static +spqr_member findArcMember( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc to find the member for */ ) { assert(dec); @@ -847,9 +911,11 @@ static spqr_member findArcMember( return representative; } -static spqr_member findArcMemberNoCompression( - const SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Finds the member in which the arc is located, without compressing the member union-find tree */ +static +spqr_member findArcMemberNoCompression( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc to find the member for */ ) { assert(dec); @@ -860,9 +926,11 @@ static spqr_member findArcMemberNoCompression( return representative; } -static spqr_member findMemberParent( - SCIP_NETMATDECDATA* dec, - spqr_member member +/**< Find the representative parent member of the given member. Note the given member must be representative. */ +static +spqr_member findMemberParent( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_member member /**< The member to find the parent for. Must be representative. */ ) { assert(dec); @@ -881,9 +949,12 @@ static spqr_member findMemberParent( return parent_representative; } -static spqr_member findMemberParentNoCompression( - const SCIP_NETMATDECDATA* dec, - spqr_member member +/**< Find the representative parent member of the given member. Note the given member must be representative. + * This version does not perform compression of the union-find tree, and should only be used in debug or asserts. */ +static +spqr_member findMemberParentNoCompression( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_member member /**< The member to find the parent for. Must be representative. */ ) { assert(dec); @@ -899,9 +970,11 @@ static spqr_member findMemberParentNoCompression( return parent_representative; } -static spqr_member findArcChildMember( - SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Find the child member associated to the given arc. Can only call for virtual arcs.*/ +static +spqr_member findArcChildMember( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc to find the child member for */ ) { assert(dec); @@ -913,9 +986,12 @@ static spqr_member findArcChildMember( return representative; } -static spqr_member findArcChildMemberNoCompression( - const SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Find the child member associated to the given arc. Can only call for virtual arcs. + * This version does not compress the union-find tree and should only be used for debugging and asserts. */ +static +spqr_member findArcChildMemberNoCompression( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc to find the child member for */ ) { assert(dec); @@ -926,10 +1002,11 @@ static spqr_member findArcChildMemberNoCompression( return representative; } -// Only accounts for CHILD markers, not parent markers! -static SCIP_Bool arcIsMarker( - const SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Checks if the arc has a child member */ +static +SCIP_Bool arcIsChildMarker( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc to check */ ) { assert(dec); @@ -939,9 +1016,11 @@ static SCIP_Bool arcIsMarker( return SPQRmemberIsValid(dec->arcs[arc].childMember); } -static SCIP_Bool arcIsTree( - const SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Check whether the given arc is a tree arc or not, i.e. whether it belongs to a (virtual) row or column or not */ +static +SCIP_Bool arcIsTree( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc to check */ ) { assert(dec); @@ -960,9 +1039,12 @@ typedef struct #ifndef NDEBUG -static SCIP_Bool arcIsRepresentative( - const SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Check if an arc is a representative in the signed union-find data structure for arc directions. In each member, + * exactly one arc is the representative. */ +static +SCIP_Bool arcIsRepresentative( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc to check */ ) { assert(dec); @@ -974,9 +1056,11 @@ static SCIP_Bool arcIsRepresentative( #endif -static ArcSign findArcSign( - SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Find an arcs representative and its direction in the signed union-find data structure for arcs */ +static +ArcSign findArcSign( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc to find the representative and direction for */ ) { assert(dec); @@ -1020,9 +1104,12 @@ static ArcSign findArcSign( return sign; } -static ArcSign findArcSignNoCompression( - const SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Find an arcs representative and its direction in the signed union-find data structure for arcs. + * This version does not compress the union-find tree and should only be used in debug and asserts. */ +static +ArcSign findArcSignNoCompression( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc to find the representative and direction for */ ) { assert(dec); @@ -1047,72 +1134,85 @@ static ArcSign findArcSignNoCompression( return sign; } -//Find the arc tail/head, but accounting for reflection -static spqr_node findEffectiveArcHead( - SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Finds the arcs head, taking into account whether it is reversed by the signed union-find data structure.*/ +static +spqr_node findEffectiveArcHead( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc to find the head for */ ) { assert(dec); if( findArcSign(dec, arc).reversed ) { return findArcTail(dec, arc); - } else + } + else { return findArcHead(dec, arc); } } -static spqr_node findEffectiveArcTail( - SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Finds the arcs tail, taking into account whether it is reversed by the signed union-find data structure.*/ +static +spqr_node findEffectiveArcTail( + SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc to find the tail for */ ) { assert(dec); if( findArcSign(dec, arc).reversed ) { return findArcHead(dec, arc); - } else + } + else { return findArcTail(dec, arc); } } - -static spqr_node findEffectiveArcHeadNoCompression( - const SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Finds the arcs head, taking into account whether it is reversed by the signed union-find data structure. + * This version does not compress the union-find tree and should only be used in debug and asserts. */ +static +spqr_node findEffectiveArcHeadNoCompression( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc to find the head for */ ) { assert(dec); if( findArcSignNoCompression(dec, arc).reversed ) { return findArcTailNoCompression(dec, arc); - } else + } + else { return findArcHeadNoCompression(dec, arc); } } -static spqr_node findEffectiveArcTailNoCompression( - const SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Finds the arcs tail, taking into account whether it is reversed by the signed union-find data structure. + * This version does not compress the union-find tree and should only be used in debug and asserts. */ +static +spqr_node findEffectiveArcTailNoCompression( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc to find the tail for */ ) { assert(dec); if( findArcSignNoCompression(dec, arc).reversed ) { return findArcHeadNoCompression(dec, arc); - } else + } + else { return findArcTailNoCompression(dec, arc); } } -/** Merges the sign union-find structures for two arc sets. If reflectRelative is set to true then all arcs of the +/**< Merges the sign union-find structures for two arc sets. If reflectRelative is set to true then all arcs of the * represented by the second arc are reversed w.r.t. their current orientation. Otherwise, all arcs keep the same * reversed status with respect to the root node of the union find tree. */ -static spqr_arc mergeArcSigns( +static +spqr_arc mergeArcSigns( SCIP_NETMATDECDATA* dec, /**< The decomposition data structure */ spqr_arc first, /**< Representative arc of the first arc set */ spqr_arc second, /**< Representative arc of the second arc set */ @@ -1151,9 +1251,12 @@ static spqr_arc mergeArcSigns( return first; } -static SCIP_Bool arcIsReversedNonRigid( - const SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Checks whether an arc is reversed for arcs in non-rigid members. For non-rigid members, we do not use the + * union-find datastructure, as we can get away without it. */ +static +SCIP_Bool arcIsReversedNonRigid( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc to check */ ) { assert(dec); @@ -1163,10 +1266,11 @@ static SCIP_Bool arcIsReversedNonRigid( return dec->arcs[arc].reversed; } - -static spqr_element arcGetElement( - const SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Gets the element (row/column) associated to the given arc */ +static +spqr_element arcGetElement( + const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ + spqr_arc arc /**< The arc to get the element for */ ) { assert(dec); @@ -1176,7 +1280,9 @@ static spqr_element arcGetElement( return dec->arcs[arc].element; } -static SCIP_Bool netMatDecDataContainsRow( +/**< Check whether the network matrix decomposition contains an edge for the given row index */ +static +SCIP_Bool netMatDecDataContainsRow( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ int row /**< The row index that is checked */ ) @@ -1186,7 +1292,9 @@ static SCIP_Bool netMatDecDataContainsRow( return SPQRarcIsValid(dec->rowArcs[row]); } -static SCIP_Bool netMatDecDataContainsColumn( +/**< Check whether the network matrix decomposition contains an edge for the given column index */ +static +SCIP_Bool netMatDecDataContainsColumn( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ int column /**< The column index that is checked */ ) @@ -1196,10 +1304,12 @@ static SCIP_Bool netMatDecDataContainsColumn( return SPQRarcIsValid(dec->columnArcs[column]); } -static void setDecompositionColumnArc( - SCIP_NETMATDECDATA* dec, - spqr_col col, - spqr_arc arc +/**< Associate the given arc to the given column in the network matrix decomposition. */ +static +void setDecompositionColumnArc( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_col col, /**< The column to associate */ + spqr_arc arc /**< The arc to associate to the column */ ) { assert(SPQRcolIsValid(col) && col < dec->memColumns); @@ -1208,10 +1318,12 @@ static void setDecompositionColumnArc( dec->columnArcs[col] = arc; } -static void setDecompositionRowArc( - SCIP_NETMATDECDATA* dec, - spqr_row row, - spqr_arc arc +/**< Associate the given arc to the given row in the network matrix decomposition. */ +static +void setDecompositionRowArc( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_row row, /**< The row to associate */ + spqr_arc arc /**< The arc to associate to the row */ ) { assert(SPQRrowIsValid(row) && row < dec->memRows); @@ -1220,9 +1332,11 @@ static void setDecompositionRowArc( dec->rowArcs[row] = arc; } -static spqr_arc getDecompositionColumnArc( - const SCIP_NETMATDECDATA* dec, - spqr_col col +/**< Get the decomposition arc associated to the given column. */ +static +spqr_arc getDecompositionColumnArc( + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_col col /**< The given column */ ) { assert(SPQRcolIsValid(col) && col < dec->memColumns); @@ -1230,9 +1344,11 @@ static spqr_arc getDecompositionColumnArc( return dec->columnArcs[col]; } -static spqr_arc getDecompositionRowArc( - const SCIP_NETMATDECDATA* dec, - spqr_row row +/**< Get the decomposition arc associated to the given row. */ +static +spqr_arc getDecompositionRowArc( + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_row row /**< The given row */ ) { assert(SPQRrowIsValid(row) && row < dec->memRows); @@ -1240,7 +1356,9 @@ static spqr_arc getDecompositionRowArc( return dec->rowArcs[row]; } -static SCIP_RETCODE netMatDecDataCreate( +/**< Initialize the network matrix decomposition data structure */ +static +SCIP_RETCODE netMatDecDataCreate( SCIP* scip, /**< SCIP data structure */ SCIP_NETMATDECDATA** pdec, /**< buffer to store pointer to created decomposition */ int nrows, /**< The maximal number of rows that the decomposition can expect */ @@ -1312,7 +1430,9 @@ static SCIP_RETCODE netMatDecDataCreate( return SCIP_OKAY; } -static void netMatDecDataFree( +/**< Free the network matrix decomposition data structure */ +static +void netMatDecDataFree( SCIP_NETMATDECDATA** pdec /**< pointer to the network matrix decomposition to freed */ ){ assert(pdec); @@ -1328,9 +1448,11 @@ static void netMatDecDataFree( SCIPfreeBlockMemory(dec->env, pdec); } -static spqr_arc getFirstMemberArc( - const SCIP_NETMATDECDATA* dec, - spqr_member member +/**< Get the first arc of the linked list of arcs contained in the given member */ +static +spqr_arc getFirstMemberArc( + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member member /**< The given member */ ) { assert(dec); @@ -1339,9 +1461,11 @@ static spqr_arc getFirstMemberArc( return dec->members[member].firstArc; } -static spqr_arc getNextMemberArc( - const SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Given the current arc, get the next arc of the linked list of arcs that are in the same member */ +static +spqr_arc getNextMemberArc( + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_arc arc /**< The current arc */ ) { assert(dec); @@ -1351,9 +1475,11 @@ static spqr_arc getNextMemberArc( return arc; } -static spqr_arc getPreviousMemberArc( - const SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Given the current arc, get the previous arc of the linked list of arcs that are in the same member */ +static +spqr_arc getPreviousMemberArc( + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_arc arc /**< The current arc */ ) { assert(dec); @@ -1363,10 +1489,12 @@ static spqr_arc getPreviousMemberArc( return arc; } -static void addArcToMemberArcList( - SCIP_NETMATDECDATA* dec, - spqr_arc arc, - spqr_member member +/**< Adds an arc to the linked list of arcs of the given member */ +static +void addArcToMemberArcList( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_arc arc, /**< The arc to add */ + spqr_member member /**< The member to add the arc to */ ) { spqr_arc firstMemberArc = getFirstMemberArc(dec, member); @@ -1378,7 +1506,8 @@ static void addArcToMemberArcList( dec->arcs[arc].arcListNode.previous = lastMemberArc; dec->arcs[firstMemberArc].arcListNode.previous = arc; dec->arcs[lastMemberArc].arcListNode.next = arc; - } else + } + else { assert(dec->members[member].numArcs == 0); dec->arcs[arc].arcListNode.next = arc; @@ -1388,11 +1517,13 @@ static void addArcToMemberArcList( ++( dec->members[member].numArcs ); } -static SCIP_RETCODE createArc( - SCIP_NETMATDECDATA* dec, - spqr_member member, - SCIP_Bool reversed, - spqr_arc* pArc +/**< Create a new arc in the network matrix decomposition */ +static +SCIP_RETCODE createArc( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member member, /**< The member that contains the arc */ + SCIP_Bool reversed, /**< Is the arc reversed or not? */ + spqr_arc* pArc /**< out-pointer to the id that is assigned to the arc. */ ) { assert(dec); @@ -1403,7 +1534,8 @@ static SCIP_RETCODE createArc( if( SPQRarcIsValid(idx)) { dec->firstFreeArc = dec->arcs[idx].arcListNode.next; - } else + } + else { //Enlarge array, no free nodes in arc list int newSize = 2 * dec->memArcs; @@ -1437,12 +1569,14 @@ static SCIP_RETCODE createArc( return SCIP_OKAY; } -static SCIP_RETCODE createRowArc( - SCIP_NETMATDECDATA* dec, - spqr_member member, - spqr_arc* pArc, - spqr_row row, - SCIP_Bool reversed +/**< Create a new arc in the network matrix decomposition that is associated to the given row */ +static +SCIP_RETCODE createRowArc( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member member, /**< The member that contains the arc */ + spqr_arc* pArc, /**< out-pointer to the id that is assigned to the arc. */ + spqr_row row, /**< The row associated to the arc */ + SCIP_Bool reversed /**< Is the arc reversed or not? */ ) { SCIP_CALL(createArc(dec, member, reversed, pArc)); @@ -1453,12 +1587,14 @@ static SCIP_RETCODE createRowArc( return SCIP_OKAY; } -static SCIP_RETCODE createColumnArc( - SCIP_NETMATDECDATA* dec, - spqr_member member, - spqr_arc* pArc, - spqr_col column, - SCIP_Bool reversed +/**< Create a new arc in the network matrix decomposition that is associated to the given column */ +static +SCIP_RETCODE createColumnArc( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member member, /**< The member that contains the arc */ + spqr_arc* pArc, /**< out-pointer to the id that is assigned to the arc. */ + spqr_col column, /**< The column associated to the arc */ + SCIP_Bool reversed /**< Is the arc reversed or not? */ ) { SCIP_CALL(createArc(dec, member, reversed, pArc)); @@ -1469,10 +1605,12 @@ static SCIP_RETCODE createColumnArc( return SCIP_OKAY; } -static SCIP_RETCODE createMember( - SCIP_NETMATDECDATA* dec, - SPQRMemberType type, - spqr_member* pMember +/**< Create a new member in the network matrix decomposition. */ +static +SCIP_RETCODE createMember( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SPQRMemberType type, /**< The SPQR-type of the member */ + spqr_member* pMember /**< out-pointer to the id that is assigned to the member. */ ) { assert(dec); @@ -1499,9 +1637,11 @@ static SCIP_RETCODE createMember( return SCIP_OKAY; } -static SCIP_RETCODE createNode( - SCIP_NETMATDECDATA* dec, - spqr_node* pNode +/**< Create a new node in the network matrix decomposition. */ +static +SCIP_RETCODE createNode( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_node* pNode /**< out-pointer to the id that is assigned to the node */ ) { @@ -1520,11 +1660,13 @@ static SCIP_RETCODE createNode( return SCIP_OKAY; } -static void removeArcFromNodeArcList( - SCIP_NETMATDECDATA* dec, - spqr_arc arc, - spqr_node node, - SCIP_Bool nodeIsHead +/**< Remove an arc from the linked list of arcs that are adjacent to a given node */ +static +void removeArcFromNodeArcList( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_arc arc, /**< The arc to remove */ + spqr_node node, /**< The node to remove the arc from */ + SCIP_Bool nodeIsHead /**< Indicates whether the node is the arcs head, or tail */ ) { SPQRNetworkDecompositionArcListNode* arcListNode = nodeIsHead ? &dec->arcs[arc].headArcListNode @@ -1533,7 +1675,8 @@ static void removeArcFromNodeArcList( if( dec->nodes[node].numArcs == 1 ) { dec->nodes[node].firstArc = SPQR_INVALID_ARC; - } else + } + else { spqr_arc next_arc = arcListNode->next; spqr_arc prev_arc = arcListNode->previous; @@ -1555,11 +1698,13 @@ static void removeArcFromNodeArcList( --( dec->nodes[node].numArcs ); } -static void addArcToNodeArcList( - SCIP_NETMATDECDATA* dec, - spqr_arc arc, - spqr_node node, - SCIP_Bool nodeIsHead +/**< Add an arc to the linked list of arcs that are adjacent to a given node */ +static +void addArcToNodeArcList( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_arc arc, /**< The arc to add */ + spqr_node node, /**< The node to add the arc to */ + SCIP_Bool nodeIsHead /**< Indicates whether the node is the arcs head, or tail */ ) { assert(nodeIsRepresentative(dec, node)); @@ -1585,7 +1730,8 @@ static void addArcToNodeArcList( previousListNode->next = arc; nextListNode->previous = arc; - } else + } + else { arcListNode->next = arc; arcListNode->previous = arc; @@ -1595,26 +1741,31 @@ static void addArcToNodeArcList( if( nodeIsHead ) { dec->arcs[arc].head = node; - } else + } + else { dec->arcs[arc].tail = node; } } -static void setArcHeadAndTail( - SCIP_NETMATDECDATA* dec, - spqr_arc arc, - spqr_node head, - spqr_node tail +/**< Initializes the data structures of the arcs head and tail nodes */ +static +void setArcHeadAndTail( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_arc arc, /**< The arc to initialize */ + spqr_node head, /**< The arc its head node*/ + spqr_node tail /**< The arc its tail node*/ ) { addArcToNodeArcList(dec, arc, head, TRUE); addArcToNodeArcList(dec, arc, tail, FALSE); } -static void clearArcHeadAndTail( - SCIP_NETMATDECDATA* dec, - spqr_arc arc +/**< Deinitializes the data structures of the arcs head and tail nodes */ +static +void clearArcHeadAndTail( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_arc arc /**< The arc to deinitialize */ ) { removeArcFromNodeArcList(dec, arc, findArcHead(dec, arc), TRUE); @@ -1623,11 +1774,13 @@ static void clearArcHeadAndTail( dec->arcs[arc].tail = SPQR_INVALID_NODE; } -static void changeArcHead( - SCIP_NETMATDECDATA* dec, - spqr_arc arc, - spqr_node oldHead, - spqr_node newHead +/**< Change the arc's head node to a new node */ +static +void changeArcHead( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_arc arc, /**< The given arc */ + spqr_node oldHead, /**< The current head node of the arc */ + spqr_node newHead /**< The new head node of the arc */ ) { assert(nodeIsRepresentative(dec, oldHead)); @@ -1636,11 +1789,13 @@ static void changeArcHead( addArcToNodeArcList(dec, arc, newHead, TRUE); } -static void changeArcTail( - SCIP_NETMATDECDATA* dec, - spqr_arc arc, - spqr_node oldTail, - spqr_node newTail +/**< Change the arc's head node to a new node */ +static +void changeArcTail( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_arc arc, /**< The given arc */ + spqr_node oldTail, /**< The current tail node of the arc */ + spqr_node newTail /**< The new tail node of the arc */ ) { assert(nodeIsRepresentative(dec, oldTail)); @@ -1649,9 +1804,11 @@ static void changeArcTail( addArcToNodeArcList(dec, arc, newTail, FALSE); } -static int nodeDegree( - SCIP_NETMATDECDATA* dec, - spqr_node node +/**< Returns the degree of the given node */ +static +int nodeDegree( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_node node /**< The given node */ ) { assert(dec); @@ -1660,9 +1817,11 @@ static int nodeDegree( return dec->nodes[node].numArcs; } -static SPQRMemberType getMemberType( - const SCIP_NETMATDECDATA* dec, - spqr_member member +/**< Get the SPQR-type of the given member */ +static +SPQRMemberType getMemberType( + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member member /**< The given member */ ) { assert(dec); @@ -1672,10 +1831,12 @@ static SPQRMemberType getMemberType( return dec->members[member].type; } -static void updateMemberType( - const SCIP_NETMATDECDATA* dec, - spqr_member member, - SPQRMemberType type +/**< Update the SPQR-type of the given member */ +static +void updateMemberType( + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member member, /**< The member to update the type for */ + SPQRMemberType type /**< The new SPQR-type of the member */ ) { assert(dec); @@ -1686,9 +1847,11 @@ static void updateMemberType( dec->members[member].type = type; } -static spqr_arc markerToParent( - const SCIP_NETMATDECDATA* dec, - spqr_member member +/**< Returns the virtual arc pointing to the parent member (in the arborescence) of the given member */ +static +spqr_arc markerToParent( + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member member /**< The given member */ ) { assert(dec); @@ -1698,10 +1861,12 @@ static spqr_arc markerToParent( return dec->members[member].markerToParent; } -static void updateMemberParentInformation( - SCIP_NETMATDECDATA* dec, - const spqr_member newMember, - const spqr_member toRemove +/**< Updates the parent information of a member that is identified with another another member */ +static +void updateMemberParentInformation( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + const spqr_member newMember, /**< The new large member containing both members */ + const spqr_member toRemove /**< The member that was merged and removed */ ) { assert(memberIsRepresentative(dec, newMember)); @@ -1716,9 +1881,11 @@ static void updateMemberParentInformation( dec->members[toRemove].parentMember = SPQR_INVALID_MEMBER; } -static spqr_arc markerOfParent( - const SCIP_NETMATDECDATA* dec, - spqr_member member +/**< Returns the virtual arc of the parent member that points to the given member */ +static +spqr_arc markerOfParent( + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member member /**< The given member */ ) { assert(dec); @@ -1728,10 +1895,11 @@ static spqr_arc markerOfParent( return dec->members[member].markerOfParent; } - -static int getNumMemberArcs( - const SCIP_NETMATDECDATA* dec, - spqr_member member +/**< Returns the number of arcs in the member */ +static +int getNumMemberArcs( + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member member /**< The member to get the number of arcs of */ ) { assert(dec); @@ -1741,25 +1909,36 @@ static int getNumMemberArcs( return dec->members[member].numArcs; } -static int getNumNodes(const SCIP_NETMATDECDATA* dec) +/**< Returns the number of nodes in complete network matrix decomposition */ +static +int getNumNodes( + const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */ + ) { assert(dec); return dec->numNodes; } -static int getNumMembers(const SCIP_NETMATDECDATA* dec) +/**< Returns the number of members in complete network matrix decomposition */ +static +int getNumMembers( + const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */ + ) { assert(dec); return dec->numMembers; } -static SCIP_RETCODE createStandaloneParallel( - SCIP_NETMATDECDATA* dec, - spqr_col* columns, - SCIP_Bool* reversed, - int num_columns, - spqr_row row, - spqr_member* pMember +/**< Creates a standalone parallel member with the given row and columns that is not connected to other members of the + * network matrix decomposition. New arcs are created for the given row and columns */ +static +SCIP_RETCODE createStandaloneParallel( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_col* columns, /**< The columns in the parallel member */ + SCIP_Bool* reversed, /**< Array indicating the direction of each column edge */ + int num_columns, /**< The number of columns in the parallel member */ + spqr_row row, /**< The row in the parallel member */ + spqr_member* pMember /**< out-pointer to the new parallel member id */ ) { spqr_member member; @@ -1779,13 +1958,16 @@ static SCIP_RETCODE createStandaloneParallel( return SCIP_OKAY; } -static SCIP_RETCODE createConnectedParallel( - SCIP_NETMATDECDATA* dec, - spqr_col* columns, - SCIP_Bool* reversed, - int num_columns, - spqr_row row, - spqr_member* pMember +/**< Creates a parallel member with the given row and columns is connected to other members of the + * network matrix decomposition. New arcs are created for the given row and columns */ +static +SCIP_RETCODE createConnectedParallel( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_col* columns, /**< The columns in the parallel member */ + SCIP_Bool* reversed, /**< Array indicating the direction of each column edge */ + int num_columns, /**< The number of columns in the parallel member */ + spqr_row row, /**< The row in the parallel member */ + spqr_member* pMember /**< out-pointer to the new parallel member id */ ) { spqr_member member; @@ -1804,13 +1986,16 @@ static SCIP_RETCODE createConnectedParallel( return SCIP_OKAY; } -static SCIP_RETCODE createStandaloneSeries( - SCIP_NETMATDECDATA* dec, - spqr_row* rows, - SCIP_Bool* reversed, - int numRows, - spqr_col col, - spqr_member* pMember +/**< Creates a standalone series member with the given row and columns that is not connected to other members of the + * network matrix decomposition. New arcs are created for the given rows and column */ +static +SCIP_RETCODE createStandaloneSeries( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_row* rows, /**< The rows in the series member */ + SCIP_Bool* reversed, /**< Array indicating the direction of each row edge */ + int numRows, /**< The number of rows in the parallel member */ + spqr_col col, /**< The column in the parallel member */ + spqr_member* pMember /**< out-pointer to the new series member id */ ) { spqr_member member; @@ -1829,13 +2014,16 @@ static SCIP_RETCODE createStandaloneSeries( return SCIP_OKAY; } -static SCIP_RETCODE createConnectedSeries( - SCIP_NETMATDECDATA* dec, - spqr_row* rows, - SCIP_Bool* reversed, - int numRows, - spqr_col col, - spqr_member* pMember +/**< Creates a series member with the given row and columns that is connected to some other member of the + * network matrix decomposition. New arcs are created for the given rows and column */ +static +SCIP_RETCODE createConnectedSeries( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_row* rows, /**< The rows in the series member */ + SCIP_Bool* reversed, /**< Array indicating the direction of each row edge */ + int numRows, /**< The number of rows in the parallel member */ + spqr_col col, /**< The column in the parallel member */ + spqr_member* pMember /**< out-pointer to the new series member id */ ) { spqr_member member; @@ -1853,10 +2041,12 @@ static SCIP_RETCODE createConnectedSeries( return SCIP_OKAY; } -static void removeArcFromMemberArcList( - SCIP_NETMATDECDATA* dec, - spqr_arc arc, - spqr_member member +/**< Remove an arc from the linked list containing all arcs of a single member */ +static +void removeArcFromMemberArcList( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_arc arc, /**< The arc to remove */ + spqr_member member /**< The member to remove it from */ ) { assert(findArcMemberNoCompression(dec, arc) == member); @@ -1884,25 +2074,29 @@ static void removeArcFromMemberArcList( --( dec->members[member].numArcs ); } +/**< Data structure for the algorithms to find fundamental cycle within the network matrix decomposition */ typedef struct { spqr_arc arc; SCIP_Bool reversed; } FindCycleCall; -static void process_arc( - spqr_row* fundamental_cycle_arcs, - int* num_cycle_arcs, - FindCycleCall* callStack, - int* callStackSize, - spqr_arc arc, - const SCIP_NETMATDECDATA* dec, - SCIP_Bool* fundamental_cycle_direction, - SCIP_Bool arcIsReversed +/**< Processes a single arc for the algorithm to find cycles in the network matrix decomposition: + * if virtual, pushes it on the callstack, if non-virtual, adds it to the found cycle. */ +static +void process_arc( + spqr_row* cyclearcs, /**< The found cycle so far */ + int* num_cycle_arcs, /**< The number of arcs in the cycle so far */ + FindCycleCall* callStack, /**< The call stack of virtual edges to process still */ + int* callStackSize, /**< The number of virtual edges on the callstack */ + spqr_arc arc, /**< The current arc to process */ + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_Bool* cycledir, /**< Whether the current arc is reversed w.r.t to the cycle/path */ + SCIP_Bool arcIsReversed /**< The arcIsReversed status from the network matrix decomposition */ ) { assert(arcIsTree(dec, arc)); - if( !arcIsMarker(dec, arc)) + if( !arcIsChildMarker(dec, arc)) { spqr_member current_member = findArcMemberNoCompression(dec, arc); if( markerToParent(dec, current_member) == arc ) @@ -1917,8 +2111,8 @@ static void process_arc( spqr_element element = arcGetElement(dec, arc); assert(SPQRelementIsRow(element)); spqr_row row = SPQRelementToRow(element); - fundamental_cycle_arcs[*num_cycle_arcs] = row; - fundamental_cycle_direction[*num_cycle_arcs] = arcIsReversed; + cyclearcs[*num_cycle_arcs] = row; + cycledir[*num_cycle_arcs] = arcIsReversed; ++( *num_cycle_arcs ); } } else @@ -1932,13 +2126,19 @@ static void process_arc( } } -static int decompositionGetFundamentalCycleRows( - const SCIP_NETMATDECDATA* dec, - spqr_col column, - spqr_row* output, - SCIP_Bool* computedSignStorage +/**< Find the fundamental path of a cycle. This is a slow method and only intended for debugging and testing. */ +static +int decompositionGetFundamentalCycleRows( + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_col column, /**< The column to find the fundamental path for */ + spqr_row* output, /**< preallocated array to store fundamental path in. Must have at least + **< the number of rows in the decomposition allocated */ + SCIP_Bool* computedSignStorage /**< Boolean array for storage of whether the path occurs forwards or + **< backwards. Must have at least the same size as output array. */ ) { + /*Basic idea; for each component, do a dfs over the tree formed by the row arcs to find the relevant edges. + This is easy for SPQ-nodes, for R nodes we need to search */ spqr_arc arc = getDecompositionColumnArc(dec, column); if( SPQRarcIsInvalid(arc)) { @@ -2110,15 +2310,20 @@ static int decompositionGetFundamentalCycleRows( return num_rows; } -static SCIP_Bool netMatDecDataVerifyCycle( - SCIP* scip, - const SCIP_NETMATDECDATA* dec, - int column, - const int* nonzrowidx, - const double* nonzvals, - int num_rows, - int* pathrowstorage, - SCIP_Bool* pathsignstorage +/**< Given a cycle (e.g. a matrix column), checks if the column's cycle matches the cycle in the + * network matrix decomposition */ +static +SCIP_Bool netMatDecDataVerifyCycle( + SCIP* scip, /**< The SCIP datastructure */ + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + int column, /**< The column to check */ + const int* nonzrowidx, /**< Array with the nonzero row indices of the column */ + const double* nonzvals, /**< Array with the nonzero entry values of the column's row indices */ + int num_rows, /**< The number of nonzeros in the column */ + int* pathrowstorage, /**< Temporary storage vector for storing the fundamental path. Must have + **< at least as many entries allocated as the number of rows in dec. */ + SCIP_Bool* pathsignstorage /**< Temporary storage for the fundamental path directions. Must have + **< at least as many entries allocated as the number of rows in dec. */ ) { int num_found_rows = decompositionGetFundamentalCycleRows(dec, column, pathrowstorage, pathsignstorage); @@ -2193,33 +2398,51 @@ static SCIP_Bool netMatDecDataVerifyCycle( return good; } -static spqr_member largestMemberID(const SCIP_NETMATDECDATA* dec) +/**< Returns the largest member id that is currently in the decomposition */ +static +spqr_member largestMemberID( + const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */ + ) { return dec->numMembers; } -static spqr_arc largestArcID(const SCIP_NETMATDECDATA* dec) +/**< Returns the largest arc id that is currently in the decomposition */ +static +spqr_arc largestArcID( + const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */ + ) { return dec->numArcs; } -static spqr_node largestNodeID(const SCIP_NETMATDECDATA* dec) +/**< Returns the largest node id that is currently in the decomposition */ +static +spqr_node largestNodeID( + const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */ + ) { return dec->numNodes; } -static int numConnectedComponents(const SCIP_NETMATDECDATA* dec) +/**< Returns the number of SPQR trees in the SPQR forest, i.e. the number of connected components. */ +static +int numConnectedComponents( + const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */ + ) { return dec->numConnectedComponents; } -static SCIP_RETCODE createChildMarker( - SCIP_NETMATDECDATA* dec, - spqr_member member, - spqr_member child, - SCIP_Bool isTree, - spqr_arc* pArc, - SCIP_Bool reversed +/**< Creates a child marker in the network decomposition */ +static +SCIP_RETCODE createChildMarker( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member member, /**< The member to create the arc in */ + spqr_member child, /**< The member that the arc points to */ + SCIP_Bool isTree, /**< Indicates if the new arc is a tree arc */ + spqr_arc* pArc, /**< Out-pointer to store the new arc's id */ + SCIP_Bool reversed /**< Sets the reversed field of the arc */ ) { SCIP_CALL(createArc(dec, member, reversed, pArc)); @@ -2230,14 +2453,16 @@ static SCIP_RETCODE createChildMarker( return SCIP_OKAY; } -static SCIP_RETCODE createParentMarker( - SCIP_NETMATDECDATA* dec, - spqr_member member, - SCIP_Bool isTree, - spqr_member parent, - spqr_arc parentMarker, - spqr_arc* arc, - SCIP_Bool reversed +/**< Creates a parent marker in the network decomposition */ +static +SCIP_RETCODE createParentMarker( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member member, /**< The member to create the arc in */ + SCIP_Bool isTree, /**< Indicates if the new arc is a tree arc */ + spqr_member parent, /**< The member that the arc points to */ + spqr_arc parentMarker, /**< The parent arc in the parent that points to this member */ + spqr_arc* arc, /**< Out-pointer to store the new arc's id */ + SCIP_Bool reversed /**< Sets the reversed field of the arc */ ) { @@ -2252,13 +2477,15 @@ static SCIP_RETCODE createParentMarker( return SCIP_OKAY; } -static SCIP_RETCODE createMarkerPair( - SCIP_NETMATDECDATA* dec, - spqr_member parentMember, - spqr_member childMember, - SCIP_Bool parentIsTree, - SCIP_Bool childMarkerReversed, - SCIP_Bool parentMarkerReversed +/**< Creates a child-marker parent-marker pair in the network decomposition */ +static +SCIP_RETCODE createMarkerPair( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member parentMember, /**< The parent member */ + spqr_member childMember, /**< The child member */ + SCIP_Bool parentIsTree, /**< Is the edge in the parent member (the child marker) a tree edge? */ + SCIP_Bool childMarkerReversed, /**< Is the child marker arc reversed?*/ + SCIP_Bool parentMarkerReversed /**< IS the parent marker arc reversed? */ ) { spqr_arc parentToChildMarker = SPQR_INVALID_ARC; @@ -2273,15 +2500,17 @@ static SCIP_RETCODE createMarkerPair( return SCIP_OKAY; } -static SCIP_RETCODE createMarkerPairWithReferences( - SCIP_NETMATDECDATA* dec, - spqr_member parentMember, - spqr_member childMember, - SCIP_Bool parentIsTree, - SCIP_Bool childMarkerReversed, - SCIP_Bool parentMarkerReversed, - spqr_arc* parentToChild, - spqr_arc* childToParent +/**< Creates a child-marker parent-marker pair in the network decomposition, and returns the assigned arc id's */ +static +SCIP_RETCODE createMarkerPairWithReferences( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member parentMember, /**< The parent member */ + spqr_member childMember, /**< The child member */ + SCIP_Bool parentIsTree, /**< Is the edge in the parent member (the child marker) a tree edge? */ + SCIP_Bool childMarkerReversed, /**< Is the child marker arc reversed?*/ + SCIP_Bool parentMarkerReversed, /**< IS the parent marker arc reversed? */ + spqr_arc* parentToChild, /**< Output-pointer containing arc id of the arc in the parent member */ + spqr_arc* childToParent /**< Output-pointer containing arc id of the arc in the child member */ ) { SCIP_CALL(createChildMarker(dec, parentMember, childMember, parentIsTree, parentToChild, childMarkerReversed)); @@ -2291,11 +2520,14 @@ static SCIP_RETCODE createMarkerPairWithReferences( return SCIP_OKAY; } -static void moveArcToNewMember( - SCIP_NETMATDECDATA* dec, - spqr_arc arc, - spqr_member oldMember, - spqr_member newMember +/**< Moves a given arc from one member to another, updating the linked lists it is contained in and the parent/child + * information of the relevant members. */ +static +void moveArcToNewMember( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_arc arc, /**< The arc to move */ + spqr_member oldMember, /**< The member that currently contains the arc */ + spqr_member newMember /**< The member to move the arc to */ ) { assert(SPQRarcIsValid(arc)); @@ -2331,10 +2563,12 @@ static void moveArcToNewMember( } } -static void mergeMemberArcList( - SCIP_NETMATDECDATA* dec, - spqr_member toMergeInto, - spqr_member toRemove +/**< Merges the arc linked list of two members into one linked list. */ +static +void mergeMemberArcList( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member toMergeInto, /**< The member id that gets the new large linked list containing both */ + spqr_member toRemove /**< The member id that is invalidated, whose linked list is moved away. */ ) { spqr_arc firstIntoArc = getFirstMemberArc(dec, toMergeInto); @@ -2357,9 +2591,11 @@ static void mergeMemberArcList( dec->members[toRemove].firstArc = SPQR_INVALID_ARC; } -static void changeLoopToSeries( - SCIP_NETMATDECDATA* dec, - spqr_member member +/**< Changes the type of a member from loop to series */ +static +void changeLoopToSeries( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member member /**< The member whose type is changed */ ) { assert(SPQRmemberIsValid(member)); @@ -2373,9 +2609,11 @@ static void changeLoopToSeries( dec->members[member].type = SPQR_MEMBERTYPE_SERIES; } -static void changeLoopToParallel( - SCIP_NETMATDECDATA* dec, - spqr_member member +/**< Changes the type of a member from loop to parallel */ +static +void changeLoopToParallel( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member member /**< The member whose type is changed */ ) { assert(SPQRmemberIsValid(member)); @@ -2389,7 +2627,12 @@ static void changeLoopToParallel( dec->members[member].type = SPQR_MEMBERTYPE_PARALLEL; } -static SCIP_Bool netMatDecDataIsMinimal(const SCIP_NETMATDECDATA* dec) +/**< Checks if the network decomposition is minimal, i.e. if it does not contain two adjacent parlalel or series + **< members */ +static +SCIP_Bool netMatDecDataIsMinimal( + const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */ + ) { //Relies on parents/children etc. being set correctly in the tree SCIP_Bool isMinimal = TRUE; @@ -2415,18 +2658,23 @@ static SCIP_Bool netMatDecDataIsMinimal(const SCIP_NETMATDECDATA* dec) return isMinimal; } -static void decreaseNumConnectedComponents( - SCIP_NETMATDECDATA* dec, - int by +/**< Decreases the count of the number of connected components in the network matrix decomposition */ +static +void decreaseNumConnectedComponents( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + int by /**< The number of connected components to remove. */ ) { dec->numConnectedComponents -= by; assert(dec->numConnectedComponents >= 1); } -static void reorderComponent( - SCIP_NETMATDECDATA* dec, - spqr_member newRoot +/**< Reorders the arborescence of the SPQR that contains a given member so that the given member becomes the new root + * of the arborescence. */ +static +void reorderComponent( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member newRoot /**< The member to make the new root */ ) { assert(dec); @@ -2471,13 +2719,16 @@ static void reorderComponent( } } - -static void netMatDecDataRemoveComponent( - SCIP_NETMATDECDATA* dec, - const int* componentRows, - int numRows, - const int* componentCols, - int numCols +/**< Deletes the SPQR tree (connected component) containing the given component rows and columns. + * Note that the implementation of this method does not actually modify the SPQR tree, but rather unlinks the rows and + * columns from the relevant arcs. Thus, this method is a bit hacky and should be used with care. */ +static +void netMatDecDataRemoveComponent( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + const int* componentRows, /**< The rows of the connected component*/ + int numRows, /**< The number of rows */ + const int* componentCols, /**< The columns of the connected component*/ + int numCols /**< The number of columns */ ) { //The below just removes the 'link' but not the internal datastructures. @@ -2503,8 +2754,12 @@ static void netMatDecDataRemoveComponent( } #ifdef SCIP_DEBUG - //Debugging functions to print the decomposition -static char typeToChar(SPQRMemberType type) + +/**< Converts the members type to an associated character */ //Debugging functions to print the decomposition +static +char typeToChar( + SPQRMemberType type /**< The member type */ + ) { switch (type) { @@ -2521,8 +2776,17 @@ static char typeToChar(SPQRMemberType type) } } -static void arcToDot(FILE *stream, const SCIP_NETMATDEC *dec, - spqr_arc arc, unsigned long dot_head, unsigned long dot_tail, SCIP_Bool useElementNames) +/**< Prints an arc in DOT format */ +static +void arcToDot( + FILE* stream, /**< The stream to write the arc to */ + const SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ + spqr_arc arc, /**< The arc to write */ + unsigned long dot_head, /**< The head id of the arc */ + unsigned long dot_tail, /**< The tail id of the arc */ + SCIP_Bool useElementNames /**< If TRUE, prints the corresponding row/column index. If FALSE, + **< prints the spqr_arc id instead. */ + ) { assert(SPQRarcIsValid(arc)); spqr_member member = findArcMemberNoCompression(dec, arc); @@ -2578,7 +2842,14 @@ static void arcToDot(FILE *stream, const SCIP_NETMATDEC *dec, } } -static void decompositionToDot(FILE *stream, const SCIP_NETMATDEC *dec, SCIP_Bool useElementNames) +/**< Outputs the network decomposition as a DOT file */ +static +void decompositionToDot( + FILE* stream, /**< The stream to write to */ + const SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ + SCIP_Bool useElementNames /**< If TRUE, prints the corresponding row/column index. If FALSE, + **< prints the spqr_arc id instead. */ + ) { fprintf(stream, "//decomposition\ndigraph decomposition{\n compound = TRUE;\n"); for (spqr_member member = 0; member < dec->numMembers; ++member) @@ -2645,14 +2916,15 @@ static void decompositionToDot(FILE *stream, const SCIP_NETMATDEC *dec, SCIP_Boo } #endif -static SCIP_RETCODE mergeGivenMemberIntoParent( - SCIP_NETMATDECDATA* dec, - spqr_member member, - spqr_member parent, - spqr_arc parentToChild, - spqr_arc childToParent, - SCIP_Bool headToHead, - spqr_member* mergedMember +static +SCIP_RETCODE mergeGivenMemberIntoParent( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member member, /**< The member to merge */ + spqr_member parent, /**< The parent of the member to merge into */ + spqr_arc parentToChild, /**< The arc from the parent pointing to the member */ + spqr_arc childToParent, /**< The arc in the child pointing to the parent */ + SCIP_Bool headToHead, /**< Identify the head of parentToChild with the head of childToParent?*/ + spqr_member* mergedMember /**< Out-pointer to the member id of the final member */ ) { assert(dec); @@ -2703,7 +2975,9 @@ static SCIP_RETCODE mergeGivenMemberIntoParent( return SCIP_OKAY; } -static int maxValue( +/**< Returns the maximum of two values. */ +static +int maxValue( int a, int b ) @@ -4831,7 +5105,7 @@ static SCIP_RETCODE splitSeries( adjacentMember = findMemberParent(dec, reducedMember->member); adjacentMarker = markerOfParent(dec, reducedMember->member); memberMarker = arc; - } else if( arcIsMarker(dec, arc)) + } else if( arcIsChildMarker(dec, arc)) { adjacentMember = findArcChildMember(dec, arc); adjacentMarker = markerToParent(dec, adjacentMember); @@ -5543,8 +5817,8 @@ static SCIP_RETCODE columnTransformSingleRigid( if( SPQRarcIsValid(existingArcWithPath)) { SCIP_Bool isParent = FALSE; - spqr_member adjacentMember = arcIsMarker(dec, existingArcWithPath) ? - findArcChildMember(dec,existingArcWithPath) : SPQR_INVALID_MEMBER; + spqr_member adjacentMember = arcIsChildMarker(dec, existingArcWithPath) ? + findArcChildMember(dec,existingArcWithPath) : SPQR_INVALID_MEMBER; if( existingArcWithPath == markerToParent(dec, member)) { adjacentMember = findMemberParent(dec, member); @@ -6548,8 +6822,8 @@ static SCIP_RETCODE allocateRigidSearchMemory( //TODO: see if tradeoff for performance bound by checking max # of nodes of rigid is worth it to reduce size //of the following allocations - int largestID = largestNodeID( - dec);//TODO: only update the stack sizes of the following when needed? The preallocation might be causing performance problems + int largestID = largestNodeID(dec); + //TODO: only update the stack sizes of the following when needed? The preallocation may not be worth it. if( largestID > newRow->memIntersectionDFSData ) { int newSize = maxValue(2 * newRow->memIntersectionDFSData, largestID); @@ -8515,7 +8789,7 @@ static SCIP_RETCODE rigidTransformArcIntoCycle( markerCycleMember = adjacentMember; markerCycleArc = markerOfParent(dec, member); } - } else if( arcIsMarker(dec, arc)) + } else if( arcIsChildMarker(dec, arc)) { adjacentMember = findArcChildMember(dec, arc); if( getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_SERIES ) @@ -8772,7 +9046,7 @@ static SCIP_RETCODE splitParallelRowAddition( { adjacentMember = findMemberParent(dec, member); adjacentArc = markerOfParent(dec, member); - } else if( arcIsMarker(dec, treeArc)) + } else if( arcIsChildMarker(dec, treeArc)) { adjacentMember = findArcChildMember(dec, treeArc); adjacentArc = markerToParent(dec, adjacentMember); @@ -8876,7 +9150,7 @@ static SCIP_RETCODE splitParallelRowAddition( if( arc == markerToParent(dec, member)) { adjacentMember = findMemberParent(dec, member); - } else if( arcIsMarker(dec, arc)) + } else if( arcIsChildMarker(dec, arc)) { adjacentMember = findArcChildMember(dec, arc); } From bdf7046194873c1afe2b0fd40d7b8cf2cca161de Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 23 Jul 2024 15:44:15 +0200 Subject: [PATCH 20/63] Add documentation for network column addition --- src/scip/network.c | 606 +++++++++++++++++++++++++++------------------ 1 file changed, 364 insertions(+), 242 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 10651d5155..d62067d08f 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -2991,12 +2991,20 @@ int maxValue( typedef int path_arc_id; #define INVALID_PATH_ARC (-1) -static SCIP_Bool pathArcIsInvalid(const path_arc_id arc) +/**< Returns true if the path arc is invalid */ +static +SCIP_Bool pathArcIsInvalid( + const path_arc_id arc /**< The path arc to check */ + ) { return arc < 0; } -static SCIP_Bool pathArcIsValid(const path_arc_id arc) +/**< Returns true if the path arc is valid */ +static +SCIP_Bool pathArcIsValid( + const path_arc_id arc /**< The path arc to check */ + ) { return !pathArcIsInvalid(arc); } @@ -3020,12 +3028,19 @@ typedef struct typedef int reduced_member_id; #define INVALID_REDUCED_MEMBER (-1) -static SCIP_Bool reducedMemberIsInvalid(const reduced_member_id id) +/**< Returns true if the reduced member is invalid */ +static +SCIP_Bool reducedMemberIsInvalid( + const reduced_member_id id /**< The reduced member to check */ + ) { return id < 0; } -static SCIP_Bool reducedMemberIsValid(const reduced_member_id id) +static +SCIP_Bool reducedMemberIsValid( + const reduced_member_id id /**< The reduced member to check */ + ) { return !reducedMemberIsInvalid(id); } @@ -3055,12 +3070,19 @@ typedef enum OUT_TAIL = 3 /**< The directed path goes out of the tail of the virtual arc */ } MemberPathType; -static SCIP_Bool isInto(MemberPathType type) +/**< Check if the path type is into */ +static +SCIP_Bool isInto( + MemberPathType type /**< The path type to check */ + ) { return type == INTO_HEAD || type == INTO_TAIL; } - -static SCIP_Bool isHead(MemberPathType type) +/**< Check if the path end node is the head or tail node of the corresponding arc */ +static +SCIP_Bool isHead( + MemberPathType type /**< The path type to check */ + ) { return type == INTO_HEAD || type == OUT_HEAD; } @@ -3191,10 +3213,12 @@ typedef struct int memLeafMembers; /**< Number of allocated slots in leafMembers array*/ } SCIP_NETCOLADD; -static void cleanupPreviousIteration( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol -) +/**< Clean up internal temporary data structures that were used in the previous column iteration. */ +static +void cleanupPreviousIteration( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */ + ) { assert(dec); assert(newCol); @@ -3239,9 +3263,11 @@ static void cleanupPreviousIteration( newCol->numPathArcs = 0; } -static SCIP_RETCODE SCIPnetcoladdCreate( - SCIP* scip, - SCIP_NETCOLADD** pcoladd +/**< Create a new network column addition datastructure */ +static +SCIP_RETCODE SCIPnetcoladdCreate( + SCIP* scip, /**< SCIP environment */ + SCIP_NETCOLADD** pcoladd /**< Out-pointer for the created network column addition datastructure */ ) { assert(scip); @@ -3301,9 +3327,11 @@ static SCIP_RETCODE SCIPnetcoladdCreate( return SCIP_OKAY; } -static void SCIPnetcoladdFree( - SCIP* scip, - SCIP_NETCOLADD** pcoladd +/**< Free a network column addition datastructure */ +static +void SCIPnetcoladdFree( + SCIP* scip, /**< SCIP environment */ + SCIP_NETCOLADD** pcoladd /**< Pointer to the network column addition datastructure to be freed */ ) { assert(scip); @@ -3327,15 +3355,19 @@ static void SCIPnetcoladdFree( SCIPfreeBlockMemory(scip, pcoladd); } - -static reduced_member_id createReducedMembersToRoot( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - const spqr_member firstMember +/**< Adds members to the reduced member tree, by starting at the given member, jumping up to the parent, repeating this + * procedure until the root has been added. */ +static +reduced_member_id createReducedMembersToRoot( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + const spqr_member firstMember /**< The member to create a reduced member for */ ) { assert(SPQRmemberIsValid(firstMember)); + /**< Originally a recursive algorithm, but for large matrices we need to unroll recursion to prevent stack overflows + * from too many recursive calls */ CreateReducedMembersCallstack* callstack = newCol->createReducedMembersCallStack; callstack[0].member = firstMember; int callDepth = 0; @@ -3435,9 +3467,11 @@ static reduced_member_id createReducedMembersToRoot( return returnedMember; } -static SCIP_RETCODE constructReducedDecomposition( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol +/**< Construct the reduced decomposition, which is the smallest subtree containing all members path arcs */ +static +SCIP_RETCODE constructReducedDecomposition( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */ ) { assert(dec); @@ -3583,7 +3617,11 @@ static SCIP_RETCODE constructReducedDecomposition( return SCIP_OKAY; } -static void cleanUpMemberInformation(SCIP_NETCOLADD* newCol) +/**< Clean up the memberinformation array at the end of an iteration */ +static +void cleanUpMemberInformation( + SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */ + ) { //This loop is at the end as memberInformation is also used to assign the cut arcs during propagation //Clean up the memberInformation array @@ -3599,12 +3637,14 @@ static void cleanUpMemberInformation(SCIP_NETCOLADD* newCol) #endif } -static void createPathArc( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - const spqr_arc arc, - const reduced_member_id reducedMember, - SCIP_Bool reversed +/**< Marks the given arc as a path arc and adds it to the relevant data structures. */ +static +void createPathArc( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + const spqr_arc arc, /**< The arc to mark as a path arc */ + const reduced_member_id reducedMember, /**< The reduced member containing the arc */ + SCIP_Bool reversed /**< Indicates if the new column entry has +1 or -1 (TRUE) for the arc */ ) { assert(dec); @@ -3641,7 +3681,8 @@ static void createPathArc( assert(listNode->arcHead < newCol->memNodePathDegree && listNode->arcTail < newCol->memNodePathDegree); ++newCol->nodeInPathDegree[listNode->arcHead]; ++newCol->nodeOutPathDegree[listNode->arcTail]; - } else + } + else { listNode->arcHead = SPQR_INVALID_NODE; listNode->arcTail = SPQR_INVALID_NODE; @@ -3649,9 +3690,11 @@ static void createPathArc( listNode->reversed = reversed; } -static SCIP_RETCODE createPathArcs( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol +/**< Mark all the row indices of the new column as path arcs*/ +static +SCIP_RETCODE createPathArcs( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */ ) { int maxNumPathArcs = newCol->numDecompositionRowArcs + getNumMembers(dec); @@ -3700,17 +3743,16 @@ static SCIP_RETCODE createPathArcs( } -/** Saves the information of the current row and partitions it based on whether or not the given columns are - * already part of the decomposition. - */ -static SCIP_RETCODE -newColUpdateColInformation( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - spqr_col column, - const spqr_row* nonzeroRows, - const double* nonzeroValues, - int numNonzeros +/**< Saves the information of the current row and partitions it based on whether or not the given columns are + **< already part of the decomposition. */ +static +SCIP_RETCODE newColUpdateColInformation( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + spqr_col column, /**< The column that is checked */ + const spqr_row* nonzeroRows, /**< The column's row indices */ + const double* nonzeroValues, /**< The column's nonzero values */ + int numNonzeros /**< The number of nonzeros in the column */ ) { newCol->newColIndex = column; @@ -3757,9 +3799,11 @@ newColUpdateColInformation( return SCIP_OKAY; } -static SCIP_RETCODE computeLeafMembers( - const SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol +/**< Compute and store the leaf members of the reduced SPQR forest */ +static +SCIP_RETCODE computeLeafMembers( + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */ ) { if( newCol->numReducedMembers > newCol->memLeafMembers ) @@ -3781,16 +3825,21 @@ static SCIP_RETCODE computeLeafMembers( return SCIP_OKAY; } -static void determineRigidPath( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - SPQRColReducedMember* redMem +/**< Checks if the path arcs in the given rigid member form a path */ +static +void determineRigidPath( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + SPQRColReducedMember* redMem /**< The rigid reduced member to determine the path in */ ) { assert(dec); assert(newCol); assert(redMem); + /* Simply count the in and out degrees of the nodes to check if the path arcs form a path. We have a valid path if + * and only if the head of the tail of the path are the only nodes with 1 adjacent edge, and all other nodes + * have 2 adjacent edges*/ SCIP_Bool isValidPath = TRUE; redMem->rigidPathStart = SPQR_INVALID_NODE; redMem->rigidPathEnd = SPQR_INVALID_NODE; @@ -3841,10 +3890,12 @@ static void determineRigidPath( } } -static void determineSingleRigidType( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - reduced_member_id reducedMember +/**< Determines the member's type for the case where the reduced tree consists of a single rigid member */ +static +void determineSingleRigidType( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + reduced_member_id reducedMember /**< The reduced member to check */ ) { assert(dec); @@ -3859,10 +3910,12 @@ static void determineSingleRigidType( } } -static void determineSingleComponentType( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - reduced_member_id reducedMember +/**< Determines the member's type for the case where the reduced tree consists of a single member */ +static +void determineSingleComponentType( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + reduced_member_id reducedMember /**< The reduced member to check */ ) { assert(dec); @@ -3954,15 +4007,16 @@ static void determineSingleComponentType( } } - -static void determinePathSeriesType( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - reduced_member_id reducedMember, - spqr_member member, - MemberPathType previousType, - spqr_arc source, - spqr_arc target +/**< Determines the path type of a series member */ +static +void determinePathSeriesType( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + reduced_member_id reducedMember, /**< The reduced member to check */ + spqr_member member, /**< The reduced member's member id */ + MemberPathType previousType, /**< Type of the previous reduced member in the path */ + spqr_arc source, /**< The marker connecting to the previous reduced member in the path */ + spqr_arc target /**< The marker connecting to the next reduced member in the path */ ) { assert(dec); @@ -4078,14 +4132,16 @@ static void determinePathSeriesType( } } -static void determinePathParallelType( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - reduced_member_id reducedMember, - spqr_member member, - MemberPathType previousType, - spqr_arc source, - spqr_arc target +/**< Determines the path type of a parallel member */ +static +void determinePathParallelType( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + reduced_member_id reducedMember, /**< The reduced member to check */ + spqr_member member, /**< The reduced member's member id */ + MemberPathType previousType, /**< Type of the previous reduced member in the path */ + spqr_arc source, /**< The marker connecting to the previous reduced member in the path */ + spqr_arc target /**< The marker connecting to the next reduced member in the path */ ) { assert(dec); @@ -4146,13 +4202,15 @@ static void determinePathParallelType( } } -static void determinePathRigidType( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - reduced_member_id reducedMember, - MemberPathType previousType, - spqr_arc source, - spqr_arc target +/**< Determines the path type of a rigid member */ +static +void determinePathRigidType( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + reduced_member_id reducedMember, /**< The reduced member to check */ + MemberPathType previousType, /**< Type of the previous reduced member in the path */ + spqr_arc source, /**< The marker connecting to the previous reduced member in the path */ + spqr_arc target /**< The marker connecting to the next reduced member in the path */ ) { @@ -4451,14 +4509,16 @@ static void determinePathRigidType( } } -static void determinePathMemberType( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - reduced_member_id reducedMember, - spqr_member member, - MemberPathType previousType, - spqr_arc source, - spqr_arc target +/**< Determines the path type of a single member */ +static +void determinePathMemberType( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + reduced_member_id reducedMember, /**< The reduced member to check */ + spqr_member member, /**< The reduced member's member id */ + MemberPathType previousType, /**< Type of the previous reduced member in the path */ + spqr_arc source, /**< The marker connecting to the previous reduced member in the path */ + spqr_arc target /**< The marker connecting to the next reduced member in the path */ ) { newCol->reducedMembers[reducedMember].pathSourceArc = source; @@ -4493,10 +4553,13 @@ static void determinePathMemberType( } } -static void determinePathTypes( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - SPQRColReducedComponent* component +/**< Determines the path types of all reduced members. The reduced members themselves also form a path in the + * reduced decomposition. */ +static +void determinePathTypes( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + SPQRColReducedComponent* component /**< The component to determine the path types for */ ) { assert(dec); @@ -4577,13 +4640,16 @@ static void determinePathTypes( //since we return anyways, no need to check newCol->remainsNetwork explicitly } -static void checkRigidLeaf( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - reduced_member_id leaf, - spqr_arc toParent, - reduced_member_id parent, - spqr_arc toChild +/**< Check if a rigid leaf closes a cycle with its child. If so, we can propagate this cycle to a virtual arc of the + * child node member and shrink the decomposition. */ +static +void checkRigidLeaf( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + reduced_member_id leaf, /**< The leaf node of the reduced SPQR tree */ + spqr_arc toParent, /**< The virtual arc to the leaf node's neighbour */ + reduced_member_id parent, /**< The neighbouring member of the leaf node. */ + spqr_arc toChild /**< The virtual arc from the neighbouring member pointing to the leaf. */ ) { SPQRColReducedMember* leafMember = &newCol->reducedMembers[leaf]; @@ -4605,13 +4671,16 @@ static void checkRigidLeaf( leafMember->type = REDUCEDMEMBER_TYPE_MERGED; } -static ReducedMemberType checkLeaf( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - reduced_member_id leaf, - spqr_arc toParent, - reduced_member_id parent, - spqr_arc toChild +/**< Check if a leaf node closes a cycle with its child. If so, we can propagate this cycle to a virtual arc of the + * child node member and shrink the decomposition. */ +static +ReducedMemberType checkLeaf( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + reduced_member_id leaf, /**< The leaf node of the reduced SPQR tree */ + spqr_arc toParent, /**< The virtual arc to the leaf node's neighbour */ + reduced_member_id parent, /**< The neighbouring member of the leaf node. */ + spqr_arc toChild /**< The virtual arc from the neighbouring member pointing to the leaf. */ ) { assert(dec); @@ -4701,9 +4770,11 @@ static ReducedMemberType checkLeaf( return newCol->reducedMembers[leaf].type; } -static void propagateCycles( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol +/**< Recursively removes leaf nodes whose path forms cycles with the virtual arc to its children. */ +static +void propagateCycles( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */ ) { assert(dec); @@ -4829,10 +4900,12 @@ static void propagateCycles( } } -static void determineComponentTypes( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - SPQRColReducedComponent* component +/**< Determine the type of a single component */ +static +void determineComponentTypes( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + SPQRColReducedComponent* component /**< The component to determine the types for */ ) { assert(dec); @@ -4850,13 +4923,15 @@ static void determineComponentTypes( } } -static SCIP_RETCODE SCIPnetcoladdCheck( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* coladd, - int column, - const int* nonzrows, - const double* nonzvals, - int nnonzs +/**< Checks if the given column can be added to the network matrix decomposition. See header for more info. */ +static +SCIP_RETCODE SCIPnetcoladdCheck( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* coladd, /**< The network matrix column addition data structure */ + int column, /**< The column to add */ + const int* nonzrows, /**< The column's nonzero row indices */ + const double* nonzvals, /**< The column's nonzero entries */ + int nnonzs /**< The number of nonzeros in the column */ ) { assert(dec); @@ -4902,7 +4977,9 @@ typedef struct SCIP_Bool reversed; /**< Is the new column reversed? */ } NewColInformation; -static NewColInformation emptyNewColInformation(void) +/**< Initializes an empty newcolinformation struct */ +static +NewColInformation emptyNewColInformation(void) { NewColInformation information; information.member = SPQR_INVALID_MEMBER; @@ -4913,9 +4990,11 @@ static NewColInformation emptyNewColInformation(void) return information; } -static void setTerminalHead( - NewColInformation* info, - spqr_node node +/**< Set the head node of the new column edge to be added */ +static +void setTerminalHead( + NewColInformation* info, /**< The new column information */ + spqr_node node /**< The head node of the new column edge */ ) { assert(SPQRnodeIsValid(node)); @@ -4924,9 +5003,11 @@ static void setTerminalHead( info->head = node; } -static void setTerminalTail( - NewColInformation* info, - spqr_node node +/**< Set the tail node of the new column edge to be added */ +static +void setTerminalTail( + NewColInformation* info, /**< The new column information */ + spqr_node node /**< The tail node of the new column edge */ ) { assert(SPQRnodeIsValid(node)); @@ -4935,39 +5016,48 @@ static void setTerminalTail( info->tail = node; } -static void setTerminalReversed( - NewColInformation* info, - SCIP_Bool reversed +/**< Set whether the new column arc should be reversed */ +static +void setTerminalReversed( + NewColInformation* info, /**< The new column information */ + SCIP_Bool reversed /**< Should the new column arc be reversed? */ ) { assert(info); info->reversed = reversed; } -static void setTerminalMember( - NewColInformation* info, - spqr_member member +/**< Set the member that contains the new column arc */ +static +void setTerminalMember( + NewColInformation* info, /**< The new column information */ + spqr_member member /**< The decomposition member that contains the new column arc */ ) { assert(info); info->member = member; } -static void setTerminalRepresentative( - NewColInformation* info, - spqr_arc representative +/**< Set the representative arc of the new column arc */ +static +void setTerminalRepresentative( + NewColInformation* info, /**< The new column information */ + spqr_arc representative /**< The representative arc of the new column arc */ ) { assert(info); info->representative = representative; } -static SCIP_RETCODE splitParallel( - SCIP_NETMATDECDATA* dec, - spqr_member parallel, - spqr_arc arc1, - spqr_arc arc2, - spqr_member* childParallel +/**< Splits a parallel member into two adjacent parallel members connected by a virtual edge pair. + * One member keeps the two arcs passed to this function, the other member gets all other arcs. */ +static +SCIP_RETCODE splitParallel( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + spqr_member parallel, /**< The parallel member */ + spqr_arc arc1, /**< First arc to keep. */ + spqr_arc arc2, /**< Second arc to keep. */ + spqr_member* childParallel /**< Out pointer to the new child parallel member. */ ) { assert(dec); @@ -4993,13 +5083,16 @@ static SCIP_RETCODE splitParallel( return SCIP_OKAY; } -static SCIP_RETCODE splitSeries( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - SPQRColReducedMember* reducedMember, - spqr_member member, - spqr_member* loopMember, - NewColInformation* newColInfo +/**< Very elaborate function that splits a series member into multiple members based on the structure of the path arcs. + * The updated member reflects the structure of the updated SPQR tree after the new column arc is added. */ +static +SCIP_RETCODE splitSeries( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + SPQRColReducedMember* reducedMember, /**< The reduced member of the series member to split. */ + spqr_member member, /**< The series member to split. */ + spqr_member* loopMember, /**< Out-pointer to a new loop member that may be created */ + NewColInformation* newColInfo /**< New column information struct */ ) { assert(dec); @@ -5164,16 +5257,19 @@ static SCIP_RETCODE splitSeries( return SCIP_OKAY; } - -static SCIP_RETCODE splitSeriesMerging( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - SPQRColReducedMember* reducedMember, - spqr_member member, - spqr_arc* pathRepresentative, - spqr_arc* nonPathRepresentative, - spqr_arc exceptionArc1, - spqr_arc exceptionArc2 +/**< Very elaborate function that splits a series member into multiple members based on the structure of the path arcs. + * The updated member reflects the structure of the updated SPQR tree after the new column arc is added. + * This variant is only used on series members that are part of a reduced tree that is not a single member. */ +static +SCIP_RETCODE splitSeriesMerging( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + SPQRColReducedMember* reducedMember, /**< The reduced member of the series member to split. */ + spqr_member member, /**< The series member to split. */ + spqr_arc* pathRepresentative, /**< Out pointer pointing to the tree arc in the final series node */ + spqr_arc* nonPathRepresentative, /**< Out pointer pointing to the non-tree arc in the final series node*/ + spqr_arc exceptionArc1, /**< The first exception arc. Set to SPQR_INVALID_ARC to ignore */ + spqr_arc exceptionArc2 /**< The second exception arc. Set to SPQR_INVALID_ARC to ignore */ ) { assert(dec); @@ -5295,13 +5391,15 @@ static SCIP_RETCODE splitSeriesMerging( return SCIP_OKAY; } -static SCIP_RETCODE transformFirstPathMember( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - reduced_member_id reducedMember, - NewColInformation* newColInfo, - spqr_arc* representativeArc, - spqr_member* mergedMember +/**< Transforms the first member in the path of members to reflect the new column update */ +static +SCIP_RETCODE transformFirstPathMember( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + reduced_member_id reducedMember, /**< The reduced member to transform */ + NewColInformation* newColInfo, /**< The new column information */ + spqr_arc* representativeArc, /**< Pointer to the representative of the member, needed for merging.*/ + spqr_member* mergedMember /**< Pointer to the merged member */ ) { spqr_member member = newCol->reducedMembers[reducedMember].member; @@ -5395,15 +5493,17 @@ static SCIP_RETCODE transformFirstPathMember( return SCIP_OKAY; } -static SCIP_RETCODE transformAndMergeParallel( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - reduced_member_id current, - reduced_member_id next, - spqr_member nextMember, - SCIP_Bool nextIsParent, - spqr_arc* representativeArc, - spqr_member* mergedMember +/**< Transforms the next parallel member in the path of members and merge it into the current member */ +static +SCIP_RETCODE transformAndMergeParallel( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + reduced_member_id current, /**< The current reduced member id */ + reduced_member_id next, /**< The next reduced member */ + spqr_member nextMember, /**< The member of the next reduced member in the path */ + SCIP_Bool nextIsParent, /**< Is the next reduced member the parent of the current member? */ + spqr_arc* representativeArc, /**< Pointer to the representative of the member, needed for merging.*/ + spqr_member* mergedMember /**< Pointer to the merged member */ ) { //Split off edges not in current subtree to form one parallel (or one edge) @@ -5465,16 +5565,18 @@ static SCIP_RETCODE transformAndMergeParallel( return SCIP_OKAY; } -static SCIP_RETCODE transformAndMergeSeries( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - reduced_member_id current, - reduced_member_id next, - spqr_member nextMember, - SCIP_Bool nextIsParent, - spqr_arc* representativeArc, - spqr_member* mergedMember, - NewColInformation* info +/**< Transforms the next series member in the path of members and merge it into the current member */ +static +SCIP_RETCODE transformAndMergeSeries( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + reduced_member_id current, /**< The current reduced member id */ + reduced_member_id next, /**< The next reduced member */ + spqr_member nextMember, /**< The member of the next reduced member in the path */ + SCIP_Bool nextIsParent, /**< Is the next reduced member the parent of the current member? */ + spqr_arc* representativeArc, /**< Pointer to the representative of the member, needed for merging.*/ + spqr_member* mergedMember, /**< Pointer to the merged member */ + NewColInformation* info /**< The new column information */ ) { @@ -5605,23 +5707,24 @@ static SCIP_RETCODE transformAndMergeSeries( return SCIP_OKAY; } -static SCIP_RETCODE transformAndMergeRigid( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - reduced_member_id current, - reduced_member_id next, - spqr_member nextMember, - SCIP_Bool nextIsParent, - spqr_arc* representativeArc, - spqr_member* mergedMember, - NewColInformation* info +/**< Transforms the next rigid member in the path of members and merge it into the current member */ +static +SCIP_RETCODE transformAndMergeRigid( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + reduced_member_id current, /**< The current reduced member id */ + reduced_member_id next, /**< The next reduced member */ + spqr_member nextMember, /**< The member of the next reduced member in the path */ + SCIP_Bool nextIsParent, /**< Is the next reduced member the parent of the current member? */ + spqr_arc* representativeArc, /**< Pointer to the representative of the member, needed for merging.*/ + spqr_member* mergedMember, /**< Pointer to the merged member */ + NewColInformation* info /**< The new column information */ ) { SPQRColReducedMember* redMem = &newCol->reducedMembers[next]; spqr_arc source = redMem->pathSourceArc; spqr_arc sourceRepresentative = findArcSign(dec, source).representative; - spqr_member newMergedMember = SPQR_INVALID_MEMBER; if( nextIsParent ) @@ -5670,15 +5773,17 @@ static SCIP_RETCODE transformAndMergeRigid( return SCIP_OKAY; } -static SCIP_RETCODE transformAndMerge( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - reduced_member_id current, - reduced_member_id next, - spqr_arc* representativeArc, - spqr_member* mergedMember, - SCIP_Bool nextIsParent, - NewColInformation* info +/**< Transforms the next member in the path of members and merge it into the current member */ +static +SCIP_RETCODE transformAndMerge( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + reduced_member_id current, /**< The current reduced member id */ + reduced_member_id next, /**< The next reduced member */ + spqr_arc* representativeArc, /**< Pointer to the representative of the member, needed for merging.*/ + spqr_member* mergedMember, /**< Pointer to the merged member */ + SCIP_Bool nextIsParent, /**< Is the next reduced member the parent of the current member? */ + NewColInformation* info /**< The new column information */ ) { spqr_member nextMember = newCol->reducedMembers[next].member; @@ -5712,11 +5817,13 @@ static SCIP_RETCODE transformAndMerge( return SCIP_OKAY; } -static SCIP_RETCODE transformPath( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - SPQRColReducedComponent* component, - NewColInformation* newColInfo +/**< Transforms a single component when it contains multiple reduced members. */ +static +SCIP_RETCODE transformPath( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + SPQRColReducedComponent* component, /**< The component to transform */ + NewColInformation* newColInfo /**< The new column information */ ) { //Realize first member @@ -5740,11 +5847,13 @@ static SCIP_RETCODE transformPath( return SCIP_OKAY; } -static SCIP_RETCODE columnTransformSingleParallel( - SCIP_NETCOLADD* newCol, - reduced_member_id reducedMemberId, - spqr_member member, - NewColInformation* newColInfo +/**< Transform a single parallel member to add the new column */ +static +SCIP_RETCODE columnTransformSingleParallel( + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + reduced_member_id reducedMemberId, /**< The reduced member to transform */ + spqr_member member, /**< The member to transform */ + NewColInformation* newColInfo /**< The new column information */ ) { SPQRColReducedMember* reducedMember = &newCol->reducedMembers[reducedMemberId]; @@ -5755,12 +5864,14 @@ static SCIP_RETCODE columnTransformSingleParallel( return SCIP_OKAY; } -static SCIP_RETCODE columnTransformSingleSeries( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - reduced_member_id reducedMemberId, - spqr_member member, - NewColInformation* newColInfo +/**< Transform a single series member to add the new column */ +static +SCIP_RETCODE columnTransformSingleSeries( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + reduced_member_id reducedMemberId, /**< The reduced member to transform */ + spqr_member member, /**< The member to transform */ + NewColInformation* newColInfo /**< The new column information */ ) { @@ -5778,12 +5889,14 @@ static SCIP_RETCODE columnTransformSingleSeries( return SCIP_OKAY; } -static SCIP_RETCODE columnTransformSingleRigid( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - reduced_member_id reducedMemberId, - spqr_member member, - NewColInformation* newColInfo +/**< Transform a single rigid member to add the new column */ +static +SCIP_RETCODE columnTransformSingleRigid( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + reduced_member_id reducedMemberId, /**< The reduced member to transform */ + spqr_member member, /**< The member to transform */ + NewColInformation* newColInfo /**< The new column information */ ) { assert(dec); @@ -5908,11 +6021,13 @@ static SCIP_RETCODE columnTransformSingleRigid( return SCIP_OKAY; } -static SCIP_RETCODE transformComponent( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol, - SPQRColReducedComponent* component, - NewColInformation* newColInfo +/**< Transform a component to reflect the new column */ +static +SCIP_RETCODE transformComponent( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ + SPQRColReducedComponent* component, /**< The component to transform */ + NewColInformation* newColInfo /**< The new column information */ ) { assert(dec); @@ -5962,14 +6077,21 @@ static SCIP_RETCODE transformComponent( return SCIP_OKAY; } -static SCIP_Bool SCIPnetcoladdRemainsNetwork(const SCIP_NETCOLADD* newCol) +/**< Check if the submatrix stored remains a network matrix with the new column update */ +static +SCIP_Bool SCIPnetcoladdRemainsNetwork( + const SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */ + ) { return newCol->remainsNetwork; } -static SCIP_RETCODE SCIPnetcoladdAdd( - SCIP_NETMATDECDATA* dec, - SCIP_NETCOLADD* newCol +/**< Add the new column to the network decomposition as an arc. Only use this function after SCIPnetcoladdCheck() has + * been called. */ +static +SCIP_RETCODE SCIPnetcoladdAdd( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */ ) { assert(dec); From e995a32d42c8963715c9353ed82deee7da347433 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Wed, 24 Jul 2024 11:28:39 +0200 Subject: [PATCH 21/63] Document network matrix row addition --- src/scip/network.c | 1001 +++++++++++++++++++++++++------------------- src/scip/network.h | 2 +- 2 files changed, 564 insertions(+), 439 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index d62067d08f..62ab74ae30 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -3024,7 +3024,7 @@ typedef struct } PathArcListNode; /**< Index for the reduced members, which are the members of the sub-SPQR tree containing all path arcs. - * Note that these are indexed separately from the members of the SPQR tree! */ + **< Note that these are indexed separately from the members of the SPQR tree! */ typedef int reduced_member_id; #define INVALID_REDUCED_MEMBER (-1) @@ -6196,7 +6196,9 @@ SCIP_RETCODE SCIPnetcoladdAdd( /** ---------- START functions for row addition ----------------------------------------------------------------------*/ -static int minValue( +/**< Computes the minimum of two values */ +static +int minValue( int a, int b ) @@ -6208,12 +6210,19 @@ static int minValue( typedef int cut_arc_id; #define INVALID_CUT_ARC (-1) -static SCIP_Bool cutArcIsInvalid(const cut_arc_id arc) +/**< Checks if the given cut arc is invalid (negative) */ +static +SCIP_Bool cutArcIsInvalid( + const cut_arc_id arc /**< The cut arc to check */ + ) { return arc < 0; } -static SCIP_Bool cutArcIsValid(const cut_arc_id arc) +/**< Checks if the given cut arc is valid (nonnegative) */ +static SCIP_Bool cutArcIsValid( + const cut_arc_id arc /**< The cut arc to check */ + ) { return !cutArcIsInvalid(arc); } @@ -6436,7 +6445,9 @@ typedef struct SCIP_Bool reversed; /**< Orientation of the arc w.r.t. the representative */ } NewRowInformation; -static NewRowInformation emptyNewRowInformation(void) +/**< Initializes an empty NewRowInformation struct */ +static +NewRowInformation emptyNewRowInformation(void) { NewRowInformation information; information.member = SPQR_INVALID_MEMBER; @@ -6447,17 +6458,16 @@ static NewRowInformation emptyNewRowInformation(void) return information; } -/** - * Saves the information of the current row and partitions it based on whether or not the given columns are - * already part of the decomposition. - */ -static SCIP_RETCODE newRowUpdateRowInformation( - const SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const spqr_row row, - const spqr_col* columns, - const double* columnValues, - const int numColumns +/** Saves the information of the current row and partitions it based on whether or not the given columns are + * already part of the decomposition. */ +static +SCIP_RETCODE newRowUpdateRowInformation( + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition datastructure */ + const spqr_row row, /**< The row to check */ + const spqr_col* columns, /**< The column indices of the nonzeros in the row */ + const double* columnValues, /**< The matrix entries of the nonzeros in the row */ + const int numColumns /**< The number of nonzeros in the row */ ) { newRow->newRowIndex = row; @@ -6504,10 +6514,13 @@ static SCIP_RETCODE newRowUpdateRowInformation( return SCIP_OKAY; } -static reduced_member_id createRowReducedMembersToRoot( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const spqr_member firstMember +/**< Adds members to the reduced member tree, by starting at the given member, jumping up to the parent, repeating this + * procedure until the root has been added. */ +static +reduced_member_id createRowReducedMembersToRoot( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition datastructure */ + const spqr_member firstMember /**< The member to create a reduced member for */ ) { assert(SPQRmemberIsValid(firstMember)); @@ -6608,13 +6621,11 @@ static reduced_member_id createRowReducedMembersToRoot( } -/** - * Construct a smaller sub tree of the decomposition on which the cut arcs lie. - * @return - */ -static SCIP_RETCODE constructRowReducedDecomposition( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow +/**< Construct the reduced decomposition, which is the smallest subtree containing all members cut arcs */ +static +SCIP_RETCODE constructRowReducedDecomposition( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow /**< The network matrix row addition datastructure */ ) { #ifndef NDEBUG @@ -6759,18 +6770,14 @@ static SCIP_RETCODE constructRowReducedDecomposition( } -/** - * Marks an arc as 'cut'. This implies that its cycle in the decomposition must be elongated - * @param newRow - * @param arc - * @param reducedMember - */ -static void createCutArc( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const spqr_arc arc, - const reduced_member_id reducedMember, - SCIP_Bool reversed +/**< Marks an arc as 'cut'. This implies that its cycle in the decomposition must be elongated.*/ +static +void createCutArc( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition datastructure */ + const spqr_arc arc, /**< The arc to mark as a cut arc */ + const reduced_member_id reducedMember, /**< The reduced member containing the arc */ + SCIP_Bool reversed /**< Indicates if the new row entry has +1 or -1 (TRUE) for the arc */ ) { cut_arc_id cut_arc = newRow->numCutArcs; @@ -6812,13 +6819,12 @@ static void createCutArc( listNode->arcReversed = reversed; } -/** - * Creates all cut arcs within the decomposition for the new row. - * Note this preallocates memory for cut arcs which may be created by propagation. - */ -static SCIP_RETCODE createReducedDecompositionCutArcs( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow +/**< Creates all cut arcs within the decomposition for the new row. + * Note this preallocates memory for cut arcs which may be created by propagation. */ +static +SCIP_RETCODE createReducedDecompositionCutArcs( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow /**< The network matrix row addition datastructure */ ) { //Allocate memory for cut arcs @@ -6864,14 +6870,12 @@ static SCIP_RETCODE createReducedDecompositionCutArcs( return SCIP_OKAY; } -/** - * Determines the members of the reduced decomposition which are leafs. - * This is used in propagation to ensure propagation is only checked for components which have at most one neighbour - * which is not propagated. - */ -static SCIP_RETCODE determineLeafReducedMembers( - const SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow +/**< Determines the members of the reduced decomposition which are leafs. This is used in propagation to ensure + * propagation is only checked for components which have at most one neighbour that is not propagated. */ +static +SCIP_RETCODE determineLeafReducedMembers( + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow /**< The network matrix row addition datastructure */ ) { if( newRow->numDecompositionColumnArcs > newRow->memLeafMembers ) @@ -6893,12 +6897,11 @@ static SCIP_RETCODE determineLeafReducedMembers( return SCIP_OKAY; } -/** - * Preallocates memory arrays necessary for searching rigid components. - */ -static SCIP_RETCODE allocateRigidSearchMemory( - const SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow +/**< Preallocates memory arrays necessary for searching rigid components. */ +static +SCIP_RETCODE allocateRigidSearchMemory( + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow /**< The network matrix row addition datastructure */ ) { int totalNumNodes = getNumNodes(dec); @@ -7002,10 +7005,13 @@ static SCIP_RETCODE allocateRigidSearchMemory( return SCIP_OKAY; } -static void zeroOutColors( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const spqr_node firstRemoveNode +/**< Clears the colors for all nodes. We do DFS in reverse (reverse exploration order), as zeroing out the entire + * array is more expensive. */ +static +void zeroOutColors( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition datastructure */ + const spqr_node firstRemoveNode /**< The first node that was colored in the original DFS */ ) { assert(firstRemoveNode < newRow->memNodeColors); @@ -7053,9 +7059,11 @@ static void zeroOutColors( } } -static void cleanUpPreviousIteration( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow +/**< Cleans up various arrays used in previous iterations; this is cheaper than reallocating empty memory. */ +static +void cleanUpPreviousIteration( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow /**< The network matrix row addition datastructure */ ) { //zero out coloring information from previous check @@ -7092,10 +7100,12 @@ static void cleanUpPreviousIteration( #endif } -static void rigidFindStarNodes( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const reduced_member_id toCheck +/**< Finds all the star nodes, i.e. nodes that are adjacent to all cut arcs, in a rigid member */ +static +void rigidFindStarNodes( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + const reduced_member_id toCheck /**< The reduced member to check */ ) { //4 cases: @@ -7192,11 +7202,14 @@ static void rigidFindStarNodes( } } -static void zeroOutColorsExceptNeighbourhood( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const spqr_node articulationNode, - const spqr_node startRemoveNode +/**< Clears the colors for all nodes, but the neighbours. We do DFS in reverse (reverse exploration order), + * as zeroing out the entire array is more expensive. */ +static +void zeroOutColorsExceptNeighbourhood( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + const spqr_node articulationNode, /**< The node whose neighbours we want to keep */ + const spqr_node startRemoveNode /**< The node where DFS started */ ) { COLOR_STATUS* neighbourColors = newRow->temporaryColorArray; @@ -7236,13 +7249,15 @@ static void zeroOutColorsExceptNeighbourhood( } -//Theoretically, there is a faster algorithm using lowest common ancestor queries, but it is quite complicated. -//Thus, we stick with the 'simple' version below, which is easily fast enough in practice. -static void intersectionOfAllPaths( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const reduced_member_id toCheck, - int* const nodeNumPaths +/**< Find the intersection of all cut arc paths in the given rigid member. + * Theoretically, there is a faster algorithm using lowest common ancestor queries, but it is quite complicated. + * Thus, we stick with the 'simple' version below, which is typically also faster in practice. */ +static +void intersectionOfAllPaths( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + const reduced_member_id toCheck, /**< The rigid member to check */ + int* const nodeNumPaths /**< Tracks for each node, how many cut arc paths cross it */ ) { int* intersectionPathDepth = newRow->intersectionPathDepth; @@ -7352,9 +7367,11 @@ static void intersectionOfAllPaths( } while( cutArcIsValid(cutArc)); } -static void addArticulationNode( - SCIP_NETROWADD* newRow, - spqr_node articulationNode +/**< Add a node to array of articulation nodes */ +static +void addArticulationNode( + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + spqr_node articulationNode /**< The node to add */ ) { #ifndef NDEBUG @@ -7367,11 +7384,14 @@ static void addArticulationNode( ++newRow->numArticulationNodes; } -static void articulationPoints( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - ArticulationNodeInformation* nodeInfo, - reduced_member_id reducedMember +/**< Find all the articulation points of the rigid member graph where the cut arcs are removed. Articulation points + * are nodes whose removal disconnects the remaining graph. */ +static +void articulationPoints( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + ArticulationNodeInformation* nodeInfo, /**< Stores information about the found articulation nodes */ + reduced_member_id reducedMember /**< The reduced member to find the articulation nodes for */ ) { const SCIP_Bool* arcRemoved = newRow->isArcCut; @@ -7454,13 +7474,17 @@ static void articulationPoints( } } -static void rigidConnectedColoringRecursive( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - spqr_node articulationNode, - spqr_node firstProcessNode, - COLOR_STATUS firstColor, - SCIP_Bool* isGood +/**< For a given articulation node, partitions the rigid member's graph where it is removed into a SOURCE and SINK + * partition; all cut arcs must point (after reversal) from the source partition to the sink partition. This is akin + * to finding a 2-coloring, and uses a DFS to do so. This function specifically executes the DFS/recursive part.*/ +static +void rigidConnectedColoringRecursive( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + spqr_node articulationNode, /**< The articulation node to obtain the colouring for */ + spqr_node firstProcessNode, /**< The first node to process for the DFS */ + COLOR_STATUS firstColor, /**< The partition that the first node lies in. */ + SCIP_Bool* isGood /**< Returns whether the coloring was consistent */ ) { const SCIP_Bool* isArcCut = newRow->isArcCut; @@ -7528,12 +7552,16 @@ static void rigidConnectedColoringRecursive( } } -static void rigidConnectedColoring( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const reduced_member_id reducedMember, - const spqr_node node, - SCIP_Bool* const isGood +/**< For a given articulation node, partitions the rigid member's graph where it is removed into a SOURCE and SINK + * partition; all cut arcs must point (after reversal) from the source partition to the sink partition. This is akin + * to finding a 2-coloring, and uses a DFS to do so.*/ +static +void rigidConnectedColoring( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + const reduced_member_id reducedMember, /**< The rigid member whose graph to color */ + const spqr_node node, /**< The articulation node to obtain the coloring for */ + SCIP_Bool* const isGood /**< Returns whether the coloring was consistent */ ) { @@ -7597,11 +7625,15 @@ static void rigidConnectedColoring( } } -static spqr_node checkNeighbourColoringArticulationNode( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const spqr_node articulationNode, - spqr_arc* const adjacentSplittingArc +/**< Given a coloring for an articulation node, we can prove that a neighbouring node is splittable if and only if it is + * the unique node in the neighbourhood of the articulation node within the SOURCE or SINK partition. In this function, + * we check for a splittable articulation node if such an adjacent node exists, and return it if possible. */ +static +spqr_node checkNeighbourColoringArticulationNode( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + const spqr_node articulationNode, /**< The articulation whose neighbours are to be checked */ + spqr_arc* const adjacentSplittingArc/**< The arc connecting the two splittable nodes. */ ) { spqr_node firstSideCandidate = SPQR_INVALID_NODE; @@ -7657,13 +7689,15 @@ static spqr_node checkNeighbourColoringArticulationNode( return SPQR_INVALID_NODE; } - -static void rigidGetSplittableArticulationPointsOnPath( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const reduced_member_id toCheck, - spqr_node firstNode, - spqr_node secondNode +/**< Find all the splittable articulation points that lie on the intersection of all cut arc cycles. + * firstNode and secondNode can be set to limit the nodes that this function checks to the given nodes.*/ +static +void rigidGetSplittableArticulationPointsOnPath( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + const reduced_member_id toCheck, /**< The rigid member to check */ + spqr_node firstNode, /**< Optional: the first given node that should be checked */ + spqr_node secondNode /**< Optional: the second given node that should be checked */ ) { assert(newRow->reducedMembers[toCheck].numCutArcs > 1); @@ -7760,16 +7794,17 @@ static void rigidGetSplittableArticulationPointsOnPath( newRow->remainsNetwork = FALSE; } -static void determineParallelType( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const reduced_member_id toCheckMember, - const spqr_arc markerToOther, - const reduced_member_id otherMember, - const spqr_arc markerToCheck +/**< Checks if a leaf parallel member can be propagated away from the reduced decomposition */ +static +void determineParallelType( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + const reduced_member_id toCheckMember, /**< The parallel leaf member to check */ + const spqr_arc markerToOther, /**< Marker to the non-leaf member */ + const reduced_member_id otherMember, /**< The connected non-leaf member */ + const spqr_arc markerToCheck /**< Marker from the non-leaf to the leaf parallel member */ ) { - SPQRRowReducedMember* member = &newRow->reducedMembers[toCheckMember]; assert(cutArcIsValid(member->firstCutArc)); @@ -7811,13 +7846,15 @@ static void determineParallelType( } } -static void determineSeriesType( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const reduced_member_id toCheckMember, - const spqr_arc markerToOther, - const reduced_member_id otherMember, - const spqr_arc markerToCheck +/**< Checks if a leaf series member can be propagated away from the reduced decomposition */ +static +void determineSeriesType( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + const reduced_member_id toCheckMember, /**< The series leaf member to check */ + const spqr_arc markerToOther, /**< Marker to the non-leaf member */ + const reduced_member_id otherMember, /**< The connected non-leaf member */ + const spqr_arc markerToCheck /**< Marker from the non-leaf to the leaf series member */ ) { //Propagation only calls this function if the arc is tree already, so we do not check it here. @@ -7832,13 +7869,15 @@ static void determineSeriesType( newRow->cutArcs[cutArc].arcReversed); } -static void determineRigidType( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const reduced_member_id toCheckMember, - const spqr_arc markerToOther, - const reduced_member_id otherMember, - const spqr_arc markerToCheck +/**< Checks if a leaf rigid member can be propagated away from the reduced decomposition */ +static +void determineRigidType( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + const reduced_member_id toCheckMember, /**< The rigid leaf member to check */ + const spqr_arc markerToOther, /**< Marker to the non-leaf member */ + const reduced_member_id otherMember, /**< The connected non-leaf member */ + const spqr_arc markerToCheck /**< Marker from the non-leaf to the rigid leaf member */ ) { assert(newRow->reducedMembers[toCheckMember].numCutArcs > @@ -7894,13 +7933,15 @@ static void determineRigidType( } } -static RowReducedMemberType determineType( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const reduced_member_id toCheckMember, - const spqr_arc markerToOther, - const reduced_member_id otherMember, - const spqr_arc markerToCheck +/**< Checks if a leaf member can be propagated away from the reduced decomposition */ +static +RowReducedMemberType determineType( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + const reduced_member_id toCheckMember, /**< The leaf member to check */ + const spqr_arc markerToOther, /**< Marker to the non-leaf member */ + const reduced_member_id otherMember, /**< The connected non-leaf member */ + const spqr_arc markerToCheck /**< Marker from the non-leaf to the leaf member */ ) { assert(newRow->reducedMembers[toCheckMember].type == TYPE_UNDETERMINED); @@ -7931,9 +7972,11 @@ static RowReducedMemberType determineType( return newRow->reducedMembers[toCheckMember].type; } -static void propagateComponents( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow +/**< Propagates away all leaf members that are propagatable to shrink the reduced decomposition */ +static +void propagateComponents( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow /**< The network matrix row addition data structure */ ) { int leafArrayIndex = 0; @@ -8035,11 +8078,13 @@ typedef struct spqr_node second; } Nodes; -static Nodes -rigidDetermineCandidateNodesFromAdjacentComponents( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const reduced_member_id toCheck +/**< Computes the intersection nodes of all markers that point to reduced tree members. These are the only nodes that + * may become Y-splittable, after propagation. */ +static +Nodes rigidDetermineCandidateNodesFromAdjacentComponents( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + const reduced_member_id toCheck /**< The rigid member to check */ ) { @@ -8104,9 +8149,11 @@ rigidDetermineCandidateNodesFromAdjacentComponents( return pair; } -static SCIP_RETCODE allocateTreeSearchMemory( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow +/**< Allocates memory for various procedures that need to do tree-search for rigid members (e.g. DFS or BFS) */ +static +SCIP_RETCODE allocateTreeSearchMemory( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow /**< The network matrix row addition data structure */ ) { int necessarySpace = newRow->numReducedMembers; @@ -8120,10 +8167,12 @@ static SCIP_RETCODE allocateTreeSearchMemory( return SCIP_OKAY; } -static void determineSingleRowRigidType( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id reducedMember +/**< Determine the type of a rigid member when it is the only member in the reduced decomposition */ +static +void determineSingleRowRigidType( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id reducedMember /**< The rigid member to determine the type for */ ) { assert(newRow->reducedMembers[reducedMember].numCutArcs > @@ -8149,10 +8198,12 @@ static void determineSingleRowRigidType( } } -static void determineSingleParallelType( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id reducedMember +/**< Determine the type of a parallel member when it is the only member in the reduced decomposition */ +static +void determineSingleParallelType( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id reducedMember /**< The parallel member to determine the type for */ ) { SPQRRowReducedMember* redMember = &newRow->reducedMembers[reducedMember]; @@ -8186,9 +8237,11 @@ static void determineSingleParallelType( } } -static void determineSingleSeriesType( - SCIP_NETROWADD* newRow, - reduced_member_id reducedMember +/**< Determine the type of a series member when it is the only member in the reduced decomposition */ +static +void determineSingleSeriesType( + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id reducedMember /**< The series member to determine the type for */ ) { assert(newRow->reducedMembers[reducedMember].numCutArcs == 1); @@ -8196,12 +8249,15 @@ static void determineSingleSeriesType( newRow->reducedMembers[reducedMember].type = TYPE_MERGED; } -static spqr_node determineAndColorSplitNode( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id id, - spqr_node candidateOne, - spqr_node candidateTwo +/**< Sets the given split nodes; performs bookkeeping so that we have an easier time updating the graph in many + * edge cases. Algorithmically speaking, does nothing important. */ +static +spqr_node determineAndColorSplitNode( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id id, /**< The reduced member that we compute thes plit node for */ + spqr_node candidateOne, /**< The first candidate node */ + spqr_node candidateTwo /**< The second candidate node */ ) { if( SPQRnodeIsInvalid(newRow->reducedMembers[id].splitNode)) @@ -8276,10 +8332,13 @@ static spqr_node determineAndColorSplitNode( return splitNode; } -static void determineSplitTypeFirstLeaf( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id reducedId +/**< After propagation, determines the split type of the first leaf node of the reduced decomposition. The leaf nodes + * of the decomposition after propagation can only be of type P or R. */ +static +void determineSplitTypeFirstLeaf( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id reducedId /**< The member to determine the split type for */ ) { spqr_member member = newRow->reducedMembers[reducedId].member; @@ -8387,19 +8446,21 @@ static void determineSplitTypeFirstLeaf( redMember->type = TYPE_MERGED; } -/**< A data structure that tells us if the head or tail of a virtual arc is split, and if the other node is in the +/**< A data structure that tells us if the head or tail of a marked arc is split, and if the other node is in the * source or the sink partition. */ typedef struct { - SCIP_Bool headSplit; /**< Is the head or tail of the virtual arc split?*/ + SCIP_Bool headSplit; /**< Is the head or tail of the marked arc split?*/ SCIP_Bool otherIsSource; /**< Is the non-split node in the source or sink partition? */ } SplitOrientation; -static SplitOrientation getRelativeOrientationRigid( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id reducedId, - spqr_arc arcToNext +/**< Get the split orientation for a given rigid member and marked arc. */ +static +SplitOrientation getRelativeOrientationRigid( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id reducedId, /**< The member to determine the split orientation for */ + spqr_arc arcToNext /**< The marked arc to determine the orientaiton for */ ) { assert(findArcMemberNoCompression(dec, arcToNext) == newRow->reducedMembers[reducedId].member); @@ -8443,11 +8504,13 @@ static SplitOrientation getRelativeOrientationRigid( return orientation; } -static SplitOrientation getRelativeOrientationParallel( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id reducedId, - spqr_arc arcToNext +/**< Get the split orientation for a given parallel member and marked arc. */ +static +SplitOrientation getRelativeOrientationParallel( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id reducedId, /**< The member to determine the split orientation for */ + spqr_arc arcToNext /**< The marked arc to determine the orientaiton for */ ) { assert(findArcMemberNoCompression(dec, arcToNext) == newRow->reducedMembers[reducedId].member); @@ -8464,11 +8527,13 @@ static SplitOrientation getRelativeOrientationParallel( return orientation; } -static SplitOrientation getRelativeOrientationSeries( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id reducedId, - spqr_arc arcToNext +/**< Get the split orientation for a given series member and marked arc. */ +static +SplitOrientation getRelativeOrientationSeries( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id reducedId, /**< The member to determine the split orientation for */ + spqr_arc arcToNext /**< The marked arc to determine the orientaiton for */ ) { assert(findArcMemberNoCompression(dec, arcToNext) == newRow->reducedMembers[reducedId].member); @@ -8486,11 +8551,13 @@ static SplitOrientation getRelativeOrientationSeries( return orientation; } -static SplitOrientation getRelativeOrientation( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id reducedId, - spqr_arc arcToNext +/**< Get the split orientation for a given member and marked arc. */ +static +SplitOrientation getRelativeOrientation( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id reducedId, /**< The member to determine the split orientation for */ + spqr_arc arcToNext /**< The marked arc to determine the orientaiton for */ ) { switch( getMemberType(dec, newRow->reducedMembers[reducedId].member) ) @@ -8514,12 +8581,14 @@ static SplitOrientation getRelativeOrientation( } } -static void determineSplitTypeSeries( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id reducedId, - spqr_arc marker, - SplitOrientation previousOrientation +/**< Determine the split type of a series node when the SPQR tree is not a singular member. */ +static +void determineSplitTypeSeries( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id reducedId, /**< The member to determine the split orientation for */ + spqr_arc marker, /**< The marker to the previous member whose type was already determined */ + SplitOrientation previousOrientation /**< The orientation to the previous member */ ) { int numAdjacentMembers = @@ -8562,12 +8631,14 @@ static void determineSplitTypeSeries( newRow->reducedMembers[reducedId].type = TYPE_MERGED; } -static void determineSplitTypeParallel( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id reducedId, - spqr_arc marker, - SplitOrientation previousOrientation +/**< Determine the split type of a parallel node when the SPQR tree is not a singular member. */ +static +void determineSplitTypeParallel( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id reducedId, /**< The member to determine the split orientation for */ + spqr_arc marker, /**< The marker to the previous member whose type was already determined */ + SplitOrientation previousOrientation /**< The orientation to the previous member */ ) { SPQRRowReducedMember* redMember = &newRow->reducedMembers[reducedId]; @@ -8618,13 +8689,15 @@ static void determineSplitTypeParallel( redMember->type = TYPE_MERGED; } -static void determineSplitTypeRigid( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id reducedId, - spqr_member member, - spqr_arc marker, - SplitOrientation previousOrientation +/**< Determine the split type of a rigid node when the SPQR tree is not a singular member. */ +static +void determineSplitTypeRigid( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id reducedId, /**< The reduced member to determine the split orientation for */ + spqr_member member, /**< The member belonging to the reduced member */ + spqr_arc marker, /**< The marker to the previous member whose type was already determined */ + SplitOrientation previousOrientation /**< The orientation to the previous member */ ) { assert(dec); @@ -8729,12 +8802,15 @@ static void determineSplitTypeRigid( newRow->reducedMembers[reducedId].type = TYPE_MERGED; } -static void determineSplitTypeNext( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id current, - reduced_member_id next, - SCIP_Bool currentIsParent +/**< Determine the split type of a node when the SPQR tree is not a singular member. This function is used for all + * the nodes that are not first in the given ordering.*/ +static +void determineSplitTypeNext( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id current, /**< The current node, whose type has already been determined */ + reduced_member_id next, /**< The next node to determine the type of, adjacent to the current node*/ + SCIP_Bool currentIsParent /**< Is the current node a parent of the next node? */ ) { spqr_member member = newRow->reducedMembers[next].member; @@ -8771,10 +8847,12 @@ static void determineSplitTypeNext( } } -static void determineMergeableTypes( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id root +/**< For the given root reduced member of a component, determine if the component is updateable/transformable. */ +static +void determineMergeableTypes( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id root /**< The root of the given component */ ) { assert(newRow->numReducedMembers <= newRow->memMergeTreeCallData); @@ -8874,7 +8952,11 @@ static void determineMergeableTypes( } } -static void cleanUpRowMemberInformation(SCIP_NETROWADD* newRow) +/**< Empty the new member information array */ +static +void cleanUpRowMemberInformation( + SCIP_NETROWADD* newRow /**< The network matrix row addition data structure */ + ) { //This loop is at the end as memberInformation is also used to assign the cut arcs during propagation //Clean up the memberInformation array @@ -8890,12 +8972,15 @@ static void cleanUpRowMemberInformation(SCIP_NETROWADD* newRow) #endif } -static SCIP_RETCODE rigidTransformArcIntoCycle( - SCIP_NETMATDECDATA* dec, - const spqr_member member, - const spqr_arc arc, - const SCIP_Bool reverseArcDirection, - NewRowInformation* const newRowInformation +/**< Transforms a rigid arc by putting it in series with the new column arc. We need to do some magic to keep our + * the internal datastructures consistent in this case. */ +static +SCIP_RETCODE rigidTransformArcIntoCycle( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + const spqr_member member, /**< The rigid member that contains the arc that is moved */ + const spqr_arc arc, /**< The arc that is moved to a new series member */ + const SCIP_Bool reverseArcDirection,/**< Is the given arc reversed? */ + NewRowInformation* const newRowInfo /**< Stores information on how to add the new row */ ) { //If a cycle already exists, just expand it with the new arc. @@ -8922,13 +9007,13 @@ static SCIP_RETCODE rigidTransformArcIntoCycle( } if( markerCycleMember != SPQR_INVALID_MEMBER) { - newRowInformation->member = markerCycleMember; + newRowInfo->member = markerCycleMember; if( arcIsReversedNonRigid(dec, markerCycleArc)) { - newRowInformation->reversed = reverseArcDirection; + newRowInfo->reversed = reverseArcDirection; } else { - newRowInformation->reversed = !reverseArcDirection; + newRowInfo->reversed = !reverseArcDirection; } return SCIP_OKAY; @@ -8990,18 +9075,20 @@ static SCIP_RETCODE rigidTransformArcIntoCycle( dec->arcs[arc].element = arcIsTree(dec, arc) ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; dec->arcs[arc].childMember = newCycle; } - newRowInformation->member = newCycle; - newRowInformation->reversed = !reverseArcDirection; + newRowInfo->member = newCycle; + newRowInfo->reversed = !reverseArcDirection; return SCIP_OKAY; } -static SCIP_RETCODE transformSingleRigid( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const reduced_member_id reducedMember, - const spqr_member member, - NewRowInformation* const newRowInformation +/**< Updates a single rigid member to reflect the new row. */ +static +SCIP_RETCODE transformSingleRigid( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + const reduced_member_id reducedMember, /**< The reduced member to transform */ + const spqr_member member, /**< The member belonging to the reduced member */ + NewRowInformation* const newRowInfo /**< Stores information about the new row placement */ ) { if( SPQRarcIsValid(newRow->reducedMembers[reducedMember].articulationArc)) @@ -9025,7 +9112,7 @@ static SCIP_RETCODE transformSingleRigid( } SCIP_CALL(rigidTransformArcIntoCycle(dec, member, newRow->reducedMembers[reducedMember].articulationArc, - reversed, newRowInformation)); + reversed, newRowInfo)); return SCIP_OKAY; } @@ -9055,19 +9142,18 @@ static SCIP_RETCODE transformSingleRigid( cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; } while( cutArcIsValid(cutArcIdx)); - newRowInformation->member = member; + newRowInfo->member = member; if( newRow->reducedMembers[reducedMember].otherIsSource ) { - newRowInformation->head = newNode; - newRowInformation->tail = splitNode; + newRowInfo->head = newNode; + newRowInfo->tail = splitNode; } else { - newRowInformation->head = splitNode; - newRowInformation->tail = newNode; + newRowInfo->head = splitNode; + newRowInfo->tail = newNode; } - newRowInformation->representative = findArcSign(dec, - newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc).representative; - newRowInformation->reversed = FALSE; + newRowInfo->representative = findArcSign(dec,newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc).representative; + newRowInfo->reversed = FALSE; return SCIP_OKAY; } @@ -9115,23 +9201,23 @@ static SCIP_RETCODE transformSingleRigid( } while( TRUE ); /*lint !e506*/ newRow->reducedMembers[reducedMember].coloredNode = SPQR_INVALID_NODE; - newRowInformation->member = member; - newRowInformation->head = newNode; - newRowInformation->tail = splitNode; - newRowInformation->representative = findArcSign(dec, - newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc).representative; - newRowInformation->reversed = FALSE; + newRowInfo->member = member; + newRowInfo->head = newNode; + newRowInfo->tail = splitNode; + newRowInfo->representative = findArcSign(dec,newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc).representative; + newRowInfo->reversed = FALSE; return SCIP_OKAY; } - -static SCIP_RETCODE splitParallelRowAddition( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const reduced_member_id reducedMember, - const spqr_member member, - NewRowInformation* newRowInfo +/**< Splits a single parallel member into two adjacent ones, where the cut arcs and non-cut arcs get their own member */ +static +SCIP_RETCODE splitParallelRowAddition( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + const reduced_member_id reducedMember, /**< The reduced member to transform */ + const spqr_member member, /**< The member belonging to the reduced member */ + NewRowInformation* const newRowInfo /**< Stores information about the new row placement */ ) { assert(newRow->reducedMembers[reducedMember].numCutArcs > 0); @@ -9312,26 +9398,30 @@ static SCIP_RETCODE splitParallelRowAddition( return SCIP_OKAY; } -static SCIP_RETCODE transformSingleParallel( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const reduced_member_id reducedMember, - const spqr_member member, - NewRowInformation* info +/**< Updates a single rigid member to reflect the new row. */ +static +SCIP_RETCODE transformSingleParallel( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + const reduced_member_id reducedMember, /**< The reduced member to transform */ + const spqr_member member, /**< The member belonging to the reduced member */ + NewRowInformation* info /**< Stores information about the new row placement */ ) { SCIP_CALL(splitParallelRowAddition(dec, newRow, reducedMember, member, info)); return SCIP_OKAY; } -static SCIP_RETCODE splitSeriesMergingRowAddition( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - const reduced_member_id reducedMember, - const spqr_member member, - spqr_member* const mergingMember, - SCIP_Bool* const isCut, - spqr_arc* representativeEdge +/**< Split a series member into multiple series members for merging */ +static +SCIP_RETCODE splitSeriesMergingRowAddition( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + const reduced_member_id reducedMember, /**< The reduced series member */ + const spqr_member member, /**< The member belonging to the reduced member */ + spqr_member* const mergingMember, /**< The member that contains the arcs that are to be merged */ + SCIP_Bool* const isCut, /**< Array that contains whether each arc is Cut */ + spqr_arc* representativeEdge /**< Pointer to the representative arc for the split off arcs */ ) { assert(getNumMemberArcs(dec, member) >= 3); @@ -9439,13 +9529,15 @@ static SCIP_RETCODE splitSeriesMergingRowAddition( return SCIP_OKAY; } -static SCIP_RETCODE splitParallelMerging( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id reducedMember, - spqr_member member, - spqr_member* const pMergeMember, - spqr_arc* const cutRepresentative +/**< Split a parallel member into multiple parallel members for merging */ +static +SCIP_RETCODE splitParallelMerging( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id reducedMember, /**< The reduced parallel member */ + spqr_member member, /**< The member belonging to the reduced member */ + spqr_member* const pMergeMember, /**< The member that contains the arcs that are to be merged */ + spqr_arc* const cutRepresentative /**< Pointer to the representative arc for all cut arcs */ ) { //When merging, we cannot have propagated members; @@ -9664,11 +9756,13 @@ static SCIP_RETCODE splitParallelMerging( return SCIP_OKAY; } -static SCIP_RETCODE splitFirstLeaf( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id leaf, - NewRowInformation* const newRowInformation +/**< Update the first leaf to reflect the addition of the new row */ +static +SCIP_RETCODE splitFirstLeaf( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id leaf, /**< The leaf to split the first node for */ + NewRowInformation* const newRowInfo /**< Stores how to add the new row */ ) { assert(cutArcIsValid(newRow->reducedMembers[leaf].firstCutArc)); @@ -9734,19 +9828,19 @@ static SCIP_RETCODE splitFirstLeaf( updateMemberType(dec, mergeMember, SPQR_MEMBERTYPE_RIGID); - newRowInformation->member = mergeMember; + newRowInfo->member = mergeMember; if( newRow->reducedMembers[leaf].otherIsSource ) { - newRowInformation->head = thirdNode; - newRowInformation->tail = splitNode; + newRowInfo->head = thirdNode; + newRowInfo->tail = splitNode; } else { - newRowInformation->head = splitNode; - newRowInformation->tail = thirdNode; + newRowInfo->head = splitNode; + newRowInfo->tail = thirdNode; } - newRowInformation->reversed = FALSE; - newRowInformation->representative = splitArc; + newRowInfo->reversed = FALSE; + newRowInfo->representative = splitArc; return SCIP_OKAY; } @@ -9792,29 +9886,31 @@ static SCIP_RETCODE splitFirstLeaf( firstNodeArc = iterArc; } } while( TRUE ); /*lint !e506*/ - newRowInformation->head = newNode; - newRowInformation->tail = splitNode; - newRowInformation->member = member; - newRowInformation->reversed = FALSE; - newRowInformation->representative = findArcSign(dec, iterArc).representative; + newRowInfo->head = newNode; + newRowInfo->tail = splitNode; + newRowInfo->member = member; + newRowInfo->reversed = FALSE; + newRowInfo->representative = findArcSign(dec, iterArc).representative; return SCIP_OKAY; } -static SCIP_RETCODE mergeSplitMemberIntoParent( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - spqr_member member, - spqr_member parent, - spqr_arc parentToChild, - spqr_arc childToParent, - SCIP_Bool headToHead, - spqr_node parentNode, - spqr_node childNode, - spqr_member* mergedMember, - spqr_node* arcNodeOne, - spqr_node* arcNodeTwo, - spqr_node* thirdNode +/**< Merge an (updated) member into its parent. This function is mainly there to prevent duplication. */ +static +SCIP_RETCODE mergeSplitMemberIntoParent( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + spqr_member member, /**< The member to merge */ + spqr_member parent, /**< The member's parent */ + spqr_arc parentToChild, /**< The marker pointing from the parent to child */ + spqr_arc childToParent, /**< The marker pointing from the child to the parent */ + SCIP_Bool headToHead, /**< Should the marker heads be identified with one another? */ + spqr_node parentNode, /**< A node in the parent that is not adjacent to the marker */ + spqr_node childNode, /**< A node in the child that is not adjacent to the marker */ + spqr_member* mergedMember, /**< Pointer to the member that contains both members after merging */ + spqr_node* arcNodeOne, /**< The first identified node of the two marker arcs */ + spqr_node* arcNodeTwo, /**< The second identified node of the two marker arcs */ + spqr_node* thirdNode /**< The node that parentNode and childNode are identified into */ ) { assert(dec); @@ -9875,13 +9971,17 @@ static SCIP_RETCODE mergeSplitMemberIntoParent( return SCIP_OKAY; } -static SCIP_RETCODE splitAndMergeSeries( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id smallMember, - SCIP_Bool largeIsParent, - NewRowInformation* const newRowInformation, - spqr_member member +/**< Update a series member to reflect the addition of the new row, and merge it into the previous members that were + * updated. */ +static +SCIP_RETCODE splitAndMergeSeries( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id smallMember, /**< The reduced series member */ + SCIP_Bool largeIsParent, /**< Whether the large member containing previously updated members is + * the parent of this member */ + NewRowInformation* const newRowInfo, /**< Stores the information on how to add the new row */ + spqr_member member /**< The member belonging to the reduced series member */ ) { SCIP_Bool isCut = FALSE; @@ -9945,17 +10045,17 @@ static SCIP_RETCODE splitAndMergeSeries( arcSetRepresentative(dec, splitArc, SPQR_INVALID_ARC); } - spqr_member otherMember = newRowInformation->member; + spqr_member otherMember = newRowInfo->member; spqr_arc otherMarker = largeIsParent ? markerOfParent(dec, mergingMember) : markerToParent(dec, otherMember); - assert(nodeIsRepresentative(dec, newRowInformation->tail)); - assert(nodeIsRepresentative(dec, newRowInformation->head)); + assert(nodeIsRepresentative(dec, newRowInfo->tail)); + assert(nodeIsRepresentative(dec, newRowInfo->head)); spqr_node splitNode = newRow->reducedMembers[smallMember].splitHead ? findEffectiveArcHead(dec, otherMarker) : findEffectiveArcTail(dec, otherMarker); - spqr_node otherNode = splitNode == newRowInformation->head ? newRowInformation->tail : newRowInformation->head; - assert(splitNode == newRowInformation->head || splitNode == newRowInformation->tail); - newRowInformation->representative = mergeArcSigns(dec, newRowInformation->representative, splitArc, FALSE); + spqr_node otherNode = splitNode == newRowInfo->head ? newRowInfo->tail : newRowInfo->head; + assert(splitNode == newRowInfo->head || splitNode == newRowInfo->tail); + newRowInfo->representative = mergeArcSigns(dec, newRowInfo->representative, splitArc, FALSE); spqr_member mergedMember = SPQR_INVALID_MEMBER; spqr_node arcNodeOne; @@ -9978,37 +10078,41 @@ static SCIP_RETCODE splitAndMergeSeries( } newRow->reducedMembers[smallMember].member = mergedMember; - newRowInformation->member = mergedMember; + newRowInfo->member = mergedMember; SCIP_Bool splitIsReferenceHead = newRow->reducedMembers[smallMember].splitHead; - SCIP_Bool splitIsNewRowHead = splitNode == newRowInformation->head; + SCIP_Bool splitIsNewRowHead = splitNode == newRowInfo->head; if( !splitIsReferenceHead && !splitIsNewRowHead ) { - newRowInformation->head = thirdNode; - newRowInformation->tail = arcNodeOne; + newRowInfo->head = thirdNode; + newRowInfo->tail = arcNodeOne; } else if( !splitIsReferenceHead && splitIsNewRowHead ) { - newRowInformation->head = arcNodeOne; - newRowInformation->tail = thirdNode; + newRowInfo->head = arcNodeOne; + newRowInfo->tail = thirdNode; } else if( splitIsReferenceHead && !splitIsNewRowHead ) { - newRowInformation->head = thirdNode; - newRowInformation->tail = arcNodeTwo; + newRowInfo->head = thirdNode; + newRowInfo->tail = arcNodeTwo; } else if( splitIsReferenceHead && splitIsNewRowHead ) { - newRowInformation->head = arcNodeTwo; - newRowInformation->tail = thirdNode; + newRowInfo->head = arcNodeTwo; + newRowInfo->tail = thirdNode; } return SCIP_OKAY; } -static SCIP_RETCODE splitAndMergeParallel( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id smallMember, - SCIP_Bool largeIsParent, - NewRowInformation* const newRowInformation, - spqr_member member +/**< Update a parallel member to reflect the addition of the new row, and merge it into the previous members that were + * updated. */ +static +SCIP_RETCODE splitAndMergeParallel( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id smallMember, /**< The reduced parallel member */ + SCIP_Bool largeIsParent, /**< Whether the large member containing previously updated members is + * the parent of this member */ + NewRowInformation* const newRowInfo, /**< Stores the information on how to add the new row */ + spqr_member member /**< The member belonging to the reduced parallel member */ ) { spqr_member mergeMember = SPQR_INVALID_MEMBER; @@ -10062,19 +10166,19 @@ static SCIP_RETCODE splitAndMergeParallel( } while( arc != first_arc ); arcSetRepresentative(dec, splitArc, SPQR_INVALID_ARC); - spqr_member otherMember = newRowInformation->member; + spqr_member otherMember = newRowInfo->member; spqr_arc otherMarker = largeIsParent ? markerOfParent(dec, mergeMember) : markerToParent(dec, otherMember); - assert(nodeIsRepresentative(dec, newRowInformation->tail)); - assert(nodeIsRepresentative(dec, newRowInformation->head)); + assert(nodeIsRepresentative(dec, newRowInfo->tail)); + assert(nodeIsRepresentative(dec, newRowInfo->head)); spqr_node largeSplitNode = newRow->reducedMembers[smallMember].splitHead ? findEffectiveArcHead(dec, otherMarker) : findEffectiveArcTail(dec, otherMarker); spqr_node largeOtherNode = - largeSplitNode == newRowInformation->head ? newRowInformation->tail : newRowInformation->head; - assert(largeSplitNode == newRowInformation->head || largeSplitNode == newRowInformation->tail); + largeSplitNode == newRowInfo->head ? newRowInfo->tail : newRowInfo->head; + assert(largeSplitNode == newRowInfo->head || largeSplitNode == newRowInfo->tail); - newRowInformation->representative = mergeArcSigns(dec, newRowInformation->representative, splitArc, FALSE); + newRowInfo->representative = mergeArcSigns(dec, newRowInfo->representative, splitArc, FALSE); spqr_member mergedMember = SPQR_INVALID_MEMBER; spqr_node arcNodeOne; @@ -10097,38 +10201,42 @@ static SCIP_RETCODE splitAndMergeParallel( } - newRowInformation->member = mergedMember; + newRowInfo->member = mergedMember; SCIP_Bool splitIsReferenceHead = newRow->reducedMembers[smallMember].splitHead; - SCIP_Bool splitIsNewRowHead = largeSplitNode == newRowInformation->head; + SCIP_Bool splitIsNewRowHead = largeSplitNode == newRowInfo->head; if( !splitIsReferenceHead && !splitIsNewRowHead ) { - newRowInformation->head = mergeNodeThree; - newRowInformation->tail = arcNodeOne; + newRowInfo->head = mergeNodeThree; + newRowInfo->tail = arcNodeOne; } else if( !splitIsReferenceHead && splitIsNewRowHead ) { - newRowInformation->head = arcNodeOne; - newRowInformation->tail = mergeNodeThree; + newRowInfo->head = arcNodeOne; + newRowInfo->tail = mergeNodeThree; } else if( splitIsReferenceHead && !splitIsNewRowHead ) { - newRowInformation->head = mergeNodeThree; - newRowInformation->tail = arcNodeTwo; + newRowInfo->head = mergeNodeThree; + newRowInfo->tail = arcNodeTwo; } else if( splitIsReferenceHead && splitIsNewRowHead ) { - newRowInformation->head = arcNodeTwo; - newRowInformation->tail = mergeNodeThree; + newRowInfo->head = arcNodeTwo; + newRowInfo->tail = mergeNodeThree; } return SCIP_OKAY; } -static SCIP_RETCODE splitAndMergeRigid( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id smallMember, - SCIP_Bool largeIsParent, - NewRowInformation* const newRowInformation, - spqr_member member +/**< Update a rigid member to reflect the addition of the new row, and merge it into the previous members that were + * updated. */ +static +SCIP_RETCODE splitAndMergeRigid( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id smallMember, /**< The reduced rigid member */ + SCIP_Bool largeIsParent, /**< Whether the large member containing previously updated members is + * the parent of this member */ + NewRowInformation* const newRowInfo, /**< Stores the information on how to add the new row */ + spqr_member member /**< The member belonging to the reduced rigid member */ ) { @@ -10136,7 +10244,7 @@ static SCIP_RETCODE splitAndMergeRigid( SCIP_CALL(createNode(dec, &newNode)); spqr_member smallMemberMember = member; - spqr_member largeMemberMember = newRowInformation->member; + spqr_member largeMemberMember = newRowInfo->member; spqr_arc smallMarker = largeIsParent ? markerToParent(dec, smallMemberMember) : markerOfParent(dec, largeMemberMember); @@ -10191,7 +10299,7 @@ static SCIP_RETCODE splitAndMergeRigid( spqr_arc representative = findArcSign(dec, smallMarker).representative; - newRowInformation->representative = mergeArcSigns(dec, newRowInformation->representative, representative, + newRowInfo->representative = mergeArcSigns(dec, newRowInfo->representative, representative, newRow->reducedMembers[smallMember].willBeReversed); spqr_node largeMarkerHead = findArcHead(dec, largeMarker); @@ -10202,12 +10310,10 @@ static SCIP_RETCODE splitAndMergeRigid( largeMarkerHead = largeMarkerTail; largeMarkerTail = temp; } - assert(newRowInformation->head == largeMarkerHead || newRowInformation->head == largeMarkerTail || - newRowInformation->tail == largeMarkerHead || newRowInformation->tail == largeMarkerTail); - spqr_node largeOtherNode = ( newRowInformation->head == largeMarkerHead || - newRowInformation->head == largeMarkerTail ) - ? newRowInformation->tail - : newRowInformation->head; + assert(newRowInfo->head == largeMarkerHead || newRowInfo->head == largeMarkerTail || + newRowInfo->tail == largeMarkerHead || newRowInfo->tail == largeMarkerTail); + spqr_node largeOtherNode = ( newRowInfo->head == largeMarkerHead || newRowInfo->head == largeMarkerTail ) + ? newRowInfo->tail : newRowInfo->head; spqr_member mergedMember = SPQR_INVALID_MEMBER; spqr_node arcNodeOne; @@ -10230,44 +10336,47 @@ static SCIP_RETCODE splitAndMergeRigid( &arcNodeTwo, &mergeNodeThree)); } - newRowInformation->member = mergedMember; + newRowInfo->member = mergedMember; - SCIP_Bool otherIsHead = largeOtherNode == newRowInformation->head; - SCIP_Bool adjacentToMarkerHead = ( newRowInformation->tail == largeMarkerHead || - newRowInformation->head == largeMarkerHead ); + SCIP_Bool otherIsHead = largeOtherNode == newRowInfo->head; + SCIP_Bool adjacentToMarkerHead = ( newRowInfo->tail == largeMarkerHead || + newRowInfo->head == largeMarkerHead ); if( adjacentToMarkerHead ) { if( otherIsHead ) { - newRowInformation->head = mergeNodeThree; - newRowInformation->tail = arcNodeTwo; + newRowInfo->head = mergeNodeThree; + newRowInfo->tail = arcNodeTwo; } else { - newRowInformation->head = arcNodeTwo; - newRowInformation->tail = mergeNodeThree; + newRowInfo->head = arcNodeTwo; + newRowInfo->tail = mergeNodeThree; } } else { if( otherIsHead ) { - newRowInformation->head = mergeNodeThree; - newRowInformation->tail = arcNodeOne; + newRowInfo->head = mergeNodeThree; + newRowInfo->tail = arcNodeOne; } else { - newRowInformation->head = arcNodeOne; - newRowInformation->tail = mergeNodeThree; + newRowInfo->head = arcNodeOne; + newRowInfo->tail = mergeNodeThree; } } return SCIP_OKAY; } -static SCIP_RETCODE splitAndMerge( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id smallMember, - SCIP_Bool largeIsParent, - NewRowInformation* const newRowInformation +/**< Update a member to reflect the addition of the new row, and merge it into the previous members that were updated.*/ +static +SCIP_RETCODE splitAndMerge( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id smallMember, /**< The reduced rigid member */ + SCIP_Bool largeIsParent, /**< Whether the large member containing previously updated members is + * the parent of this member */ + NewRowInformation* const newRowInfo /**< Stores the information on how to add the new row */ ) { spqr_member member = newRow->reducedMembers[smallMember].member; @@ -10275,17 +10384,17 @@ static SCIP_RETCODE splitAndMerge( { case SPQR_MEMBERTYPE_RIGID: { - SCIP_CALL(splitAndMergeRigid(dec, newRow, smallMember, largeIsParent, newRowInformation, member)); + SCIP_CALL(splitAndMergeRigid(dec, newRow, smallMember, largeIsParent, newRowInfo, member)); break; } case SPQR_MEMBERTYPE_PARALLEL: { - SCIP_CALL(splitAndMergeParallel(dec, newRow, smallMember, largeIsParent, newRowInformation, member)); + SCIP_CALL(splitAndMergeParallel(dec, newRow, smallMember, largeIsParent, newRowInfo, member)); break; } case SPQR_MEMBERTYPE_SERIES: { - SCIP_CALL(splitAndMergeSeries(dec, newRow, smallMember, largeIsParent, newRowInformation, member)); + SCIP_CALL(splitAndMergeSeries(dec, newRow, smallMember, largeIsParent, newRowInfo, member)); break; } case SPQR_MEMBERTYPE_LOOP: @@ -10297,11 +10406,14 @@ static SCIP_RETCODE splitAndMerge( return SCIP_OKAY; } -static SCIP_RETCODE mergeTree( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - reduced_member_id root, - NewRowInformation* const newRowInformation +/**< Update an SPQR tree with multiple members to reflect the addition of a new row, + * merging it into one big rigid node. */ +static +SCIP_RETCODE mergeTree( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + reduced_member_id root, /**< The root node of the SPQR tree that is to be merged */ + NewRowInformation* const newRowInfo /**< Stores the information on how to add the new row */ ) { //We use the same ordering as when finding @@ -10321,7 +10433,7 @@ static SCIP_RETCODE mergeTree( } } } - SCIP_CALL(splitFirstLeaf(dec, newRow, leaf, newRowInformation)); + SCIP_CALL(splitFirstLeaf(dec, newRow, leaf, newRowInfo)); reduced_member_id baseNode = leaf; reduced_member_id nextNode = newRow->reducedMembers[baseNode].parent; @@ -10329,7 +10441,7 @@ static SCIP_RETCODE mergeTree( while( reducedMemberIsValid(nextNode)) { //check this node - SCIP_CALL(splitAndMerge(dec, newRow, nextNode, FALSE, newRowInformation)); + SCIP_CALL(splitAndMerge(dec, newRow, nextNode, FALSE, newRowInfo)); //Recursively merge the children //use a while loop to avoid recursion; we may get stack overflows for large graphs @@ -10351,7 +10463,7 @@ static SCIP_RETCODE mergeTree( if( currentchild == baseNode || newRow->reducedMembers[currentchild].type == TYPE_PROPAGATED){ continue; } - SCIP_CALL(splitAndMerge(dec, newRow, currentchild, TRUE, newRowInformation)); + SCIP_CALL(splitAndMerge(dec, newRow, currentchild, TRUE, newRowInfo)); //recursively process the child depth += 1; @@ -10367,11 +10479,13 @@ static SCIP_RETCODE mergeTree( return SCIP_OKAY; } -static SCIP_RETCODE transformComponentRowAddition( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* newRow, - SPQRRowReducedComponent* component, - NewRowInformation* const newRowInformation +/**< Update an SPQR tree (a signle component of the SPQR forest) to reflect addition of a new row. */ +static +SCIP_RETCODE transformComponentRowAddition( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ + SPQRRowReducedComponent* component, /**< The component to transform */ + NewRowInformation* const newRowInfo /**< Stores the information on how to add the new row */ ) { assert(component); @@ -10387,21 +10501,21 @@ static SCIP_RETCODE transformComponentRowAddition( switch( type ) { case SPQR_MEMBERTYPE_RIGID: - SCIP_CALL(transformSingleRigid(dec, newRow, reducedMember, member, newRowInformation)); + SCIP_CALL(transformSingleRigid(dec, newRow, reducedMember, member, newRowInfo)); break; case SPQR_MEMBERTYPE_PARALLEL: { - SCIP_CALL(transformSingleParallel(dec, newRow, reducedMember, member, newRowInformation)); + SCIP_CALL(transformSingleParallel(dec, newRow, reducedMember, member, newRowInfo)); break; } case SPQR_MEMBERTYPE_LOOP: case SPQR_MEMBERTYPE_SERIES: { - newRowInformation->member = member; + newRowInfo->member = member; cut_arc_id cutArc = newRow->reducedMembers[reducedMember].firstCutArc; spqr_arc arc = newRow->cutArcs[cutArc].arc; - newRowInformation->reversed = arcIsReversedNonRigid(dec, arc) == newRow->cutArcs[cutArc].arcReversed; + newRowInfo->reversed = arcIsReversedNonRigid(dec, arc) == newRow->cutArcs[cutArc].arcReversed; if( type == SPQR_MEMBERTYPE_LOOP ) { if( getNumMemberArcs(dec, member) == 2 ) @@ -10422,15 +10536,16 @@ static SCIP_RETCODE transformComponentRowAddition( return SCIP_OKAY; } - SCIP_CALL(mergeTree(dec, newRow, component->root, newRowInformation)); + SCIP_CALL(mergeTree(dec, newRow, component->root, newRowInfo)); return SCIP_OKAY; } - -static SCIP_RETCODE SCIPnetrowaddCreate( - SCIP* scip, - SCIP_NETROWADD** prowadd +/**< Create the network row addition data structure */ +static +SCIP_RETCODE SCIPnetrowaddCreate( + SCIP* scip, /**< Main SCIP instance */ + SCIP_NETROWADD** prowadd /**< Pointer to store the new row addition struct at */ ) { assert(scip); @@ -10520,9 +10635,11 @@ static SCIP_RETCODE SCIPnetrowaddCreate( return SCIP_OKAY; } -static void SCIPnetrowaddFree( - SCIP* scip, - SCIP_NETROWADD** prowadd +/**< Frees the network row addition data structure */ +static +void SCIPnetrowaddFree( + SCIP* scip, /**< Main SCIP instance */ + SCIP_NETROWADD** prowadd /**< Pointer to row addition struct to be freed */ ) { assert(*prowadd); @@ -10556,13 +10673,15 @@ static void SCIPnetrowaddFree( SCIPfreeBlockMemory(scip, prowadd); } -static SCIP_RETCODE SCIPnetrowaddCheck( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* rowadd, - int row, - const int* nonzcols, - const double* nonzvals, - int nnonzs +/**< Checks if the given row can be added to the network decomposition */ +static +SCIP_RETCODE SCIPnetrowaddCheck( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* rowadd, /**< The network matrix row addition data structure */ + int row, /**< The row to be checked */ + const int* nonzcols, /**< The column indices of the row's nonzero values */ + const double* nonzvals, /**< The matrix entries of the row's nonzeroes */ + int nnonzs /**< The number of nonzeroes in the row */ ) { assert(dec); @@ -10602,9 +10721,11 @@ static SCIP_RETCODE SCIPnetrowaddCheck( return SCIP_OKAY; } -static SCIP_RETCODE SCIPnetrowaddAdd( - SCIP_NETMATDECDATA* dec, - SCIP_NETROWADD* rowadd +/**< Adds the last checked row to the network decomposition */ +static +SCIP_RETCODE SCIPnetrowaddAdd( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + SCIP_NETROWADD* rowadd /**< The network matrix row addition data structure */ ) { assert(rowadd->remainsNetwork); @@ -10705,7 +10826,11 @@ static SCIP_RETCODE SCIPnetrowaddAdd( return SCIP_OKAY; } -static SCIP_Bool SCIPnetrowaddRemainsNetwork(const SCIP_NETROWADD* rowadd) +/**< Returns whether the last checked row can be added to the network decomposition */ +static +SCIP_Bool SCIPnetrowaddRemainsNetwork( + const SCIP_NETROWADD* rowadd /**< The network matrix row addition data structure */ + ) { return rowadd->remainsNetwork; } diff --git a/src/scip/network.h b/src/scip/network.h index bbeca2b622..89755169f1 100644 --- a/src/scip/network.h +++ b/src/scip/network.h @@ -144,7 +144,7 @@ SCIP_Bool SCIPnetmatdecContainsColumn( * * Note that this method is 'stupid', and does not delete the associated graph data structure. * Moreover, it does not explicitly check if the rows/columns that the user provides are a connected - * component of the submatrix given by the decomposition. + * component of the submatrix given by the decomposition. Use with care! * If this is not the case, then calling this function is considered a bug. */ SCIP_EXPORT From c19e446eab34c1971501ccadc9b9f15cf9597456 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Wed, 24 Jul 2024 11:46:01 +0200 Subject: [PATCH 22/63] Fix formatting in network.c --- src/scip/network.c | 696 +++++++++++++++++++++++++++++---------------- 1 file changed, 456 insertions(+), 240 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 62ab74ae30..2f8709a2ee 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -2056,7 +2056,8 @@ void removeArcFromMemberArcList( { dec->members[member].firstArc = SPQR_INVALID_ARC; - } else + } + else { spqr_arc nextArc = dec->arcs[arc].arcListNode.next; spqr_arc prevArc = dec->arcs[arc].arcListNode.previous; @@ -2106,7 +2107,8 @@ void process_arc( callStack[*callStackSize].arc = other_arc; callStack[*callStackSize].reversed = arcIsReversed; ++( *callStackSize ); - } else + } + else { spqr_element element = arcGetElement(dec, arc); assert(SPQRelementIsRow(element)); @@ -2115,7 +2117,8 @@ void process_arc( cycledir[*num_cycle_arcs] = arcIsReversed; ++( *num_cycle_arcs ); } - } else + } + else { spqr_member child_member = findArcChildMemberNoCompression(dec, arc); spqr_arc other_arc = markerToParent(dec, child_member); @@ -2229,11 +2232,13 @@ int decompositionGetFundamentalCycleRows( { --pathSearchCallStackSize; dfsData = &pathSearchCallStack[pathSearchCallStackSize - 1]; - } else + } + else { break; } - } while( pathSearchCallStackSize > 0 ); + } + while( pathSearchCallStackSize > 0 ); } for( int i = 0; i < pathSearchCallStackSize; ++i ) { @@ -2267,7 +2272,8 @@ int decompositionGetFundamentalCycleRows( tree_count++; } iter_arc = getNextMemberArc(dec, iter_arc); - } while( iter_arc != first_arc ); + } + while( iter_arc != first_arc ); if( tree_count != 1 ) { return -1; @@ -2288,12 +2294,14 @@ int decompositionGetFundamentalCycleRows( SCIP_Bool treeIsReversed = arcIsReversedNonRigid(dec, iter_arc); process_arc(output, &num_rows, callStack, &callStackSize, iter_arc, dec, computedSignStorage, ( columnReversed == treeIsReversed ) != reverseEverything); - } else + } + else { nontree_count++; } iter_arc = getNextMemberArc(dec, iter_arc); - } while( iter_arc != first_arc ); + } + while( iter_arc != first_arc ); if( nontree_count != 1 ) { return -1; @@ -2708,11 +2716,13 @@ void reorderComponent( member = oldParent; newMarkerToParent = oldMarkerOfParent; markerOfNewParent = oldMarkerToParent; - } else + } + else { break; } - } while( TRUE ); /*lint !e506*/ + } + while( TRUE ); /*lint !e506*/ dec->members[newRoot].parentMember = SPQR_INVALID_MEMBER; dec->members[newRoot].markerToParent = SPQR_INVALID_ARC; dec->members[newRoot].markerOfParent = SPQR_INVALID_ARC; @@ -3411,7 +3421,8 @@ reduced_member_id createReducedMembersToRoot( callstack[callDepth].member = parentMember; continue; - } else + } + else { //we found a new reduced decomposition component @@ -3772,13 +3783,14 @@ SCIP_RETCODE newColUpdateColInformation( SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->decompositionRowArcs, newCol->memDecompositionRowArcs, newNumArcs)); SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->decompositionArcReversed, - newCol->memDecompositionRowArcs, newNumArcs)); + newCol->memDecompositionRowArcs, newNumArcs)); newCol->memDecompositionRowArcs = newNumArcs; } newCol->decompositionRowArcs[newCol->numDecompositionRowArcs] = rowArc; newCol->decompositionArcReversed[newCol->numDecompositionRowArcs] = reversed; ++newCol->numDecompositionRowArcs; - } else + } + else { //Not in the decomposition: add it to the set of arcs which are newly added with this row. if( newCol->numNewRowArcs == newCol->memNewRowArcs ) @@ -3970,7 +3982,8 @@ void determineSingleComponentType( { passesForwards = newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc); - } else if( + } + else if( ( newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc)) != passesForwards ) { @@ -3992,7 +4005,8 @@ void determineSingleComponentType( //Type -> Cycle; //Propagate arc redMem->type = REDUCEDMEMBER_TYPE_CYCLE; - } else + } + else { //Type -> single_end redMem->type = REDUCEDMEMBER_TYPE_MERGED; @@ -4037,8 +4051,9 @@ void determinePathSeriesType( { passesForwards = newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc); - } else if(( newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc)) != - passesForwards ) + } + else if(( newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc)) != + passesForwards ) { good = FALSE; break; @@ -4064,7 +4079,8 @@ void determinePathSeriesType( if(( firstReversed == targetReversed ) == reversePath ) { redMem->pathType = INTO_HEAD; - } else + } + else { redMem->pathType = OUT_HEAD; } @@ -4126,7 +4142,8 @@ void determinePathSeriesType( if( isInto(previousType)) { redMem->pathType = INTO_HEAD; - } else + } + else { redMem->pathType = OUT_HEAD; } @@ -4251,21 +4268,25 @@ void determinePathRigidType( if( isTargetTail ) { redMem->pathType = INTO_TAIL; - } else + } + else { redMem->pathType = INTO_HEAD; } - } else + } + else { if( isTargetTail ) { redMem->pathType = OUT_TAIL; - } else + } + else { redMem->pathType = OUT_HEAD; } } - } else + } + else { redMem->reverseArcs = TRUE; //because of the reversal, all the heads/tails are switched below @@ -4274,16 +4295,19 @@ void determinePathRigidType( if( isTargetTail ) { redMem->pathType = INTO_HEAD; - } else + } + else { redMem->pathType = INTO_TAIL; } - } else + } + else { if( isTargetTail ) { redMem->pathType = OUT_HEAD; - } else + } + else { redMem->pathType = OUT_TAIL; } @@ -4306,16 +4330,20 @@ void determinePathRigidType( if( redMem->rigidPathEnd == targetHead ) { redMem->pathType = INTO_HEAD; - } else if( redMem->rigidPathEnd == targetTail ) + } + else if( redMem->rigidPathEnd == targetTail ) { redMem->pathType = INTO_TAIL; - } else if( redMem->rigidPathStart == targetHead ) + } + else if( redMem->rigidPathStart == targetHead ) { redMem->pathType = OUT_HEAD; - } else if( redMem->rigidPathStart == targetTail ) + } + else if( redMem->rigidPathStart == targetTail ) { redMem->pathType = OUT_TAIL; - } else + } + else { redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; newCol->remainsNetwork = FALSE; @@ -4343,7 +4371,8 @@ void determinePathRigidType( } assert(startsAtHead || endsAtTail);//both can hold; they can form cycle but other components can not be reduced // redMem->reverseArcs = isInto(previousType) != startsAtHead; //Reverse only if there is no path starting at head - } else + } + else {//Into tail or outHead //Check if path starts at tail or ends at head if( !startsAtTail && !endsAtHead ) @@ -4384,23 +4413,28 @@ void determinePathRigidType( if( startsAtHead && endsAtTail ) { outReverse = ( startsAtTargetHead || startsAtTargetTail ) == isInto(previousType); - } else if( startsAtHead ) + } + else if( startsAtHead ) { outReverse = !isInto(previousType); - } else + } + else { assert(endsAtTail); outReverse = isInto(previousType); } - } else + } + else { if( startsAtTail && endsAtHead ) { outReverse = ( startsAtTargetHead || startsAtTargetTail ) == isInto(previousType); - } else if( startsAtTail ) + } + else if( startsAtTail ) { outReverse = !isInto(previousType); - } else + } + else { assert(endsAtHead); outReverse = isInto(previousType); @@ -4414,59 +4448,72 @@ void determinePathRigidType( if( startsAtHead && endsAtTail ) { outHead = ( startsAtTargetTail || endsAtTargetHead ) == isInto(previousType); - } else if( startsAtHead ) + } + else if( startsAtHead ) { if( endsAtTargetHead ) { outHead = isInto(previousType); - } else if( endsAtTargetTail ) + } + else if( endsAtTargetTail ) { outHead = !isInto(previousType); - } else + } + else { isBad = TRUE; } - } else + } + else { assert(endsAtTail); if( startsAtTargetTail ) { outHead = isInto(previousType); - } else if( startsAtTargetHead ) + } + else if( startsAtTargetHead ) { outHead = !isInto(previousType); - } else + } + else { isBad = TRUE; } } - } else + } + else { if( startsAtTail && endsAtHead ) { outHead = ( startsAtTargetTail || endsAtTargetHead ) == isInto(previousType); - } else if( startsAtTail ) + } + else if( startsAtTail ) { if( endsAtTargetHead ) { outHead = isInto(previousType); - } else if( endsAtTargetTail ) + } + else if( endsAtTargetTail ) { outHead = !isInto(previousType); - } else + } + else { isBad = TRUE; } - } else + } + else { assert(endsAtHead); if( startsAtTargetTail ) { outHead = isInto(previousType); - } else if( startsAtTargetHead ) + } + else if( startsAtTargetHead ) { outHead = !isInto(previousType); - } else + } + else { isBad = TRUE; } @@ -4483,7 +4530,8 @@ void determinePathRigidType( if( isInto(previousType)) { redMem->pathType = outHead ? INTO_HEAD : INTO_TAIL; - } else + } + else { redMem->pathType = outHead ? OUT_HEAD : OUT_TAIL; } @@ -4494,7 +4542,8 @@ void determinePathRigidType( if( isInto(previousType) == isHead(previousType)) { redMem->reverseArcs = startsAtHead != isInto(previousType); - } else + } + else { redMem->reverseArcs = startsAtTail != isInto(previousType); } @@ -4503,7 +4552,8 @@ void determinePathRigidType( if( isInto(previousType)) { redMem->pathType = INTO_HEAD; - } else + } + else { redMem->pathType = OUT_HEAD; } @@ -4724,7 +4774,8 @@ ReducedMemberType checkLeaf( { passesForwards = newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc); - } else if( + } + else if( ( newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc)) != passesForwards ) { @@ -4753,7 +4804,8 @@ ReducedMemberType checkLeaf( createPathArc(dec, newCol, toChild, parent, ( parentReversed == firstArcReversed ) != firstArcInPathReverse); - } else + } + else { //Type -> single_end reducedMember->type = REDUCEDMEMBER_TYPE_MERGED; @@ -4799,14 +4851,17 @@ void propagateCycles( if( newCol->reducedMembers[next].numPropagatedChildren == newCol->reducedMembers[next].numChildren ) { newCol->leafMembers[leafArrayIndex] = next; - } else + } + else { ++leafArrayIndex; } - } else if( type == REDUCEDMEMBER_TYPE_NOT_NETWORK ) + } + else if( type == REDUCEDMEMBER_TYPE_NOT_NETWORK ) { return; - } else + } + else { assert(type == REDUCEDMEMBER_TYPE_MERGED); ++leafArrayIndex; @@ -4820,7 +4875,8 @@ void propagateCycles( newCol->reducedComponents[component].pathEndMembers[newCol->reducedComponents[component].numPathEndMembers] = leaf; ++newCol->reducedComponents[component].numPathEndMembers; } - } else + } + else { ++leafArrayIndex; int component = newCol->reducedMembers[leaf].componentIndex; @@ -4870,7 +4926,8 @@ void propagateCycles( { root = child; continue; - } else if( type == REDUCEDMEMBER_TYPE_NOT_NETWORK ) + } + else if( type == REDUCEDMEMBER_TYPE_NOT_NETWORK ) { return; } @@ -4916,7 +4973,8 @@ void determineComponentTypes( { assert(component->root == component->pathEndMembers[0]); determineSingleComponentType(dec, newCol, component->root); - } else + } + else { assert(component->numPathEndMembers == 2); determinePathTypes(dec, newCol, component); @@ -5076,7 +5134,8 @@ SCIP_RETCODE splitParallel( if( parentMoved ) { SCIP_CALL(createMarkerPair(dec, *childParallel, parallel, !childContainsTree, FALSE, FALSE)); - } else + } + else { SCIP_CALL(createMarkerPair(dec, parallel, *childParallel, childContainsTree, FALSE, FALSE)); } @@ -5121,7 +5180,8 @@ SCIP_RETCODE splitSeries( if( reversed == reducedMember->pathBackwards ) { setTerminalReversed(newColInfo, !reversed); - } else + } + else { setTerminalReversed(newColInfo, reversed); } @@ -5170,7 +5230,8 @@ SCIP_RETCODE splitSeries( { SCIP_CALL(createMarkerPair(dec, member, *loopMember, TRUE, FALSE, FALSE)); SCIP_CALL(createMarkerPair(dec, *loopMember, pathMember, TRUE, FALSE, TRUE)); - } else + } + else { SCIP_CALL(createMarkerPair(dec, pathMember, *loopMember, FALSE, FALSE, TRUE)); SCIP_CALL(createMarkerPair(dec, *loopMember, member, FALSE, FALSE, FALSE)); @@ -5198,7 +5259,8 @@ SCIP_RETCODE splitSeries( adjacentMember = findMemberParent(dec, reducedMember->member); adjacentMarker = markerOfParent(dec, reducedMember->member); memberMarker = arc; - } else if( arcIsChildMarker(dec, arc)) + } + else if( arcIsChildMarker(dec, arc)) { adjacentMember = findArcChildMember(dec, arc); adjacentMarker = markerToParent(dec, adjacentMember); @@ -5208,7 +5270,8 @@ SCIP_RETCODE splitSeries( break;//There is only a singular such arc } arc = getNextMemberArc(dec, arc); - } while( arc != firstArc ); + } + while( arc != firstArc ); if( SPQRmemberIsValid(adjacentMember)) { @@ -5244,7 +5307,8 @@ SCIP_RETCODE splitSeries( if( parentMoved ) { SCIP_CALL(createMarkerPair(dec, pathMember, member, FALSE, FALSE, FALSE)); - } else + } + else { SCIP_CALL(createMarkerPair(dec, member, pathMember, TRUE, FALSE, FALSE)); } @@ -5309,17 +5373,20 @@ SCIP_RETCODE splitSeriesMerging( { SCIP_CALL(createMarkerPairWithReferences(dec, pathMember, member, FALSE, inNewReversed, inOldReversed, &ignored, pathRepresentative)); - } else + } + else { SCIP_CALL(createMarkerPairWithReferences(dec, member, pathMember, TRUE, inOldReversed, inNewReversed, pathRepresentative, &ignored)); } - } else + } + else { if( pathArcIsValid(reducedMember->firstPathArc)) { *pathRepresentative = newCol->pathArcs[reducedMember->firstPathArc].arc; - } else + } + else { *pathRepresentative = SPQR_INVALID_ARC; } @@ -5340,7 +5407,8 @@ SCIP_RETCODE splitSeriesMerging( { parentMoved = parentMoved || markerToParent(dec, member) == arc; moveArcToNewMember(dec, arc, member, nonPathMember); - } else + } + else { canStop = TRUE; } @@ -5349,7 +5417,8 @@ SCIP_RETCODE splitSeriesMerging( { break; } - } while( TRUE ); /*lint !e506*/ + } + while( TRUE ); /*lint !e506*/ assert(getNumMemberArcs(dec, nonPathMember) >= 2); SCIP_Bool representativeIsTree = !arcIsTree(dec, exceptionArc1); if( SPQRarcIsValid(exceptionArc2)) @@ -5363,12 +5432,14 @@ SCIP_RETCODE splitSeriesMerging( { SCIP_CALL(createMarkerPairWithReferences(dec, nonPathMember, member, !representativeIsTree, inNewReversed, inOldReversed, &ignored, nonPathRepresentative)); - } else + } + else { SCIP_CALL(createMarkerPairWithReferences(dec, member, nonPathMember, representativeIsTree, inOldReversed, inNewReversed, nonPathRepresentative, &ignored)); } - } else + } + else { *nonPathRepresentative = SPQR_INVALID_ARC; if( numNonPathArcs != 0 ) @@ -5383,7 +5454,8 @@ SCIP_RETCODE splitSeriesMerging( break; } arc = getNextMemberArc(dec, arc); - } while( arc != firstArc ); + } + while( arc != firstArc ); assert(*nonPathRepresentative != SPQR_INVALID_ARC); } } @@ -5460,14 +5532,16 @@ SCIP_RETCODE transformFirstPathMember( if( arcIsReversedNonRigid(dec, pathRepresentative) == targetReversed ) { setArcHeadAndTail(dec, pathRepresentative, a, c); - } else + } + else { setArcHeadAndTail(dec, pathRepresentative, c, a); } if( arcIsReversedNonRigid(dec, nonPathRepresentative) == targetReversed ) { setArcHeadAndTail(dec, nonPathRepresentative, b, a); - } else + } + else { setArcHeadAndTail(dec, nonPathRepresentative, a, b); } @@ -5483,7 +5557,8 @@ SCIP_RETCODE transformFirstPathMember( if( pathType == INTO_HEAD ) { setTerminalTail(newColInfo, a); - } else + } + else { setTerminalHead(newColInfo, a); } @@ -5534,7 +5609,8 @@ SCIP_RETCODE transformAndMergeParallel( if( arcIsReversedNonRigid(dec, arc) == sourceReversed ) { setArcHeadAndTail(dec, arc, sourceHead, sourceTail); - } else + } + else { setArcHeadAndTail(dec, arc, sourceTail, sourceHead); } @@ -5542,7 +5618,8 @@ SCIP_RETCODE transformAndMergeParallel( arcSetReversed(dec, arc, FALSE); arc = getNextMemberArc(dec, arc); - } while( arc != firstArc ); + } + while( arc != firstArc ); arcSetRepresentative(dec, source, SPQR_INVALID_ARC); } @@ -5553,12 +5630,15 @@ SCIP_RETCODE transformAndMergeParallel( spqr_member newMergedMember = SPQR_INVALID_MEMBER; if( nextIsParent ) { - SCIP_CALL( mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, - source, newCol->reducedMembers[current].pathTargetArc, TRUE, &newMergedMember) ); - } else + SCIP_CALL(mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, + source, newCol->reducedMembers[current].pathTargetArc, TRUE, + &newMergedMember)); + } + else { - SCIP_CALL( mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, - newCol->reducedMembers[current].pathTargetArc, source, TRUE, &newMergedMember) ); + SCIP_CALL(mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, + newCol->reducedMembers[current].pathTargetArc, source, TRUE, + &newMergedMember)); } *mergedMember = newMergedMember; @@ -5606,7 +5686,8 @@ SCIP_RETCODE transformAndMergeSeries( if( SPQRarcIsValid(pathRepresentative)) { SCIP_CALL(createNode(dec, &c)); - } else + } + else { c = b; } @@ -5615,12 +5696,14 @@ SCIP_RETCODE transformAndMergeSeries( if( hasNonPath && hasTarget ) { SCIP_CALL(createNode(dec, &d)); - } else + } + else { if( hasNonPath ) { d = c; - } else + } + else { d = a; } @@ -5631,7 +5714,8 @@ SCIP_RETCODE transformAndMergeSeries( if( pathStartInHead ) { setArcHeadAndTail(dec, source, b, a); - } else + } + else { setArcHeadAndTail(dec, source, a, b); } @@ -5640,7 +5724,8 @@ SCIP_RETCODE transformAndMergeSeries( if(( arcIsReversedNonRigid(dec, pathRepresentative) == sourceReversed ) == pathStartInHead ) { setArcHeadAndTail(dec, pathRepresentative, c, b); - } else + } + else { setArcHeadAndTail(dec, pathRepresentative, b, c); } @@ -5652,7 +5737,8 @@ SCIP_RETCODE transformAndMergeSeries( if(( arcIsReversedNonRigid(dec, target) == sourceReversed ) == pathStartInHead ) { setArcHeadAndTail(dec, target, d, c); - } else + } + else { setArcHeadAndTail(dec, target, c, d); } @@ -5664,7 +5750,8 @@ SCIP_RETCODE transformAndMergeSeries( if(( arcIsReversedNonRigid(dec, nonPathRepresentative) == sourceReversed ) == pathStartInHead ) { setArcHeadAndTail(dec, nonPathRepresentative, a, d); - } else + } + else { setArcHeadAndTail(dec, nonPathRepresentative, d, a); } @@ -5680,12 +5767,15 @@ SCIP_RETCODE transformAndMergeSeries( spqr_member newMergedMember = SPQR_INVALID_MEMBER; if( nextIsParent ) { - SCIP_CALL( mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, - source, newCol->reducedMembers[current].pathTargetArc, TRUE, &newMergedMember) ); - } else + SCIP_CALL(mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, + source, newCol->reducedMembers[current].pathTargetArc, TRUE, + &newMergedMember)); + } + else { - SCIP_CALL( mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, - newCol->reducedMembers[current].pathTargetArc, source, TRUE, &newMergedMember) ); + SCIP_CALL(mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, + newCol->reducedMembers[current].pathTargetArc, source, TRUE, + &newMergedMember)); } *mergedMember = newMergedMember; @@ -5697,7 +5787,8 @@ SCIP_RETCODE transformAndMergeSeries( if( isInto(newCol->reducedMembers[current].pathType)) { setTerminalHead(info, c); - } else + } + else { setTerminalTail(info, c); } @@ -5729,14 +5820,15 @@ SCIP_RETCODE transformAndMergeRigid( if( nextIsParent ) { - SCIP_CALL( mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, - source, newCol->reducedMembers[current].pathTargetArc, !redMem->reverseArcs, - &newMergedMember) ); - } else + SCIP_CALL(mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, + source, newCol->reducedMembers[current].pathTargetArc, !redMem->reverseArcs, + &newMergedMember)); + } + else { - SCIP_CALL( mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, - newCol->reducedMembers[current].pathTargetArc, source, !redMem->reverseArcs, - &newMergedMember) ); + SCIP_CALL(mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, + newCol->reducedMembers[current].pathTargetArc, source, !redMem->reverseArcs, + &newMergedMember)); } *mergedMember = newMergedMember; @@ -5752,16 +5844,19 @@ SCIP_RETCODE transformAndMergeRigid( if( redMem->reverseArcs ) { setTerminalHead(info, redMem->rigidPathStart); - } else + } + else { setTerminalHead(info, redMem->rigidPathEnd); } - } else + } + else { if( redMem->reverseArcs ) { setTerminalTail(info, redMem->rigidPathEnd); - } else + } + else { setTerminalTail(info, redMem->rigidPathStart); } @@ -5926,12 +6021,13 @@ SCIP_RETCODE columnTransformSingleRigid( break; } arc = getNextNodeArc(dec, arc, reducedMember->rigidPathStart); - } while( arc != firstArc ); + } + while( arc != firstArc ); if( SPQRarcIsValid(existingArcWithPath)) { SCIP_Bool isParent = FALSE; spqr_member adjacentMember = arcIsChildMarker(dec, existingArcWithPath) ? - findArcChildMember(dec,existingArcWithPath) : SPQR_INVALID_MEMBER; + findArcChildMember(dec, existingArcWithPath) : SPQR_INVALID_MEMBER; if( existingArcWithPath == markerToParent(dec, member)) { adjacentMember = findMemberParent(dec, member); @@ -5943,7 +6039,8 @@ SCIP_RETCODE columnTransformSingleRigid( SCIP_Bool markerReversed = arcIsReversedNonRigid(dec, parallelMarker); setTerminalMember(newColInfo, adjacentMember); setTerminalReversed(newColInfo, markerReversed == pathInSameDirection); - } else + } + else { //create a new parallel and move the edge there //This is a bit painful, because we cannot actually remove edges because of the union-find data structure @@ -5959,15 +6056,18 @@ SCIP_RETCODE columnTransformSingleRigid( if( SPQRelementIsColumn(element)) { SCIP_CALL(createColumnArc(dec, adjacentParallel, &duplicate, SPQRelementToColumn(element), FALSE)); - } else + } + else { SCIP_CALL(createRowArc(dec, adjacentParallel, &duplicate, SPQRelementToRow(element), FALSE)); } - } else if( isParent ) + } + else if( isParent ) { SCIP_CALL(createParentMarker(dec, adjacentParallel, arcIsTree(dec, existingArcWithPath), adjacentMember, markerOfParent(dec, member), &duplicate, FALSE)); - } else + } + else { SCIP_CALL(createChildMarker(dec, adjacentParallel, adjacentMember, arcIsTree(dec, existingArcWithPath), &duplicate, FALSE)); @@ -5980,7 +6080,8 @@ SCIP_RETCODE columnTransformSingleRigid( { SCIP_CALL(createChildMarker(dec, adjacentParallel, member, !arcIsTree(dec, existingArcWithPath), ¶llelMarker, FALSE)); - } else + } + else { SCIP_CALL(createParentMarker(dec, adjacentParallel, !arcIsTree(dec, existingArcWithPath), member, existingArcWithPath, ¶llelMarker, FALSE)); @@ -5998,7 +6099,8 @@ SCIP_RETCODE columnTransformSingleRigid( : MARKER_COLUMN_ELEMENT; dec->arcs[existingArcWithPath].childMember = adjacentParallel; - } else + } + else { dec->arcs[existingArcWithPath].element = arcIsTree(dec, existingArcWithPath) ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; @@ -6103,7 +6205,8 @@ SCIP_RETCODE SCIPnetcoladdAdd( spqr_member member; SCIP_CALL(createStandaloneSeries(dec, newCol->newRowArcs, newCol->newRowArcReversed, newCol->numNewRowArcs, newCol->newColIndex, &member)); - } else if( newCol->numReducedComponents == 1 ) + } + else if( newCol->numReducedComponents == 1 ) { NewColInformation information = emptyNewColInformation(); SCIP_CALL(transformComponent(dec, newCol, &newCol->reducedComponents[0], &information)); @@ -6120,7 +6223,8 @@ SCIP_RETCODE SCIPnetcoladdAdd( arcSetRepresentative(dec, colArc, information.representative); arcSetReversed(dec, colArc, information.reversed != arcIsReversedNonRigid(dec, information.representative)); } - } else + } + else { spqr_member newSeries = SPQR_INVALID_MEMBER; SCIP_CALL(createConnectedSeries(dec, newCol->newRowArcs, newCol->newRowArcReversed, newCol->numNewRowArcs, @@ -6147,7 +6251,8 @@ SCIP_RETCODE SCIPnetcoladdAdd( changeLoopToParallel(dec, information.member); } } - } else + } + else { #ifndef NDEBUG int numDecComponentsBefore = numConnectedComponents(dec); @@ -6167,7 +6272,8 @@ SCIP_RETCODE SCIPnetcoladdAdd( moveArcToNewMember(dec, arc, information.member, newSeries); arcSetReversed(dec, arc, !newCol->arcInPathReversed[arc]); dec->members[information.member].type = SPQR_MEMBERTYPE_UNASSIGNED; - } else + } + else { reorderComponent(dec, information.member);//reorder the subtree so that the newly series member is a parent @@ -6493,7 +6599,8 @@ SCIP_RETCODE newRowUpdateRowInformation( newRow->decompositionColumnArcs[newRow->numDecompositionColumnArcs] = columnArc; newRow->decompositionColumnArcReversed[newRow->numDecompositionColumnArcs] = reversed; ++newRow->numDecompositionColumnArcs; - } else + } + else { //Not in the decomposition: add it to the set of arcs which are newly added with this row. if( newRow->numColumnArcs == newRow->memColumnArcs ) @@ -6571,7 +6678,8 @@ reduced_member_id createRowReducedMembersToRoot( callstack[callDepth].member = parentMember; continue; - } else + } + else { //we found a new reduced decomposition component @@ -6810,7 +6918,8 @@ void createCutArc( SCIPswapInts(&listNode->arcHead, &listNode->arcTail); } assert(SPQRnodeIsValid(listNode->arcHead) && SPQRnodeIsValid(listNode->arcTail)); - } else + } + else { listNode->arcHead = SPQR_INVALID_NODE; listNode->arcTail = SPQR_INVALID_NODE; @@ -7314,11 +7423,13 @@ void intersectionOfAllPaths( { --pathSearchCallStackSize; dfsData = &pathSearchCallStack[pathSearchCallStackSize - 1]; - } else + } + else { break; } - } while( pathSearchCallStackSize > 0 ); + } + while( pathSearchCallStackSize > 0 ); } } @@ -7364,7 +7475,8 @@ void intersectionOfAllPaths( assert(SPQRnodeIsValid(source) && SPQRnodeIsValid(target)); assert(source == target); - } while( cutArcIsValid(cutArc)); + } + while( cutArcIsValid(cutArc)); } /**< Add a node to array of articulation nodes */ @@ -7441,7 +7553,8 @@ void articulationPoints( nodeInfo[otherNode].discoveryTime = time; continue; - } else + } + else { nodeInfo[node].low = minValue(nodeInfo[node].low, nodeInfo[otherNode].discoveryTime); } @@ -7458,7 +7571,7 @@ void articulationPoints( spqr_node current_node = callStack[depth].node; spqr_node other_node = callStack[depth + 1].node; nodeInfo[current_node].low = minValue(nodeInfo[current_node].low, - nodeInfo[other_node].low); + nodeInfo[other_node].low); if( depth != 0 && !callStack[depth].isAP && nodeInfo[current_node].discoveryTime <= nodeInfo[other_node].low ) @@ -7526,7 +7639,8 @@ void rigidConnectedColoringRecursive( if( isArcCut[callData->arc] ) { nodeColors[otherNode] = currentColor == COLOR_SOURCE ? COLOR_SINK : COLOR_SOURCE;//reverse the colors - } else + } + else { nodeColors[otherNode] = currentColor; } @@ -7578,7 +7692,8 @@ void rigidConnectedColoring( assert(newRow->nodeColors[findArcHeadNoCompression(dec, memberArc)] == UNCOLORED); assert(newRow->nodeColors[findArcTailNoCompression(dec, memberArc)] == UNCOLORED); memberArc = getNextMemberArc(dec, memberArc); - } while( firstArc != memberArc ); + } + while( firstArc != memberArc ); } #endif @@ -7600,7 +7715,8 @@ void rigidConnectedColoring( { firstProcessNode = tail; firstColor = COLOR_SOURCE; - } else + } + else { assert(head != node); firstProcessNode = head; @@ -7617,7 +7733,8 @@ void rigidConnectedColoring( { zeroOutColors(dec, newRow, firstProcessNode); newRow->reducedMembers[reducedMember].coloredNode = SPQR_INVALID_NODE; - } else + } + else { //Otherwise, we zero out all colors but the ones which we need zeroOutColorsExceptNeighbourhood(dec, newRow, node, firstProcessNode); @@ -7657,31 +7774,36 @@ spqr_node checkNeighbourColoringArticulationNode( { firstSideCandidate = otherNode; firstSideArc = moveArc; - } else if( numFirstSide > 0 ) + } + else if( numFirstSide > 0 ) { firstSideCandidate = SPQR_INVALID_NODE; } ++numFirstSide; - } else + } + else { if( numSecondSide == 0 && arcIsTree(dec, moveArc)) { secondSideCandidate = otherNode; secondSideArc = moveArc; - } else if( numSecondSide > 0 ) + } + else if( numSecondSide > 0 ) { secondSideCandidate = SPQR_INVALID_NODE; } ++numSecondSide; } moveArc = getNextNodeArc(dec, moveArc, articulationNode); - } while( moveArc != firstArc ); + } + while( moveArc != firstArc ); if( numFirstSide == 1 ) { *adjacentSplittingArc = firstSideArc; return firstSideCandidate; - } else if( numSecondSide == 1 ) + } + else if( numSecondSide == 1 ) { *adjacentSplittingArc = secondSideArc; return secondSideCandidate; @@ -7819,7 +7941,8 @@ void determineParallelType( if( countedCutArcs == 0 ) { isReversed = arcIsReversed; - } else if( arcIsReversed != isReversed ) + } + else if( arcIsReversed != isReversed ) { good = FALSE; break; @@ -7838,7 +7961,8 @@ void determineParallelType( { //In all other cases, the bond can be split so that the result will be okay! newRow->reducedMembers[toCheckMember].type = TYPE_MERGED; - } else + } + else { SCIP_Bool markerIsReversed = arcIsReversedNonRigid(dec, markerToOther); createCutArc(dec, newRow, markerToCheck, otherMember, markerIsReversed != isReversed); @@ -7916,16 +8040,19 @@ void determineRigidType( newRow->reducedMembers[toCheckMember].otherIsSource; createCutArc(dec, newRow, markerToCheck, otherMember, reverse); - } else if( newRow->reducedMembers[toCheckMember].splitNode == markerHead || - newRow->reducedMembers[toCheckMember].splitNode == markerTail ) + } + else if( newRow->reducedMembers[toCheckMember].splitNode == markerHead || + newRow->reducedMembers[toCheckMember].splitNode == markerTail ) { newRow->reducedMembers[toCheckMember].type = TYPE_MERGED; - } else if( SPQRarcIsValid(newRow->reducedMembers[toCheckMember].articulationArc) && - ( newRow->reducedMembers[toCheckMember].otherNode == markerHead || - newRow->reducedMembers[toCheckMember].otherNode == markerTail )) + } + else if( SPQRarcIsValid(newRow->reducedMembers[toCheckMember].articulationArc) && + ( newRow->reducedMembers[toCheckMember].otherNode == markerHead || + newRow->reducedMembers[toCheckMember].otherNode == markerTail )) { newRow->reducedMembers[toCheckMember].type = TYPE_MERGED; - } else + } + else { //Found source or sinks, but not adjacent to the marker newRow->reducedMembers[toCheckMember].type = TYPE_NOT_NETWORK; @@ -8002,19 +8129,23 @@ void propagateComponents( if( newRow->reducedMembers[next].numPropagatedChildren == newRow->reducedMembers[next].numChildren ) { newRow->leafMembers[leafArrayIndex] = next; - } else + } + else { ++leafArrayIndex; } - } else if( type == TYPE_NOT_NETWORK ) + } + else if( type == TYPE_NOT_NETWORK ) { return; - } else + } + else { assert(type == TYPE_MERGED); ++leafArrayIndex; } - } else + } + else { ++leafArrayIndex; } @@ -8054,14 +8185,17 @@ void propagateComponents( if( type == TYPE_PROPAGATED ) { root = child; - } else if( type == TYPE_NOT_NETWORK ) + } + else if( type == TYPE_NOT_NETWORK ) { return; - } else + } + else { break; } - } else + } + else { break; } @@ -8106,7 +8240,8 @@ Nodes rigidDetermineCandidateNodesFromAdjacentComponents( { pair.first = head; pair.second = tail; - } else + } + else { if( pair.first != head && pair.first != tail ) { @@ -8134,7 +8269,8 @@ Nodes rigidDetermineCandidateNodesFromAdjacentComponents( { pair.first = head; pair.second = tail; - } else + } + else { if( pair.first != head && pair.first != tail ) { @@ -8191,7 +8327,8 @@ void determineSingleRowRigidType( if( SPQRnodeIsValid(newRow->reducedMembers[reducedMember].splitNode)) { newRow->reducedMembers[reducedMember].type = TYPE_MERGED; - } else + } + else { newRow->reducedMembers[reducedMember].type = TYPE_NOT_NETWORK; newRow->remainsNetwork = FALSE; @@ -8220,7 +8357,8 @@ void determineSingleParallelType( if( countedCutArcs == 0 ) { isReversed = arcIsReversed; - } else if( arcIsReversed != isReversed ) + } + else if( arcIsReversed != isReversed ) { good = FALSE; break; @@ -8231,7 +8369,8 @@ void determineSingleParallelType( { redMember->type = TYPE_NOT_NETWORK; newRow->remainsNetwork = FALSE; - } else + } + else { redMember->type = TYPE_MERGED; } @@ -8297,7 +8436,8 @@ spqr_node determineAndColorSplitNode( if( newRow->reducedMembers[id].numCutArcs == 1 ) { color = newRow->reducedMembers[id].otherIsSource ? COLOR_SINK : COLOR_SOURCE; - } else + } + else { color = newRow->reducedMembers[id].otherIsSource ? COLOR_SOURCE : COLOR_SINK; } @@ -8307,11 +8447,13 @@ spqr_node determineAndColorSplitNode( spqr_node other = head == splitNode ? findArcTail(dec, iterArc) : head; newRow->nodeColors[other] = color; iterArc = getNextNodeArc(dec, iterArc, splitNode); - } while( iterArc != firstNodeArc ); + } + while( iterArc != firstNodeArc ); newRow->nodeColors[newRow->reducedMembers[id].splitNode] = newRow->reducedMembers[id].otherIsSource ? COLOR_SINK : COLOR_SOURCE; - } else + } + else { COLOR_STATUS splitColor = newRow->nodeColors[splitNode]; @@ -8323,7 +8465,8 @@ spqr_node determineAndColorSplitNode( spqr_node other = head == splitNode ? findArcTail(dec, iterArc) : head; newRow->nodeColors[other] = splitColor; iterArc = getNextNodeArc(dec, iterArc, splitNode); - } while( iterArc != firstNodeArc ); + } + while( iterArc != firstNodeArc ); newRow->nodeColors[newRow->reducedMembers[id].splitNode] = splitColor == COLOR_SOURCE ? COLOR_SINK : COLOR_SOURCE; newRow->nodeColors[splitNode] = UNCOLORED; } @@ -8362,7 +8505,8 @@ void determineSplitTypeFirstLeaf( if( countedCutArcs == 0 ) { isReversed = arcIsReversed; - } else if( arcIsReversed != isReversed ) + } + else if( arcIsReversed != isReversed ) { good = FALSE; break; @@ -8373,7 +8517,8 @@ void determineSplitTypeFirstLeaf( { redMember->type = TYPE_NOT_NETWORK; newRow->remainsNetwork = FALSE; - } else + } + else { spqr_arc marker = markerToParent(dec, member); redMember->type = TYPE_MERGED; @@ -8496,7 +8641,8 @@ SplitOrientation getRelativeOrientationRigid( { orientation.headSplit = arcHead != splitNode; orientation.otherIsSource = newRow->nodeColors[other] != COLOR_SOURCE; - } else + } + else { orientation.headSplit = arcHead == splitNode; orientation.otherIsSource = newRow->nodeColors[other] == COLOR_SOURCE; @@ -8520,7 +8666,8 @@ SplitOrientation getRelativeOrientationParallel( if( arcIsReversedNonRigid(dec, arcToNext) == arcIsReversedNonRigid(dec, newRow->reducedMembers[reducedId].splitArc)) { orientation.headSplit = newRow->reducedMembers[reducedId].splitHead; - } else + } + else { orientation.headSplit = !newRow->reducedMembers[reducedId].splitHead; } @@ -8544,7 +8691,8 @@ SplitOrientation getRelativeOrientationSeries( if( arcIsReversedNonRigid(dec, arcToNext) == arcIsReversedNonRigid(dec, newRow->reducedMembers[reducedId].splitArc)) { orientation.headSplit = !newRow->reducedMembers[reducedId].splitHead; - } else + } + else { orientation.headSplit = newRow->reducedMembers[reducedId].splitHead; } @@ -8654,7 +8802,8 @@ void determineSplitTypeParallel( if( countedCutArcs == 0 ) { isReversed = arcIsReversed; - } else if( arcIsReversed != isReversed ) + } + else if( arcIsReversed != isReversed ) { good = FALSE; break; @@ -8996,7 +9145,8 @@ SCIP_RETCODE rigidTransformArcIntoCycle( markerCycleMember = adjacentMember; markerCycleArc = markerOfParent(dec, member); } - } else if( arcIsChildMarker(dec, arc)) + } + else if( arcIsChildMarker(dec, arc)) { adjacentMember = findArcChildMember(dec, arc); if( getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_SERIES ) @@ -9011,7 +9161,8 @@ SCIP_RETCODE rigidTransformArcIntoCycle( if( arcIsReversedNonRigid(dec, markerCycleArc)) { newRowInfo->reversed = reverseArcDirection; - } else + } + else { newRowInfo->reversed = !reverseArcDirection; } @@ -9032,16 +9183,19 @@ SCIP_RETCODE rigidTransformArcIntoCycle( if( SPQRelementIsColumn(element)) { SCIP_CALL(createColumnArc(dec, newCycle, &duplicate, SPQRelementToColumn(element), TRUE)); - } else + } + else { SCIP_CALL(createRowArc(dec, newCycle, &duplicate, SPQRelementToRow(element), TRUE)); } - } else if( isParent ) + } + else if( isParent ) { //create parent marker SCIP_CALL(createParentMarker(dec, newCycle, arcIsTree(dec, arc), adjacentMember, markerOfParent(dec, member), &duplicate, TRUE)); - } else + } + else { //create child marker SCIP_CALL(createChildMarker(dec, newCycle, adjacentMember, arcIsTree(dec, arc), &duplicate, TRUE)); @@ -9054,7 +9208,8 @@ SCIP_RETCODE rigidTransformArcIntoCycle( { SCIP_CALL(createChildMarker(dec, newCycle, member, !arcIsTree(dec, arc), &cycleMarker, FALSE)); - } else + } + else { SCIP_CALL(createParentMarker(dec, newCycle, !arcIsTree(dec, arc), member, arc, &cycleMarker, FALSE)); @@ -9070,7 +9225,8 @@ SCIP_RETCODE rigidTransformArcIntoCycle( dec->arcs[arc].element = arcIsTree(dec, arc) ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; dec->arcs[arc].childMember = SPQR_INVALID_MEMBER; - } else + } + else { dec->arcs[arc].element = arcIsTree(dec, arc) ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; dec->arcs[arc].childMember = newCycle; @@ -9105,7 +9261,8 @@ SCIP_RETCODE transformSingleRigid( { reversed = ( newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcHead(dec, arc)) == newRow->reducedMembers[reducedMember].otherIsSource; - } else + } + else { reversed = ( newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcHead(dec, arc)) != newRow->reducedMembers[reducedMember].otherIsSource; @@ -9134,25 +9291,29 @@ SCIP_RETCODE transformSingleRigid( if( arcHead == splitNode ) { changeArcHead(dec, cutArc, arcHead, newNode); - } else + } + else { changeArcTail(dec, cutArc, findArcTail(dec, cutArc), newNode); } cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; - } while( cutArcIsValid(cutArcIdx)); + } + while( cutArcIsValid(cutArcIdx)); newRowInfo->member = member; if( newRow->reducedMembers[reducedMember].otherIsSource ) { newRowInfo->head = newNode; newRowInfo->tail = splitNode; - } else + } + else { newRowInfo->head = splitNode; newRowInfo->tail = newNode; } - newRowInfo->representative = findArcSign(dec,newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc).representative; + newRowInfo->representative = findArcSign(dec, + newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc).representative; newRowInfo->reversed = FALSE; return SCIP_OKAY; @@ -9180,7 +9341,8 @@ SCIP_RETCODE transformSingleRigid( if( otherHead == splitNode ) { changeArcHead(dec, iterArc, otherHead, newNode); - } else + } + else { changeArcTail(dec, iterArc, otherTail, newNode); } @@ -9198,13 +9360,15 @@ SCIP_RETCODE transformSingleRigid( { firstNodeArc = iterArc; } - } while( TRUE ); /*lint !e506*/ + } + while( TRUE ); /*lint !e506*/ newRow->reducedMembers[reducedMember].coloredNode = SPQR_INVALID_NODE; newRowInfo->member = member; newRowInfo->head = newNode; newRowInfo->tail = splitNode; - newRowInfo->representative = findArcSign(dec,newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc).representative; + newRowInfo->representative = findArcSign(dec, + newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc).representative; newRowInfo->reversed = FALSE; return SCIP_OKAY; @@ -9237,7 +9401,8 @@ SCIP_RETCODE splitParallelRowAddition( break; } treeArc = getNextMemberArc(dec, treeArc); - } while( treeArc != getFirstMemberArc(dec, member)); + } + while( treeArc != getFirstMemberArc(dec, member)); assert(arcIsTree(dec, treeArc)); SCIP_Bool treeReversed = arcIsReversedNonRigid(dec, treeArc); @@ -9254,7 +9419,8 @@ SCIP_RETCODE splitParallelRowAddition( { adjacentMember = findMemberParent(dec, member); adjacentArc = markerOfParent(dec, member); - } else if( arcIsChildMarker(dec, treeArc)) + } + else if( arcIsChildMarker(dec, treeArc)) { adjacentMember = findArcChildMember(dec, treeArc); adjacentArc = markerToParent(dec, adjacentMember); @@ -9270,7 +9436,8 @@ SCIP_RETCODE splitParallelRowAddition( if( arcIsReversedNonRigid(dec, treeArc) == arcIsReversedNonRigid(dec, adjacentArc)) { newRowInfo->reversed = !firstReversed; - } else + } + else { newRowInfo->reversed = firstReversed; } @@ -9296,7 +9463,8 @@ SCIP_RETCODE splitParallelRowAddition( if( parentCut ) { SCIP_CALL(createMarkerPair(dec, cutMember, member, TRUE, FALSE, TRUE)); - } else + } + else { SCIP_CALL(createMarkerPair(dec, member, cutMember, FALSE, TRUE, FALSE)); } @@ -9305,11 +9473,13 @@ SCIP_RETCODE splitParallelRowAddition( if( treeReversed ) { newRowInfo->reversed = firstReversed == arcIsReversedNonRigid(dec, treeArc); - } else + } + else { newRowInfo->reversed = firstReversed != arcIsReversedNonRigid(dec, treeArc); } - } else + } + else { spqr_member cutMember = SPQR_INVALID_MEMBER; SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember)); @@ -9334,7 +9504,8 @@ SCIP_RETCODE splitParallelRowAddition( { SCIP_CALL(createMarkerPair(dec, newSeries, member, TRUE, FALSE, FALSE)); SCIP_CALL(createMarkerPair(dec, cutMember, newSeries, TRUE, FALSE, TRUE)); - } else + } + else { SCIP_CALL(createMarkerPair(dec, member, newSeries, FALSE, FALSE, FALSE)); SCIP_CALL(createMarkerPair(dec, newSeries, cutMember, FALSE, TRUE, FALSE)); @@ -9358,7 +9529,8 @@ SCIP_RETCODE splitParallelRowAddition( if( arc == markerToParent(dec, member)) { adjacentMember = findMemberParent(dec, member); - } else if( arcIsChildMarker(dec, arc)) + } + else if( arcIsChildMarker(dec, arc)) { adjacentMember = findArcChildMember(dec, arc); } @@ -9386,7 +9558,8 @@ SCIP_RETCODE splitParallelRowAddition( if( parentCut ) { SCIP_CALL(createMarkerPair(dec, newSeries, member, TRUE, TRUE, FALSE)); - } else + } + else { SCIP_CALL(createMarkerPair(dec, member, newSeries, FALSE, FALSE, TRUE)); } @@ -9466,7 +9639,8 @@ SCIP_RETCODE splitSeriesMergingRowAddition( break; } arc = getNextMemberArc(dec, arc); - } while( arc != firstArc ); + } + while( arc != firstArc ); *mergingMember = member; return SCIP_OKAY; } @@ -9516,7 +9690,8 @@ SCIP_RETCODE splitSeriesMergingRowAddition( { SCIP_CALL(createMarkerPairWithReferences(dec, mergingSeries, member, coTreeToMergingMember, TRUE, FALSE, representativeEdge, &ignoreArc)); - } else + } + else { SCIP_CALL( createMarkerPairWithReferences(dec, member, mergingSeries, !coTreeToMergingMember, FALSE, TRUE, &ignoreArc, @@ -9583,14 +9758,16 @@ SCIP_RETCODE splitParallelMerging( { SCIP_CALL( createMarkerPairWithReferences(dec, cutMember, member, TRUE, FALSE, FALSE, &ignoreArc, cutRepresentative)); - } else + } + else { SCIP_CALL( createMarkerPairWithReferences(dec, member, cutMember, FALSE, FALSE, FALSE, cutRepresentative, &ignoreArc)); } *pMergeMember = member; - } else if( createCutParallel ) + } + else if( createCutParallel ) { assert(!keepOriginalParallel); @@ -9615,7 +9792,8 @@ SCIP_RETCODE splitParallelMerging( { SCIP_CALL( createMarkerPairWithReferences(dec, cutMember, member, TRUE, FALSE, FALSE, &ignoreArc, cutRepresentative)); - } else + } + else { SCIP_CALL( createMarkerPairWithReferences(dec, member, cutMember, FALSE, FALSE, FALSE, cutRepresentative, &ignoreArc)); @@ -9672,13 +9850,15 @@ SCIP_RETCODE splitParallelMerging( { SCIP_CALL(createMarkerPairWithReferences(dec, mergingMember, member, !treeToMergingMember, FALSE, FALSE, &ignoreArgument, &noCutRepresentative)); - } else + } + else { SCIP_CALL(createMarkerPairWithReferences(dec, member, mergingMember, treeToMergingMember, FALSE, FALSE, &noCutRepresentative, &ignoreArgument)); } *pMergeMember = mergingMember; - } else if( keepOriginalParallel ) + } + else if( keepOriginalParallel ) { assert(!createCutParallel); if( cutArcIsValid(newRow->reducedMembers[reducedMember].firstCutArc)) @@ -9687,7 +9867,8 @@ SCIP_RETCODE splitParallelMerging( } *pMergeMember = member; - } else + } + else { assert(!keepOriginalParallel && !createCutParallel); @@ -9746,7 +9927,8 @@ SCIP_RETCODE splitParallelMerging( { SCIP_CALL(createMarkerPairWithReferences(dec, mergingMember, member, !treeToMergingMember, FALSE, FALSE, &ignoreArgument, &noCutRepresentative)); - } else + } + else { SCIP_CALL(createMarkerPairWithReferences(dec, member, mergingMember, treeToMergingMember, FALSE, FALSE, &noCutRepresentative, &ignoreArgument)); @@ -9801,16 +9983,19 @@ SCIP_RETCODE splitFirstLeaf( if( arcIsReversedNonRigid(dec, arc)) { setArcHeadAndTail(dec, arc, secondNode, firstNode); - } else + } + else { setArcHeadAndTail(dec, arc, firstNode, secondNode); } - } else + } + else { if(( arcIsReversedNonRigid(dec, arc) == splitArcReversed ) == splitHead ) { setArcHeadAndTail(dec, arc, thirdNode, otherNode); - } else + } + else { setArcHeadAndTail(dec, arc, otherNode, thirdNode); } @@ -9819,12 +10004,14 @@ SCIP_RETCODE splitFirstLeaf( if( arc == splitArc ) { arcSetRepresentative(dec, arc, SPQR_INVALID_ARC); - } else + } + else { arcSetRepresentative(dec, arc, splitArc); } arc = getNextMemberArc(dec, arc); - } while( arc != first_arc ); + } + while( arc != first_arc ); updateMemberType(dec, mergeMember, SPQR_MEMBERTYPE_RIGID); @@ -9833,7 +10020,8 @@ SCIP_RETCODE splitFirstLeaf( { newRowInfo->head = thirdNode; newRowInfo->tail = splitNode; - } else + } + else { newRowInfo->head = splitNode; newRowInfo->tail = thirdNode; @@ -9868,7 +10056,8 @@ SCIP_RETCODE splitFirstLeaf( if( otherHead == splitNode ) { changeArcHead(dec, iterArc, otherHead, newNode); - } else + } + else { changeArcTail(dec, iterArc, otherTail, newNode); } @@ -9885,7 +10074,8 @@ SCIP_RETCODE splitFirstLeaf( { firstNodeArc = iterArc; } - } while( TRUE ); /*lint !e506*/ + } + while( TRUE ); /*lint !e506*/ newRowInfo->head = newNode; newRowInfo->tail = splitNode; newRowInfo->member = member; @@ -10014,26 +10204,31 @@ SCIP_RETCODE splitAndMergeSeries( if( splitHead ) { setArcHeadAndTail(dec, splitArc, b, a); - } else + } + else { setArcHeadAndTail(dec, splitArc, a, b); } - } else if( arc == nonVirtualArc ) + } + else if( arc == nonVirtualArc ) { if(( arcIsReversedNonRigid(dec, arc) == splitReversed ) == splitHead ) { setArcHeadAndTail(dec, arc, a, d); - } else + } + else { setArcHeadAndTail(dec, arc, d, a); } - } else + } + else { spqr_node otherNode = cutArcIsValid(newRow->reducedMembers[smallMember].firstCutArc) ? c : b; if(( arcIsReversedNonRigid(dec, arc) == splitReversed ) == splitHead ) { setArcHeadAndTail(dec, arc, d, otherNode); - } else + } + else { setArcHeadAndTail(dec, arc, otherNode, d); } @@ -10041,7 +10236,8 @@ SCIP_RETCODE splitAndMergeSeries( arcSetReversed(dec, arc, FALSE); arcSetRepresentative(dec, arc, splitArc); arc = getNextMemberArc(dec, arc); - } while( arc != firstArc ); + } + while( arc != firstArc ); arcSetRepresentative(dec, splitArc, SPQR_INVALID_ARC); } @@ -10068,7 +10264,8 @@ SCIP_RETCODE splitAndMergeSeries( &arcNodeOne, &arcNodeTwo, &thirdNode)); - } else + } + else { SCIP_CALL(mergeSplitMemberIntoParent(dec, newRow, otherMember, mergingMember, splitArc, otherMarker, TRUE, c, otherNode, &mergedMember, @@ -10085,15 +10282,18 @@ SCIP_RETCODE splitAndMergeSeries( { newRowInfo->head = thirdNode; newRowInfo->tail = arcNodeOne; - } else if( !splitIsReferenceHead && splitIsNewRowHead ) + } + else if( !splitIsReferenceHead && splitIsNewRowHead ) { newRowInfo->head = arcNodeOne; newRowInfo->tail = thirdNode; - } else if( splitIsReferenceHead && !splitIsNewRowHead ) + } + else if( splitIsReferenceHead && !splitIsNewRowHead ) { newRowInfo->head = thirdNode; newRowInfo->tail = arcNodeTwo; - } else if( splitIsReferenceHead && splitIsNewRowHead ) + } + else if( splitIsReferenceHead && splitIsNewRowHead ) { newRowInfo->head = arcNodeTwo; newRowInfo->tail = thirdNode; @@ -10146,16 +10346,19 @@ SCIP_RETCODE splitAndMergeParallel( if( arcIsReversedNonRigid(dec, arc)) { setArcHeadAndTail(dec, arc, secondNode, firstNode); - } else + } + else { setArcHeadAndTail(dec, arc, firstNode, secondNode); } - } else + } + else { if(( arcIsReversedNonRigid(dec, arc) == splitArcReversed ) == splitHead ) { setArcHeadAndTail(dec, arc, thirdNode, otherNode); - } else + } + else { setArcHeadAndTail(dec, arc, otherNode, thirdNode); } @@ -10163,7 +10366,8 @@ SCIP_RETCODE splitAndMergeParallel( arcSetReversed(dec, arc, FALSE); arcSetRepresentative(dec, arc, splitArc); arc = getNextMemberArc(dec, arc); - } while( arc != first_arc ); + } + while( arc != first_arc ); arcSetRepresentative(dec, splitArc, SPQR_INVALID_ARC); spqr_member otherMember = newRowInfo->member; @@ -10191,7 +10395,8 @@ SCIP_RETCODE splitAndMergeParallel( &arcNodeOne, &arcNodeTwo, &mergeNodeThree)); - } else + } + else { SCIP_CALL(mergeSplitMemberIntoParent(dec, newRow, otherMember, mergeMember, splitArc, otherMarker, TRUE, thirdNode, largeOtherNode, &mergedMember, @@ -10209,15 +10414,18 @@ SCIP_RETCODE splitAndMergeParallel( { newRowInfo->head = mergeNodeThree; newRowInfo->tail = arcNodeOne; - } else if( !splitIsReferenceHead && splitIsNewRowHead ) + } + else if( !splitIsReferenceHead && splitIsNewRowHead ) { newRowInfo->head = arcNodeOne; newRowInfo->tail = mergeNodeThree; - } else if( splitIsReferenceHead && !splitIsNewRowHead ) + } + else if( splitIsReferenceHead && !splitIsNewRowHead ) { newRowInfo->head = mergeNodeThree; newRowInfo->tail = arcNodeTwo; - } else if( splitIsReferenceHead && splitIsNewRowHead ) + } + else if( splitIsReferenceHead && splitIsNewRowHead ) { newRowInfo->head = arcNodeTwo; newRowInfo->tail = mergeNodeThree; @@ -10273,7 +10481,8 @@ SCIP_RETCODE splitAndMergeRigid( if( otherHead == splitNode ) { changeArcHead(dec, iterArc, otherHead, newNode); - } else + } + else { changeArcTail(dec, iterArc, otherTail, newNode); } @@ -10294,13 +10503,14 @@ SCIP_RETCODE splitAndMergeRigid( { firstNodeArc = iterArc; } - } while( TRUE ); /*lint !e506*/ + } + while( TRUE ); /*lint !e506*/ } spqr_arc representative = findArcSign(dec, smallMarker).representative; newRowInfo->representative = mergeArcSigns(dec, newRowInfo->representative, representative, - newRow->reducedMembers[smallMember].willBeReversed); + newRow->reducedMembers[smallMember].willBeReversed); spqr_node largeMarkerHead = findArcHead(dec, largeMarker); spqr_node largeMarkerTail = findArcTail(dec, largeMarker); @@ -10311,7 +10521,7 @@ SCIP_RETCODE splitAndMergeRigid( largeMarkerTail = temp; } assert(newRowInfo->head == largeMarkerHead || newRowInfo->head == largeMarkerTail || - newRowInfo->tail == largeMarkerHead || newRowInfo->tail == largeMarkerTail); + newRowInfo->tail == largeMarkerHead || newRowInfo->tail == largeMarkerTail); spqr_node largeOtherNode = ( newRowInfo->head == largeMarkerHead || newRowInfo->head == largeMarkerTail ) ? newRowInfo->tail : newRowInfo->head; @@ -10327,7 +10537,8 @@ SCIP_RETCODE splitAndMergeRigid( &arcNodeOne, &arcNodeTwo, &mergeNodeThree)); - } else + } + else { SCIP_CALL( mergeSplitMemberIntoParent(dec, newRow, largeMemberMember, smallMemberMember, smallMarker, largeMarker, TRUE, @@ -10340,25 +10551,28 @@ SCIP_RETCODE splitAndMergeRigid( SCIP_Bool otherIsHead = largeOtherNode == newRowInfo->head; SCIP_Bool adjacentToMarkerHead = ( newRowInfo->tail == largeMarkerHead || - newRowInfo->head == largeMarkerHead ); + newRowInfo->head == largeMarkerHead ); if( adjacentToMarkerHead ) { if( otherIsHead ) { newRowInfo->head = mergeNodeThree; newRowInfo->tail = arcNodeTwo; - } else + } + else { newRowInfo->head = arcNodeTwo; newRowInfo->tail = mergeNodeThree; } - } else + } + else { if( otherIsHead ) { newRowInfo->head = mergeNodeThree; newRowInfo->tail = arcNodeOne; - } else + } + else { newRowInfo->head = arcNodeOne; newRowInfo->tail = mergeNodeThree; @@ -10521,8 +10735,6 @@ SCIP_RETCODE transformComponentRowAddition( if( getNumMemberArcs(dec, member) == 2 ) { changeLoopToSeries(dec, member); - } else - { } } break; @@ -10734,7 +10946,8 @@ SCIP_RETCODE SCIPnetrowaddAdd( spqr_member newMember = SPQR_INVALID_MEMBER; SCIP_CALL(createStandaloneParallel(dec, rowadd->newColumnArcs, rowadd->newColumnReversed, rowadd->numColumnArcs, rowadd->newRowIndex, &newMember)); - } else if( rowadd->numReducedComponents == 1 ) + } + else if( rowadd->numReducedComponents == 1 ) { NewRowInformation information = emptyNewRowInformation(); SCIP_CALL(transformComponentRowAddition(dec, rowadd, &rowadd->reducedComponents[0], &information)); @@ -10751,7 +10964,8 @@ SCIP_RETCODE SCIPnetrowaddAdd( arcSetRepresentative(dec, rowArc, information.representative); arcSetReversed(dec, rowArc, information.reversed != arcIsReversedNonRigid(dec, information.representative)); } - } else + } + else { spqr_member new_row_parallel = SPQR_INVALID_MEMBER; SCIP_CALL(createConnectedParallel(dec, rowadd->newColumnArcs, rowadd->newColumnReversed, rowadd->numColumnArcs, @@ -10779,7 +10993,8 @@ SCIP_RETCODE SCIPnetrowaddAdd( changeLoopToSeries(dec, information.member); } } - } else + } + else { #ifndef NDEBUG int numDecComponentsBefore = numConnectedComponents(dec); @@ -10800,7 +11015,8 @@ SCIP_RETCODE SCIPnetrowaddAdd( moveArcToNewMember(dec, arc, information.member, new_row_parallel); arcSetReversed(dec, arc, rowadd->isArcCutReversed[arc]); dec->members[information.member].type = SPQR_MEMBERTYPE_UNASSIGNED; - } else + } + else { reorderComponent(dec, information.member);//Make sure the new component is the root of the local decomposition tree From cb28027ea25af4c5f82a3c078279915eff92ba9a Mon Sep 17 00:00:00 2001 From: Stefan Vigerske Date: Mon, 29 Jul 2024 09:24:25 +0200 Subject: [PATCH 23/63] change spaces; minor fixes for doxygen, spelling, etc - use C-style comments in header at least --- src/scip/network.c | 46 ++++++++++++++++++++++++---------------------- src/scip/network.h | 17 +++++++++-------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 2f8709a2ee..ff0b5c0629 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -24,12 +24,13 @@ /**@file network.c * @ingroup OTHER_CFILES - * @brief Methods for detecting network (sub)matrices + * @brief Methods for detecting network (sub)matrices * @author Rolf van der Hulst + * * Detecting if a matrix is a network matrix can be quite complex. Below is an introductory text, which may help with * navigating the functions and datastructures in this file by giving a general overview. * More details can be found in; - * R.P. van der Hulst and M.Walter "A row-wise algorithm for graph realization" + * R.P. van der Hulst and M. Walter "A row-wise algorithm for graph realization" * and * R.E. Bixby and D.K. Wagner "An almost linear-time algorithm for graph realization" * for the column-wise algorithm. @@ -51,36 +52,37 @@ * Each block has a 2-connected realization graph. * * If a graph \f$G\f$ realizing the network matrix has a 2-separation \f$(E_1,E_2)\f$ at vertices u and v, then we can - * obtain an another graph representing the network matrix, by inverting the direction of all edges in \f$E_2\f$ and - * replacing u by v and vice-versa, to obtain a different graph that realizes the network matrix. One can also imagine + * obtain another graph representing the network matrix, by inverting the direction of all edges in \f$E_2\f$ and + * replacing u by v and vice-versa. One can also imagine * adding a virtual edge \f$e'=\{u,v\}\f$ to both E_1 and E_2. In the trivial realization, we simply map the head of * \f$e'\f$ in \f$E_1\f$ to the head of \f$e'\f$ in \f$E_2\f$ and remove \f$e'\f$ from both graphs. In the second * realization we do the same thing, but first invert all the edges in \f$E_2\f$, including \f$e'\f$. * An SPQR tree \f$\mathcal{T}=(\mathcal{V},\mathcal{E})\f$ is a tree data structure that represents the structure of * all 2-separations in a 2-connected graph. Each member \f$\nu\in\mathcal{V}\f$ has an associated skeleton graph that - * has one of four different types; - * (S) - The member's skeleton graph is a cycle with at least 3 edges (also referred to as series or polygon) - * (P) - The member's skeleton graph consists of at least 3 parallel edges and 2 nodes (also referred to as bond) - * (Q) - The member's skeleton graph consists of at most 2 edges connecting two nodes (also referred to as loop) - * (R) - The member's skeleton graph is 3-connected and consists of at least 4 edges. + * has one of four different types: + * - (S) The member's skeleton graph is a cycle with at least 3 edges (also referred to as series or polygon) + * - (P) The member's skeleton graph consists of at least 3 parallel edges and 2 nodes (also referred to as bond) + * - (Q) The member's skeleton graph consists of at most 2 edges connecting two nodes (also referred to as loop) + * - (R) The member's skeleton graph is 3-connected and consists of at least 4 edges. * * An SPQR tree is considered minimal if it has no P-P or S-S connections. Each connected matrix has a unique minimal * SPQR tree. Each edge \f$\{\nu,\mu\}\in\mathcal{E}\f$ defines a 2-separation of the underlying graph. In particular, * each edge has one virtual edge in the member graph that it connects to the other member graph in the edge. * - * We can obtain a realization of the graph underlying the network matrix by doing the following operations; + * We can obtain a realization of the graph underlying the network matrix by doing the following operations: * 1. Permute the edges of each (S)-member arbitrarily - * 2. For each edge in $\mathcal{E}$, pick one of the two orientations of the virtual edges and merge the adjacent + * 2. For each edge in \f$\mathcal{E}\f$, pick one of the two orientations of the virtual edges and merge the adjacent * member graphs accordingly. + * * In this way, all the graphs given by the network matrix are represented. In order to efficiently perform the merge * of two member graphs, the member and node labels are given by union-find datastructures. Additionally, we also - * introduce a signed-union find datastructure on the arcs of the graphs, so that we can efficiently invert the arcs + * introduce a signed-union-find datastructure on the arcs of the graphs, so that we can efficiently invert the arcs * of one side of a 2-separation. * The 1-separations can be handled by storing an SPQR forest, with a (minimal) SPQR tree for every connected block * of the network matrix. * * For adding a column to the network matrix, one can show that one can add a column only if the nonzeros of the column - * Form a path with the correct signs in some graph represented by the network matrix. We solve the problem for each + * form a path with the correct signs in some graph represented by the network matrix. We solve the problem for each * graph represented by the network matrix simultaneously by decomposing over the SPQR tree. First, we compute the path * in each member. Then, we attempt to combine the paths by orienting the 2-separations so that the different member * paths form a path in the represented graph. @@ -93,8 +95,8 @@ * Finally, we can easily join the paths of multiple SPQR trees using a series node to obtain the final path. * * The ideas for the row-wise algorithm have many parallels with the column-wise algorithm. One can add a row to a - * network matrix if and only if a node is 'splittable' with respect to a certain auxilliary graph formed by the nonzero - * columns indices of the row, for a graph represented by the network matrix. In particular, this auxilliary graph must + * network matrix if and only if a node is 'splittable' with respect to a certain auxiliary graph formed by the nonzero + * columns indices of the row, for a graph represented by the network matrix. In particular, this auxiliary graph must * be a directed bipartite graph; then, the arcs incident to the given node can be reassigned to two new nodes, so that * the paths of the columns corresponding to the nonzeros of the row can be elongated to contain the new row, which is * placed between the two new nodes. @@ -108,25 +110,25 @@ * * Implementation notes: * 1. Quite a few algorithms used for network matrix detection are recursive in nature. However, recursive calls can - * cause stack overflows, particularly with large graphs. Quite frequently in the code, we need to allocate the - * call-data of these algorithms on the heap, instead, and use while loops to simulate the recursion. + * cause stack overflows, particularly with large graphs. Quite frequently in the code, we need to allocate the + * call-data of these algorithms on the heap, instead, and use while loops to simulate the recursion. * * 2. In order to make the code fast in practice, a lot of emphasis is put on reusing allocated memory and avoiding - * allocations. In particular for the column-wise algorithm, even allocating and zeroing an array of size m+n for an - * m x n matrix can significantly slow down the code! + * allocations. In particular for the column-wise algorithm, even allocating and zeroing an array of size m+n for an + * m x n matrix can significantly slow down the code! * - * 3. The graphs of the S,P and Q members do not need to be stored explicitly, as they always have the same structure. + * 3. The graphs of the S, P and Q members do not need to be stored explicitly, as they always have the same structure. * This also makes it easier to permute edges of S nodes on the fly. * * TODO: fix tracking connectivity more cleanly, should not be left up to the algorithms ideally */ +#include #include "scip/network.h" #include "scip/scip.h" -#include #include "blockmemshell/memory.h" -///Types which define matrix sizes +/* types which define matrix sizes */ typedef int spqr_matrix_size; typedef spqr_matrix_size spqr_row; typedef spqr_matrix_size spqr_col; diff --git a/src/scip/network.h b/src/scip/network.h index 89755169f1..c81fe9c3a3 100644 --- a/src/scip/network.h +++ b/src/scip/network.h @@ -51,16 +51,17 @@ * it is up to the user to ensure this when interleaving column and row addition steps. * * More details can be found in: - * - R.P. van der Hulst and M.Walter "A row-wise algorithm for graph realization" + * - R.P. van der Hulst and M. Walter "A row-wise algorithm for graph realization" * - R.E. Bixby and D.K. Wagner "An almost linear-time algorithm for graph realization" + * * Note that although these publications contain the methods for undirected graphs (and binary matrices), * their ideas are relatively easily extended to directed graphs and ternary matrices. * Implementation details are described in further detail in network.c */ -/** TODO: add method that realizes a SCIP digraph from the decomposition */ -/** TODO: add method that *cleanly* removes complete components of the SPQR tree */ -/** TODO: add node-arc incidence matrix methods */ +/** @TODO add method that realizes a SCIP digraph from the decomposition */ +/** @TODO add method that *cleanly* removes complete components of the SPQR tree */ +/** @TODO add node-arc incidence matrix methods */ /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/ #ifndef __SCIP_NETWORK_H__ @@ -69,6 +70,7 @@ #include "scip/def.h" #include "scip/type_retcode.h" #include "scip/type_scip.h" + #ifdef cplusplus extern "C" { #endif @@ -94,7 +96,7 @@ void SCIPnetmatdecFree( SCIP_NETMATDEC** pdec /**< pointer to the network matrix decomposition to free */ ); -/** Tries to add a given column to the network network matrix +/** tries to add a given column to the network network matrix * * Note that the first call to this method for a given decomposition may be a bit slower, * due to memory initialization. @@ -109,7 +111,7 @@ SCIP_RETCODE SCIPnetmatdecTryAddCol( SCIP_Bool* success /**< Buffer to store whether the column was added */ ); -/** Tries to add a given column to the network network matrix +/** tries to add a given column to the network network matrix * * Note that the first call to this method for a given decomposition may be a bit slower, * due to memory initialization. @@ -184,9 +186,8 @@ SCIP_Bool SCIPnetmatdecVerifyCycle( * equal or greater than the number of rows in the decomposition. */ ); - #ifdef cplusplus } #endif -#endif //__SCIP_NETWORK_H__ +#endif /*__SCIP_NETWORK_H__ */ From ed95f876d776f57780557d2d6aba2a818159bdab Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Mon, 29 Jul 2024 16:53:44 +0200 Subject: [PATCH 24/63] Fix unfinished documentation sentence --- src/scip/network.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/scip/network.c b/src/scip/network.c index ff0b5c0629..1b4dbbbca8 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -103,7 +103,8 @@ * Similarly to the column-case, splittability of each graph represented by the network matrix can be computed at once * by computing the splittability (and the corresponding bipartition) of every member graph. * Similarly to the column algorithm, we can propagate; If a member is a leaf of the SPQR tree and both nodes of the - * 2-separation connecting it to the rest of graph are splittable, then we can and update our datastructures to. + * 2-separation connecting it to the rest of graph are splittable, then we can remove the leaf from the reduced + * SPQR tree and mark the virtual edge connecting to the subtree. * Finally, we are left with some minimal subtree with splittable vertices for each member graph. If we can merge all * splittable vertices of the member graphs in the subtree into a single splittable vertex, then we perform this merge, * and split this vertex. This yields us a new, larger node of type R (rigid). From b06516436728d52eea0a230c634a11a9a930ac9e Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Mon, 29 Jul 2024 18:47:01 +0200 Subject: [PATCH 25/63] Improve documentation + additional error handling in network.h --- src/scip/network.c | 9 +++++++++ src/scip/network.h | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 1b4dbbbca8..7203412c13 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -4999,6 +4999,10 @@ SCIP_RETCODE SCIPnetcoladdCheck( assert(coladd); assert(nnonzs == 0 || ( nonzrows && nonzvals )); + /* A column can only be added once */ + if( netMatDecDataContainsColumn(dec,column)){ + return SCIP_INVALIDDATA; + } coladd->remainsNetwork = TRUE; cleanupPreviousIteration(dec, coladd); //assert that previous iteration was cleaned up @@ -10903,6 +10907,11 @@ SCIP_RETCODE SCIPnetrowaddCheck( assert(rowadd); assert(nnonzs == 0 || nonzcols); + /* A row can only be added once */ + if( netMatDecDataContainsRow(dec,row)){ + return SCIP_INVALIDDATA; + } + rowadd->remainsNetwork = TRUE; cleanUpPreviousIteration(dec, rowadd); diff --git a/src/scip/network.h b/src/scip/network.h index c81fe9c3a3..185c81b0b5 100644 --- a/src/scip/network.h +++ b/src/scip/network.h @@ -23,13 +23,17 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /**@file network.h - * @ingroup OTHER_CFILES + * @ingroup PUBLICCOREAPI * @brief Methods for detecting network matrices * @author Rolf van der Hulst * * This file contains algorithms for incrementally growing (augmenting) network matrices, * which are a large subclass of totally unimodular matrices. * + * @addtogroup NetworkMatrix + * + * @{ + * * A matrix \f$M \in \{-1,0,1\}^{m\times n} \f$ is a network matrix if there exists a directed graph \f$G = (V,A)\f$ * with \f$|A| = m+n\f$ arcs and a spanning forest \f$T\f$ with \f$|T| = m\f$ such that \f$M\f$'s rows index \f$T\f$ and * \f$M\f$'s columns index \f$A\setminus T\f$, @@ -59,9 +63,9 @@ * Implementation details are described in further detail in network.c */ -/** @TODO add method that realizes a SCIP digraph from the decomposition */ -/** @TODO add method that *cleanly* removes complete components of the SPQR tree */ -/** @TODO add node-arc incidence matrix methods */ +/* TODO add method that realizes a SCIP digraph from the decomposition */ +/* TODO add method that *cleanly* removes complete components of the SPQR tree */ +/* TODO add node-arc incidence matrix methods */ /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/ #ifndef __SCIP_NETWORK_H__ @@ -96,7 +100,11 @@ void SCIPnetmatdecFree( SCIP_NETMATDEC** pdec /**< pointer to the network matrix decomposition to free */ ); -/** tries to add a given column to the network network matrix +/** tries to add a given column to the network matrix. Any nonzero row indices in the column that are not yet in the + * network matrix decomposition are added, too. + * + * @note Each column and row may be added only once. Trying to add a column that is already in the decomposition will + * result in an error. * * Note that the first call to this method for a given decomposition may be a bit slower, * due to memory initialization. @@ -111,12 +119,18 @@ SCIP_RETCODE SCIPnetmatdecTryAddCol( SCIP_Bool* success /**< Buffer to store whether the column was added */ ); -/** tries to add a given column to the network network matrix +/** tries to add a given row to the network matrix. Any nonzero column indices in the row that are not yet in the + * network matrix decomposition are added, too. + * + * @note Each column and row may be added only once. Trying to add a row that is already in the decomposition will + * result in an error. * * Note that the first call to this method for a given decomposition may be a bit slower, * due to memory initialization. + * * If the user is only interested in determining if a certain (sub)matrix is network or not, using * SCIPnetmatdecTryAddCol() will generally be faster, unless the (sub)matrix has many more columns than rows. + * */ SCIP_EXPORT SCIP_RETCODE SCIPnetmatdecTryAddRow( @@ -144,10 +158,10 @@ SCIP_Bool SCIPnetmatdecContainsColumn( /** removes a connected component of the matrix from the network decomposition * - * Note that this method is 'stupid', and does not delete the associated graph data structure. + * @note This method is 'stupid', and does not delete the associated graph data structure in the decomposition. * Moreover, it does not explicitly check if the rows/columns that the user provides are a connected - * component of the submatrix given by the decomposition. Use with care! - * If this is not the case, then calling this function is considered a bug. + * component of the submatrix given by the decomposition. If this is not the case, this will lead to buggy behavior. + * Use with care! */ SCIP_EXPORT void SCIPnetmatdecRemoveComponent( @@ -160,8 +174,9 @@ void SCIPnetmatdecRemoveComponent( /** checks if the network matrix decomposition is minimal. * - * It is minimal if it does not contain adjacent parallel or series skeletons. - * The network matrix decomposition should always be minimal. This method should only be used in tests or asserts. + * A network matrix decomposition is minimal if it does not contain adjacent parallel or series skeletons. + * The network matrix decomposition we compute should always be minimal. + * This method should only be used in tests or asserts. */ SCIP_EXPORT SCIP_Bool SCIPnetmatdecIsMinimal( @@ -186,6 +201,8 @@ SCIP_Bool SCIPnetmatdecVerifyCycle( * equal or greater than the number of rows in the decomposition. */ ); +/**@} */ + #ifdef cplusplus } #endif From a3af66a71b43021996a1f5870e2378731776d4dc Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 30 Jul 2024 06:50:57 +0200 Subject: [PATCH 26/63] Add network matrices to documentation --- doc/xternal.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/xternal.c b/doc/xternal.c index 64d5cb3dc7..8084a73992 100644 --- a/doc/xternal.c +++ b/doc/xternal.c @@ -9054,6 +9054,11 @@ * @brief methods for creating and accessing user decompositions */ +/**@defgroup NetworkMatrix Network Matrix + * @ingroup DataStructures + * @brief Methods for detecting network matrices and converting them to the underlying graphs. + */ + /**@defgroup SymGraph Symmetry Detection Graph * @ingroup DataStructures * @brief methods for creating and manipulating symmetry detection graphs From 3cd7361204751733957705f195fdb4394ca74c0f Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 30 Jul 2024 12:00:08 +0200 Subject: [PATCH 27/63] Refactor network matrix methods to only use block memory pointer --- src/scip/network.c | 308 ++++++++++++++++++------------------ src/scip/network.h | 7 +- tests/src/network/network.c | 13 +- 3 files changed, 165 insertions(+), 163 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 7203412c13..ae916d241a 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -409,7 +409,7 @@ typedef struct int memColumns; /**< The (maximal) number of columns of the matrix */ spqr_arc* columnArcs; /**< Maps the columns of the matrix to arcs in the decomposition */ - SCIP* env; /**< SCIP pointer stored for later use */ + BMS_BLKMEM * env; /**< used memory allocator */ int numConnectedComponents; /** The number of disjoint SPQR trees in the SPQR forest */ } SCIP_NETMATDECDATA; @@ -1362,19 +1362,19 @@ spqr_arc getDecompositionRowArc( /**< Initialize the network matrix decomposition data structure */ static SCIP_RETCODE netMatDecDataCreate( - SCIP* scip, /**< SCIP data structure */ + BMS_BLKMEM* blkmem, /**< Block memory */ SCIP_NETMATDECDATA** pdec, /**< buffer to store pointer to created decomposition */ int nrows, /**< The maximal number of rows that the decomposition can expect */ int ncols /**< The maximal number of columns that the decomposition can expect */ ) { - assert(scip); + assert(blkmem); assert(pdec); assert(!*pdec); - SCIP_CALL(SCIPallocBlockMemory(scip, pdec)); + SCIP_ALLOC(BMSallocBlockMemory(blkmem, pdec)); SCIP_NETMATDECDATA* dec = *pdec; - dec->env = scip; + dec->env = blkmem; //Initialize arc array data int initialMemArcs = 8; @@ -1382,7 +1382,7 @@ SCIP_RETCODE netMatDecDataCreate( assert(initialMemArcs > 0); dec->memArcs = initialMemArcs; dec->numArcs = 0; - SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->arcs, dec->memArcs)); + SCIP_ALLOC(BMSallocBlockMemoryArray(blkmem, &dec->arcs, dec->memArcs)); for( spqr_arc i = 0; i < dec->memArcs; ++i ) { dec->arcs[i].arcListNode.next = i + 1; @@ -1398,7 +1398,7 @@ SCIP_RETCODE netMatDecDataCreate( assert(initialMemMembers > 0); dec->memMembers = initialMemMembers; dec->numMembers = 0; - SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->members, dec->memMembers)); + SCIP_ALLOC(BMSallocBlockMemoryArray(blkmem, &dec->members, dec->memMembers)); } //Initialize node array data @@ -1407,13 +1407,13 @@ SCIP_RETCODE netMatDecDataCreate( assert(initialMemNodes > 0); dec->memNodes = initialMemNodes; dec->numNodes = 0; - SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->nodes, dec->memNodes)); + SCIP_ALLOC(BMSallocBlockMemoryArray(blkmem, &dec->nodes, dec->memNodes)); } //Initialize mappings for rows { dec->memRows = nrows; - SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->rowArcs, dec->memRows)); + SCIP_ALLOC(BMSallocBlockMemoryArray(blkmem, &dec->rowArcs, dec->memRows)); for( int i = 0; i < dec->memRows; ++i ) { dec->rowArcs[i] = SPQR_INVALID_ARC; @@ -1422,7 +1422,7 @@ SCIP_RETCODE netMatDecDataCreate( //Initialize mappings for columns { dec->memColumns = ncols; - SCIP_CALL(SCIPallocBlockMemoryArray(scip, &dec->columnArcs, dec->memColumns)); + SCIP_ALLOC(BMSallocBlockMemoryArray(blkmem, &dec->columnArcs, dec->memColumns)); for( int i = 0; i < dec->memColumns; ++i ) { dec->columnArcs[i] = SPQR_INVALID_ARC; @@ -1442,13 +1442,13 @@ void netMatDecDataFree( assert(*pdec); SCIP_NETMATDECDATA* dec = *pdec; - SCIPfreeBlockMemoryArray(dec->env, &dec->columnArcs, dec->memColumns); - SCIPfreeBlockMemoryArray(dec->env, &dec->rowArcs, dec->memRows); - SCIPfreeBlockMemoryArray(dec->env, &dec->nodes, dec->memNodes); - SCIPfreeBlockMemoryArray(dec->env, &dec->members, dec->memMembers); - SCIPfreeBlockMemoryArray(dec->env, &dec->arcs, dec->memArcs); + BMSfreeBlockMemoryArray(dec->env, &dec->columnArcs, dec->memColumns); + BMSfreeBlockMemoryArray(dec->env, &dec->rowArcs, dec->memRows); + BMSfreeBlockMemoryArray(dec->env, &dec->nodes, dec->memNodes); + BMSfreeBlockMemoryArray(dec->env, &dec->members, dec->memMembers); + BMSfreeBlockMemoryArray(dec->env, &dec->arcs, dec->memArcs); - SCIPfreeBlockMemory(dec->env, pdec); + BMSfreeBlockMemory(dec->env, pdec); } /**< Get the first arc of the linked list of arcs contained in the given member */ @@ -1542,7 +1542,7 @@ SCIP_RETCODE createArc( { //Enlarge array, no free nodes in arc list int newSize = 2 * dec->memArcs; - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &dec->arcs, dec->memArcs, newSize)); + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &dec->arcs, dec->memArcs, newSize)); for( int i = dec->memArcs + 1; i < newSize; ++i ) { dec->arcs[i].arcListNode.next = i + 1; @@ -1622,7 +1622,7 @@ SCIP_RETCODE createMember( if( dec->numMembers == dec->memMembers ) { int newSize = dec->memMembers * 2; - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &dec->members, dec->memMembers, newSize)); + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &dec->members, dec->memMembers, newSize)); dec->memMembers = newSize; } SPQRNetworkDecompositionMember* data = &dec->members[dec->numMembers]; @@ -1651,7 +1651,7 @@ SCIP_RETCODE createNode( if( dec->numNodes == dec->memNodes ) { int newSize = dec->memNodes * 2; - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &dec->nodes, dec->memNodes, newSize)); + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &dec->nodes, dec->memNodes, newSize)); dec->memNodes = newSize; } *pNode = dec->numNodes; @@ -2153,8 +2153,7 @@ int decompositionGetFundamentalCycleRows( int num_rows = 0; FindCycleCall* callStack; - SCIP_RETCODE result = SCIPallocBlockMemoryArray(dec->env, &callStack, dec->memRows); - if( result != SCIP_OKAY ) + if( BMSallocBlockMemoryArray(dec->env, &callStack, dec->memRows) == NULL ) { return -1; } @@ -2163,8 +2162,7 @@ int decompositionGetFundamentalCycleRows( callStack[0].reversed = FALSE; SCIP_Bool* nodeVisited; - result = SCIPallocBlockMemoryArray(dec->env, &nodeVisited, dec->numNodes); - if( result != SCIP_OKAY ) + if( BMSallocBlockMemoryArray(dec->env, &nodeVisited, dec->numNodes) == NULL ) { return -1; } @@ -2179,8 +2177,7 @@ int decompositionGetFundamentalCycleRows( spqr_arc nodeArc; } DFSCallData; DFSCallData* pathSearchCallStack; - result = SCIPallocBlockMemoryArray(dec->env, &pathSearchCallStack, dec->numNodes); - if( result != SCIP_OKAY ) + if( BMSallocBlockMemoryArray(dec->env, &pathSearchCallStack, dec->numNodes) == NULL ) { return -1; } @@ -2315,9 +2312,9 @@ int decompositionGetFundamentalCycleRows( assert(FALSE); } } - SCIPfreeBlockMemoryArray(dec->env, &pathSearchCallStack, dec->numNodes); - SCIPfreeBlockMemoryArray(dec->env, &nodeVisited, dec->numNodes); - SCIPfreeBlockMemoryArray(dec->env, &callStack, dec->memRows); + BMSfreeBlockMemoryArray(dec->env, &pathSearchCallStack, dec->numNodes); + BMSfreeBlockMemoryArray(dec->env, &nodeVisited, dec->numNodes); + BMSfreeBlockMemoryArray(dec->env, &callStack, dec->memRows); return num_rows; } @@ -2325,7 +2322,7 @@ int decompositionGetFundamentalCycleRows( * network matrix decomposition */ static SCIP_Bool netMatDecDataVerifyCycle( - SCIP* scip, /**< The SCIP datastructure */ + BMS_BUFMEM* bufmem, /**< Buffer memory */ const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ int column, /**< The column to check */ const int* nonzrowidx, /**< Array with the nonzero row indices of the column */ @@ -2350,15 +2347,14 @@ SCIP_Bool netMatDecDataVerifyCycle( spqr_row * pathRow; int * pathRowReversed; - SCIP_RETCODE code = SCIPallocBufferArray(scip, &pathRow, num_rows); - if( code != SCIP_OKAY ) + if( BMSallocBufferMemoryArray(bufmem, &pathRow, num_rows) == NULL ) { return FALSE; } - code = SCIPallocBufferArray(scip, &pathRowReversed, num_rows); - if( code != SCIP_OKAY ) + + if( BMSallocBufferMemoryArray(bufmem, &pathRowReversed, num_rows) == NULL ) { - SCIPfreeBufferArray(scip,&pathRow); + BMSfreeBufferMemoryArray(bufmem,&pathRow); return FALSE; } for( int i = 0; i < num_rows; ++i ) @@ -2370,19 +2366,18 @@ SCIP_Bool netMatDecDataVerifyCycle( spqr_row * secondPathRow; int * secondPathRowReversed; - code = SCIPallocBufferArray(scip, &secondPathRow, num_rows); - if( code != SCIP_OKAY ) + if( BMSallocBufferMemoryArray(bufmem, &secondPathRow, num_rows) == NULL ) { - SCIPfreeBufferArray(scip,&pathRow); - SCIPfreeBufferArray(scip,&pathRowReversed); + BMSfreeBufferMemoryArray(bufmem,&pathRow); + BMSfreeBufferMemoryArray(bufmem,&pathRowReversed); return FALSE; } - code = SCIPallocBufferArray(scip, &secondPathRowReversed, num_rows); - if( code != SCIP_OKAY ) + + if( BMSallocBufferMemoryArray(bufmem, &secondPathRowReversed, num_rows) == NULL ) { - SCIPfreeBufferArray(scip,&pathRow); - SCIPfreeBufferArray(scip,&pathRowReversed); - SCIPfreeBufferArray(scip,&secondPathRow); + BMSfreeBufferMemoryArray(bufmem,&pathRow); + BMSfreeBufferMemoryArray(bufmem,&pathRowReversed); + BMSfreeBufferMemoryArray(bufmem,&secondPathRow); return FALSE; } for( int i = 0; i < num_rows; ++i ) @@ -2402,10 +2397,10 @@ SCIP_Bool netMatDecDataVerifyCycle( break; } } - SCIPfreeBufferArray(scip, &secondPathRowReversed); - SCIPfreeBufferArray(scip, &secondPathRow); - SCIPfreeBufferArray(scip, &pathRowReversed); - SCIPfreeBufferArray(scip, &pathRow); + BMSfreeBufferMemoryArray(bufmem, &secondPathRowReversed); + BMSfreeBufferMemoryArray(bufmem, &secondPathRow); + BMSfreeBufferMemoryArray(bufmem, &pathRowReversed); + BMSfreeBufferMemoryArray(bufmem, &pathRow); return good; } @@ -3279,13 +3274,13 @@ void cleanupPreviousIteration( /**< Create a new network column addition datastructure */ static SCIP_RETCODE SCIPnetcoladdCreate( - SCIP* scip, /**< SCIP environment */ + BMS_BLKMEM* blkmem, /**< Block memory */ SCIP_NETCOLADD** pcoladd /**< Out-pointer for the created network column addition datastructure */ ) { - assert(scip); + assert(blkmem); - SCIP_CALL(SCIPallocBlockMemory(scip, pcoladd)); + SCIP_ALLOC(BMSallocBlockMemory(blkmem, pcoladd)); SCIP_NETCOLADD* newCol = *pcoladd; newCol->remainsNetwork = FALSE; @@ -3343,29 +3338,29 @@ SCIP_RETCODE SCIPnetcoladdCreate( /**< Free a network column addition datastructure */ static void SCIPnetcoladdFree( - SCIP* scip, /**< SCIP environment */ + BMS_BLKMEM* blkmem, /**< Block memory */ SCIP_NETCOLADD** pcoladd /**< Pointer to the network column addition datastructure to be freed */ ) { - assert(scip); + assert(blkmem); SCIP_NETCOLADD* newCol = *pcoladd; - SCIPfreeBlockMemoryArray(scip, &newCol->decompositionRowArcs, newCol->memDecompositionRowArcs); - SCIPfreeBlockMemoryArray(scip, &newCol->decompositionArcReversed, newCol->memDecompositionRowArcs); - SCIPfreeBlockMemoryArray(scip, &newCol->newRowArcs, newCol->memNewRowArcs); - SCIPfreeBlockMemoryArray(scip, &newCol->newRowArcReversed, newCol->memNewRowArcs); - SCIPfreeBlockMemoryArray(scip, &newCol->createReducedMembersCallStack, newCol->memCreateReducedMembersCallStack); - SCIPfreeBlockMemoryArray(scip, &newCol->arcInPath, newCol->memArcsInPath); - SCIPfreeBlockMemoryArray(scip, &newCol->arcInPathReversed, newCol->memArcsInPath); - SCIPfreeBlockMemoryArray(scip, &newCol->nodeInPathDegree, newCol->memNodePathDegree); - SCIPfreeBlockMemoryArray(scip, &newCol->nodeOutPathDegree, newCol->memNodePathDegree); - SCIPfreeBlockMemoryArray(scip, &newCol->pathArcs, newCol->memPathArcs); - SCIPfreeBlockMemoryArray(scip, &newCol->childrenStorage, newCol->memChildrenStorage); - SCIPfreeBlockMemoryArray(scip, &newCol->memberInformation, newCol->memMemberInformation); - SCIPfreeBlockMemoryArray(scip, &newCol->reducedComponents, newCol->memReducedComponents); - SCIPfreeBlockMemoryArray(scip, &newCol->reducedMembers, newCol->memReducedMembers); - SCIPfreeBlockMemoryArray(scip, &newCol->leafMembers, newCol->memLeafMembers); - - SCIPfreeBlockMemory(scip, pcoladd); + BMSfreeBlockMemoryArray(blkmem, &newCol->decompositionRowArcs, newCol->memDecompositionRowArcs); + BMSfreeBlockMemoryArray(blkmem, &newCol->decompositionArcReversed, newCol->memDecompositionRowArcs); + BMSfreeBlockMemoryArray(blkmem, &newCol->newRowArcs, newCol->memNewRowArcs); + BMSfreeBlockMemoryArray(blkmem, &newCol->newRowArcReversed, newCol->memNewRowArcs); + BMSfreeBlockMemoryArray(blkmem, &newCol->createReducedMembersCallStack, newCol->memCreateReducedMembersCallStack); + BMSfreeBlockMemoryArray(blkmem, &newCol->arcInPath, newCol->memArcsInPath); + BMSfreeBlockMemoryArray(blkmem, &newCol->arcInPathReversed, newCol->memArcsInPath); + BMSfreeBlockMemoryArray(blkmem, &newCol->nodeInPathDegree, newCol->memNodePathDegree); + BMSfreeBlockMemoryArray(blkmem, &newCol->nodeOutPathDegree, newCol->memNodePathDegree); + BMSfreeBlockMemoryArray(blkmem, &newCol->pathArcs, newCol->memPathArcs); + BMSfreeBlockMemoryArray(blkmem, &newCol->childrenStorage, newCol->memChildrenStorage); + BMSfreeBlockMemoryArray(blkmem, &newCol->memberInformation, newCol->memMemberInformation); + BMSfreeBlockMemoryArray(blkmem, &newCol->reducedComponents, newCol->memReducedComponents); + BMSfreeBlockMemoryArray(blkmem, &newCol->reducedMembers, newCol->memReducedMembers); + BMSfreeBlockMemoryArray(blkmem, &newCol->leafMembers, newCol->memLeafMembers); + + BMSfreeBlockMemory(blkmem, pcoladd); } /**< Adds members to the reduced member tree, by starting at the given member, jumping up to the parent, repeating this @@ -3509,15 +3504,15 @@ SCIP_RETCODE constructReducedDecomposition( if( newSize > newCol->memReducedMembers ) { int newArraySize = maxValue(2 * newCol->memReducedMembers, newSize); - SCIP_CALL( - SCIPreallocBlockMemoryArray(dec->env, &newCol->reducedMembers, newCol->memReducedMembers, newArraySize)); + SCIP_ALLOC( + BMSreallocBlockMemoryArray(dec->env, &newCol->reducedMembers, newCol->memReducedMembers, newArraySize)); newCol->memReducedMembers = newArraySize; } if( newSize > newCol->memMemberInformation ) { int updatedSize = maxValue(2 * newCol->memMemberInformation, newSize); - SCIP_CALL( - SCIPreallocBlockMemoryArray(dec->env, &newCol->memberInformation, newCol->memMemberInformation, updatedSize)); + SCIP_ALLOC( + BMSreallocBlockMemoryArray(dec->env, &newCol->memberInformation, newCol->memMemberInformation, updatedSize)); for( int i = newCol->memMemberInformation; i < updatedSize; ++i ) { newCol->memberInformation[i].reducedMember = INVALID_REDUCED_MEMBER; @@ -3530,8 +3525,8 @@ SCIP_RETCODE constructReducedDecomposition( if( numComponents > newCol->memReducedComponents ) { int updatedSize = maxValue(2 * newCol->memReducedComponents, numComponents); - SCIP_CALL( - SCIPreallocBlockMemoryArray(dec->env, &newCol->reducedComponents, newCol->memReducedComponents, updatedSize)); + SCIP_ALLOC( + BMSreallocBlockMemoryArray(dec->env, &newCol->reducedComponents, newCol->memReducedComponents, updatedSize)); newCol->memReducedComponents = updatedSize; } @@ -3539,7 +3534,7 @@ SCIP_RETCODE constructReducedDecomposition( if( newCol->memCreateReducedMembersCallStack < numMembers ) { int updatedSize = maxValue(2 * newCol->memCreateReducedMembersCallStack, numMembers); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->createReducedMembersCallStack, + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->createReducedMembersCallStack, newCol->memCreateReducedMembersCallStack, updatedSize)); newCol->memCreateReducedMembersCallStack = updatedSize; } @@ -3589,8 +3584,8 @@ SCIP_RETCODE constructReducedDecomposition( if( newCol->memChildrenStorage < numTotalChildren ) { int newMemSize = maxValue(newCol->memChildrenStorage * 2, numTotalChildren); - SCIP_CALL( - SCIPreallocBlockMemoryArray(dec->env, &newCol->childrenStorage, newCol->memChildrenStorage, newMemSize)); + SCIP_ALLOC( + BMSreallocBlockMemoryArray(dec->env, &newCol->childrenStorage, newCol->memChildrenStorage, newMemSize)); newCol->memChildrenStorage = newMemSize; } newCol->numChildrenStorage = numTotalChildren; @@ -3715,15 +3710,15 @@ SCIP_RETCODE createPathArcs( if( newCol->memPathArcs < maxNumPathArcs ) { int newMaxNumArcs = 2 * maxNumPathArcs;//safety factor to prevent very frequent reallocations - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->pathArcs, newCol->memPathArcs, newMaxNumArcs)); + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->pathArcs, newCol->memPathArcs, newMaxNumArcs)); newCol->memPathArcs = newMaxNumArcs; } int maxPathArcIndex = largestArcID(dec); if( newCol->memArcsInPath < maxPathArcIndex ) { int newSize = 2 * maxPathArcIndex;//safety factor to prevent very frequent reallocations - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->arcInPath, newCol->memArcsInPath, newSize)); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->arcInPathReversed, newCol->memArcsInPath, newSize)); + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->arcInPath, newCol->memArcsInPath, newSize)); + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->arcInPathReversed, newCol->memArcsInPath, newSize)); for( int i = newCol->memArcsInPath; i < newSize; ++i ) { @@ -3736,8 +3731,8 @@ SCIP_RETCODE createPathArcs( if( newCol->memNodePathDegree < maxNumNodes ) { int newSize = 2 * maxNumNodes;//safety factor to prevent very frequent reallocations - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->nodeInPathDegree, newCol->memNodePathDegree, newSize)); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->nodeOutPathDegree, newCol->memNodePathDegree, newSize)); + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->nodeInPathDegree, newCol->memNodePathDegree, newSize)); + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->nodeOutPathDegree, newCol->memNodePathDegree, newSize)); for( int i = newCol->memNodePathDegree; i < newSize; ++i ) { newCol->nodeInPathDegree[i] = 0; @@ -3783,9 +3778,9 @@ SCIP_RETCODE newColUpdateColInformation( if( newCol->numDecompositionRowArcs == newCol->memDecompositionRowArcs ) { int newNumArcs = newCol->memDecompositionRowArcs == 0 ? 8 : 2 * newCol->memDecompositionRowArcs; - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->decompositionRowArcs, + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->decompositionRowArcs, newCol->memDecompositionRowArcs, newNumArcs)); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->decompositionArcReversed, + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->decompositionArcReversed, newCol->memDecompositionRowArcs, newNumArcs)); newCol->memDecompositionRowArcs = newNumArcs; } @@ -3799,9 +3794,9 @@ SCIP_RETCODE newColUpdateColInformation( if( newCol->numNewRowArcs == newCol->memNewRowArcs ) { int newNumArcs = newCol->memNewRowArcs == 0 ? 8 : 2 * newCol->memNewRowArcs; - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->newRowArcs, + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->newRowArcs, newCol->memNewRowArcs, newNumArcs)); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->newRowArcReversed, + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->newRowArcReversed, newCol->memNewRowArcs, newNumArcs)); newCol->memNewRowArcs = newNumArcs; } @@ -3824,7 +3819,7 @@ SCIP_RETCODE computeLeafMembers( if( newCol->numReducedMembers > newCol->memLeafMembers ) { int newSize = maxValue(newCol->numReducedMembers, 2 * newCol->memLeafMembers); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newCol->leafMembers, newCol->memLeafMembers, newSize)); + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->leafMembers, newCol->memLeafMembers, newSize)); newCol->memLeafMembers = newSize; } newCol->numLeafMembers = 0; @@ -6597,9 +6592,9 @@ SCIP_RETCODE newRowUpdateRowInformation( if( newRow->numDecompositionColumnArcs == newRow->memDecompositionColumnArcs ) { int newNumArcs = newRow->memDecompositionColumnArcs == 0 ? 8 : 2 * newRow->memDecompositionColumnArcs; - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->decompositionColumnArcs, + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->decompositionColumnArcs, newRow->memDecompositionColumnArcs, newNumArcs)); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->decompositionColumnArcReversed, + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->decompositionColumnArcReversed, newRow->memDecompositionColumnArcs, newNumArcs)); newRow->memDecompositionColumnArcs = newNumArcs; } @@ -6613,9 +6608,9 @@ SCIP_RETCODE newRowUpdateRowInformation( if( newRow->numColumnArcs == newRow->memColumnArcs ) { int newNumArcs = newRow->memColumnArcs == 0 ? 8 : 2 * newRow->memColumnArcs; - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->newColumnArcs, + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->newColumnArcs, newRow->memColumnArcs, newNumArcs)); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->newColumnReversed, + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->newColumnReversed, newRow->memColumnArcs, newNumArcs)); newRow->memColumnArcs = newNumArcs; } @@ -6763,14 +6758,14 @@ SCIP_RETCODE constructRowReducedDecomposition( if( newSize > newRow->memReducedMembers ) { int updatedSize = maxValue(2 * newRow->memReducedMembers, newSize); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->reducedMembers, newRow->memReducedMembers, updatedSize)); + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->reducedMembers, newRow->memReducedMembers, updatedSize)); newRow->memReducedMembers = updatedSize; } if( newSize > newRow->memMemberInformation ) { int updatedSize = maxValue(2 * newRow->memMemberInformation, newSize); - SCIP_CALL( - SCIPreallocBlockMemoryArray(dec->env, &newRow->memberInformation, newRow->memMemberInformation, updatedSize)); + SCIP_ALLOC( + BMSreallocBlockMemoryArray(dec->env, &newRow->memberInformation, newRow->memMemberInformation, updatedSize)); for( int i = newRow->memMemberInformation; i < updatedSize; ++i ) { newRow->memberInformation[i].reducedMember = INVALID_REDUCED_MEMBER; @@ -6783,8 +6778,8 @@ SCIP_RETCODE constructRowReducedDecomposition( if( numComponents > newRow->memReducedComponents ) { int updatedSize = maxValue(2 * newRow->memReducedComponents, numComponents); - SCIP_CALL( - SCIPreallocBlockMemoryArray(dec->env, &newRow->reducedComponents, newRow->memReducedComponents, updatedSize)); + SCIP_ALLOC( + BMSreallocBlockMemoryArray(dec->env, &newRow->reducedComponents, newRow->memReducedComponents, updatedSize)); newRow->memReducedComponents = updatedSize; } @@ -6792,7 +6787,7 @@ SCIP_RETCODE constructRowReducedDecomposition( if( newRow->memCreateReducedMembersCallstack < numMembers ) { int updatedSize = maxValue(2 * newRow->memCreateReducedMembersCallstack, numMembers); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->createReducedMembersCallstack, + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->createReducedMembersCallstack, newRow->memCreateReducedMembersCallstack, updatedSize)); newRow->memCreateReducedMembersCallstack = updatedSize; } @@ -6842,7 +6837,7 @@ SCIP_RETCODE constructRowReducedDecomposition( if( newRow->memChildrenStorage < numTotalChildren ) { int newMemSize = maxValue(newRow->memChildrenStorage * 2, numTotalChildren); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->childrenStorage, newRow->memChildrenStorage, + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->childrenStorage, newRow->memChildrenStorage, newMemSize)); newRow->memChildrenStorage = newMemSize; } @@ -6948,8 +6943,8 @@ SCIP_RETCODE createReducedDecompositionCutArcs( if( maxArcID > newRow->memIsArcCut ) { int newSize = maxValue(maxArcID, 2 * newRow->memIsArcCut); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->isArcCut, newRow->memIsArcCut, newSize)); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->isArcCutReversed, newRow->memIsArcCut, newSize)); + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->isArcCut, newRow->memIsArcCut, newSize)); + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->isArcCutReversed, newRow->memIsArcCut, newSize)); for( int i = newRow->memIsArcCut; i < newSize; ++i ) { newRow->isArcCut[i] = FALSE; @@ -6969,7 +6964,7 @@ SCIP_RETCODE createReducedDecompositionCutArcs( if( numNeededArcs > newRow->memCutArcs ) { int newSize = maxValue(newRow->memCutArcs * 2, numNeededArcs); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->cutArcs, newRow->memCutArcs, newSize)); + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->cutArcs, newRow->memCutArcs, newSize)); newRow->memCutArcs = newSize; } newRow->numCutArcs = 0; @@ -6997,7 +6992,7 @@ SCIP_RETCODE determineLeafReducedMembers( if( newRow->numDecompositionColumnArcs > newRow->memLeafMembers ) { int updatedSize = maxValue(newRow->numDecompositionColumnArcs, 2 * newRow->memLeafMembers); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->leafMembers, newRow->memLeafMembers, updatedSize)); + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->leafMembers, newRow->memLeafMembers, updatedSize)); newRow->memLeafMembers = updatedSize; } newRow->numLeafMembers = 0; @@ -7025,7 +7020,7 @@ SCIP_RETCODE allocateRigidSearchMemory( if( maxNumNodes > newRow->memNodeColors ) { int newSize = maxValue(2 * newRow->memNodeColors, maxNumNodes); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->nodeColors, newRow->memNodeColors, newSize)); + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->nodeColors, newRow->memNodeColors, newSize)); for( int i = newRow->memNodeColors; i < newSize; ++i ) { newRow->nodeColors[i] = UNCOLORED; @@ -7036,27 +7031,27 @@ SCIP_RETCODE allocateRigidSearchMemory( if( totalNumNodes > newRow->memArticulationNodes ) { int newSize = maxValue(2 * newRow->memArticulationNodes, totalNumNodes); - SCIP_CALL( - SCIPreallocBlockMemoryArray(dec->env, &newRow->articulationNodes, newRow->memArticulationNodes, newSize)); + SCIP_ALLOC( + BMSreallocBlockMemoryArray(dec->env, &newRow->articulationNodes, newRow->memArticulationNodes, newSize)); newRow->memArticulationNodes = newSize; } if( totalNumNodes > newRow->memNodeSearchInfo ) { int newSize = maxValue(2 * newRow->memNodeSearchInfo, totalNumNodes); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->articulationNodeSearchInfo, newRow->memNodeSearchInfo, + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->articulationNodeSearchInfo, newRow->memNodeSearchInfo, newSize)); newRow->memNodeSearchInfo = newSize; } if( totalNumNodes > newRow->memCrossingPathCount ) { int newSize = maxValue(2 * newRow->memCrossingPathCount, totalNumNodes); - SCIP_CALL( - SCIPreallocBlockMemoryArray(dec->env, &newRow->crossingPathCount, newRow->memCrossingPathCount, newSize)); + SCIP_ALLOC( + BMSreallocBlockMemoryArray(dec->env, &newRow->crossingPathCount, newRow->memCrossingPathCount, newSize)); newRow->memCrossingPathCount = newSize; } if( totalNumNodes > newRow->memTemporaryColorArray ){ int newSize = maxValue(2 * newRow->memTemporaryColorArray, totalNumNodes); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->temporaryColorArray, + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->temporaryColorArray, newRow->memTemporaryColorArray, newSize)); newRow->memTemporaryColorArray = newSize; } @@ -7068,20 +7063,20 @@ SCIP_RETCODE allocateRigidSearchMemory( if( largestID > newRow->memIntersectionDFSData ) { int newSize = maxValue(2 * newRow->memIntersectionDFSData, largestID); - SCIP_CALL( - SCIPreallocBlockMemoryArray(dec->env, &newRow->intersectionDFSData, newRow->memIntersectionDFSData, newSize)); + SCIP_ALLOC( + BMSreallocBlockMemoryArray(dec->env, &newRow->intersectionDFSData, newRow->memIntersectionDFSData, newSize)); newRow->memIntersectionDFSData = newSize; } if( largestID > newRow->memColorDFSData ) { int newSize = maxValue(2 * newRow->memColorDFSData, largestID); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->colorDFSData, newRow->memColorDFSData, newSize)); + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->colorDFSData, newRow->memColorDFSData, newSize)); newRow->memColorDFSData = newSize; } if( largestID > newRow->memArtDFSData ) { int newSize = maxValue(2 * newRow->memArtDFSData, largestID); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->artDFSData, newRow->memArtDFSData, newSize)); + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->artDFSData, newRow->memArtDFSData, newSize)); newRow->memArtDFSData = newSize; } @@ -7093,7 +7088,7 @@ SCIP_RETCODE allocateRigidSearchMemory( if( largestID > newRow->memIntersectionPathDepth ) { int newSize = maxValue(2 * newRow->memIntersectionPathDepth, largestID); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->intersectionPathDepth, newRow->memIntersectionPathDepth, + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->intersectionPathDepth, newRow->memIntersectionPathDepth, newSize)); for( int i = newRow->memIntersectionPathDepth; i < newSize; ++i ) { @@ -7108,8 +7103,8 @@ SCIP_RETCODE allocateRigidSearchMemory( if( largestID > newRow->memIntersectionPathParent ) { int newSize = maxValue(2 * newRow->memIntersectionPathParent, largestID); - SCIP_CALL( - SCIPreallocBlockMemoryArray(dec->env, &newRow->intersectionPathParent, newRow->memIntersectionPathParent, + SCIP_ALLOC( + BMSreallocBlockMemoryArray(dec->env, &newRow->intersectionPathParent, newRow->memIntersectionPathParent, newSize)); for( int i = newRow->memIntersectionPathParent; i < newSize; ++i ) { @@ -8303,7 +8298,7 @@ SCIP_RETCODE allocateTreeSearchMemory( if( necessarySpace > newRow->memMergeTreeCallData ) { int updatedSize = maxValue(2 * newRow->memMergeTreeCallData, necessarySpace); - SCIP_CALL(SCIPreallocBlockMemoryArray(dec->env, &newRow->mergeTreeCallData, newRow->memMergeTreeCallData, + SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->mergeTreeCallData, newRow->memMergeTreeCallData, updatedSize)); newRow->memMergeTreeCallData = updatedSize; } @@ -10763,12 +10758,12 @@ SCIP_RETCODE transformComponentRowAddition( /**< Create the network row addition data structure */ static SCIP_RETCODE SCIPnetrowaddCreate( - SCIP* scip, /**< Main SCIP instance */ + BMS_BLKMEM* blkmem, /**< Block memory */ SCIP_NETROWADD** prowadd /**< Pointer to store the new row addition struct at */ ) { - assert(scip); - SCIP_CALL(SCIPallocBlockMemory(scip, prowadd)); + assert(blkmem); + SCIP_ALLOC(BMSallocBlockMemory(blkmem, prowadd)); SCIP_NETROWADD* newRow = *prowadd; newRow->remainsNetwork = TRUE; @@ -10857,7 +10852,7 @@ SCIP_RETCODE SCIPnetrowaddCreate( /**< Frees the network row addition data structure */ static void SCIPnetrowaddFree( - SCIP* scip, /**< Main SCIP instance */ + BMS_BLKMEM* blkmem, /**< Block memory */ SCIP_NETROWADD** prowadd /**< Pointer to row addition struct to be freed */ ) { @@ -10865,31 +10860,31 @@ void SCIPnetrowaddFree( SCIP_NETROWADD* newRow = *prowadd; - SCIPfreeBlockMemoryArray(scip, &newRow->temporaryColorArray, newRow->memTemporaryColorArray); - SCIPfreeBlockMemoryArray(scip, &newRow->createReducedMembersCallstack, newRow->memCreateReducedMembersCallstack); - SCIPfreeBlockMemoryArray(scip, &newRow->artDFSData, newRow->memArtDFSData); - SCIPfreeBlockMemoryArray(scip, &newRow->colorDFSData, newRow->memColorDFSData); - SCIPfreeBlockMemoryArray(scip, &newRow->mergeTreeCallData, newRow->memMergeTreeCallData); - SCIPfreeBlockMemoryArray(scip, &newRow->intersectionDFSData, newRow->memIntersectionDFSData); - SCIPfreeBlockMemoryArray(scip, &newRow->intersectionPathParent, newRow->memIntersectionPathParent); - SCIPfreeBlockMemoryArray(scip, &newRow->intersectionPathDepth, newRow->memIntersectionPathDepth); - SCIPfreeBlockMemoryArray(scip, &newRow->crossingPathCount, newRow->memCrossingPathCount); - SCIPfreeBlockMemoryArray(scip, &newRow->articulationNodeSearchInfo, newRow->memNodeSearchInfo); - SCIPfreeBlockMemoryArray(scip, &newRow->articulationNodes, newRow->memArticulationNodes); - SCIPfreeBlockMemoryArray(scip, &newRow->nodeColors, newRow->memNodeColors); - SCIPfreeBlockMemoryArray(scip, &newRow->isArcCut, newRow->memIsArcCut); - SCIPfreeBlockMemoryArray(scip, &newRow->isArcCutReversed, newRow->memIsArcCut); - SCIPfreeBlockMemoryArray(scip, &newRow->decompositionColumnArcs, newRow->memDecompositionColumnArcs); - SCIPfreeBlockMemoryArray(scip, &newRow->decompositionColumnArcReversed, newRow->memDecompositionColumnArcs); - SCIPfreeBlockMemoryArray(scip, &newRow->newColumnArcs, newRow->memColumnArcs); - SCIPfreeBlockMemoryArray(scip, &newRow->newColumnReversed, newRow->memColumnArcs); - SCIPfreeBlockMemoryArray(scip, &newRow->childrenStorage, newRow->memChildrenStorage); - SCIPfreeBlockMemoryArray(scip, &newRow->cutArcs, newRow->memCutArcs); - SCIPfreeBlockMemoryArray(scip, &newRow->memberInformation, newRow->memMemberInformation); - SCIPfreeBlockMemoryArray(scip, &newRow->reducedComponents, newRow->memReducedComponents); - SCIPfreeBlockMemoryArray(scip, &newRow->reducedMembers, newRow->memReducedMembers); - SCIPfreeBlockMemoryArray(scip, &newRow->leafMembers, newRow->memLeafMembers); - SCIPfreeBlockMemory(scip, prowadd); + BMSfreeBlockMemoryArray(blkmem, &newRow->temporaryColorArray, newRow->memTemporaryColorArray); + BMSfreeBlockMemoryArray(blkmem, &newRow->createReducedMembersCallstack, newRow->memCreateReducedMembersCallstack); + BMSfreeBlockMemoryArray(blkmem, &newRow->artDFSData, newRow->memArtDFSData); + BMSfreeBlockMemoryArray(blkmem, &newRow->colorDFSData, newRow->memColorDFSData); + BMSfreeBlockMemoryArray(blkmem, &newRow->mergeTreeCallData, newRow->memMergeTreeCallData); + BMSfreeBlockMemoryArray(blkmem, &newRow->intersectionDFSData, newRow->memIntersectionDFSData); + BMSfreeBlockMemoryArray(blkmem, &newRow->intersectionPathParent, newRow->memIntersectionPathParent); + BMSfreeBlockMemoryArray(blkmem, &newRow->intersectionPathDepth, newRow->memIntersectionPathDepth); + BMSfreeBlockMemoryArray(blkmem, &newRow->crossingPathCount, newRow->memCrossingPathCount); + BMSfreeBlockMemoryArray(blkmem, &newRow->articulationNodeSearchInfo, newRow->memNodeSearchInfo); + BMSfreeBlockMemoryArray(blkmem, &newRow->articulationNodes, newRow->memArticulationNodes); + BMSfreeBlockMemoryArray(blkmem, &newRow->nodeColors, newRow->memNodeColors); + BMSfreeBlockMemoryArray(blkmem, &newRow->isArcCut, newRow->memIsArcCut); + BMSfreeBlockMemoryArray(blkmem, &newRow->isArcCutReversed, newRow->memIsArcCut); + BMSfreeBlockMemoryArray(blkmem, &newRow->decompositionColumnArcs, newRow->memDecompositionColumnArcs); + BMSfreeBlockMemoryArray(blkmem, &newRow->decompositionColumnArcReversed, newRow->memDecompositionColumnArcs); + BMSfreeBlockMemoryArray(blkmem, &newRow->newColumnArcs, newRow->memColumnArcs); + BMSfreeBlockMemoryArray(blkmem, &newRow->newColumnReversed, newRow->memColumnArcs); + BMSfreeBlockMemoryArray(blkmem, &newRow->childrenStorage, newRow->memChildrenStorage); + BMSfreeBlockMemoryArray(blkmem, &newRow->cutArcs, newRow->memCutArcs); + BMSfreeBlockMemoryArray(blkmem, &newRow->memberInformation, newRow->memMemberInformation); + BMSfreeBlockMemoryArray(blkmem, &newRow->reducedComponents, newRow->memReducedComponents); + BMSfreeBlockMemoryArray(blkmem, &newRow->reducedMembers, newRow->memReducedMembers); + BMSfreeBlockMemoryArray(blkmem, &newRow->leafMembers, newRow->memLeafMembers); + BMSfreeBlockMemory(blkmem, prowadd); } /**< Checks if the given row can be added to the network decomposition */ @@ -11071,16 +11066,17 @@ struct SCIP_Netmatdec{ }; SCIP_RETCODE SCIPnetmatdecCreate( - SCIP* scip, /**< SCIP data structure */ + BMS_BLKMEM* blkmem, /**< Block memory */ SCIP_NETMATDEC** pdec, /**< buffer to store pointer to created decomposition */ int nrows, /**< The maximal number of rows that the decomposition can expect */ int ncols /**< The maximal number of columns that the decomposition can expect */ ) { - SCIP_CALL(SCIPallocBlockMemory(scip, pdec)); + + SCIP_ALLOC(BMSallocBlockMemory(blkmem,pdec)); SCIP_NETMATDEC* dec = *pdec; dec->dec = NULL; - SCIP_CALL(netMatDecDataCreate(scip, &dec->dec, nrows, ncols)); + SCIP_CALL(netMatDecDataCreate(blkmem, &dec->dec, nrows, ncols)); dec->rowadd = NULL; dec->coladd = NULL; return SCIP_OKAY; @@ -11091,17 +11087,17 @@ void SCIPnetmatdecFree( ) { SCIP_NETMATDEC* dec = *pdec; - SCIP* scip = dec->dec->env; + BMS_BLKMEM* blkmem = dec->dec->env; if( dec->coladd != NULL) { - SCIPnetcoladdFree(scip, &dec->coladd); + SCIPnetcoladdFree(blkmem, &dec->coladd); } if( dec->rowadd != NULL) { - SCIPnetrowaddFree(scip, &dec->rowadd); + SCIPnetrowaddFree(blkmem, &dec->rowadd); } netMatDecDataFree(&dec->dec); - SCIPfreeBlockMemory(scip,pdec); + BMSfreeBlockMemory(blkmem,pdec); } SCIP_RETCODE SCIPnetmatdecTryAddCol( @@ -11186,7 +11182,7 @@ SCIP_Bool SCIPnetmatdecIsMinimal( SCIP_Bool SCIPnetmatdecVerifyCycle( - SCIP* scip, /**< SCIP data structure */ + BMS_BUFMEM* bufmem, /**< Buffer memory */ SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ int column, /**< The column to check */ int* nonzrowidx, /**< Array with the column's nonzero row indices */ @@ -11198,5 +11194,5 @@ SCIP_Bool SCIPnetmatdecVerifyCycle( * equal or greater than the number of rows in the decomposition. */ ) { - return netMatDecDataVerifyCycle(scip,dec->dec,column,nonzrowidx,nonzvals,nnonzs,pathrowstorage,pathsignstorage); + return netMatDecDataVerifyCycle(bufmem,dec->dec,column,nonzrowidx,nonzvals,nnonzs,pathrowstorage,pathsignstorage); } diff --git a/src/scip/network.h b/src/scip/network.h index 185c81b0b5..aa5511c52c 100644 --- a/src/scip/network.h +++ b/src/scip/network.h @@ -73,7 +73,8 @@ #include "scip/def.h" #include "scip/type_retcode.h" -#include "scip/type_scip.h" +#include "scip/type_mem.h" +#include "blockmemshell/memory.h" #ifdef cplusplus extern "C" { @@ -88,7 +89,7 @@ typedef struct SCIP_Netmatdec SCIP_NETMATDEC; */ SCIP_EXPORT SCIP_RETCODE SCIPnetmatdecCreate( - SCIP* scip, /**< SCIP data structure */ + BMS_BLKMEM* blkmem, /**< Block memory */ SCIP_NETMATDEC** pdec, /**< buffer to store pointer to created decomposition */ int nrows, /**< The maximal number of rows that the decomposition can expect */ int ncols /**< The maximal number of columns that the decomposition can expect */ @@ -189,7 +190,7 @@ SCIP_Bool SCIPnetmatdecIsMinimal( */ SCIP_EXPORT SCIP_Bool SCIPnetmatdecVerifyCycle( - SCIP* scip, /**< SCIP data structure */ + BMS_BUFMEM * blkmem, /**< Buffer memory */ SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ int column, /**< The column to check */ int* nonzrowidx, /**< Array with the column's nonzero row indices */ diff --git a/tests/src/network/network.c b/tests/src/network/network.c index a542babca5..00e07f0b41 100644 --- a/tests/src/network/network.c +++ b/tests/src/network/network.c @@ -226,7 +226,9 @@ static SCIP_RETCODE runColumnTestCase( } cr_expect(!testCase->isRowWise); SCIP_NETMATDEC* dec = NULL; - SCIP_CALL(SCIPnetmatdecCreate(scip, &dec, testCase->nrows, testCase->ncols)); + BMS_BLKMEM * blkmem = SCIPblkmem(scip); + BMS_BUFMEM * bufmem = SCIPbuffer(scip); + SCIP_CALL(SCIPnetmatdecCreate(blkmem, &dec, testCase->nrows, testCase->ncols)); SCIP_Bool isNetwork = TRUE; @@ -259,7 +261,7 @@ static SCIP_RETCODE runColumnTestCase( int* jNonzeroRows = &testCase->entrySecondaryIndex[jColEntryStart]; double* jNonzeroValues = &testCase->entryValue[jColEntryStart]; int jNonzeros = jColEntryEnd - jColEntryStart; - SCIP_Bool cycleIsCorrect = SCIPnetmatdecVerifyCycle(scip, dec, j, + SCIP_Bool cycleIsCorrect = SCIPnetmatdecVerifyCycle(bufmem, dec, j, jNonzeroRows, jNonzeroValues, jNonzeros, tempColumnStorage, tempSignStorage); @@ -302,8 +304,11 @@ static SCIP_RETCODE runRowTestCase( DirectedTestCase colWiseCase = copyTestCase(testCase); transposeMatrixStorage(&colWiseCase); + BMS_BLKMEM * blkmem = SCIPblkmem(scip); + BMS_BUFMEM * bufmem = SCIPbuffer(scip); + SCIP_NETMATDEC* dec = NULL; - SCIP_CALL(SCIPnetmatdecCreate(scip, &dec, testCase->nrows, testCase->ncols)); + SCIP_CALL(SCIPnetmatdecCreate(blkmem, &dec, testCase->nrows, testCase->ncols)); SCIP_Bool isNetwork = TRUE; @@ -351,7 +356,7 @@ static SCIP_RETCODE runRowTestCase( double* jNonzeroValues = &colWiseCase.entryValue[jColEntryStart]; int jNonzeros = finalEntryIndex - jColEntryStart; - SCIP_Bool cycleIsCorrect = SCIPnetmatdecVerifyCycle(scip, dec, j, + SCIP_Bool cycleIsCorrect = SCIPnetmatdecVerifyCycle(bufmem, dec, j, jNonzeroRows, jNonzeroValues, jNonzeros, tempColumnStorage, tempSignStorage); From 4df0ed059078ef1f1996d483073f6f3e294750fc Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 30 Jul 2024 12:10:55 +0200 Subject: [PATCH 28/63] Rename network.h to pub_network.h --- src/CMakeLists.txt | 2 +- src/scip/network.c | 2 +- src/scip/{network.h => pub_network.h} | 2 +- tests/src/network/network.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/scip/{network.h => pub_network.h} (99%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 486e150868..7674fc7758 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -657,7 +657,7 @@ set(scipheaders scip/message_default.h scip/message.h scip/misc.h - scip/network.h + scip/pub_network.h scip/nlhdlr_bilinear.h scip/nlhdlr_convex.h scip/nlhdlr_default.h diff --git a/src/scip/network.c b/src/scip/network.c index ae916d241a..bdc8957343 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -125,7 +125,7 @@ */ #include -#include "scip/network.h" +#include "scip/pub_network.h" #include "scip/scip.h" #include "blockmemshell/memory.h" diff --git a/src/scip/network.h b/src/scip/pub_network.h similarity index 99% rename from src/scip/network.h rename to src/scip/pub_network.h index aa5511c52c..f7fb305e3e 100644 --- a/src/scip/network.h +++ b/src/scip/pub_network.h @@ -190,7 +190,7 @@ SCIP_Bool SCIPnetmatdecIsMinimal( */ SCIP_EXPORT SCIP_Bool SCIPnetmatdecVerifyCycle( - BMS_BUFMEM * blkmem, /**< Buffer memory */ + BMS_BUFMEM * bufmem, /**< Buffer memory */ SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ int column, /**< The column to check */ int* nonzrowidx, /**< Array with the column's nonzero row indices */ diff --git a/tests/src/network/network.c b/tests/src/network/network.c index 00e07f0b41..fb598e7640 100644 --- a/tests/src/network/network.c +++ b/tests/src/network/network.c @@ -29,7 +29,7 @@ /*--+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/ -#include "scip/network.h" +#include "scip/pub_network.h" #include "include/scip_test.h" From f84f017ce042d731af37fd71900ec89d5df95e1d Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 30 Jul 2024 12:11:21 +0200 Subject: [PATCH 29/63] Add changelog entry for network matrices --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 5f4dce0666..721bcb425d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ Features - detailed information about applied symmetry handling techniques can be printed to the terminal - added a new separator sepa_multilinear to generate flower cuts from AND constraints nonlinear product expressions. - added functionality to deal with hypergraphs by means of efficient access to vertices, edges and intersections edges. +- added support for (transposed) network matrix detection in pub_network.h Performance improvements ------------------------ From 72918aeea38c3b748bc0f77fcf7c727094034622 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 30 Jul 2024 12:42:44 +0200 Subject: [PATCH 30/63] Fix paper title in documentation --- src/scip/pub_network.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/scip/pub_network.h b/src/scip/pub_network.h index f7fb305e3e..1497982bfe 100644 --- a/src/scip/pub_network.h +++ b/src/scip/pub_network.h @@ -40,8 +40,8 @@ * and for each arc \f$ a = (u,v) \in A\setminus T\f$ and each arc \f$t\in T\f$ * \f[ * M_{t,a} = \begin{cases} - * +1 & \textrm{if the unique } u-v \textrm{ path in } T \textrm{ passes through } a \textrm{ forwardly} \\ - * -1 & \textrm{if the unique } u-v \textrm{ path in } T \textrm{ passes through } a \textrm{ backwardly} \\ + * +1 & \textrm{if the unique } u-v \textrm{ path in } T \textrm{ passes through } a \textrm{ forwardly}, \\ + * -1 & \textrm{if the unique } u-v \textrm{ path in } T \textrm{ passes through } a \textrm{ backwardly}, \\ * 0 & \textrm{if the unique } u-v \textrm{ path in } T \textrm{ does not pass through } a * \end{cases} * \f] @@ -55,7 +55,7 @@ * it is up to the user to ensure this when interleaving column and row addition steps. * * More details can be found in: - * - R.P. van der Hulst and M. Walter "A row-wise algorithm for graph realization" + * - R.P. van der Hulst and M. Walter "A Row-wise Algorithm for Graph Realization" * - R.E. Bixby and D.K. Wagner "An almost linear-time algorithm for graph realization" * * Note that although these publications contain the methods for undirected graphs (and binary matrices), From c9a35c07993cfaa87c80e4d6347ba3a3e3592376 Mon Sep 17 00:00:00 2001 From: Stefan Vigerske Date: Mon, 5 Aug 2024 07:56:00 +0200 Subject: [PATCH 31/63] some docu fixes and minor changes in whitespace --- src/scip/network.c | 2413 ++++++++++++++++++++++------------------ src/scip/pub_network.h | 2 +- 2 files changed, 1321 insertions(+), 1094 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index bdc8957343..4088726053 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -120,8 +120,9 @@ * * 3. The graphs of the S, P and Q members do not need to be stored explicitly, as they always have the same structure. * This also makes it easier to permute edges of S nodes on the fly. - * - * TODO: fix tracking connectivity more cleanly, should not be left up to the algorithms ideally + */ + + /* TODO: fix tracking connectivity more cleanly, should not be left up to the algorithms ideally */ #include @@ -243,7 +244,9 @@ spqr_element SPQRcolumnToElement( return (spqr_element) column; } -/** spqr_member is an index for the members of the SPQR decomposition. The members are the nodes of the SPQR tree. +/** spqr_member is an index for the members of the SPQR decomposition + * + * The members are the nodes of the SPQR tree. * Each member has an associated subgraph, sometimes called a skeleton. * If two members are adjacent in the SPQR tree, the two corresponding subgraphs are connected by a 2-separation in any * graph represented by the matrix. @@ -270,7 +273,9 @@ static SCIP_Bool SPQRmemberIsValid( return !SPQRmemberIsInvalid(member); } -/** spqr_node is an index for the nodes stored in the decomposition. The nodes are part of each member's skeleton. +/** spqr_node is an index for the nodes stored in the decomposition. + * + * The nodes are part of each member's skeleton. * Similar to spqr_member, we reserve all negative values as invalid and use these in union-find. */ typedef int spqr_node; @@ -294,7 +299,9 @@ SCIP_Bool SPQRnodeIsValid( return !SPQRnodeIsInvalid(node); } -/** spqr_arc is an index for the arcs stored in the decomposition. The arcs are part of each member's skeleton. +/** spqr_arc is an index for the arcs stored in the decomposition. + * + * The arcs are part of each member's skeleton. * Similar to spqr_node and spqr_member, we reserve all negative values as invalid and use these in some union-find. * However, in contrast to spqr_node and spqr_member, the union-find data structure does not represent the arcs, * but rather if all arcs that have the same representative we know they are in the same member. @@ -323,11 +330,11 @@ SCIP_Bool SPQRarcIsValid( /** The type of the member */ typedef enum { - SPQR_MEMBERTYPE_RIGID = 0, /** The member's skeleton is 3-connected and has at least 4 edges */ - SPQR_MEMBERTYPE_PARALLEL = 1, /** The member's skeleton consists of 2 nodes with at least 3 edges */ - SPQR_MEMBERTYPE_SERIES = 2, /** The member's skeleton is a cycle with at least 3 edges */ - SPQR_MEMBERTYPE_LOOP = 3, /** The member's skeleton consists of 2 nodes connected by 1 or 2 edges */ - SPQR_MEMBERTYPE_UNASSIGNED = 4 /** To indicate that the member is not a representative member anymore */ + SPQR_MEMBERTYPE_RIGID = 0, /**< The member's skeleton is 3-connected and has at least 4 edges */ + SPQR_MEMBERTYPE_PARALLEL = 1, /**< The member's skeleton consists of 2 nodes with at least 3 edges */ + SPQR_MEMBERTYPE_SERIES = 2, /**< The member's skeleton is a cycle with at least 3 edges */ + SPQR_MEMBERTYPE_LOOP = 3, /**< The member's skeleton consists of 2 nodes connected by 1 or 2 edges */ + SPQR_MEMBERTYPE_UNASSIGNED = 4 /**< To indicate that the member is not a representative member anymore */ } SPQRMemberType; /** Represents a single node of cyclic doubly-linked list of arc edges*/ @@ -340,10 +347,10 @@ typedef struct /** This structure stores the relevant data for a single node. */ typedef struct { - spqr_node representativeNode; /**< Points to the next node in the union-find data structure + spqr_node representativeNode; /**< Points to the next node in the union-find data structure. * Stores the rank as a negative number if this node represents itself */ spqr_arc firstArc; /**< Points to the head node of the cyclic doubly linked list containing - * the arcs that are adjacent to this node.*/ + * the arcs that are adjacent to this node. */ int numArcs; /**< The number of arcs adjacent to this node */ } SPQRNetworkDecompositionNode; @@ -360,7 +367,10 @@ typedef struct spqr_element element; /**< The element associated to this arc */ - /**Signed union-find for arc directions. If an arc is reversed, it head becomes its tail and vice-versa. + /** @name Signed union-find for arc directions. + * @{ + * + * If an arc is reversed, it's head becomes its tail and vice-versa. * For non-rigid members every arc is it's own representative, and the direction is simply given by the boolean. * For rigid members, every arc is represented by another arc in the member, * and the direction can be found by multiplying the signs along the union-find path @@ -368,23 +378,28 @@ typedef struct */ spqr_arc representative; /**< The representative of the arc */ SCIP_Bool reversed; /**< Whether the arc's head and tail are reversed, or not */ + /** @} */ } SPQRNetworkDecompositionArc; /** Structure that stores the relevant data for a single member */ typedef struct { - spqr_member representativeMember; /** The representative of this member (union-find) */ - SPQRMemberType type; /** The type of this member */ + spqr_member representativeMember; /**< The representative of this member (union-find) */ + SPQRMemberType type; /**< The type of this member */ - /**The SPQR tree is stored as an arborescence. Each member stores its parents, and each edge of the member + /** @name The SPQR tree is stored as an arborescence. + * @{ + * + * Each member stores its parents, and each edge of the member * pointing to a child member stores the associated member in childMember */ spqr_member parentMember; /**< The parent of this member in the arborescence */ spqr_arc markerToParent; /**< The arc pointing to the parent */ spqr_arc markerOfParent; /**< The arc of the parent pointing to this member */ + /** @} */ - spqr_arc firstArc; /** First arc of the linked list containing the member's arcs */ - int numArcs; /** The number of arcs associated to the member */ + spqr_arc firstArc; /**< First arc of the linked list containing the member's arcs */ + int numArcs; /**< The number of arcs associated to the member */ } SPQRNetworkDecompositionMember; /** Stores the SPQR forest data structure and its relevant data */ @@ -411,13 +426,13 @@ typedef struct BMS_BLKMEM * env; /**< used memory allocator */ - int numConnectedComponents; /** The number of disjoint SPQR trees in the SPQR forest */ + int numConnectedComponents; /**< The number of disjoint SPQR trees in the SPQR forest */ } SCIP_NETMATDECDATA; #ifndef NDEBUG -/**< Check if a node is a representative in the union-find data structure for nodes */ +/** Check if a node is a representative in the union-find data structure for nodes */ static SCIP_Bool nodeIsRepresentative( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -433,7 +448,7 @@ SCIP_Bool nodeIsRepresentative( #endif -/**< Find the node its representative node in the union-find data structure */ +/** Find the node its representative node in the union-find data structure */ static spqr_node findNode( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -448,7 +463,7 @@ spqr_node findNode( spqr_node next; //traverse down tree to find the root - while( SPQRnodeIsValid(next = dec->nodes[current].representativeNode)) + while( SPQRnodeIsValid(next = dec->nodes[current].representativeNode) ) { current = next; assert(current < dec->memNodes); @@ -458,7 +473,7 @@ spqr_node findNode( current = node; //update all pointers along path to point to root, flattening the tree - while( SPQRnodeIsValid(next = dec->nodes[current].representativeNode)) + while( SPQRnodeIsValid(next = dec->nodes[current].representativeNode) ) { dec->nodes[current].representativeNode = root; current = next; @@ -467,8 +482,10 @@ spqr_node findNode( return root; } -/**< Find the node its representative node in the union-find data structure, without compressing the union-find tree. - * Should only be used for debugging or asserts. */ +/** Find the node its representative node in the union-find data structure, without compressing the union-find tree. + * + * Should only be used for debugging or asserts. + */ static spqr_node findNodeNoCompression( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -483,7 +500,7 @@ spqr_node findNodeNoCompression( spqr_node next; //traverse down tree to find the root - while( SPQRnodeIsValid(next = dec->nodes[current].representativeNode)) + while( SPQRnodeIsValid(next = dec->nodes[current].representativeNode) ) { current = next; assert(current < dec->memNodes); @@ -492,8 +509,10 @@ spqr_node findNodeNoCompression( return root; } -/**< Find the arc's tail node in the union find data structure of the nodes. - * Updates the arc's tail to point to the representative for faster future queries. */ +/** Find the arc's tail node in the union find data structure of the nodes. + * + * Updates the arc's tail to point to the representative for faster future queries. + */ static spqr_node findArcTail( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -510,8 +529,10 @@ spqr_node findArcTail( return representative; } -/**< Find the arc's head node in the union find data structure of the nodes. - * Updates the arc's head to point to the representative for faster future queries. */ +/** Find the arc's head node in the union find data structure of the nodes. + * + * Updates the arc's head to point to the representative for faster future queries. + */ static spqr_node findArcHead( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -528,8 +549,10 @@ spqr_node findArcHead( return representative; } -/**< Find the arc's head node in the union find data structure of the nodes, without compressing the union-find tree. - * Should only be used in debugging statements or asserts. */ +/** Find the arc's head node in the union find data structure of the nodes, without compressing the union-find tree. + * + * Should only be used in debugging statements or asserts. + */ static spqr_node findArcHeadNoCompression( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -544,8 +567,10 @@ spqr_node findArcHeadNoCompression( return representative; } -/**< Find the arc's tail node in the union find data structure of the nodes, without compressing the union-find tree. - * Should only be used in debugging statements or asserts. */ +/** Find the arc's tail node in the union find data structure of the nodes, without compressing the union-find tree. + * + * Should only be used in debugging statements or asserts. + */ static spqr_node findArcTailNoCompression( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -560,8 +585,10 @@ spqr_node findArcTailNoCompression( return representative; } -/**< Find the first arc in the list of arcs that are adjacent to the given node. - * These arcs form a cyclic linked-list. */ +/** Find the first arc in the list of arcs that are adjacent to the given node. + * + * These arcs form a cyclic linked-list. + */ static spqr_arc getFirstNodeArc( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -571,11 +598,15 @@ spqr_arc getFirstNodeArc( assert(dec); assert(SPQRnodeIsValid(node)); assert(node < dec->memNodes); + return dec->nodes[node].firstArc; } -/**< Given the current arc adjacent to this node, find the next arc in the cyclic linked list of adjacent arcs to the - * given node. This function does not compress the union-find tree, and should only be used in debugging or asserts.*/ +/** Given the current arc adjacent to this node, find the next arc in the cyclic linked list of adjacent arcs to the + * given node. + * + * This function does not compress the union-find tree, and should only be used in debugging or asserts. + */ static spqr_arc getNextNodeArcNoCompression( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -600,8 +631,9 @@ spqr_arc getNextNodeArcNoCompression( return arc; } -/**< Given the current arc adjacent to this node, find the next arc in the cyclic linked list of adjacent arcs to the - * given node. */ +/** Given the current arc adjacent to this node, find the next arc in the cyclic linked list of adjacent arcs to the + * given node. + */ static spqr_arc getNextNodeArc( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -627,8 +659,9 @@ spqr_arc getNextNodeArc( return arc; } -/**< Given the current arc adjacent to this node, find the previous arc in the cyclic linked list of adjacent arcs - * to the given node. */ +/** Given the current arc adjacent to this node, find the previous arc in the cyclic linked list of adjacent arcs + * to the given node. + */ static spqr_arc getPreviousNodeArc( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -654,8 +687,10 @@ spqr_arc getPreviousNodeArc( return arc; } -/**< Update the cyclic node-arc incidence data structure to move all arcs adjacent to one node to another node. - * We typically call this when two nodes are identified with one another, and we need to merge their adjacent arcs. */ +/** Update the cyclic node-arc incidence data structure to move all arcs adjacent to one node to another node. + * + * We typically call this when two nodes are identified with one another, and we need to merge their adjacent arcs. + */ static void mergeNodeArcList( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -676,7 +711,7 @@ void mergeNodeArcList( return; } - else if( SPQRarcIsInvalid(firstFromArc)) + if( SPQRarcIsInvalid(firstFromArc)) { //Old node has no arcs; we can just return return; @@ -712,7 +747,7 @@ void mergeNodeArcList( dec->nodes[toRemove].firstArc = SPQR_INVALID_ARC; } -/**< Flips the direction a given arc. */ +/** Flips the direction a given arc. */ static void arcFlipReversed( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -722,27 +757,32 @@ void arcFlipReversed( assert(dec); assert(SPQRarcIsValid(arc)); assert(arc < dec->memArcs); + dec->arcs[arc].reversed = !dec->arcs[arc].reversed; } -/**< Sets the direction of a given arc */ +/** Sets the direction of a given arc */ static void arcSetReversed( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ spqr_arc arc, /**< The given arc */ - SCIP_Bool reversed /**< Are the head and tail reversed?*/ + SCIP_Bool reversed /**< Are the head and tail reversed? */ ) { assert(dec); assert(SPQRarcIsValid(arc)); assert(arc < dec->memArcs); + dec->arcs[arc].reversed = reversed; } -/**< Sets the representative of a given arc. The arcs reversed field is given with respect to the representative. - * In particular, whether an arc is reversed or not is determined by the sign of the path in the signed union-find - * data structure for arcs, which can be computed by multiplying the signs of the individual edges (xor over bools) */ +/** Sets the representative of a given arc. + * + * The arcs reversed field is given with respect to the representative. + * In particular, whether an arc is reversed or not is determined by the sign of the path in the signed union-find + * data structure for arcs, which can be computed by multiplying the signs of the individual edges (xor over bools). + */ static void arcSetRepresentative( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -754,11 +794,14 @@ void arcSetRepresentative( assert(SPQRarcIsValid(arc)); assert(arc < dec->memArcs); assert(representative == SPQR_INVALID_ARC || SPQRarcIsValid(representative)); + dec->arcs[arc].representative = representative; } -/**< Merge two representative nodes (Union operation) in the union-find data structure for nodes. - * Returns the id of the node that becomes representative for both.*/ +/** Merge two representative nodes (Union operation) in the union-find data structure for nodes. + * + * Returns the id of the node that becomes representative for both. + */ static spqr_node mergeNodes( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -791,8 +834,7 @@ spqr_node mergeNodes( return first; } - -/**< Check if a member is a representative in the union-find data structure for members*/ +/** Check if a member is a representative in the union-find data structure for members. */ static SCIP_Bool memberIsRepresentative( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -806,7 +848,7 @@ SCIP_Bool memberIsRepresentative( return SPQRmemberIsInvalid(dec->members[member].representativeMember); } -/**< Find the member its representative member in the union-find data structure */ +/** Find the member its representative member in the union-find data structure */ static spqr_member findMember( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -821,7 +863,7 @@ spqr_member findMember( spqr_member next; //traverse down tree to find the root - while( SPQRmemberIsValid(next = dec->members[current].representativeMember)) + while( SPQRmemberIsValid(next = dec->members[current].representativeMember) ) { current = next; assert(current < dec->memMembers); @@ -831,7 +873,7 @@ spqr_member findMember( current = member; //update all pointers along path to point to root, flattening the tree - while( SPQRmemberIsValid(next = dec->members[current].representativeMember)) + while( SPQRmemberIsValid(next = dec->members[current].representativeMember) ) { dec->members[current].representativeMember = root; current = next; @@ -840,8 +882,10 @@ spqr_member findMember( return root; } -/**< Find the member's representative member in the union-find data structure, without compressing the union-find tree. - * Should only be used for debugging or asserts. */ +/** Find the member's representative member in the union-find data structure, without compressing the union-find tree. + * + * Should only be used for debugging or asserts. + */ static spqr_member findMemberNoCompression( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -856,7 +900,7 @@ spqr_member findMemberNoCompression( spqr_member next; //traverse down tree to find the root - while( SPQRmemberIsValid(next = dec->members[current].representativeMember)) + while( SPQRmemberIsValid(next = dec->members[current].representativeMember) ) { current = next; assert(current < dec->memMembers); @@ -866,8 +910,10 @@ spqr_member findMemberNoCompression( return root; } -/**< Merge two representative members (Union operation) in the union-find data structure. - * Returns the id of the member that becomes representative for both.*/ +/** Merge two representative members (Union operation) in the union-find data structure. + * + * Returns the id of the member that becomes representative for both. + */ static spqr_member mergeMembers( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -898,7 +944,7 @@ spqr_member mergeMembers( return first; } -/**< Finds the member in which the arc is located */ +/** Finds the member in which the arc is located */ static spqr_member findArcMember( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -914,7 +960,7 @@ spqr_member findArcMember( return representative; } -/**< Finds the member in which the arc is located, without compressing the member union-find tree */ +/** Finds the member in which the arc is located, without compressing the member union-find tree */ static spqr_member findArcMemberNoCompression( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -929,7 +975,10 @@ spqr_member findArcMemberNoCompression( return representative; } -/**< Find the representative parent member of the given member. Note the given member must be representative. */ +/** Find the representative parent member of the given member. + * + * Note the given member must be representative. + */ static spqr_member findMemberParent( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -941,7 +990,6 @@ spqr_member findMemberParent( assert(SPQRmemberIsValid(member)); assert(memberIsRepresentative(dec, member)); - if( SPQRmemberIsInvalid(dec->members[member].parentMember)) { return dec->members[member].parentMember; @@ -952,8 +1000,11 @@ spqr_member findMemberParent( return parent_representative; } -/**< Find the representative parent member of the given member. Note the given member must be representative. - * This version does not perform compression of the union-find tree, and should only be used in debug or asserts. */ +/** Find the representative parent member of the given member. + * + * Note the given member must be representative. + * This version does not perform compression of the union-find tree, and should only be used in debug or asserts. + */ static spqr_member findMemberParentNoCompression( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -973,7 +1024,7 @@ spqr_member findMemberParentNoCompression( return parent_representative; } -/**< Find the child member associated to the given arc. Can only call for virtual arcs.*/ +/** Find the child member associated to the given arc, which must be virtual. */ static spqr_member findArcChildMember( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -989,8 +1040,10 @@ spqr_member findArcChildMember( return representative; } -/**< Find the child member associated to the given arc. Can only call for virtual arcs. - * This version does not compress the union-find tree and should only be used for debugging and asserts. */ +/** Find the child member associated to the given virtual arc. + * + * This version does not compress the union-find tree and should only be used for debugging and asserts. + */ static spqr_member findArcChildMemberNoCompression( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -1005,7 +1058,7 @@ spqr_member findArcChildMemberNoCompression( return representative; } -/**< Checks if the arc has a child member */ +/** Checks if the arc has a child member */ static SCIP_Bool arcIsChildMarker( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -1019,7 +1072,7 @@ SCIP_Bool arcIsChildMarker( return SPQRmemberIsValid(dec->arcs[arc].childMember); } -/**< Check whether the given arc is a tree arc or not, i.e. whether it belongs to a (virtual) row or column or not */ +/** Check whether the given arc is a tree arc or not, i.e. whether it belongs to a (virtual) row or column or not */ static SCIP_Bool arcIsTree( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -1033,7 +1086,7 @@ SCIP_Bool arcIsTree( return SPQRelementIsRow(dec->arcs[arc].element); } -/** Output data structure that stores both the arc's sign and representative*/ +/** Output data structure that stores both the arc's sign and representative */ typedef struct { spqr_arc representative; @@ -1042,8 +1095,10 @@ typedef struct #ifndef NDEBUG -/**< Check if an arc is a representative in the signed union-find data structure for arc directions. In each member, - * exactly one arc is the representative. */ +/** Check if an arc is a representative in the signed union-find data structure for arc directions. + * + * In each member, exactly one arc is the representative. + */ static SCIP_Bool arcIsRepresentative( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -1059,7 +1114,7 @@ SCIP_Bool arcIsRepresentative( #endif -/**< Find an arcs representative and its direction in the signed union-find data structure for arcs */ +/** Find an arcs representative and its direction in the signed union-find data structure for arcs. */ static ArcSign findArcSign( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -1075,7 +1130,7 @@ ArcSign findArcSign( SCIP_Bool totalReversed = dec->arcs[current].reversed; //traverse down tree to find the root - while( SPQRarcIsValid(next = dec->arcs[current].representative)) + while( SPQRarcIsValid(next = dec->arcs[current].representative) ) { current = next; assert(current < dec->memArcs); @@ -1089,7 +1144,7 @@ ArcSign findArcSign( SCIP_Bool currentReversed = totalReversed != dec->arcs[root].reversed; //update all pointers along path to point to root, flattening the tree - while( SPQRarcIsValid(next = dec->arcs[current].representative)) + while( SPQRarcIsValid(next = dec->arcs[current].representative) ) { SCIP_Bool wasReversed = dec->arcs[current].reversed; @@ -1107,8 +1162,10 @@ ArcSign findArcSign( return sign; } -/**< Find an arcs representative and its direction in the signed union-find data structure for arcs. - * This version does not compress the union-find tree and should only be used in debug and asserts. */ +/** Find an arcs representative and its direction in the signed union-find data structure for arcs. + * + * This version does not compress the union-find tree and should only be used in debug and asserts. + */ static ArcSign findArcSignNoCompression( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -1137,7 +1194,7 @@ ArcSign findArcSignNoCompression( return sign; } -/**< Finds the arcs head, taking into account whether it is reversed by the signed union-find data structure.*/ +/** Finds the arcs head, taking into account whether it is reversed by the signed union-find data structure. */ static spqr_node findEffectiveArcHead( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -1155,7 +1212,7 @@ spqr_node findEffectiveArcHead( } } -/**< Finds the arcs tail, taking into account whether it is reversed by the signed union-find data structure.*/ +/** Finds the arcs tail, taking into account whether it is reversed by the signed union-find data structure. */ static spqr_node findEffectiveArcTail( SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -1172,8 +1229,11 @@ spqr_node findEffectiveArcTail( return findArcTail(dec, arc); } } -/**< Finds the arcs head, taking into account whether it is reversed by the signed union-find data structure. - * This version does not compress the union-find tree and should only be used in debug and asserts. */ + +/** Finds the arcs head, taking into account whether it is reversed by the signed union-find data structure. + * + * This version does not compress the union-find tree and should only be used in debug and asserts. + */ static spqr_node findEffectiveArcHeadNoCompression( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -1181,6 +1241,7 @@ spqr_node findEffectiveArcHeadNoCompression( ) { assert(dec); + if( findArcSignNoCompression(dec, arc).reversed ) { return findArcTailNoCompression(dec, arc); @@ -1191,8 +1252,10 @@ spqr_node findEffectiveArcHeadNoCompression( } } -/**< Finds the arcs tail, taking into account whether it is reversed by the signed union-find data structure. - * This version does not compress the union-find tree and should only be used in debug and asserts. */ +/** Finds the arcs tail, taking into account whether it is reversed by the signed union-find data structure. + * + * This version does not compress the union-find tree and should only be used in debug and asserts. + */ static spqr_node findEffectiveArcTailNoCompression( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -1210,8 +1273,10 @@ spqr_node findEffectiveArcTailNoCompression( } } -/**< Merges the sign union-find structures for two arc sets. If reflectRelative is set to true then all arcs of the - * represented by the second arc are reversed w.r.t. their current orientation. Otherwise, all arcs keep the same +/** Merges the sign union-find structures for two arc sets. + * + * If reflectRelative is set to true then all arcs of the represented by the second arc are reversed + * w.r.t. their current orientation. Otherwise, all arcs keep the same * reversed status with respect to the root node of the union find tree. */ static @@ -1254,8 +1319,10 @@ spqr_arc mergeArcSigns( return first; } -/**< Checks whether an arc is reversed for arcs in non-rigid members. For non-rigid members, we do not use the - * union-find datastructure, as we can get away without it. */ +/** Checks whether an arc is reversed for arcs in non-rigid members. + * + * For non-rigid members, we do not use the union-find datastructure, as we can get away without it. + */ static SCIP_Bool arcIsReversedNonRigid( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -1269,7 +1336,7 @@ SCIP_Bool arcIsReversedNonRigid( return dec->arcs[arc].reversed; } -/**< Gets the element (row/column) associated to the given arc */ +/** Gets the element (row/column) associated to the given arc. */ static spqr_element arcGetElement( const SCIP_NETMATDECDATA* dec, /**< The network decomposition */ @@ -1283,7 +1350,7 @@ spqr_element arcGetElement( return dec->arcs[arc].element; } -/**< Check whether the network matrix decomposition contains an edge for the given row index */ +/** Check whether the network matrix decomposition contains an edge for the given row index. */ static SCIP_Bool netMatDecDataContainsRow( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1292,10 +1359,11 @@ SCIP_Bool netMatDecDataContainsRow( { assert(SPQRrowIsValid(row) && row < dec->memRows); assert(dec); + return SPQRarcIsValid(dec->rowArcs[row]); } -/**< Check whether the network matrix decomposition contains an edge for the given column index */ +/** Check whether the network matrix decomposition contains an edge for the given column index. */ static SCIP_Bool netMatDecDataContainsColumn( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1304,10 +1372,11 @@ SCIP_Bool netMatDecDataContainsColumn( { assert(SPQRcolIsValid(column) && column < dec->memColumns); assert(dec); + return SPQRarcIsValid(dec->columnArcs[column]); } -/**< Associate the given arc to the given column in the network matrix decomposition. */ +/** Associate the given arc to the given column in the network matrix decomposition. */ static void setDecompositionColumnArc( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1318,10 +1387,11 @@ void setDecompositionColumnArc( assert(SPQRcolIsValid(col) && col < dec->memColumns); assert(dec); assert(SPQRarcIsValid(arc)); + dec->columnArcs[col] = arc; } -/**< Associate the given arc to the given row in the network matrix decomposition. */ +/** Associate the given arc to the given row in the network matrix decomposition. */ static void setDecompositionRowArc( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1332,10 +1402,11 @@ void setDecompositionRowArc( assert(SPQRrowIsValid(row) && row < dec->memRows); assert(dec); assert(SPQRarcIsValid(arc)); + dec->rowArcs[row] = arc; } -/**< Get the decomposition arc associated to the given column. */ +/** Get the decomposition arc associated to the given column. */ static spqr_arc getDecompositionColumnArc( const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1344,10 +1415,11 @@ spqr_arc getDecompositionColumnArc( { assert(SPQRcolIsValid(col) && col < dec->memColumns); assert(dec); + return dec->columnArcs[col]; } -/**< Get the decomposition arc associated to the given row. */ +/** Get the decomposition arc associated to the given row. */ static spqr_arc getDecompositionRowArc( const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1356,10 +1428,11 @@ spqr_arc getDecompositionRowArc( { assert(SPQRrowIsValid(row) && row < dec->memRows); assert(dec); + return dec->rowArcs[row]; } -/**< Initialize the network matrix decomposition data structure */ +/** Initialize the network matrix decomposition data structure. */ static SCIP_RETCODE netMatDecDataCreate( BMS_BLKMEM* blkmem, /**< Block memory */ @@ -1372,7 +1445,7 @@ SCIP_RETCODE netMatDecDataCreate( assert(pdec); assert(!*pdec); - SCIP_ALLOC(BMSallocBlockMemory(blkmem, pdec)); + SCIP_ALLOC( BMSallocBlockMemory(blkmem, pdec) ); SCIP_NETMATDECDATA* dec = *pdec; dec->env = blkmem; @@ -1382,7 +1455,7 @@ SCIP_RETCODE netMatDecDataCreate( assert(initialMemArcs > 0); dec->memArcs = initialMemArcs; dec->numArcs = 0; - SCIP_ALLOC(BMSallocBlockMemoryArray(blkmem, &dec->arcs, dec->memArcs)); + SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &dec->arcs, dec->memArcs) ); for( spqr_arc i = 0; i < dec->memArcs; ++i ) { dec->arcs[i].arcListNode.next = i + 1; @@ -1398,7 +1471,7 @@ SCIP_RETCODE netMatDecDataCreate( assert(initialMemMembers > 0); dec->memMembers = initialMemMembers; dec->numMembers = 0; - SCIP_ALLOC(BMSallocBlockMemoryArray(blkmem, &dec->members, dec->memMembers)); + SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &dec->members, dec->memMembers) ); } //Initialize node array data @@ -1407,13 +1480,13 @@ SCIP_RETCODE netMatDecDataCreate( assert(initialMemNodes > 0); dec->memNodes = initialMemNodes; dec->numNodes = 0; - SCIP_ALLOC(BMSallocBlockMemoryArray(blkmem, &dec->nodes, dec->memNodes)); + SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &dec->nodes, dec->memNodes) ); } //Initialize mappings for rows { dec->memRows = nrows; - SCIP_ALLOC(BMSallocBlockMemoryArray(blkmem, &dec->rowArcs, dec->memRows)); + SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &dec->rowArcs, dec->memRows) ); for( int i = 0; i < dec->memRows; ++i ) { dec->rowArcs[i] = SPQR_INVALID_ARC; @@ -1422,7 +1495,7 @@ SCIP_RETCODE netMatDecDataCreate( //Initialize mappings for columns { dec->memColumns = ncols; - SCIP_ALLOC(BMSallocBlockMemoryArray(blkmem, &dec->columnArcs, dec->memColumns)); + SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &dec->columnArcs, dec->memColumns) ); for( int i = 0; i < dec->memColumns; ++i ) { dec->columnArcs[i] = SPQR_INVALID_ARC; @@ -1433,11 +1506,12 @@ SCIP_RETCODE netMatDecDataCreate( return SCIP_OKAY; } -/**< Free the network matrix decomposition data structure */ +/** Free the network matrix decomposition data structure */ static void netMatDecDataFree( SCIP_NETMATDECDATA** pdec /**< pointer to the network matrix decomposition to freed */ -){ +) +{ assert(pdec); assert(*pdec); @@ -1451,7 +1525,7 @@ void netMatDecDataFree( BMSfreeBlockMemory(dec->env, pdec); } -/**< Get the first arc of the linked list of arcs contained in the given member */ +/** Get the first arc of the linked list of arcs contained in the given member. */ static spqr_arc getFirstMemberArc( const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1461,10 +1535,11 @@ spqr_arc getFirstMemberArc( assert(dec); assert(SPQRmemberIsValid(member)); assert(member < dec->memMembers); + return dec->members[member].firstArc; } -/**< Given the current arc, get the next arc of the linked list of arcs that are in the same member */ +/** Given the current arc, get the next arc of the linked list of arcs that are in the same member. */ static spqr_arc getNextMemberArc( const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1474,11 +1549,12 @@ spqr_arc getNextMemberArc( assert(dec); assert(SPQRarcIsValid(arc)); assert(arc < dec->memArcs); + arc = dec->arcs[arc].arcListNode.next; return arc; } -/**< Given the current arc, get the previous arc of the linked list of arcs that are in the same member */ +/** Given the current arc, get the previous arc of the linked list of arcs that are in the same member. */ static spqr_arc getPreviousMemberArc( const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1488,11 +1564,12 @@ spqr_arc getPreviousMemberArc( assert(dec); assert(SPQRarcIsValid(arc)); assert(arc < dec->memArcs); + arc = dec->arcs[arc].arcListNode.previous; return arc; } -/**< Adds an arc to the linked list of arcs of the given member */ +/** Adds an arc to the linked list of arcs of the given member. */ static void addArcToMemberArcList( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1520,7 +1597,7 @@ void addArcToMemberArcList( ++( dec->members[member].numArcs ); } -/**< Create a new arc in the network matrix decomposition */ +/** Create a new arc in the network matrix decomposition. */ static SCIP_RETCODE createArc( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1542,7 +1619,7 @@ SCIP_RETCODE createArc( { //Enlarge array, no free nodes in arc list int newSize = 2 * dec->memArcs; - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &dec->arcs, dec->memArcs, newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &dec->arcs, dec->memArcs, newSize) ); for( int i = dec->memArcs + 1; i < newSize; ++i ) { dec->arcs[i].arcListNode.next = i + 1; @@ -1572,7 +1649,7 @@ SCIP_RETCODE createArc( return SCIP_OKAY; } -/**< Create a new arc in the network matrix decomposition that is associated to the given row */ +/** Create a new arc in the network matrix decomposition that is associated to the given row. */ static SCIP_RETCODE createRowArc( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1582,7 +1659,7 @@ SCIP_RETCODE createRowArc( SCIP_Bool reversed /**< Is the arc reversed or not? */ ) { - SCIP_CALL(createArc(dec, member, reversed, pArc)); + SCIP_CALL( createArc(dec, member, reversed, pArc) ); setDecompositionRowArc(dec, row, *pArc); addArcToMemberArcList(dec, *pArc, member); dec->arcs[*pArc].element = SPQRrowToElement(row); @@ -1590,7 +1667,7 @@ SCIP_RETCODE createRowArc( return SCIP_OKAY; } -/**< Create a new arc in the network matrix decomposition that is associated to the given column */ +/** Create a new arc in the network matrix decomposition that is associated to the given column. */ static SCIP_RETCODE createColumnArc( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1600,7 +1677,7 @@ SCIP_RETCODE createColumnArc( SCIP_Bool reversed /**< Is the arc reversed or not? */ ) { - SCIP_CALL(createArc(dec, member, reversed, pArc)); + SCIP_CALL( createArc(dec, member, reversed, pArc) ); setDecompositionColumnArc(dec, column, *pArc); addArcToMemberArcList(dec, *pArc, member); dec->arcs[*pArc].element = SPQRcolumnToElement(column); @@ -1608,12 +1685,12 @@ SCIP_RETCODE createColumnArc( return SCIP_OKAY; } -/**< Create a new member in the network matrix decomposition. */ +/** Create a new member in the network matrix decomposition. */ static SCIP_RETCODE createMember( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ SPQRMemberType type, /**< The SPQR-type of the member */ - spqr_member* pMember /**< out-pointer to the id that is assigned to the member. */ + spqr_member* pMember /**< out-pointer to the id that is assigned to the member */ ) { assert(dec); @@ -1622,7 +1699,7 @@ SCIP_RETCODE createMember( if( dec->numMembers == dec->memMembers ) { int newSize = dec->memMembers * 2; - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &dec->members, dec->memMembers, newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &dec->members, dec->memMembers, newSize) ); dec->memMembers = newSize; } SPQRNetworkDecompositionMember* data = &dec->members[dec->numMembers]; @@ -1640,7 +1717,7 @@ SCIP_RETCODE createMember( return SCIP_OKAY; } -/**< Create a new node in the network matrix decomposition. */ +/** Create a new node in the network matrix decomposition. */ static SCIP_RETCODE createNode( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1651,7 +1728,7 @@ SCIP_RETCODE createNode( if( dec->numNodes == dec->memNodes ) { int newSize = dec->memNodes * 2; - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &dec->nodes, dec->memNodes, newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &dec->nodes, dec->memNodes, newSize) ); dec->memNodes = newSize; } *pNode = dec->numNodes; @@ -1663,7 +1740,7 @@ SCIP_RETCODE createNode( return SCIP_OKAY; } -/**< Remove an arc from the linked list of arcs that are adjacent to a given node */ +/** Remove an arc from the linked list of arcs that are adjacent to a given node. */ static void removeArcFromNodeArcList( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1701,7 +1778,7 @@ void removeArcFromNodeArcList( --( dec->nodes[node].numArcs ); } -/**< Add an arc to the linked list of arcs that are adjacent to a given node */ +/** Add an arc to the linked list of arcs that are adjacent to a given node. */ static void addArcToNodeArcList( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1751,7 +1828,7 @@ void addArcToNodeArcList( } } -/**< Initializes the data structures of the arcs head and tail nodes */ +/** Initializes the data structures of the arcs head and tail nodes. */ static void setArcHeadAndTail( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1764,11 +1841,11 @@ void setArcHeadAndTail( addArcToNodeArcList(dec, arc, tail, FALSE); } -/**< Deinitializes the data structures of the arcs head and tail nodes */ +/** Deinitializes the data structures of the arcs head and tail nodes. */ static void clearArcHeadAndTail( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ - spqr_arc arc /**< The arc to deinitialize */ + spqr_arc arc /**< The arc to deinitialize */ ) { removeArcFromNodeArcList(dec, arc, findArcHead(dec, arc), TRUE); @@ -1777,7 +1854,7 @@ void clearArcHeadAndTail( dec->arcs[arc].tail = SPQR_INVALID_NODE; } -/**< Change the arc's head node to a new node */ +/** Change the arc's head node to a new node. */ static void changeArcHead( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1788,11 +1865,12 @@ void changeArcHead( { assert(nodeIsRepresentative(dec, oldHead)); assert(nodeIsRepresentative(dec, newHead)); + removeArcFromNodeArcList(dec, arc, oldHead, TRUE); addArcToNodeArcList(dec, arc, newHead, TRUE); } -/**< Change the arc's head node to a new node */ +/** Change the arc's head node to a new node. */ static void changeArcTail( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1803,11 +1881,12 @@ void changeArcTail( { assert(nodeIsRepresentative(dec, oldTail)); assert(nodeIsRepresentative(dec, newTail)); + removeArcFromNodeArcList(dec, arc, oldTail, FALSE); addArcToNodeArcList(dec, arc, newTail, FALSE); } -/**< Returns the degree of the given node */ +/** Returns the degree of the given node. */ static int nodeDegree( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1817,10 +1896,11 @@ int nodeDegree( assert(dec); assert(SPQRnodeIsValid(node)); assert(node < dec->memNodes); + return dec->nodes[node].numArcs; } -/**< Get the SPQR-type of the given member */ +/** Get the SPQR-type of the given member. */ static SPQRMemberType getMemberType( const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1831,10 +1911,11 @@ SPQRMemberType getMemberType( assert(SPQRmemberIsValid(member)); assert(member < dec->memMembers); assert(memberIsRepresentative(dec, member)); + return dec->members[member].type; } -/**< Update the SPQR-type of the given member */ +/** Update the SPQR-type of the given member. */ static void updateMemberType( const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1850,7 +1931,7 @@ void updateMemberType( dec->members[member].type = type; } -/**< Returns the virtual arc pointing to the parent member (in the arborescence) of the given member */ +/** Returns the virtual arc pointing to the parent member (in the arborescence) of the given member. */ static spqr_arc markerToParent( const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1861,10 +1942,11 @@ spqr_arc markerToParent( assert(SPQRmemberIsValid(member)); assert(member < dec->memMembers); assert(memberIsRepresentative(dec, member)); + return dec->members[member].markerToParent; } -/**< Updates the parent information of a member that is identified with another another member */ +/** Updates the parent information of a member that is identified with another another member. */ static void updateMemberParentInformation( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1884,7 +1966,7 @@ void updateMemberParentInformation( dec->members[toRemove].parentMember = SPQR_INVALID_MEMBER; } -/**< Returns the virtual arc of the parent member that points to the given member */ +/** Returns the virtual arc of the parent member that points to the given member. */ static spqr_arc markerOfParent( const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1895,10 +1977,11 @@ spqr_arc markerOfParent( assert(SPQRmemberIsValid(member)); assert(member < dec->memMembers); assert(memberIsRepresentative(dec, member)); + return dec->members[member].markerOfParent; } -/**< Returns the number of arcs in the member */ +/** Returns the number of arcs in the member. */ static int getNumMemberArcs( const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1909,31 +1992,37 @@ int getNumMemberArcs( assert(SPQRmemberIsValid(member)); assert(member < dec->memMembers); assert(memberIsRepresentative(dec, member)); + return dec->members[member].numArcs; } -/**< Returns the number of nodes in complete network matrix decomposition */ +/** Returns the number of nodes in complete network matrix decomposition. */ static int getNumNodes( const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */ ) { assert(dec); + return dec->numNodes; } -/**< Returns the number of members in complete network matrix decomposition */ +/** Returns the number of members in complete network matrix decomposition. */ static int getNumMembers( const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */ ) { assert(dec); + return dec->numMembers; } -/**< Creates a standalone parallel member with the given row and columns that is not connected to other members of the - * network matrix decomposition. New arcs are created for the given row and columns */ +/** Creates a standalone parallel member with the given row and columns that is not connected to other members of the + * network matrix decomposition. + * + * New arcs are created for the given row and columns. + */ static SCIP_RETCODE createStandaloneParallel( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1945,24 +2034,28 @@ SCIP_RETCODE createStandaloneParallel( ) { spqr_member member; - SCIP_CALL(createMember(dec, num_columns <= 1 ? SPQR_MEMBERTYPE_LOOP : SPQR_MEMBERTYPE_PARALLEL, &member)); + SCIP_CALL( createMember(dec, num_columns <= 1 ? SPQR_MEMBERTYPE_LOOP : SPQR_MEMBERTYPE_PARALLEL, &member) ); spqr_arc row_arc; - SCIP_CALL(createRowArc(dec, member, &row_arc, row, num_columns <= 1)); + SCIP_CALL( createRowArc(dec, member, &row_arc, row, num_columns <= 1) ); spqr_arc col_arc; for( int i = 0; i < num_columns; ++i ) { - SCIP_CALL(createColumnArc(dec, member, &col_arc, columns[i], reversed[i])); + SCIP_CALL( createColumnArc(dec, member, &col_arc, columns[i], reversed[i]) ); } *pMember = member; ++dec->numConnectedComponents; + return SCIP_OKAY; } -/**< Creates a parallel member with the given row and columns is connected to other members of the - * network matrix decomposition. New arcs are created for the given row and columns */ +/** Creates a parallel member with the given row and columns is connected to other members of the + * network matrix decomposition. + * + * New arcs are created for the given row and columns. + */ static SCIP_RETCODE createConnectedParallel( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -1974,23 +2067,26 @@ SCIP_RETCODE createConnectedParallel( ) { spqr_member member; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &member)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &member) ); spqr_arc row_arc; - SCIP_CALL(createRowArc(dec, member, &row_arc, row, FALSE)); + SCIP_CALL( createRowArc(dec, member, &row_arc, row, FALSE) ); spqr_arc col_arc; for( int i = 0; i < num_columns; ++i ) { - SCIP_CALL(createColumnArc(dec, member, &col_arc, columns[i], reversed[i])); + SCIP_CALL( createColumnArc(dec, member, &col_arc, columns[i], reversed[i]) ); } *pMember = member; return SCIP_OKAY; } -/**< Creates a standalone series member with the given row and columns that is not connected to other members of the - * network matrix decomposition. New arcs are created for the given rows and column */ +/** Creates a standalone series member with the given row and columns that is not connected to other members of the + * network matrix decomposition. + * + * New arcs are created for the given rows and column. + */ static SCIP_RETCODE createStandaloneSeries( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -2002,23 +2098,27 @@ SCIP_RETCODE createStandaloneSeries( ) { spqr_member member; - SCIP_CALL(createMember(dec, numRows <= 1 ? SPQR_MEMBERTYPE_LOOP : SPQR_MEMBERTYPE_SERIES, &member)); + SCIP_CALL( createMember(dec, numRows <= 1 ? SPQR_MEMBERTYPE_LOOP : SPQR_MEMBERTYPE_SERIES, &member) ); spqr_arc colArc; - SCIP_CALL(createColumnArc(dec, member, &colArc, col, FALSE)); + SCIP_CALL( createColumnArc(dec, member, &colArc, col, FALSE) ); spqr_arc rowArc; for( int i = 0; i < numRows; ++i ) { - SCIP_CALL(createRowArc(dec, member, &rowArc, rows[i], !reversed[i])); + SCIP_CALL( createRowArc(dec, member, &rowArc, rows[i], !reversed[i]) ); } *pMember = member; ++dec->numConnectedComponents; + return SCIP_OKAY; } -/**< Creates a series member with the given row and columns that is connected to some other member of the - * network matrix decomposition. New arcs are created for the given rows and column */ +/** Creates a series member with the given row and columns that is connected to some other member of the + * network matrix decomposition. + * + * New arcs are created for the given rows and column. + */ static SCIP_RETCODE createConnectedSeries( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -2030,21 +2130,22 @@ SCIP_RETCODE createConnectedSeries( ) { spqr_member member; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &member)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_SERIES, &member) ); spqr_arc colArc; - SCIP_CALL(createColumnArc(dec, member, &colArc, col, FALSE)); + SCIP_CALL( createColumnArc(dec, member, &colArc, col, FALSE) ); spqr_arc rowArc; for( int i = 0; i < numRows; ++i ) { - SCIP_CALL(createRowArc(dec, member, &rowArc, rows[i], !reversed[i])); + SCIP_CALL( createRowArc(dec, member, &rowArc, rows[i], !reversed[i]) ); } *pMember = member; + return SCIP_OKAY; } -/**< Remove an arc from the linked list containing all arcs of a single member */ +/** Remove an arc from the linked list containing all arcs of a single member. */ static void removeArcFromMemberArcList( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -2058,7 +2159,6 @@ void removeArcFromMemberArcList( if( dec->members[member].numArcs == 1 ) { dec->members[member].firstArc = SPQR_INVALID_ARC; - } else { @@ -2074,19 +2174,20 @@ void removeArcFromMemberArcList( } } - --( dec->members[member].numArcs ); } -/**< Data structure for the algorithms to find fundamental cycle within the network matrix decomposition */ +/** Data structure for the algorithms to find fundamental cycle within the network matrix decomposition */ typedef struct { spqr_arc arc; SCIP_Bool reversed; } FindCycleCall; -/**< Processes a single arc for the algorithm to find cycles in the network matrix decomposition: - * if virtual, pushes it on the callstack, if non-virtual, adds it to the found cycle. */ +/** Processes a single arc for the algorithm to find cycles in the network matrix decomposition. + * + * if virtual, pushes it on the callstack, if non-virtual, adds it to the found cycle + */ static void process_arc( spqr_row* cyclearcs, /**< The found cycle so far */ @@ -2100,6 +2201,7 @@ void process_arc( ) { assert(arcIsTree(dec, arc)); + if( !arcIsChildMarker(dec, arc)) { spqr_member current_member = findArcMemberNoCompression(dec, arc); @@ -2132,15 +2234,18 @@ void process_arc( } } -/**< Find the fundamental path of a cycle. This is a slow method and only intended for debugging and testing. */ +/** Find the fundamental path of a cycle. + * + * This is a slow method and only intended for debugging and testing. + */ static int decompositionGetFundamentalCycleRows( const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ spqr_col column, /**< The column to find the fundamental path for */ spqr_row* output, /**< preallocated array to store fundamental path in. Must have at least - **< the number of rows in the decomposition allocated */ + * the number of rows in the decomposition allocated */ SCIP_Bool* computedSignStorage /**< Boolean array for storage of whether the path occurs forwards or - **< backwards. Must have at least the same size as output array. */ + * backwards. Must have at least the same size as output array. */ ) { /*Basic idea; for each component, do a dfs over the tree formed by the row arcs to find the relevant edges. @@ -2315,11 +2420,13 @@ int decompositionGetFundamentalCycleRows( BMSfreeBlockMemoryArray(dec->env, &pathSearchCallStack, dec->numNodes); BMSfreeBlockMemoryArray(dec->env, &nodeVisited, dec->numNodes); BMSfreeBlockMemoryArray(dec->env, &callStack, dec->memRows); + return num_rows; } -/**< Given a cycle (e.g. a matrix column), checks if the column's cycle matches the cycle in the - * network matrix decomposition */ +/** Given a cycle (e.g. a matrix column), checks if the column's cycle matches the cycle in the + * network matrix decomposition. + */ static SCIP_Bool netMatDecDataVerifyCycle( BMS_BUFMEM* bufmem, /**< Buffer memory */ @@ -2329,9 +2436,9 @@ SCIP_Bool netMatDecDataVerifyCycle( const double* nonzvals, /**< Array with the nonzero entry values of the column's row indices */ int num_rows, /**< The number of nonzeros in the column */ int* pathrowstorage, /**< Temporary storage vector for storing the fundamental path. Must have - **< at least as many entries allocated as the number of rows in dec. */ + * at least as many entries allocated as the number of rows in dec. */ SCIP_Bool* pathsignstorage /**< Temporary storage for the fundamental path directions. Must have - **< at least as many entries allocated as the number of rows in dec. */ + * at least as many entries allocated as the number of rows in dec. */ ) { int num_found_rows = decompositionGetFundamentalCycleRows(dec, column, pathrowstorage, pathsignstorage); @@ -2354,7 +2461,7 @@ SCIP_Bool netMatDecDataVerifyCycle( if( BMSallocBufferMemoryArray(bufmem, &pathRowReversed, num_rows) == NULL ) { - BMSfreeBufferMemoryArray(bufmem,&pathRow); + BMSfreeBufferMemoryArray(bufmem, &pathRow); return FALSE; } for( int i = 0; i < num_rows; ++i ) @@ -2362,22 +2469,22 @@ SCIP_Bool netMatDecDataVerifyCycle( pathRow[i] = pathrowstorage[i]; pathRowReversed[i] = pathsignstorage[i] ? 1 : 0; } - SCIPsortIntInt(pathRow,pathRowReversed,num_rows); + SCIPsortIntInt(pathRow, pathRowReversed, num_rows); spqr_row * secondPathRow; int * secondPathRowReversed; if( BMSallocBufferMemoryArray(bufmem, &secondPathRow, num_rows) == NULL ) { - BMSfreeBufferMemoryArray(bufmem,&pathRow); - BMSfreeBufferMemoryArray(bufmem,&pathRowReversed); + BMSfreeBufferMemoryArray(bufmem, &pathRow); + BMSfreeBufferMemoryArray(bufmem, &pathRowReversed); return FALSE; } if( BMSallocBufferMemoryArray(bufmem, &secondPathRowReversed, num_rows) == NULL ) { - BMSfreeBufferMemoryArray(bufmem,&pathRow); - BMSfreeBufferMemoryArray(bufmem,&pathRowReversed); - BMSfreeBufferMemoryArray(bufmem,&secondPathRow); + BMSfreeBufferMemoryArray(bufmem, &pathRow); + BMSfreeBufferMemoryArray(bufmem, &pathRowReversed); + BMSfreeBufferMemoryArray(bufmem, &secondPathRow); return FALSE; } for( int i = 0; i < num_rows; ++i ) @@ -2386,7 +2493,7 @@ SCIP_Bool netMatDecDataVerifyCycle( secondPathRowReversed[i] = nonzvals[i] < 0.0 ? 1 : 0; } - SCIPsortIntInt(secondPathRow,secondPathRowReversed,num_rows); + SCIPsortIntInt(secondPathRow, secondPathRowReversed, num_rows); SCIP_Bool good = TRUE; for( int i = 0; i < num_rows; ++i ) @@ -2401,10 +2508,11 @@ SCIP_Bool netMatDecDataVerifyCycle( BMSfreeBufferMemoryArray(bufmem, &secondPathRow); BMSfreeBufferMemoryArray(bufmem, &pathRowReversed); BMSfreeBufferMemoryArray(bufmem, &pathRow); + return good; } -/**< Returns the largest member id that is currently in the decomposition */ +/** Returns the largest member id that is currently in the decomposition. */ static spqr_member largestMemberID( const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */ @@ -2413,7 +2521,7 @@ spqr_member largestMemberID( return dec->numMembers; } -/**< Returns the largest arc id that is currently in the decomposition */ +/** Returns the largest arc id that is currently in the decomposition. */ static spqr_arc largestArcID( const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */ @@ -2422,7 +2530,7 @@ spqr_arc largestArcID( return dec->numArcs; } -/**< Returns the largest node id that is currently in the decomposition */ +/** Returns the largest node id that is currently in the decomposition. */ static spqr_node largestNodeID( const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */ @@ -2431,7 +2539,7 @@ spqr_node largestNodeID( return dec->numNodes; } -/**< Returns the number of SPQR trees in the SPQR forest, i.e. the number of connected components. */ +/** Returns the number of SPQR trees in the SPQR forest, i.e. the number of connected components. */ static int numConnectedComponents( const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */ @@ -2440,7 +2548,7 @@ int numConnectedComponents( return dec->numConnectedComponents; } -/**< Creates a child marker in the network decomposition */ +/** Creates a child marker in the network decomposition. */ static SCIP_RETCODE createChildMarker( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -2451,15 +2559,16 @@ SCIP_RETCODE createChildMarker( SCIP_Bool reversed /**< Sets the reversed field of the arc */ ) { - SCIP_CALL(createArc(dec, member, reversed, pArc)); + SCIP_CALL( createArc(dec, member, reversed, pArc) ); dec->arcs[*pArc].element = isTree ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; dec->arcs[*pArc].childMember = child; addArcToMemberArcList(dec, *pArc, member); + return SCIP_OKAY; } -/**< Creates a parent marker in the network decomposition */ +/** Creates a parent marker in the network decomposition. */ static SCIP_RETCODE createParentMarker( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -2472,7 +2581,7 @@ SCIP_RETCODE createParentMarker( ) { - SCIP_CALL(createArc(dec, member, reversed, arc)); + SCIP_CALL( createArc(dec, member, reversed, arc) ); dec->arcs[*arc].element = isTree ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; addArcToMemberArcList(dec, *arc, member); @@ -2480,10 +2589,11 @@ SCIP_RETCODE createParentMarker( dec->members[member].parentMember = parent; dec->members[member].markerOfParent = parentMarker; dec->members[member].markerToParent = *arc; + return SCIP_OKAY; } -/**< Creates a child-marker parent-marker pair in the network decomposition */ +/** Creates a child-marker parent-marker pair in the network decomposition. */ static SCIP_RETCODE createMarkerPair( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -2495,18 +2605,16 @@ SCIP_RETCODE createMarkerPair( ) { spqr_arc parentToChildMarker = SPQR_INVALID_ARC; - SCIP_CALL( - createChildMarker(dec, parentMember, childMember, parentIsTree, &parentToChildMarker, childMarkerReversed)); + SCIP_CALL( createChildMarker(dec, parentMember, childMember, parentIsTree, &parentToChildMarker, childMarkerReversed) ); spqr_arc childToParentMarker = SPQR_INVALID_ARC; - SCIP_CALL( - createParentMarker(dec, childMember, !parentIsTree, parentMember, parentToChildMarker, &childToParentMarker, - parentMarkerReversed)); + SCIP_CALL( createParentMarker(dec, childMember, !parentIsTree, parentMember, parentToChildMarker, + &childToParentMarker, parentMarkerReversed) ); return SCIP_OKAY; } -/**< Creates a child-marker parent-marker pair in the network decomposition, and returns the assigned arc id's */ +/** Creates a child-marker parent-marker pair in the network decomposition, and returns the assigned arc id's. */ static SCIP_RETCODE createMarkerPairWithReferences( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -2519,15 +2627,16 @@ SCIP_RETCODE createMarkerPairWithReferences( spqr_arc* childToParent /**< Output-pointer containing arc id of the arc in the child member */ ) { - SCIP_CALL(createChildMarker(dec, parentMember, childMember, parentIsTree, parentToChild, childMarkerReversed)); - SCIP_CALL(createParentMarker(dec, childMember, !parentIsTree, parentMember, *parentToChild, childToParent, - parentMarkerReversed)); + SCIP_CALL( createChildMarker(dec, parentMember, childMember, parentIsTree, parentToChild, childMarkerReversed) ); + SCIP_CALL( createParentMarker(dec, childMember, !parentIsTree, parentMember, *parentToChild, childToParent, + parentMarkerReversed) ); return SCIP_OKAY; } -/**< Moves a given arc from one member to another, updating the linked lists it is contained in and the parent/child - * information of the relevant members. */ +/** Moves a given arc from one member to another, updating the linked lists it is contained in and the parent/child + * information of the relevant members. + */ static void moveArcToNewMember( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -2569,7 +2678,7 @@ void moveArcToNewMember( } } -/**< Merges the arc linked list of two members into one linked list. */ +/** Merges the arc linked list of two members into one linked list. */ static void mergeMemberArcList( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -2597,7 +2706,7 @@ void mergeMemberArcList( dec->members[toRemove].firstArc = SPQR_INVALID_ARC; } -/**< Changes the type of a member from loop to series */ +/** Changes the type of a member from loop to series. */ static void changeLoopToSeries( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -2615,7 +2724,7 @@ void changeLoopToSeries( dec->members[member].type = SPQR_MEMBERTYPE_SERIES; } -/**< Changes the type of a member from loop to parallel */ +/** Changes the type of a member from loop to parallel. */ static void changeLoopToParallel( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -2633,8 +2742,9 @@ void changeLoopToParallel( dec->members[member].type = SPQR_MEMBERTYPE_PARALLEL; } -/**< Checks if the network decomposition is minimal, i.e. if it does not contain two adjacent parlalel or series - **< members */ +/** Checks if the network decomposition is minimal, i.e. if it does not contain two adjacent parallel or series + * members. + */ static SCIP_Bool netMatDecDataIsMinimal( const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */ @@ -2664,7 +2774,7 @@ SCIP_Bool netMatDecDataIsMinimal( return isMinimal; } -/**< Decreases the count of the number of connected components in the network matrix decomposition */ +/** Decreases the count of the number of connected components in the network matrix decomposition. */ static void decreaseNumConnectedComponents( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -2675,8 +2785,9 @@ void decreaseNumConnectedComponents( assert(dec->numConnectedComponents >= 1); } -/**< Reorders the arborescence of the SPQR that contains a given member so that the given member becomes the new root - * of the arborescence. */ +/** Reorders the arborescence of the SPQR that contains a given member so that the given member becomes the new root + * of the arborescence. + */ static void reorderComponent( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -2727,9 +2838,11 @@ void reorderComponent( } } -/**< Deletes the SPQR tree (connected component) containing the given component rows and columns. +/** Deletes the SPQR tree (connected component) containing the given component rows and columns. + * * Note that the implementation of this method does not actually modify the SPQR tree, but rather unlinks the rows and - * columns from the relevant arcs. Thus, this method is a bit hacky and should be used with care. */ + * columns from the relevant arcs. Thus, this method is a bit hacky and should be used with care. + */ static void netMatDecDataRemoveComponent( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -2745,7 +2858,7 @@ void netMatDecDataRemoveComponent( for( int i = 0; i < numRows; ++i ) { spqr_row row = componentRows[i]; - if( SPQRarcIsValid(dec->rowArcs[row])) + if( SPQRarcIsValid(dec->rowArcs[row]) ) { dec->rowArcs[row] = SPQR_INVALID_ARC; } @@ -2754,7 +2867,7 @@ void netMatDecDataRemoveComponent( for( int i = 0; i < numCols; ++i ) { spqr_col col = componentCols[i]; - if( SPQRarcIsValid(dec->columnArcs[col])) + if( SPQRarcIsValid(dec->columnArcs[col]) ) { dec->columnArcs[col] = SPQR_INVALID_ARC; } @@ -2763,7 +2876,7 @@ void netMatDecDataRemoveComponent( #ifdef SCIP_DEBUG -/**< Converts the members type to an associated character */ //Debugging functions to print the decomposition +/** Converts the members type to an associated character */ //Debugging functions to print the decomposition static char typeToChar( SPQRMemberType type /**< The member type */ @@ -2784,7 +2897,7 @@ char typeToChar( } } -/**< Prints an arc in DOT format */ +/** Prints an arc in DOT format */ static void arcToDot( FILE* stream, /**< The stream to write the arc to */ @@ -2804,9 +2917,9 @@ void arcToDot( int arc_name = arc; - if (markerToParent(dec, member) == arc) + if( markerToParent(dec, member) == arc ) { - if (useElementNames) + if( useElementNames ) { arc_name = -1; } @@ -2815,11 +2928,12 @@ void arcToDot( fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_tail); fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_head); fprintf(stream, " %c_p_%d [style=dashed];\n", type, member); - } else if (arcIsMarker(dec, arc)) + } + else if( arcIsMarker(dec, arc) ) { spqr_member child = findArcChildMemberNoCompression(dec, arc); char childType = typeToChar(getMemberType(dec, child)); - if (useElementNames) + if( useElementNames ) { arc_name = -1; } @@ -2829,15 +2943,17 @@ void arcToDot( fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_head); fprintf(stream, " %c_c_%d [style=dotted];\n", type, child); fprintf(stream, " %c_p_%d -> %c_c_%d [style=dashed,dir=forward];\n", childType, child, type, child); - } else + } + else { - if (useElementNames) + if( useElementNames ) { spqr_element element = dec->arcs[arc].element; - if (SPQRelementIsRow(element)) + if( SPQRelementIsRow(element) ) { arc_name = (int) SPQRelementToRow(element); - } else + } + else { arc_name = (int) SPQRelementToColumn(element); } @@ -2850,23 +2966,25 @@ void arcToDot( } } -/**< Outputs the network decomposition as a DOT file */ +/** Outputs the network decomposition as a DOT file. */ static void decompositionToDot( FILE* stream, /**< The stream to write to */ const SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ SCIP_Bool useElementNames /**< If TRUE, prints the corresponding row/column index. If FALSE, - **< prints the spqr_arc id instead. */ + * prints the spqr_arc id instead. */ ) { fprintf(stream, "//decomposition\ndigraph decomposition{\n compound = TRUE;\n"); - for (spqr_member member = 0; member < dec->numMembers; ++member) + for( spqr_member member = 0; member < dec->numMembers; ++member ) { - if (!memberIsRepresentative(dec, member)) continue; + if( !memberIsRepresentative(dec, member) ) + continue; fprintf(stream, " subgraph member_%d{\n", member); - switch (getMemberType(dec, member)) + switch( getMemberType(dec, member) ) { - case SPQR_MEMBERTYPE_RIGID: { + case SPQR_MEMBERTYPE_RIGID: + { spqr_arc first_arc = getFirstMemberArc(dec, member); spqr_arc arc = first_arc; do @@ -2875,35 +2993,41 @@ void decompositionToDot( unsigned long arcTail = (unsigned long) findEffectiveArcTailNoCompression(dec, arc); arcToDot(stream, dec, arc, arcHead, arcTail, useElementNames); arc = getNextMemberArc(dec, arc); - } while (arc != first_arc); + } + while( arc != first_arc ); break; } case SPQR_MEMBERTYPE_LOOP: - case SPQR_MEMBERTYPE_PARALLEL: { + case SPQR_MEMBERTYPE_PARALLEL: + { spqr_arc first_arc = getFirstMemberArc(dec, member); spqr_arc arc = first_arc; do { - if (arcIsReversedNonRigid(dec, arc)) + if( arcIsReversedNonRigid(dec, arc) ) { arcToDot(stream, dec, arc, 1, 0, useElementNames); - } else + } + else { arcToDot(stream, dec, arc, 0, 1, useElementNames); } arc = getNextMemberArc(dec, arc); - } while (arc != first_arc); + } + while( arc != first_arc ); break; } - case SPQR_MEMBERTYPE_SERIES: { + case SPQR_MEMBERTYPE_SERIES: + { unsigned long i = 0; unsigned long num_member_arcs = (unsigned long) getNumMemberArcs(dec, member); spqr_arc first_arc = getFirstMemberArc(dec, member); spqr_arc arc = first_arc; - do { + do + { unsigned long head = i; unsigned long tail = (i + 1) % num_member_arcs; - if (arcIsReversedNonRigid(dec, arc)) + if( arcIsReversedNonRigid(dec, arc) ) { unsigned long temp = head; head = tail; @@ -2912,7 +3036,8 @@ void decompositionToDot( arcToDot(stream, dec, arc, head, tail, useElementNames); arc = getNextMemberArc(dec, arc); i++; - } while (arc != first_arc); + } + while( arc != first_arc ); break; } case SPQR_MEMBERTYPE_UNASSIGNED: @@ -2970,7 +3095,6 @@ SCIP_RETCODE mergeGivenMemberIntoParent( mergeNodeArcList(dec, newNode, toRemoveFrom); } - spqr_member newMember = mergeMembers(dec, member, parent); spqr_member toRemoveFrom = newMember == member ? parent : member; mergeMemberArcList(dec, newMember, toRemoveFrom); @@ -2980,10 +3104,11 @@ SCIP_RETCODE mergeGivenMemberIntoParent( } updateMemberType(dec, newMember, SPQR_MEMBERTYPE_RIGID); *mergedMember = newMember; + return SCIP_OKAY; } -/**< Returns the maximum of two values. */ +/** Returns the maximum of two values. */ static int maxValue( int a, @@ -2993,13 +3118,13 @@ int maxValue( return ( a > b ) ? a : b; } -/** ---------- START functions for column addition -------------------------------------------------------------------*/ +/* ---------- START functions for column addition ------------------------------------------------------------------- */ -/**< Path arcs are all those arcs that correspond to nonzeros of the column to be added */ +/** Path arcs are all those arcs that correspond to nonzeros of the column to be added. */ typedef int path_arc_id; #define INVALID_PATH_ARC (-1) -/**< Returns true if the path arc is invalid */ +/** Returns true if the path arc is invalid. */ static SCIP_Bool pathArcIsInvalid( const path_arc_id arc /**< The path arc to check */ @@ -3008,7 +3133,7 @@ SCIP_Bool pathArcIsInvalid( return arc < 0; } -/**< Returns true if the path arc is valid */ +/** Returns true if the path arc is valid. */ static SCIP_Bool pathArcIsValid( const path_arc_id arc /**< The path arc to check */ @@ -3017,26 +3142,30 @@ SCIP_Bool pathArcIsValid( return !pathArcIsInvalid(arc); } -/** A forward linked list-node for the path arcs. Contains a pointer to the next path arc in the member graph and the +/** A forward linked list-node for the path arcs. + * + * Contains a pointer to the next path arc in the member graph and the * next path arc in the SPQR tree. We additionally store copies of the corresponding arc's node indices. */ typedef struct { - spqr_arc arc; /**< The arc corresponding to the path arc*/ - spqr_node arcHead; /**< A copy of the arc's head node index */ - spqr_node arcTail; /**< A copy of the arc's tail node index */ - path_arc_id nextMember; /**< Array index of the next path arc in the member path arc linked list*/ - path_arc_id nextOverall; /**< Array index of the next path arc in the total path arc linked list */ - SCIP_Bool reversed; /**< Is the path arc occuring forwards or backwards in the path? + spqr_arc arc; /**< The arc corresponding to the path ar c*/ + spqr_node arcHead; /**< A copy of the arc's head node index */ + spqr_node arcTail; /**< A copy of the arc's tail node index */ + path_arc_id nextMember; /**< Array index of the next path arc in the member path arc linked list */ + path_arc_id nextOverall; /**< Array index of the next path arc in the total path arc linked list */ + SCIP_Bool reversed; /**< Is the path arc occuring forwards or backwards in the path? * Corresponds to the sign of the nonzero */ } PathArcListNode; -/**< Index for the reduced members, which are the members of the sub-SPQR tree containing all path arcs. - **< Note that these are indexed separately from the members of the SPQR tree! */ +/** Index for the reduced members, which are the members of the sub-SPQR tree containing all path arcs. + * + * Note that these are indexed separately from the members of the SPQR tree! + */ typedef int reduced_member_id; #define INVALID_REDUCED_MEMBER (-1) -/**< Returns true if the reduced member is invalid */ +/** Returns true if the reduced member is invalid. */ static SCIP_Bool reducedMemberIsInvalid( const reduced_member_id id /**< The reduced member to check */ @@ -3045,6 +3174,7 @@ SCIP_Bool reducedMemberIsInvalid( return id < 0; } +/** Returns true if the reduced member is valid. */ static SCIP_Bool reducedMemberIsValid( const reduced_member_id id /**< The reduced member to check */ @@ -3053,11 +3183,12 @@ SCIP_Bool reducedMemberIsValid( return !reducedMemberIsInvalid(id); } -/**< Array index for the children of a reduced member */ +/** Array index for the children of a reduced member */ typedef int children_idx; -/**< Type of the member, signifies to what degree we processed the member and how to treat with it when updating the - * graph */ +/** Type of the member, signifies to what degree we processed the member and how to treat with it when updating the + * graph. + */ typedef enum { REDUCEDMEMBER_TYPE_UNASSIGNED = 0, /**< We have not yet decided if the reduced member contains a valid path */ @@ -3069,7 +3200,7 @@ typedef enum * to form a valid path with the other members. */ } ReducedMemberType; -/**< Defines the structure of the path in the reduced member */ +/** Defines the structure of the path in the reduced member */ typedef enum { INTO_HEAD = 0, /**< The directed path goes into the head of the virtual arc */ @@ -3078,7 +3209,7 @@ typedef enum OUT_TAIL = 3 /**< The directed path goes out of the tail of the virtual arc */ } MemberPathType; -/**< Check if the path type is into */ +/** Check if the path type is into. */ static SCIP_Bool isInto( MemberPathType type /**< The path type to check */ @@ -3086,7 +3217,8 @@ SCIP_Bool isInto( { return type == INTO_HEAD || type == INTO_TAIL; } -/**< Check if the path end node is the head or tail node of the corresponding arc */ + +/** Check if the path end node is the head or tail node of the corresponding arc. */ static SCIP_Bool isHead( MemberPathType type /**< The path type to check */ @@ -3095,133 +3227,136 @@ SCIP_Bool isHead( return type == INTO_HEAD || type == OUT_HEAD; } -/**< A struct that keeps track of the relevant data for the members that are in the subtree given by the specified row - * arcs. We typically call these 'reduced' members (members of the reduced tree). */ +/** A struct that keeps track of the relevant data for the members that are in the subtree given by the specified row + * arcs. + * + * We typically call these 'reduced' members (members of the reduced tree). + */ typedef struct { - spqr_member member; /**< The id of the member */ - spqr_member rootMember; /**< The root member of the arborescence that contains this member */ - int depth; /**< The depth of this member in the arborescence */ - ReducedMemberType type; /**< The type of the member */ - reduced_member_id parent; /**< The reduced member id of the parent of this reduced member */ + spqr_member member; /**< The id of the member */ + spqr_member rootMember; /**< The root member of the arborescence that contains this member */ + int depth; /**< The depth of this member in the arborescence */ + ReducedMemberType type; /**< The type of the member */ + reduced_member_id parent; /**< The reduced member id of the parent of this reduced member */ - children_idx firstChild; /**< The index of the first child in the children array. */ - children_idx numChildren; /**< The number of children in the arborescence of this reduced member */ + children_idx firstChild; /**< The index of the first child in the children array. */ + children_idx numChildren; /**< The number of children in the arborescence of this reduced member */ - path_arc_id firstPathArc; /**< Head of the linked list containing the path arcs */ - int numPathArcs; /**< The number of path arcs in the linked list */ + path_arc_id firstPathArc; /**< Head of the linked list containing the path arcs */ + int numPathArcs; /**< The number of path arcs in the linked list */ - SCIP_Bool reverseArcs; /**< Will the arcs in this component be reversed? */ - spqr_node rigidPathStart; /**< The start node of the path. Only used for Rigid/3-connected nodes */ - spqr_node rigidPathEnd; /**< The end node of the path. Only used for Rigid/3-connected nodes */ + SCIP_Bool reverseArcs; /**< Will the arcs in this component be reversed? */ + spqr_node rigidPathStart; /**< The start node of the path. Only used for Rigid/3-connected nodes */ + spqr_node rigidPathEnd; /**< The end node of the path. Only used for Rigid/3-connected nodes */ - SCIP_Bool pathBackwards; /**< Indicates if the path direction is reversed with respect to the + SCIP_Bool pathBackwards; /**< Indicates if the path direction is reversed with respect to the * default orientation within series and parallel members. */ - int numPropagatedChildren; /**< Counts the number of children that are cycles that propagated to this - * reduced member */ - int componentIndex; /**< Stores the index of the component of the SPQR forest that this reduced + int numPropagatedChildren; /**< Counts the number of children that are cycles that propagated to this + * reduced member */ + int componentIndex; /**< Stores the index of the component of the SPQR forest that this reduced * member is contained in. */ - MemberPathType pathType; /**< The type of the path with respect to the virtual arc*/ - reduced_member_id nextPathMember; /**< Indicates the id of the next reduced member in the path during merging. + MemberPathType pathType; /**< The type of the path with respect to the virtual arc */ + reduced_member_id nextPathMember; /**< Indicates the id of the next reduced member in the path during merging. * During merging, the SPQR tree must be a path itself. */ - SCIP_Bool nextPathMemberIsParent; /**< Indicates if the next reduced member in the path is a parent of - * this member */ - spqr_arc pathSourceArc; /**< The virtual arc from where the path originates */ - spqr_arc pathTargetArc; /**< The virtual arc where the path has to go */ + SCIP_Bool nextPathMemberIsParent; /**< Indicates if the next reduced member in the path is a parent of + * this member */ + spqr_arc pathSourceArc; /**< The virtual arc from where the path originates */ + spqr_arc pathTargetArc; /**< The virtual arc where the path has to go */ } SPQRColReducedMember; -/**< Keeps track of the data relevant for each SPQR tree in the SPQR forest. */ +/** Keeps track of the data relevant for each SPQR tree in the SPQR forest. */ typedef struct { - int rootDepth; /**< The depth of the root node of the subtree in the arborescence */ - reduced_member_id root; /**< The reduced member id of the root */ + int rootDepth; /**< The depth of the root node of the subtree in the arborescence */ + reduced_member_id root; /**< The reduced member id of the root */ - reduced_member_id pathEndMembers[2]; /**< The reduced members that contain the ends of the path */ - int numPathEndMembers; /**< The number of reduced members that contain an end of the path */ + reduced_member_id pathEndMembers[2]; /**< The reduced members that contain the ends of the path */ + int numPathEndMembers; /**< The number of reduced members that contain an end of the path */ } SPQRColReducedComponent; -/**< Keeps track of the reduced member and the reduced member that is the root of the arborescence for a single member*/ +/** Keeps track of the reduced member and the reduced member that is the root of the arborescence for a single member */ typedef struct { - reduced_member_id reducedMember; /**< The ID of the associated reduced member */ - reduced_member_id rootDepthMinimizer; /**< The ID of the reduced member that is the root of the arborescence this + reduced_member_id reducedMember; /**< The ID of the associated reduced member */ + reduced_member_id rootDepthMinimizer; /**< The ID of the reduced member that is the root of the arborescence this * reduced member is contained in. */ } MemberInfo; -/**< Data to be used for the recursive algorithms that creates the sub-SPQR trees that contain the path edges */ +/** Data to be used for the recursive algorithms that creates the sub-SPQR trees that contain the path edges */ typedef struct { - spqr_member member; /**< The current member */ + spqr_member member; /**< The current member */ } CreateReducedMembersCallstack; -/**< The main datastructure that manages all the data for column-addition in network matrices */ +/** The main datastructure that manages all the data for column-addition in network matrices */ typedef struct { - SCIP_Bool remainsNetwork; /**< Does the addition of the current column give a network matrix? */ + SCIP_Bool remainsNetwork; /**< Does the addition of the current column give a network matrix? */ SPQRColReducedMember* reducedMembers; /**< The array of reduced members, that form the subtree containing the - * rows of the current column.*/ - int memReducedMembers; /**< Number of allocated slots in the reduced member array */ - int numReducedMembers; /**< Number of used slots in the reduced member array */ + * rows of the current column. */ + int memReducedMembers; /**< Number of allocated slots in the reduced member array */ + int numReducedMembers; /**< Number of used slots in the reduced member array */ SPQRColReducedComponent* reducedComponents;/**< The array of reduced components, * that represent the SPQR trees in the SPQR forest */ - int memReducedComponents; /**< Number of allocated slots in the reduced component array */ - int numReducedComponents; /**< Number of used slots in the reduced component array */ + int memReducedComponents;/**< Number of allocated slots in the reduced component array */ + int numReducedComponents;/**< Number of used slots in the reduced component array */ - MemberInfo* memberInformation; /**< Array with member information; tracks the reduced member id that + MemberInfo* memberInformation; /**< Array with member information; tracks the reduced member id that * corresponds to every member in the decomposition. */ - int memMemberInformation; /**< Number of allocated slots in the member information array */ - int numMemberInformation; /**< Number of used slots in the member information array */ + int memMemberInformation;/**< Number of allocated slots in the member information array */ + int numMemberInformation;/**< Number of used slots in the member information array */ - reduced_member_id* childrenStorage; /**< Array that stores the children of the reduced member arborescences. + reduced_member_id* childrenStorage; /**< Array that stores the children of the reduced member arborescences. * Each reduced member has a 'firstChild' field and a length, that points * to the subarray within this array with its children. This array is * shared here in order to minimize allocations across iterations. */ - int memChildrenStorage; /**< Number of allocated slots for the children storage array */ - int numChildrenStorage; /**< Number of used slots for the children storage array */ + int memChildrenStorage; /**< Number of allocated slots for the children storage array */ + int numChildrenStorage; /**< Number of used slots for the children storage array */ - PathArcListNode* pathArcs; /**< Array that contains the linked-list nodes of the path arcs, that + PathArcListNode* pathArcs; /**< Array that contains the linked-list nodes of the path arcs, that * correspond to the rows of the current column. */ - int memPathArcs; /**< Number of allocated slots for the path arc array */ - int numPathArcs; /**< Number of used slots for the path arc array */ - path_arc_id firstOverallPathArc; /**< Head node of the linked list containing all path arcs */ + int memPathArcs; /**< Number of allocated slots for the path arc array */ + int numPathArcs; /**< Number of used slots for the path arc array */ + path_arc_id firstOverallPathArc;/**< Head node of the linked list containing all path arcs */ - int* nodeInPathDegree; /**< Array that contains the in degree of all nodes */ - int* nodeOutPathDegree; /**< Array that contains the out degree of all nodes */ - int memNodePathDegree; /**< The number of allocated slots for the node-degree arrays */ + int* nodeInPathDegree; /**< Array that contains the in degree of all nodes */ + int* nodeOutPathDegree; /**< Array that contains the out degree of all nodes */ + int memNodePathDegree; /**< The number of allocated slots for the node-degree arrays */ - SCIP_Bool* arcInPath; /**< Is the given arc in the path? */ - SCIP_Bool* arcInPathReversed; /**< Is the given arc's direction reversed in the path? */ - int memArcsInPath; /**< The number of allocated slots for the arcInPath(Reversed) arrays */ + SCIP_Bool* arcInPath; /**< Is the given arc in the path? */ + SCIP_Bool* arcInPathReversed; /**< Is the given arc's direction reversed in the path? */ + int memArcsInPath; /**< The number of allocated slots for the arcInPath(Reversed) arrays */ CreateReducedMembersCallstack* createReducedMembersCallStack; /**< Callstack for createReducedMembers() */ - int memCreateReducedMembersCallStack; /**< Allocated memory for callstack for createReducedMembers() */ + int memCreateReducedMembersCallStack; /**< Allocated memory for callstack for createReducedMembers() */ - spqr_col newColIndex; /**< The index of the new column to be added */ + spqr_col newColIndex; /**< The index of the new column to be added */ - spqr_row* newRowArcs; /**< The row indices of the nonzeros of the column to be added, that are - * not yet in the decomposition.*/ - SCIP_Bool* newRowArcReversed; /**< True if the nonzero corresponding to the row index is -1, + spqr_row* newRowArcs; /**< The row indices of the nonzeros of the column to be added, that are + * not yet in the decomposition. */ + SCIP_Bool* newRowArcReversed; /**< True if the nonzero corresponding to the row index is -1, * false otherwise */ - int memNewRowArcs; /**< Number of allocated slots in newRowArcs(Reversed) */ - int numNewRowArcs; /**< Number of new rows in the column to be added */ - - spqr_arc* decompositionRowArcs; /**< For each row nonzero that is in the decomposition, - * stores the corresponding decomposition arc */ - SCIP_Bool* decompositionArcReversed; /**< For each row nonzero that is in the decomposition, - * stores whether the corresponding decomposition arc is reversed */ - int memDecompositionRowArcs; /**< Number of allocated slots in decompositionRowArcs(Reversed) */ - int numDecompositionRowArcs; /**< Number of used slots in decompositionRowArcs(Reversed)*/ - - spqr_member* leafMembers; /**< Array that stores the leaf members of the SPQR forest */ - int numLeafMembers; /**< Number of used slots in leafMembers array*/ - int memLeafMembers; /**< Number of allocated slots in leafMembers array*/ + int memNewRowArcs; /**< Number of allocated slots in newRowArcs(Reversed) */ + int numNewRowArcs; /**< Number of new rows in the column to be added */ + + spqr_arc* decompositionRowArcs; /**< For each row nonzero that is in the decomposition, + * stores the corresponding decomposition arc */ + SCIP_Bool* decompositionArcReversed; /**< For each row nonzero that is in the decomposition, + * stores whether the corresponding decomposition arc is reversed */ + int memDecompositionRowArcs; /**< Number of allocated slots in decompositionRowArcs(Reversed) */ + int numDecompositionRowArcs; /**< Number of used slots in decompositionRowArcs(Reversed) */ + + spqr_member* leafMembers; /**< Array that stores the leaf members of the SPQR forest */ + int numLeafMembers; /**< Number of used slots in leafMembers array */ + int memLeafMembers; /**< Number of allocated slots in leafMembers array */ } SCIP_NETCOLADD; -/**< Clean up internal temporary data structures that were used in the previous column iteration. */ +/** Clean up internal temporary data structures that were used in the previous column iteration. */ static void cleanupPreviousIteration( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -3232,15 +3367,15 @@ void cleanupPreviousIteration( assert(newCol); path_arc_id pathArc = newCol->firstOverallPathArc; - while( pathArcIsValid(pathArc)) + while( pathArcIsValid(pathArc) ) { spqr_node head = newCol->pathArcs[pathArc].arcHead; spqr_node tail = newCol->pathArcs[pathArc].arcTail; - if( SPQRnodeIsValid(head)) + if( SPQRnodeIsValid(head) ) { newCol->nodeInPathDegree[head] = 0; } - if( SPQRnodeIsValid(tail)) + if( SPQRnodeIsValid(tail) ) { newCol->nodeOutPathDegree[tail] = 0; } @@ -3271,7 +3406,7 @@ void cleanupPreviousIteration( newCol->numPathArcs = 0; } -/**< Create a new network column addition datastructure */ +/** Create a new network column addition datastructure. */ static SCIP_RETCODE SCIPnetcoladdCreate( BMS_BLKMEM* blkmem, /**< Block memory */ @@ -3280,7 +3415,7 @@ SCIP_RETCODE SCIPnetcoladdCreate( { assert(blkmem); - SCIP_ALLOC(BMSallocBlockMemory(blkmem, pcoladd)); + SCIP_ALLOC( BMSallocBlockMemory(blkmem, pcoladd) ); SCIP_NETCOLADD* newCol = *pcoladd; newCol->remainsNetwork = FALSE; @@ -3335,7 +3470,7 @@ SCIP_RETCODE SCIPnetcoladdCreate( return SCIP_OKAY; } -/**< Free a network column addition datastructure */ +/** Free a network column addition datastructure */ static void SCIPnetcoladdFree( BMS_BLKMEM* blkmem, /**< Block memory */ @@ -3343,6 +3478,7 @@ void SCIPnetcoladdFree( ) { assert(blkmem); + SCIP_NETCOLADD* newCol = *pcoladd; BMSfreeBlockMemoryArray(blkmem, &newCol->decompositionRowArcs, newCol->memDecompositionRowArcs); BMSfreeBlockMemoryArray(blkmem, &newCol->decompositionArcReversed, newCol->memDecompositionRowArcs); @@ -3363,8 +3499,9 @@ void SCIPnetcoladdFree( BMSfreeBlockMemory(blkmem, pcoladd); } -/**< Adds members to the reduced member tree, by starting at the given member, jumping up to the parent, repeating this - * procedure until the root has been added. */ +/** Adds members to the reduced member tree, by starting at the given member, jumping up to the parent, repeating this + * procedure until the root has been added. + */ static reduced_member_id createReducedMembersToRoot( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -3374,7 +3511,7 @@ reduced_member_id createReducedMembersToRoot( { assert(SPQRmemberIsValid(firstMember)); - /**< Originally a recursive algorithm, but for large matrices we need to unroll recursion to prevent stack overflows + /* Originally a recursive algorithm, but for large matrices we need to unroll recursion to prevent stack overflows * from too many recursive calls */ CreateReducedMembersCallstack* callstack = newCol->createReducedMembersCallStack; callstack[0].member = firstMember; @@ -3411,14 +3548,13 @@ reduced_member_id createReducedMembersToRoot( assert(memberIsRepresentative(dec, member)); spqr_member parentMember = findMemberParent(dec, member); - if( SPQRmemberIsValid(parentMember)) + if( SPQRmemberIsValid(parentMember) ) { //recursive call to parent member ++callDepth; assert(callDepth < newCol->memCreateReducedMembersCallStack); callstack[callDepth].member = parentMember; continue; - } else { @@ -3452,7 +3588,8 @@ reduced_member_id createReducedMembersToRoot( while( TRUE ) /*lint !e716*/ { --callDepth; - if( callDepth < 0 ) break; + if( callDepth < 0 ) + break; spqr_member parentMember = callstack[callDepth + 1].member; reduced_member_id parentReducedMember = newCol->memberInformation[parentMember].reducedMember; spqr_member currentMember = callstack[callDepth].member; @@ -3476,7 +3613,7 @@ reduced_member_id createReducedMembersToRoot( return returnedMember; } -/**< Construct the reduced decomposition, which is the smallest subtree containing all members path arcs */ +/** Construct the reduced decomposition, which is the smallest subtree containing all members path arcs. */ static SCIP_RETCODE constructReducedDecomposition( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -3491,6 +3628,7 @@ SCIP_RETCODE constructReducedDecomposition( assert(reducedMemberIsInvalid(newCol->memberInformation[i].reducedMember)); } #endif + newCol->numReducedComponents = 0; newCol->numReducedMembers = 0; if( newCol->numDecompositionRowArcs == 0 ) @@ -3504,15 +3642,13 @@ SCIP_RETCODE constructReducedDecomposition( if( newSize > newCol->memReducedMembers ) { int newArraySize = maxValue(2 * newCol->memReducedMembers, newSize); - SCIP_ALLOC( - BMSreallocBlockMemoryArray(dec->env, &newCol->reducedMembers, newCol->memReducedMembers, newArraySize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->reducedMembers, newCol->memReducedMembers, newArraySize) ); newCol->memReducedMembers = newArraySize; } if( newSize > newCol->memMemberInformation ) { int updatedSize = maxValue(2 * newCol->memMemberInformation, newSize); - SCIP_ALLOC( - BMSreallocBlockMemoryArray(dec->env, &newCol->memberInformation, newCol->memMemberInformation, updatedSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->memberInformation, newCol->memMemberInformation, updatedSize) ); for( int i = newCol->memMemberInformation; i < updatedSize; ++i ) { newCol->memberInformation[i].reducedMember = INVALID_REDUCED_MEMBER; @@ -3525,8 +3661,7 @@ SCIP_RETCODE constructReducedDecomposition( if( numComponents > newCol->memReducedComponents ) { int updatedSize = maxValue(2 * newCol->memReducedComponents, numComponents); - SCIP_ALLOC( - BMSreallocBlockMemoryArray(dec->env, &newCol->reducedComponents, newCol->memReducedComponents, updatedSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->reducedComponents, newCol->memReducedComponents, updatedSize) ); newCol->memReducedComponents = updatedSize; } @@ -3534,8 +3669,8 @@ SCIP_RETCODE constructReducedDecomposition( if( newCol->memCreateReducedMembersCallStack < numMembers ) { int updatedSize = maxValue(2 * newCol->memCreateReducedMembersCallStack, numMembers); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->createReducedMembersCallStack, - newCol->memCreateReducedMembersCallStack, updatedSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->createReducedMembersCallStack, + newCol->memCreateReducedMembersCallStack, updatedSize) ); newCol->memCreateReducedMembersCallStack = updatedSize; } @@ -3547,7 +3682,7 @@ SCIP_RETCODE constructReducedDecomposition( spqr_member arcMember = findArcMember(dec, arc); reduced_member_id reducedMember = createReducedMembersToRoot(dec, newCol, arcMember); reduced_member_id* depthMinimizer = &newCol->memberInformation[newCol->reducedMembers[reducedMember].rootMember].rootDepthMinimizer; - if( reducedMemberIsInvalid(*depthMinimizer)) + if( reducedMemberIsInvalid(*depthMinimizer) ) { *depthMinimizer = reducedMember; } @@ -3584,8 +3719,7 @@ SCIP_RETCODE constructReducedDecomposition( if( newCol->memChildrenStorage < numTotalChildren ) { int newMemSize = maxValue(newCol->memChildrenStorage * 2, numTotalChildren); - SCIP_ALLOC( - BMSreallocBlockMemoryArray(dec->env, &newCol->childrenStorage, newCol->memChildrenStorage, newMemSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->childrenStorage, newCol->memChildrenStorage, newMemSize) ); newCol->memChildrenStorage = newMemSize; } newCol->numChildrenStorage = numTotalChildren; @@ -3626,7 +3760,7 @@ SCIP_RETCODE constructReducedDecomposition( return SCIP_OKAY; } -/**< Clean up the memberinformation array at the end of an iteration */ +/** Clean up the memberinformation array at the end of an iteration. */ static void cleanUpMemberInformation( SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */ @@ -3646,7 +3780,7 @@ void cleanUpMemberInformation( #endif } -/**< Marks the given arc as a path arc and adds it to the relevant data structures. */ +/** Marks the given arc as a path arc and adds it to the relevant data structures. */ static void createPathArc( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -3699,7 +3833,7 @@ void createPathArc( listNode->reversed = reversed; } -/**< Mark all the row indices of the new column as path arcs*/ +/** Mark all the row indices of the new column as path arcs */ static SCIP_RETCODE createPathArcs( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -3710,15 +3844,15 @@ SCIP_RETCODE createPathArcs( if( newCol->memPathArcs < maxNumPathArcs ) { int newMaxNumArcs = 2 * maxNumPathArcs;//safety factor to prevent very frequent reallocations - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->pathArcs, newCol->memPathArcs, newMaxNumArcs)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->pathArcs, newCol->memPathArcs, newMaxNumArcs) ); newCol->memPathArcs = newMaxNumArcs; } int maxPathArcIndex = largestArcID(dec); if( newCol->memArcsInPath < maxPathArcIndex ) { int newSize = 2 * maxPathArcIndex;//safety factor to prevent very frequent reallocations - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->arcInPath, newCol->memArcsInPath, newSize)); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->arcInPathReversed, newCol->memArcsInPath, newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->arcInPath, newCol->memArcsInPath, newSize) ); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->arcInPathReversed, newCol->memArcsInPath, newSize) ); for( int i = newCol->memArcsInPath; i < newSize; ++i ) { @@ -3731,8 +3865,8 @@ SCIP_RETCODE createPathArcs( if( newCol->memNodePathDegree < maxNumNodes ) { int newSize = 2 * maxNumNodes;//safety factor to prevent very frequent reallocations - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->nodeInPathDegree, newCol->memNodePathDegree, newSize)); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->nodeOutPathDegree, newCol->memNodePathDegree, newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->nodeInPathDegree, newCol->memNodePathDegree, newSize) ); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->nodeOutPathDegree, newCol->memNodePathDegree, newSize) ); for( int i = newCol->memNodePathDegree; i < newSize; ++i ) { newCol->nodeInPathDegree[i] = 0; @@ -3752,8 +3886,9 @@ SCIP_RETCODE createPathArcs( } -/**< Saves the information of the current row and partitions it based on whether or not the given columns are - **< already part of the decomposition. */ +/** Saves the information of the current row and partitions it based on whether or not the given columns are + * already part of the decomposition. + */ static SCIP_RETCODE newColUpdateColInformation( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -3773,15 +3908,15 @@ SCIP_RETCODE newColUpdateColInformation( { spqr_arc rowArc = getDecompositionRowArc(dec, nonzeroRows[i]); SCIP_Bool reversed = nonzeroValues[i] < 0.0; - if( SPQRarcIsValid(rowArc)) + if( SPQRarcIsValid(rowArc) ) {//If the arc is the current decomposition: save it in the array if( newCol->numDecompositionRowArcs == newCol->memDecompositionRowArcs ) { int newNumArcs = newCol->memDecompositionRowArcs == 0 ? 8 : 2 * newCol->memDecompositionRowArcs; - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->decompositionRowArcs, - newCol->memDecompositionRowArcs, newNumArcs)); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->decompositionArcReversed, - newCol->memDecompositionRowArcs, newNumArcs)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->decompositionRowArcs, + newCol->memDecompositionRowArcs, newNumArcs) ); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->decompositionArcReversed, + newCol->memDecompositionRowArcs, newNumArcs) ); newCol->memDecompositionRowArcs = newNumArcs; } newCol->decompositionRowArcs[newCol->numDecompositionRowArcs] = rowArc; @@ -3794,10 +3929,10 @@ SCIP_RETCODE newColUpdateColInformation( if( newCol->numNewRowArcs == newCol->memNewRowArcs ) { int newNumArcs = newCol->memNewRowArcs == 0 ? 8 : 2 * newCol->memNewRowArcs; - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->newRowArcs, - newCol->memNewRowArcs, newNumArcs)); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->newRowArcReversed, - newCol->memNewRowArcs, newNumArcs)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->newRowArcs, + newCol->memNewRowArcs, newNumArcs) ); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->newRowArcReversed, + newCol->memNewRowArcs, newNumArcs) ); newCol->memNewRowArcs = newNumArcs; } newCol->newRowArcs[newCol->numNewRowArcs] = nonzeroRows[i]; @@ -3809,7 +3944,7 @@ SCIP_RETCODE newColUpdateColInformation( return SCIP_OKAY; } -/**< Compute and store the leaf members of the reduced SPQR forest */ +/** Compute and store the leaf members of the reduced SPQR forest */ static SCIP_RETCODE computeLeafMembers( const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -3819,7 +3954,7 @@ SCIP_RETCODE computeLeafMembers( if( newCol->numReducedMembers > newCol->memLeafMembers ) { int newSize = maxValue(newCol->numReducedMembers, 2 * newCol->memLeafMembers); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newCol->leafMembers, newCol->memLeafMembers, newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->leafMembers, newCol->memLeafMembers, newSize) ); newCol->memLeafMembers = newSize; } newCol->numLeafMembers = 0; @@ -3832,10 +3967,11 @@ SCIP_RETCODE computeLeafMembers( ++newCol->numLeafMembers; } } + return SCIP_OKAY; } -/**< Checks if the path arcs in the given rigid member form a path */ +/** Checks if the path arcs in the given rigid member form a path */ static void determineRigidPath( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -3853,8 +3989,7 @@ void determineRigidPath( SCIP_Bool isValidPath = TRUE; redMem->rigidPathStart = SPQR_INVALID_NODE; redMem->rigidPathEnd = SPQR_INVALID_NODE; - for( path_arc_id pathArc = redMem->firstPathArc; pathArcIsValid( - pathArc); pathArc = newCol->pathArcs[pathArc].nextMember ) + for( path_arc_id pathArc = redMem->firstPathArc; pathArcIsValid(pathArc); pathArc = newCol->pathArcs[pathArc].nextMember ) { spqr_node head = newCol->pathArcs[pathArc].arcHead; spqr_node tail = newCol->pathArcs[pathArc].arcTail; @@ -3870,7 +4005,7 @@ void determineRigidPath( { //found end node //If this is the second, stop - if( SPQRnodeIsValid(redMem->rigidPathEnd)) + if( SPQRnodeIsValid(redMem->rigidPathEnd) ) { isValidPath = FALSE; break; @@ -3881,7 +4016,7 @@ void determineRigidPath( { //Found start node. //If this is the second, stop. - if( SPQRnodeIsValid(redMem->rigidPathStart)) + if( SPQRnodeIsValid(redMem->rigidPathStart) ) { isValidPath = FALSE; break; @@ -3900,7 +4035,7 @@ void determineRigidPath( } } -/**< Determines the member's type for the case where the reduced tree consists of a single rigid member */ +/** Determines the member's type for the case where the reduced tree consists of a single rigid member. */ static void determineSingleRigidType( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -3911,6 +4046,7 @@ void determineSingleRigidType( assert(dec); assert(newCol); assert(reducedMemberIsValid(reducedMember)); + SPQRColReducedMember* redMem = &newCol->reducedMembers[reducedMember]; assert(pathArcIsValid(redMem->firstPathArc)); determineRigidPath(dec, newCol, redMem); @@ -3920,7 +4056,7 @@ void determineSingleRigidType( } } -/**< Determines the member's type for the case where the reduced tree consists of a single member */ +/** Determines the member's type for the case where the reduced tree consists of a single member. */ static void determineSingleComponentType( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -3946,7 +4082,6 @@ void determineSingleComponentType( return; } - spqr_member member = findMember(dec, newCol->reducedMembers[reducedMember].member); SPQRMemberType type = getMemberType(dec, member); switch( type ) @@ -3982,7 +4117,7 @@ void determineSingleComponentType( newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc); } else if( - ( newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc)) != + (newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc)) != passesForwards ) { good = FALSE; @@ -4019,7 +4154,7 @@ void determineSingleComponentType( } } -/**< Determines the path type of a series member */ +/** Determines the path type of a series member. */ static void determinePathSeriesType( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -4098,7 +4233,7 @@ void determinePathSeriesType( } } redMem->pathBackwards = !passesForwards; - if( SPQRarcIsValid(target)) + if( SPQRarcIsValid(target) ) { SCIP_Bool targetReversed = arcIsReversedNonRigid(dec, target); SCIP_Bool inSameDirection = sourceReversed == targetReversed; @@ -4147,7 +4282,7 @@ void determinePathSeriesType( } } -/**< Determines the path type of a parallel member */ +/** Determines the path type of a parallel member. */ static void determinePathParallelType( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -4217,7 +4352,7 @@ void determinePathParallelType( } } -/**< Determines the path type of a rigid member */ +/** Determines the path type of a rigid member. */ static void determinePathRigidType( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -4245,7 +4380,7 @@ void determinePathRigidType( SCIP_Bool sourceHeadIsTargetTail = sourceHead == targetTail; SCIP_Bool sourceTailIsTargetTail = sourceTail == targetTail; - if( !( sourceHeadIsTargetHead || sourceTailIsTargetHead || sourceHeadIsTargetTail || sourceTailIsTargetTail )) + if( !(sourceHeadIsTargetHead || sourceTailIsTargetHead || sourceHeadIsTargetTail || sourceTailIsTargetTail) ) { redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; newCol->remainsNetwork = FALSE; @@ -4319,7 +4454,7 @@ void determinePathRigidType( { return; } - if( SPQRarcIsInvalid(source)) + if( SPQRarcIsInvalid(source) ) { assert(SPQRarcIsValid(target)); spqr_node targetTail = findEffectiveArcTail(dec, target); @@ -4383,7 +4518,7 @@ void determinePathRigidType( // redMem->reverseArcs = isInto(previousType) != startsAtTail; //Reverse only if there is no path starting at tail } - if( SPQRarcIsValid(target)) + if( SPQRarcIsValid(target) ) { spqr_node targetTail = findEffectiveArcTail(dec, target); spqr_node targetHead = findEffectiveArcHead(dec, target); @@ -4397,7 +4532,7 @@ void determinePathRigidType( SCIP_Bool endsAtTargetHead = redMem->rigidPathEnd == targetHead; SCIP_Bool endsAtTargetTail = redMem->rigidPathEnd == targetTail; - if( !( startsAtTargetHead || startsAtTargetTail || endsAtTargetHead || endsAtTargetTail )) + if( !(startsAtTargetHead || startsAtTargetTail || endsAtTargetHead || endsAtTargetTail) ) { redMem->type = REDUCEDMEMBER_TYPE_NOT_NETWORK; newCol->remainsNetwork = FALSE; @@ -4406,7 +4541,7 @@ void determinePathRigidType( SCIP_Bool outReverse = FALSE; SCIP_Bool outHead = FALSE; - if( isInto(previousType) == isHead(previousType)) + if( isInto(previousType) == isHead(previousType) ) { if( startsAtHead && endsAtTail ) { @@ -4441,7 +4576,7 @@ void determinePathRigidType( //TODO: this if-else tree to compute two booleans can probably be simplified significantly SCIP_Bool isBad = FALSE; - if( isInto(previousType) == isHead(previousType)) + if( isInto(previousType) == isHead(previousType) ) { if( startsAtHead && endsAtTail ) { @@ -4525,7 +4660,7 @@ void determinePathRigidType( } redMem->reverseArcs = outReverse; - if( isInto(previousType)) + if( isInto(previousType) ) { redMem->pathType = outHead ? INTO_HEAD : INTO_TAIL; } @@ -4537,7 +4672,7 @@ void determinePathRigidType( } //TODO: is this simplifyable? - if( isInto(previousType) == isHead(previousType)) + if( isInto(previousType) == isHead(previousType) ) { redMem->reverseArcs = startsAtHead != isInto(previousType); } @@ -4547,7 +4682,7 @@ void determinePathRigidType( } //last member of the path. Since we already checked the source, //Below is technically no op, but helps with debugging - if( isInto(previousType)) + if( isInto(previousType) ) { redMem->pathType = INTO_HEAD; } @@ -4557,7 +4692,7 @@ void determinePathRigidType( } } -/**< Determines the path type of a single member */ +/** Determines the path type of a single member. */ static void determinePathMemberType( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -4573,7 +4708,7 @@ void determinePathMemberType( newCol->reducedMembers[reducedMember].pathTargetArc = target; //Check if the marked edges with the given signs //form a (reverse) directed path from one of the source's end nodes to one of the target's end nodes - switch( getMemberType(dec, member)) + switch( getMemberType(dec, member) ) { case SPQR_MEMBERTYPE_RIGID: { @@ -4601,8 +4736,10 @@ void determinePathMemberType( } } -/**< Determines the path types of all reduced members. The reduced members themselves also form a path in the - * reduced decomposition. */ +/** Determines the path types of all reduced members. + * + * The reduced members themselves also form a path in the reduced decomposition. + */ static void determinePathTypes( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -4688,8 +4825,10 @@ void determinePathTypes( //since we return anyways, no need to check newCol->remainsNetwork explicitly } -/**< Check if a rigid leaf closes a cycle with its child. If so, we can propagate this cycle to a virtual arc of the - * child node member and shrink the decomposition. */ +/** Check if a rigid leaf closes a cycle with its child. + * + * If so, we can propagate this cycle to a virtual arc of the child node member and shrink the decomposition. + */ static void checkRigidLeaf( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -4719,8 +4858,10 @@ void checkRigidLeaf( leafMember->type = REDUCEDMEMBER_TYPE_MERGED; } -/**< Check if a leaf node closes a cycle with its child. If so, we can propagate this cycle to a virtual arc of the - * child node member and shrink the decomposition. */ +/** Check if a leaf node closes a cycle with its child. + * + * If so, we can propagate this cycle to a virtual arc of the child node member and shrink the decomposition. + */ static ReducedMemberType checkLeaf( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -4739,7 +4880,7 @@ ReducedMemberType checkLeaf( assert(reducedMemberIsValid(leaf)); assert(reducedMemberIsValid(parent)); - switch( getMemberType(dec, newCol->reducedMembers[leaf].member)) + switch( getMemberType(dec, newCol->reducedMembers[leaf].member) ) { case SPQR_MEMBERTYPE_RIGID: { @@ -4774,7 +4915,7 @@ ReducedMemberType checkLeaf( newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc); } else if( - ( newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc)) != + (newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc)) != passesForwards ) { good = FALSE; @@ -4820,7 +4961,7 @@ ReducedMemberType checkLeaf( return newCol->reducedMembers[leaf].type; } -/**< Recursively removes leaf nodes whose path forms cycles with the virtual arc to its children. */ +/** Recursively removes leaf nodes whose path forms cycles with the virtual arc to its children. */ static void propagateCycles( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -4829,8 +4970,8 @@ void propagateCycles( { assert(dec); assert(newCol); - int leafArrayIndex = 0; + int leafArrayIndex = 0; while( leafArrayIndex != newCol->numLeafMembers ) { reduced_member_id leaf = newCol->leafMembers[leafArrayIndex]; @@ -4916,7 +5057,7 @@ void propagateCycles( } assert(SPQRmemberIsValid(newCol->reducedMembers[child].member)); assert(SPQRarcIsValid(markerToChild)); - if( !arcIsTree(dec, markerToChild)) + if( !arcIsTree(dec, markerToChild) ) { ReducedMemberType type = checkLeaf(dec, newCol, root, markerToChild, child, markerToParent(dec, newCol->reducedMembers[child].member)); @@ -4955,7 +5096,7 @@ void propagateCycles( } } -/**< Determine the type of a single component */ +/** Determine the type of a single component. */ static void determineComponentTypes( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -4979,7 +5120,10 @@ void determineComponentTypes( } } -/**< Checks if the given column can be added to the network matrix decomposition. See header for more info. */ +/** Checks if the given column can be added to the network matrix decomposition. + * + * See header for more info. + */ static SCIP_RETCODE SCIPnetcoladdCheck( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -4995,7 +5139,8 @@ SCIP_RETCODE SCIPnetcoladdCheck( assert(nnonzs == 0 || ( nonzrows && nonzvals )); /* A column can only be added once */ - if( netMatDecDataContainsColumn(dec,column)){ + if( netMatDecDataContainsColumn(dec,column) ) + { return SCIP_INVALIDDATA; } coladd->remainsNetwork = TRUE; @@ -5003,13 +5148,13 @@ SCIP_RETCODE SCIPnetcoladdCheck( //assert that previous iteration was cleaned up //Store call data - SCIP_CALL(newColUpdateColInformation(dec, coladd, column, nonzrows, nonzvals, nnonzs)); + SCIP_CALL( newColUpdateColInformation(dec, coladd, column, nonzrows, nonzvals, nnonzs) ); //compute reduced decomposition - SCIP_CALL(constructReducedDecomposition(dec, coladd)); + SCIP_CALL( constructReducedDecomposition(dec, coladd) ); //initialize path arcs in reduced decomposition - SCIP_CALL(createPathArcs(dec, coladd)); - SCIP_CALL(computeLeafMembers(dec, coladd)); + SCIP_CALL( createPathArcs(dec, coladd) ); + SCIP_CALL( computeLeafMembers(dec, coladd) ); propagateCycles(dec, coladd); //determine types if( coladd->remainsNetwork ) @@ -5025,19 +5170,21 @@ SCIP_RETCODE SCIPnetcoladdCheck( return SCIP_OKAY; } -/**< Struct that contains the data that tells us how to add the new column after the graph has been modified - * In the case the member is a series or parallel node, the new column and rows are placed in series or parallel, - * respectively. Otherwise, the edge can be added between head and tail in a rigid member*/ +/** Struct that contains the data that tells us how to add the new column after the graph has been modified. + * + * In the case the member is a series or parallel node, the new column and rows are placed in series or parallel, + * respectively. Otherwise, the edge can be added between head and tail in a rigid member. + */ typedef struct { - spqr_member member; /**< The member where the new column should be added */ - spqr_node head; /**< The head node of the new column (rigid members only)*/ - spqr_node tail; /**< The tail node of the new column (rigid members only)*/ - spqr_arc representative; /**< The representative arc of the new column */ - SCIP_Bool reversed; /**< Is the new column reversed? */ + spqr_member member; /**< The member where the new column should be added */ + spqr_node head; /**< The head node of the new column (rigid members only) */ + spqr_node tail; /**< The tail node of the new column (rigid members only) */ + spqr_arc representative; /**< The representative arc of the new column */ + SCIP_Bool reversed; /**< Is the new column reversed? */ } NewColInformation; -/**< Initializes an empty newcolinformation struct */ +/** Initializes an empty newcolinformation struct. */ static NewColInformation emptyNewColInformation(void) { @@ -5050,7 +5197,7 @@ NewColInformation emptyNewColInformation(void) return information; } -/**< Set the head node of the new column edge to be added */ +/** Set the head node of the new column edge to be added. */ static void setTerminalHead( NewColInformation* info, /**< The new column information */ @@ -5060,10 +5207,11 @@ void setTerminalHead( assert(SPQRnodeIsValid(node)); assert(SPQRnodeIsInvalid(info->head)); assert(info); + info->head = node; } -/**< Set the tail node of the new column edge to be added */ +/** Set the tail node of the new column edge to be added. */ static void setTerminalTail( NewColInformation* info, /**< The new column information */ @@ -5073,10 +5221,11 @@ void setTerminalTail( assert(SPQRnodeIsValid(node)); assert(info); assert(SPQRnodeIsInvalid(info->tail)); + info->tail = node; } -/**< Set whether the new column arc should be reversed */ +/** Set whether the new column arc should be reversed. */ static void setTerminalReversed( NewColInformation* info, /**< The new column information */ @@ -5084,10 +5233,11 @@ void setTerminalReversed( ) { assert(info); + info->reversed = reversed; } -/**< Set the member that contains the new column arc */ +/** Set the member that contains the new column arc. */ static void setTerminalMember( NewColInformation* info, /**< The new column information */ @@ -5095,10 +5245,11 @@ void setTerminalMember( ) { assert(info); + info->member = member; } -/**< Set the representative arc of the new column arc */ +/** Set the representative arc of the new column arc. */ static void setTerminalRepresentative( NewColInformation* info, /**< The new column information */ @@ -5106,11 +5257,14 @@ void setTerminalRepresentative( ) { assert(info); + info->representative = representative; } -/**< Splits a parallel member into two adjacent parallel members connected by a virtual edge pair. - * One member keeps the two arcs passed to this function, the other member gets all other arcs. */ +/** Splits a parallel member into two adjacent parallel members connected by a virtual edge pair. + * + * One member keeps the two arcs passed to this function, the other member gets all other arcs. + */ static SCIP_RETCODE splitParallel( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -5128,24 +5282,27 @@ SCIP_RETCODE splitParallel( SCIP_Bool childContainsTree = arcIsTree(dec, arc1) || arcIsTree(dec, arc2); spqr_arc toParent = markerToParent(dec, parallel); SCIP_Bool parentMoved = toParent == arc1 || toParent == arc2; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, childParallel)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, childParallel) ); moveArcToNewMember(dec, arc1, parallel, *childParallel); moveArcToNewMember(dec, arc2, parallel, *childParallel); if( parentMoved ) { - SCIP_CALL(createMarkerPair(dec, *childParallel, parallel, !childContainsTree, FALSE, FALSE)); + SCIP_CALL( createMarkerPair(dec, *childParallel, parallel, !childContainsTree, FALSE, FALSE) ); } else { - SCIP_CALL(createMarkerPair(dec, parallel, *childParallel, childContainsTree, FALSE, FALSE)); + SCIP_CALL( createMarkerPair(dec, parallel, *childParallel, childContainsTree, FALSE, FALSE) ); } + return SCIP_OKAY; } -/**< Very elaborate function that splits a series member into multiple members based on the structure of the path arcs. - * The updated member reflects the structure of the updated SPQR tree after the new column arc is added. */ +/** Very elaborate function that splits a series member into multiple members based on the structure of the path arcs. + * + * The updated member reflects the structure of the updated SPQR tree after the new column arc is added. + */ static SCIP_RETCODE splitSeries( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -5169,14 +5326,14 @@ SCIP_RETCODE splitSeries( if( !createPathSeries && !convertOriginal ) { spqr_member parallel; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, ¶llel)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, ¶llel) ); path_arc_id pathArcId = reducedMember->firstPathArc; assert(pathArcIsValid(pathArcId)); spqr_arc marked = newCol->pathArcs[pathArcId].arc; assert(marked != markerToParent(dec, member));//TODO: handle this case later moveArcToNewMember(dec, marked, member, parallel); SCIP_Bool reversed = arcIsReversedNonRigid(dec, marked); - SCIP_CALL(createMarkerPair(dec, member, parallel, TRUE, reversed, reversed)); + SCIP_CALL( createMarkerPair(dec, member, parallel, TRUE, reversed, reversed) ); *loopMember = parallel; if( reversed == reducedMember->pathBackwards ) @@ -5211,7 +5368,7 @@ SCIP_RETCODE splitSeries( if( createPathSeries && !convertOriginal ) { spqr_member pathMember; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember) ); path_arc_id pathArcId = reducedMember->firstPathArc; SCIP_Bool parentMoved = FALSE; @@ -5226,17 +5383,17 @@ SCIP_RETCODE splitSeries( moveArcToNewMember(dec, pathArc, member, pathMember); } - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, loopMember)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, loopMember) ); if( !parentMoved ) { - SCIP_CALL(createMarkerPair(dec, member, *loopMember, TRUE, FALSE, FALSE)); - SCIP_CALL(createMarkerPair(dec, *loopMember, pathMember, TRUE, FALSE, TRUE)); + SCIP_CALL( createMarkerPair(dec, member, *loopMember, TRUE, FALSE, FALSE) ); + SCIP_CALL( createMarkerPair(dec, *loopMember, pathMember, TRUE, FALSE, TRUE) ); } else { - SCIP_CALL(createMarkerPair(dec, pathMember, *loopMember, FALSE, FALSE, TRUE)); - SCIP_CALL(createMarkerPair(dec, *loopMember, member, FALSE, FALSE, FALSE)); + SCIP_CALL( createMarkerPair(dec, pathMember, *loopMember, FALSE, FALSE, TRUE) ); + SCIP_CALL( createMarkerPair(dec, *loopMember, member, FALSE, FALSE, FALSE) ); } setTerminalReversed(newColInfo, !reducedMember->pathBackwards); @@ -5292,7 +5449,7 @@ SCIP_RETCODE splitSeries( } spqr_member pathMember; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember) ); path_arc_id pathArcId = reducedMember->firstPathArc; SCIP_Bool parentMoved = FALSE; @@ -5308,11 +5465,11 @@ SCIP_RETCODE splitSeries( } if( parentMoved ) { - SCIP_CALL(createMarkerPair(dec, pathMember, member, FALSE, FALSE, FALSE)); + SCIP_CALL( createMarkerPair(dec, pathMember, member, FALSE, FALSE, FALSE) ); } else { - SCIP_CALL(createMarkerPair(dec, member, pathMember, TRUE, FALSE, FALSE)); + SCIP_CALL( createMarkerPair(dec, member, pathMember, TRUE, FALSE, FALSE) ); } changeLoopToParallel(dec, member); @@ -5323,9 +5480,11 @@ SCIP_RETCODE splitSeries( return SCIP_OKAY; } -/**< Very elaborate function that splits a series member into multiple members based on the structure of the path arcs. +/** Very elaborate function that splits a series member into multiple members based on the structure of the path arcs. + * * The updated member reflects the structure of the updated SPQR tree after the new column arc is added. - * This variant is only used on series members that are part of a reduced tree that is not a single member. */ + * This variant is only used on series members that are part of a reduced tree that is not a single member. + */ static SCIP_RETCODE splitSeriesMerging( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -5354,11 +5513,11 @@ SCIP_RETCODE splitSeriesMerging( if( createPathSeries ) { spqr_member pathMember; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember) ); path_arc_id pathArcId = reducedMember->firstPathArc; SCIP_Bool parentMoved = FALSE; - while( pathArcIsValid(pathArcId)) + while( pathArcIsValid(pathArcId) ) { spqr_arc pathArc = newCol->pathArcs[pathArcId].arc; pathArcId = newCol->pathArcs[pathArcId].nextMember; @@ -5373,18 +5532,18 @@ SCIP_RETCODE splitSeriesMerging( SCIP_Bool inNewReversed = FALSE; if( parentMoved ) { - SCIP_CALL(createMarkerPairWithReferences(dec, pathMember, member, FALSE, inNewReversed, inOldReversed, - &ignored, pathRepresentative)); + SCIP_CALL( createMarkerPairWithReferences(dec, pathMember, member, FALSE, inNewReversed, inOldReversed, + &ignored, pathRepresentative) ); } else { - SCIP_CALL(createMarkerPairWithReferences(dec, member, pathMember, TRUE, inOldReversed, inNewReversed, - pathRepresentative, &ignored)); + SCIP_CALL( createMarkerPairWithReferences(dec, member, pathMember, TRUE, inOldReversed, inNewReversed, + pathRepresentative, &ignored) ); } } else { - if( pathArcIsValid(reducedMember->firstPathArc)) + if( pathArcIsValid(reducedMember->firstPathArc) ) { *pathRepresentative = newCol->pathArcs[reducedMember->firstPathArc].arc; } @@ -5397,7 +5556,7 @@ SCIP_RETCODE splitSeriesMerging( if( createNonPathSeries ) { spqr_member nonPathMember; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &nonPathMember)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_SERIES, &nonPathMember) ); spqr_arc arc = getFirstMemberArc(dec, member); SCIP_Bool parentMoved = FALSE; @@ -5423,7 +5582,7 @@ SCIP_RETCODE splitSeriesMerging( while( TRUE ); /*lint !e506*/ assert(getNumMemberArcs(dec, nonPathMember) >= 2); SCIP_Bool representativeIsTree = !arcIsTree(dec, exceptionArc1); - if( SPQRarcIsValid(exceptionArc2)) + if( SPQRarcIsValid(exceptionArc2) ) { representativeIsTree = representativeIsTree || !arcIsTree(dec, exceptionArc2); } @@ -5432,13 +5591,13 @@ SCIP_RETCODE splitSeriesMerging( SCIP_Bool inNewReversed = FALSE; if( parentMoved ) { - SCIP_CALL(createMarkerPairWithReferences(dec, nonPathMember, member, !representativeIsTree, - inNewReversed, inOldReversed, &ignored, nonPathRepresentative)); + SCIP_CALL( createMarkerPairWithReferences(dec, nonPathMember, member, !representativeIsTree, + inNewReversed, inOldReversed, &ignored, nonPathRepresentative) ); } else { - SCIP_CALL(createMarkerPairWithReferences(dec, member, nonPathMember, representativeIsTree, - inOldReversed, inNewReversed, nonPathRepresentative, &ignored)); + SCIP_CALL( createMarkerPairWithReferences(dec, member, nonPathMember, representativeIsTree, + inOldReversed, inNewReversed, nonPathRepresentative, &ignored) ); } } else @@ -5465,7 +5624,7 @@ SCIP_RETCODE splitSeriesMerging( return SCIP_OKAY; } -/**< Transforms the first member in the path of members to reflect the new column update */ +/** Transforms the first member in the path of members to reflect the new column update. */ static SCIP_RETCODE transformFirstPathMember( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -5504,8 +5663,8 @@ SCIP_RETCODE transformFirstPathMember( SPQRColReducedMember* redMem = &newCol->reducedMembers[reducedMember]; spqr_arc pathRepresentative = SPQR_INVALID_ARC; spqr_arc nonPathRepresentative = SPQR_INVALID_ARC; - SCIP_CALL(splitSeriesMerging(dec, newCol, redMem, member, &pathRepresentative, &nonPathRepresentative, target, - SPQR_INVALID_ARC)); + SCIP_CALL( splitSeriesMerging(dec, newCol, redMem, member, &pathRepresentative, &nonPathRepresentative, target, + SPQR_INVALID_ARC) ); assert( target != pathRepresentative && target != nonPathRepresentative && pathRepresentative != nonPathRepresentative); @@ -5516,9 +5675,9 @@ SCIP_RETCODE transformFirstPathMember( spqr_node a = SPQR_INVALID_NODE; spqr_node b = SPQR_INVALID_NODE; spqr_node c = SPQR_INVALID_NODE; - SCIP_CALL(createNode(dec, &a)); - SCIP_CALL(createNode(dec, &b)); - SCIP_CALL(createNode(dec, &c)); + SCIP_CALL( createNode(dec, &a) ); + SCIP_CALL( createNode(dec, &b) ); + SCIP_CALL( createNode(dec, &c) ); // a -- b // \ / @@ -5570,7 +5729,7 @@ SCIP_RETCODE transformFirstPathMember( return SCIP_OKAY; } -/**< Transforms the next parallel member in the path of members and merge it into the current member */ +/** Transforms the next parallel member in the path of members and merge it into the current member. */ static SCIP_RETCODE transformAndMergeParallel( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -5589,7 +5748,7 @@ SCIP_RETCODE transformAndMergeParallel( spqr_arc target = newCol->reducedMembers[next].pathTargetArc; if( getNumMemberArcs(dec, nextMember) > 3 ) { - SCIP_CALL(splitParallel(dec, nextMember, source, target, &childParallel)); + SCIP_CALL( splitParallel(dec, nextMember, source, target, &childParallel) ); nextMember = childParallel; newCol->reducedMembers[next].member = childParallel; } @@ -5597,8 +5756,8 @@ SCIP_RETCODE transformAndMergeParallel( spqr_node sourceHead = SPQR_INVALID_NODE; spqr_node sourceTail = SPQR_INVALID_NODE; - SCIP_CALL(createNode(dec, &sourceHead)); - SCIP_CALL(createNode(dec, &sourceTail)); + SCIP_CALL( createNode(dec, &sourceHead) ); + SCIP_CALL( createNode(dec, &sourceTail) ); //set edge nodes and arc union-find data { @@ -5632,22 +5791,22 @@ SCIP_RETCODE transformAndMergeParallel( spqr_member newMergedMember = SPQR_INVALID_MEMBER; if( nextIsParent ) { - SCIP_CALL(mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, - source, newCol->reducedMembers[current].pathTargetArc, TRUE, - &newMergedMember)); + SCIP_CALL( mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, + source, newCol->reducedMembers[current].pathTargetArc, TRUE, + &newMergedMember) ); } else { - SCIP_CALL(mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, - newCol->reducedMembers[current].pathTargetArc, source, TRUE, - &newMergedMember)); + SCIP_CALL( mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, + newCol->reducedMembers[current].pathTargetArc, source, TRUE, + &newMergedMember) ); } *mergedMember = newMergedMember; return SCIP_OKAY; } -/**< Transforms the next series member in the path of members and merge it into the current member */ +/** Transforms the next series member in the path of members and merge it into the current member. */ static SCIP_RETCODE transformAndMergeSeries( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -5661,7 +5820,6 @@ SCIP_RETCODE transformAndMergeSeries( NewColInformation* info /**< The new column information */ ) { - SPQRColReducedMember* redMem = &newCol->reducedMembers[next]; spqr_arc source = redMem->pathSourceArc; spqr_arc target = redMem->pathTargetArc; @@ -5669,7 +5827,7 @@ SCIP_RETCODE transformAndMergeSeries( spqr_arc pathRepresentative = SPQR_INVALID_ARC; spqr_arc nonPathRepresentative = SPQR_INVALID_ARC; SCIP_CALL( - splitSeriesMerging(dec, newCol, redMem, nextMember, &pathRepresentative, &nonPathRepresentative, source, target)); + splitSeriesMerging(dec, newCol, redMem, nextMember, &pathRepresentative, &nonPathRepresentative, source, target) ); //After splitting there is the following possibilities for nodes a-d: //(a)-source-(b)-path-(c)-target-(d)-nonpath-(a) //(a)-source-(b)-path-(c)-target-(d==a) @@ -5683,11 +5841,11 @@ SCIP_RETCODE transformAndMergeSeries( spqr_node b = SPQR_INVALID_NODE; spqr_node c = SPQR_INVALID_NODE; spqr_node d = SPQR_INVALID_NODE; - SCIP_CALL(createNode(dec, &a)); - SCIP_CALL(createNode(dec, &b)); - if( SPQRarcIsValid(pathRepresentative)) + SCIP_CALL( createNode(dec, &a) ); + SCIP_CALL( createNode(dec, &b) ); + if( SPQRarcIsValid(pathRepresentative) ) { - SCIP_CALL(createNode(dec, &c)); + SCIP_CALL( createNode(dec, &c) ); } else { @@ -5697,7 +5855,7 @@ SCIP_RETCODE transformAndMergeSeries( SCIP_Bool hasTarget = SPQRarcIsValid(target); if( hasNonPath && hasTarget ) { - SCIP_CALL(createNode(dec, &d)); + SCIP_CALL( createNode(dec, &d) ); } else { @@ -5723,7 +5881,7 @@ SCIP_RETCODE transformAndMergeSeries( } if( SPQRarcIsValid(pathRepresentative)) { - if(( arcIsReversedNonRigid(dec, pathRepresentative) == sourceReversed ) == pathStartInHead ) + if( (arcIsReversedNonRigid(dec, pathRepresentative) == sourceReversed) == pathStartInHead ) { setArcHeadAndTail(dec, pathRepresentative, c, b); } @@ -5736,7 +5894,7 @@ SCIP_RETCODE transformAndMergeSeries( } if( hasTarget ) { - if(( arcIsReversedNonRigid(dec, target) == sourceReversed ) == pathStartInHead ) + if( (arcIsReversedNonRigid(dec, target) == sourceReversed) == pathStartInHead ) { setArcHeadAndTail(dec, target, d, c); } @@ -5749,7 +5907,7 @@ SCIP_RETCODE transformAndMergeSeries( } if( hasNonPath ) { - if(( arcIsReversedNonRigid(dec, nonPathRepresentative) == sourceReversed ) == pathStartInHead ) + if( (arcIsReversedNonRigid(dec, nonPathRepresentative) == sourceReversed) == pathStartInHead ) { setArcHeadAndTail(dec, nonPathRepresentative, a, d); } @@ -5769,15 +5927,15 @@ SCIP_RETCODE transformAndMergeSeries( spqr_member newMergedMember = SPQR_INVALID_MEMBER; if( nextIsParent ) { - SCIP_CALL(mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, - source, newCol->reducedMembers[current].pathTargetArc, TRUE, - &newMergedMember)); + SCIP_CALL( mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, + source, newCol->reducedMembers[current].pathTargetArc, TRUE, + &newMergedMember) ); } else { - SCIP_CALL(mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, - newCol->reducedMembers[current].pathTargetArc, source, TRUE, - &newMergedMember)); + SCIP_CALL( mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, + newCol->reducedMembers[current].pathTargetArc, source, TRUE, + &newMergedMember) ); } *mergedMember = newMergedMember; @@ -5797,10 +5955,11 @@ SCIP_RETCODE transformAndMergeSeries( setTerminalMember(info, *mergedMember); setTerminalRepresentative(info, *representativeArc); } + return SCIP_OKAY; } -/**< Transforms the next rigid member in the path of members and merge it into the current member */ +/** Transforms the next rigid member in the path of members and merge it into the current member. */ static SCIP_RETCODE transformAndMergeRigid( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -5822,26 +5981,26 @@ SCIP_RETCODE transformAndMergeRigid( if( nextIsParent ) { - SCIP_CALL(mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, - source, newCol->reducedMembers[current].pathTargetArc, !redMem->reverseArcs, - &newMergedMember)); + SCIP_CALL( mergeGivenMemberIntoParent(dec, *mergedMember, nextMember, + source, newCol->reducedMembers[current].pathTargetArc, !redMem->reverseArcs, + &newMergedMember) ); } else { - SCIP_CALL(mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, - newCol->reducedMembers[current].pathTargetArc, source, !redMem->reverseArcs, - &newMergedMember)); + SCIP_CALL( mergeGivenMemberIntoParent(dec, nextMember, *mergedMember, + newCol->reducedMembers[current].pathTargetArc, source, !redMem->reverseArcs, + &newMergedMember) ); } *mergedMember = newMergedMember; *representativeArc = mergeArcSigns(dec, *representativeArc, sourceRepresentative, redMem->reverseArcs); - if( SPQRarcIsInvalid(redMem->pathTargetArc)) + if( SPQRarcIsInvalid(redMem->pathTargetArc) ) { //We are in the last node; finish the path setTerminalReversed(info, FALSE); - if( isInto(newCol->reducedMembers[current].pathType)) + if( isInto(newCol->reducedMembers[current].pathType) ) { if( redMem->reverseArcs ) { @@ -5870,7 +6029,7 @@ SCIP_RETCODE transformAndMergeRigid( return SCIP_OKAY; } -/**< Transforms the next member in the path of members and merge it into the current member */ +/** Transforms the next member in the path of members and merge it into the current member. */ static SCIP_RETCODE transformAndMerge( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -5884,24 +6043,24 @@ SCIP_RETCODE transformAndMerge( ) { spqr_member nextMember = newCol->reducedMembers[next].member; - switch( getMemberType(dec, nextMember)) + switch( getMemberType(dec, nextMember) ) { case SPQR_MEMBERTYPE_RIGID: { - SCIP_CALL(transformAndMergeRigid(dec, newCol, current, next, nextMember, nextIsParent, - representativeArc, mergedMember, info)); + SCIP_CALL( transformAndMergeRigid(dec, newCol, current, next, nextMember, nextIsParent, + representativeArc, mergedMember, info) ); break; } case SPQR_MEMBERTYPE_PARALLEL: { - SCIP_CALL(transformAndMergeParallel(dec, newCol, current, next, nextMember, nextIsParent, - representativeArc, mergedMember)); + SCIP_CALL( transformAndMergeParallel(dec, newCol, current, next, nextMember, nextIsParent, + representativeArc, mergedMember) ); break; } case SPQR_MEMBERTYPE_SERIES: { - SCIP_CALL(transformAndMergeSeries(dec, newCol, current, next, nextMember, nextIsParent, - representativeArc, mergedMember, info)); + SCIP_CALL( transformAndMergeSeries(dec, newCol, current, next, nextMember, nextIsParent, + representativeArc, mergedMember, info) ); break; } case SPQR_MEMBERTYPE_LOOP: @@ -5914,7 +6073,7 @@ SCIP_RETCODE transformAndMerge( return SCIP_OKAY; } -/**< Transforms a single component when it contains multiple reduced members. */ +/** Transforms a single component when it contains multiple reduced members. */ static SCIP_RETCODE transformPath( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -5928,12 +6087,12 @@ SCIP_RETCODE transformPath( spqr_arc representativeArc = SPQR_INVALID_ARC; spqr_member mergedMember = SPQR_INVALID_MEMBER; - SCIP_CALL(transformFirstPathMember(dec, newCol, firstPathMember, newColInfo, &representativeArc, &mergedMember)); + SCIP_CALL( transformFirstPathMember(dec, newCol, firstPathMember, newColInfo, &representativeArc, &mergedMember) ); //Iteratively call function which realizes next member and merges them together. reduced_member_id current = firstPathMember; reduced_member_id next = newCol->reducedMembers[current].nextPathMember; SCIP_Bool nextIsParent = newCol->reducedMembers[current].nextPathMemberIsParent; - while( reducedMemberIsValid(next)) + while( reducedMemberIsValid(next) ) { SCIP_CALL( transformAndMerge(dec, newCol, current, next, &representativeArc, &mergedMember, nextIsParent, newColInfo)); @@ -5941,10 +6100,11 @@ SCIP_RETCODE transformPath( next = newCol->reducedMembers[current].nextPathMember; nextIsParent = newCol->reducedMembers[current].nextPathMemberIsParent; } + return SCIP_OKAY; } -/**< Transform a single parallel member to add the new column */ +/** Transform a single parallel member to add the new column. */ static SCIP_RETCODE columnTransformSingleParallel( SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */ @@ -5958,10 +6118,11 @@ SCIP_RETCODE columnTransformSingleParallel( //The new arc can be placed in parallel; just add it to this member setTerminalReversed(newColInfo, reducedMember->pathBackwards); setTerminalMember(newColInfo, member); + return SCIP_OKAY; } -/**< Transform a single series member to add the new column */ +/** Transform a single series member to add the new column. */ static SCIP_RETCODE columnTransformSingleSeries( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -5971,7 +6132,6 @@ SCIP_RETCODE columnTransformSingleSeries( NewColInformation* newColInfo /**< The new column information */ ) { - if( getNumMemberArcs(dec, member) == 1 ) { newColInfo->member = member; @@ -5981,12 +6141,12 @@ SCIP_RETCODE columnTransformSingleSeries( //Isolated single cycle spqr_member loopMember; SPQRColReducedMember* reducedMember = &newCol->reducedMembers[reducedMemberId]; - SCIP_CALL(splitSeries(dec, newCol, reducedMember, member, &loopMember, newColInfo)); + SCIP_CALL( splitSeries(dec, newCol, reducedMember, member, &loopMember, newColInfo) ); return SCIP_OKAY; } -/**< Transform a single rigid member to add the new column */ +/** Transform a single rigid member to add the new column. */ static SCIP_RETCODE columnTransformSingleRigid( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -6000,6 +6160,7 @@ SCIP_RETCODE columnTransformSingleRigid( assert(newCol); assert(newColInfo); assert(reducedMemberIsValid(reducedMemberId)); + //The path is already computed, so we can simply take the start and end nodes. //However, there is one exception, which is that an arc connecting these two nodes already exists in the member //If so, we create a new parallel member with the new arc and this member, unless the existing arc already points @@ -6025,12 +6186,12 @@ SCIP_RETCODE columnTransformSingleRigid( arc = getNextNodeArc(dec, arc, reducedMember->rigidPathStart); } while( arc != firstArc ); - if( SPQRarcIsValid(existingArcWithPath)) + if( SPQRarcIsValid(existingArcWithPath) ) { SCIP_Bool isParent = FALSE; spqr_member adjacentMember = arcIsChildMarker(dec, existingArcWithPath) ? findArcChildMember(dec, existingArcWithPath) : SPQR_INVALID_MEMBER; - if( existingArcWithPath == markerToParent(dec, member)) + if( existingArcWithPath == markerToParent(dec, member) ) { adjacentMember = findMemberParent(dec, member); isParent = TRUE; @@ -6049,30 +6210,30 @@ SCIP_RETCODE columnTransformSingleRigid( //So what we do instead, is convert the current edge to a marker edge, and 'duplicate' //it in the new parallel member, and add the new marker there too, manually spqr_member adjacentParallel = SPQR_INVALID_MEMBER; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &adjacentParallel)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &adjacentParallel) ); //'duplicate' a new arc in the parallel to be the current arc spqr_arc duplicate = SPQR_INVALID_ARC; spqr_element element = arcGetElement(dec, existingArcWithPath); - if( element != MARKER_COLUMN_ELEMENT && element != MARKER_ROW_ELEMENT) + if( element != MARKER_COLUMN_ELEMENT && element != MARKER_ROW_ELEMENT ) { - if( SPQRelementIsColumn(element)) + if( SPQRelementIsColumn(element) ) { - SCIP_CALL(createColumnArc(dec, adjacentParallel, &duplicate, SPQRelementToColumn(element), FALSE)); + SCIP_CALL( createColumnArc(dec, adjacentParallel, &duplicate, SPQRelementToColumn(element), FALSE) ); } else { - SCIP_CALL(createRowArc(dec, adjacentParallel, &duplicate, SPQRelementToRow(element), FALSE)); + SCIP_CALL( createRowArc(dec, adjacentParallel, &duplicate, SPQRelementToRow(element), FALSE) ); } } else if( isParent ) { - SCIP_CALL(createParentMarker(dec, adjacentParallel, arcIsTree(dec, existingArcWithPath), adjacentMember, - markerOfParent(dec, member), &duplicate, FALSE)); + SCIP_CALL( createParentMarker(dec, adjacentParallel, arcIsTree(dec, existingArcWithPath), adjacentMember, + markerOfParent(dec, member), &duplicate, FALSE) ); } else { - SCIP_CALL(createChildMarker(dec, adjacentParallel, adjacentMember, arcIsTree(dec, existingArcWithPath), - &duplicate, FALSE)); + SCIP_CALL( createChildMarker(dec, adjacentParallel, adjacentMember, arcIsTree(dec, existingArcWithPath), + &duplicate, FALSE) ); dec->members[adjacentMember].parentMember = adjacentParallel; dec->members[adjacentMember].markerOfParent = duplicate; } @@ -6080,13 +6241,13 @@ SCIP_RETCODE columnTransformSingleRigid( spqr_arc parallelMarker = SPQR_INVALID_ARC; if( isParent ) { - SCIP_CALL(createChildMarker(dec, adjacentParallel, member, !arcIsTree(dec, existingArcWithPath), - ¶llelMarker, FALSE)); + SCIP_CALL( createChildMarker(dec, adjacentParallel, member, !arcIsTree(dec, existingArcWithPath), + ¶llelMarker, FALSE) ); } else { - SCIP_CALL(createParentMarker(dec, adjacentParallel, !arcIsTree(dec, existingArcWithPath), - member, existingArcWithPath, ¶llelMarker, FALSE)); + SCIP_CALL( createParentMarker(dec, adjacentParallel, !arcIsTree(dec, existingArcWithPath), + member, existingArcWithPath, ¶llelMarker, FALSE) ); } //Change the existing edge to a marker @@ -6122,10 +6283,11 @@ SCIP_RETCODE columnTransformSingleRigid( setTerminalHead(newColInfo, reducedMember->rigidPathEnd); setTerminalRepresentative(newColInfo, findArcSign(dec, newCol->pathArcs[reducedMember->firstPathArc].arc).representative); + return SCIP_OKAY; } -/**< Transform a component to reflect the new column */ +/** Transform a component to reflect the new column. */ static SCIP_RETCODE transformComponent( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -6152,18 +6314,18 @@ SCIP_RETCODE transformComponent( { case SPQR_MEMBERTYPE_RIGID: { - SCIP_CALL(columnTransformSingleRigid(dec, newCol, reducedMember, member, newColInfo)); + SCIP_CALL( columnTransformSingleRigid(dec, newCol, reducedMember, member, newColInfo) ); break; } case SPQR_MEMBERTYPE_PARALLEL: { - SCIP_CALL(columnTransformSingleParallel(newCol, reducedMember, member, newColInfo)); + SCIP_CALL( columnTransformSingleParallel(newCol, reducedMember, member, newColInfo) ); break; } case SPQR_MEMBERTYPE_LOOP: case SPQR_MEMBERTYPE_SERIES: { - SCIP_CALL(columnTransformSingleSeries(dec, newCol, reducedMember, member, newColInfo)); + SCIP_CALL( columnTransformSingleSeries(dec, newCol, reducedMember, member, newColInfo) ); break; } case SPQR_MEMBERTYPE_UNASSIGNED: @@ -6176,12 +6338,12 @@ SCIP_RETCODE transformComponent( return SCIP_OKAY; } // Otherwise, the reduced members form a path which can be merged into a single component of type R - SCIP_CALL(transformPath(dec, newCol, component, newColInfo)); + SCIP_CALL( transformPath(dec, newCol, component, newColInfo) ); return SCIP_OKAY; } -/**< Check if the submatrix stored remains a network matrix with the new column update */ +/** Check if the submatrix stored remains a network matrix with the new column update. */ static SCIP_Bool SCIPnetcoladdRemainsNetwork( const SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */ @@ -6190,8 +6352,10 @@ SCIP_Bool SCIPnetcoladdRemainsNetwork( return newCol->remainsNetwork; } -/**< Add the new column to the network decomposition as an arc. Only use this function after SCIPnetcoladdCheck() has - * been called. */ +/** Add the new column to the network decomposition as an arc. + * + * Only use this function after SCIPnetcoladdCheck() has been called. + */ static SCIP_RETCODE SCIPnetcoladdAdd( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -6205,19 +6369,19 @@ SCIP_RETCODE SCIPnetcoladdAdd( if( newCol->numReducedComponents == 0 ) { spqr_member member; - SCIP_CALL(createStandaloneSeries(dec, newCol->newRowArcs, newCol->newRowArcReversed, - newCol->numNewRowArcs, newCol->newColIndex, &member)); + SCIP_CALL( createStandaloneSeries(dec, newCol->newRowArcs, newCol->newRowArcReversed, + newCol->numNewRowArcs, newCol->newColIndex, &member) ); } else if( newCol->numReducedComponents == 1 ) { NewColInformation information = emptyNewColInformation(); - SCIP_CALL(transformComponent(dec, newCol, &newCol->reducedComponents[0], &information)); + SCIP_CALL( transformComponent(dec, newCol, &newCol->reducedComponents[0], &information) ); assert(memberIsRepresentative(dec, information.member)); if( newCol->numNewRowArcs == 0 ) { spqr_arc colArc = SPQR_INVALID_ARC; - SCIP_CALL(createColumnArc(dec, information.member, &colArc, newCol->newColIndex, information.reversed)); - if( SPQRnodeIsValid(information.head)) + SCIP_CALL( createColumnArc(dec, information.member, &colArc, newCol->newColIndex, information.reversed) ); + if( SPQRnodeIsValid(information.head) ) { assert(SPQRnodeIsValid(information.tail)); assert(SPQRarcIsValid(information.representative)); @@ -6229,13 +6393,13 @@ SCIP_RETCODE SCIPnetcoladdAdd( else { spqr_member newSeries = SPQR_INVALID_MEMBER; - SCIP_CALL(createConnectedSeries(dec, newCol->newRowArcs, newCol->newRowArcReversed, newCol->numNewRowArcs, - newCol->newColIndex, &newSeries)); + SCIP_CALL( createConnectedSeries(dec, newCol->newRowArcs, newCol->newRowArcReversed, newCol->numNewRowArcs, + newCol->newColIndex, &newSeries) ); spqr_arc markerArc = SPQR_INVALID_ARC; spqr_arc ignore = SPQR_INVALID_ARC; - SCIP_CALL(createMarkerPairWithReferences(dec, information.member, newSeries, FALSE, information.reversed, TRUE, - &markerArc, &ignore)); - if( SPQRnodeIsValid(information.head)) + SCIP_CALL( createMarkerPairWithReferences(dec, information.member, newSeries, FALSE, information.reversed, TRUE, + &markerArc, &ignore) ); + if( SPQRnodeIsValid(information.head) ) { assert(SPQRnodeIsValid(information.tail)); assert(SPQRarcIsValid(information.representative)); @@ -6260,12 +6424,12 @@ SCIP_RETCODE SCIPnetcoladdAdd( int numDecComponentsBefore = numConnectedComponents(dec); #endif spqr_member newSeries = SPQR_INVALID_MEMBER; - SCIP_CALL(createConnectedSeries(dec, newCol->newRowArcs, newCol->newRowArcReversed, - newCol->numNewRowArcs, newCol->newColIndex, &newSeries)); + SCIP_CALL( createConnectedSeries(dec, newCol->newRowArcs, newCol->newRowArcReversed, + newCol->numNewRowArcs, newCol->newColIndex, &newSeries) ); for( int i = 0; i < newCol->numReducedComponents; ++i ) { NewColInformation information = emptyNewColInformation(); - SCIP_CALL(transformComponent(dec, newCol, &newCol->reducedComponents[i], &information)); + SCIP_CALL( transformComponent(dec, newCol, &newCol->reducedComponents[i], &information) ); if( getMemberType(dec, information.member) == SPQR_MEMBERTYPE_LOOP ) { assert(getNumMemberArcs(dec, information.member) == 1); @@ -6277,13 +6441,12 @@ SCIP_RETCODE SCIPnetcoladdAdd( } else { - reorderComponent(dec, - information.member);//reorder the subtree so that the newly series member is a parent + reorderComponent(dec, information.member);//reorder the subtree so that the newly series member is a parent spqr_arc markerArc = SPQR_INVALID_ARC; spqr_arc ignore = SPQR_INVALID_ARC; - SCIP_CALL(createMarkerPairWithReferences(dec, newSeries, information.member, TRUE, information.reversed, - TRUE, &ignore, &markerArc)); - if( SPQRnodeIsValid(information.head)) + SCIP_CALL( createMarkerPairWithReferences(dec, newSeries, information.member, TRUE, information.reversed, + TRUE, &ignore, &markerArc) ); + if( SPQRnodeIsValid(information.head) ) { assert(SPQRnodeIsValid(information.tail)); assert(SPQRarcIsValid(information.representative)); @@ -6297,14 +6460,15 @@ SCIP_RETCODE SCIPnetcoladdAdd( decreaseNumConnectedComponents(dec, newCol->numReducedComponents - 1); assert(numConnectedComponents(dec) == ( numDecComponentsBefore - newCol->numReducedComponents + 1 )); } + return SCIP_OKAY; } -/** ---------- END functions for column addition ---------------------------------------------------------------------*/ +/* ---------- END functions for column addition --------------------------------------------------------------------- */ -/** ---------- START functions for row addition ----------------------------------------------------------------------*/ +/* ---------- START functions for row addition ---------------------------------------------------------------------- */ -/**< Computes the minimum of two values */ +/** Computes the minimum of two values */ static int minValue( int a, @@ -6314,11 +6478,11 @@ int minValue( return a < b ? a : b; } -/**< Index type for the nonzeros of the new row, e.g. the columns whose tree path must be elongated with the new row */ +/** Index type for the nonzeros of the new row, e.g. the columns whose tree path must be elongated with the new row */ typedef int cut_arc_id; #define INVALID_CUT_ARC (-1) -/**< Checks if the given cut arc is invalid (negative) */ +/** Checks if the given cut arc is invalid (negative). */ static SCIP_Bool cutArcIsInvalid( const cut_arc_id arc /**< The cut arc to check */ @@ -6327,7 +6491,7 @@ SCIP_Bool cutArcIsInvalid( return arc < 0; } -/**< Checks if the given cut arc is valid (nonnegative) */ +/** Checks if the given cut arc is valid (nonnegative). */ static SCIP_Bool cutArcIsValid( const cut_arc_id arc /**< The cut arc to check */ ) @@ -6335,22 +6499,26 @@ static SCIP_Bool cutArcIsValid( return !cutArcIsInvalid(arc); } -/**< Linked list node for the cut arcs. Contains links to the next cut arc in the whole decomposition and the next cut - * arc in the current member. */ +/** Linked list node for the cut arcs. + * + * Contains links to the next cut arc in the whole decomposition and the next cut arc in the current member. + */ typedef struct { - spqr_arc arc; /**< The arc id */ - spqr_node arcHead; /**< The arc's head node */ - spqr_node arcTail; /**< The arc's tail node */ - cut_arc_id nextMember; /**< Index to next linked list node of the linked list containing the cut arcs + spqr_arc arc; /**< The arc id */ + spqr_node arcHead; /**< The arc's head node */ + spqr_node arcTail; /**< The arc's tail node */ + cut_arc_id nextMember; /**< Index to next linked list node of the linked list containing the cut arcs * of the arcs member.*/ - cut_arc_id nextOverall; /**< Index to next linked list node of the linked list containing the cut arcs + cut_arc_id nextOverall; /**< Index to next linked list node of the linked list containing the cut arcs * of the complete decomposition.*/ - SCIP_Bool arcReversed; /**< Should the new row have reverse direction in the cut arcs path? */ + SCIP_Bool arcReversed; /**< Should the new row have reverse direction in the cut arcs path? */ } CutArcListNode; -/**< Type of the reduced member; indicates whether the member is processed, and whether it should be merged or left - * the same. */ +/** Type of the reduced member + * + * Indicates whether the member is processed, and whether it should be merged or left the same. + */ typedef enum { TYPE_UNDETERMINED = 0, /**< The type of this member has not yet been determined */ @@ -6360,45 +6528,48 @@ typedef enum * does not create a network matrix */ } RowReducedMemberType; -/**< Stores for every vertex information needed for computing articulation nodes in rigid members. */ +/** Stores for every vertex information needed for computing articulation nodes in rigid members. */ typedef struct { - int low; /**< What is the lowest discovery time vertex that can be reached from this + int low; /**< What is the lowest discovery time vertex that can be reached from this * subtree?*/ - int discoveryTime; /**< When was the vertex first discovered? */ + int discoveryTime; /**< When was the vertex first discovered? */ } ArticulationNodeInformation; -/**< Call stack data structure for performing DFS on the rigid member graphs */ +/** Call stack data structure for performing DFS on the rigid member graphs. */ typedef struct { - spqr_node node; /**< The current node of the DFS call */ - spqr_arc nodeArc; /**< The current arc of the node that is being processed */ + spqr_node node; /**< The current node of the DFS call */ + spqr_arc nodeArc; /**< The current arc of the node that is being processed */ } DFSCallData; -/**< Call stack data structure for merging the SPQR tree into a single rigid member */ +/** Call stack data structure for merging the SPQR tree into a single rigid member. */ typedef struct { - children_idx currentChild; /**< The index of the current child that is being merged */ - reduced_member_id id; /**< The index of the current member */ + children_idx currentChild; /**< The index of the current child that is being merged */ + reduced_member_id id; /**< The index of the current member */ } MergeTreeCallData; -/**< Call stack data structure that determines whether a bipartition of the nodes exists that satisfies the requirements*/ +/** Call stack data structure that determines whether a bipartition of the nodes exists that satisfies the requirements. */ typedef struct { - spqr_node node; /**< The current node of the call */ - spqr_arc arc; /**< The current arc that is being processed */ + spqr_node node; /**< The current node of the call */ + spqr_arc arc; /**< The current arc that is being processed */ } ColorDFSCallData; -/**< Call stack data structure for recursive algorithm to find articulation point in rigid member graphs (Tarjan) */ +/** Call stack data structure for recursive algorithm to find articulation point in rigid member graphs (Tarjan). */ typedef struct { - spqr_arc arc; /**< The arc that is currently being processed */ - spqr_node node; /**< The node that is currently being processed */ - spqr_node parent; /**< The node's parent */ - SCIP_Bool isAP; /**< Is the current node an articulation point? */ + spqr_arc arc; /**< The arc that is currently being processed */ + spqr_node node; /**< The node that is currently being processed */ + spqr_node parent; /**< The node's parent */ + SCIP_Bool isAP; /**< Is the current node an articulation point? */ } ArticulationPointCallStack; -/**< Colors assigned to the node in the partitioning algorithm. It must either be in the source or the sink partition.*/ +/** Colors assigned to the node in the partitioning algorithm. + * + * It must either be in the source or the sink partition. + */ typedef enum { UNCOLORED = 0, /**< The current node has not yet been processed */ @@ -6406,154 +6577,154 @@ typedef enum COLOR_SINK = 2 /**< The current node belongs to the sink partition */ } COLOR_STATUS; -/**< Struct that stores the data of a single reduced member. */ +/** Struct that stores the data of a single reduced member. */ typedef struct { - spqr_member member; /**< The id of the decomposition member */ - spqr_member rootMember; /**< The decomposition member that is the root node of the arborescence + spqr_member member; /**< The id of the decomposition member */ + spqr_member rootMember; /**< The decomposition member that is the root node of the arborescence * containing this member */ - int depth; /**< The depth of this member in the arborescence */ - RowReducedMemberType type; /**< The type of the member */ - reduced_member_id parent; /**< The reduced member id of the parent of this reduced member */ + int depth; /**< The depth of this member in the arborescence */ + RowReducedMemberType type; /**< The type of the member */ + reduced_member_id parent; /**< The reduced member id of the parent of this reduced member */ - children_idx firstChild; /**< The index of the first child in the children array. */ - children_idx numChildren; /**< The number of children in the arborescence of this reduced member */ - children_idx numPropagatedChildren; /**< Counts the number of children that are propagated to this reduced - * member */ + children_idx firstChild; /**< The index of the first child in the children array. */ + children_idx numChildren; /**< The number of children in the arborescence of this reduced member */ + children_idx numPropagatedChildren; /**< Counts the number of children that are propagated to this reduced + * member */ - cut_arc_id firstCutArc; /**< Head of the linked list containing the cut arcs */ - int numCutArcs; /**< The number of cut arcs in the linked list */ + cut_arc_id firstCutArc; /**< Head of the linked list containing the cut arcs */ + int numCutArcs; /**< The number of cut arcs in the linked list */ - spqr_arc splitArc; /**< An arc adjacent to the split node. */ - SCIP_Bool splitHead; /**< Is the head or the tail of the split arc split in the realization? */ - SCIP_Bool otherIsSource; /**< Is the nonsplit node of the split arc in the source or the sink + spqr_arc splitArc; /**< An arc adjacent to the split node. */ + SCIP_Bool splitHead; /**< Is the head or the tail of the split arc split in the realization? */ + SCIP_Bool otherIsSource; /**< Is the nonsplit node of the split arc in the source or the sink * partition? In rigid members this refers to the articulation arc. */ /* Rigid member fields */ - spqr_node otherNode; /**< The other nonsplit node adjacent to the virtual edge */ - spqr_node splitNode; /**< The node to be split */ - SCIP_Bool allHaveCommonNode; /**< Do all cut edges share a common node? */ - SCIP_Bool otherNodeSplit; /**< Is the other node a split node, too? */ - SCIP_Bool willBeReversed; /**< Will all the arcs in this component be reversed? */ - spqr_arc articulationArc; /**< Indicates the arc between the split node and the other ndoe, if both + spqr_node otherNode; /**< The other nonsplit node adjacent to the virtual edge */ + spqr_node splitNode; /**< The node to be split */ + SCIP_Bool allHaveCommonNode; /**< Do all cut edges share a common node? */ + SCIP_Bool otherNodeSplit; /**< Is the other node a split node, too? */ + SCIP_Bool willBeReversed; /**< Will all the arcs in this component be reversed? */ + spqr_arc articulationArc; /**< Indicates the arc between the split node and the other ndoe, if both * are splittable. */ - spqr_node coloredNode; /**< Points to a colored node so that we can efficiently zero out colors + spqr_node coloredNode; /**< Points to a colored node so that we can efficiently zero out colors * by backtracking our DFS */ } SPQRRowReducedMember; -/**< Keeps track of the data relevant for each SPQR tree in the SPQR forest. */ +/** Keeps track of the data relevant for each SPQR tree in the SPQR forest. */ typedef struct { - int rootDepth; /**< The depth of the root node of the subtree in the arborescence */ - reduced_member_id root; /**< The reduced member id of the root */ + int rootDepth; /**< The depth of the root node of the subtree in the arborescence */ + reduced_member_id root; /**< The reduced member id of the root */ } SPQRRowReducedComponent; -/**< The main datastructure that manages all the data for row-addition in network matrices */ +/** The main datastructure that manages all the data for row-addition in network matrices */ typedef struct { - SCIP_Bool remainsNetwork; /**< Does the addition of the current row give a network matrix? */ + SCIP_Bool remainsNetwork; /**< Does the addition of the current row give a network matrix? */ SPQRRowReducedMember* reducedMembers; /**< The array of reduced members, that form the subtree containing the * rows of the current column.*/ - int memReducedMembers; /**< Number of allocated slots in the reduced member array */ - int numReducedMembers; /**< Number of used slots in the reduced member array */ + int memReducedMembers; /**< Number of allocated slots in the reduced member array */ + int numReducedMembers; /**< Number of used slots in the reduced member array */ - SPQRRowReducedComponent* reducedComponents;/**< The array of reduced components, - * that represent the SPQR trees in the SPQR forest */ - int memReducedComponents; /**< Number of allocated slots in the reduced component array */ - int numReducedComponents; /**< Number of used slots in the reduced component array */ + SPQRRowReducedComponent* reducedComponents; /**< The array of reduced components, + * that represent the SPQR trees in the SPQR forest */ + int memReducedComponents; /**< Number of allocated slots in the reduced component array */ + int numReducedComponents; /**< Number of used slots in the reduced component array */ - MemberInfo* memberInformation; /**< Array with member information; tracks the reduced member id that + MemberInfo* memberInformation; /**< Array with member information; tracks the reduced member id that * corresponds to every member in the decomposition. */ - int memMemberInformation; /**< Number of allocated slots in the member information array */ - int numMemberInformation; /**< Number of used slots in the member information array */ + int memMemberInformation; /**< Number of allocated slots in the member information array */ + int numMemberInformation; /**< Number of used slots in the member information array */ - reduced_member_id* childrenStorage; /**< Array that stores the children of the reduced member arborescences. + reduced_member_id* childrenStorage; /**< Array that stores the children of the reduced member arborescences. * Each reduced member has a 'firstChild' field and a length, that points * to the subarray within this array with its children. This array is * shared here in order to minimize allocations across iterations. */ - int memChildrenStorage; /**< Number of allocated slots for the children storage array */ - int numChildrenStorage; /**< Number of used slots for the children storage array */ + int memChildrenStorage; /**< Number of allocated slots for the children storage array */ + int numChildrenStorage; /**< Number of used slots for the children storage array */ - CutArcListNode* cutArcs; /**< Array containing the linked list nodes of the cut arcs */ - int memCutArcs; /**< Number of allocated entries in cutArcs */ - int numCutArcs; /**< Number of used entries in cutArcs */ - cut_arc_id firstOverallCutArc; /**< Index of the head node of the linked list containing all cut arcs */ + CutArcListNode* cutArcs; /**< Array containing the linked list nodes of the cut arcs */ + int memCutArcs; /**< Number of allocated entries in cutArcs */ + int numCutArcs; /**< Number of used entries in cutArcs */ + cut_arc_id firstOverallCutArc; /**< Index of the head node of the linked list containing all cut arcs */ - spqr_row newRowIndex; /**< The index of the new row to be added */ + spqr_row newRowIndex; /**< The index of the new row to be added */ - spqr_col* newColumnArcs; /**< The nonzero columns in the new row that do not yet occur in the + spqr_col* newColumnArcs; /**< The nonzero columns in the new row that do not yet occur in the * decomposition */ - SCIP_Bool* newColumnReversed; /**< True if the nonzero entry of the new column is -1, False otherwise */ - int memColumnArcs; /**< Number of allocated slots in newColumnArcs/newColumnReversed */ - int numColumnArcs; /**< Number of new columns in the row to be added */ + SCIP_Bool* newColumnReversed; /**< True if the nonzero entry of the new column is -1, False otherwise */ + int memColumnArcs; /**< Number of allocated slots in newColumnArcs/newColumnReversed */ + int numColumnArcs; /**< Number of new columns in the row to be added */ - reduced_member_id* leafMembers; /**< Array that stores the leaf members of the SPQR forest */ - int numLeafMembers; /**< Number of used slots in leafMembers array*/ - int memLeafMembers; /**< Number of allocated slots in leafMembers array*/ + reduced_member_id* leafMembers; /**< Array that stores the leaf members of the SPQR forest */ + int numLeafMembers; /**< Number of used slots in leafMembers array */ + int memLeafMembers; /**< Number of allocated slots in leafMembers array */ - spqr_arc* decompositionColumnArcs; /**< For each nonzero column of the new row that is in the decomposition, - * stores the corresponding decomposition arc */ - SCIP_Bool* decompositionColumnArcReversed;/**< For each nonzero column of the new row that is in the decomposition, - * stores whether the corresponding decomposition arc is reversed */ - int memDecompositionColumnArcs; /**< Number of allocated slots in decompositionColumnArcs(Reversed) */ - int numDecompositionColumnArcs; /**< Number of used slots in decompositionColumnArcs(Reversed) */ + spqr_arc* decompositionColumnArcs; /**< For each nonzero column of the new row that is in the decomposition, + * stores the corresponding decomposition arc */ + SCIP_Bool* decompositionColumnArcReversed; /**< For each nonzero column of the new row that is in the decomposition, + * stores whether the corresponding decomposition arc is reversed */ + int memDecompositionColumnArcs; /**< Number of allocated slots in decompositionColumnArcs(Reversed) */ + int numDecompositionColumnArcs; /**< Number of used slots in decompositionColumnArcs(Reversed) */ - SCIP_Bool* isArcCut; /**< Stores for each arc, if the arc a cut arc?*/ - SCIP_Bool* isArcCutReversed; /**< Is the new row in reverse direction on the arcs cycle? */ - int memIsArcCut; /**< The allocated size of the isArcCut(Reversed) arrays */ + SCIP_Bool* isArcCut; /**< Stores for each arc, if the arc a cut arc? */ + SCIP_Bool* isArcCutReversed; /**< Is the new row in reverse direction on the arcs cycle? */ + int memIsArcCut; /**< The allocated size of the isArcCut(Reversed) arrays */ - COLOR_STATUS* nodeColors; /**< Stores the color of each node */ - int memNodeColors; /**< The allocated size of the nodeColors array */ + COLOR_STATUS* nodeColors; /**< Stores the color of each node */ + int memNodeColors; /**< The allocated size of the nodeColors array */ - spqr_node* articulationNodes; /**< Temp. array for storing articulation nodes of member graph-cut arcs*/ - int numArticulationNodes; /**< Number of used slots in articulation nodes array */ - int memArticulationNodes; /**< Number of allocated slots in articulation nodes array */ + spqr_node* articulationNodes; /**< Temp. array for storing articulation nodes of member graph-cut arcs */ + int numArticulationNodes; /**< Number of used slots in articulation nodes array */ + int memArticulationNodes; /**< Number of allocated slots in articulation nodes array */ - ArticulationNodeInformation* articulationNodeSearchInfo; /**numDecompositionColumnArcs == newRow->memDecompositionColumnArcs ) { int newNumArcs = newRow->memDecompositionColumnArcs == 0 ? 8 : 2 * newRow->memDecompositionColumnArcs; - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->decompositionColumnArcs, - newRow->memDecompositionColumnArcs, newNumArcs)); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->decompositionColumnArcReversed, - newRow->memDecompositionColumnArcs, newNumArcs)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->decompositionColumnArcs, + newRow->memDecompositionColumnArcs, newNumArcs) ); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->decompositionColumnArcReversed, + newRow->memDecompositionColumnArcs, newNumArcs) ); newRow->memDecompositionColumnArcs = newNumArcs; } newRow->decompositionColumnArcs[newRow->numDecompositionColumnArcs] = columnArc; @@ -6608,10 +6780,10 @@ SCIP_RETCODE newRowUpdateRowInformation( if( newRow->numColumnArcs == newRow->memColumnArcs ) { int newNumArcs = newRow->memColumnArcs == 0 ? 8 : 2 * newRow->memColumnArcs; - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->newColumnArcs, - newRow->memColumnArcs, newNumArcs)); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->newColumnReversed, - newRow->memColumnArcs, newNumArcs)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->newColumnArcs, + newRow->memColumnArcs, newNumArcs) ); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->newColumnReversed, + newRow->memColumnArcs, newNumArcs) ); newRow->memColumnArcs = newNumArcs; } newRow->newColumnArcs[newRow->numColumnArcs] = columns[i]; @@ -6623,8 +6795,9 @@ SCIP_RETCODE newRowUpdateRowInformation( return SCIP_OKAY; } -/**< Adds members to the reduced member tree, by starting at the given member, jumping up to the parent, repeating this - * procedure until the root has been added. */ +/** Adds members to the reduced member tree, by starting at the given member, jumping up to the parent, repeating this + * procedure until the root has been added. + */ static reduced_member_id createRowReducedMembersToRoot( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -6672,14 +6845,13 @@ reduced_member_id createRowReducedMembersToRoot( assert(memberIsRepresentative(dec, member)); spqr_member parentMember = findMemberParent(dec, member); - if( SPQRmemberIsValid(parentMember)) + if( SPQRmemberIsValid(parentMember) ) { //recursive call to parent member ++callDepth; assert(callDepth < newRow->memCreateReducedMembersCallstack); callstack[callDepth].member = parentMember; continue; - } else { @@ -6709,7 +6881,8 @@ reduced_member_id createRowReducedMembersToRoot( while( TRUE ) /*lint !e716*/ { --callDepth; - if( callDepth < 0 ) break; + if( callDepth < 0 ) + break; spqr_member parentMember = callstack[callDepth + 1].member; reduced_member_id parentReducedMember = newRow->memberInformation[parentMember].reducedMember; spqr_member currentMember = callstack[callDepth].member; @@ -6731,7 +6904,7 @@ reduced_member_id createRowReducedMembersToRoot( } -/**< Construct the reduced decomposition, which is the smallest subtree containing all members cut arcs */ +/** Construct the reduced decomposition, which is the smallest subtree containing all members cut arcs. */ static SCIP_RETCODE constructRowReducedDecomposition( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -6758,14 +6931,13 @@ SCIP_RETCODE constructRowReducedDecomposition( if( newSize > newRow->memReducedMembers ) { int updatedSize = maxValue(2 * newRow->memReducedMembers, newSize); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->reducedMembers, newRow->memReducedMembers, updatedSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->reducedMembers, newRow->memReducedMembers, updatedSize) ); newRow->memReducedMembers = updatedSize; } if( newSize > newRow->memMemberInformation ) { int updatedSize = maxValue(2 * newRow->memMemberInformation, newSize); - SCIP_ALLOC( - BMSreallocBlockMemoryArray(dec->env, &newRow->memberInformation, newRow->memMemberInformation, updatedSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->memberInformation, newRow->memMemberInformation, updatedSize) ); for( int i = newRow->memMemberInformation; i < updatedSize; ++i ) { newRow->memberInformation[i].reducedMember = INVALID_REDUCED_MEMBER; @@ -6778,8 +6950,7 @@ SCIP_RETCODE constructRowReducedDecomposition( if( numComponents > newRow->memReducedComponents ) { int updatedSize = maxValue(2 * newRow->memReducedComponents, numComponents); - SCIP_ALLOC( - BMSreallocBlockMemoryArray(dec->env, &newRow->reducedComponents, newRow->memReducedComponents, updatedSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->reducedComponents, newRow->memReducedComponents, updatedSize) ); newRow->memReducedComponents = updatedSize; } @@ -6787,8 +6958,8 @@ SCIP_RETCODE constructRowReducedDecomposition( if( newRow->memCreateReducedMembersCallstack < numMembers ) { int updatedSize = maxValue(2 * newRow->memCreateReducedMembersCallstack, numMembers); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->createReducedMembersCallstack, - newRow->memCreateReducedMembersCallstack, updatedSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->createReducedMembersCallstack, + newRow->memCreateReducedMembersCallstack, updatedSize) ); newRow->memCreateReducedMembersCallstack = updatedSize; } @@ -6800,7 +6971,7 @@ SCIP_RETCODE constructRowReducedDecomposition( spqr_member arcMember = findArcMember(dec, arc); reduced_member_id reducedMember = createRowReducedMembersToRoot(dec, newRow, arcMember); reduced_member_id* depthMinimizer = &newRow->memberInformation[newRow->reducedMembers[reducedMember].rootMember].rootDepthMinimizer; - if( reducedMemberIsInvalid(*depthMinimizer)) + if( reducedMemberIsInvalid(*depthMinimizer) ) { *depthMinimizer = reducedMember; } @@ -6837,8 +7008,8 @@ SCIP_RETCODE constructRowReducedDecomposition( if( newRow->memChildrenStorage < numTotalChildren ) { int newMemSize = maxValue(newRow->memChildrenStorage * 2, numTotalChildren); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->childrenStorage, newRow->memChildrenStorage, - newMemSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->childrenStorage, newRow->memChildrenStorage, + newMemSize) ); newRow->memChildrenStorage = newMemSize; } newRow->numChildrenStorage = numTotalChildren; @@ -6856,7 +7027,7 @@ SCIP_RETCODE constructRowReducedDecomposition( reduced_member_id parentReducedMember = SPQRmemberIsValid(parentMember) ? newRow->memberInformation[parentMember].reducedMember : INVALID_REDUCED_MEMBER; - if( reducedMemberIsValid(parentReducedMember)) + if( reducedMemberIsValid(parentReducedMember) ) { SPQRRowReducedMember* parentReducedMemberData = &newRow->reducedMembers[parentReducedMember]; newRow->childrenStorage[parentReducedMemberData->firstChild + @@ -6879,8 +7050,10 @@ SCIP_RETCODE constructRowReducedDecomposition( return SCIP_OKAY; } - -/**< Marks an arc as 'cut'. This implies that its cycle in the decomposition must be elongated.*/ +/** Marks an arc as 'cut'. + * + * This implies that its cycle in the decomposition must be elongated. + */ static void createCutArc( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -6930,8 +7103,10 @@ void createCutArc( listNode->arcReversed = reversed; } -/**< Creates all cut arcs within the decomposition for the new row. - * Note this preallocates memory for cut arcs which may be created by propagation. */ +/** Creates all cut arcs within the decomposition for the new row. + * + * Note this preallocates memory for cut arcs which may be created by propagation. + */ static SCIP_RETCODE createReducedDecompositionCutArcs( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -6943,8 +7118,8 @@ SCIP_RETCODE createReducedDecompositionCutArcs( if( maxArcID > newRow->memIsArcCut ) { int newSize = maxValue(maxArcID, 2 * newRow->memIsArcCut); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->isArcCut, newRow->memIsArcCut, newSize)); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->isArcCutReversed, newRow->memIsArcCut, newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->isArcCut, newRow->memIsArcCut, newSize) ); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->isArcCutReversed, newRow->memIsArcCut, newSize) ); for( int i = newRow->memIsArcCut; i < newSize; ++i ) { newRow->isArcCut[i] = FALSE; @@ -6964,7 +7139,7 @@ SCIP_RETCODE createReducedDecompositionCutArcs( if( numNeededArcs > newRow->memCutArcs ) { int newSize = maxValue(newRow->memCutArcs * 2, numNeededArcs); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->cutArcs, newRow->memCutArcs, newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->cutArcs, newRow->memCutArcs, newSize) ); newRow->memCutArcs = newSize; } newRow->numCutArcs = 0; @@ -6981,8 +7156,11 @@ SCIP_RETCODE createReducedDecompositionCutArcs( return SCIP_OKAY; } -/**< Determines the members of the reduced decomposition which are leafs. This is used in propagation to ensure - * propagation is only checked for components which have at most one neighbour that is not propagated. */ +/** Determines the members of the reduced decomposition which are leafs. + * + * This is used in propagation to ensure + * propagation is only checked for components which have at most one neighbour that is not propagated. + */ static SCIP_RETCODE determineLeafReducedMembers( const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -6992,7 +7170,7 @@ SCIP_RETCODE determineLeafReducedMembers( if( newRow->numDecompositionColumnArcs > newRow->memLeafMembers ) { int updatedSize = maxValue(newRow->numDecompositionColumnArcs, 2 * newRow->memLeafMembers); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->leafMembers, newRow->memLeafMembers, updatedSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->leafMembers, newRow->memLeafMembers, updatedSize) ); newRow->memLeafMembers = updatedSize; } newRow->numLeafMembers = 0; @@ -7005,10 +7183,11 @@ SCIP_RETCODE determineLeafReducedMembers( ++newRow->numLeafMembers; } } + return SCIP_OKAY; } -/**< Preallocates memory arrays necessary for searching rigid components. */ +/** Preallocates memory arrays necessary for searching rigid components. */ static SCIP_RETCODE allocateRigidSearchMemory( const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -7020,7 +7199,7 @@ SCIP_RETCODE allocateRigidSearchMemory( if( maxNumNodes > newRow->memNodeColors ) { int newSize = maxValue(2 * newRow->memNodeColors, maxNumNodes); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->nodeColors, newRow->memNodeColors, newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->nodeColors, newRow->memNodeColors, newSize) ); for( int i = newRow->memNodeColors; i < newSize; ++i ) { newRow->nodeColors[i] = UNCOLORED; @@ -7031,28 +7210,27 @@ SCIP_RETCODE allocateRigidSearchMemory( if( totalNumNodes > newRow->memArticulationNodes ) { int newSize = maxValue(2 * newRow->memArticulationNodes, totalNumNodes); - SCIP_ALLOC( - BMSreallocBlockMemoryArray(dec->env, &newRow->articulationNodes, newRow->memArticulationNodes, newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->articulationNodes, newRow->memArticulationNodes, newSize) ); newRow->memArticulationNodes = newSize; } if( totalNumNodes > newRow->memNodeSearchInfo ) { int newSize = maxValue(2 * newRow->memNodeSearchInfo, totalNumNodes); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->articulationNodeSearchInfo, newRow->memNodeSearchInfo, - newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->articulationNodeSearchInfo, newRow->memNodeSearchInfo, + newSize) ); newRow->memNodeSearchInfo = newSize; } if( totalNumNodes > newRow->memCrossingPathCount ) { int newSize = maxValue(2 * newRow->memCrossingPathCount, totalNumNodes); - SCIP_ALLOC( - BMSreallocBlockMemoryArray(dec->env, &newRow->crossingPathCount, newRow->memCrossingPathCount, newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->crossingPathCount, newRow->memCrossingPathCount, newSize) ); newRow->memCrossingPathCount = newSize; } - if( totalNumNodes > newRow->memTemporaryColorArray ){ + if( totalNumNodes > newRow->memTemporaryColorArray ) + { int newSize = maxValue(2 * newRow->memTemporaryColorArray, totalNumNodes); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->temporaryColorArray, - newRow->memTemporaryColorArray, newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->temporaryColorArray, + newRow->memTemporaryColorArray, newSize) ); newRow->memTemporaryColorArray = newSize; } @@ -7063,20 +7241,19 @@ SCIP_RETCODE allocateRigidSearchMemory( if( largestID > newRow->memIntersectionDFSData ) { int newSize = maxValue(2 * newRow->memIntersectionDFSData, largestID); - SCIP_ALLOC( - BMSreallocBlockMemoryArray(dec->env, &newRow->intersectionDFSData, newRow->memIntersectionDFSData, newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->intersectionDFSData, newRow->memIntersectionDFSData, newSize) ); newRow->memIntersectionDFSData = newSize; } if( largestID > newRow->memColorDFSData ) { int newSize = maxValue(2 * newRow->memColorDFSData, largestID); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->colorDFSData, newRow->memColorDFSData, newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->colorDFSData, newRow->memColorDFSData, newSize) ); newRow->memColorDFSData = newSize; } if( largestID > newRow->memArtDFSData ) { int newSize = maxValue(2 * newRow->memArtDFSData, largestID); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->artDFSData, newRow->memArtDFSData, newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->artDFSData, newRow->memArtDFSData, newSize) ); newRow->memArtDFSData = newSize; } @@ -7088,8 +7265,8 @@ SCIP_RETCODE allocateRigidSearchMemory( if( largestID > newRow->memIntersectionPathDepth ) { int newSize = maxValue(2 * newRow->memIntersectionPathDepth, largestID); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->intersectionPathDepth, newRow->memIntersectionPathDepth, - newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->intersectionPathDepth, newRow->memIntersectionPathDepth, + newSize) ); for( int i = newRow->memIntersectionPathDepth; i < newSize; ++i ) { newRow->intersectionPathDepth[i] = -1; @@ -7103,9 +7280,8 @@ SCIP_RETCODE allocateRigidSearchMemory( if( largestID > newRow->memIntersectionPathParent ) { int newSize = maxValue(2 * newRow->memIntersectionPathParent, largestID); - SCIP_ALLOC( - BMSreallocBlockMemoryArray(dec->env, &newRow->intersectionPathParent, newRow->memIntersectionPathParent, - newSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->intersectionPathParent, newRow->memIntersectionPathParent, + newSize) ); for( int i = newRow->memIntersectionPathParent; i < newSize; ++i ) { newRow->intersectionPathParent[i] = SPQR_INVALID_NODE; @@ -7116,8 +7292,11 @@ SCIP_RETCODE allocateRigidSearchMemory( return SCIP_OKAY; } -/**< Clears the colors for all nodes. We do DFS in reverse (reverse exploration order), as zeroing out the entire - * array is more expensive. */ +/** Clears the colors for all nodes. + * + * We do DFS in reverse (reverse exploration order), as zeroing out the entire + * array is more expensive. + */ static void zeroOutColors( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -7129,14 +7308,14 @@ void zeroOutColors( newRow->nodeColors[firstRemoveNode] = UNCOLORED; ColorDFSCallData* data = newRow->colorDFSData; - if( newRow->colorDFSData == NULL) + if( newRow->colorDFSData == NULL ) { return; } data[0].node = firstRemoveNode; data[0].arc = getFirstNodeArc(dec, firstRemoveNode); - if( SPQRarcIsInvalid(data[0].arc)) + if( SPQRarcIsInvalid(data[0].arc) ) { return; } @@ -7163,14 +7342,17 @@ void zeroOutColors( } callData->arc = getNextNodeArc(dec, callData->arc, callData->node); - while( depth >= 0 && data[depth].arc == getFirstNodeArc(dec, data[depth].node)) + while( depth >= 0 && data[depth].arc == getFirstNodeArc(dec, data[depth].node) ) { --depth; } } } -/**< Cleans up various arrays used in previous iterations; this is cheaper than reallocating empty memory. */ +/** Cleans up various arrays used in previous iterations. + * + * This is cheaper than reallocating empty memory. + */ static void cleanUpPreviousIteration( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -7195,7 +7377,7 @@ void cleanUpPreviousIteration( //For cut arcs: clear them from the array from previous iteration cut_arc_id cutArcIdx = newRow->firstOverallCutArc; - while( cutArcIsValid(cutArcIdx)) + while( cutArcIsValid(cutArcIdx) ) { spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; cutArcIdx = newRow->cutArcs[cutArcIdx].nextOverall; @@ -7211,7 +7393,7 @@ void cleanUpPreviousIteration( #endif } -/**< Finds all the star nodes, i.e. nodes that are adjacent to all cut arcs, in a rigid member */ +/** Finds all the star nodes, i.e. nodes that are adjacent to all cut arcs, in a rigid member. */ static void rigidFindStarNodes( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -7247,7 +7429,7 @@ void rigidFindStarNodes( spqr_node intersectionNodes[2] = {head, tail}; - while( cutArcIsValid(newRow->cutArcs[cutArcIdx].nextMember)) + while( cutArcIsValid(newRow->cutArcs[cutArcIdx].nextMember) ) { cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; cutArc = newRow->cutArcs[cutArcIdx].arc; @@ -7273,14 +7455,14 @@ void rigidFindStarNodes( intersectionNodes[i] = SPQR_INVALID_NODE; } } - if( SPQRnodeIsInvalid(intersectionNodes[0]) && SPQRnodeIsInvalid(intersectionNodes[1])) + if( SPQRnodeIsInvalid(intersectionNodes[0]) && SPQRnodeIsInvalid(intersectionNodes[1]) ) { newRow->reducedMembers[toCheck].splitNode = SPQR_INVALID_NODE; newRow->reducedMembers[toCheck].allHaveCommonNode = FALSE; return;//not all arcs are adjacent to a single node, need to check articulation nodes } } - if( SPQRnodeIsInvalid(cutArcsHead) && SPQRnodeIsInvalid(cutArcsTail)) + if( SPQRnodeIsInvalid(cutArcsHead) && SPQRnodeIsInvalid(cutArcsTail) ) { //All arcs adjacent to a single node, but not in same direction; not network newRow->remainsNetwork = FALSE; @@ -7313,8 +7495,10 @@ void rigidFindStarNodes( } } -/**< Clears the colors for all nodes, but the neighbours. We do DFS in reverse (reverse exploration order), - * as zeroing out the entire array is more expensive. */ +/** Clears the colors for all nodes, but the neighbours. + * + * We do DFS in reverse (reverse exploration order), as zeroing out the entire array is more expensive. + */ static void zeroOutColorsExceptNeighbourhood( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -7338,7 +7522,8 @@ void zeroOutColorsExceptNeighbourhood( i++; assert(i <= nodeDegree(dec, articulationNode)); artItArc = getNextNodeArc(dec, artItArc, articulationNode); - } while( artItArc != artFirstArc ); + } + while( artItArc != artFirstArc ); } zeroOutColors(dec, newRow, startRemoveNode); @@ -7355,14 +7540,16 @@ void zeroOutColorsExceptNeighbourhood( i++; assert(i <= nodeDegree(dec, articulationNode)); artItArc = getNextNodeArc(dec, artItArc, articulationNode); - } while( artItArc != artFirstArc ); + } + while( artItArc != artFirstArc ); } - } -/**< Find the intersection of all cut arc paths in the given rigid member. +/** Find the intersection of all cut arc paths in the given rigid member. + * * Theoretically, there is a faster algorithm using lowest common ancestor queries, but it is quite complicated. - * Thus, we stick with the 'simple' version below, which is typically also faster in practice. */ + * Thus, we stick with the 'simple' version below, which is typically also faster in practice. + */ static void intersectionOfAllPaths( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -7400,7 +7587,7 @@ void intersectionOfAllPaths( //cannot be a tree arc which is its parent if( arcIsTree(dec, dfsData->nodeArc) && ( pathSearchCallStackSize <= 1 || - dfsData->nodeArc != pathSearchCallStack[pathSearchCallStackSize - 2].nodeArc )) + dfsData->nodeArc != pathSearchCallStack[pathSearchCallStackSize - 2].nodeArc ) ) { spqr_node head = findArcHeadNoCompression(dec, dfsData->nodeArc); spqr_node tail = findArcTailNoCompression(dec, dfsData->nodeArc); @@ -7421,7 +7608,7 @@ void intersectionOfAllPaths( do { dfsData->nodeArc = getNextNodeArc(dec, dfsData->nodeArc, dfsData->node); - if( dfsData->nodeArc == getFirstNodeArc(dec, dfsData->node)) + if( dfsData->nodeArc == getFirstNodeArc(dec, dfsData->node) ) { --pathSearchCallStackSize; dfsData = &pathSearchCallStack[pathSearchCallStackSize - 1]; @@ -7476,12 +7663,11 @@ void intersectionOfAllPaths( nodeNumPaths[source]--; assert(SPQRnodeIsValid(source) && SPQRnodeIsValid(target)); assert(source == target); - } while( cutArcIsValid(cutArc)); } -/**< Add a node to array of articulation nodes */ +/** Add a node to array of articulation nodes. */ static void addArticulationNode( SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ @@ -7498,8 +7684,10 @@ void addArticulationNode( ++newRow->numArticulationNodes; } -/**< Find all the articulation points of the rigid member graph where the cut arcs are removed. Articulation points - * are nodes whose removal disconnects the remaining graph. */ +/** Find all the articulation points of the rigid member graph where the cut arcs are removed. + * + * Articulation points are nodes whose removal disconnects the remaining graph. + */ static void articulationPoints( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -7589,14 +7777,17 @@ void articulationPoints( } } -/**< For a given articulation node, partitions the rigid member's graph where it is removed into a SOURCE and SINK - * partition; all cut arcs must point (after reversal) from the source partition to the sink partition. This is akin - * to finding a 2-coloring, and uses a DFS to do so. This function specifically executes the DFS/recursive part.*/ +/** For a given articulation node, partitions the rigid member's graph where it is removed into a SOURCE and SINK + * partition. + * + * All cut arcs must point (after reversal) from the source partition to the sink partition. This is akin + * to finding a 2-coloring, and uses a DFS to do so. This function specifically executes the DFS/recursive part. + */ static void rigidConnectedColoringRecursive( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ - spqr_node articulationNode, /**< The articulation node to obtain the colouring for */ + spqr_node articulationNode, /**< The articulation node to obtain the coloring for */ spqr_node firstProcessNode, /**< The first node to process for the DFS */ COLOR_STATUS firstColor, /**< The partition that the first node lies in. */ SCIP_Bool* isGood /**< Returns whether the coloring was consistent */ @@ -7654,23 +7845,26 @@ void rigidConnectedColoringRecursive( data[depth].arc = getFirstNodeArc(dec, otherNode); continue; } - if( isArcCut[callData->arc] != ( currentColor != otherColor )) + if( isArcCut[callData->arc] != ( currentColor != otherColor ) ) { *isGood = FALSE; break; } } callData->arc = getNextNodeArc(dec, callData->arc, callData->node); - while( depth >= 0 && data[depth].arc == getFirstNodeArc(dec, data[depth].node)) + while( depth >= 0 && data[depth].arc == getFirstNodeArc(dec, data[depth].node) ) { --depth; } } } -/**< For a given articulation node, partitions the rigid member's graph where it is removed into a SOURCE and SINK - * partition; all cut arcs must point (after reversal) from the source partition to the sink partition. This is akin - * to finding a 2-coloring, and uses a DFS to do so.*/ +/** For a given articulation node, partitions the rigid member's graph where it is removed into a SOURCE and SINK + * partition. + * + * All cut arcs must point (after reversal) from the source partition to the sink partition. This is akin + * to finding a 2-coloring, and uses a DFS to do so. + */ static void rigidConnectedColoring( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -7680,12 +7874,10 @@ void rigidConnectedColoring( SCIP_Bool* const isGood /**< Returns whether the coloring was consistent */ ) { - //we should only perform this function if there's more than one cut arc assert(newRow->reducedMembers[reducedMember].numCutArcs > 1); #ifndef NDEBUG { - spqr_member member = newRow->reducedMembers[reducedMember].member; spqr_arc firstArc = getFirstMemberArc(dec, member); spqr_arc memberArc = firstArc; @@ -7731,7 +7923,7 @@ void rigidConnectedColoring( rigidConnectedColoringRecursive(dec, newRow, node, firstProcessNode, firstColor, isGood); // Need to zero all colors for next attempts if we failed - if( !( *isGood )) + if( !( *isGood ) ) { zeroOutColors(dec, newRow, firstProcessNode); newRow->reducedMembers[reducedMember].coloredNode = SPQR_INVALID_NODE; @@ -7744,9 +7936,10 @@ void rigidConnectedColoring( } } -/**< Given a coloring for an articulation node, we can prove that a neighbouring node is splittable if and only if it is +/** Given a coloring for an articulation node, we can prove that a neighbouring node is splittable if and only if it is * the unique node in the neighbourhood of the articulation node within the SOURCE or SINK partition. In this function, - * we check for a splittable articulation node if such an adjacent node exists, and return it if possible. */ + * we check for a splittable articulation node if such an adjacent node exists, and return it if possible. + */ static spqr_node checkNeighbourColoringArticulationNode( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -7813,8 +8006,10 @@ spqr_node checkNeighbourColoringArticulationNode( return SPQR_INVALID_NODE; } -/**< Find all the splittable articulation points that lie on the intersection of all cut arc cycles. - * firstNode and secondNode can be set to limit the nodes that this function checks to the given nodes.*/ +/** Find all the splittable articulation points that lie on the intersection of all cut arc cycles. + * + * firstNode and secondNode can be set to limit the nodes that this function checks to the given nodes. + */ static void rigidGetSplittableArticulationPointsOnPath( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -7857,7 +8052,7 @@ void rigidGetSplittableArticulationPointsOnPath( SCIP_Bool isOnPath = nodeNumPaths[articulationNode] == numCutArcs; if( isOnPath && (( SPQRnodeIsInvalid(firstNode) && SPQRnodeIsInvalid(secondNode)) || articulationNode == firstNode || - articulationNode == secondNode )) + articulationNode == secondNode ) ) { SCIP_Bool isGood = TRUE; rigidConnectedColoring(dec, newRow, toCheck, articulationNode, &isGood); @@ -7877,7 +8072,7 @@ void rigidGetSplittableArticulationPointsOnPath( //is also an articulation node if( SPQRnodeIsValid(adjacentSplittingNode) && (( SPQRnodeIsInvalid(firstNode) && SPQRnodeIsInvalid(secondNode)) || adjacentSplittingNode == firstNode || - adjacentSplittingNode == secondNode )) + adjacentSplittingNode == secondNode ) ) { SCIP_Bool isArticulationNode = FALSE; for( int j = 0; j < newRow->numArticulationNodes; ++j ) @@ -7906,7 +8101,8 @@ void rigidGetSplittableArticulationPointsOnPath( spqr_node otherNode = articulationNode == head ? tail : head; newRow->nodeColors[otherNode] = UNCOLORED; itArc = getNextNodeArc(dec, itArc, articulationNode); - } while( itArc != firstNodeArc ); + } + while( itArc != firstNodeArc ); } } } @@ -7918,7 +8114,7 @@ void rigidGetSplittableArticulationPointsOnPath( newRow->remainsNetwork = FALSE; } -/**< Checks if a leaf parallel member can be propagated away from the reduced decomposition */ +/** Checks if a leaf parallel member can be propagated away from the reduced decomposition. */ static void determineParallelType( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -7959,7 +8155,7 @@ void determineParallelType( } //we can only propagate if the marker arc is a tree arc and all other arcs are cut if( !arcIsTree(dec, markerToOther) || - countedCutArcs != ( getNumMemberArcs(dec, newRow->reducedMembers[toCheckMember].member) - 1 )) + countedCutArcs != ( getNumMemberArcs(dec, newRow->reducedMembers[toCheckMember].member) - 1 ) ) { //In all other cases, the bond can be split so that the result will be okay! newRow->reducedMembers[toCheckMember].type = TYPE_MERGED; @@ -7972,7 +8168,7 @@ void determineParallelType( } } -/**< Checks if a leaf series member can be propagated away from the reduced decomposition */ +/** Checks if a leaf series member can be propagated away from the reduced decomposition. */ static void determineSeriesType( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -7995,7 +8191,7 @@ void determineSeriesType( newRow->cutArcs[cutArc].arcReversed); } -/**< Checks if a leaf rigid member can be propagated away from the reduced decomposition */ +/** Checks if a leaf rigid member can be propagated away from the reduced decomposition. */ static void determineRigidType( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -8006,8 +8202,8 @@ void determineRigidType( const spqr_arc markerToCheck /**< Marker from the non-leaf to the rigid leaf member */ ) { - assert(newRow->reducedMembers[toCheckMember].numCutArcs > - 0);//Checking for propagation only makes sense if there is at least one cut arc + //Checking for propagation only makes sense if there is at least one cut arc + assert(newRow->reducedMembers[toCheckMember].numCutArcs > 0); rigidFindStarNodes(dec, newRow, toCheckMember); if( !newRow->remainsNetwork ) @@ -8022,7 +8218,7 @@ void determineRigidType( markerHead = markerTail; markerTail = temp; } - if( SPQRnodeIsInvalid(newRow->reducedMembers[toCheckMember].splitNode)) + if( SPQRnodeIsInvalid(newRow->reducedMembers[toCheckMember].splitNode) ) { //not a star => attempt to find splittable nodes using articulation node algorithms //We save some work by telling the methods that only the marker nodes should be checked @@ -8033,7 +8229,6 @@ void determineRigidType( return; } - if( SPQRarcIsValid(newRow->reducedMembers[toCheckMember].articulationArc) && newRow->reducedMembers[toCheckMember].articulationArc == markerToOther ) { @@ -8050,7 +8245,7 @@ void determineRigidType( } else if( SPQRarcIsValid(newRow->reducedMembers[toCheckMember].articulationArc) && ( newRow->reducedMembers[toCheckMember].otherNode == markerHead || - newRow->reducedMembers[toCheckMember].otherNode == markerTail )) + newRow->reducedMembers[toCheckMember].otherNode == markerTail ) ) { newRow->reducedMembers[toCheckMember].type = TYPE_MERGED; } @@ -8062,7 +8257,7 @@ void determineRigidType( } } -/**< Checks if a leaf member can be propagated away from the reduced decomposition */ +/** Checks if a leaf member can be propagated away from the reduced decomposition. */ static RowReducedMemberType determineType( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -8074,7 +8269,7 @@ RowReducedMemberType determineType( ) { assert(newRow->reducedMembers[toCheckMember].type == TYPE_UNDETERMINED); - switch( getMemberType(dec, newRow->reducedMembers[toCheckMember].member)) + switch( getMemberType(dec, newRow->reducedMembers[toCheckMember].member) ) { case SPQR_MEMBERTYPE_RIGID: { @@ -8101,7 +8296,7 @@ RowReducedMemberType determineType( return newRow->reducedMembers[toCheckMember].type; } -/**< Propagates away all leaf members that are propagatable to shrink the reduced decomposition */ +/** Propagates away all leaf members that are propagatable to shrink the reduced decomposition. */ static void propagateComponents( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -8119,7 +8314,7 @@ void propagateComponents( //next is invalid if the member is not in the reduced decomposition. next = newRow->reducedMembers[leaf].parent; spqr_arc parentMarker = markerToParent(dec, newRow->reducedMembers[leaf].member); - if( next != INVALID_REDUCED_MEMBER && arcIsTree(dec, parentMarker)) + if( next != INVALID_REDUCED_MEMBER && arcIsTree(dec, parentMarker) ) { assert(reducedMemberIsValid(next)); assert(SPQRarcIsValid(parentMarker)); @@ -8178,7 +8373,7 @@ void propagateComponents( } assert(SPQRmemberIsValid(newRow->reducedMembers[child].member)); assert(SPQRarcIsValid(markerToChild)); - if( !arcIsTree(dec, markerToChild)) + if( !arcIsTree(dec, markerToChild) ) { break; } @@ -8207,15 +8402,17 @@ void propagateComponents( } } -/**< A data structure representing a node pair */ +/** A data structure representing a node pair. */ typedef struct { - spqr_node first; - spqr_node second; + spqr_node first; + spqr_node second; } Nodes; -/**< Computes the intersection nodes of all markers that point to reduced tree members. These are the only nodes that - * may become Y-splittable, after propagation. */ +/** Computes the intersection nodes of all markers that point to reduced tree members. + * + * These are the only nodes that may become Y-splittable, after propagation. + */ static Nodes rigidDetermineCandidateNodesFromAdjacentComponents( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -8223,7 +8420,6 @@ Nodes rigidDetermineCandidateNodesFromAdjacentComponents( const reduced_member_id toCheck /**< The rigid member to check */ ) { - Nodes pair; pair.first = SPQR_INVALID_NODE; pair.second = SPQR_INVALID_NODE; @@ -8238,7 +8434,7 @@ Nodes rigidDetermineCandidateNodesFromAdjacentComponents( spqr_arc arc = markerOfParent(dec, newRow->reducedMembers[reducedChild].member); spqr_node head = findArcHead(dec, arc); spqr_node tail = findArcTail(dec, arc); - if( SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second)) + if( SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second) ) { pair.first = head; pair.second = tail; @@ -8254,7 +8450,7 @@ Nodes rigidDetermineCandidateNodesFromAdjacentComponents( pair.second = SPQR_INVALID_NODE; } } - if( SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second)) + if( SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second) ) { return pair; } @@ -8267,7 +8463,7 @@ Nodes rigidDetermineCandidateNodesFromAdjacentComponents( spqr_arc arc = markerToParent(dec, newRow->reducedMembers[toCheck].member); spqr_node head = findArcHead(dec, arc); spqr_node tail = findArcTail(dec, arc); - if( SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second)) + if( SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second) ) { pair.first = head; pair.second = tail; @@ -8287,7 +8483,7 @@ Nodes rigidDetermineCandidateNodesFromAdjacentComponents( return pair; } -/**< Allocates memory for various procedures that need to do tree-search for rigid members (e.g. DFS or BFS) */ +/** Allocates memory for various procedures that need to do tree-search for rigid members (e.g. DFS or BFS). */ static SCIP_RETCODE allocateTreeSearchMemory( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -8298,14 +8494,14 @@ SCIP_RETCODE allocateTreeSearchMemory( if( necessarySpace > newRow->memMergeTreeCallData ) { int updatedSize = maxValue(2 * newRow->memMergeTreeCallData, necessarySpace); - SCIP_ALLOC(BMSreallocBlockMemoryArray(dec->env, &newRow->mergeTreeCallData, newRow->memMergeTreeCallData, - updatedSize)); + SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->mergeTreeCallData, newRow->memMergeTreeCallData, + updatedSize) ); newRow->memMergeTreeCallData = updatedSize; } return SCIP_OKAY; } -/**< Determine the type of a rigid member when it is the only member in the reduced decomposition */ +/** Determine the type of a rigid member when it is the only member in the reduced decomposition. */ static void determineSingleRowRigidType( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -8313,20 +8509,21 @@ void determineSingleRowRigidType( reduced_member_id reducedMember /**< The rigid member to determine the type for */ ) { - assert(newRow->reducedMembers[reducedMember].numCutArcs > - 0);//Checking for propagation only makes sense if there is at least one cut arc + //Checking for propagation only makes sense if there is at least one cut arc + assert(newRow->reducedMembers[reducedMember].numCutArcs > 0); + rigidFindStarNodes(dec, newRow, reducedMember); if( !newRow->remainsNetwork ) { return; } - if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedMember].splitNode)) + if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedMember].splitNode) ) { //not a star => attempt to find splittable nodes using articulation node algorithms rigidGetSplittableArticulationPointsOnPath(dec, newRow, reducedMember, SPQR_INVALID_NODE, SPQR_INVALID_NODE); } - if( SPQRnodeIsValid(newRow->reducedMembers[reducedMember].splitNode)) + if( SPQRnodeIsValid(newRow->reducedMembers[reducedMember].splitNode) ) { newRow->reducedMembers[reducedMember].type = TYPE_MERGED; } @@ -8337,7 +8534,7 @@ void determineSingleRowRigidType( } } -/**< Determine the type of a parallel member when it is the only member in the reduced decomposition */ +/** Determine the type of a parallel member when it is the only member in the reduced decomposition. */ static void determineSingleParallelType( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -8378,7 +8575,7 @@ void determineSingleParallelType( } } -/**< Determine the type of a series member when it is the only member in the reduced decomposition */ +/** Determine the type of a series member when it is the only member in the reduced decomposition. */ static void determineSingleSeriesType( SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ @@ -8390,13 +8587,16 @@ void determineSingleSeriesType( newRow->reducedMembers[reducedMember].type = TYPE_MERGED; } -/**< Sets the given split nodes; performs bookkeeping so that we have an easier time updating the graph in many - * edge cases. Algorithmically speaking, does nothing important. */ +/** Sets the given split nodes; performs bookkeeping so that we have an easier time updating the graph in many + * edge cases. + * + * Algorithmically speaking, does nothing important. + */ static spqr_node determineAndColorSplitNode( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ - reduced_member_id id, /**< The reduced member that we compute thes plit node for */ + reduced_member_id id, /**< The reduced member that we compute the split node for */ spqr_node candidateOne, /**< The first candidate node */ spqr_node candidateTwo /**< The second candidate node */ ) @@ -8419,13 +8619,14 @@ spqr_node determineAndColorSplitNode( spqr_node other = head == splitNode ? findArcTail(dec, iterArc) : head; newRow->nodeColors[other] = color; iterArc = getNextNodeArc(dec, iterArc, splitNode); - } while( iterArc != firstNodeArc ); + } + while( iterArc != firstNodeArc ); newRow->reducedMembers[id].coloredNode = splitNode; } return splitNode; } splitNode = newRow->reducedMembers[id].otherNode; - if( SPQRnodeIsInvalid(splitNode) || ( splitNode != candidateOne && splitNode != candidateTwo )) + if( SPQRnodeIsInvalid(splitNode) || ( splitNode != candidateOne && splitNode != candidateTwo ) ) { return SPQR_INVALID_NODE; } @@ -8477,8 +8678,10 @@ spqr_node determineAndColorSplitNode( return splitNode; } -/**< After propagation, determines the split type of the first leaf node of the reduced decomposition. The leaf nodes - * of the decomposition after propagation can only be of type P or R. */ +/** After propagation, determines the split type of the first leaf node of the reduced decomposition. + * + * The leaf nodes of the decomposition after propagation can only be of type P or R. + */ static void determineSplitTypeFirstLeaf( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -8542,10 +8745,10 @@ void determineSplitTypeFirstLeaf( markerTail = temp; } - if( !SPQRnodeIsValid(newRow->reducedMembers[reducedId].splitNode)) + if( !SPQRnodeIsValid(newRow->reducedMembers[reducedId].splitNode) ) { - assert(newRow->reducedMembers[reducedId].numCutArcs > - 0);//Checking for propagation only makes sense if there is at least one cut arc + //Checking for propagation only makes sense if there is at least one cut arc + assert(newRow->reducedMembers[reducedId].numCutArcs > 0); rigidFindStarNodes(dec, newRow, reducedId); if( !newRow->remainsNetwork ) @@ -8553,7 +8756,7 @@ void determineSplitTypeFirstLeaf( return; } - if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode)) + if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode) ) { //not a star => attempt to find splittable nodes using articulation node algorithms //We save some work by telling the methods that only the marker nodes should be checked @@ -8563,7 +8766,7 @@ void determineSplitTypeFirstLeaf( { return; } - if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode)) + if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode) ) { redMember->type = TYPE_NOT_NETWORK; newRow->remainsNetwork = FALSE; @@ -8580,7 +8783,7 @@ void determineSplitTypeFirstLeaf( ( otherNode == markerTail || otherNode == markerHead ))); splitNode = determineAndColorSplitNode(dec, newRow, reducedId, markerHead, markerTail); - if( SPQRnodeIsInvalid(splitNode)) + if( SPQRnodeIsInvalid(splitNode) ) { redMember->type = TYPE_NOT_NETWORK; newRow->remainsNetwork = FALSE; @@ -8593,21 +8796,22 @@ void determineSplitTypeFirstLeaf( redMember->type = TYPE_MERGED; } -/**< A data structure that tells us if the head or tail of a marked arc is split, and if the other node is in the - * source or the sink partition. */ +/** A data structure that tells us if the head or tail of a marked arc is split, and if the other node is in the + * source or the sink partition. + */ typedef struct { - SCIP_Bool headSplit; /**< Is the head or tail of the marked arc split?*/ - SCIP_Bool otherIsSource; /**< Is the non-split node in the source or sink partition? */ + SCIP_Bool headSplit; /**< Is the head or tail of the marked arc split?*/ + SCIP_Bool otherIsSource; /**< Is the non-split node in the source or sink partition? */ } SplitOrientation; -/**< Get the split orientation for a given rigid member and marked arc. */ +/** Get the split orientation for a given rigid member and marked arc. */ static SplitOrientation getRelativeOrientationRigid( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ reduced_member_id reducedId, /**< The member to determine the split orientation for */ - spqr_arc arcToNext /**< The marked arc to determine the orientaiton for */ + spqr_arc arcToNext /**< The marked arc to determine the orientation for */ ) { assert(findArcMemberNoCompression(dec, arcToNext) == newRow->reducedMembers[reducedId].member); @@ -8652,7 +8856,7 @@ SplitOrientation getRelativeOrientationRigid( return orientation; } -/**< Get the split orientation for a given parallel member and marked arc. */ +/** Get the split orientation for a given parallel member and marked arc. */ static SplitOrientation getRelativeOrientationParallel( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -8663,6 +8867,7 @@ SplitOrientation getRelativeOrientationParallel( { assert(findArcMemberNoCompression(dec, arcToNext) == newRow->reducedMembers[reducedId].member); assert(SPQRarcIsValid(newRow->reducedMembers[reducedId].splitArc) && SPQRarcIsValid(arcToNext)); + SplitOrientation orientation; orientation.otherIsSource = newRow->reducedMembers[reducedId].otherIsSource; if( arcIsReversedNonRigid(dec, arcToNext) == arcIsReversedNonRigid(dec, newRow->reducedMembers[reducedId].splitArc)) @@ -8676,7 +8881,7 @@ SplitOrientation getRelativeOrientationParallel( return orientation; } -/**< Get the split orientation for a given series member and marked arc. */ +/** Get the split orientation for a given series member and marked arc. */ static SplitOrientation getRelativeOrientationSeries( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -8687,8 +8892,8 @@ SplitOrientation getRelativeOrientationSeries( { assert(findArcMemberNoCompression(dec, arcToNext) == newRow->reducedMembers[reducedId].member); assert(SPQRarcIsValid(newRow->reducedMembers[reducedId].splitArc) && SPQRarcIsValid(arcToNext)); - SplitOrientation orientation; + SplitOrientation orientation; orientation.otherIsSource = newRow->reducedMembers[reducedId].otherIsSource; if( arcIsReversedNonRigid(dec, arcToNext) == arcIsReversedNonRigid(dec, newRow->reducedMembers[reducedId].splitArc)) { @@ -8701,7 +8906,7 @@ SplitOrientation getRelativeOrientationSeries( return orientation; } -/**< Get the split orientation for a given member and marked arc. */ +/** Get the split orientation for a given member and marked arc. */ static SplitOrientation getRelativeOrientation( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -8731,7 +8936,7 @@ SplitOrientation getRelativeOrientation( } } -/**< Determine the split type of a series node when the SPQR tree is not a singular member. */ +/** Determine the split type of a series node when the SPQR tree is not a singular member. */ static void determineSplitTypeSeries( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -8756,7 +8961,7 @@ void determineSplitTypeSeries( return; } cut_arc_id cutArc = newRow->reducedMembers[reducedId].firstCutArc; - if( cutArcIsValid(cutArc)) + if( cutArcIsValid(cutArc) ) { spqr_arc arc = newRow->cutArcs[cutArc].arc; SCIP_Bool good = ((( arcIsReversedNonRigid(dec, arc) == arcIsReversedNonRigid(dec, marker)) == @@ -8781,7 +8986,7 @@ void determineSplitTypeSeries( newRow->reducedMembers[reducedId].type = TYPE_MERGED; } -/**< Determine the split type of a parallel node when the SPQR tree is not a singular member. */ +/** Determine the split type of a parallel node when the SPQR tree is not a singular member. */ static void determineSplitTypeParallel( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -8840,7 +9045,7 @@ void determineSplitTypeParallel( redMember->type = TYPE_MERGED; } -/**< Determine the split type of a rigid node when the SPQR tree is not a singular member. */ +/** Determine the split type of a rigid node when the SPQR tree is not a singular member. */ static void determineSplitTypeRigid( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -8856,13 +9061,13 @@ void determineSplitTypeRigid( assert(getMemberType(dec, member) == SPQR_MEMBERTYPE_RIGID); Nodes nodes = rigidDetermineCandidateNodesFromAdjacentComponents(dec, newRow, reducedId); - if( SPQRnodeIsInvalid(nodes.first) && SPQRnodeIsInvalid(nodes.second)) + if( SPQRnodeIsInvalid(nodes.first) && SPQRnodeIsInvalid(nodes.second) ) { newRow->remainsNetwork = FALSE; newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; return; } - if( SPQRnodeIsInvalid(nodes.first) && SPQRnodeIsValid(nodes.second)) + if( SPQRnodeIsInvalid(nodes.first) && SPQRnodeIsValid(nodes.second) ) { nodes.first = nodes.second; nodes.second = SPQR_INVALID_NODE; @@ -8893,10 +9098,10 @@ void determineSplitTypeRigid( newRow->reducedMembers[reducedId].type = TYPE_MERGED; return; } - if( !SPQRnodeIsValid(newRow->reducedMembers[reducedId].splitNode)) + if( !SPQRnodeIsValid(newRow->reducedMembers[reducedId].splitNode) ) { - assert(newRow->reducedMembers[reducedId].numCutArcs > - 0);//Checking for propagation only makes sense if there is at least one cut arc + //Checking for propagation only makes sense if there is at least one cut arc + assert(newRow->reducedMembers[reducedId].numCutArcs > 0); rigidFindStarNodes(dec, newRow, reducedId); if( !newRow->remainsNetwork ) @@ -8904,7 +9109,7 @@ void determineSplitTypeRigid( return; } - if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode)) + if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode) ) { //not a star => attempt to find splittable nodes using articulation node algorithms //We save some work by telling the methods that only the marker nodes should be checked @@ -8914,7 +9119,7 @@ void determineSplitTypeRigid( { return; } - if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode)) + if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode) ) { newRow->remainsNetwork = FALSE; newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; @@ -8923,7 +9128,7 @@ void determineSplitTypeRigid( } spqr_node splitNode = determineAndColorSplitNode(dec, newRow, reducedId, nodes.first, nodes.second); - if( SPQRnodeIsInvalid(splitNode)) + if( SPQRnodeIsInvalid(splitNode) ) { newRow->remainsNetwork = FALSE; newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK; @@ -8953,14 +9158,16 @@ void determineSplitTypeRigid( newRow->reducedMembers[reducedId].type = TYPE_MERGED; } -/**< Determine the split type of a node when the SPQR tree is not a singular member. This function is used for all - * the nodes that are not first in the given ordering.*/ +/** Determine the split type of a node when the SPQR tree is not a singular member. + * + * This function is used for all the nodes that are not first in the given ordering. + */ static void determineSplitTypeNext( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */ reduced_member_id current, /**< The current node, whose type has already been determined */ - reduced_member_id next, /**< The next node to determine the type of, adjacent to the current node*/ + reduced_member_id next, /**< The next node to determine the type of, adjacent to the current node */ SCIP_Bool currentIsParent /**< Is the current node a parent of the next node? */ ) { @@ -8998,7 +9205,7 @@ void determineSplitTypeNext( } } -/**< For the given root reduced member of a component, determine if the component is updateable/transformable. */ +/** For the given root reduced member of a component, determine if the component is updateable/transformable. */ static void determineMergeableTypes( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -9013,7 +9220,7 @@ void determineMergeableTypes( if( newRow->reducedMembers[root].type == TYPE_UNDETERMINED ) { spqr_member member = newRow->reducedMembers[root].member; - switch( getMemberType(dec, member)) + switch( getMemberType(dec, member) ) { case SPQR_MEMBERTYPE_RIGID: determineSingleRowRigidType(dec, newRow, root); @@ -9034,7 +9241,7 @@ void determineMergeableTypes( return; } - //go to a leaf. We need to start in a leaf to avoid the ambiguity of choosing an orientation + //Go to a leaf. We need to start in a leaf to avoid the ambiguity of choosing an orientation //in members which have no cut arcs; otherwise, we might choose the wrong one reduced_member_id leaf = root; while( newRow->reducedMembers[leaf].numChildren != newRow->reducedMembers[leaf].numPropagatedChildren ) @@ -9059,7 +9266,7 @@ void determineMergeableTypes( reduced_member_id baseNode = leaf; reduced_member_id nextNode = newRow->reducedMembers[baseNode].parent; - while( reducedMemberIsValid(nextNode)) + while( reducedMemberIsValid(nextNode) ) { //check this node determineSplitTypeNext(dec, newRow, baseNode, nextNode, FALSE); @@ -9069,26 +9276,30 @@ void determineMergeableTypes( } //Add other nodes in the subtree //use a while loop to avoid recursion; we may get stack overflows for large graphs - MergeTreeCallData * data = newRow->mergeTreeCallData; + MergeTreeCallData* data = newRow->mergeTreeCallData; data[0].id = nextNode; data[0].currentChild = newRow->reducedMembers[nextNode].firstChild ; int depth = 0; - while(depth >= 0){ + while( depth >= 0 ) + { reduced_member_id id = data[depth].id; children_idx childidx = data[depth].currentChild; - if(childidx == newRow->reducedMembers[id].firstChild + newRow->reducedMembers[id].numChildren){ + if( childidx == newRow->reducedMembers[id].firstChild + newRow->reducedMembers[id].numChildren ) + { --depth; continue; } reduced_member_id currentchild = newRow->childrenStorage[childidx]; data[depth].currentChild += 1; //skip this child if we already processed it or it is not merged - if( currentchild == baseNode || newRow->reducedMembers[currentchild].type == TYPE_PROPAGATED){ + if( currentchild == baseNode || newRow->reducedMembers[currentchild].type == TYPE_PROPAGATED ) + { continue; } determineSplitTypeNext(dec,newRow,id,currentchild,TRUE); - if(!newRow->remainsNetwork){ + if( !newRow->remainsNetwork ) + { return; } //recursively process the child @@ -9103,7 +9314,7 @@ void determineMergeableTypes( } } -/**< Empty the new member information array */ +/** Empty the new member information array. */ static void cleanUpRowMemberInformation( SCIP_NETROWADD* newRow /**< The network matrix row addition data structure */ @@ -9123,8 +9334,10 @@ void cleanUpRowMemberInformation( #endif } -/**< Transforms a rigid arc by putting it in series with the new column arc. We need to do some magic to keep our - * the internal datastructures consistent in this case. */ +/** Transforms a rigid arc by putting it in series with the new column arc. + * + * We need to do some magic to keep our the internal datastructures consistent in this case. + */ static SCIP_RETCODE rigidTransformArcIntoCycle( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -9148,7 +9361,7 @@ SCIP_RETCODE rigidTransformArcIntoCycle( markerCycleArc = markerOfParent(dec, member); } } - else if( arcIsChildMarker(dec, arc)) + else if( arcIsChildMarker(dec, arc) ) { adjacentMember = findArcChildMember(dec, arc); if( getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_SERIES ) @@ -9157,10 +9370,10 @@ SCIP_RETCODE rigidTransformArcIntoCycle( markerCycleArc = markerToParent(dec, adjacentMember); } } - if( markerCycleMember != SPQR_INVALID_MEMBER) + if( markerCycleMember != SPQR_INVALID_MEMBER ) { newRowInfo->member = markerCycleMember; - if( arcIsReversedNonRigid(dec, markerCycleArc)) + if( arcIsReversedNonRigid(dec, markerCycleArc) ) { newRowInfo->reversed = reverseArcDirection; } @@ -9174,33 +9387,33 @@ SCIP_RETCODE rigidTransformArcIntoCycle( //Otherwise, we create a new cycle spqr_member newCycle; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &newCycle)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_SERIES, &newCycle) ); //We would like to move the edge but unfortunately cannot do so without destroying the arc union-find datastructure. //Thus, we 'convert' the arc into a marker and add the new series spqr_arc duplicate = SPQR_INVALID_ARC; spqr_element element = arcGetElement(dec, arc); - if( element != MARKER_COLUMN_ELEMENT && element != MARKER_ROW_ELEMENT) + if( element != MARKER_COLUMN_ELEMENT && element != MARKER_ROW_ELEMENT ) { - if( SPQRelementIsColumn(element)) + if( SPQRelementIsColumn(element) ) { - SCIP_CALL(createColumnArc(dec, newCycle, &duplicate, SPQRelementToColumn(element), TRUE)); + SCIP_CALL( createColumnArc(dec, newCycle, &duplicate, SPQRelementToColumn(element), TRUE) ); } else { - SCIP_CALL(createRowArc(dec, newCycle, &duplicate, SPQRelementToRow(element), TRUE)); + SCIP_CALL( createRowArc(dec, newCycle, &duplicate, SPQRelementToRow(element), TRUE) ); } } else if( isParent ) { //create parent marker - SCIP_CALL(createParentMarker(dec, newCycle, arcIsTree(dec, arc), adjacentMember, - markerOfParent(dec, member), &duplicate, TRUE)); + SCIP_CALL( createParentMarker(dec, newCycle, arcIsTree(dec, arc), adjacentMember, + markerOfParent(dec, member), &duplicate, TRUE) ); } else { //create child marker - SCIP_CALL(createChildMarker(dec, newCycle, adjacentMember, arcIsTree(dec, arc), &duplicate, TRUE)); + SCIP_CALL( createChildMarker(dec, newCycle, adjacentMember, arcIsTree(dec, arc), &duplicate, TRUE) ); dec->members[adjacentMember].parentMember = newCycle; dec->members[adjacentMember].markerOfParent = duplicate; } @@ -9208,13 +9421,13 @@ SCIP_RETCODE rigidTransformArcIntoCycle( spqr_arc cycleMarker = SPQR_INVALID_ARC; if( isParent ) { - SCIP_CALL(createChildMarker(dec, newCycle, member, !arcIsTree(dec, arc), - &cycleMarker, FALSE)); + SCIP_CALL( createChildMarker(dec, newCycle, member, !arcIsTree(dec, arc), + &cycleMarker, FALSE) ); } else { - SCIP_CALL(createParentMarker(dec, newCycle, !arcIsTree(dec, arc), - member, arc, &cycleMarker, FALSE)); + SCIP_CALL( createParentMarker(dec, newCycle, !arcIsTree(dec, arc), + member, arc, &cycleMarker, FALSE) ); } //Change the existing edge to a marker if( isParent ) @@ -9226,7 +9439,6 @@ SCIP_RETCODE rigidTransformArcIntoCycle( dec->members[member].markerOfParent = cycleMarker; dec->arcs[arc].element = arcIsTree(dec, arc) ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT; dec->arcs[arc].childMember = SPQR_INVALID_MEMBER; - } else { @@ -9239,7 +9451,7 @@ SCIP_RETCODE rigidTransformArcIntoCycle( return SCIP_OKAY; } -/**< Updates a single rigid member to reflect the new row. */ +/** Updates a single rigid member to reflect the new row. */ static SCIP_RETCODE transformSingleRigid( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -9249,14 +9461,13 @@ SCIP_RETCODE transformSingleRigid( NewRowInformation* const newRowInfo /**< Stores information about the new row placement */ ) { - if( SPQRarcIsValid(newRow->reducedMembers[reducedMember].articulationArc)) + if( SPQRarcIsValid(newRow->reducedMembers[reducedMember].articulationArc) ) { spqr_arc arc = newRow->reducedMembers[reducedMember].articulationArc; //Cut arc is propagated into a cycle with new arc assert(newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcHeadNoCompression(dec, arc) || newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcTailNoCompression(dec, arc)); - SCIP_Bool reversed; if( newRow->isArcCut[arc] ) @@ -9270,8 +9481,8 @@ SCIP_RETCODE transformSingleRigid( newRow->reducedMembers[reducedMember].otherIsSource; } - SCIP_CALL(rigidTransformArcIntoCycle(dec, member, newRow->reducedMembers[reducedMember].articulationArc, - reversed, newRowInfo)); + SCIP_CALL( rigidTransformArcIntoCycle(dec, member, newRow->reducedMembers[reducedMember].articulationArc, + reversed, newRowInfo) ); return SCIP_OKAY; } @@ -9283,7 +9494,7 @@ SCIP_RETCODE transformSingleRigid( { //Create a new node; move all cut arcs end of split node to it and add new arc between new node and split node spqr_node newNode = SPQR_INVALID_NODE; - SCIP_CALL(createNode(dec, &newNode)); + SCIP_CALL( createNode(dec, &newNode) ); cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; do @@ -9301,7 +9512,7 @@ SCIP_RETCODE transformSingleRigid( cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; } - while( cutArcIsValid(cutArcIdx)); + while( cutArcIsValid(cutArcIdx) ); newRowInfo->member = member; if( newRow->reducedMembers[reducedMember].otherIsSource ) @@ -9323,7 +9534,7 @@ SCIP_RETCODE transformSingleRigid( //Articulation point was split (based on coloring) spqr_node newNode = SPQR_INVALID_NODE; - SCIP_CALL(createNode(dec, &newNode)); + SCIP_CALL( createNode(dec, &newNode) ); spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode); spqr_arc iterArc = firstNodeArc; @@ -9376,7 +9587,7 @@ SCIP_RETCODE transformSingleRigid( return SCIP_OKAY; } -/**< Splits a single parallel member into two adjacent ones, where the cut arcs and non-cut arcs get their own member */ +/** Splits a single parallel member into two adjacent ones, where the cut arcs and non-cut arcs get their own member. */ static SCIP_RETCODE splitParallelRowAddition( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -9404,7 +9615,7 @@ SCIP_RETCODE splitParallelRowAddition( } treeArc = getNextMemberArc(dec, treeArc); } - while( treeArc != getFirstMemberArc(dec, member)); + while( treeArc != getFirstMemberArc(dec, member) ); assert(arcIsTree(dec, treeArc)); SCIP_Bool treeReversed = arcIsReversedNonRigid(dec, treeArc); @@ -9417,12 +9628,12 @@ SCIP_RETCODE splitParallelRowAddition( { spqr_member adjacentMember = SPQR_INVALID_MEMBER; spqr_arc adjacentArc = SPQR_INVALID_ARC; - if( treeArc == markerToParent(dec, member)) + if( treeArc == markerToParent(dec, member) ) { adjacentMember = findMemberParent(dec, member); adjacentArc = markerOfParent(dec, member); } - else if( arcIsChildMarker(dec, treeArc)) + else if( arcIsChildMarker(dec, treeArc) ) { adjacentMember = findArcChildMember(dec, treeArc); adjacentArc = markerToParent(dec, adjacentMember); @@ -9435,7 +9646,7 @@ SCIP_RETCODE splitParallelRowAddition( if( SPQRmemberIsValid(adjacentMember) && getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_SERIES ) { newRowInfo->member = adjacentMember; - if( arcIsReversedNonRigid(dec, treeArc) == arcIsReversedNonRigid(dec, adjacentArc)) + if( arcIsReversedNonRigid(dec, treeArc) == arcIsReversedNonRigid(dec, adjacentArc) ) { newRowInfo->reversed = !firstReversed; } @@ -9446,29 +9657,29 @@ SCIP_RETCODE splitParallelRowAddition( return SCIP_OKAY; } spqr_member cutMember = SPQR_INVALID_MEMBER; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember) ); cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; assert(cutArcIsValid(cutArcIdx)); SCIP_Bool parentCut = FALSE; - while( cutArcIsValid(cutArcIdx)) + while( cutArcIsValid(cutArcIdx) ) { spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; moveArcToNewMember(dec, cutArc, member, cutMember); - if( cutArc == markerToParent(dec, member)) + if( cutArc == markerToParent(dec, member) ) { parentCut = TRUE; } } if( parentCut ) { - SCIP_CALL(createMarkerPair(dec, cutMember, member, TRUE, FALSE, TRUE)); + SCIP_CALL( createMarkerPair(dec, cutMember, member, TRUE, FALSE, TRUE) ); } else { - SCIP_CALL(createMarkerPair(dec, member, cutMember, FALSE, TRUE, FALSE)); + SCIP_CALL( createMarkerPair(dec, member, cutMember, FALSE, TRUE, FALSE) ); } changeLoopToSeries(dec, member); newRowInfo->member = member; @@ -9484,33 +9695,33 @@ SCIP_RETCODE splitParallelRowAddition( else { spqr_member cutMember = SPQR_INVALID_MEMBER; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember) ); cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; assert(cutArcIsValid(cutArcIdx)); SCIP_Bool parentCut = FALSE; - while( cutArcIsValid(cutArcIdx)) + while( cutArcIsValid(cutArcIdx) ) { spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; moveArcToNewMember(dec, cutArc, member, cutMember); - if( cutArc == markerToParent(dec, member)) + if( cutArc == markerToParent(dec, member) ) { parentCut = TRUE; } } spqr_member newSeries; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &newSeries)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_SERIES, &newSeries) ); if( parentCut ) { - SCIP_CALL(createMarkerPair(dec, newSeries, member, TRUE, FALSE, FALSE)); - SCIP_CALL(createMarkerPair(dec, cutMember, newSeries, TRUE, FALSE, TRUE)); + SCIP_CALL( createMarkerPair(dec, newSeries, member, TRUE, FALSE, FALSE) ); + SCIP_CALL( createMarkerPair(dec, cutMember, newSeries, TRUE, FALSE, TRUE) ); } else { - SCIP_CALL(createMarkerPair(dec, member, newSeries, FALSE, FALSE, FALSE)); - SCIP_CALL(createMarkerPair(dec, newSeries, cutMember, FALSE, TRUE, FALSE)); + SCIP_CALL( createMarkerPair(dec, member, newSeries, FALSE, FALSE, FALSE) ); + SCIP_CALL( createMarkerPair(dec, newSeries, cutMember, FALSE, TRUE, FALSE) ); } newRowInfo->member = newSeries; cut_arc_id firstCut = newRow->reducedMembers[reducedMember].firstCutArc; @@ -9528,42 +9739,42 @@ SCIP_RETCODE splitParallelRowAddition( #ifndef NDEBUG spqr_arc arc = newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc; spqr_member adjacentMember = SPQR_INVALID_MEMBER; - if( arc == markerToParent(dec, member)) + if( arc == markerToParent(dec, member) ) { adjacentMember = findMemberParent(dec, member); } - else if( arcIsChildMarker(dec, arc)) + else if( arcIsChildMarker(dec, arc) ) { adjacentMember = findArcChildMember(dec, arc); } - if( SPQRmemberIsValid(adjacentMember)) + if( SPQRmemberIsValid(adjacentMember) ) { assert(getMemberType(dec, adjacentMember) != SPQR_MEMBERTYPE_SERIES); } #endif spqr_member newSeries; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &newSeries)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_SERIES, &newSeries) ); cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; assert(cutArcIsValid(cutArcIdx)); SCIP_Bool parentCut = FALSE; - while( cutArcIsValid(cutArcIdx)) + while( cutArcIsValid(cutArcIdx) ) { spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; moveArcToNewMember(dec, cutArc, member, newSeries); - if( cutArc == markerToParent(dec, member)) + if( cutArc == markerToParent(dec, member) ) { parentCut = TRUE; } } if( parentCut ) { - SCIP_CALL(createMarkerPair(dec, newSeries, member, TRUE, TRUE, FALSE)); + SCIP_CALL( createMarkerPair(dec, newSeries, member, TRUE, TRUE, FALSE) ); } else { - SCIP_CALL(createMarkerPair(dec, member, newSeries, FALSE, FALSE, TRUE)); + SCIP_CALL( createMarkerPair(dec, member, newSeries, FALSE, FALSE, TRUE) ); } newRowInfo->member = newSeries; cut_arc_id cutArcId = newRow->reducedMembers[reducedMember].firstCutArc; @@ -9573,7 +9784,7 @@ SCIP_RETCODE splitParallelRowAddition( return SCIP_OKAY; } -/**< Updates a single rigid member to reflect the new row. */ +/** Updates a single rigid member to reflect the new row. */ static SCIP_RETCODE transformSingleParallel( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -9583,11 +9794,11 @@ SCIP_RETCODE transformSingleParallel( NewRowInformation* info /**< Stores information about the new row placement */ ) { - SCIP_CALL(splitParallelRowAddition(dec, newRow, reducedMember, member, info)); + SCIP_CALL( splitParallelRowAddition(dec, newRow, reducedMember, member, info) ); return SCIP_OKAY; } -/**< Split a series member into multiple series members for merging */ +/** Split a series member into multiple series members for merging. */ static SCIP_RETCODE splitSeriesMergingRowAddition( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -9621,7 +9832,7 @@ SCIP_RETCODE splitSeriesMergingRowAddition( } } } - if( SPQRarcIsInvalid(otherArc)) + if( SPQRarcIsInvalid(otherArc) ) { #ifndef NDEBUG reduced_member_id parent = newRow->reducedMembers[reducedMember].parent; @@ -9648,7 +9859,7 @@ SCIP_RETCODE splitSeriesMergingRowAddition( } //Split off the relevant part of the series member spqr_member mergingSeries = SPQR_INVALID_MEMBER; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_SERIES, &mergingSeries)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_SERIES, &mergingSeries) ); //Move all marker arcs which point to another component in the reduced decomposition to the new member //This should be exactly 2, as with 3 the result is not network anymore //move all mergeable children and parent arcs to the mergingMember @@ -9665,7 +9876,7 @@ SCIP_RETCODE splitSeriesMergingRowAddition( { spqr_arc moveArc = markerOfParent(dec, findMember(dec, newRow->reducedMembers[child].member)); moveArcToNewMember(dec, moveArc, member, mergingSeries); - if( !arcIsTree(dec, moveArc)) + if( !arcIsTree(dec, moveArc) ) { coTreeToMergingMember = TRUE; } @@ -9682,7 +9893,7 @@ SCIP_RETCODE splitSeriesMergingRowAddition( spqr_arc moveArc = markerToParent(dec, member); moveArcToNewMember(dec, moveArc, member, mergingSeries); parentToMergingMember = TRUE; - if( !arcIsTree(dec, moveArc)) + if( !arcIsTree(dec, moveArc) ) { coTreeToMergingMember = TRUE; } @@ -9690,23 +9901,24 @@ SCIP_RETCODE splitSeriesMergingRowAddition( spqr_arc ignoreArc = SPQR_INVALID_ARC; if( parentToMergingMember ) { - SCIP_CALL(createMarkerPairWithReferences(dec, mergingSeries, member, coTreeToMergingMember, TRUE, FALSE, - representativeEdge, &ignoreArc)); + SCIP_CALL( createMarkerPairWithReferences(dec, mergingSeries, member, coTreeToMergingMember, TRUE, FALSE, + representativeEdge, &ignoreArc) ); } else { SCIP_CALL( createMarkerPairWithReferences(dec, member, mergingSeries, !coTreeToMergingMember, FALSE, TRUE, &ignoreArc, - representativeEdge)); + representativeEdge) ); } *mergingMember = mergingSeries; assert(getNumMemberArcs(dec, mergingSeries) == 3); assert(getNumMemberArcs(dec, member) >= 3); + return SCIP_OKAY; } -/**< Split a parallel member into multiple parallel members for merging */ +/** Split a parallel member into multiple parallel members for merging. */ static SCIP_RETCODE splitParallelMerging( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -9740,17 +9952,17 @@ SCIP_RETCODE splitParallelMerging( if( createCutParallel && keepOriginalParallel ) { SCIP_Bool parentCut = FALSE; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember) ); cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; assert(cutArcIsValid(cutArcIdx)); - while( cutArcIsValid(cutArcIdx)) + while( cutArcIsValid(cutArcIdx) ) { spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; moveArcToNewMember(dec, cutArc, member, cutMember); - if( cutArc == markerToParent(dec, member)) + if( cutArc == markerToParent(dec, member) ) { parentCut = TRUE; } @@ -9759,12 +9971,12 @@ SCIP_RETCODE splitParallelMerging( if( parentCut ) { SCIP_CALL( - createMarkerPairWithReferences(dec, cutMember, member, TRUE, FALSE, FALSE, &ignoreArc, cutRepresentative)); + createMarkerPairWithReferences(dec, cutMember, member, TRUE, FALSE, FALSE, &ignoreArc, cutRepresentative) ); } else { SCIP_CALL( - createMarkerPairWithReferences(dec, member, cutMember, FALSE, FALSE, FALSE, cutRepresentative, &ignoreArc)); + createMarkerPairWithReferences(dec, member, cutMember, FALSE, FALSE, FALSE, cutRepresentative, &ignoreArc) ); } *pMergeMember = member; @@ -9774,17 +9986,17 @@ SCIP_RETCODE splitParallelMerging( assert(!keepOriginalParallel); SCIP_Bool parentCut = FALSE; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember) ); cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc; assert(cutArcIsValid(cutArcIdx)); - while( cutArcIsValid(cutArcIdx)) + while( cutArcIsValid(cutArcIdx) ) { spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc; cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember; moveArcToNewMember(dec, cutArc, member, cutMember); - if( cutArc == markerToParent(dec, member)) + if( cutArc == markerToParent(dec, member) ) { parentCut = TRUE; } @@ -9793,12 +10005,12 @@ SCIP_RETCODE splitParallelMerging( if( parentCut ) { SCIP_CALL( - createMarkerPairWithReferences(dec, cutMember, member, TRUE, FALSE, FALSE, &ignoreArc, cutRepresentative)); + createMarkerPairWithReferences(dec, cutMember, member, TRUE, FALSE, FALSE, &ignoreArc, cutRepresentative) ); } else { SCIP_CALL( - createMarkerPairWithReferences(dec, member, cutMember, FALSE, FALSE, FALSE, cutRepresentative, &ignoreArc)); + createMarkerPairWithReferences(dec, member, cutMember, FALSE, FALSE, FALSE, cutRepresentative, &ignoreArc) ); } @@ -9806,7 +10018,7 @@ SCIP_RETCODE splitParallelMerging( spqr_member mergingMember = member; SCIP_Bool parentToMergingMember = FALSE; SCIP_Bool treeToMergingMember = FALSE; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &mergingMember)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &mergingMember) ); //move all mergeable children and parent arcs to the mergingMember for( children_idx i = newRow->reducedMembers[reducedMember].firstChild; i < newRow->reducedMembers[reducedMember].firstChild + @@ -9820,7 +10032,7 @@ SCIP_RETCODE splitParallelMerging( { spqr_arc moveArc = markerOfParent(dec, findMember(dec, newRow->reducedMembers[child].member)); moveArcToNewMember(dec, moveArc, member, mergingMember); - if( arcIsTree(dec, moveArc)) + if( arcIsTree(dec, moveArc) ) { treeToMergingMember = TRUE; } @@ -9833,15 +10045,15 @@ SCIP_RETCODE splitParallelMerging( spqr_arc moveArc = markerToParent(dec, member); moveArcToNewMember(dec, moveArc, member, mergingMember); parentToMergingMember = TRUE; - if( arcIsTree(dec, moveArc)) + if( arcIsTree(dec, moveArc) ) { treeToMergingMember = TRUE; } } //If there is only one cut arc, we also move it. - if( SPQRarcIsValid(*cutRepresentative)) + if( SPQRarcIsValid(*cutRepresentative) ) { - if( *cutRepresentative == markerToParent(dec, member)) + if( *cutRepresentative == markerToParent(dec, member) ) { parentToMergingMember = TRUE; } @@ -9850,31 +10062,30 @@ SCIP_RETCODE splitParallelMerging( spqr_arc ignoreArgument = SPQR_INVALID_ARC; if( parentToMergingMember || parentCut ) { - SCIP_CALL(createMarkerPairWithReferences(dec, mergingMember, member, !treeToMergingMember, FALSE, FALSE, - &ignoreArgument, &noCutRepresentative)); + SCIP_CALL( createMarkerPairWithReferences(dec, mergingMember, member, !treeToMergingMember, FALSE, FALSE, + &ignoreArgument, &noCutRepresentative) ); } else { - SCIP_CALL(createMarkerPairWithReferences(dec, member, mergingMember, treeToMergingMember, FALSE, FALSE, - &noCutRepresentative, &ignoreArgument)); + SCIP_CALL( createMarkerPairWithReferences(dec, member, mergingMember, treeToMergingMember, FALSE, FALSE, + &noCutRepresentative, &ignoreArgument) ); } *pMergeMember = mergingMember; } else if( keepOriginalParallel ) { assert(!createCutParallel); - if( cutArcIsValid(newRow->reducedMembers[reducedMember].firstCutArc)) + if( cutArcIsValid(newRow->reducedMembers[reducedMember].firstCutArc) ) { *cutRepresentative = newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc; } *pMergeMember = member; - } else { assert(!keepOriginalParallel && !createCutParallel); - if( cutArcIsValid(newRow->reducedMembers[reducedMember].firstCutArc)) + if( cutArcIsValid(newRow->reducedMembers[reducedMember].firstCutArc) ) { *cutRepresentative = newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc; } @@ -9883,7 +10094,7 @@ SCIP_RETCODE splitParallelMerging( spqr_member mergingMember = member; SCIP_Bool parentToMergingMember = FALSE; SCIP_Bool treeToMergingMember = FALSE; - SCIP_CALL(createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &mergingMember)); + SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &mergingMember) ); //move all mergeable children and parent arcs to the mergingMember for( children_idx i = newRow->reducedMembers[reducedMember].firstChild; i < newRow->reducedMembers[reducedMember].firstChild + @@ -9897,7 +10108,7 @@ SCIP_RETCODE splitParallelMerging( { spqr_arc moveArc = markerOfParent(dec, findMember(dec, newRow->reducedMembers[child].member)); moveArcToNewMember(dec, moveArc, member, mergingMember); - if( arcIsTree(dec, moveArc)) + if( arcIsTree(dec, moveArc) ) { treeToMergingMember = TRUE; } @@ -9910,15 +10121,15 @@ SCIP_RETCODE splitParallelMerging( spqr_arc moveArc = markerToParent(dec, member); moveArcToNewMember(dec, moveArc, member, mergingMember); parentToMergingMember = TRUE; - if( arcIsTree(dec, moveArc)) + if( arcIsTree(dec, moveArc) ) { treeToMergingMember = TRUE; } } //If there is only one cut arc, we also move it. - if( SPQRarcIsValid(*cutRepresentative)) + if( SPQRarcIsValid(*cutRepresentative) ) { - if( *cutRepresentative == markerToParent(dec, member)) + if( *cutRepresentative == markerToParent(dec, member) ) { parentToMergingMember = TRUE; } @@ -9927,20 +10138,21 @@ SCIP_RETCODE splitParallelMerging( spqr_arc ignoreArgument = SPQR_INVALID_ARC; if( parentToMergingMember ) { - SCIP_CALL(createMarkerPairWithReferences(dec, mergingMember, member, !treeToMergingMember, FALSE, FALSE, - &ignoreArgument, &noCutRepresentative)); + SCIP_CALL( createMarkerPairWithReferences(dec, mergingMember, member, !treeToMergingMember, FALSE, FALSE, + &ignoreArgument, &noCutRepresentative) ); } else { - SCIP_CALL(createMarkerPairWithReferences(dec, member, mergingMember, treeToMergingMember, FALSE, FALSE, - &noCutRepresentative, &ignoreArgument)); + SCIP_CALL( createMarkerPairWithReferences(dec, member, mergingMember, treeToMergingMember, FALSE, FALSE, + &noCutRepresentative, &ignoreArgument) ); } *pMergeMember = mergingMember; } + return SCIP_OKAY; } -/**< Update the first leaf to reflect the addition of the new row */ +/** Update the first leaf to reflect the addition of the new row */ static SCIP_RETCODE splitFirstLeaf( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -9956,15 +10168,15 @@ SCIP_RETCODE splitFirstLeaf( { spqr_member mergeMember = SPQR_INVALID_MEMBER; spqr_arc cutRepresentative = SPQR_INVALID_ARC; - SCIP_CALL(splitParallelMerging(dec, newRow, leaf, member, &mergeMember, &cutRepresentative)); + SCIP_CALL( splitParallelMerging(dec, newRow, leaf, member, &mergeMember, &cutRepresentative) ); newRow->reducedMembers[leaf].member = mergeMember; spqr_node firstNode = SPQR_INVALID_NODE; spqr_node secondNode = SPQR_INVALID_NODE; spqr_node thirdNode = SPQR_INVALID_NODE; - SCIP_CALL(createNode(dec, &firstNode)); - SCIP_CALL(createNode(dec, &secondNode)); - SCIP_CALL(createNode(dec, &thirdNode)); + SCIP_CALL( createNode(dec, &firstNode) ); + SCIP_CALL( createNode(dec, &secondNode) ); + SCIP_CALL( createNode(dec, &thirdNode) ); spqr_arc splitArc = newRow->reducedMembers[leaf].splitArc; assert(findArcMemberNoCompression(dec, splitArc) == mergeMember); @@ -9982,7 +10194,7 @@ SCIP_RETCODE splitFirstLeaf( { if( arc != cutRepresentative ) { - if( arcIsReversedNonRigid(dec, arc)) + if( arcIsReversedNonRigid(dec, arc) ) { setArcHeadAndTail(dec, arc, secondNode, firstNode); } @@ -9993,7 +10205,7 @@ SCIP_RETCODE splitFirstLeaf( } else { - if(( arcIsReversedNonRigid(dec, arc) == splitArcReversed ) == splitHead ) + if( (arcIsReversedNonRigid(dec, arc) == splitArcReversed) == splitHead ) { setArcHeadAndTail(dec, arc, thirdNode, otherNode); } @@ -10015,7 +10227,6 @@ SCIP_RETCODE splitFirstLeaf( } while( arc != first_arc ); - updateMemberType(dec, mergeMember, SPQR_MEMBERTYPE_RIGID); newRowInfo->member = mergeMember; if( newRow->reducedMembers[leaf].otherIsSource ) @@ -10037,7 +10248,7 @@ SCIP_RETCODE splitFirstLeaf( assert(getMemberType(dec, member) == SPQR_MEMBERTYPE_RIGID); spqr_node newNode = SPQR_INVALID_NODE;//Sink node - SCIP_CALL(createNode(dec, &newNode)); + SCIP_CALL( createNode(dec, &newNode) ); spqr_node splitNode = newRow->reducedMembers[leaf].splitNode; @@ -10087,7 +10298,10 @@ SCIP_RETCODE splitFirstLeaf( return SCIP_OKAY; } -/**< Merge an (updated) member into its parent. This function is mainly there to prevent duplication. */ +/** Merge an (updated) member into its parent. + * + * This function is mainly there to prevent duplication. + */ static SCIP_RETCODE mergeSplitMemberIntoParent( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -10160,11 +10374,13 @@ SCIP_RETCODE mergeSplitMemberIntoParent( } updateMemberType(dec, newMember, SPQR_MEMBERTYPE_RIGID); *mergedMember = newMember; + return SCIP_OKAY; } -/**< Update a series member to reflect the addition of the new row, and merge it into the previous members that were - * updated. */ +/** Update a series member to reflect the addition of the new row, and merge it into the previous members that were + * updated. + */ static SCIP_RETCODE splitAndMergeSeries( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -10179,7 +10395,8 @@ SCIP_RETCODE splitAndMergeSeries( SCIP_Bool isCut = FALSE; spqr_member mergingMember = SPQR_INVALID_MEMBER; spqr_arc nonVirtualArc = SPQR_INVALID_ARC; - SCIP_CALL(splitSeriesMergingRowAddition(dec, newRow, smallMember, member, &mergingMember, &isCut, &nonVirtualArc)); + + SCIP_CALL( splitSeriesMergingRowAddition(dec, newRow, smallMember, member, &mergingMember, &isCut, &nonVirtualArc) ); assert(getNumMemberArcs(dec, mergingMember) == 3); //create the split series. There's two possible configurations, based on whether it contains a cut edge or not @@ -10187,10 +10404,10 @@ SCIP_RETCODE splitAndMergeSeries( spqr_node b = SPQR_INVALID_NODE; spqr_node c = SPQR_INVALID_NODE; spqr_node d = SPQR_INVALID_NODE; - SCIP_CALL(createNode(dec, &a)); - SCIP_CALL(createNode(dec, &b)); - SCIP_CALL(createNode(dec, &c)); - SCIP_CALL(createNode(dec, &d)); + SCIP_CALL( createNode(dec, &a) ); + SCIP_CALL( createNode(dec, &b) ); + SCIP_CALL( createNode(dec, &c) ); + SCIP_CALL( createNode(dec, &d) ); spqr_arc splitArc = newRow->reducedMembers[smallMember].splitArc; @@ -10214,7 +10431,7 @@ SCIP_RETCODE splitAndMergeSeries( } else if( arc == nonVirtualArc ) { - if(( arcIsReversedNonRigid(dec, arc) == splitReversed ) == splitHead ) + if( (arcIsReversedNonRigid(dec, arc) == splitReversed) == splitHead ) { setArcHeadAndTail(dec, arc, a, d); } @@ -10226,7 +10443,7 @@ SCIP_RETCODE splitAndMergeSeries( else { spqr_node otherNode = cutArcIsValid(newRow->reducedMembers[smallMember].firstCutArc) ? c : b; - if(( arcIsReversedNonRigid(dec, arc) == splitReversed ) == splitHead ) + if( (arcIsReversedNonRigid(dec, arc) == splitReversed) == splitHead ) { setArcHeadAndTail(dec, arc, d, otherNode); } @@ -10261,19 +10478,19 @@ SCIP_RETCODE splitAndMergeSeries( spqr_node thirdNode; if( largeIsParent ) { - SCIP_CALL(mergeSplitMemberIntoParent(dec, newRow, mergingMember, otherMember, otherMarker, splitArc, TRUE, - otherNode, c, &mergedMember, - &arcNodeOne, - &arcNodeTwo, - &thirdNode)); + SCIP_CALL( mergeSplitMemberIntoParent(dec, newRow, mergingMember, otherMember, otherMarker, splitArc, TRUE, + otherNode, c, &mergedMember, + &arcNodeOne, + &arcNodeTwo, + &thirdNode) ); } else { - SCIP_CALL(mergeSplitMemberIntoParent(dec, newRow, otherMember, mergingMember, splitArc, otherMarker, TRUE, - c, otherNode, &mergedMember, - &arcNodeOne, - &arcNodeTwo, - &thirdNode)); + SCIP_CALL( mergeSplitMemberIntoParent(dec, newRow, otherMember, mergingMember, splitArc, otherMarker, TRUE, + c, otherNode, &mergedMember, + &arcNodeOne, + &arcNodeTwo, + &thirdNode) ); } newRow->reducedMembers[smallMember].member = mergedMember; @@ -10304,8 +10521,9 @@ SCIP_RETCODE splitAndMergeSeries( return SCIP_OKAY; } -/**< Update a parallel member to reflect the addition of the new row, and merge it into the previous members that were - * updated. */ +/** Update a parallel member to reflect the addition of the new row, and merge it into the previous members that were + * updated. + */ static SCIP_RETCODE splitAndMergeParallel( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -10319,15 +10537,15 @@ SCIP_RETCODE splitAndMergeParallel( { spqr_member mergeMember = SPQR_INVALID_MEMBER; spqr_arc cutRepresentative = SPQR_INVALID_ARC; - SCIP_CALL(splitParallelMerging(dec, newRow, smallMember, member, &mergeMember, &cutRepresentative)); + SCIP_CALL( splitParallelMerging(dec, newRow, smallMember, member, &mergeMember, &cutRepresentative) ); newRow->reducedMembers[smallMember].member = mergeMember; spqr_node firstNode = SPQR_INVALID_NODE; spqr_node secondNode = SPQR_INVALID_NODE; spqr_node thirdNode = SPQR_INVALID_NODE; - SCIP_CALL(createNode(dec, &firstNode)); - SCIP_CALL(createNode(dec, &secondNode)); - SCIP_CALL(createNode(dec, &thirdNode)); + SCIP_CALL( createNode(dec, &firstNode) ); + SCIP_CALL( createNode(dec, &secondNode) ); + SCIP_CALL( createNode(dec, &thirdNode) ); spqr_arc splitArc = newRow->reducedMembers[smallMember].splitArc; assert(findArcMemberNoCompression(dec, splitArc) == mergeMember); @@ -10345,7 +10563,7 @@ SCIP_RETCODE splitAndMergeParallel( { if( arc != cutRepresentative ) { - if( arcIsReversedNonRigid(dec, arc)) + if( arcIsReversedNonRigid(dec, arc) ) { setArcHeadAndTail(dec, arc, secondNode, firstNode); } @@ -10356,7 +10574,7 @@ SCIP_RETCODE splitAndMergeParallel( } else { - if(( arcIsReversedNonRigid(dec, arc) == splitArcReversed ) == splitHead ) + if( (arcIsReversedNonRigid(dec, arc) == splitArcReversed) == splitHead ) { setArcHeadAndTail(dec, arc, thirdNode, otherNode); } @@ -10392,22 +10610,21 @@ SCIP_RETCODE splitAndMergeParallel( spqr_node mergeNodeThree; if( largeIsParent ) { - SCIP_CALL(mergeSplitMemberIntoParent(dec, newRow, mergeMember, otherMember, otherMarker, splitArc, TRUE, - largeOtherNode, thirdNode, &mergedMember, - &arcNodeOne, - &arcNodeTwo, - &mergeNodeThree)); + SCIP_CALL( mergeSplitMemberIntoParent(dec, newRow, mergeMember, otherMember, otherMarker, splitArc, TRUE, + largeOtherNode, thirdNode, &mergedMember, + &arcNodeOne, + &arcNodeTwo, + &mergeNodeThree) ); } else { - SCIP_CALL(mergeSplitMemberIntoParent(dec, newRow, otherMember, mergeMember, splitArc, otherMarker, TRUE, - thirdNode, largeOtherNode, &mergedMember, - &arcNodeOne, - &arcNodeTwo, - &mergeNodeThree)); + SCIP_CALL( mergeSplitMemberIntoParent(dec, newRow, otherMember, mergeMember, splitArc, otherMarker, TRUE, + thirdNode, largeOtherNode, &mergedMember, + &arcNodeOne, + &arcNodeTwo, + &mergeNodeThree) ); } - newRowInfo->member = mergedMember; SCIP_Bool splitIsReferenceHead = newRow->reducedMembers[smallMember].splitHead; @@ -10436,8 +10653,9 @@ SCIP_RETCODE splitAndMergeParallel( return SCIP_OKAY; } -/**< Update a rigid member to reflect the addition of the new row, and merge it into the previous members that were - * updated. */ +/** Update a rigid member to reflect the addition of the new row, and merge it into the previous members that were + * updated. + */ static SCIP_RETCODE splitAndMergeRigid( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -10449,9 +10667,8 @@ SCIP_RETCODE splitAndMergeRigid( spqr_member member /**< The member belonging to the reduced rigid member */ ) { - spqr_node newNode = SPQR_INVALID_NODE;//Sink node - SCIP_CALL(createNode(dec, &newNode)); + SCIP_CALL( createNode(dec, &newNode) ); spqr_member smallMemberMember = member; spqr_member largeMemberMember = newRowInfo->member; @@ -10538,7 +10755,7 @@ SCIP_RETCODE splitAndMergeRigid( largeOtherNode, smallOtherNode, &mergedMember, &arcNodeOne, &arcNodeTwo, - &mergeNodeThree)); + &mergeNodeThree) ); } else { @@ -10547,7 +10764,7 @@ SCIP_RETCODE splitAndMergeRigid( smallOtherNode, largeOtherNode, &mergedMember, &arcNodeOne, &arcNodeTwo, - &mergeNodeThree)); + &mergeNodeThree) ); } newRowInfo->member = mergedMember; @@ -10584,7 +10801,7 @@ SCIP_RETCODE splitAndMergeRigid( return SCIP_OKAY; } -/**< Update a member to reflect the addition of the new row, and merge it into the previous members that were updated.*/ +/** Update a member to reflect the addition of the new row, and merge it into the previous members that were updated. */ static SCIP_RETCODE splitAndMerge( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -10600,17 +10817,17 @@ SCIP_RETCODE splitAndMerge( { case SPQR_MEMBERTYPE_RIGID: { - SCIP_CALL(splitAndMergeRigid(dec, newRow, smallMember, largeIsParent, newRowInfo, member)); + SCIP_CALL( splitAndMergeRigid(dec, newRow, smallMember, largeIsParent, newRowInfo, member) ); break; } case SPQR_MEMBERTYPE_PARALLEL: { - SCIP_CALL(splitAndMergeParallel(dec, newRow, smallMember, largeIsParent, newRowInfo, member)); + SCIP_CALL( splitAndMergeParallel(dec, newRow, smallMember, largeIsParent, newRowInfo, member) ); break; } case SPQR_MEMBERTYPE_SERIES: { - SCIP_CALL(splitAndMergeSeries(dec, newRow, smallMember, largeIsParent, newRowInfo, member)); + SCIP_CALL( splitAndMergeSeries(dec, newRow, smallMember, largeIsParent, newRowInfo, member) ); break; } case SPQR_MEMBERTYPE_LOOP: @@ -10622,8 +10839,9 @@ SCIP_RETCODE splitAndMerge( return SCIP_OKAY; } -/**< Update an SPQR tree with multiple members to reflect the addition of a new row, - * merging it into one big rigid node. */ +/** Update an SPQR tree with multiple members to reflect the addition of a new row, + * merging it into one big rigid node. + */ static SCIP_RETCODE mergeTree( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -10649,15 +10867,15 @@ SCIP_RETCODE mergeTree( } } } - SCIP_CALL(splitFirstLeaf(dec, newRow, leaf, newRowInfo)); + SCIP_CALL( splitFirstLeaf(dec, newRow, leaf, newRowInfo) ); reduced_member_id baseNode = leaf; reduced_member_id nextNode = newRow->reducedMembers[baseNode].parent; - while( reducedMemberIsValid(nextNode)) + while( reducedMemberIsValid(nextNode) ) { //check this node - SCIP_CALL(splitAndMerge(dec, newRow, nextNode, FALSE, newRowInfo)); + SCIP_CALL( splitAndMerge(dec, newRow, nextNode, FALSE, newRowInfo) ); //Recursively merge the children //use a while loop to avoid recursion; we may get stack overflows for large graphs @@ -10666,20 +10884,23 @@ SCIP_RETCODE mergeTree( data[0].id = nextNode; data[0].currentChild = newRow->reducedMembers[nextNode].firstChild ; int depth = 0; - while(depth >= 0){ + while( depth >= 0 ) + { reduced_member_id id = data[depth].id; children_idx childidx = data[depth].currentChild; - if(childidx == newRow->reducedMembers[id].firstChild + newRow->reducedMembers[id].numChildren){ + if( childidx == newRow->reducedMembers[id].firstChild + newRow->reducedMembers[id].numChildren ) + { --depth; continue; } reduced_member_id currentchild = newRow->childrenStorage[childidx]; data[depth].currentChild += 1; //skip this child if we already processed it or it is not merged - if( currentchild == baseNode || newRow->reducedMembers[currentchild].type == TYPE_PROPAGATED){ + if( currentchild == baseNode || newRow->reducedMembers[currentchild].type == TYPE_PROPAGATED ) + { continue; } - SCIP_CALL(splitAndMerge(dec, newRow, currentchild, TRUE, newRowInfo)); + SCIP_CALL( splitAndMerge(dec, newRow, currentchild, TRUE, newRowInfo) ); //recursively process the child depth += 1; @@ -10695,7 +10916,7 @@ SCIP_RETCODE mergeTree( return SCIP_OKAY; } -/**< Update an SPQR tree (a signle component of the SPQR forest) to reflect addition of a new row. */ +/** Update an SPQR tree (a single component of the SPQR forest) to reflect addition of a new row. */ static SCIP_RETCODE transformComponentRowAddition( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -10717,11 +10938,11 @@ SCIP_RETCODE transformComponentRowAddition( switch( type ) { case SPQR_MEMBERTYPE_RIGID: - SCIP_CALL(transformSingleRigid(dec, newRow, reducedMember, member, newRowInfo)); + SCIP_CALL( transformSingleRigid(dec, newRow, reducedMember, member, newRowInfo) ); break; case SPQR_MEMBERTYPE_PARALLEL: { - SCIP_CALL(transformSingleParallel(dec, newRow, reducedMember, member, newRowInfo)); + SCIP_CALL( transformSingleParallel(dec, newRow, reducedMember, member, newRowInfo) ); break; } case SPQR_MEMBERTYPE_LOOP: @@ -10750,12 +10971,12 @@ SCIP_RETCODE transformComponentRowAddition( return SCIP_OKAY; } - SCIP_CALL(mergeTree(dec, newRow, component->root, newRowInfo)); + SCIP_CALL( mergeTree(dec, newRow, component->root, newRowInfo) ); return SCIP_OKAY; } -/**< Create the network row addition data structure */ +/** Create the network row addition data structure. */ static SCIP_RETCODE SCIPnetrowaddCreate( BMS_BLKMEM* blkmem, /**< Block memory */ @@ -10763,7 +10984,7 @@ SCIP_RETCODE SCIPnetrowaddCreate( ) { assert(blkmem); - SCIP_ALLOC(BMSallocBlockMemory(blkmem, prowadd)); + SCIP_ALLOC( BMSallocBlockMemory(blkmem, prowadd) ); SCIP_NETROWADD* newRow = *prowadd; newRow->remainsNetwork = TRUE; @@ -10849,7 +11070,7 @@ SCIP_RETCODE SCIPnetrowaddCreate( return SCIP_OKAY; } -/**< Frees the network row addition data structure */ +/** Frees the network row addition data structure. */ static void SCIPnetrowaddFree( BMS_BLKMEM* blkmem, /**< Block memory */ @@ -10887,7 +11108,7 @@ void SCIPnetrowaddFree( BMSfreeBlockMemory(blkmem, prowadd); } -/**< Checks if the given row can be added to the network decomposition */ +/** Checks if the given row can be added to the network decomposition. */ static SCIP_RETCODE SCIPnetrowaddCheck( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -10903,20 +11124,21 @@ SCIP_RETCODE SCIPnetrowaddCheck( assert(nnonzs == 0 || nonzcols); /* A row can only be added once */ - if( netMatDecDataContainsRow(dec,row)){ + if( netMatDecDataContainsRow(dec,row) ) + { return SCIP_INVALIDDATA; } rowadd->remainsNetwork = TRUE; cleanUpPreviousIteration(dec, rowadd); - SCIP_CALL(newRowUpdateRowInformation(dec, rowadd, row, nonzcols, nonzvals, nnonzs)); - SCIP_CALL(constructRowReducedDecomposition(dec, rowadd)); - SCIP_CALL(createReducedDecompositionCutArcs(dec, rowadd)); + SCIP_CALL( newRowUpdateRowInformation(dec, rowadd, row, nonzcols, nonzvals, nnonzs) ); + SCIP_CALL( constructRowReducedDecomposition(dec, rowadd) ); + SCIP_CALL( createReducedDecompositionCutArcs(dec, rowadd) ); - SCIP_CALL(determineLeafReducedMembers(dec, rowadd)); - SCIP_CALL(allocateRigidSearchMemory(dec, rowadd)); - SCIP_CALL(allocateTreeSearchMemory(dec, rowadd)); + SCIP_CALL( determineLeafReducedMembers(dec, rowadd) ); + SCIP_CALL( allocateRigidSearchMemory(dec, rowadd) ); + SCIP_CALL( allocateTreeSearchMemory(dec, rowadd) ); //Check for each component if the cut arcs propagate through a row tree marker to a cut arc in another component //From the leafs inward. propagateComponents(dec, rowadd); @@ -10940,7 +11162,7 @@ SCIP_RETCODE SCIPnetrowaddCheck( return SCIP_OKAY; } -/**< Adds the last checked row to the network decomposition */ +/** Adds the last checked row to the network decomposition. */ static SCIP_RETCODE SCIPnetrowaddAdd( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ @@ -10951,18 +11173,18 @@ SCIP_RETCODE SCIPnetrowaddAdd( if( rowadd->numReducedComponents == 0 ) { spqr_member newMember = SPQR_INVALID_MEMBER; - SCIP_CALL(createStandaloneParallel(dec, rowadd->newColumnArcs, rowadd->newColumnReversed, - rowadd->numColumnArcs, rowadd->newRowIndex, &newMember)); + SCIP_CALL( createStandaloneParallel(dec, rowadd->newColumnArcs, rowadd->newColumnReversed, + rowadd->numColumnArcs, rowadd->newRowIndex, &newMember) ); } else if( rowadd->numReducedComponents == 1 ) { NewRowInformation information = emptyNewRowInformation(); - SCIP_CALL(transformComponentRowAddition(dec, rowadd, &rowadd->reducedComponents[0], &information)); + SCIP_CALL( transformComponentRowAddition(dec, rowadd, &rowadd->reducedComponents[0], &information) ); if( rowadd->numColumnArcs == 0 ) { spqr_arc rowArc = SPQR_INVALID_ARC; - SCIP_CALL(createRowArc(dec, information.member, &rowArc, rowadd->newRowIndex, information.reversed)); + SCIP_CALL( createRowArc(dec, information.member, &rowArc, rowadd->newRowIndex, information.reversed) ); if( SPQRnodeIsValid(information.head)) { assert(SPQRnodeIsValid(information.tail)); @@ -10975,13 +11197,13 @@ SCIP_RETCODE SCIPnetrowaddAdd( else { spqr_member new_row_parallel = SPQR_INVALID_MEMBER; - SCIP_CALL(createConnectedParallel(dec, rowadd->newColumnArcs, rowadd->newColumnReversed, rowadd->numColumnArcs, - rowadd->newRowIndex, &new_row_parallel)); + SCIP_CALL( createConnectedParallel(dec, rowadd->newColumnArcs, rowadd->newColumnReversed, rowadd->numColumnArcs, + rowadd->newRowIndex, &new_row_parallel) ); spqr_arc markerArc = SPQR_INVALID_ARC; spqr_arc ignore = SPQR_INVALID_ARC; - SCIP_CALL(createMarkerPairWithReferences(dec, information.member, new_row_parallel, TRUE, - information.reversed, FALSE, - &markerArc, &ignore)); + SCIP_CALL( createMarkerPairWithReferences(dec, information.member, new_row_parallel, TRUE, + information.reversed, FALSE, + &markerArc, &ignore) ); if( SPQRnodeIsValid(information.head)) { assert(SPQRnodeIsValid(information.tail)); @@ -11007,13 +11229,13 @@ SCIP_RETCODE SCIPnetrowaddAdd( int numDecComponentsBefore = numConnectedComponents(dec); #endif spqr_member new_row_parallel = SPQR_INVALID_MEMBER; - SCIP_CALL(createConnectedParallel(dec, rowadd->newColumnArcs, rowadd->newColumnReversed, rowadd->numColumnArcs, - rowadd->newRowIndex, &new_row_parallel)); + SCIP_CALL( createConnectedParallel(dec, rowadd->newColumnArcs, rowadd->newColumnReversed, rowadd->numColumnArcs, + rowadd->newRowIndex, &new_row_parallel) ); for( int i = 0; i < rowadd->numReducedComponents; ++i ) { NewRowInformation information = emptyNewRowInformation(); - SCIP_CALL(transformComponentRowAddition(dec, rowadd, &rowadd->reducedComponents[i], &information)); + SCIP_CALL( transformComponentRowAddition(dec, rowadd, &rowadd->reducedComponents[i], &information) ); if( getMemberType(dec, information.member) == SPQR_MEMBERTYPE_LOOP ) { assert(getNumMemberArcs(dec, information.member) == 1); @@ -11025,13 +11247,12 @@ SCIP_RETCODE SCIPnetrowaddAdd( } else { - reorderComponent(dec, - information.member);//Make sure the new component is the root of the local decomposition tree + reorderComponent(dec, information.member);//Make sure the new component is the root of the local decomposition tree spqr_arc markerArc = SPQR_INVALID_ARC; spqr_arc ignore = SPQR_INVALID_ARC; - SCIP_CALL(createMarkerPairWithReferences(dec, new_row_parallel, information.member, FALSE, - FALSE, information.reversed, - &ignore, &markerArc)); + SCIP_CALL( createMarkerPairWithReferences(dec, new_row_parallel, information.member, FALSE, + FALSE, information.reversed, + &ignore, &markerArc) ); if( SPQRnodeIsValid(information.head)) { assert(SPQRnodeIsValid(information.tail)); @@ -11046,10 +11267,11 @@ SCIP_RETCODE SCIPnetrowaddAdd( decreaseNumConnectedComponents(dec, rowadd->numReducedComponents - 1); assert(numConnectedComponents(dec) == ( numDecComponentsBefore - rowadd->numReducedComponents + 1 )); } + return SCIP_OKAY; } -/**< Returns whether the last checked row can be added to the network decomposition */ +/** Returns whether the last checked row can be added to the network decomposition. */ static SCIP_Bool SCIPnetrowaddRemainsNetwork( const SCIP_NETROWADD* rowadd /**< The network matrix row addition data structure */ @@ -11058,11 +11280,12 @@ SCIP_Bool SCIPnetrowaddRemainsNetwork( return rowadd->remainsNetwork; } -/**< A generic data structure that stores a decomposition and can perform both column and row additions */ -struct SCIP_Netmatdec{ - SCIP_NETMATDECDATA * dec; - SCIP_NETROWADD * rowadd; - SCIP_NETCOLADD * coladd; +/** A generic data structure that stores a decomposition and can perform both column and row additions. */ +struct SCIP_Netmatdec +{ + SCIP_NETMATDECDATA* dec; + SCIP_NETROWADD* rowadd; + SCIP_NETCOLADD* coladd; }; SCIP_RETCODE SCIPnetmatdecCreate( @@ -11072,13 +11295,14 @@ SCIP_RETCODE SCIPnetmatdecCreate( int ncols /**< The maximal number of columns that the decomposition can expect */ ) { - - SCIP_ALLOC(BMSallocBlockMemory(blkmem,pdec)); + SCIP_ALLOC( BMSallocBlockMemory(blkmem,pdec) ); SCIP_NETMATDEC* dec = *pdec; + dec->dec = NULL; - SCIP_CALL(netMatDecDataCreate(blkmem, &dec->dec, nrows, ncols)); + SCIP_CALL( netMatDecDataCreate(blkmem, &dec->dec, nrows, ncols) ); dec->rowadd = NULL; dec->coladd = NULL; + return SCIP_OKAY; } @@ -11088,11 +11312,12 @@ void SCIPnetmatdecFree( { SCIP_NETMATDEC* dec = *pdec; BMS_BLKMEM* blkmem = dec->dec->env; - if( dec->coladd != NULL) + + if( dec->coladd != NULL ) { SCIPnetcoladdFree(blkmem, &dec->coladd); } - if( dec->rowadd != NULL) + if( dec->rowadd != NULL ) { SCIPnetrowaddFree(blkmem, &dec->rowadd); } @@ -11109,17 +11334,18 @@ SCIP_RETCODE SCIPnetmatdecTryAddCol( SCIP_Bool* success /**< Buffer to store whether the column was added */ ) { - if( dec->coladd == NULL) + if( dec->coladd == NULL ) { - SCIP_CALL(SCIPnetcoladdCreate(dec->dec->env, &dec->coladd)); + SCIP_CALL( SCIPnetcoladdCreate(dec->dec->env, &dec->coladd) ); } - SCIP_CALL(SCIPnetcoladdCheck(dec->dec, dec->coladd, column, nonzrows, nonzvals, nnonzs)); + SCIP_CALL( SCIPnetcoladdCheck(dec->dec, dec->coladd, column, nonzrows, nonzvals, nnonzs) ); *success = SCIPnetcoladdRemainsNetwork(dec->coladd); if( *success ) { - SCIP_CALL(SCIPnetcoladdAdd(dec->dec, dec->coladd)); + SCIP_CALL( SCIPnetcoladdAdd(dec->dec, dec->coladd) ); } + return SCIP_OKAY; } @@ -11132,17 +11358,18 @@ SCIP_RETCODE SCIPnetmatdecTryAddRow( SCIP_Bool* success /**< Buffer to store whether the row was added */ ) { - if( dec->rowadd == NULL) + if( dec->rowadd == NULL ) { - SCIP_CALL(SCIPnetrowaddCreate(dec->dec->env, &dec->rowadd)); + SCIP_CALL( SCIPnetrowaddCreate(dec->dec->env, &dec->rowadd) ); } - SCIP_CALL(SCIPnetrowaddCheck(dec->dec, dec->rowadd, row, nonzcols, nonzvals, nnonzs)); + SCIP_CALL( SCIPnetrowaddCheck(dec->dec, dec->rowadd, row, nonzcols, nonzvals, nnonzs) ); *success = SCIPnetrowaddRemainsNetwork(dec->rowadd); if( *success ) { - SCIP_CALL(SCIPnetrowaddAdd(dec->dec, dec->rowadd)); + SCIP_CALL( SCIPnetrowaddAdd(dec->dec, dec->rowadd) ); } + return SCIP_OKAY; } @@ -11170,7 +11397,7 @@ void SCIPnetmatdecRemoveComponent( int ncols /**< The number of columns to delete */ ) { - netMatDecDataRemoveComponent(dec->dec,componentrows,nrows,componentcols,ncols); + netMatDecDataRemoveComponent(dec->dec, componentrows, nrows, componentcols, ncols); } SCIP_Bool SCIPnetmatdecIsMinimal( @@ -11189,10 +11416,10 @@ SCIP_Bool SCIPnetmatdecVerifyCycle( double* nonzvals, /**< Array with the column's nonzero values */ int nnonzs, /**< Number of nonzeros in the column */ int* pathrowstorage, /**< A buffer to hold the computed path's rows. Should have size equal or - * greater than the number of rows in the decomposition. */ + * greater than the number of rows in the decomposition. */ SCIP_Bool* pathsignstorage /**< A buffer to store the computed path's row signs. Should have size - * equal or greater than the number of rows in the decomposition. */ + * equal or greater than the number of rows in the decomposition. */ ) { - return netMatDecDataVerifyCycle(bufmem,dec->dec,column,nonzrowidx,nonzvals,nnonzs,pathrowstorage,pathsignstorage); + return netMatDecDataVerifyCycle(bufmem, dec->dec, column, nonzrowidx, nonzvals, nnonzs, pathrowstorage, pathsignstorage); } diff --git a/src/scip/pub_network.h b/src/scip/pub_network.h index 1497982bfe..5fabb8dfea 100644 --- a/src/scip/pub_network.h +++ b/src/scip/pub_network.h @@ -22,7 +22,7 @@ /* */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -/**@file network.h +/**@file pub_network.h * @ingroup PUBLICCOREAPI * @brief Methods for detecting network matrices * @author Rolf van der Hulst From 251356a5cb5edcf6072b2f5aa7d35d365f7cb4f8 Mon Sep 17 00:00:00 2001 From: Stefan Vigerske Date: Tue, 6 Aug 2024 07:17:40 +0200 Subject: [PATCH 32/63] replace maxValue/minValue by SCIP's MAX/MIN --- src/scip/network.c | 75 +++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 48 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 4088726053..ef53b699aa 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -3108,16 +3108,6 @@ SCIP_RETCODE mergeGivenMemberIntoParent( return SCIP_OKAY; } -/** Returns the maximum of two values. */ -static -int maxValue( - int a, - int b -) -{ - return ( a > b ) ? a : b; -} - /* ---------- START functions for column addition ------------------------------------------------------------------- */ /** Path arcs are all those arcs that correspond to nonzeros of the column to be added. */ @@ -3641,13 +3631,13 @@ SCIP_RETCODE constructReducedDecomposition( int newSize = largestMemberID(dec);//Is this sufficient? if( newSize > newCol->memReducedMembers ) { - int newArraySize = maxValue(2 * newCol->memReducedMembers, newSize); + int newArraySize = MAX(2 * newCol->memReducedMembers, newSize); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->reducedMembers, newCol->memReducedMembers, newArraySize) ); newCol->memReducedMembers = newArraySize; } if( newSize > newCol->memMemberInformation ) { - int updatedSize = maxValue(2 * newCol->memMemberInformation, newSize); + int updatedSize = MAX(2 * newCol->memMemberInformation, newSize); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->memberInformation, newCol->memMemberInformation, updatedSize) ); for( int i = newCol->memMemberInformation; i < updatedSize; ++i ) { @@ -3660,7 +3650,7 @@ SCIP_RETCODE constructReducedDecomposition( int numComponents = numConnectedComponents(dec); if( numComponents > newCol->memReducedComponents ) { - int updatedSize = maxValue(2 * newCol->memReducedComponents, numComponents); + int updatedSize = MAX(2 * newCol->memReducedComponents, numComponents); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->reducedComponents, newCol->memReducedComponents, updatedSize) ); newCol->memReducedComponents = updatedSize; } @@ -3668,7 +3658,7 @@ SCIP_RETCODE constructReducedDecomposition( int numMembers = getNumMembers(dec); if( newCol->memCreateReducedMembersCallStack < numMembers ) { - int updatedSize = maxValue(2 * newCol->memCreateReducedMembersCallStack, numMembers); + int updatedSize = MAX(2 * newCol->memCreateReducedMembersCallStack, numMembers); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->createReducedMembersCallStack, newCol->memCreateReducedMembersCallStack, updatedSize) ); newCol->memCreateReducedMembersCallStack = updatedSize; @@ -3718,7 +3708,7 @@ SCIP_RETCODE constructReducedDecomposition( if( newCol->memChildrenStorage < numTotalChildren ) { - int newMemSize = maxValue(newCol->memChildrenStorage * 2, numTotalChildren); + int newMemSize = MAX(newCol->memChildrenStorage * 2, numTotalChildren); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->childrenStorage, newCol->memChildrenStorage, newMemSize) ); newCol->memChildrenStorage = newMemSize; } @@ -3953,7 +3943,7 @@ SCIP_RETCODE computeLeafMembers( { if( newCol->numReducedMembers > newCol->memLeafMembers ) { - int newSize = maxValue(newCol->numReducedMembers, 2 * newCol->memLeafMembers); + int newSize = MAX(newCol->numReducedMembers, 2 * newCol->memLeafMembers); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->leafMembers, newCol->memLeafMembers, newSize) ); newCol->memLeafMembers = newSize; } @@ -6468,16 +6458,6 @@ SCIP_RETCODE SCIPnetcoladdAdd( /* ---------- START functions for row addition ---------------------------------------------------------------------- */ -/** Computes the minimum of two values */ -static -int minValue( - int a, - int b -) -{ - return a < b ? a : b; -} - /** Index type for the nonzeros of the new row, e.g. the columns whose tree path must be elongated with the new row */ typedef int cut_arc_id; #define INVALID_CUT_ARC (-1) @@ -6930,13 +6910,13 @@ SCIP_RETCODE constructRowReducedDecomposition( int newSize = largestMemberID(dec);//Is this sufficient? if( newSize > newRow->memReducedMembers ) { - int updatedSize = maxValue(2 * newRow->memReducedMembers, newSize); + int updatedSize = MAX(2 * newRow->memReducedMembers, newSize); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->reducedMembers, newRow->memReducedMembers, updatedSize) ); newRow->memReducedMembers = updatedSize; } if( newSize > newRow->memMemberInformation ) { - int updatedSize = maxValue(2 * newRow->memMemberInformation, newSize); + int updatedSize = MAX(2 * newRow->memMemberInformation, newSize); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->memberInformation, newRow->memMemberInformation, updatedSize) ); for( int i = newRow->memMemberInformation; i < updatedSize; ++i ) { @@ -6949,7 +6929,7 @@ SCIP_RETCODE constructRowReducedDecomposition( int numComponents = numConnectedComponents(dec); if( numComponents > newRow->memReducedComponents ) { - int updatedSize = maxValue(2 * newRow->memReducedComponents, numComponents); + int updatedSize = MAX(2 * newRow->memReducedComponents, numComponents); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->reducedComponents, newRow->memReducedComponents, updatedSize) ); newRow->memReducedComponents = updatedSize; } @@ -6957,7 +6937,7 @@ SCIP_RETCODE constructRowReducedDecomposition( int numMembers = getNumMembers(dec); if( newRow->memCreateReducedMembersCallstack < numMembers ) { - int updatedSize = maxValue(2 * newRow->memCreateReducedMembersCallstack, numMembers); + int updatedSize = MAX(2 * newRow->memCreateReducedMembersCallstack, numMembers); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->createReducedMembersCallstack, newRow->memCreateReducedMembersCallstack, updatedSize) ); newRow->memCreateReducedMembersCallstack = updatedSize; @@ -7007,7 +6987,7 @@ SCIP_RETCODE constructRowReducedDecomposition( if( newRow->memChildrenStorage < numTotalChildren ) { - int newMemSize = maxValue(newRow->memChildrenStorage * 2, numTotalChildren); + int newMemSize = MAX(newRow->memChildrenStorage * 2, numTotalChildren); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->childrenStorage, newRow->memChildrenStorage, newMemSize) ); newRow->memChildrenStorage = newMemSize; @@ -7117,7 +7097,7 @@ SCIP_RETCODE createReducedDecompositionCutArcs( spqr_arc maxArcID = largestArcID(dec); if( maxArcID > newRow->memIsArcCut ) { - int newSize = maxValue(maxArcID, 2 * newRow->memIsArcCut); + int newSize = MAX(maxArcID, 2 * newRow->memIsArcCut); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->isArcCut, newRow->memIsArcCut, newSize) ); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->isArcCutReversed, newRow->memIsArcCut, newSize) ); for( int i = newRow->memIsArcCut; i < newSize; ++i ) @@ -7138,7 +7118,7 @@ SCIP_RETCODE createReducedDecompositionCutArcs( int numNeededArcs = newRow->numDecompositionColumnArcs * 4; if( numNeededArcs > newRow->memCutArcs ) { - int newSize = maxValue(newRow->memCutArcs * 2, numNeededArcs); + int newSize = MAX(newRow->memCutArcs * 2, numNeededArcs); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->cutArcs, newRow->memCutArcs, newSize) ); newRow->memCutArcs = newSize; } @@ -7169,7 +7149,7 @@ SCIP_RETCODE determineLeafReducedMembers( { if( newRow->numDecompositionColumnArcs > newRow->memLeafMembers ) { - int updatedSize = maxValue(newRow->numDecompositionColumnArcs, 2 * newRow->memLeafMembers); + int updatedSize = MAX(newRow->numDecompositionColumnArcs, 2 * newRow->memLeafMembers); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->leafMembers, newRow->memLeafMembers, updatedSize) ); newRow->memLeafMembers = updatedSize; } @@ -7198,7 +7178,7 @@ SCIP_RETCODE allocateRigidSearchMemory( int maxNumNodes = 2 * dec->numArcs; if( maxNumNodes > newRow->memNodeColors ) { - int newSize = maxValue(2 * newRow->memNodeColors, maxNumNodes); + int newSize = MAX(2 * newRow->memNodeColors, maxNumNodes); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->nodeColors, newRow->memNodeColors, newSize) ); for( int i = newRow->memNodeColors; i < newSize; ++i ) { @@ -7209,26 +7189,26 @@ SCIP_RETCODE allocateRigidSearchMemory( if( totalNumNodes > newRow->memArticulationNodes ) { - int newSize = maxValue(2 * newRow->memArticulationNodes, totalNumNodes); + int newSize = MAX(2 * newRow->memArticulationNodes, totalNumNodes); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->articulationNodes, newRow->memArticulationNodes, newSize) ); newRow->memArticulationNodes = newSize; } if( totalNumNodes > newRow->memNodeSearchInfo ) { - int newSize = maxValue(2 * newRow->memNodeSearchInfo, totalNumNodes); + int newSize = MAX(2 * newRow->memNodeSearchInfo, totalNumNodes); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->articulationNodeSearchInfo, newRow->memNodeSearchInfo, newSize) ); newRow->memNodeSearchInfo = newSize; } if( totalNumNodes > newRow->memCrossingPathCount ) { - int newSize = maxValue(2 * newRow->memCrossingPathCount, totalNumNodes); + int newSize = MAX(2 * newRow->memCrossingPathCount, totalNumNodes); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->crossingPathCount, newRow->memCrossingPathCount, newSize) ); newRow->memCrossingPathCount = newSize; } if( totalNumNodes > newRow->memTemporaryColorArray ) { - int newSize = maxValue(2 * newRow->memTemporaryColorArray, totalNumNodes); + int newSize = MAX(2 * newRow->memTemporaryColorArray, totalNumNodes); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->temporaryColorArray, newRow->memTemporaryColorArray, newSize) ); newRow->memTemporaryColorArray = newSize; @@ -7240,19 +7220,19 @@ SCIP_RETCODE allocateRigidSearchMemory( //TODO: only update the stack sizes of the following when needed? The preallocation may not be worth it. if( largestID > newRow->memIntersectionDFSData ) { - int newSize = maxValue(2 * newRow->memIntersectionDFSData, largestID); + int newSize = MAX(2 * newRow->memIntersectionDFSData, largestID); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->intersectionDFSData, newRow->memIntersectionDFSData, newSize) ); newRow->memIntersectionDFSData = newSize; } if( largestID > newRow->memColorDFSData ) { - int newSize = maxValue(2 * newRow->memColorDFSData, largestID); + int newSize = MAX(2 * newRow->memColorDFSData, largestID); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->colorDFSData, newRow->memColorDFSData, newSize) ); newRow->memColorDFSData = newSize; } if( largestID > newRow->memArtDFSData ) { - int newSize = maxValue(2 * newRow->memArtDFSData, largestID); + int newSize = MAX(2 * newRow->memArtDFSData, largestID); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->artDFSData, newRow->memArtDFSData, newSize) ); newRow->memArtDFSData = newSize; } @@ -7264,7 +7244,7 @@ SCIP_RETCODE allocateRigidSearchMemory( if( largestID > newRow->memIntersectionPathDepth ) { - int newSize = maxValue(2 * newRow->memIntersectionPathDepth, largestID); + int newSize = MAX(2 * newRow->memIntersectionPathDepth, largestID); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->intersectionPathDepth, newRow->memIntersectionPathDepth, newSize) ); for( int i = newRow->memIntersectionPathDepth; i < newSize; ++i ) @@ -7279,7 +7259,7 @@ SCIP_RETCODE allocateRigidSearchMemory( } if( largestID > newRow->memIntersectionPathParent ) { - int newSize = maxValue(2 * newRow->memIntersectionPathParent, largestID); + int newSize = MAX(2 * newRow->memIntersectionPathParent, largestID); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->intersectionPathParent, newRow->memIntersectionPathParent, newSize) ); for( int i = newRow->memIntersectionPathParent; i < newSize; ++i ) @@ -7746,7 +7726,7 @@ void articulationPoints( } else { - nodeInfo[node].low = minValue(nodeInfo[node].low, nodeInfo[otherNode].discoveryTime); + nodeInfo[node].low = MIN(nodeInfo[node].low, nodeInfo[otherNode].discoveryTime); } } } @@ -7760,8 +7740,7 @@ void articulationPoints( spqr_node current_node = callStack[depth].node; spqr_node other_node = callStack[depth + 1].node; - nodeInfo[current_node].low = minValue(nodeInfo[current_node].low, - nodeInfo[other_node].low); + nodeInfo[current_node].low = MIN(nodeInfo[current_node].low, nodeInfo[other_node].low); if( depth != 0 && !callStack[depth].isAP && nodeInfo[current_node].discoveryTime <= nodeInfo[other_node].low ) @@ -8493,7 +8472,7 @@ SCIP_RETCODE allocateTreeSearchMemory( int necessarySpace = newRow->numReducedMembers; if( necessarySpace > newRow->memMergeTreeCallData ) { - int updatedSize = maxValue(2 * newRow->memMergeTreeCallData, necessarySpace); + int updatedSize = MAX(2 * newRow->memMergeTreeCallData, necessarySpace); SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->mergeTreeCallData, newRow->memMergeTreeCallData, updatedSize) ); newRow->memMergeTreeCallData = updatedSize; From faad18cb41691e063022551061da656644f9b789 Mon Sep 17 00:00:00 2001 From: Stefan Vigerske Date: Tue, 6 Aug 2024 07:28:24 +0200 Subject: [PATCH 33/63] simplify init of NewCol/RowInformation --- src/scip/network.c | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index ef53b699aa..674240deaf 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -5174,18 +5174,7 @@ typedef struct SCIP_Bool reversed; /**< Is the new column reversed? */ } NewColInformation; -/** Initializes an empty newcolinformation struct. */ -static -NewColInformation emptyNewColInformation(void) -{ - NewColInformation information; - information.member = SPQR_INVALID_MEMBER; - information.head = SPQR_INVALID_NODE; - information.tail = SPQR_INVALID_NODE; - information.reversed = FALSE; - information.representative = SPQR_INVALID_ARC; - return information; -} +#define NEWCOLINFORMATION_EMPTY { SPQR_INVALID_MEMBER, SPQR_INVALID_NODE, SPQR_INVALID_NODE, SPQR_INVALID_ARC, FALSE } /** Set the head node of the new column edge to be added. */ static @@ -6364,7 +6353,7 @@ SCIP_RETCODE SCIPnetcoladdAdd( } else if( newCol->numReducedComponents == 1 ) { - NewColInformation information = emptyNewColInformation(); + NewColInformation information = NEWCOLINFORMATION_EMPTY; SCIP_CALL( transformComponent(dec, newCol, &newCol->reducedComponents[0], &information) ); assert(memberIsRepresentative(dec, information.member)); if( newCol->numNewRowArcs == 0 ) @@ -6418,7 +6407,7 @@ SCIP_RETCODE SCIPnetcoladdAdd( newCol->numNewRowArcs, newCol->newColIndex, &newSeries) ); for( int i = 0; i < newCol->numReducedComponents; ++i ) { - NewColInformation information = emptyNewColInformation(); + NewColInformation information = NEWCOLINFORMATION_EMPTY; SCIP_CALL( transformComponent(dec, newCol, &newCol->reducedComponents[i], &information) ); if( getMemberType(dec, information.member) == SPQR_MEMBERTYPE_LOOP ) { @@ -6704,18 +6693,7 @@ typedef struct SCIP_Bool reversed; /**< Orientation of the arc w.r.t. the representative */ } NewRowInformation; -/** Initializes an empty NewRowInformation struct */ -static -NewRowInformation emptyNewRowInformation(void) -{ - NewRowInformation information; - information.member = SPQR_INVALID_MEMBER; - information.head = SPQR_INVALID_NODE; - information.tail = SPQR_INVALID_NODE; - information.representative = SPQR_INVALID_ARC; - information.reversed = FALSE; - return information; -} +#define NEWROWINFORMATION_EMPTY { SPQR_INVALID_MEMBER, SPQR_INVALID_NODE, SPQR_INVALID_NODE, SPQR_INVALID_ARC, FALSE } /** Saves the information of the current row and partitions it based on whether or not the given columns are * already part of the decomposition. @@ -11157,7 +11135,7 @@ SCIP_RETCODE SCIPnetrowaddAdd( } else if( rowadd->numReducedComponents == 1 ) { - NewRowInformation information = emptyNewRowInformation(); + NewRowInformation information = NEWROWINFORMATION_EMPTY; SCIP_CALL( transformComponentRowAddition(dec, rowadd, &rowadd->reducedComponents[0], &information) ); if( rowadd->numColumnArcs == 0 ) @@ -11212,7 +11190,7 @@ SCIP_RETCODE SCIPnetrowaddAdd( rowadd->newRowIndex, &new_row_parallel) ); for( int i = 0; i < rowadd->numReducedComponents; ++i ) { - NewRowInformation information = emptyNewRowInformation(); + NewRowInformation information = NEWROWINFORMATION_EMPTY; SCIP_CALL( transformComponentRowAddition(dec, rowadd, &rowadd->reducedComponents[i], &information) ); if( getMemberType(dec, information.member) == SPQR_MEMBERTYPE_LOOP ) From d47046450542f6e4e4bf710d1cc16d8c2d85005e Mon Sep 17 00:00:00 2001 From: Stefan Vigerske Date: Tue, 6 Aug 2024 07:35:32 +0200 Subject: [PATCH 34/63] remove SCIP from name of local functions --- src/scip/network.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 674240deaf..4cc7fcd7f6 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -3398,7 +3398,7 @@ void cleanupPreviousIteration( /** Create a new network column addition datastructure. */ static -SCIP_RETCODE SCIPnetcoladdCreate( +SCIP_RETCODE netcoladdCreate( BMS_BLKMEM* blkmem, /**< Block memory */ SCIP_NETCOLADD** pcoladd /**< Out-pointer for the created network column addition datastructure */ ) @@ -3462,7 +3462,7 @@ SCIP_RETCODE SCIPnetcoladdCreate( /** Free a network column addition datastructure */ static -void SCIPnetcoladdFree( +void netcoladdFree( BMS_BLKMEM* blkmem, /**< Block memory */ SCIP_NETCOLADD** pcoladd /**< Pointer to the network column addition datastructure to be freed */ ) @@ -5115,7 +5115,7 @@ void determineComponentTypes( * See header for more info. */ static -SCIP_RETCODE SCIPnetcoladdCheck( +SCIP_RETCODE netcoladdCheck( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ SCIP_NETCOLADD* coladd, /**< The network matrix column addition data structure */ int column, /**< The column to add */ @@ -6324,7 +6324,7 @@ SCIP_RETCODE transformComponent( /** Check if the submatrix stored remains a network matrix with the new column update. */ static -SCIP_Bool SCIPnetcoladdRemainsNetwork( +SCIP_Bool netcoladdRemainsNetwork( const SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */ ) { @@ -6336,14 +6336,14 @@ SCIP_Bool SCIPnetcoladdRemainsNetwork( * Only use this function after SCIPnetcoladdCheck() has been called. */ static -SCIP_RETCODE SCIPnetcoladdAdd( +SCIP_RETCODE netcoladdAdd( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */ ) { assert(dec); assert(newCol); - assert(SCIPnetcoladdRemainsNetwork(newCol)); + assert(netcoladdRemainsNetwork(newCol)); if( newCol->numReducedComponents == 0 ) { @@ -10935,7 +10935,7 @@ SCIP_RETCODE transformComponentRowAddition( /** Create the network row addition data structure. */ static -SCIP_RETCODE SCIPnetrowaddCreate( +SCIP_RETCODE netrowaddCreate( BMS_BLKMEM* blkmem, /**< Block memory */ SCIP_NETROWADD** prowadd /**< Pointer to store the new row addition struct at */ ) @@ -11029,7 +11029,7 @@ SCIP_RETCODE SCIPnetrowaddCreate( /** Frees the network row addition data structure. */ static -void SCIPnetrowaddFree( +void netrowaddFree( BMS_BLKMEM* blkmem, /**< Block memory */ SCIP_NETROWADD** prowadd /**< Pointer to row addition struct to be freed */ ) @@ -11067,7 +11067,7 @@ void SCIPnetrowaddFree( /** Checks if the given row can be added to the network decomposition. */ static -SCIP_RETCODE SCIPnetrowaddCheck( +SCIP_RETCODE netrowaddCheck( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ SCIP_NETROWADD* rowadd, /**< The network matrix row addition data structure */ int row, /**< The row to be checked */ @@ -11121,7 +11121,7 @@ SCIP_RETCODE SCIPnetrowaddCheck( /** Adds the last checked row to the network decomposition. */ static -SCIP_RETCODE SCIPnetrowaddAdd( +SCIP_RETCODE netrowaddAdd( SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ SCIP_NETROWADD* rowadd /**< The network matrix row addition data structure */ ) @@ -11230,7 +11230,7 @@ SCIP_RETCODE SCIPnetrowaddAdd( /** Returns whether the last checked row can be added to the network decomposition. */ static -SCIP_Bool SCIPnetrowaddRemainsNetwork( +SCIP_Bool netrowaddRemainsNetwork( const SCIP_NETROWADD* rowadd /**< The network matrix row addition data structure */ ) { @@ -11272,11 +11272,11 @@ void SCIPnetmatdecFree( if( dec->coladd != NULL ) { - SCIPnetcoladdFree(blkmem, &dec->coladd); + netcoladdFree(blkmem, &dec->coladd); } if( dec->rowadd != NULL ) { - SCIPnetrowaddFree(blkmem, &dec->rowadd); + netrowaddFree(blkmem, &dec->rowadd); } netMatDecDataFree(&dec->dec); BMSfreeBlockMemory(blkmem,pdec); @@ -11293,14 +11293,14 @@ SCIP_RETCODE SCIPnetmatdecTryAddCol( { if( dec->coladd == NULL ) { - SCIP_CALL( SCIPnetcoladdCreate(dec->dec->env, &dec->coladd) ); + SCIP_CALL( netcoladdCreate(dec->dec->env, &dec->coladd) ); } - SCIP_CALL( SCIPnetcoladdCheck(dec->dec, dec->coladd, column, nonzrows, nonzvals, nnonzs) ); - *success = SCIPnetcoladdRemainsNetwork(dec->coladd); + SCIP_CALL( netcoladdCheck(dec->dec, dec->coladd, column, nonzrows, nonzvals, nnonzs) ); + *success = netcoladdRemainsNetwork(dec->coladd); if( *success ) { - SCIP_CALL( SCIPnetcoladdAdd(dec->dec, dec->coladd) ); + SCIP_CALL( netcoladdAdd(dec->dec, dec->coladd) ); } return SCIP_OKAY; @@ -11317,14 +11317,14 @@ SCIP_RETCODE SCIPnetmatdecTryAddRow( { if( dec->rowadd == NULL ) { - SCIP_CALL( SCIPnetrowaddCreate(dec->dec->env, &dec->rowadd) ); + SCIP_CALL( netrowaddCreate(dec->dec->env, &dec->rowadd) ); } - SCIP_CALL( SCIPnetrowaddCheck(dec->dec, dec->rowadd, row, nonzcols, nonzvals, nnonzs) ); - *success = SCIPnetrowaddRemainsNetwork(dec->rowadd); + SCIP_CALL( netrowaddCheck(dec->dec, dec->rowadd, row, nonzcols, nonzvals, nnonzs) ); + *success = netrowaddRemainsNetwork(dec->rowadd); if( *success ) { - SCIP_CALL( SCIPnetrowaddAdd(dec->dec, dec->rowadd) ); + SCIP_CALL( netrowaddAdd(dec->dec, dec->rowadd) ); } return SCIP_OKAY; From 87725fffb5e888006c76e551dc4d0961f5d797db Mon Sep 17 00:00:00 2001 From: Stefan Vigerske Date: Tue, 6 Aug 2024 07:39:56 +0200 Subject: [PATCH 35/63] move pub_network.h in CMakeLists.txt --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7674fc7758..0ff22bbb5f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -657,7 +657,6 @@ set(scipheaders scip/message_default.h scip/message.h scip/misc.h - scip/pub_network.h scip/nlhdlr_bilinear.h scip/nlhdlr_convex.h scip/nlhdlr_default.h @@ -748,6 +747,7 @@ set(scipheaders scip/pub_misc_rowprep.h scip/pub_misc_select.h scip/pub_misc_sort.h + scip/pub_network.h scip/pub_nlhdlr.h scip/pub_nlp.h scip/pub_nlpi.h From 84e4c325d350e600603a95320cc9321c43b1c1ce Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 6 Aug 2024 14:15:37 +0200 Subject: [PATCH 36/63] Create initial stub for implied integer detection --- Makefile | 1 + src/CMakeLists.txt | 2 + src/scip/presol_implint.c | 211 ++++++++++++++++++++++++++++++++++++++ src/scip/presol_implint.h | 56 ++++++++++ src/scip/scipdefplugins.c | 1 + src/scip/scipdefplugins.h | 1 + 6 files changed, 272 insertions(+) create mode 100644 src/scip/presol_implint.c create mode 100644 src/scip/presol_implint.h diff --git a/Makefile b/Makefile index 3de2155e5d..2584671225 100644 --- a/Makefile +++ b/Makefile @@ -717,6 +717,7 @@ SCIPPLUGINLIBOBJ= scip/benders_default.o \ scip/presol_dualinfer.o\ scip/presol_gateextraction.o \ scip/presol_implics.o \ + scip/presol_implint.o \ scip/presol_inttobinary.o \ scip/presol_qpkktref.o \ scip/presol_redvub.o \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0ff22bbb5f..b3533bd5e2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -195,6 +195,7 @@ set(scipsources scip/presol_dualinfer.c scip/presol_gateextraction.c scip/presol_implics.c + scip/presol_implint.c scip/presol_inttobinary.c scip/presol_qpkktref.c scip/presol_redvub.c @@ -692,6 +693,7 @@ set(scipheaders scip/presol_gateextraction.h scip/presol.h scip/presol_implics.h + scip/presol_implint.h scip/presol_inttobinary.h scip/presol_qpkktref.h scip/presol_redvub.h diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c new file mode 100644 index 0000000000..dff36bdc2d --- /dev/null +++ b/src/scip/presol_implint.c @@ -0,0 +1,211 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* This file is part of the program and library */ +/* SCIP --- Solving Constraint Integer Programs */ +/* */ +/* Copyright (c) 2002-2024 Zuse Institute Berlin (ZIB) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/* You should have received a copy of the Apache-2.0 license */ +/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/**@file presol_implint.c + * @ingroup DEFPLUGINS_PRESOL + * @brief Presolver that detects implied integer variables + * @author Rolf van der Hulst + */ + +/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/ + +#include + +#include "scip/presol_implint.h" +#include "scip/scip_presol.h" + +#include "scip/pub_message.h" + +#define PRESOL_NAME "implint" +#define PRESOL_DESC "detects implied integer variables" +#define PRESOL_PRIORITY 0 /**< priority of the presolver (>= 0: before, < 0: after constraint handlers); combined with propagators */ +#define PRESOL_MAXROUNDS -1 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */ +#define PRESOL_TIMING SCIP_PRESOLTIMING_MEDIUM /* timing of the presolver (fast, medium, or exhaustive) */ + + +/* + * Data structures + */ + +/* TODO: fill in the necessary presolver data */ + +/** presolver data */ +struct SCIP_PresolData +{ +}; + + +/* + * Local methods + */ + +/* put your local methods here, and declare them static */ + + +/* + * Callback methods of presolver + */ + +/* TODO: Implement all necessary presolver methods. The methods with an #if 0 ... #else #define ... are optional */ + + +/** copy method for constraint handler plugins (called when SCIP copies plugins) */ +#if 0 +static +SCIP_DECL_PRESOLCOPY(presolCopyImplint) +{ /*lint --e{715}*/ + SCIPerrorMessage("method of implint presolver not implemented yet\n"); + SCIPABORT(); /*lint --e{527}*/ + + return SCIP_OKAY; +} +#else +#define presolCopyImplint NULL +#endif + + +/** destructor of presolver to free user data (called when SCIP is exiting) */ +#if 0 +static +SCIP_DECL_PRESOLFREE(presolFreeImplint) +{ /*lint --e{715}*/ + SCIPerrorMessage("method of implint presolver not implemented yet\n"); + SCIPABORT(); /*lint --e{527}*/ + + return SCIP_OKAY; +} +#else +#define presolFreeImplint NULL +#endif + + +/** initialization method of presolver (called after problem was transformed) */ +#if 0 +static +SCIP_DECL_PRESOLINIT(presolInitImplint) +{ /*lint --e{715}*/ + SCIPerrorMessage("method of implint presolver not implemented yet\n"); + SCIPABORT(); /*lint --e{527}*/ + + return SCIP_OKAY; +} +#else +#define presolInitImplint NULL +#endif + + +/** deinitialization method of presolver (called before transformed problem is freed) */ +#if 0 +static +SCIP_DECL_PRESOLEXIT(presolExitImplint) +{ /*lint --e{715}*/ + SCIPerrorMessage("method of implint presolver not implemented yet\n"); + SCIPABORT(); /*lint --e{527}*/ + + return SCIP_OKAY; +} +#else +#define presolExitImplint NULL +#endif + + +/** presolving initialization method of presolver (called when presolving is about to begin) */ +#if 0 +static +SCIP_DECL_PRESOLINITPRE(presolInitpreImplint) +{ /*lint --e{715}*/ + SCIPerrorMessage("method of implint presolver not implemented yet\n"); + SCIPABORT(); /*lint --e{527}*/ + + return SCIP_OKAY; +} +#else +#define presolInitpreImplint NULL +#endif + + +/** presolving deinitialization method of presolver (called after presolving has been finished) */ +#if 0 +static +SCIP_DECL_PRESOLEXITPRE(presolExitpreImplint) +{ /*lint --e{715}*/ + SCIPerrorMessage("method of implint presolver not implemented yet\n"); + SCIPABORT(); /*lint --e{527}*/ + + return SCIP_OKAY; +} +#else +#define presolExitpreImplint NULL +#endif + + +/** execution method of presolver */ + +static +SCIP_DECL_PRESOLEXEC(presolExecImplint) +{ + + return SCIP_OKAY; +} + + +/* + * presolver specific interface methods + */ + +/** creates the implint presolver and includes it in SCIP */ +SCIP_RETCODE SCIPincludePresolImplint( + SCIP* scip /**< SCIP data structure */ +) +{ + SCIP_PRESOLDATA* presoldata; + SCIP_PRESOL* presol; + + /* create implint presolver data */ + presoldata = NULL; + /* TODO: (optional) create presolver specific data here */ + + presol = NULL; + + /* use SCIPincludePresolBasic() plus setter functions if you want to set callbacks one-by-one and your code should + * compile independent of new callbacks being added in future SCIP versions + */ + SCIP_CALL( SCIPincludePresolBasic(scip, &presol, PRESOL_NAME, PRESOL_DESC, PRESOL_PRIORITY, PRESOL_MAXROUNDS, + PRESOL_TIMING, presolExecImplint, presoldata) ); + + assert(presol != NULL); + + /* set non fundamental callbacks via setter functions */ + SCIP_CALL( SCIPsetPresolCopy(scip, presol, presolCopyImplint) ); + SCIP_CALL( SCIPsetPresolFree(scip, presol, presolFreeImplint) ); + SCIP_CALL( SCIPsetPresolInit(scip, presol, presolInitImplint) ); + SCIP_CALL( SCIPsetPresolExit(scip, presol, presolExitImplint) ); + SCIP_CALL( SCIPsetPresolInitpre(scip, presol, presolInitpreImplint) ); + SCIP_CALL( SCIPsetPresolExitpre(scip, presol, presolExitpreImplint) ); + + /* add implint presolver parameters */ + /* TODO: (optional) add presolver specific parameters with SCIPaddTypeParam() here */ + + return SCIP_OKAY; +} diff --git a/src/scip/presol_implint.h b/src/scip/presol_implint.h new file mode 100644 index 0000000000..1093e38d05 --- /dev/null +++ b/src/scip/presol_implint.h @@ -0,0 +1,56 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* This file is part of the program and library */ +/* SCIP --- Solving Constraint Integer Programs */ +/* */ +/* Copyright (c) 2002-2024 Zuse Institute Berlin (ZIB) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/* You should have received a copy of the Apache-2.0 license */ +/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/**@file presol_implint.h + * @ingroup PRESOLVERS + * @brief Presolver that detects implied integer variables + * @author Rolf van der Hulst + */ + +#ifndef __SCIP_PRESOL_IMPLINT_H__ +#define __SCIP_PRESOL_IMPLINT_H__ + +#include "scip/def.h" +#include "scip/type_retcode.h" +#include "scip/type_scip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** creates the implied integer presolver and includes it in SCIP + * + * @ingroup PresolverIncludes + */ +SCIP_EXPORT + SCIP_RETCODE SCIPincludePresolImplint( + SCIP* scip /**< SCIP data structure */ +); + + +#ifdef __cplusplus +} +#endif + +#endif //__SCIP_PRESOL_IMPLINT_H__ diff --git a/src/scip/scipdefplugins.c b/src/scip/scipdefplugins.c index 3b54c64355..05c5161894 100644 --- a/src/scip/scipdefplugins.c +++ b/src/scip/scipdefplugins.c @@ -109,6 +109,7 @@ SCIP_RETCODE SCIPincludeDefaultPlugins( SCIP_CALL( SCIPincludePresolDualinfer(scip) ); SCIP_CALL( SCIPincludePresolGateextraction(scip) ); SCIP_CALL( SCIPincludePresolImplics(scip) ); + SCIP_CALL( SCIPincludePresolImplint(scip) ); SCIP_CALL( SCIPincludePresolInttobinary(scip) ); #ifdef SCIP_WITH_PAPILO SCIP_CALL( SCIPincludePresolMILP(scip) ); diff --git a/src/scip/scipdefplugins.h b/src/scip/scipdefplugins.h index f22eb3e355..734ce44c72 100644 --- a/src/scip/scipdefplugins.h +++ b/src/scip/scipdefplugins.h @@ -182,6 +182,7 @@ #include "scip/presol_dualinfer.h" #include "scip/presol_gateextraction.h" #include "scip/presol_implics.h" +#include "scip/presol_implint.h" #include "scip/presol_inttobinary.h" #include "scip/presol_milp.h" #include "scip/presol_redvub.h" From 27f66c05b1dc5abac1977e06f3e5f97669389e5d Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Fri, 9 Aug 2024 07:35:39 +0200 Subject: [PATCH 37/63] Add method to construct graph from network matrix --- src/scip/network.c | 324 +++++++++++++++++++++++++++++++++++- src/scip/pub_network.h | 15 ++ tests/src/network/network.c | 56 +++++++ 3 files changed, 390 insertions(+), 5 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 4cc7fcd7f6..6d9ddbc6df 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -126,6 +126,9 @@ */ #include + +#include "scip/misc.h" +#include "scip/pub_misc.h" #include "scip/pub_network.h" #include "scip/scip.h" #include "blockmemshell/memory.h" @@ -2875,7 +2878,6 @@ void netMatDecDataRemoveComponent( } #ifdef SCIP_DEBUG - /** Converts the members type to an associated character */ //Debugging functions to print the decomposition static char typeToChar( @@ -2901,7 +2903,7 @@ char typeToChar( static void arcToDot( FILE* stream, /**< The stream to write the arc to */ - const SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ spqr_arc arc, /**< The arc to write */ unsigned long dot_head, /**< The head id of the arc */ unsigned long dot_tail, /**< The tail id of the arc */ @@ -2929,7 +2931,7 @@ void arcToDot( fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_head); fprintf(stream, " %c_p_%d [style=dashed];\n", type, member); } - else if( arcIsMarker(dec, arc) ) + else if( arcIsChildMarker(dec, arc) ) { spqr_member child = findArcChildMemberNoCompression(dec, arc); char childType = typeToChar(getMemberType(dec, child)); @@ -2970,7 +2972,7 @@ void arcToDot( static void decompositionToDot( FILE* stream, /**< The stream to write to */ - const SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ + const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ SCIP_Bool useElementNames /**< If TRUE, prints the corresponding row/column index. If FALSE, * prints the spqr_arc id instead. */ ) @@ -3108,6 +3110,309 @@ SCIP_RETCODE mergeGivenMemberIntoParent( return SCIP_OKAY; } +static +SCIP_RETCODE netMatDecDataCreateDiGraph( + SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */ + BMS_BLKMEM * blkmem, /**< The block memory to use for the created digraph */ + SCIP_DIGRAPH** pdigraph, /**< Pointer to the pointer to store the created digraph */ + SCIP_Bool createrowarcs /**< Should the row arcs be added to the created digraph? */ +) +{ + + /* Compute the number of rows m in the network matrix decomposition. The underlying graph has m+1 vertices. */ + int nrows = 0; + for( int i = 0; i < dec->memRows; ++i ) + { + if( netMatDecDataContainsRow(dec, i)) + { + ++nrows; + } + } + + int nvertices = nrows + 1; + SCIP_CALL( SCIPdigraphCreate(pdigraph, blkmem, nvertices) ); + + /* Map decomposition nodes to graph nodes. The first node of each component is mapped to node 0 */ + int largestNode = largestNodeID(dec); + int* dectographnode; + SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem,&dectographnode, largestNode) ); + for( int i = 0; i < largestNode; ++i ) + { + dectographnode[i] = -1; + } + + + SCIP_DIGRAPH* digraph = *pdigraph; + /* Recursively explore the decomposition, adding the edges of each component and identifying them as needed */ + + typedef struct{ + spqr_member member; + spqr_arc arc; + int graphfirstarchead; + int graphfirstarctail; + } CallData; + spqr_member largestmember = largestMemberID(dec); + CallData* calldata; + int ncalldata = 0; + SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &calldata, largestmember) ); + + SCIP_Bool* rowprocessed; + SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &rowprocessed, dec->memRows) ); + for( int i = 0; i < dec->memRows; ++i ) + { + rowprocessed[i] = FALSE; + } + + /* This outer loop loops over all components; this is a bit ugly unfortunately */ + int nextnodeindex = 1; /* 0 is assigned to the first arc of the root members' head */ + for( int i = 0; i memRows ; ++i ) + { + spqr_arc rowarc = dec->rowArcs[i]; + if( SPQRarcIsInvalid(rowarc)) + { + continue; + } + assert(netMatDecDataContainsRow(dec, i)); + if( rowprocessed[i] ) + { + continue; + } + + /* Find the root member of the SPQR tree */ + spqr_member arcmember = findArcMember(dec,rowarc); + spqr_member rootmember = arcmember; + do + { + spqr_member parent = findMemberParent(dec, rootmember); + if( SPQRmemberIsInvalid(parent)) + { + break; + } + rootmember = parent; + } + while( TRUE ); + + calldata[0].member = rootmember; + ++ncalldata; + /* We identify all the SPQR trees at node 0 */ + { + calldata[0].arc = getFirstMemberArc(dec,rootmember); + calldata[0].graphfirstarchead = 0; + calldata[0].graphfirstarctail = nextnodeindex; + ++nextnodeindex; + } + + while( ncalldata != 0 ) + { + /* Pop the next member to process of the call stack */ + CallData explore = calldata[ncalldata-1]; + --ncalldata; + + switch( getMemberType(dec, explore.member) ) + { + case SPQR_MEMBERTYPE_RIGID: + { + /* First, we set the initial arc's nodes */ + { + assert(dectographnode[findEffectiveArcHeadNoCompression(dec, explore.arc)] == -1); + assert(dectographnode[findEffectiveArcTailNoCompression(dec, explore.arc)] == -1); + spqr_node exploreHead = findArcHead(dec, explore.arc); + spqr_node exploreTail = findArcTail(dec, explore.arc); + if( findArcSign(dec, explore.arc).reversed ) + { + SCIPswapInts(&exploreTail, &exploreTail); + } + dectographnode[exploreHead] = explore.graphfirstarchead; + dectographnode[exploreTail] = explore.graphfirstarctail; + } + /* Add all the members arcs to the graph */ + spqr_arc firstArc = getFirstMemberArc(dec, explore.member); + spqr_arc arc = firstArc; + do + { + spqr_node head = findArcHead(dec, arc); + spqr_node tail = findArcTail(dec, arc); + if( findArcSign(dec, arc).reversed ) + { + SCIPswapInts(&head, &tail); + } + if( dectographnode[head] == -1 ) + { + dectographnode[head] = nextnodeindex; + ++nextnodeindex; + } + if( dectographnode[tail] == -1 ) + { + dectographnode[tail] = nextnodeindex; + ++nextnodeindex; + } + /* If an arc is a marker to a child node, we add it to the call stack */ + if( arcIsChildMarker(dec, arc)) + { + spqr_member child = findArcChildMember(dec, arc); + spqr_arc toparent = markerToParent(dec, child); + /* Identify head with head and tail with tail */ + /* Push member onto the processing stack (recursive call) */ + + calldata[ncalldata].member = child; + calldata[ncalldata].arc = toparent; + calldata[ncalldata].graphfirstarchead = dectographnode[head]; + calldata[ncalldata].graphfirstarctail = dectographnode[tail]; + + ++ncalldata; + } + else if( arc != markerToParent(dec, explore.member)) + { + //We only realize non-virtual arcs + if( createrowarcs || !arcIsTree(dec, arc)) + { + SCIP_CALL( SCIPdigraphAddArc(digraph, dectographnode[tail], dectographnode[head], NULL) ); + } + } + spqr_element element = arcGetElement(dec, arc); + if( SPQRelementIsRow(element) && element != MARKER_ROW_ELEMENT) + { + spqr_row row = SPQRelementToRow(element); + rowprocessed[row] = TRUE; + } + arc = getNextMemberArc(dec, arc); + } + while( arc != firstArc ); + break; + } + case SPQR_MEMBERTYPE_LOOP: + case SPQR_MEMBERTYPE_PARALLEL: + { + spqr_arc firstArc = getFirstMemberArc(dec, explore.member); + spqr_arc arc = firstArc; + do + { + /* If an edge is a marker to a child node, we add it to the call stack */ + if( arcIsChildMarker(dec, arc)) + { + spqr_member child = findArcChildMember(dec, arc); + spqr_arc toparent = markerToParent(dec, child); + SCIP_Bool directionsmatch = + arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc); + /* Identify head with head and tail with tail */ + /* Push member onto the processing stack (recursive call) */ + calldata[ncalldata].member = child; + calldata[ncalldata].arc = toparent; + calldata[ncalldata].graphfirstarchead = directionsmatch ? explore.graphfirstarchead + : explore.graphfirstarctail; + calldata[ncalldata].graphfirstarctail = directionsmatch ? explore.graphfirstarctail + : explore.graphfirstarchead; + + ++ncalldata; + } + else if( arc != markerToParent(dec, explore.member) ) + { + /* We only realize non-virtual arcs */ + if( createrowarcs || !arcIsTree(dec, arc) ) + { + if( arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc)) + { + SCIP_CALL( + SCIPdigraphAddArc(digraph, explore.graphfirstarctail, explore.graphfirstarchead, NULL) ); + } + else + { + SCIP_CALL( + SCIPdigraphAddArc(digraph, explore.graphfirstarchead, explore.graphfirstarctail, NULL) ); + } + } + } + spqr_element element = arcGetElement(dec, arc); + if( SPQRelementIsRow(element) && element != MARKER_ROW_ELEMENT) + { + spqr_row row = SPQRelementToRow(element); + rowprocessed[row] = TRUE; + } + arc = getNextMemberArc(dec, arc); + } + while( arc != firstArc ); + break; + } + case SPQR_MEMBERTYPE_SERIES: + { + int count = getNumMemberArcs(dec, explore.member); + spqr_arc arc = explore.arc; + int firstnode = explore.graphfirstarchead; + int secondnode = explore.graphfirstarctail; + for( int j = 0; j < count; ++j ) + { + if( arcIsChildMarker(dec, arc)) + { + spqr_member child = findArcChildMember(dec, arc); + spqr_arc toparent = markerToParent(dec, child); + SCIP_Bool directionsmatch = + arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc); + /* Identify head with head and tail with tail */ + /* Push member onto the processing stack (recursive call) */ + calldata[ncalldata].member = child; + calldata[ncalldata].arc = toparent; + calldata[ncalldata].graphfirstarchead = directionsmatch ? firstnode : secondnode; + calldata[ncalldata].graphfirstarctail = directionsmatch ? secondnode : firstnode; + + ++ncalldata; + } + else if( arc != markerToParent(dec, explore.member)) + { + /* We only realize non-virtual arcs */ + if( createrowarcs || !arcIsTree(dec, arc)) + { + if( arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc) ) + { + SCIP_CALL( SCIPdigraphAddArc(digraph, secondnode, firstnode, NULL) ); + } + else + { + SCIP_CALL( SCIPdigraphAddArc(digraph, firstnode, secondnode, NULL) ); + } + } + } + + spqr_element element = arcGetElement(dec, arc); + if( SPQRelementIsRow(element) && element != MARKER_ROW_ELEMENT ) + { + spqr_row row = SPQRelementToRow(element); + rowprocessed[row] = TRUE; + } + + arc = getNextMemberArc(dec, arc); + secondnode = firstnode; + if( j >= count - 2 ) + { + firstnode = explore.graphfirstarctail; + } + else + { + firstnode = nextnodeindex; + ++nextnodeindex; + } + } + assert(arc == explore.arc); /* Check that we added all arcs */ + break; + } + case SPQR_MEMBERTYPE_UNASSIGNED:{ + SCIPerrorMessage("Can not create graph for unassigned member type, exiting. \n"); + SCIPABORT(); + return SCIP_ERROR; + } + } + + } + } + + /* We cannot have more vertices then there are in the graph. + * If we processed everything right, all vertices should have been hit. */ + assert(nextnodeindex == nvertices); + + BMSfreeBlockMemoryArray(blkmem,&rowprocessed,dec->memRows); + BMSfreeBlockMemoryArray(blkmem,&calldata,largestmember); + BMSfreeBlockMemoryArray(blkmem,&dectographnode,largestNode); + return SCIP_OKAY; +} /* ---------- START functions for column addition ------------------------------------------------------------------- */ /** Path arcs are all those arcs that correspond to nonzeros of the column to be added. */ @@ -11364,7 +11669,6 @@ SCIP_Bool SCIPnetmatdecIsMinimal( return netMatDecDataIsMinimal(dec->dec); } - SCIP_Bool SCIPnetmatdecVerifyCycle( BMS_BUFMEM* bufmem, /**< Buffer memory */ SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ @@ -11380,3 +11684,13 @@ SCIP_Bool SCIPnetmatdecVerifyCycle( { return netMatDecDataVerifyCycle(bufmem, dec->dec, column, nonzrowidx, nonzvals, nnonzs, pathrowstorage, pathsignstorage); } + +SCIP_RETCODE SCIPnetmatdecCreateDiGraph( + SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ + BMS_BLKMEM * blkmem, /**< The block memory to use for the created digraph */ + SCIP_DIGRAPH** pdigraph, /**< Pointer to the pointer to store the created digraph */ + SCIP_Bool createrowarcs /**< Should the row arcs be added to the created digraph? */ +) +{ + return netMatDecDataCreateDiGraph(dec->dec,blkmem,pdigraph); +} diff --git a/src/scip/pub_network.h b/src/scip/pub_network.h index 5fabb8dfea..633556be0e 100644 --- a/src/scip/pub_network.h +++ b/src/scip/pub_network.h @@ -74,6 +74,7 @@ #include "scip/def.h" #include "scip/type_retcode.h" #include "scip/type_mem.h" +#include "scip/type_misc.h" #include "blockmemshell/memory.h" #ifdef cplusplus @@ -202,6 +203,20 @@ SCIP_Bool SCIPnetmatdecVerifyCycle( * equal or greater than the number of rows in the decomposition. */ ); +/** Constructs a realization of an underlying directed graph belonging to the network matrix. + * + * Since many different realizations are possible, we use the default orientation of the two-separations to associate + * pairs of nodes to each other. In particular, we choose to connect the nodes of different 2-connected components + * in node 0. This way, the rank of the underlying matrix is equal to m+1-c, where c is the number of undirected + * connected components of the graph where the row/tree edges have been left out. + */ +SCIP_EXPORT +SCIP_RETCODE SCIPnetmatdecCreateDiGraph( + SCIP_NETMATDEC* dec, /**< The network matrix decomposition */ + BMS_BLKMEM * blkmem, /**< The block memory to use for the created digraph */ + SCIP_DIGRAPH** pdigraph, /**< Pointer to the pointer to store the created digraph */ + SCIP_Bool createrowarcs /**< Should the row arcs be added to the created digraph? */ +); /**@} */ #ifdef cplusplus diff --git a/tests/src/network/network.c b/tests/src/network/network.c index fb598e7640..c2f31a662c 100644 --- a/tests/src/network/network.c +++ b/tests/src/network/network.c @@ -385,6 +385,50 @@ static SCIP_RETCODE runRowTestCase( return SCIP_OKAY; } +static SCIP_RETCODE runRowTestCaseGraph( + DirectedTestCase* testCase +) +{ + if( !testCase->isRowWise ) + { + transposeMatrixStorage(testCase); + } + cr_expect(testCase->isRowWise); + + //We keep a column-wise copy to check the columns easily + DirectedTestCase colWiseCase = copyTestCase(testCase); + transposeMatrixStorage(&colWiseCase); + + BMS_BLKMEM * blkmem = SCIPblkmem(scip); + BMS_BUFMEM * bufmem = SCIPbuffer(scip); + + SCIP_NETMATDEC* dec = NULL; + SCIP_CALL(SCIPnetmatdecCreate(blkmem, &dec, testCase->nrows, testCase->ncols)); + + SCIP_Bool isNetwork = TRUE; + + for( int i = 0; i < testCase->nrows; ++i ) + { + int rowEntryStart = testCase->primaryIndexStart[i]; + int rowEntryEnd = testCase->primaryIndexStart[i + 1]; + int* nonzeroCols = &testCase->entrySecondaryIndex[rowEntryStart]; + double* nonzeroValues = &testCase->entryValue[rowEntryStart]; + int nonzeros = rowEntryEnd - rowEntryStart; + cr_assert(nonzeros >= 0); + //Check if adding the row preserves the network matrix + SCIP_CALL(SCIPnetmatdecTryAddRow(dec,i,nonzeroCols,nonzeroValues,nonzeros,&isNetwork)); + assert(isNetwork); + } + SCIP_DIGRAPH* graph; + SCIP_CALL( SCIPnetmatdecCreateDiGraph(dec, blkmem, &graph) ); + + SCIPdigraphPrint(graph, SCIPgetMessagehdlr(scip),stdout); + SCIPdigraphFree(&graph); + freeTestCase(&colWiseCase); + + SCIPnetmatdecFree(&dec); + return SCIP_OKAY; +} TestSuite(network, .init = setup, .fini = teardown); Test(network, coladd_single_column, .description = "Try adding a single column") @@ -1899,3 +1943,15 @@ Test(network, rowadd_singlerigid_8, .description = "Updating a single rigid memb freeTestCase(&testCase); } //TODO: test interleaved addition, test using random sampling + test erdos-renyi generated graphs + +Test(network, rowadd_singlerigid_graph, .description = "Computing the graph for a single rigid member") +{ + DirectedTestCase testCase = stringToTestCase( + "+1 0 +1 " + "+1 +1 0 " + "0 -1 +1 " + "+1 +1 0 ", + 4, 3); + runRowTestCaseGraph(&testCase); + freeTestCase(&testCase); +} From eb87ae5fc32cd90f2023a8cd3e224d11be9a41d9 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Fri, 9 Aug 2024 07:42:13 +0200 Subject: [PATCH 38/63] Add missing call parameter --- src/scip/network.c | 2 +- tests/src/network/network.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 6d9ddbc6df..a08250cd06 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -11692,5 +11692,5 @@ SCIP_RETCODE SCIPnetmatdecCreateDiGraph( SCIP_Bool createrowarcs /**< Should the row arcs be added to the created digraph? */ ) { - return netMatDecDataCreateDiGraph(dec->dec,blkmem,pdigraph); + return netMatDecDataCreateDiGraph(dec->dec,blkmem,pdigraph,createrowarcs); } diff --git a/tests/src/network/network.c b/tests/src/network/network.c index c2f31a662c..6d30082ff0 100644 --- a/tests/src/network/network.c +++ b/tests/src/network/network.c @@ -420,7 +420,7 @@ static SCIP_RETCODE runRowTestCaseGraph( assert(isNetwork); } SCIP_DIGRAPH* graph; - SCIP_CALL( SCIPnetmatdecCreateDiGraph(dec, blkmem, &graph) ); + SCIP_CALL( SCIPnetmatdecCreateDiGraph(dec, blkmem, &graph, TRUE) ); SCIPdigraphPrint(graph, SCIPgetMessagehdlr(scip),stdout); SCIPdigraphFree(&graph); From 309035e6f6ddf149b2036c82412d21d6bbcdd90a Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Fri, 9 Aug 2024 09:24:14 +0200 Subject: [PATCH 39/63] Add indices to realized graph --- src/scip/network.c | 25 ++++++++++++++++++------- src/scip/pub_network.h | 2 ++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index a08250cd06..28396135aa 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -3266,7 +3266,11 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( //We only realize non-virtual arcs if( createrowarcs || !arcIsTree(dec, arc)) { - SCIP_CALL( SCIPdigraphAddArc(digraph, dectographnode[tail], dectographnode[head], NULL) ); + spqr_element element = arcGetElement(dec,arc); + long index = SPQRelementIsRow(element) ? SPQRelementToRow(element) : + SPQRelementToColumn(element) + dec->memRows; + SCIP_CALL( SCIPdigraphAddArc(digraph, dectographnode[tail], dectographnode[head], + (void*) index) ); } } spqr_element element = arcGetElement(dec, arc); @@ -3310,15 +3314,18 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( /* We only realize non-virtual arcs */ if( createrowarcs || !arcIsTree(dec, arc) ) { + spqr_element element = arcGetElement(dec,arc); + long index = SPQRelementIsRow(element) ? SPQRelementToRow(element) : + SPQRelementToColumn(element) + dec->memRows; if( arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc)) { - SCIP_CALL( - SCIPdigraphAddArc(digraph, explore.graphfirstarctail, explore.graphfirstarchead, NULL) ); + SCIP_CALL( SCIPdigraphAddArc(digraph, explore.graphfirstarctail, + explore.graphfirstarchead, (void*) index) ); } else { - SCIP_CALL( - SCIPdigraphAddArc(digraph, explore.graphfirstarchead, explore.graphfirstarctail, NULL) ); + SCIP_CALL( SCIPdigraphAddArc(digraph, explore.graphfirstarchead, + explore.graphfirstarctail, (void*) index) ); } } } @@ -3361,13 +3368,17 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( /* We only realize non-virtual arcs */ if( createrowarcs || !arcIsTree(dec, arc)) { + spqr_element element = arcGetElement(dec,arc); + long index = SPQRelementIsRow(element) ? SPQRelementToRow(element) : + SPQRelementToColumn(element) + dec->memRows; + if( arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc) ) { - SCIP_CALL( SCIPdigraphAddArc(digraph, secondnode, firstnode, NULL) ); + SCIP_CALL( SCIPdigraphAddArc(digraph, secondnode, firstnode, (void*) index) ); } else { - SCIP_CALL( SCIPdigraphAddArc(digraph, firstnode, secondnode, NULL) ); + SCIP_CALL( SCIPdigraphAddArc(digraph, firstnode, secondnode, (void*) index) ); } } } diff --git a/src/scip/pub_network.h b/src/scip/pub_network.h index 633556be0e..091eb2b032 100644 --- a/src/scip/pub_network.h +++ b/src/scip/pub_network.h @@ -205,6 +205,8 @@ SCIP_Bool SCIPnetmatdecVerifyCycle( /** Constructs a realization of an underlying directed graph belonging to the network matrix. * + * The arc data of the digraph contains the row/column indices of the graph. If index < nrows, then the index is the + * corresponding row. If the index >= nrows, then index-nrows is the column index. * Since many different realizations are possible, we use the default orientation of the two-separations to associate * pairs of nodes to each other. In particular, we choose to connect the nodes of different 2-connected components * in node 0. This way, the rank of the underlying matrix is equal to m+1-c, where c is the number of undirected From 071a120f58cf923b8f4120c66c04156c182795be Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 13 Aug 2024 15:36:28 +0200 Subject: [PATCH 40/63] Add implied integer detection plugin --- src/scip/presol_implint.c | 557 +++++++++++++++++++++++++++++++++++++- src/scip/presol_implint.h | 4 +- 2 files changed, 552 insertions(+), 9 deletions(-) diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index dff36bdc2d..e30576d6c2 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -24,24 +24,41 @@ /**@file presol_implint.c * @ingroup DEFPLUGINS_PRESOL - * @brief Presolver that detects implied integer variables + * @brief Presolver that detects implicit integer variables * @author Rolf van der Hulst */ +/* TODO: explore integer to implicit integer conversion */ +/* TODO: fix to work in MINLP context */ /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/ #include #include "scip/presol_implint.h" -#include "scip/scip_presol.h" + +#include "scip/pub_matrix.h" #include "scip/pub_message.h" +#include "scip/pub_misc.h" +#include "scip/pub_network.h" +#include "scip/pub_var.h" +#include "scip/scip_general.h" +#include "scip/scip_message.h" +#include "scip/scip_mem.h" +#include "scip/scip_nlp.h" +#include "scip/scip_numerics.h" +#include "scip/scip_presol.h" +#include "scip/scip_pricer.h" +#include "scip/scip_probing.h" +#include "scip/scip_timing.h" +#include "scip/scip_var.h" + #define PRESOL_NAME "implint" -#define PRESOL_DESC "detects implied integer variables" -#define PRESOL_PRIORITY 0 /**< priority of the presolver (>= 0: before, < 0: after constraint handlers); combined with propagators */ +#define PRESOL_DESC "detects implicit integer variables" +#define PRESOL_PRIORITY 100 /**< priority of the presolver (>= 0: before, < 0: after constraint handlers); combined with propagators */ #define PRESOL_MAXROUNDS -1 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */ -#define PRESOL_TIMING SCIP_PRESOLTIMING_MEDIUM /* timing of the presolver (fast, medium, or exhaustive) */ +#define PRESOL_TIMING SCIP_PRESOLTIMING_EXHAUSTIVE /* timing of the presolver (fast, medium, or exhaustive) */ /* @@ -53,16 +70,471 @@ /** presolver data */ struct SCIP_PresolData { + int temp; //TODO fix to stop complaining compiler }; /* * Local methods */ +typedef enum +{ + VAR_CLASS_INTEGER_CANDIDATE = 0, /**< The variable has integrality constraints and could potentially + *< be an implicit integer. */ + VAR_CLASS_INTEGER_FIXED = 1, /**< All other variables with integrality constraints*/ + VAR_CLASS_CONTINUOUS_CANDIDATE = 2, /**< The variable is continuous and could be an implicit integer */ + VAR_CLASS_CONTINUOUS_FIXED = 3 /**< All other continuous variables */ +} VAR_CLASS; + +/** + * Struct that contains information about the blocks/components of the submatrix given by the continuous columns + */ +typedef struct{ + int nmatrixrows; /**< Number of rows in the matrix for the linear part of the problem */ + int nmatrixcols; /**< Number of columns in the matrix for the linear part of the problem */ + + SCIP_VARTYPE * coltype; /**< SCIP_VARTYPE of the associated column */ + + int* rowcomponent; /**< Maps a row to the index of the component it belongs to */ + int* colcomponent; /**< Maps a column to the index of the component it belongs to */ + + int* componentrows; /**< Flattened array of array of rows that are in a given component. */ + int* componentcols; /**< Flattened array of array of columns that are in a given component. */ + int* componentrowstart; /**< The index of componentrows where the given component starts. */ + int* componentcolstart; /**< The index of componentcols where the given component starts. */ + int* ncomponentrows; /**< The number of rows in the given component. */ + int* ncomponentcols; /**< The number of columns in the given component */ + + int ncomponents; +} MATRIX_COMPONENTS; + +static +SCIP_RETCODE createMatrixComponents( + SCIP* scip, + SCIP_MATRIX* matrix, + MATRIX_COMPONENTS** pmatrixcomponents +) +{ + SCIP_CALL( SCIPallocBlockMemory(scip, pmatrixcomponents) ); + MATRIX_COMPONENTS * comp = *pmatrixcomponents; + + int nrows = SCIPmatrixGetNRows(matrix); + int ncols = SCIPmatrixGetNColumns(matrix); + + comp->nmatrixrows = nrows; + comp->nmatrixcols = ncols; + + SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->coltype, ncols) ); + for( int i = 0; i < ncols; ++i ) + { + SCIP_VAR * var = SCIPmatrixGetVar(matrix,i); + comp->coltype[i] = SCIPvarGetType(var); + } + + SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->rowcomponent, nrows) ); + for( int i = 0; i < nrows; ++i ) + { + comp->rowcomponent[i] = -1; + } + SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->colcomponent, ncols) ); + for( int i = 0; i < ncols; ++i ) + { + comp->colcomponent[i] = -1; + } + + SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->componentrows, nrows) ); + SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->componentcols, ncols) ); + //There will be at most ncols components + SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->componentrowstart, ncols) ); + SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->componentcolstart, ncols) ); + SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->ncomponentrows, ncols) ); + SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->ncomponentcols, ncols) ); + + comp->ncomponents = 0; + + return SCIP_OKAY; +} + +static +void freeMatrixInfo( + SCIP* scip, + MATRIX_COMPONENTS** pmatrixcomponents +) +{ + MATRIX_COMPONENTS* comp = *pmatrixcomponents; + SCIPfreeBlockMemoryArray(scip, &comp->ncomponentcols, comp->nmatrixcols); + SCIPfreeBlockMemoryArray(scip, &comp->ncomponentrows, comp->nmatrixcols); + SCIPfreeBlockMemoryArray(scip, &comp->componentcolstart, comp->nmatrixcols); + SCIPfreeBlockMemoryArray(scip, &comp->componentrowstart, comp->nmatrixcols); + SCIPfreeBlockMemoryArray(scip, &comp->componentcols, comp->nmatrixcols); + SCIPfreeBlockMemoryArray(scip, &comp->componentrows, comp->nmatrixrows); + SCIPfreeBlockMemoryArray(scip, &comp->colcomponent, comp->nmatrixcols); + SCIPfreeBlockMemoryArray(scip, &comp->rowcomponent, comp->nmatrixrows); + SCIPfreeBlockMemoryArray(scip, &comp->coltype, comp->nmatrixcols); + + SCIPfreeBlockMemory(scip, pmatrixcomponents); +} + +static +SCIP_RETCODE computeContinuousComponents( + SCIP * scip, + SCIP_MATRIX* matrix, + MATRIX_COMPONENTS* comp +) +{ + int currentcomponent = 0; + int rowindex = 0; + int colindex = 0; + /* We let rows and columns share an index by mapping column i to index nrows + i*/ + int* dfsstack = NULL; + int ndfsstack = 0; + SCIP_CALL(SCIPallocBufferArray(scip, &dfsstack, comp->nmatrixcols + comp->nmatrixrows)); + + for( int i = 0; i < comp->nmatrixcols; ++i ) + { + /* Check if earlier DFS already found column */ + if( comp->colcomponent[i] != -1 || comp->coltype[i] != SCIP_VARTYPE_CONTINUOUS){ + continue; + } + /* If not, then we have a new component, and perform a DFS to find all connected columns and rows */ + comp->componentrowstart[currentcomponent] = rowindex; + comp->componentcolstart[currentcomponent] = colindex; + + assert(ndfsstack == 0); + /* Push column to DFS search */ + dfsstack[ndfsstack] = comp->nmatrixrows + i; + ++ndfsstack; + comp->colcomponent[i] = currentcomponent; + + while( ndfsstack != 0 ){ + --ndfsstack; + int index = dfsstack[ndfsstack]; + /* process column or row, adding new connected rows/columns to the dfs stack */ + if( index >= comp->nmatrixrows ){ + int column = index - comp->nmatrixrows; + assert(comp->coltype[column] == SCIP_VARTYPE_CONTINUOUS); + comp->componentcols[colindex] = column; + ++colindex; + + /* Push connected rows to the dfs stack */ + int colnnonzs = SCIPmatrixGetColNNonzs(matrix, column); + int* colrows = SCIPmatrixGetColIdxPtr(matrix, column); + for( int j = 0; j < colnnonzs; ++j ) + { + int row = colrows[j]; + if( comp->rowcomponent[row] == -1 ){ + dfsstack[ndfsstack] = row; + ++ndfsstack; + comp->rowcomponent[row] = currentcomponent; + } + } + } + else + { + int row = index; + comp->componentrows[rowindex] = row; + ++rowindex; + + /* Push any connected continuous columns on the dfs search stack */ + int rownnonzs = SCIPmatrixGetRowNNonzs(matrix,row); + int* rowcols = SCIPmatrixGetRowIdxPtr(matrix,row); + for( int j = 0; j < rownnonzs; ++j ) + { + int col = rowcols[j]; + if( comp->colcomponent[col] == -1 && comp->coltype[col] == SCIP_VARTYPE_CONTINUOUS){ + comp->colcomponent[col] = currentcomponent; + dfsstack[ndfsstack] = comp->nmatrixrows + col; + ++ndfsstack; + } + } + } + } + + /* We are done with DFS for this component, save the sizes */ + comp->ncomponentrows[currentcomponent] = rowindex - comp->componentrowstart[currentcomponent]; + comp->ncomponentcols[currentcomponent] = colindex - comp->componentcolstart[currentcomponent]; + ++currentcomponent; + ++comp->ncomponents; + assert(currentcomponent == comp->ncomponents); + } + SCIPfreeBufferArray(scip,&dfsstack); + return SCIP_OKAY; +} + +typedef struct{ + SCIP_Bool* rowintegral; /**< Are all the non-continuous column entries and lhs rhs integral? */ + SCIP_Bool* rowequality; /**< Is the row an equality? */ + SCIP_Bool* rowbadnumerics; /**< Does the row contain large entries that make numerics difficult? */ + int* rownnonz; /**< Number of nonzeros in the column */ + int* rowncontinuous; /**< The number of those nonzeros that are in continuous columns */ + int* rowncontinuouspmone; /**< The number of continuous columns +-1 entries */ + +} MATRIX_STATISTICS; + +static +SCIP_RETCODE computeMatrixStatistics( + SCIP * scip, + SCIP_MATRIX* matrix, + MATRIX_STATISTICS** pstats +) +{ + SCIP_CALL( SCIPallocBuffer(scip,pstats) ); + MATRIX_STATISTICS* stats = *pstats; + + int nrows = SCIPmatrixGetNRows(matrix); + + SCIP_CALL( SCIPallocBufferArray(scip,&stats->rowintegral,nrows) ); + SCIP_CALL( SCIPallocBufferArray(scip,&stats->rowequality,nrows) ); + SCIP_CALL( SCIPallocBufferArray(scip,&stats->rowbadnumerics,nrows) ); + + SCIP_CALL( SCIPallocBufferArray(scip,&stats->rownnonz,nrows) ); + SCIP_CALL( SCIPallocBufferArray(scip,&stats->rowncontinuous,nrows) ); + SCIP_CALL( SCIPallocBufferArray(scip,&stats->rowncontinuouspmone,nrows) ); + + + for( int i = 0; i < nrows; ++i ) + { + double lhs = SCIPmatrixGetRowLhs(matrix,i); + double rhs = SCIPmatrixGetRowRhs(matrix,i); + int * cols = SCIPmatrixGetRowIdxPtr(matrix,i); + double * vals = SCIPmatrixGetRowValPtr(matrix,i); + int nnonz = SCIPmatrixGetRowNNonzs(matrix,i); + stats->rownnonz[i] = nnonz; + stats->rowequality[i] = SCIPisFeasEQ(scip,lhs,rhs) && !( SCIPisInfinity(scip,-lhs) || SCIPisInfinity(scip, rhs) ); + + SCIP_Bool integral = ( SCIPisInfinity(scip,-lhs) || SCIPisIntegral(scip,lhs)) && + ( SCIPisInfinity(scip,rhs) || SCIPisIntegral(scip,rhs)); + SCIP_Bool badnumerics = FALSE; + + int ncontinuous = 0; + int ncontinuouspmone = 0; + for( int j = 0; j < nnonz; ++j ) + { + SCIP_Bool continuous = SCIPvarGetType(SCIPmatrixGetVar(matrix,cols[j])) == SCIP_VARTYPE_CONTINUOUS; + double value = vals[j]; + if(continuous){ + ++ncontinuous; + } + if(continuous && ABS(value) == 1.0){ + ++ncontinuouspmone; + } + if(!continuous){ + integral = integral && SCIPisIntegral(scip,value); + } + if(ABS(value) > 1e7){ + badnumerics = TRUE; + } + } + + stats->rowncontinuous[i] = ncontinuous; + stats->rowncontinuouspmone[i] = ncontinuouspmone; + stats->rowintegral[i] = integral; + stats->rowbadnumerics[i] = badnumerics; + } + + + return SCIP_OKAY; +} -/* put your local methods here, and declare them static */ +static +void freeMatrixStatistics( + SCIP* scip, + MATRIX_STATISTICS** pstats +) +{ + MATRIX_STATISTICS* stats= *pstats; + SCIPfreeBufferArray(scip,&stats->rowncontinuouspmone); + SCIPfreeBufferArray(scip,&stats->rowncontinuous); + SCIPfreeBufferArray(scip,&stats->rownnonz); + SCIPfreeBufferArray(scip,&stats->rowequality); + SCIPfreeBufferArray(scip,&stats->rowintegral); + SCIPfreeBufferArray(scip,&stats->rowbadnumerics); + + SCIPfreeBuffer(scip,pstats); +} +static +SCIP_RETCODE findImpliedIntegers( + SCIP * scip, + SCIP_MATRIX* matrix, + MATRIX_COMPONENTS* comp, + MATRIX_STATISTICS* stats, + int* nchgvartypes +) +{ + //TODO: some checks to prevent expensive memory initialization if not necessary (e.g. there must be some candidates) + SCIP_NETMATDEC * dec = NULL; + SCIP_CALL(SCIPnetmatdecCreate(SCIPblkmem(scip),&dec,comp->nmatrixrows,comp->nmatrixcols)); + + SCIP_NETMATDEC * transdec = NULL; + SCIP_CALL(SCIPnetmatdecCreate(SCIPblkmem(scip),&transdec,comp->nmatrixcols,comp->nmatrixrows)); + + int planarcomponents = 0; + int goodcomponents = 0; + int nbadnumerics = 0; + int nbadintegrality = 0; + int nnonnetwork = 0; + + /* Because the rows may also contain non-continuous columns, we need to remove these from the array that we + * pass to the network matrix decomposition method. We use these working arrays for this purpose. */ + double* tempValArray; + int* tempIdxArray; + SCIP_CALL(SCIPallocBufferArray(scip,&tempValArray,comp->nmatrixcols)); + SCIP_CALL(SCIPallocBufferArray(scip,&tempIdxArray,comp->nmatrixcols)); + + + for( int component = 0; component < comp->ncomponents; ++component ) + { + int startrow = comp->componentrowstart[component]; + int nrows = comp->ncomponentrows[component]; + SCIP_Bool componentokay = TRUE; + for( int i = startrow; i < startrow + nrows; ++i ) + { + int row = comp->componentrows[i]; + if(stats->rowncontinuous[row] != stats->rowncontinuouspmone[row]){ + componentokay = FALSE; + ++nbadintegrality; + break; + } + if(!stats->rowintegral[row]){ + componentokay = FALSE; + ++nbadintegrality; + break; + } + if(stats->rowbadnumerics[row]){ + componentokay = FALSE; + ++nbadnumerics; + break; + } + } + if(!componentokay){ + continue; + } + int startcol = comp->componentcolstart[component]; + int ncols = comp->ncomponentcols[component]; + + /* Check if the component is a network matrix */ + SCIP_Bool componentnetwork = TRUE; + + /* We use the row-wise algorithm only if the number of columns is much larger than the number of rows. + * Generally, the column-wise algorithm will be faster, but in these extreme cases, the row algorithm is faster. + * TODO: test/tune this parameter + */ + //TODO for ~50-150 seems to be about even on neos-...-inde and neos-...-isar, test further + if(nrows * 20 < ncols){ + printf("Row addition: %f \n",(double) ncols / (double) nrows); + for( int i = startrow; i < startrow + nrows && componentnetwork; ++i ) + { + int row = comp->componentrows[i]; + int nrownnoz = SCIPmatrixGetRowNNonzs(matrix,row); + int* rowcols = SCIPmatrixGetRowIdxPtr(matrix,row); + double* rowvals = SCIPmatrixGetRowValPtr(matrix,row); + int ncontnonz = 0; + for( int j = 0; j < nrownnoz; ++j ) + { + int col = rowcols[j]; + if(SCIPvarGetType(SCIPmatrixGetVar(matrix,col)) == SCIP_VARTYPE_CONTINUOUS) + { + tempIdxArray[ncontnonz] = col; + tempValArray[ncontnonz] = rowvals[j]; + ++ncontnonz; + assert(ABS(rowvals[j]) == 1.0); + } + } + + SCIP_CALL( SCIPnetmatdecTryAddRow(dec,row,tempIdxArray,tempValArray,ncontnonz,&componentnetwork) ); + } + }else{ + for( int i = startcol; i < startcol + ncols && componentnetwork; ++i ) + { + int col = comp->componentcols[i]; + int ncolnnonz = SCIPmatrixGetColNNonzs(matrix,col); + int* colrows = SCIPmatrixGetColIdxPtr(matrix,col); + double* colvals = SCIPmatrixGetColValPtr(matrix,col); + SCIP_CALL( SCIPnetmatdecTryAddCol(dec,col,colrows,colvals,ncolnnonz,&componentnetwork) ); + } + } + + if( !componentnetwork ) + { + SCIPnetmatdecRemoveComponent(dec,&comp->componentrows[startrow], nrows, &comp->componentcols[startcol], ncols); + ++nnonnetwork; + } + + SCIP_Bool componenttransnetwork = TRUE; + + /* For the transposed matrix, the situation is exactly reversed because the row/column algorithms are swapped */ + //TODO: tune parameter + if(nrows < ncols * 20){ + for( int i = startrow; i < startrow + nrows && componenttransnetwork ; ++i ) + { + int row = comp->componentrows[i]; + int nrownnoz = SCIPmatrixGetRowNNonzs(matrix,row); + int* rowcols = SCIPmatrixGetRowIdxPtr(matrix,row); + double* rowvals = SCIPmatrixGetRowValPtr(matrix,row); + int ncontnonz = 0; + for( int j = 0; j < nrownnoz; ++j ) + { + int col = rowcols[j]; + if(SCIPvarGetType(SCIPmatrixGetVar(matrix,col)) == SCIP_VARTYPE_CONTINUOUS) + { + tempIdxArray[ncontnonz] = col; + tempValArray[ncontnonz] = rowvals[j]; + ++ncontnonz; + assert(ABS(rowvals[j]) == 1.0); + } + } + + SCIP_CALL( SCIPnetmatdecTryAddCol(transdec,row,tempIdxArray,tempValArray,ncontnonz, + &componenttransnetwork) ); + } + }else{ + for( int i = startcol; i < startcol + ncols && componenttransnetwork; ++i ) + { + int col = comp->componentcols[i]; + int ncolnnonz = SCIPmatrixGetColNNonzs(matrix,col); + int* colrows = SCIPmatrixGetColIdxPtr(matrix,col); + double* colvals = SCIPmatrixGetColValPtr(matrix,col); + SCIP_CALL( SCIPnetmatdecTryAddRow(transdec,col,colrows,colvals,ncolnnonz,&componenttransnetwork) ); + } + } + + if( !componenttransnetwork ) + { + SCIPnetmatdecRemoveComponent(transdec,&comp->componentcols[startcol], + ncols, &comp->componentrows[startrow], nrows); + } + + if( !componentnetwork && !componenttransnetwork){ + ++nnonnetwork; + continue; + } + ++goodcomponents; + if(componentnetwork && componenttransnetwork){ + ++planarcomponents; + } + for( int i = startcol; i < startcol + ncols; ++i ) + { + int col = comp->componentcols[i]; + SCIP_VAR * var = SCIPmatrixGetVar(matrix,col); + SCIP_Bool infeasible = FALSE; + SCIP_CALL(SCIPchgVarType(scip,var,SCIP_VARTYPE_IMPLINT,&infeasible)); + (*nchgvartypes)++; + //TODO: inform of changed variable types + assert(!infeasible); + } + } + SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH,NULL, + "implied integer components: %d (%d planar) / %d (disqualified: %d by integrality, %d by numerics, %d not network) \n", + goodcomponents,planarcomponents,comp->ncomponents,nbadintegrality,nbadnumerics,nnonnetwork); + + SCIPfreeBufferArray(scip,&tempIdxArray); + SCIPfreeBufferArray(scip,&tempValArray); + + SCIPnetmatdecFree(&dec); + return SCIP_OKAY; +} /* * Callback methods of presolver */ @@ -165,7 +637,78 @@ SCIP_DECL_PRESOLEXITPRE(presolExitpreImplint) static SCIP_DECL_PRESOLEXEC(presolExecImplint) { - + *result = SCIP_DIDNOTRUN; + + //TODO: re-check these conditions again + //Disable implicit integer detection if we are probing or in NLP context + if(( SCIPgetStage(scip) != SCIP_STAGE_PRESOLVING ) || SCIPinProbing(scip) || SCIPisNLPEnabled(scip)) + { + return SCIP_OKAY; + } + //Since implied integer detection relies on rows not being changed, we disable it for branch-and-price applications + if( SCIPisStopped(scip) || SCIPgetNActivePricers(scip) > 0 ) + { + return SCIP_OKAY; + } + + *result = SCIP_DIDNOTFIND; + + double starttime = SCIPgetSolvingTime(scip); + SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, + " (%.1fs) implied integer detection started\n", starttime); + + SCIP_Bool initialized; + SCIP_Bool complete; + SCIP_Bool infeasible; + SCIP_MATRIX* matrix = NULL; + SCIP_Bool onlyifcomplete = TRUE; + SCIP_CALL( SCIPmatrixCreate(scip, &matrix, onlyifcomplete, &initialized, &complete, &infeasible, + naddconss, ndelconss, nchgcoefs, nchgbds, nfixedvars) ); + /*If infeasibility was detected during matrix creation, we return. */ + if( infeasible ) + { + if( initialized ) + { + SCIPmatrixFree(scip, &matrix); + } + *result = SCIP_CUTOFF; + return SCIP_OKAY; + } + + /*For now, we only work on pure MILP's TODO; use uplocks/downlocks */ + if( !( initialized && complete )) + { + if( initialized ) + { + SCIPmatrixFree(scip, &matrix); + } + SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, + " (%.1fs) implied integer detection stopped because problem is not an MILP\n", + SCIPgetSolvingTime(scip)); + return SCIP_OKAY; + } + + int beforechanged = *nchgvartypes; + MATRIX_COMPONENTS* comp = NULL; + MATRIX_STATISTICS* stats = NULL; + SCIP_CALL( createMatrixComponents(scip, matrix, &comp) ); + SCIP_CALL( computeMatrixStatistics(scip, matrix, &stats) ); + SCIP_CALL( computeContinuousComponents(scip, matrix, comp) ); + SCIP_CALL( findImpliedIntegers(scip,matrix,comp, stats, nchgvartypes) ); + int afterchanged = *nchgvartypes; + + + double endtime = SCIPgetSolvingTime(scip); + if(afterchanged == beforechanged){ + SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, + " (%.1fs) no implied integers detected (time: %.2fs)\n", endtime,endtime-starttime); + *result = SCIP_DIDNOTFIND; + }else{ + *result = SCIP_SUCCESS; + } + freeMatrixStatistics(scip,&stats); + freeMatrixInfo(scip, &comp); + SCIPmatrixFree(scip, &matrix); return SCIP_OKAY; } diff --git a/src/scip/presol_implint.h b/src/scip/presol_implint.h index 1093e38d05..3a6e5a5292 100644 --- a/src/scip/presol_implint.h +++ b/src/scip/presol_implint.h @@ -24,7 +24,7 @@ /**@file presol_implint.h * @ingroup PRESOLVERS - * @brief Presolver that detects implied integer variables + * @brief Presolver that detects implicit integer variables * @author Rolf van der Hulst */ @@ -39,7 +39,7 @@ extern "C" { #endif -/** creates the implied integer presolver and includes it in SCIP +/** creates the implicit integer presolver and includes it in SCIP * * @ingroup PresolverIncludes */ From 142cbe1c456115b3848d04a2208473ca66b76bbe Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 13 Aug 2024 16:09:00 +0200 Subject: [PATCH 41/63] Fix linter warnings for implied integer detection --- src/scip/network.c | 34 +++++++++++++++++++++++----------- src/scip/presol_implint.c | 20 ++++---------------- tests/src/network/network.c | 1 - 3 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 28396135aa..519247150c 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -3267,10 +3267,14 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( if( createrowarcs || !arcIsTree(dec, arc)) { spqr_element element = arcGetElement(dec,arc); - long index = SPQRelementIsRow(element) ? SPQRelementToRow(element) : - SPQRelementToColumn(element) + dec->memRows; + long ind; + if( SPQRelementIsRow(element) ){ + ind = SPQRelementToRow(element); + }else{ + ind = SPQRelementToColumn(element) + dec->memRows; + } SCIP_CALL( SCIPdigraphAddArc(digraph, dectographnode[tail], dectographnode[head], - (void*) index) ); + (void*) ind) ); } } spqr_element element = arcGetElement(dec, arc); @@ -3315,17 +3319,21 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( if( createrowarcs || !arcIsTree(dec, arc) ) { spqr_element element = arcGetElement(dec,arc); - long index = SPQRelementIsRow(element) ? SPQRelementToRow(element) : - SPQRelementToColumn(element) + dec->memRows; + long ind; + if( SPQRelementIsRow(element) ){ + ind = SPQRelementToRow(element); + }else{ + ind = SPQRelementToColumn(element) + dec->memRows; + } if( arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc)) { SCIP_CALL( SCIPdigraphAddArc(digraph, explore.graphfirstarctail, - explore.graphfirstarchead, (void*) index) ); + explore.graphfirstarchead, (void*) ind) ); } else { SCIP_CALL( SCIPdigraphAddArc(digraph, explore.graphfirstarchead, - explore.graphfirstarctail, (void*) index) ); + explore.graphfirstarctail, (void*) ind) ); } } } @@ -3369,16 +3377,20 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( if( createrowarcs || !arcIsTree(dec, arc)) { spqr_element element = arcGetElement(dec,arc); - long index = SPQRelementIsRow(element) ? SPQRelementToRow(element) : - SPQRelementToColumn(element) + dec->memRows; + long ind; + if( SPQRelementIsRow(element) ){ + ind = SPQRelementToRow(element); + }else{ + ind = SPQRelementToColumn(element) + dec->memRows; + } if( arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc) ) { - SCIP_CALL( SCIPdigraphAddArc(digraph, secondnode, firstnode, (void*) index) ); + SCIP_CALL( SCIPdigraphAddArc(digraph, secondnode, firstnode, (void*) ind) ); } else { - SCIP_CALL( SCIPdigraphAddArc(digraph, firstnode, secondnode, (void*) index) ); + SCIP_CALL( SCIPdigraphAddArc(digraph, firstnode, secondnode, (void*) ind) ); } } } diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index e30576d6c2..88abdf49be 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -74,18 +74,6 @@ struct SCIP_PresolData }; -/* - * Local methods - */ -typedef enum -{ - VAR_CLASS_INTEGER_CANDIDATE = 0, /**< The variable has integrality constraints and could potentially - *< be an implicit integer. */ - VAR_CLASS_INTEGER_FIXED = 1, /**< All other variables with integrality constraints*/ - VAR_CLASS_CONTINUOUS_CANDIDATE = 2, /**< The variable is continuous and could be an implicit integer */ - VAR_CLASS_CONTINUOUS_FIXED = 3 /**< All other continuous variables */ -} VAR_CLASS; - /** * Struct that contains information about the blocks/components of the submatrix given by the continuous columns */ @@ -208,10 +196,10 @@ SCIP_RETCODE computeContinuousComponents( while( ndfsstack != 0 ){ --ndfsstack; - int index = dfsstack[ndfsstack]; + int ind = dfsstack[ndfsstack]; /* process column or row, adding new connected rows/columns to the dfs stack */ - if( index >= comp->nmatrixrows ){ - int column = index - comp->nmatrixrows; + if( ind >= comp->nmatrixrows ){ + int column = ind - comp->nmatrixrows; assert(comp->coltype[column] == SCIP_VARTYPE_CONTINUOUS); comp->componentcols[colindex] = column; ++colindex; @@ -231,7 +219,7 @@ SCIP_RETCODE computeContinuousComponents( } else { - int row = index; + int row = ind; comp->componentrows[rowindex] = row; ++rowindex; diff --git a/tests/src/network/network.c b/tests/src/network/network.c index 6d30082ff0..f8812b1e29 100644 --- a/tests/src/network/network.c +++ b/tests/src/network/network.c @@ -400,7 +400,6 @@ static SCIP_RETCODE runRowTestCaseGraph( transposeMatrixStorage(&colWiseCase); BMS_BLKMEM * blkmem = SCIPblkmem(scip); - BMS_BUFMEM * bufmem = SCIPbuffer(scip); SCIP_NETMATDEC* dec = NULL; SCIP_CALL(SCIPnetmatdecCreate(blkmem, &dec, testCase->nrows, testCase->ncols)); From 16849201dc2e224aaf7bc3a300ac51f329fcee59 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 13 Aug 2024 16:16:21 +0200 Subject: [PATCH 42/63] Fix memory leak in implied integer detection presolver --- src/scip/presol_implint.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index 88abdf49be..b7c2acaca6 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -519,6 +519,7 @@ SCIP_RETCODE findImpliedIntegers( SCIPfreeBufferArray(scip,&tempIdxArray); SCIPfreeBufferArray(scip,&tempValArray); + SCIPnetmatdecFree(&transdec); SCIPnetmatdecFree(&dec); return SCIP_OKAY; From a456cb5d0e12617ead5fa0b5b967b363b169e088 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Wed, 14 Aug 2024 12:18:48 +0200 Subject: [PATCH 43/63] Test variable bound substitution for implied integer variables --- src/scip/cuts.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/scip/cuts.c b/src/scip/cuts.c index b2503c0541..de0fe928a7 100644 --- a/src/scip/cuts.c +++ b/src/scip/cuts.c @@ -2626,7 +2626,7 @@ SCIP_RETCODE findBestLb( *simplebound = *bestlb; - if( usevbds && SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS ) + if( usevbds && (SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPvarGetType(var) == SCIP_VARTYPE_IMPLINT)) { SCIP_Real bestvlb; int bestvlbidx; @@ -2687,7 +2687,7 @@ SCIP_RETCODE findBestUb( *simplebound = *bestub; - if( usevbds && SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS ) + if( usevbds && (SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPvarGetType(var) == SCIP_VARTYPE_IMPLINT)) { SCIP_Real bestvub; int bestvubidx; @@ -5298,7 +5298,7 @@ SCIP_RETCODE determineBoundForSNF( bestlb[varposinrow] = bestslb[varposinrow]; bestlbtype[varposinrow] = bestslbtype[varposinrow]; - if( SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS ) + if( SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPvarGetType(var) == SCIP_VARTYPE_IMPLINT) { SCIP_Real bestvlb; int bestvlbidx; @@ -5320,7 +5320,7 @@ SCIP_RETCODE determineBoundForSNF( bestub[varposinrow] = bestsub[varposinrow]; bestubtype[varposinrow] = bestsubtype[varposinrow]; - if( SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS ) + if( SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPvarGetType(var) == SCIP_VARTYPE_IMPLINT ) { SCIP_Real bestvub; int bestvubidx; From 2855d747194350a487a7eeae444cf2c544e743d0 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Fri, 16 Aug 2024 09:09:34 +0200 Subject: [PATCH 44/63] Tune implied integer usage in cutting planes --- src/scip/cutsel_hybrid.c | 15 ++++++++++++++- src/scip/presol_implint.c | 12 +++++++++--- src/scip/sepa_mcf.c | 2 +- src/scip/sepa_mixing.c | 2 +- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/scip/cutsel_hybrid.c b/src/scip/cutsel_hybrid.c index 3d7796e81a..e445bd7abf 100644 --- a/src/scip/cutsel_hybrid.c +++ b/src/scip/cutsel_hybrid.c @@ -111,7 +111,20 @@ SCIP_Real scoring( SCIP_Real efficacy; if( intsupportweight > 0.0 ) - intsupport = intsupportweight * SCIPgetRowNumIntCols(scip, cuts[i]) / (SCIP_Real) SCIProwGetNNonz(cuts[i]); + { + int nIntegral = 0; + SCIP_COL ** rowcols = SCIProwGetCols(cuts[i]); + for( int j = 0; j < SCIProwGetNNonz(cuts[i]); ++j ) + { + SCIP_VAR * var = SCIPcolGetVar(rowcols[j]); + if( SCIPvarGetType(var) == SCIP_VARTYPE_BINARY || SCIPvarGetType(var) == SCIP_VARTYPE_INTEGER){ + ++nIntegral; + } + } + double fraction = (SCIP_Real) nIntegral / (SCIP_Real) SCIProwGetNNonz(cuts[i]); + intsupport = intsupportweight * fraction; +// intsupport = intsupportweight * SCIPgetRowNumIntCols(scip, cuts[i]) / (SCIP_Real) SCIProwGetNNonz(cuts[i]); + } else intsupport = 0.0; diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index b7c2acaca6..9e0f12bb3a 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -357,6 +357,8 @@ SCIP_RETCODE findImpliedIntegers( SCIP_NETMATDEC * transdec = NULL; SCIP_CALL(SCIPnetmatdecCreate(SCIPblkmem(scip),&transdec,comp->nmatrixcols,comp->nmatrixrows)); + int networkcomponents = 0; + int transposednetworkcomponents = 0; int planarcomponents = 0; int goodcomponents = 0; int nbadnumerics = 0; @@ -446,7 +448,6 @@ SCIP_RETCODE findImpliedIntegers( if( !componentnetwork ) { SCIPnetmatdecRemoveComponent(dec,&comp->componentrows[startrow], nrows, &comp->componentcols[startcol], ncols); - ++nnonnetwork; } SCIP_Bool componenttransnetwork = TRUE; @@ -500,6 +501,10 @@ SCIP_RETCODE findImpliedIntegers( ++goodcomponents; if(componentnetwork && componenttransnetwork){ ++planarcomponents; + }else if(componentnetwork){ + ++networkcomponents; + }else{ + ++transposednetworkcomponents; } for( int i = startcol; i < startcol + ncols; ++i ) { @@ -513,8 +518,9 @@ SCIP_RETCODE findImpliedIntegers( } } SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH,NULL, - "implied integer components: %d (%d planar) / %d (disqualified: %d by integrality, %d by numerics, %d not network) \n", - goodcomponents,planarcomponents,comp->ncomponents,nbadintegrality,nbadnumerics,nnonnetwork); + "implied integer components: %d (%d planar, %d network, %d tr. network) / %d (disqualified: %d by integrality, %d by numerics, %d not network) \n", + goodcomponents,planarcomponents,networkcomponents,transposednetworkcomponents, + comp->ncomponents,nbadintegrality,nbadnumerics,nnonnetwork); SCIPfreeBufferArray(scip,&tempIdxArray); SCIPfreeBufferArray(scip,&tempValArray); diff --git a/src/scip/sepa_mcf.c b/src/scip/sepa_mcf.c index b0a4a4bccd..73fede25fd 100644 --- a/src/scip/sepa_mcf.c +++ b/src/scip/sepa_mcf.c @@ -998,7 +998,7 @@ SCIP_RETCODE extractFlowRows( * integer: score +500 * binary: score +100 */ - if( ncontvars == rowlen ) + if( ncontvars == rowlen || nimplintvars == rowlen) flowrowscores[r] += 1000.0; else if( nintvars + nimplintvars == rowlen ) flowrowscores[r] += 500.0; diff --git a/src/scip/sepa_mixing.c b/src/scip/sepa_mixing.c index 3bc1073a44..cab60f2dba 100644 --- a/src/scip/sepa_mixing.c +++ b/src/scip/sepa_mixing.c @@ -270,7 +270,7 @@ SCIP_RETCODE separateCuts( else { /* only generate cuts based on continuous variables */ - firstvar = SCIPgetNBinVars(scip) + SCIPgetNIntVars(scip) + SCIPgetNImplVars(scip); + firstvar = SCIPgetNBinVars(scip) + SCIPgetNIntVars(scip) ; } nvars = SCIPgetNVars(scip); if( firstvar == nvars ) From e998e0eb9d269533d14ad95d899fe400d04b211c Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Mon, 19 Aug 2024 12:32:27 +0200 Subject: [PATCH 45/63] Try to tune CMIR for implied integers --- src/scip/cuts.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/scip/cuts.c b/src/scip/cuts.c index de0fe928a7..4ae94a8e3e 100644 --- a/src/scip/cuts.c +++ b/src/scip/cuts.c @@ -3112,7 +3112,7 @@ SCIP_RETCODE cutsTransformMIR( vars = SCIPgetVars(scip); nvars = SCIPgetNVars(scip); - firstcontvar = nvars - SCIPgetNContVars(scip); + firstcontvar = nvars - SCIPgetNContVars(scip) - SCIPgetNImplVars(scip); /* determine the best bounds for the continuous variables */ for( i = 0; i < *nnz && cutinds[i] >= firstcontvar; ++i ) @@ -3441,7 +3441,7 @@ SCIP_RETCODE cutsRoundMIR( * (due to sorting in cutsTransformMIR the ordering is continuous before integral) */ - firstcontvar = SCIPgetNVars(scip) - SCIPgetNContVars(scip); + firstcontvar = SCIPgetNVars(scip) - SCIPgetNContVars(scip) - SCIPgetNImplVars(scip); vars = SCIPgetVars(scip); #ifndef NDEBUG /*in debug mode check that all continuous variables of the aggrrow come before the integral variables */ @@ -4271,7 +4271,7 @@ SCIP_RETCODE SCIPcutGenerationHeuristicCMIR( *success = FALSE; nvars = SCIPgetNVars(scip); - firstcontvar = nvars - SCIPgetNContVars(scip); + firstcontvar = nvars - SCIPgetNContVars(scip) - SCIPgetNImplVars(scip); vars = SCIPgetVars(scip); /* allocate temporary memory */ From fb89f8a1b052bae7780d2f5de1b9d095f3e9fcff Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Fri, 23 Aug 2024 15:29:49 +0200 Subject: [PATCH 46/63] Fix cut rounding procedure to use implied integrality and handle variable substitutions --- src/scip/cuts.c | 177 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 149 insertions(+), 28 deletions(-) diff --git a/src/scip/cuts.c b/src/scip/cuts.c index 4ae94a8e3e..870440d57c 100644 --- a/src/scip/cuts.c +++ b/src/scip/cuts.c @@ -3084,9 +3084,11 @@ SCIP_RETCODE cutsTransformMIR( int* bestubtypes; SCIP_BOUNDTYPE* selectedbounds; int i; + int aggrrowimplintstart; int aggrrowintstart; int nvars; int firstcontvar; + int firstimplvar; SCIP_VAR** vars; assert(varsign != NULL); @@ -3112,7 +3114,8 @@ SCIP_RETCODE cutsTransformMIR( vars = SCIPgetVars(scip); nvars = SCIPgetNVars(scip); - firstcontvar = nvars - SCIPgetNContVars(scip) - SCIPgetNImplVars(scip); + firstcontvar = nvars - SCIPgetNContVars(scip); + firstimplvar = nvars - SCIPgetNContVars(scip) - SCIPgetNImplVars(scip); /* determine the best bounds for the continuous variables */ for( i = 0; i < *nnz && cutinds[i] >= firstcontvar; ++i ) @@ -3125,11 +3128,11 @@ SCIP_RETCODE cutsTransformMIR( goto TERMINATE; } - /* remember start of integer variables in the aggrrow */ - aggrrowintstart = i; + /* remember start of implied integer variables in the aggrrow */ + aggrrowimplintstart = i; /* perform bound substitution for continuous variables */ - for( i = 0; i < aggrrowintstart; ++i ) + for( i = 0; i < aggrrowimplintstart; ++i ) { int v = cutinds[i]; @@ -3155,14 +3158,78 @@ SCIP_RETCODE cutsTransformMIR( } } + /* remove implied integer variables that now have a zero coefficient due to variable bound usage of continuous + * variables and determine the bound to use for the implied integer variables that are left */ + while(i < *nnz && cutinds[i] >= firstimplvar) + { + assert(SCIPvarGetType(vars[cutinds[i]]) == SCIP_VARTYPE_IMPLINT); + SCIP_Real QUAD(coef); + int v = cutinds[i]; + + QUAD_ARRAY_LOAD(coef, cutcoefs, v); + + /* due to variable bound usage for the continuous variables cancellation may have occurred */ + if( EPSZ(QUAD_TO_DBL(coef), QUAD_EPSILON) ) + { + QUAD_ASSIGN(coef, 0.0); + QUAD_ARRAY_STORE(cutcoefs, v, coef); + --(*nnz); + cutinds[i] = cutinds[*nnz]; + /* do not increase i, since last element is copied to the i-th position */ + continue; + } + + /* determine the best bounds for the implied integer variable. */ + SCIP_CALL( determineBestBounds(scip, vars[v], sol, boundswitch, usevbds ? 2 : 0, allowlocal, fixintegralrhs, + ignoresol, boundsfortrans, boundtypesfortrans, + bestlbs + i, bestubs + i, bestlbtypes + i, bestubtypes + i, selectedbounds + i, freevariable) ); + + /* increase i */ + ++i; + + if( *freevariable ) + goto TERMINATE; + + } + + aggrrowintstart = i; + /* now perform the bound substitution on the implied integer variables */ + for( i = aggrrowimplintstart; i < aggrrowintstart; ++i ) + { + int v = cutinds[i]; + + if( selectedbounds[i] == SCIP_BOUNDTYPE_LOWER ) + { + assert(!SCIPisInfinity(scip, -bestlbs[i])); + + /* use lower bound as transformation bound: x'_j := x_j - lb_j */ + boundtype[i] = bestlbtypes[i]; + varsign[i] = +1; + + performBoundSubstitution(scip, cutinds, cutcoefs, QUAD(cutrhs), nnz, varsign[i], boundtype[i], bestlbs[i], v, localbdsused); + } + else + { + assert(!SCIPisInfinity(scip, bestubs[i])); + + /* use upper bound as transformation bound: x'_j := ub_j - x_j */ + boundtype[i] = bestubtypes[i]; + varsign[i] = -1; + + performBoundSubstitution(scip, cutinds, cutcoefs, QUAD(cutrhs), nnz, varsign[i], boundtype[i], bestubs[i], v, localbdsused); + } + + } + /* remove integral variables that now have a zero coefficient due to variable bound usage of continuous variables - * and determine the bound to use for the integer variables that are left + * and implied integer variables, and determine the bound to use for the integer variables that are left */ while( i < *nnz ) { SCIP_Real QUAD(coef); int v = cutinds[i]; - assert(cutinds[i] < firstcontvar); + assert(cutinds[i] < firstimplvar); + assert(SCIPvarGetType(vars[v]) == SCIP_VARTYPE_INTEGER || SCIPvarGetType(vars[v]) == SCIP_VARTYPE_BINARY); QUAD_ARRAY_LOAD(coef, cutcoefs, v); @@ -3219,6 +3286,10 @@ SCIP_RETCODE cutsTransformMIR( } } + if(aggrrowimplintstart != 0 && aggrrowintstart != aggrrowimplintstart && aggrrowintstart != *nnz) + { + int pause = 3; + } if( fixintegralrhs ) { SCIP_Real f0; @@ -3441,7 +3512,7 @@ SCIP_RETCODE cutsRoundMIR( * (due to sorting in cutsTransformMIR the ordering is continuous before integral) */ - firstcontvar = SCIPgetNVars(scip) - SCIPgetNContVars(scip) - SCIPgetNImplVars(scip); + firstcontvar = SCIPgetNVars(scip) - SCIPgetNContVars(scip); vars = SCIPgetVars(scip); #ifndef NDEBUG /*in debug mode check that all continuous variables of the aggrrow come before the integral variables */ @@ -3470,6 +3541,7 @@ SCIP_RETCODE cutsRoundMIR( assert(var != NULL); assert(SCIPvarGetProbindex(var) == v); assert(varsign[i] == +1 || varsign[i] == -1); + assert(SCIPvarIsIntegral(var)); /* calculate the coefficient in the retransformed cut */ { @@ -3510,44 +3582,93 @@ SCIP_RETCODE cutsRoundMIR( QUAD_ARRAY_STORE(cutcoefs, v, cutaj); /* integral var uses standard bound */ - assert(boundtype[i] < 0); - - /* move the constant term -a~_j * lb_j == -a^_j * lb_j , or a~_j * ub_j == -a^_j * ub_j to the rhs */ - if( varsign[i] == +1 ) + if(boundtype[i] < 0) { - /* lower bound was used */ - if( boundtype[i] == -1 ) + /* move the constant term -a~_j * lb_j == -a^_j * lb_j , or a~_j * ub_j == -a^_j * ub_j to the rhs */ + if( varsign[i] == +1 ) { - assert(!SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var))); - SCIPquadprecProdQD(tmp, cutaj, SCIPvarGetLbGlobal(var)); - SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); /* rhs += cutaj * SCIPvarGetLbGlobal(var) */ + /* lower bound was used */ + if( boundtype[i] == -1 ) + { + assert(!SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var))); + SCIPquadprecProdQD(tmp, cutaj, SCIPvarGetLbGlobal(var)); + SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); /* rhs += cutaj * SCIPvarGetLbGlobal(var) */ + } + else + { + assert(!SCIPisInfinity(scip, -SCIPvarGetLbLocal(var))); + SCIPquadprecProdQD(tmp, cutaj, SCIPvarGetLbLocal(var)); + SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); /* rhs += cutaj * SCIPvarGetLbLocal(var) */ + } } else { - assert(!SCIPisInfinity(scip, -SCIPvarGetLbLocal(var))); - SCIPquadprecProdQD(tmp, cutaj, SCIPvarGetLbLocal(var)); - SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); /* rhs += cutaj * SCIPvarGetLbLocal(var) */ + /* upper bound was used */ + if( boundtype[i] == -1 ) + { + assert(!SCIPisInfinity(scip, SCIPvarGetUbGlobal(var))); + SCIPquadprecProdQD(tmp, cutaj, SCIPvarGetUbGlobal(var)); + SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); /* rhs += cutaj * SCIPvarGetUbGlobal(var) */ + } + else + { + assert(!SCIPisInfinity(scip, SCIPvarGetUbLocal(var))); + SCIPquadprecProdQD(tmp, cutaj, SCIPvarGetUbLocal(var)); + SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); /* rhs += cutaj * SCIPvarGetUbLocal(var) */ + } } } else { - /* upper bound was used */ - if( boundtype[i] == -1 ) + SCIP_VAR** vbz; + SCIP_Real* vbb; + SCIP_Real* vbd; + SCIP_Real QUAD(zcoef); + int vbidx; + int zidx; + + /* variable bound */ + vbidx = boundtype[i]; + + /* change mirrhs and cutaj of integer variable z_j of variable bound */ + if( varsign[i] == +1 ) { - assert(!SCIPisInfinity(scip, SCIPvarGetUbGlobal(var))); - SCIPquadprecProdQD(tmp, cutaj, SCIPvarGetUbGlobal(var)); - SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); /* rhs += cutaj * SCIPvarGetUbGlobal(var) */ + /* variable lower bound was used */ + assert(0 <= vbidx && vbidx < SCIPvarGetNVlbs(var)); + vbz = SCIPvarGetVlbVars(var); + vbb = SCIPvarGetVlbCoefs(var); + vbd = SCIPvarGetVlbConstants(var); } else { - assert(!SCIPisInfinity(scip, SCIPvarGetUbLocal(var))); - SCIPquadprecProdQD(tmp, cutaj, SCIPvarGetUbLocal(var)); - SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); /* rhs += cutaj * SCIPvarGetUbLocal(var) */ + /* variable upper bound was used */ + assert(0 <= vbidx && vbidx < SCIPvarGetNVubs(var)); + vbz = SCIPvarGetVubVars(var); + vbb = SCIPvarGetVubCoefs(var); + vbd = SCIPvarGetVubConstants(var); } + assert(SCIPvarIsActive(vbz[vbidx])); + zidx = SCIPvarGetProbindex(vbz[vbidx]); + assert(0 <= zidx && zidx < firstcontvar); + + SCIPquadprecProdQD(tmp, cutaj, vbd[vbidx]); + SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); + + SCIPquadprecProdQD(tmp, cutaj, vbb[vbidx]); + QUAD_ARRAY_LOAD(zcoef, cutcoefs, zidx); + + /* update sparsity pattern */ + if( QUAD_HI(zcoef) == 0.0 ) + cutinds[(*nnz)++] = zidx; + + SCIPquadprecSumQQ(zcoef, zcoef, -tmp); + QUAD_HI(zcoef) = NONZERO(QUAD_HI(zcoef)); + QUAD_ARRAY_STORE(cutcoefs, zidx, zcoef); + assert(QUAD_HI(zcoef) != 0.0); } } - /* now process the continuous variables; postpone deletion of zeros untill all continuous variables have been processed */ + /* now process the continuous variables; postpone deletion of zeros until all continuous variables have been processed */ ndelcontvars = 0; while( i >= ndelcontvars ) { From 6b61ee1e7eb3d32612783fb39ee172ab284177dd Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Mon, 26 Aug 2024 11:19:06 +0200 Subject: [PATCH 47/63] Revert performance-tuning commits for implied integrality detection --- src/scip/cuts.c | 183 +++++++------------------------------- src/scip/cutsel_hybrid.c | 15 +--- src/scip/presol_implint.c | 12 +-- src/scip/sepa_mcf.c | 2 +- src/scip/sepa_mixing.c | 2 +- 5 files changed, 37 insertions(+), 177 deletions(-) diff --git a/src/scip/cuts.c b/src/scip/cuts.c index 870440d57c..b2503c0541 100644 --- a/src/scip/cuts.c +++ b/src/scip/cuts.c @@ -2626,7 +2626,7 @@ SCIP_RETCODE findBestLb( *simplebound = *bestlb; - if( usevbds && (SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPvarGetType(var) == SCIP_VARTYPE_IMPLINT)) + if( usevbds && SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS ) { SCIP_Real bestvlb; int bestvlbidx; @@ -2687,7 +2687,7 @@ SCIP_RETCODE findBestUb( *simplebound = *bestub; - if( usevbds && (SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPvarGetType(var) == SCIP_VARTYPE_IMPLINT)) + if( usevbds && SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS ) { SCIP_Real bestvub; int bestvubidx; @@ -3084,11 +3084,9 @@ SCIP_RETCODE cutsTransformMIR( int* bestubtypes; SCIP_BOUNDTYPE* selectedbounds; int i; - int aggrrowimplintstart; int aggrrowintstart; int nvars; int firstcontvar; - int firstimplvar; SCIP_VAR** vars; assert(varsign != NULL); @@ -3115,7 +3113,6 @@ SCIP_RETCODE cutsTransformMIR( vars = SCIPgetVars(scip); nvars = SCIPgetNVars(scip); firstcontvar = nvars - SCIPgetNContVars(scip); - firstimplvar = nvars - SCIPgetNContVars(scip) - SCIPgetNImplVars(scip); /* determine the best bounds for the continuous variables */ for( i = 0; i < *nnz && cutinds[i] >= firstcontvar; ++i ) @@ -3128,73 +3125,11 @@ SCIP_RETCODE cutsTransformMIR( goto TERMINATE; } - /* remember start of implied integer variables in the aggrrow */ - aggrrowimplintstart = i; + /* remember start of integer variables in the aggrrow */ + aggrrowintstart = i; /* perform bound substitution for continuous variables */ - for( i = 0; i < aggrrowimplintstart; ++i ) - { - int v = cutinds[i]; - - if( selectedbounds[i] == SCIP_BOUNDTYPE_LOWER ) - { - assert(!SCIPisInfinity(scip, -bestlbs[i])); - - /* use lower bound as transformation bound: x'_j := x_j - lb_j */ - boundtype[i] = bestlbtypes[i]; - varsign[i] = +1; - - performBoundSubstitution(scip, cutinds, cutcoefs, QUAD(cutrhs), nnz, varsign[i], boundtype[i], bestlbs[i], v, localbdsused); - } - else - { - assert(!SCIPisInfinity(scip, bestubs[i])); - - /* use upper bound as transformation bound: x'_j := ub_j - x_j */ - boundtype[i] = bestubtypes[i]; - varsign[i] = -1; - - performBoundSubstitution(scip, cutinds, cutcoefs, QUAD(cutrhs), nnz, varsign[i], boundtype[i], bestubs[i], v, localbdsused); - } - } - - /* remove implied integer variables that now have a zero coefficient due to variable bound usage of continuous - * variables and determine the bound to use for the implied integer variables that are left */ - while(i < *nnz && cutinds[i] >= firstimplvar) - { - assert(SCIPvarGetType(vars[cutinds[i]]) == SCIP_VARTYPE_IMPLINT); - SCIP_Real QUAD(coef); - int v = cutinds[i]; - - QUAD_ARRAY_LOAD(coef, cutcoefs, v); - - /* due to variable bound usage for the continuous variables cancellation may have occurred */ - if( EPSZ(QUAD_TO_DBL(coef), QUAD_EPSILON) ) - { - QUAD_ASSIGN(coef, 0.0); - QUAD_ARRAY_STORE(cutcoefs, v, coef); - --(*nnz); - cutinds[i] = cutinds[*nnz]; - /* do not increase i, since last element is copied to the i-th position */ - continue; - } - - /* determine the best bounds for the implied integer variable. */ - SCIP_CALL( determineBestBounds(scip, vars[v], sol, boundswitch, usevbds ? 2 : 0, allowlocal, fixintegralrhs, - ignoresol, boundsfortrans, boundtypesfortrans, - bestlbs + i, bestubs + i, bestlbtypes + i, bestubtypes + i, selectedbounds + i, freevariable) ); - - /* increase i */ - ++i; - - if( *freevariable ) - goto TERMINATE; - - } - - aggrrowintstart = i; - /* now perform the bound substitution on the implied integer variables */ - for( i = aggrrowimplintstart; i < aggrrowintstart; ++i ) + for( i = 0; i < aggrrowintstart; ++i ) { int v = cutinds[i]; @@ -3218,18 +3153,16 @@ SCIP_RETCODE cutsTransformMIR( performBoundSubstitution(scip, cutinds, cutcoefs, QUAD(cutrhs), nnz, varsign[i], boundtype[i], bestubs[i], v, localbdsused); } - } /* remove integral variables that now have a zero coefficient due to variable bound usage of continuous variables - * and implied integer variables, and determine the bound to use for the integer variables that are left + * and determine the bound to use for the integer variables that are left */ while( i < *nnz ) { SCIP_Real QUAD(coef); int v = cutinds[i]; - assert(cutinds[i] < firstimplvar); - assert(SCIPvarGetType(vars[v]) == SCIP_VARTYPE_INTEGER || SCIPvarGetType(vars[v]) == SCIP_VARTYPE_BINARY); + assert(cutinds[i] < firstcontvar); QUAD_ARRAY_LOAD(coef, cutcoefs, v); @@ -3286,10 +3219,6 @@ SCIP_RETCODE cutsTransformMIR( } } - if(aggrrowimplintstart != 0 && aggrrowintstart != aggrrowimplintstart && aggrrowintstart != *nnz) - { - int pause = 3; - } if( fixintegralrhs ) { SCIP_Real f0; @@ -3541,7 +3470,6 @@ SCIP_RETCODE cutsRoundMIR( assert(var != NULL); assert(SCIPvarGetProbindex(var) == v); assert(varsign[i] == +1 || varsign[i] == -1); - assert(SCIPvarIsIntegral(var)); /* calculate the coefficient in the retransformed cut */ { @@ -3582,93 +3510,44 @@ SCIP_RETCODE cutsRoundMIR( QUAD_ARRAY_STORE(cutcoefs, v, cutaj); /* integral var uses standard bound */ - if(boundtype[i] < 0) + assert(boundtype[i] < 0); + + /* move the constant term -a~_j * lb_j == -a^_j * lb_j , or a~_j * ub_j == -a^_j * ub_j to the rhs */ + if( varsign[i] == +1 ) { - /* move the constant term -a~_j * lb_j == -a^_j * lb_j , or a~_j * ub_j == -a^_j * ub_j to the rhs */ - if( varsign[i] == +1 ) + /* lower bound was used */ + if( boundtype[i] == -1 ) { - /* lower bound was used */ - if( boundtype[i] == -1 ) - { - assert(!SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var))); - SCIPquadprecProdQD(tmp, cutaj, SCIPvarGetLbGlobal(var)); - SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); /* rhs += cutaj * SCIPvarGetLbGlobal(var) */ - } - else - { - assert(!SCIPisInfinity(scip, -SCIPvarGetLbLocal(var))); - SCIPquadprecProdQD(tmp, cutaj, SCIPvarGetLbLocal(var)); - SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); /* rhs += cutaj * SCIPvarGetLbLocal(var) */ - } + assert(!SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var))); + SCIPquadprecProdQD(tmp, cutaj, SCIPvarGetLbGlobal(var)); + SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); /* rhs += cutaj * SCIPvarGetLbGlobal(var) */ } else { - /* upper bound was used */ - if( boundtype[i] == -1 ) - { - assert(!SCIPisInfinity(scip, SCIPvarGetUbGlobal(var))); - SCIPquadprecProdQD(tmp, cutaj, SCIPvarGetUbGlobal(var)); - SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); /* rhs += cutaj * SCIPvarGetUbGlobal(var) */ - } - else - { - assert(!SCIPisInfinity(scip, SCIPvarGetUbLocal(var))); - SCIPquadprecProdQD(tmp, cutaj, SCIPvarGetUbLocal(var)); - SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); /* rhs += cutaj * SCIPvarGetUbLocal(var) */ - } + assert(!SCIPisInfinity(scip, -SCIPvarGetLbLocal(var))); + SCIPquadprecProdQD(tmp, cutaj, SCIPvarGetLbLocal(var)); + SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); /* rhs += cutaj * SCIPvarGetLbLocal(var) */ } } else { - SCIP_VAR** vbz; - SCIP_Real* vbb; - SCIP_Real* vbd; - SCIP_Real QUAD(zcoef); - int vbidx; - int zidx; - - /* variable bound */ - vbidx = boundtype[i]; - - /* change mirrhs and cutaj of integer variable z_j of variable bound */ - if( varsign[i] == +1 ) + /* upper bound was used */ + if( boundtype[i] == -1 ) { - /* variable lower bound was used */ - assert(0 <= vbidx && vbidx < SCIPvarGetNVlbs(var)); - vbz = SCIPvarGetVlbVars(var); - vbb = SCIPvarGetVlbCoefs(var); - vbd = SCIPvarGetVlbConstants(var); + assert(!SCIPisInfinity(scip, SCIPvarGetUbGlobal(var))); + SCIPquadprecProdQD(tmp, cutaj, SCIPvarGetUbGlobal(var)); + SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); /* rhs += cutaj * SCIPvarGetUbGlobal(var) */ } else { - /* variable upper bound was used */ - assert(0 <= vbidx && vbidx < SCIPvarGetNVubs(var)); - vbz = SCIPvarGetVubVars(var); - vbb = SCIPvarGetVubCoefs(var); - vbd = SCIPvarGetVubConstants(var); + assert(!SCIPisInfinity(scip, SCIPvarGetUbLocal(var))); + SCIPquadprecProdQD(tmp, cutaj, SCIPvarGetUbLocal(var)); + SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); /* rhs += cutaj * SCIPvarGetUbLocal(var) */ } - assert(SCIPvarIsActive(vbz[vbidx])); - zidx = SCIPvarGetProbindex(vbz[vbidx]); - assert(0 <= zidx && zidx < firstcontvar); - - SCIPquadprecProdQD(tmp, cutaj, vbd[vbidx]); - SCIPquadprecSumQQ(*cutrhs, *cutrhs, tmp); - - SCIPquadprecProdQD(tmp, cutaj, vbb[vbidx]); - QUAD_ARRAY_LOAD(zcoef, cutcoefs, zidx); - - /* update sparsity pattern */ - if( QUAD_HI(zcoef) == 0.0 ) - cutinds[(*nnz)++] = zidx; - - SCIPquadprecSumQQ(zcoef, zcoef, -tmp); - QUAD_HI(zcoef) = NONZERO(QUAD_HI(zcoef)); - QUAD_ARRAY_STORE(cutcoefs, zidx, zcoef); - assert(QUAD_HI(zcoef) != 0.0); } } - /* now process the continuous variables; postpone deletion of zeros until all continuous variables have been processed */ + /* now process the continuous variables; postpone deletion of zeros untill all continuous variables have been processed */ ndelcontvars = 0; while( i >= ndelcontvars ) { @@ -4392,7 +4271,7 @@ SCIP_RETCODE SCIPcutGenerationHeuristicCMIR( *success = FALSE; nvars = SCIPgetNVars(scip); - firstcontvar = nvars - SCIPgetNContVars(scip) - SCIPgetNImplVars(scip); + firstcontvar = nvars - SCIPgetNContVars(scip); vars = SCIPgetVars(scip); /* allocate temporary memory */ @@ -5419,7 +5298,7 @@ SCIP_RETCODE determineBoundForSNF( bestlb[varposinrow] = bestslb[varposinrow]; bestlbtype[varposinrow] = bestslbtype[varposinrow]; - if( SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPvarGetType(var) == SCIP_VARTYPE_IMPLINT) + if( SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS ) { SCIP_Real bestvlb; int bestvlbidx; @@ -5441,7 +5320,7 @@ SCIP_RETCODE determineBoundForSNF( bestub[varposinrow] = bestsub[varposinrow]; bestubtype[varposinrow] = bestsubtype[varposinrow]; - if( SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPvarGetType(var) == SCIP_VARTYPE_IMPLINT ) + if( SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS ) { SCIP_Real bestvub; int bestvubidx; diff --git a/src/scip/cutsel_hybrid.c b/src/scip/cutsel_hybrid.c index e445bd7abf..3d7796e81a 100644 --- a/src/scip/cutsel_hybrid.c +++ b/src/scip/cutsel_hybrid.c @@ -111,20 +111,7 @@ SCIP_Real scoring( SCIP_Real efficacy; if( intsupportweight > 0.0 ) - { - int nIntegral = 0; - SCIP_COL ** rowcols = SCIProwGetCols(cuts[i]); - for( int j = 0; j < SCIProwGetNNonz(cuts[i]); ++j ) - { - SCIP_VAR * var = SCIPcolGetVar(rowcols[j]); - if( SCIPvarGetType(var) == SCIP_VARTYPE_BINARY || SCIPvarGetType(var) == SCIP_VARTYPE_INTEGER){ - ++nIntegral; - } - } - double fraction = (SCIP_Real) nIntegral / (SCIP_Real) SCIProwGetNNonz(cuts[i]); - intsupport = intsupportweight * fraction; -// intsupport = intsupportweight * SCIPgetRowNumIntCols(scip, cuts[i]) / (SCIP_Real) SCIProwGetNNonz(cuts[i]); - } + intsupport = intsupportweight * SCIPgetRowNumIntCols(scip, cuts[i]) / (SCIP_Real) SCIProwGetNNonz(cuts[i]); else intsupport = 0.0; diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index 9e0f12bb3a..b7c2acaca6 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -357,8 +357,6 @@ SCIP_RETCODE findImpliedIntegers( SCIP_NETMATDEC * transdec = NULL; SCIP_CALL(SCIPnetmatdecCreate(SCIPblkmem(scip),&transdec,comp->nmatrixcols,comp->nmatrixrows)); - int networkcomponents = 0; - int transposednetworkcomponents = 0; int planarcomponents = 0; int goodcomponents = 0; int nbadnumerics = 0; @@ -448,6 +446,7 @@ SCIP_RETCODE findImpliedIntegers( if( !componentnetwork ) { SCIPnetmatdecRemoveComponent(dec,&comp->componentrows[startrow], nrows, &comp->componentcols[startcol], ncols); + ++nnonnetwork; } SCIP_Bool componenttransnetwork = TRUE; @@ -501,10 +500,6 @@ SCIP_RETCODE findImpliedIntegers( ++goodcomponents; if(componentnetwork && componenttransnetwork){ ++planarcomponents; - }else if(componentnetwork){ - ++networkcomponents; - }else{ - ++transposednetworkcomponents; } for( int i = startcol; i < startcol + ncols; ++i ) { @@ -518,9 +513,8 @@ SCIP_RETCODE findImpliedIntegers( } } SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH,NULL, - "implied integer components: %d (%d planar, %d network, %d tr. network) / %d (disqualified: %d by integrality, %d by numerics, %d not network) \n", - goodcomponents,planarcomponents,networkcomponents,transposednetworkcomponents, - comp->ncomponents,nbadintegrality,nbadnumerics,nnonnetwork); + "implied integer components: %d (%d planar) / %d (disqualified: %d by integrality, %d by numerics, %d not network) \n", + goodcomponents,planarcomponents,comp->ncomponents,nbadintegrality,nbadnumerics,nnonnetwork); SCIPfreeBufferArray(scip,&tempIdxArray); SCIPfreeBufferArray(scip,&tempValArray); diff --git a/src/scip/sepa_mcf.c b/src/scip/sepa_mcf.c index 73fede25fd..b0a4a4bccd 100644 --- a/src/scip/sepa_mcf.c +++ b/src/scip/sepa_mcf.c @@ -998,7 +998,7 @@ SCIP_RETCODE extractFlowRows( * integer: score +500 * binary: score +100 */ - if( ncontvars == rowlen || nimplintvars == rowlen) + if( ncontvars == rowlen ) flowrowscores[r] += 1000.0; else if( nintvars + nimplintvars == rowlen ) flowrowscores[r] += 500.0; diff --git a/src/scip/sepa_mixing.c b/src/scip/sepa_mixing.c index cab60f2dba..3bc1073a44 100644 --- a/src/scip/sepa_mixing.c +++ b/src/scip/sepa_mixing.c @@ -270,7 +270,7 @@ SCIP_RETCODE separateCuts( else { /* only generate cuts based on continuous variables */ - firstvar = SCIPgetNBinVars(scip) + SCIPgetNIntVars(scip) ; + firstvar = SCIPgetNBinVars(scip) + SCIPgetNIntVars(scip) + SCIPgetNImplVars(scip); } nvars = SCIPgetNVars(scip); if( firstvar == nvars ) From 3f6c11588e6e402e4d511d314da9331f1d3a3160 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Thu, 29 Aug 2024 09:03:03 +0200 Subject: [PATCH 48/63] Add changelog entry for implint plugin --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 721bcb425d..e3a6202ad3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,7 @@ Features - added a new separator sepa_multilinear to generate flower cuts from AND constraints nonlinear product expressions. - added functionality to deal with hypergraphs by means of efficient access to vertices, edges and intersections edges. - added support for (transposed) network matrix detection in pub_network.h +- added a new presolver presol_implint which detects implied integers by detecting (transposed) network submatrices in the problem. For now, this plugin is disabled by default. Performance improvements ------------------------ From 147001fe9bfa65b677b52853ce1073b52eaad608 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Thu, 29 Aug 2024 14:07:25 +0200 Subject: [PATCH 49/63] Make implint presolver use disjoint set datastructures for component detection --- src/scip/presol_implint.c | 316 +++++++++++++++++++++++++------------- 1 file changed, 210 insertions(+), 106 deletions(-) diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index b7c2acaca6..741f711a81 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -41,36 +41,36 @@ #include "scip/pub_message.h" #include "scip/pub_misc.h" #include "scip/pub_network.h" +#include "scip/pub_presol.h" #include "scip/pub_var.h" #include "scip/scip_general.h" #include "scip/scip_message.h" #include "scip/scip_mem.h" #include "scip/scip_nlp.h" #include "scip/scip_numerics.h" +#include "scip/scip_param.h" #include "scip/scip_presol.h" #include "scip/scip_pricer.h" +#include "scip/scip_prob.h" #include "scip/scip_probing.h" #include "scip/scip_timing.h" #include "scip/scip_var.h" - #define PRESOL_NAME "implint" #define PRESOL_DESC "detects implicit integer variables" -#define PRESOL_PRIORITY 100 /**< priority of the presolver (>= 0: before, < 0: after constraint handlers); combined with propagators */ -#define PRESOL_MAXROUNDS -1 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */ +#define PRESOL_PRIORITY 100 /**< priority of the presolver (>= 0: before, < 0: after constraint handlers); combined with propagators */ +#define PRESOL_MAXROUNDS -1 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */ #define PRESOL_TIMING SCIP_PRESOLTIMING_EXHAUSTIVE /* timing of the presolver (fast, medium, or exhaustive) */ - -/* - * Data structures - */ - -/* TODO: fill in the necessary presolver data */ +#define DEFAULT_CONVERTINTEGERS FALSE +#define DEFAULT_COLUMNROWRATIO 50.0 /** presolver data */ struct SCIP_PresolData { - int temp; //TODO fix to stop complaining compiler + SCIP_Bool convertintegers; /**< Should we detect implied integrality of columns that are integer? */ + double columnrowratio; /**< Use the network row addition algorithm when the column to row ratio becomes larger than + * this threshold. Otherwise, use the column addition algorithm. */ }; @@ -163,6 +163,48 @@ void freeMatrixInfo( SCIPfreeBlockMemory(scip, pmatrixcomponents); } +static int disjointSetFind(int * disjointset, int index){ + assert(disjointset); + int current = index; + int next; + //traverse down tree + while( (next = disjointset[current]) >= 0 ){ + current = next; + } + int root = current; + + //compress indices along path + current = index; + while( (next = disjointset[current]) >= 0 ){ + disjointset[current] = root; + current = next; + } + return root; +} + +static int disjointSetMerge(int * disjointset, int first, int second){ + assert(disjointset); + assert(disjointset[first] < 0); + assert(disjointset[second] < 0); + assert(first != second);//We cannot merge a node into itself + + //The rank is stored as a negative number: we decrement it making the negative number larger. + //We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement. + int firstRank = disjointset[first]; + int secondRank = disjointset[second]; + if( firstRank > secondRank ) + { + SCIPswapInts(&first, &second); + } + //first becomes representative + disjointset[second] = first; + if( firstRank == secondRank ) + { + --disjointset[first]; + } + return first; +} + static SCIP_RETCODE computeContinuousComponents( SCIP * scip, @@ -170,82 +212,127 @@ SCIP_RETCODE computeContinuousComponents( MATRIX_COMPONENTS* comp ) { - int currentcomponent = 0; - int rowindex = 0; - int colindex = 0; /* We let rows and columns share an index by mapping column i to index nrows + i*/ - int* dfsstack = NULL; - int ndfsstack = 0; - SCIP_CALL(SCIPallocBufferArray(scip, &dfsstack, comp->nmatrixcols + comp->nmatrixrows)); + int* disjointset = NULL; + SCIP_CALL(SCIPallocBufferArray(scip, &disjointset, comp->nmatrixcols + comp->nmatrixrows)); + //First n entries belong to columns, last entries to rows + for( int i = 0; i < comp->nmatrixcols + comp->nmatrixrows; ++i ) + { + disjointset[i] = -1; + } - for( int i = 0; i < comp->nmatrixcols; ++i ) + for( int col = 0; col < comp->nmatrixcols; ++col ) { - /* Check if earlier DFS already found column */ - if( comp->colcomponent[i] != -1 || comp->coltype[i] != SCIP_VARTYPE_CONTINUOUS){ + if( comp->coltype[col] != SCIP_VARTYPE_CONTINUOUS){ continue; } - /* If not, then we have a new component, and perform a DFS to find all connected columns and rows */ - comp->componentrowstart[currentcomponent] = rowindex; - comp->componentcolstart[currentcomponent] = colindex; - - assert(ndfsstack == 0); - /* Push column to DFS search */ - dfsstack[ndfsstack] = comp->nmatrixrows + i; - ++ndfsstack; - comp->colcomponent[i] = currentcomponent; - - while( ndfsstack != 0 ){ - --ndfsstack; - int ind = dfsstack[ndfsstack]; - /* process column or row, adding new connected rows/columns to the dfs stack */ - if( ind >= comp->nmatrixrows ){ - int column = ind - comp->nmatrixrows; - assert(comp->coltype[column] == SCIP_VARTYPE_CONTINUOUS); - comp->componentcols[colindex] = column; - ++colindex; - - /* Push connected rows to the dfs stack */ - int colnnonzs = SCIPmatrixGetColNNonzs(matrix, column); - int* colrows = SCIPmatrixGetColIdxPtr(matrix, column); - for( int j = 0; j < colnnonzs; ++j ) - { - int row = colrows[j]; - if( comp->rowcomponent[row] == -1 ){ - dfsstack[ndfsstack] = row; - ++ndfsstack; - comp->rowcomponent[row] = currentcomponent; - } - } + int colnnonzs = SCIPmatrixGetColNNonzs(matrix, col); + int* colrows = SCIPmatrixGetColIdxPtr(matrix, col); + + int colrep = disjointSetFind(disjointset,col); + for( int i = 0; i < colnnonzs; ++i ) + { + int colrow = colrows[i]; + int index = colrow + comp->nmatrixcols; + int rowrep = disjointSetFind(disjointset,index); + if(colrep != rowrep){ + colrep = disjointSetMerge(disjointset,colrep,rowrep); } - else - { - int row = ind; - comp->componentrows[rowindex] = row; - ++rowindex; + } + } - /* Push any connected continuous columns on the dfs search stack */ - int rownnonzs = SCIPmatrixGetRowNNonzs(matrix,row); - int* rowcols = SCIPmatrixGetRowIdxPtr(matrix,row); - for( int j = 0; j < rownnonzs; ++j ) - { - int col = rowcols[j]; - if( comp->colcomponent[col] == -1 && comp->coltype[col] == SCIP_VARTYPE_CONTINUOUS){ - comp->colcomponent[col] = currentcomponent; - dfsstack[ndfsstack] = comp->nmatrixrows + col; - ++ndfsstack; - } - } + /** Now, fill in the relevant data. */ + int * representativecomponent; + SCIP_CALL(SCIPallocBufferArray(scip, &representativecomponent, comp->nmatrixcols + comp->nmatrixrows)); + for( int i = 0; i < comp->nmatrixcols + comp->nmatrixrows; ++i ) + { + representativecomponent[i] = -1; + } + comp->ncomponents = 0; + for( int col = 0; col < comp->nmatrixcols; ++col ) + { + if( comp->coltype[col] != SCIP_VARTYPE_CONTINUOUS ) + { + continue; + } + int colroot = disjointSetFind(disjointset,col); + int component = representativecomponent[colroot]; + if( component < 0){ + //add new component + component = comp->ncomponents; + representativecomponent[colroot] = component; + comp->ncomponentcols[component] = 0; + comp->ncomponentrows[component] = 0; + ++comp->ncomponents; + } + comp->colcomponent[col] = component; + ++comp->ncomponentcols[component]; + } + for( int row = 0; row < comp->nmatrixrows; ++row ) + { + int rowroot = disjointSetFind(disjointset,row + comp->nmatrixcols); + int component = representativecomponent[rowroot]; + if( component < 0){ + //Any rows that have roots that we have not seen yet are rows that have no continuous columns + //We can safely skip these for finding the continuous connected components + continue; + } + comp->rowcomponent[row] = component; + ++comp->ncomponentrows[component]; + } + if(comp->ncomponents != 0){ + comp->componentrowstart[0] = 0; + comp->componentcolstart[0] = 0; + for( int i = 1; i < comp->ncomponents; ++i ) + { + comp->componentrowstart[i] = comp->componentrowstart[i-1] + comp->ncomponentrows[i-1]; + comp->componentcolstart[i] = comp->componentcolstart[i-1] + comp->ncomponentcols[i-1]; + } + int * componentnextrowindex; + int * componentnextcolindex; + SCIP_CALL( SCIPallocBufferArray(scip,&componentnextrowindex,comp->ncomponents) ); + SCIP_CALL( SCIPallocBufferArray(scip,&componentnextcolindex,comp->ncomponents) ); + for( int i = 0; i < comp->ncomponents; ++i ) + { + componentnextcolindex[i] = 0; + componentnextrowindex[i] = 0; + } + + for( int i = 0; i < comp->nmatrixcols; ++i ) + { + int component = comp->colcomponent[i]; + if(component < 0){ + continue; } + int index = comp->componentcolstart[component] + componentnextcolindex[component]; + comp->componentcols[index] = i; + ++componentnextcolindex[component]; + } + for( int i = 0; i < comp->nmatrixrows; ++i ) + { + int component = comp->rowcomponent[i]; + if(component < 0){ + continue; + } + int index = comp->componentrowstart[component] + componentnextrowindex[component]; + comp->componentrows[index] = i; + ++componentnextrowindex[component]; } - /* We are done with DFS for this component, save the sizes */ - comp->ncomponentrows[currentcomponent] = rowindex - comp->componentrowstart[currentcomponent]; - comp->ncomponentcols[currentcomponent] = colindex - comp->componentcolstart[currentcomponent]; - ++currentcomponent; - ++comp->ncomponents; - assert(currentcomponent == comp->ncomponents); +#ifndef NDEBUG + for( int i = 0; i < comp->ncomponents; ++i ) + { + assert(componentnextrowindex[i] == comp->ncomponentrows[i]); + assert(componentnextcolindex[i] == comp->ncomponentcols[i]); + } +#endif + + SCIPfreeBufferArray(scip,&componentnextcolindex); + SCIPfreeBufferArray(scip,&componentnextrowindex); } - SCIPfreeBufferArray(scip,&dfsstack); + + SCIPfreeBufferArray(scip,&representativecomponent); + SCIPfreeBufferArray(scip,&disjointset); return SCIP_OKAY; } @@ -344,6 +431,7 @@ void freeMatrixStatistics( static SCIP_RETCODE findImpliedIntegers( SCIP * scip, + SCIP_PRESOLDATA* presoldata, SCIP_MATRIX* matrix, MATRIX_COMPONENTS* comp, MATRIX_STATISTICS* stats, @@ -406,11 +494,9 @@ SCIP_RETCODE findImpliedIntegers( /* We use the row-wise algorithm only if the number of columns is much larger than the number of rows. * Generally, the column-wise algorithm will be faster, but in these extreme cases, the row algorithm is faster. - * TODO: test/tune this parameter + * Only very little instances should have this at all. */ - //TODO for ~50-150 seems to be about even on neos-...-inde and neos-...-isar, test further - if(nrows * 20 < ncols){ - printf("Row addition: %f \n",(double) ncols / (double) nrows); + if( nrows * presoldata->columnrowratio < ncols){ for( int i = startrow; i < startrow + nrows && componentnetwork; ++i ) { int row = comp->componentrows[i]; @@ -432,7 +518,9 @@ SCIP_RETCODE findImpliedIntegers( SCIP_CALL( SCIPnetmatdecTryAddRow(dec,row,tempIdxArray,tempValArray,ncontnonz,&componentnetwork) ); } - }else{ + } + else + { for( int i = startcol; i < startcol + ncols && componentnetwork; ++i ) { int col = comp->componentcols[i]; @@ -446,14 +534,12 @@ SCIP_RETCODE findImpliedIntegers( if( !componentnetwork ) { SCIPnetmatdecRemoveComponent(dec,&comp->componentrows[startrow], nrows, &comp->componentcols[startcol], ncols); - ++nnonnetwork; } SCIP_Bool componenttransnetwork = TRUE; /* For the transposed matrix, the situation is exactly reversed because the row/column algorithms are swapped */ - //TODO: tune parameter - if(nrows < ncols * 20){ + if(nrows < ncols * presoldata->columnrowratio){ for( int i = startrow; i < startrow + nrows && componenttransnetwork ; ++i ) { int row = comp->componentrows[i]; @@ -476,7 +562,9 @@ SCIP_RETCODE findImpliedIntegers( SCIP_CALL( SCIPnetmatdecTryAddCol(transdec,row,tempIdxArray,tempValArray,ncontnonz, &componenttransnetwork) ); } - }else{ + } + else + { for( int i = startcol; i < startcol + ncols && componenttransnetwork; ++i ) { int col = comp->componentcols[i]; @@ -508,13 +596,12 @@ SCIP_RETCODE findImpliedIntegers( SCIP_Bool infeasible = FALSE; SCIP_CALL(SCIPchgVarType(scip,var,SCIP_VARTYPE_IMPLINT,&infeasible)); (*nchgvartypes)++; - //TODO: inform of changed variable types assert(!infeasible); } } - SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH,NULL, + SCIPverbMessage(scip, SCIP_VERBLEVEL_FULL, NULL, "implied integer components: %d (%d planar) / %d (disqualified: %d by integrality, %d by numerics, %d not network) \n", - goodcomponents,planarcomponents,comp->ncomponents,nbadintegrality,nbadnumerics,nnonnetwork); + goodcomponents, planarcomponents, comp->ncomponents, nbadintegrality, nbadnumerics, nnonnetwork); SCIPfreeBufferArray(scip,&tempIdxArray); SCIPfreeBufferArray(scip,&tempValArray); @@ -547,18 +634,21 @@ SCIP_DECL_PRESOLCOPY(presolCopyImplint) /** destructor of presolver to free user data (called when SCIP is exiting) */ -#if 0 + static SCIP_DECL_PRESOLFREE(presolFreeImplint) -{ /*lint --e{715}*/ - SCIPerrorMessage("method of implint presolver not implemented yet\n"); - SCIPABORT(); /*lint --e{527}*/ +{ + SCIP_PRESOLDATA* presoldata; + + /* free presolver data */ + presoldata = SCIPpresolGetData(presol); + assert(presoldata != NULL); + + SCIPfreeBlockMemory(scip, &presoldata); + SCIPpresolSetData(presol, NULL); return SCIP_OKAY; } -#else -#define presolFreeImplint NULL -#endif /** initialization method of presolver (called after problem was transformed) */ @@ -642,6 +732,13 @@ SCIP_DECL_PRESOLEXEC(presolExecImplint) *result = SCIP_DIDNOTFIND; + /* Exit early if there are no candidates variables to upgrade */ + SCIP_PRESOLDATA* presoldata = SCIPpresolGetData(presol); + if(!presoldata->convertintegers && SCIPgetNContVars(scip) == 0) + { + return SCIP_OKAY; + } + double starttime = SCIPgetSolvingTime(scip); SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) implied integer detection started\n", starttime); @@ -671,7 +768,7 @@ SCIP_DECL_PRESOLEXEC(presolExecImplint) { SCIPmatrixFree(scip, &matrix); } - SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, + SCIPverbMessage(scip, SCIP_VERBLEVEL_FULL, NULL, " (%.1fs) implied integer detection stopped because problem is not an MILP\n", SCIPgetSolvingTime(scip)); return SCIP_OKAY; @@ -683,7 +780,7 @@ SCIP_DECL_PRESOLEXEC(presolExecImplint) SCIP_CALL( createMatrixComponents(scip, matrix, &comp) ); SCIP_CALL( computeMatrixStatistics(scip, matrix, &stats) ); SCIP_CALL( computeContinuousComponents(scip, matrix, comp) ); - SCIP_CALL( findImpliedIntegers(scip,matrix,comp, stats, nchgvartypes) ); + SCIP_CALL( findImpliedIntegers(scip, presoldata, matrix, comp, stats, nchgvartypes) ); int afterchanged = *nchgvartypes; @@ -692,7 +789,12 @@ SCIP_DECL_PRESOLEXEC(presolExecImplint) SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) no implied integers detected (time: %.2fs)\n", endtime,endtime-starttime); *result = SCIP_DIDNOTFIND; - }else{ + } + else + { + SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, + " (%.1fs) %d implied integers detected (time: %.2fs)\n",endtime,*nchgvartypes,endtime-starttime); + *result = SCIP_SUCCESS; } freeMatrixStatistics(scip,&stats); @@ -715,14 +817,9 @@ SCIP_RETCODE SCIPincludePresolImplint( SCIP_PRESOL* presol; /* create implint presolver data */ - presoldata = NULL; - /* TODO: (optional) create presolver specific data here */ + SCIP_CALL( SCIPallocBlockMemory(scip, &presoldata) ); - presol = NULL; - - /* use SCIPincludePresolBasic() plus setter functions if you want to set callbacks one-by-one and your code should - * compile independent of new callbacks being added in future SCIP versions - */ + /* include implint presolver */ SCIP_CALL( SCIPincludePresolBasic(scip, &presol, PRESOL_NAME, PRESOL_DESC, PRESOL_PRIORITY, PRESOL_MAXROUNDS, PRESOL_TIMING, presolExecImplint, presoldata) ); @@ -736,8 +833,15 @@ SCIP_RETCODE SCIPincludePresolImplint( SCIP_CALL( SCIPsetPresolInitpre(scip, presol, presolInitpreImplint) ); SCIP_CALL( SCIPsetPresolExitpre(scip, presol, presolExitpreImplint) ); - /* add implint presolver parameters */ - /* TODO: (optional) add presolver specific parameters with SCIPaddTypeParam() here */ + SCIP_CALL( SCIPaddBoolParam(scip, + "presolving/implint/convertintegers", + "should we detect implied integrality for integer variables in the problem?", + &presoldata->convertintegers, TRUE, DEFAULT_CONVERTINTEGERS, NULL, NULL) ); + + SCIP_CALL( SCIPaddRealParam(scip, + "presolving/implint/columnrowratio", + "Use the network row addition algorithm when the column to row ratio becomes larger than this threshold.", + &presoldata->columnrowratio, TRUE, DEFAULT_COLUMNROWRATIO,0.0,1e12, NULL, NULL) ); return SCIP_OKAY; } From cbdbf9886d1ebc9d9ead3ae2087d2f97cc617cc5 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Thu, 29 Aug 2024 14:29:32 +0200 Subject: [PATCH 50/63] Fix linter warnings --- src/scip/network.c | 8 ++++---- src/scip/presol_implint.c | 32 +++++++++++++++++--------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 519247150c..80fabb8d1f 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -3190,7 +3190,7 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( } rootmember = parent; } - while( TRUE ); + while( TRUE ); /*lint !e506*/ calldata[0].member = rootmember; ++ncalldata; @@ -3271,7 +3271,7 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( if( SPQRelementIsRow(element) ){ ind = SPQRelementToRow(element); }else{ - ind = SPQRelementToColumn(element) + dec->memRows; + ind = (SPQRelementToColumn(element) + dec->memRows); /*lint !e776 */ } SCIP_CALL( SCIPdigraphAddArc(digraph, dectographnode[tail], dectographnode[head], (void*) ind) ); @@ -3323,7 +3323,7 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( if( SPQRelementIsRow(element) ){ ind = SPQRelementToRow(element); }else{ - ind = SPQRelementToColumn(element) + dec->memRows; + ind = (SPQRelementToColumn(element) + dec->memRows); /*lint !e776 */ } if( arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc)) { @@ -3381,7 +3381,7 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( if( SPQRelementIsRow(element) ){ ind = SPQRelementToRow(element); }else{ - ind = SPQRelementToColumn(element) + dec->memRows; + ind = (SPQRelementToColumn(element) + dec->memRows); /*lint !e776 */ } if( arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc) ) diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index 741f711a81..0c29e57a87 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -98,9 +98,9 @@ typedef struct{ static SCIP_RETCODE createMatrixComponents( - SCIP* scip, - SCIP_MATRIX* matrix, - MATRIX_COMPONENTS** pmatrixcomponents + SCIP* scip, + SCIP_MATRIX* matrix, + MATRIX_COMPONENTS** pmatrixcomponents ) { SCIP_CALL( SCIPallocBlockMemory(scip, pmatrixcomponents) ); @@ -145,8 +145,8 @@ SCIP_RETCODE createMatrixComponents( static void freeMatrixInfo( - SCIP* scip, - MATRIX_COMPONENTS** pmatrixcomponents + SCIP* scip, + MATRIX_COMPONENTS** pmatrixcomponents ) { MATRIX_COMPONENTS* comp = *pmatrixcomponents; @@ -163,9 +163,11 @@ void freeMatrixInfo( SCIPfreeBlockMemory(scip, pmatrixcomponents); } -static int disjointSetFind(int * disjointset, int index){ +static int disjointSetFind(int * disjointset, + int ind + ){ assert(disjointset); - int current = index; + int current = ind; int next; //traverse down tree while( (next = disjointset[current]) >= 0 ){ @@ -174,7 +176,7 @@ static int disjointSetFind(int * disjointset, int index){ int root = current; //compress indices along path - current = index; + current = ind; while( (next = disjointset[current]) >= 0 ){ disjointset[current] = root; current = next; @@ -233,8 +235,8 @@ SCIP_RETCODE computeContinuousComponents( for( int i = 0; i < colnnonzs; ++i ) { int colrow = colrows[i]; - int index = colrow + comp->nmatrixcols; - int rowrep = disjointSetFind(disjointset,index); + int ind = colrow + comp->nmatrixcols; + int rowrep = disjointSetFind(disjointset,ind); if(colrep != rowrep){ colrep = disjointSetMerge(disjointset,colrep,rowrep); } @@ -304,8 +306,8 @@ SCIP_RETCODE computeContinuousComponents( if(component < 0){ continue; } - int index = comp->componentcolstart[component] + componentnextcolindex[component]; - comp->componentcols[index] = i; + int ind = comp->componentcolstart[component] + componentnextcolindex[component]; + comp->componentcols[ind] = i; ++componentnextcolindex[component]; } for( int i = 0; i < comp->nmatrixrows; ++i ) @@ -314,8 +316,8 @@ SCIP_RETCODE computeContinuousComponents( if(component < 0){ continue; } - int index = comp->componentrowstart[component] + componentnextrowindex[component]; - comp->componentrows[index] = i; + int ind = comp->componentrowstart[component] + componentnextrowindex[component]; + comp->componentrows[ind] = i; ++componentnextrowindex[component]; } @@ -715,7 +717,7 @@ SCIP_DECL_PRESOLEXITPRE(presolExitpreImplint) static SCIP_DECL_PRESOLEXEC(presolExecImplint) -{ +{ /*lint --e{715}*/ *result = SCIP_DIDNOTRUN; //TODO: re-check these conditions again From 7d4af29409e13d989a99e20cb58bfb001eb04e63 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 3 Sep 2024 09:18:22 +0200 Subject: [PATCH 51/63] disable implied integer detection plugin --- src/scip/presol_implint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index 0c29e57a87..bf0f5f4875 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -59,7 +59,7 @@ #define PRESOL_NAME "implint" #define PRESOL_DESC "detects implicit integer variables" #define PRESOL_PRIORITY 100 /**< priority of the presolver (>= 0: before, < 0: after constraint handlers); combined with propagators */ -#define PRESOL_MAXROUNDS -1 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */ +#define PRESOL_MAXROUNDS 0 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */ #define PRESOL_TIMING SCIP_PRESOLTIMING_EXHAUSTIVE /* timing of the presolver (fast, medium, or exhaustive) */ #define DEFAULT_CONVERTINTEGERS FALSE From 24d58d937df3325cdcb2c779ff8608df577e5a1c Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Wed, 4 Sep 2024 12:07:41 +0200 Subject: [PATCH 52/63] Fix scip coding style and add documentation --- src/scip/network.c | 14 +- src/scip/presol_implint.c | 284 +++++++++++++++++++++++--------------- src/scip/presol_implint.h | 25 +++- 3 files changed, 203 insertions(+), 120 deletions(-) diff --git a/src/scip/network.c b/src/scip/network.c index 80fabb8d1f..822eaf5f5a 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -3268,9 +3268,12 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( { spqr_element element = arcGetElement(dec,arc); long ind; - if( SPQRelementIsRow(element) ){ + if( SPQRelementIsRow(element) ) + { ind = SPQRelementToRow(element); - }else{ + } + else + { ind = (SPQRelementToColumn(element) + dec->memRows); /*lint !e776 */ } SCIP_CALL( SCIPdigraphAddArc(digraph, dectographnode[tail], dectographnode[head], @@ -3320,9 +3323,12 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( { spqr_element element = arcGetElement(dec,arc); long ind; - if( SPQRelementIsRow(element) ){ + if( SPQRelementIsRow(element) ) + { ind = SPQRelementToRow(element); - }else{ + } + else + { ind = (SPQRelementToColumn(element) + dec->memRows); /*lint !e776 */ } if( arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc)) diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index bf0f5f4875..f9cfd3733e 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -64,13 +64,17 @@ #define DEFAULT_CONVERTINTEGERS FALSE #define DEFAULT_COLUMNROWRATIO 50.0 +#define DEFAULT_NUMERICSLIMIT 1e7 /** presolver data */ struct SCIP_PresolData { - SCIP_Bool convertintegers; /**< Should we detect implied integrality of columns that are integer? */ - double columnrowratio; /**< Use the network row addition algorithm when the column to row ratio becomes larger than - * this threshold. Otherwise, use the column addition algorithm. */ + SCIP_Bool convertintegers; /**< Should we detect implied integrality of columns that are integer? */ + SCIP_Real columnrowratio; /**< Use the network row addition algorithm when the column to row ratio + * becomes larger than this threshold. Otherwise, use column addition. */ + SCIP_Real numericslimit; /**< A row that contains variables with coefficients that are greater in + * absolute value than this limit is not considered for + * implied integrality detection. */ }; @@ -86,8 +90,8 @@ typedef struct{ int* rowcomponent; /**< Maps a row to the index of the component it belongs to */ int* colcomponent; /**< Maps a column to the index of the component it belongs to */ - int* componentrows; /**< Flattened array of array of rows that are in a given component. */ - int* componentcols; /**< Flattened array of array of columns that are in a given component. */ + int* componentrows; /**< Flattened array of arrays of rows that are in a given component. */ + int* componentcols; /**< Flattened array of arrays of columns that are in a given component. */ int* componentrowstart; /**< The index of componentrows where the given component starts. */ int* componentcolstart; /**< The index of componentcols where the given component starts. */ int* ncomponentrows; /**< The number of rows in the given component. */ @@ -96,12 +100,13 @@ typedef struct{ int ncomponents; } MATRIX_COMPONENTS; +/**< Creates the matrix components data structure */ static SCIP_RETCODE createMatrixComponents( - SCIP* scip, - SCIP_MATRIX* matrix, - MATRIX_COMPONENTS** pmatrixcomponents -) + SCIP* scip, /**< SCIP data structure */ + SCIP_MATRIX* matrix, /**< The constraint matrix */ + MATRIX_COMPONENTS** pmatrixcomponents /**< Pointer to create the matrix components data structure */ + ) { SCIP_CALL( SCIPallocBlockMemory(scip, pmatrixcomponents) ); MATRIX_COMPONENTS * comp = *pmatrixcomponents; @@ -143,11 +148,12 @@ SCIP_RETCODE createMatrixComponents( return SCIP_OKAY; } +/**< Frees the matrix components data structure */ static void freeMatrixInfo( - SCIP* scip, - MATRIX_COMPONENTS** pmatrixcomponents -) + SCIP* scip, /**< SCIP data structure */ + MATRIX_COMPONENTS** pmatrixcomponents /**< Pointer to the allocated matrix components data structure */ + ) { MATRIX_COMPONENTS* comp = *pmatrixcomponents; SCIPfreeBlockMemoryArray(scip, &comp->ncomponentcols, comp->nmatrixcols); @@ -163,42 +169,58 @@ void freeMatrixInfo( SCIPfreeBlockMemory(scip, pmatrixcomponents); } -static int disjointSetFind(int * disjointset, - int ind - ){ - assert(disjointset); +/**< Finds the representative of an element in the disjoint set datastructure. + * Afterwards compresses the path to speed up subsequent queries. */ +static +int disjointSetFind( + int* disjointset, /**< The array storing the disjoint set representatives */ + int ind /**< The index to find */ + ) +{ + assert(disjointset != NULL); int current = ind; int next; - //traverse down tree - while( (next = disjointset[current]) >= 0 ){ + /* traverse down tree */ + while( (next = disjointset[current]) >= 0 ) + { current = next; } int root = current; - //compress indices along path + /* compress indices along path */ current = ind; - while( (next = disjointset[current]) >= 0 ){ + while( (next = disjointset[current]) >= 0 ) + { disjointset[current] = root; current = next; } return root; } -static int disjointSetMerge(int * disjointset, int first, int second){ +/**< Merges two sets/elements into one set. Returns the index of the merged element. + * The provided elements to be merged must be representative (i.e. returned by disjointSetFind()). */ +static +int disjointSetMerge( + int* disjointset, /**< The array storing the disjoint set representatives */ + int first, /**< The first index to merge */ + int second /**< The second index to merge */ + ) +{ assert(disjointset); - assert(disjointset[first] < 0); - assert(disjointset[second] < 0); - assert(first != second);//We cannot merge a node into itself + assert(disjointset[first] <= -1); + assert(disjointset[second] <= -1); + assert(first != second); /* We cannot merge a node into itself */ - //The rank is stored as a negative number: we decrement it making the negative number larger. - //We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement. + /* The rank is stored as a negative number: we decrement it making the negative number larger. + * The rank is an upper bound on the height of the tree. We want the new root to be the one with 'largest' rank, + * so smallest number. This way, we ensure that the tree remains shallow. If they are equal, we decrement. */ int firstRank = disjointset[first]; int secondRank = disjointset[second]; if( firstRank > secondRank ) { SCIPswapInts(&first, &second); } - //first becomes representative + /* first becomes representative */ disjointset[second] = first; if( firstRank == secondRank ) { @@ -207,17 +229,19 @@ static int disjointSetMerge(int * disjointset, int first, int second){ return first; } +/**< Computes the continuous connected components, i.e. the connected components of the submatrix given by all + * continuous columns */ static SCIP_RETCODE computeContinuousComponents( - SCIP * scip, - SCIP_MATRIX* matrix, - MATRIX_COMPONENTS* comp -) + SCIP* scip, /**< SCIP data structure */ + SCIP_MATRIX* matrix, /**< The constraint matrix to compute the components for */ + MATRIX_COMPONENTS* comp /**< The connected components data structure to store the components in */ + ) { - /* We let rows and columns share an index by mapping column i to index nrows + i*/ + /* We let rows and columns share an index by mapping row index i to artificial column index i + nmatrixcols */ int* disjointset = NULL; - SCIP_CALL(SCIPallocBufferArray(scip, &disjointset, comp->nmatrixcols + comp->nmatrixrows)); - //First n entries belong to columns, last entries to rows + SCIP_CALL( SCIPallocBufferArray(scip, &disjointset, comp->nmatrixcols + comp->nmatrixrows) ); + /* First n entries belong to columns, last entries to rows */ for( int i = 0; i < comp->nmatrixcols + comp->nmatrixrows; ++i ) { disjointset[i] = -1; @@ -225,7 +249,8 @@ SCIP_RETCODE computeContinuousComponents( for( int col = 0; col < comp->nmatrixcols; ++col ) { - if( comp->coltype[col] != SCIP_VARTYPE_CONTINUOUS){ + if( comp->coltype[col] != SCIP_VARTYPE_CONTINUOUS) + { continue; } int colnnonzs = SCIPmatrixGetColNNonzs(matrix, col); @@ -237,7 +262,8 @@ SCIP_RETCODE computeContinuousComponents( int colrow = colrows[i]; int ind = colrow + comp->nmatrixcols; int rowrep = disjointSetFind(disjointset,ind); - if(colrep != rowrep){ + if(colrep != rowrep) + { colrep = disjointSetMerge(disjointset,colrep,rowrep); } } @@ -259,8 +285,9 @@ SCIP_RETCODE computeContinuousComponents( } int colroot = disjointSetFind(disjointset,col); int component = representativecomponent[colroot]; - if( component < 0){ - //add new component + if( component < 0) + { + /* add new component */ component = comp->ncomponents; representativecomponent[colroot] = component; comp->ncomponentcols[component] = 0; @@ -282,7 +309,8 @@ SCIP_RETCODE computeContinuousComponents( comp->rowcomponent[row] = component; ++comp->ncomponentrows[component]; } - if(comp->ncomponents != 0){ + if( comp->ncomponents >= 1 ) + { comp->componentrowstart[0] = 0; comp->componentcolstart[0] = 0; for( int i = 1; i < comp->ncomponents; ++i ) @@ -292,32 +320,34 @@ SCIP_RETCODE computeContinuousComponents( } int * componentnextrowindex; int * componentnextcolindex; - SCIP_CALL( SCIPallocBufferArray(scip,&componentnextrowindex,comp->ncomponents) ); - SCIP_CALL( SCIPallocBufferArray(scip,&componentnextcolindex,comp->ncomponents) ); + SCIP_CALL( SCIPallocBufferArray(scip, &componentnextrowindex, comp->ncomponents) ); + SCIP_CALL( SCIPallocBufferArray(scip, &componentnextcolindex, comp->ncomponents) ); for( int i = 0; i < comp->ncomponents; ++i ) { - componentnextcolindex[i] = 0; - componentnextrowindex[i] = 0; + componentnextcolindex[i] = comp->componentcolstart[i]; + componentnextrowindex[i] = comp->componentrowstart[i]; } - for( int i = 0; i < comp->nmatrixcols; ++i ) + for( int col = 0; col < comp->nmatrixcols; ++col ) { - int component = comp->colcomponent[i]; - if(component < 0){ + int component = comp->colcomponent[col]; + if(component < 0) + { continue; } - int ind = comp->componentcolstart[component] + componentnextcolindex[component]; - comp->componentcols[ind] = i; + int ind = componentnextcolindex[component]; + comp->componentcols[ind] = col; ++componentnextcolindex[component]; } - for( int i = 0; i < comp->nmatrixrows; ++i ) + for( int row = 0; row < comp->nmatrixrows; ++row ) { - int component = comp->rowcomponent[i]; - if(component < 0){ + int component = comp->rowcomponent[row]; + if(component < 0) + { continue; } - int ind = comp->componentrowstart[component] + componentnextrowindex[component]; - comp->componentrows[ind] = i; + int ind = componentnextrowindex[component]; + comp->componentrows[ind] = row; ++componentnextrowindex[component]; } @@ -339,21 +369,24 @@ SCIP_RETCODE computeContinuousComponents( } typedef struct{ - SCIP_Bool* rowintegral; /**< Are all the non-continuous column entries and lhs rhs integral? */ + SCIP_Bool* rowintegral; /**< Are all row entries of non-continuous columns and the row sides integral? */ SCIP_Bool* rowequality; /**< Is the row an equality? */ SCIP_Bool* rowbadnumerics; /**< Does the row contain large entries that make numerics difficult? */ - int* rownnonz; /**< Number of nonzeros in the column */ + int* rownnonz; /**< Number of nonzeros in the row */ int* rowncontinuous; /**< The number of those nonzeros that are in continuous columns */ - int* rowncontinuouspmone; /**< The number of continuous columns +-1 entries */ + int* rowncontinuouspmone; /**< The number of +-1 entries in continuous columns */ } MATRIX_STATISTICS; +/**< Creates the matrix statistics data structure */ static SCIP_RETCODE computeMatrixStatistics( - SCIP * scip, - SCIP_MATRIX* matrix, - MATRIX_STATISTICS** pstats -) + SCIP* scip, /**< SCIP data structure */ + SCIP_MATRIX* matrix, /**< The constraint matrix to compute the statistics for */ + MATRIX_STATISTICS** pstats, /**< Pointer to allocate the statistics data structure at */ + SCIP_Real numericslimit /**< The limit beyond which we consider integrality of coefficients + * to be unreliable */ + ) { SCIP_CALL( SCIPallocBuffer(scip,pstats) ); MATRIX_STATISTICS* stats = *pstats; @@ -371,16 +404,16 @@ SCIP_RETCODE computeMatrixStatistics( for( int i = 0; i < nrows; ++i ) { - double lhs = SCIPmatrixGetRowLhs(matrix,i); - double rhs = SCIPmatrixGetRowRhs(matrix,i); + SCIP_Real lhs = SCIPmatrixGetRowLhs(matrix,i); + SCIP_Real rhs = SCIPmatrixGetRowRhs(matrix,i); int * cols = SCIPmatrixGetRowIdxPtr(matrix,i); - double * vals = SCIPmatrixGetRowValPtr(matrix,i); + SCIP_Real * vals = SCIPmatrixGetRowValPtr(matrix,i); int nnonz = SCIPmatrixGetRowNNonzs(matrix,i); stats->rownnonz[i] = nnonz; - stats->rowequality[i] = SCIPisFeasEQ(scip,lhs,rhs) && !( SCIPisInfinity(scip,-lhs) || SCIPisInfinity(scip, rhs) ); + stats->rowequality[i] = !SCIPisInfinity(scip, -lhs) && !SCIPisInfinity(scip, rhs) && SCIPisEQ(scip, lhs, rhs); - SCIP_Bool integral = ( SCIPisInfinity(scip,-lhs) || SCIPisIntegral(scip,lhs)) && - ( SCIPisInfinity(scip,rhs) || SCIPisIntegral(scip,rhs)); + SCIP_Bool integral = ( SCIPisInfinity(scip, -lhs) || SCIPisIntegral(scip, lhs) ) + && ( SCIPisInfinity(scip, rhs) || SCIPisIntegral(scip, rhs) ); SCIP_Bool badnumerics = FALSE; int ncontinuous = 0; @@ -388,17 +421,21 @@ SCIP_RETCODE computeMatrixStatistics( for( int j = 0; j < nnonz; ++j ) { SCIP_Bool continuous = SCIPvarGetType(SCIPmatrixGetVar(matrix,cols[j])) == SCIP_VARTYPE_CONTINUOUS; - double value = vals[j]; - if(continuous){ + SCIP_Real value = vals[j]; + if( continuous ) + { ++ncontinuous; } - if(continuous && ABS(value) == 1.0){ + if(continuous && ABS(value) == 1.0) + { ++ncontinuouspmone; } - if(!continuous){ + if(!continuous) + { integral = integral && SCIPisIntegral(scip,value); } - if(ABS(value) > 1e7){ + if(ABS(value) > numericslimit) + { badnumerics = TRUE; } } @@ -413,49 +450,58 @@ SCIP_RETCODE computeMatrixStatistics( return SCIP_OKAY; } +/**< Frees the matrix statistics data structure */ static void freeMatrixStatistics( - SCIP* scip, - MATRIX_STATISTICS** pstats + SCIP* scip, /**< SCIP data structure */ + MATRIX_STATISTICS** pstats /**< Pointer to the statistics data structure to be freed */ ) { MATRIX_STATISTICS* stats= *pstats; SCIPfreeBufferArray(scip,&stats->rowncontinuouspmone); SCIPfreeBufferArray(scip,&stats->rowncontinuous); SCIPfreeBufferArray(scip,&stats->rownnonz); + SCIPfreeBufferArray(scip,&stats->rowbadnumerics); SCIPfreeBufferArray(scip,&stats->rowequality); SCIPfreeBufferArray(scip,&stats->rowintegral); - SCIPfreeBufferArray(scip,&stats->rowbadnumerics); SCIPfreeBuffer(scip,pstats); } +/**< Given the continuous components and statistics on the matrix, detect components that have implied integer variables + * by checking if the component is a (transposed) network matrix detection and if all the bounds/rhs are integral. + * For every component, we detect if the associated matrix is either a network matrix or a transposed network matrix + * (or both, in which case it represents a planar graph). + * We choose to check if it is a (transposed) network matrix either in a row-wise or in a column-wise fashion, + * depending on the size of the component. + * Finally, every variable that is in a network matrix or transposed network matrix is changed to an implied integer. + */ static SCIP_RETCODE findImpliedIntegers( - SCIP * scip, - SCIP_PRESOLDATA* presoldata, - SCIP_MATRIX* matrix, - MATRIX_COMPONENTS* comp, - MATRIX_STATISTICS* stats, - int* nchgvartypes -) + SCIP* scip, /**< SCIP data structure */ + SCIP_PRESOLDATA* presoldata, /**< The data belonging to the presolver */ + SCIP_MATRIX* matrix, /**< The constraint matrix to compute implied integers for */ + MATRIX_COMPONENTS* comp, /**< The continuous connected components of the matrix */ + MATRIX_STATISTICS* stats, /**< Statistics of the matrix */ + int* nchgvartypes /**< Pointer to count the number of changed variable types */ + ) { //TODO: some checks to prevent expensive memory initialization if not necessary (e.g. there must be some candidates) SCIP_NETMATDEC * dec = NULL; - SCIP_CALL(SCIPnetmatdecCreate(SCIPblkmem(scip),&dec,comp->nmatrixrows,comp->nmatrixcols)); + SCIP_CALL( SCIPnetmatdecCreate(SCIPblkmem(scip),&dec,comp->nmatrixrows,comp->nmatrixcols) ); SCIP_NETMATDEC * transdec = NULL; - SCIP_CALL(SCIPnetmatdecCreate(SCIPblkmem(scip),&transdec,comp->nmatrixcols,comp->nmatrixrows)); + SCIP_CALL( SCIPnetmatdecCreate(SCIPblkmem(scip),&transdec,comp->nmatrixcols,comp->nmatrixrows) ); - int planarcomponents = 0; - int goodcomponents = 0; + int nplanarcomponents = 0; + int ngoodcomponents = 0; int nbadnumerics = 0; int nbadintegrality = 0; int nnonnetwork = 0; /* Because the rows may also contain non-continuous columns, we need to remove these from the array that we * pass to the network matrix decomposition method. We use these working arrays for this purpose. */ - double* tempValArray; + SCIP_Real* tempValArray; int* tempIdxArray; SCIP_CALL(SCIPallocBufferArray(scip,&tempValArray,comp->nmatrixcols)); SCIP_CALL(SCIPallocBufferArray(scip,&tempIdxArray,comp->nmatrixcols)); @@ -469,23 +515,21 @@ SCIP_RETCODE findImpliedIntegers( for( int i = startrow; i < startrow + nrows; ++i ) { int row = comp->componentrows[i]; - if(stats->rowncontinuous[row] != stats->rowncontinuouspmone[row]){ - componentokay = FALSE; - ++nbadintegrality; - break; - } - if(!stats->rowintegral[row]){ + if(stats->rowncontinuous[row] != stats->rowncontinuouspmone[row] ||!stats->rowintegral[row] ) + { componentokay = FALSE; ++nbadintegrality; break; } - if(stats->rowbadnumerics[row]){ + if(stats->rowbadnumerics[row]) + { componentokay = FALSE; ++nbadnumerics; break; } } - if(!componentokay){ + if(!componentokay) + { continue; } int startcol = comp->componentcolstart[component]; @@ -496,20 +540,21 @@ SCIP_RETCODE findImpliedIntegers( /* We use the row-wise algorithm only if the number of columns is much larger than the number of rows. * Generally, the column-wise algorithm will be faster, but in these extreme cases, the row algorithm is faster. - * Only very little instances should have this at all. + * Only very few instances should use the row-wise algorithm. */ - if( nrows * presoldata->columnrowratio < ncols){ + if( nrows * presoldata->columnrowratio < ncols ) + { for( int i = startrow; i < startrow + nrows && componentnetwork; ++i ) { int row = comp->componentrows[i]; int nrownnoz = SCIPmatrixGetRowNNonzs(matrix,row); int* rowcols = SCIPmatrixGetRowIdxPtr(matrix,row); - double* rowvals = SCIPmatrixGetRowValPtr(matrix,row); + SCIP_Real* rowvals = SCIPmatrixGetRowValPtr(matrix,row); int ncontnonz = 0; for( int j = 0; j < nrownnoz; ++j ) { int col = rowcols[j]; - if(SCIPvarGetType(SCIPmatrixGetVar(matrix,col)) == SCIP_VARTYPE_CONTINUOUS) + if( comp->coltype[col] == SCIP_VARTYPE_CONTINUOUS ) { tempIdxArray[ncontnonz] = col; tempValArray[ncontnonz] = rowvals[j]; @@ -528,7 +573,7 @@ SCIP_RETCODE findImpliedIntegers( int col = comp->componentcols[i]; int ncolnnonz = SCIPmatrixGetColNNonzs(matrix,col); int* colrows = SCIPmatrixGetColIdxPtr(matrix,col); - double* colvals = SCIPmatrixGetColValPtr(matrix,col); + SCIP_Real* colvals = SCIPmatrixGetColValPtr(matrix,col); SCIP_CALL( SCIPnetmatdecTryAddCol(dec,col,colrows,colvals,ncolnnonz,&componentnetwork) ); } } @@ -541,18 +586,19 @@ SCIP_RETCODE findImpliedIntegers( SCIP_Bool componenttransnetwork = TRUE; /* For the transposed matrix, the situation is exactly reversed because the row/column algorithms are swapped */ - if(nrows < ncols * presoldata->columnrowratio){ + if( nrows <= ncols * presoldata->columnrowratio ) + { for( int i = startrow; i < startrow + nrows && componenttransnetwork ; ++i ) { int row = comp->componentrows[i]; int nrownnoz = SCIPmatrixGetRowNNonzs(matrix,row); int* rowcols = SCIPmatrixGetRowIdxPtr(matrix,row); - double* rowvals = SCIPmatrixGetRowValPtr(matrix,row); + SCIP_Real* rowvals = SCIPmatrixGetRowValPtr(matrix,row); int ncontnonz = 0; for( int j = 0; j < nrownnoz; ++j ) { int col = rowcols[j]; - if(SCIPvarGetType(SCIPmatrixGetVar(matrix,col)) == SCIP_VARTYPE_CONTINUOUS) + if( comp->coltype[col] == SCIP_VARTYPE_CONTINUOUS ) { tempIdxArray[ncontnonz] = col; tempValArray[ncontnonz] = rowvals[j]; @@ -572,7 +618,7 @@ SCIP_RETCODE findImpliedIntegers( int col = comp->componentcols[i]; int ncolnnonz = SCIPmatrixGetColNNonzs(matrix,col); int* colrows = SCIPmatrixGetColIdxPtr(matrix,col); - double* colvals = SCIPmatrixGetColValPtr(matrix,col); + SCIP_Real* colvals = SCIPmatrixGetColValPtr(matrix,col); SCIP_CALL( SCIPnetmatdecTryAddRow(transdec,col,colrows,colvals,ncolnnonz,&componenttransnetwork) ); } } @@ -587,9 +633,10 @@ SCIP_RETCODE findImpliedIntegers( ++nnonnetwork; continue; } - ++goodcomponents; - if(componentnetwork && componenttransnetwork){ - ++planarcomponents; + ++ngoodcomponents; + if(componentnetwork && componenttransnetwork) + { + ++nplanarcomponents; } for( int i = startcol; i < startcol + ncols; ++i ) { @@ -603,7 +650,7 @@ SCIP_RETCODE findImpliedIntegers( } SCIPverbMessage(scip, SCIP_VERBLEVEL_FULL, NULL, "implied integer components: %d (%d planar) / %d (disqualified: %d by integrality, %d by numerics, %d not network) \n", - goodcomponents, planarcomponents, comp->ncomponents, nbadintegrality, nbadnumerics, nnonnetwork); + ngoodcomponents, nplanarcomponents, comp->ncomponents, nbadintegrality, nbadnumerics, nnonnetwork); SCIPfreeBufferArray(scip,&tempIdxArray); SCIPfreeBufferArray(scip,&tempValArray); @@ -613,6 +660,7 @@ SCIP_RETCODE findImpliedIntegers( return SCIP_OKAY; } + /* * Callback methods of presolver */ @@ -721,12 +769,12 @@ SCIP_DECL_PRESOLEXEC(presolExecImplint) *result = SCIP_DIDNOTRUN; //TODO: re-check these conditions again - //Disable implicit integer detection if we are probing or in NLP context + /* Disable implicit integer detection if we are probing or in NLP context */ if(( SCIPgetStage(scip) != SCIP_STAGE_PRESOLVING ) || SCIPinProbing(scip) || SCIPisNLPEnabled(scip)) { return SCIP_OKAY; } - //Since implied integer detection relies on rows not being changed, we disable it for branch-and-price applications + /* Since implied integer detection relies on rows being static, we disable it for branch-and-price applications*/ if( SCIPisStopped(scip) || SCIPgetNActivePricers(scip) > 0 ) { return SCIP_OKAY; @@ -741,7 +789,7 @@ SCIP_DECL_PRESOLEXEC(presolExecImplint) return SCIP_OKAY; } - double starttime = SCIPgetSolvingTime(scip); + SCIP_Real starttime = SCIPgetSolvingTime(scip); SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) implied integer detection started\n", starttime); @@ -780,16 +828,17 @@ SCIP_DECL_PRESOLEXEC(presolExecImplint) MATRIX_COMPONENTS* comp = NULL; MATRIX_STATISTICS* stats = NULL; SCIP_CALL( createMatrixComponents(scip, matrix, &comp) ); - SCIP_CALL( computeMatrixStatistics(scip, matrix, &stats) ); + SCIP_CALL( computeMatrixStatistics(scip, matrix, &stats, presoldata->numericslimit) ); SCIP_CALL( computeContinuousComponents(scip, matrix, comp) ); SCIP_CALL( findImpliedIntegers(scip, presoldata, matrix, comp, stats, nchgvartypes) ); int afterchanged = *nchgvartypes; - double endtime = SCIPgetSolvingTime(scip); - if(afterchanged == beforechanged){ + SCIP_Real endtime = SCIPgetSolvingTime(scip); + if( afterchanged == beforechanged ) + { SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, - " (%.1fs) no implied integers detected (time: %.2fs)\n", endtime,endtime-starttime); + " (%.1fs) no implied integers detected (time: %.2fs)\n", endtime, endtime - starttime); *result = SCIP_DIDNOTFIND; } else @@ -813,7 +862,7 @@ SCIP_DECL_PRESOLEXEC(presolExecImplint) /** creates the implint presolver and includes it in SCIP */ SCIP_RETCODE SCIPincludePresolImplint( SCIP* scip /**< SCIP data structure */ -) + ) { SCIP_PRESOLDATA* presoldata; SCIP_PRESOL* presol; @@ -845,5 +894,10 @@ SCIP_RETCODE SCIPincludePresolImplint( "presolving/implint/columnrowratio", "Use the network row addition algorithm when the column to row ratio becomes larger than this threshold.", &presoldata->columnrowratio, TRUE, DEFAULT_COLUMNROWRATIO,0.0,1e12, NULL, NULL) ); + + SCIP_CALL(SCIPaddRealParam(scip, + "presolving/implint/numericslimit", + "A row that contains variables with coefficients that are greater in absolute value than this limit is not considered for implied integrality detection.", + &presoldata->numericslimit, TRUE, DEFAULT_NUMERICSLIMIT, 1e5, 1e99, NULL, NULL) ); return SCIP_OKAY; } diff --git a/src/scip/presol_implint.h b/src/scip/presol_implint.h index 3a6e5a5292..e935de83ed 100644 --- a/src/scip/presol_implint.h +++ b/src/scip/presol_implint.h @@ -26,6 +26,28 @@ * @ingroup PRESOLVERS * @brief Presolver that detects implicit integer variables * @author Rolf van der Hulst + * + * This presolver looks for implicit integer variables, which are variables whose integrality is implied. + * The linear constraint handler handles the simple (primal) case such as 2x + 2y + z = 3, where z is implied integer by + * x and y. It also handles a more complicated dual case, where we have 'dual' implied integrality if z occurs only in + * inequalities of the primal form (where the equality becomes an inequality), and has integral bounds. + * + * In this plugin we explicitly look for the following structure in the constraint matrix: + * \f[ + * \begin{array}{llll} + * A x & + B y & & \leq c\\ + * D x & & + E z & \leq f\\ + * + * & & x & \in Z^{p_1} \\ + * & & y & \in Z^{p_2} \times R^{n_2-p_2}\\ + * & & z & \in Z^{p_3} \times R^{n_3-p_3} + * \end{array} + * \f] + * where A and c are integral and B is totally unimodular. It is not difficult to see that after fixing the x variables, + * that the remaining problem on the y variables is an integral polyhedron (and independent of the z variables). + * Hence, y is implied integer by x. + * + * Note that this presolver only treats integral rows, where SCIPisIntegral() is used to check integrality. */ #ifndef __SCIP_PRESOL_IMPLINT_H__ @@ -39,6 +61,7 @@ extern "C" { #endif + /** creates the implicit integer presolver and includes it in SCIP * * @ingroup PresolverIncludes @@ -46,7 +69,7 @@ extern "C" { SCIP_EXPORT SCIP_RETCODE SCIPincludePresolImplint( SCIP* scip /**< SCIP data structure */ -); + ); #ifdef __cplusplus From 5c209825dbefe85fd290e977a31cd240a020ac84 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 24 Sep 2024 16:44:35 +0200 Subject: [PATCH 53/63] Add check for bound integrality --- src/scip/presol_implint.c | 55 +++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index f9cfd3733e..8464b8ad25 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -375,6 +375,7 @@ typedef struct{ int* rownnonz; /**< Number of nonzeros in the row */ int* rowncontinuous; /**< The number of those nonzeros that are in continuous columns */ int* rowncontinuouspmone; /**< The number of +-1 entries in continuous columns */ + SCIP_Bool* colintegralbounds; /**< Does the column have integral bounds? */ } MATRIX_STATISTICS; @@ -392,14 +393,17 @@ SCIP_RETCODE computeMatrixStatistics( MATRIX_STATISTICS* stats = *pstats; int nrows = SCIPmatrixGetNRows(matrix); + int ncols = SCIPmatrixGetNColumns(matrix); + + SCIP_CALL( SCIPallocBufferArray(scip, &stats->rowintegral, nrows) ); + SCIP_CALL( SCIPallocBufferArray(scip, &stats->rowequality, nrows) ); + SCIP_CALL( SCIPallocBufferArray(scip, &stats->rowbadnumerics, nrows) ); - SCIP_CALL( SCIPallocBufferArray(scip,&stats->rowintegral,nrows) ); - SCIP_CALL( SCIPallocBufferArray(scip,&stats->rowequality,nrows) ); - SCIP_CALL( SCIPallocBufferArray(scip,&stats->rowbadnumerics,nrows) ); + SCIP_CALL( SCIPallocBufferArray(scip, &stats->rownnonz, nrows) ); + SCIP_CALL( SCIPallocBufferArray(scip, &stats->rowncontinuous, nrows) ); + SCIP_CALL( SCIPallocBufferArray(scip, &stats->rowncontinuouspmone, nrows) ); - SCIP_CALL( SCIPallocBufferArray(scip,&stats->rownnonz,nrows) ); - SCIP_CALL( SCIPallocBufferArray(scip,&stats->rowncontinuous,nrows) ); - SCIP_CALL( SCIPallocBufferArray(scip,&stats->rowncontinuouspmone,nrows) ); + SCIP_CALL( SCIPallocBufferArray(scip, &stats->colintegralbounds, ncols) ); for( int i = 0; i < nrows; ++i ) @@ -445,6 +449,17 @@ SCIP_RETCODE computeMatrixStatistics( stats->rowintegral[i] = integral; stats->rowbadnumerics[i] = badnumerics; } + for( int i = 0; i < ncols; ++i ) + { + + SCIP_Real lb = SCIPmatrixGetColLb(matrix,i); + SCIP_Real ub = SCIPmatrixGetColUb(matrix,i); + stats->colintegralbounds[i] = ( SCIPisInfinity(scip, -lb) || SCIPisIntegral(scip,lb) ) + && ( SCIPisInfinity(scip, ub) || SCIPisIntegral(scip,ub) ); + + /* Check that integer variables have integer bounds, as expected. */ + assert(SCIPvarGetType(SCIPmatrixGetVar(matrix,i)) == SCIP_VARTYPE_CONTINUOUS || stats->colintegralbounds[i]); + } return SCIP_OKAY; @@ -458,12 +473,13 @@ void freeMatrixStatistics( ) { MATRIX_STATISTICS* stats= *pstats; - SCIPfreeBufferArray(scip,&stats->rowncontinuouspmone); - SCIPfreeBufferArray(scip,&stats->rowncontinuous); - SCIPfreeBufferArray(scip,&stats->rownnonz); - SCIPfreeBufferArray(scip,&stats->rowbadnumerics); - SCIPfreeBufferArray(scip,&stats->rowequality); - SCIPfreeBufferArray(scip,&stats->rowintegral); + SCIPfreeBufferArray(scip, &stats->colintegralbounds); + SCIPfreeBufferArray(scip, &stats->rowncontinuouspmone); + SCIPfreeBufferArray(scip, &stats->rowncontinuous); + SCIPfreeBufferArray(scip, &stats->rownnonz); + SCIPfreeBufferArray(scip, &stats->rowbadnumerics); + SCIPfreeBufferArray(scip, &stats->rowequality); + SCIPfreeBufferArray(scip, &stats->rowintegral); SCIPfreeBuffer(scip,pstats); } @@ -528,12 +544,23 @@ SCIP_RETCODE findImpliedIntegers( break; } } + int startcol = comp->componentcolstart[component]; + int ncols = comp->ncomponentcols[component]; + + for( int i = startcol; i < startcol + ncols && componentokay; ++i ) + { + int col = comp->componentcols[i]; + if( !stats->colintegralbounds[col] ){ + componentokay = FALSE; + ++nbadintegrality; + break; + } + } + if(!componentokay) { continue; } - int startcol = comp->componentcolstart[component]; - int ncols = comp->ncomponentcols[component]; /* Check if the component is a network matrix */ SCIP_Bool componentnetwork = TRUE; From 35ccbd73fdbbed41978be9e0eeb520ab09b34d54 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 8 Oct 2024 11:38:42 +0200 Subject: [PATCH 54/63] Use end instead of start indices for matrix arrays --- src/scip/presol_implint.c | 57 ++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index 8464b8ad25..3997df787c 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -92,11 +92,8 @@ typedef struct{ int* componentrows; /**< Flattened array of arrays of rows that are in a given component. */ int* componentcols; /**< Flattened array of arrays of columns that are in a given component. */ - int* componentrowstart; /**< The index of componentrows where the given component starts. */ - int* componentcolstart; /**< The index of componentcols where the given component starts. */ - int* ncomponentrows; /**< The number of rows in the given component. */ - int* ncomponentcols; /**< The number of columns in the given component */ - + int* componentrowend; /**< The index of componentrows where the given component ends. */ + int* componentcolend; /**< The index of componentcols where the given component ends. */ int ncomponents; } MATRIX_COMPONENTS; @@ -137,11 +134,9 @@ SCIP_RETCODE createMatrixComponents( SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->componentrows, nrows) ); SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->componentcols, ncols) ); - //There will be at most ncols components - SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->componentrowstart, ncols) ); - SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->componentcolstart, ncols) ); - SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->ncomponentrows, ncols) ); - SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->ncomponentcols, ncols) ); + /* There will be at most ncols components */ + SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->componentrowend, ncols) ); + SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->componentcolend, ncols) ); comp->ncomponents = 0; @@ -156,10 +151,9 @@ void freeMatrixInfo( ) { MATRIX_COMPONENTS* comp = *pmatrixcomponents; - SCIPfreeBlockMemoryArray(scip, &comp->ncomponentcols, comp->nmatrixcols); - SCIPfreeBlockMemoryArray(scip, &comp->ncomponentrows, comp->nmatrixcols); - SCIPfreeBlockMemoryArray(scip, &comp->componentcolstart, comp->nmatrixcols); - SCIPfreeBlockMemoryArray(scip, &comp->componentrowstart, comp->nmatrixcols); + + SCIPfreeBlockMemoryArray(scip, &comp->componentcolend, comp->nmatrixcols); + SCIPfreeBlockMemoryArray(scip, &comp->componentrowend, comp->nmatrixcols); SCIPfreeBlockMemoryArray(scip, &comp->componentcols, comp->nmatrixcols); SCIPfreeBlockMemoryArray(scip, &comp->componentrows, comp->nmatrixrows); SCIPfreeBlockMemoryArray(scip, &comp->colcomponent, comp->nmatrixcols); @@ -272,6 +266,7 @@ SCIP_RETCODE computeContinuousComponents( /** Now, fill in the relevant data. */ int * representativecomponent; SCIP_CALL(SCIPallocBufferArray(scip, &representativecomponent, comp->nmatrixcols + comp->nmatrixrows)); + for( int i = 0; i < comp->nmatrixcols + comp->nmatrixrows; ++i ) { representativecomponent[i] = -1; @@ -290,12 +285,12 @@ SCIP_RETCODE computeContinuousComponents( /* add new component */ component = comp->ncomponents; representativecomponent[colroot] = component; - comp->ncomponentcols[component] = 0; - comp->ncomponentrows[component] = 0; + comp->componentcolend[component] = 0; + comp->componentrowend[component] = 0; ++comp->ncomponents; } comp->colcomponent[col] = component; - ++comp->ncomponentcols[component]; + ++comp->componentcolend[component]; } for( int row = 0; row < comp->nmatrixrows; ++row ) { @@ -307,25 +302,25 @@ SCIP_RETCODE computeContinuousComponents( continue; } comp->rowcomponent[row] = component; - ++comp->ncomponentrows[component]; + ++comp->componentrowend[component]; } if( comp->ncomponents >= 1 ) { - comp->componentrowstart[0] = 0; - comp->componentcolstart[0] = 0; for( int i = 1; i < comp->ncomponents; ++i ) { - comp->componentrowstart[i] = comp->componentrowstart[i-1] + comp->ncomponentrows[i-1]; - comp->componentcolstart[i] = comp->componentcolstart[i-1] + comp->ncomponentcols[i-1]; + comp->componentrowend[i] += comp->componentrowend[i-1]; + comp->componentcolend[i] += comp->componentcolend[i-1]; } int * componentnextrowindex; int * componentnextcolindex; SCIP_CALL( SCIPallocBufferArray(scip, &componentnextrowindex, comp->ncomponents) ); SCIP_CALL( SCIPallocBufferArray(scip, &componentnextcolindex, comp->ncomponents) ); - for( int i = 0; i < comp->ncomponents; ++i ) + componentnextrowindex[0] = 0; + componentnextcolindex[0] = 0; + for( int i = 1; i < comp->ncomponents; ++i ) { - componentnextcolindex[i] = comp->componentcolstart[i]; - componentnextrowindex[i] = comp->componentrowstart[i]; + componentnextcolindex[i] = comp->componentcolend[i-1]; + componentnextrowindex[i] = comp->componentrowend[i-1]; } for( int col = 0; col < comp->nmatrixcols; ++col ) @@ -354,8 +349,8 @@ SCIP_RETCODE computeContinuousComponents( #ifndef NDEBUG for( int i = 0; i < comp->ncomponents; ++i ) { - assert(componentnextrowindex[i] == comp->ncomponentrows[i]); - assert(componentnextcolindex[i] == comp->ncomponentcols[i]); + assert(componentnextrowindex[i] == comp->componentrowend[i]); + assert(componentnextcolindex[i] == comp->componentcolend[i]); } #endif @@ -525,8 +520,8 @@ SCIP_RETCODE findImpliedIntegers( for( int component = 0; component < comp->ncomponents; ++component ) { - int startrow = comp->componentrowstart[component]; - int nrows = comp->ncomponentrows[component]; + int startrow = (component == 0) ? 0 : comp->componentrowend[component-1]; + int nrows = comp->componentrowend[component] - startrow; SCIP_Bool componentokay = TRUE; for( int i = startrow; i < startrow + nrows; ++i ) { @@ -544,8 +539,8 @@ SCIP_RETCODE findImpliedIntegers( break; } } - int startcol = comp->componentcolstart[component]; - int ncols = comp->ncomponentcols[component]; + int startcol = (component == 0) ? 0 : comp->componentcolend[component-1]; + int ncols = comp->componentcolend[component] - startcol; for( int i = startcol; i < startcol + ncols && componentokay; ++i ) { From 8c9191188e019ba72ac994019c1132e9e9018111 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 8 Oct 2024 11:48:30 +0200 Subject: [PATCH 55/63] Resolve suggestions --- src/scip/presol_implint.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index 3997df787c..5ca969da07 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -282,6 +282,7 @@ SCIP_RETCODE computeContinuousComponents( int component = representativecomponent[colroot]; if( component < 0) { + assert(component == -1); /* add new component */ component = comp->ncomponents; representativecomponent[colroot] = component; @@ -297,6 +298,7 @@ SCIP_RETCODE computeContinuousComponents( int rowroot = disjointSetFind(disjointset,row + comp->nmatrixcols); int component = representativecomponent[rowroot]; if( component < 0){ + assert(component == -1); //Any rows that have roots that we have not seen yet are rows that have no continuous columns //We can safely skip these for finding the continuous connected components continue; @@ -328,6 +330,7 @@ SCIP_RETCODE computeContinuousComponents( int component = comp->colcomponent[col]; if(component < 0) { + assert(component == -1); continue; } int ind = componentnextcolindex[component]; @@ -339,6 +342,7 @@ SCIP_RETCODE computeContinuousComponents( int component = comp->rowcomponent[row]; if(component < 0) { + assert(component == -1); continue; } int ind = componentnextrowindex[component]; @@ -424,12 +428,11 @@ SCIP_RETCODE computeMatrixStatistics( if( continuous ) { ++ncontinuous; + if(SCIPisEQ(scip,ABS(value),1.0)){ + ++ncontinuouspmone; + } } - if(continuous && ABS(value) == 1.0) - { - ++ncontinuouspmone; - } - if(!continuous) + else { integral = integral && SCIPisIntegral(scip,value); } @@ -581,7 +584,7 @@ SCIP_RETCODE findImpliedIntegers( tempIdxArray[ncontnonz] = col; tempValArray[ncontnonz] = rowvals[j]; ++ncontnonz; - assert(ABS(rowvals[j]) == 1.0); + assert(SCIPisEQ(scip,ABS(rowvals[j]),1.0)); } } @@ -625,7 +628,7 @@ SCIP_RETCODE findImpliedIntegers( tempIdxArray[ncontnonz] = col; tempValArray[ncontnonz] = rowvals[j]; ++ncontnonz; - assert(ABS(rowvals[j]) == 1.0); + assert(SCIPisEQ(scip,ABS(rowvals[j]),1.0)); } } From a64eb0a196b608807eb9b1af0e681d94b834afe3 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 8 Oct 2024 12:30:20 +0200 Subject: [PATCH 56/63] Use cached data --- src/scip/presol_implint.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index 5ca969da07..2b6003cd69 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -383,6 +383,7 @@ static SCIP_RETCODE computeMatrixStatistics( SCIP* scip, /**< SCIP data structure */ SCIP_MATRIX* matrix, /**< The constraint matrix to compute the statistics for */ + MATRIX_COMPONENTS* comp, /**< Datastructure that contains the components of the matrix */ MATRIX_STATISTICS** pstats, /**< Pointer to allocate the statistics data structure at */ SCIP_Real numericslimit /**< The limit beyond which we consider integrality of coefficients * to be unreliable */ @@ -423,7 +424,7 @@ SCIP_RETCODE computeMatrixStatistics( int ncontinuouspmone = 0; for( int j = 0; j < nnonz; ++j ) { - SCIP_Bool continuous = SCIPvarGetType(SCIPmatrixGetVar(matrix,cols[j])) == SCIP_VARTYPE_CONTINUOUS; + SCIP_Bool continuous = comp->coltype[cols[j]] == SCIP_VARTYPE_CONTINUOUS; SCIP_Real value = vals[j]; if( continuous ) { @@ -456,7 +457,7 @@ SCIP_RETCODE computeMatrixStatistics( && ( SCIPisInfinity(scip, ub) || SCIPisIntegral(scip,ub) ); /* Check that integer variables have integer bounds, as expected. */ - assert(SCIPvarGetType(SCIPmatrixGetVar(matrix,i)) == SCIP_VARTYPE_CONTINUOUS || stats->colintegralbounds[i]); + assert(comp->coltype[i] == SCIP_VARTYPE_CONTINUOUS || stats->colintegralbounds[i]); } @@ -853,7 +854,7 @@ SCIP_DECL_PRESOLEXEC(presolExecImplint) MATRIX_COMPONENTS* comp = NULL; MATRIX_STATISTICS* stats = NULL; SCIP_CALL( createMatrixComponents(scip, matrix, &comp) ); - SCIP_CALL( computeMatrixStatistics(scip, matrix, &stats, presoldata->numericslimit) ); + SCIP_CALL( computeMatrixStatistics(scip, matrix, comp, &stats, presoldata->numericslimit) ); SCIP_CALL( computeContinuousComponents(scip, matrix, comp) ); SCIP_CALL( findImpliedIntegers(scip, presoldata, matrix, comp, stats, nchgvartypes) ); int afterchanged = *nchgvartypes; From 32844d1f82c6a4341c609ffb9caef268f60454d9 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 8 Oct 2024 12:35:57 +0200 Subject: [PATCH 57/63] Clarify comment --- src/scip/presol_implint.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index 2b6003cd69..7e9e445213 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -79,7 +79,10 @@ struct SCIP_PresolData /** - * Struct that contains information about the blocks/components of the submatrix given by the continuous columns + * Struct that contains information about the blocks/components of the submatrix given by the continuous columns. + * Note that currently, the matrix is given exactly the SCIP_MATRIX created using SCIPmatrixCreate, but this may change + * once MINLP problems are also accounted for. + * @todo extend the plugin to also work for MINLP problems. This changes the computed matrix. */ typedef struct{ int nmatrixrows; /**< Number of rows in the matrix for the linear part of the problem */ From f752bca4232bfc2e3db1cc65f00f22761ddf3c21 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 8 Oct 2024 13:29:30 +0200 Subject: [PATCH 58/63] Skip unnecessary work --- src/scip/presol_implint.c | 119 ++++++++++++++++++++------------------ 1 file changed, 64 insertions(+), 55 deletions(-) diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index 7e9e445213..0f007ba452 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -59,7 +59,7 @@ #define PRESOL_NAME "implint" #define PRESOL_DESC "detects implicit integer variables" #define PRESOL_PRIORITY 100 /**< priority of the presolver (>= 0: before, < 0: after constraint handlers); combined with propagators */ -#define PRESOL_MAXROUNDS 0 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */ +#define PRESOL_MAXROUNDS 1 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */ #define PRESOL_TIMING SCIP_PRESOLTIMING_EXHAUSTIVE /* timing of the presolver (fast, medium, or exhaustive) */ #define DEFAULT_CONVERTINTEGERS FALSE @@ -108,7 +108,7 @@ SCIP_RETCODE createMatrixComponents( MATRIX_COMPONENTS** pmatrixcomponents /**< Pointer to create the matrix components data structure */ ) { - SCIP_CALL( SCIPallocBlockMemory(scip, pmatrixcomponents) ); + SCIP_CALL( SCIPallocBuffer(scip, pmatrixcomponents) ); MATRIX_COMPONENTS * comp = *pmatrixcomponents; int nrows = SCIPmatrixGetNRows(matrix); @@ -117,29 +117,29 @@ SCIP_RETCODE createMatrixComponents( comp->nmatrixrows = nrows; comp->nmatrixcols = ncols; - SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->coltype, ncols) ); + SCIP_CALL( SCIPallocBufferArray(scip, &comp->coltype, ncols) ); for( int i = 0; i < ncols; ++i ) { SCIP_VAR * var = SCIPmatrixGetVar(matrix,i); comp->coltype[i] = SCIPvarGetType(var); } - SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->rowcomponent, nrows) ); + SCIP_CALL( SCIPallocBufferArray(scip, &comp->rowcomponent, nrows) ); for( int i = 0; i < nrows; ++i ) { comp->rowcomponent[i] = -1; } - SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->colcomponent, ncols) ); + SCIP_CALL( SCIPallocBufferArray(scip, &comp->colcomponent, ncols) ); for( int i = 0; i < ncols; ++i ) { comp->colcomponent[i] = -1; } - SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->componentrows, nrows) ); - SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->componentcols, ncols) ); + SCIP_CALL( SCIPallocBufferArray(scip, &comp->componentrows, nrows) ); + SCIP_CALL( SCIPallocBufferArray(scip, &comp->componentcols, ncols) ); /* There will be at most ncols components */ - SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->componentrowend, ncols) ); - SCIP_CALL( SCIPallocBlockMemoryArray(scip, &comp->componentcolend, ncols) ); + SCIP_CALL( SCIPallocBufferArray(scip, &comp->componentrowend, ncols) ); + SCIP_CALL( SCIPallocBufferArray(scip, &comp->componentcolend, ncols) ); comp->ncomponents = 0; @@ -148,22 +148,22 @@ SCIP_RETCODE createMatrixComponents( /**< Frees the matrix components data structure */ static -void freeMatrixInfo( +void freeMatrixComponents( SCIP* scip, /**< SCIP data structure */ MATRIX_COMPONENTS** pmatrixcomponents /**< Pointer to the allocated matrix components data structure */ ) { MATRIX_COMPONENTS* comp = *pmatrixcomponents; - - SCIPfreeBlockMemoryArray(scip, &comp->componentcolend, comp->nmatrixcols); - SCIPfreeBlockMemoryArray(scip, &comp->componentrowend, comp->nmatrixcols); - SCIPfreeBlockMemoryArray(scip, &comp->componentcols, comp->nmatrixcols); - SCIPfreeBlockMemoryArray(scip, &comp->componentrows, comp->nmatrixrows); - SCIPfreeBlockMemoryArray(scip, &comp->colcomponent, comp->nmatrixcols); - SCIPfreeBlockMemoryArray(scip, &comp->rowcomponent, comp->nmatrixrows); - SCIPfreeBlockMemoryArray(scip, &comp->coltype, comp->nmatrixcols); - - SCIPfreeBlockMemory(scip, pmatrixcomponents); + /**< Make sure to free in reverse */ + SCIPfreeBufferArray(scip, &comp->componentcolend); + SCIPfreeBufferArray(scip, &comp->componentrowend); + SCIPfreeBufferArray(scip, &comp->componentcols); + SCIPfreeBufferArray(scip, &comp->componentrows); + SCIPfreeBufferArray(scip, &comp->colcomponent); + SCIPfreeBufferArray(scip, &comp->rowcomponent); + SCIPfreeBufferArray(scip, &comp->coltype); + + SCIPfreeBuffer(scip, pmatrixcomponents); } /**< Finds the representative of an element in the disjoint set datastructure. @@ -370,6 +370,8 @@ SCIP_RETCODE computeContinuousComponents( return SCIP_OKAY; } +/**< A temporary data structure that stores some statistics/data on the rows and columns. + * This is freed again after implied integer detection is finished. */ typedef struct{ SCIP_Bool* rowintegral; /**< Are all row entries of non-continuous columns and the row sides integral? */ SCIP_Bool* rowequality; /**< Is the row an equality? */ @@ -475,6 +477,7 @@ void freeMatrixStatistics( ) { MATRIX_STATISTICS* stats= *pstats; + /**< make sure, for performance, that these frees occur in reverse */ SCIPfreeBufferArray(scip, &stats->colintegralbounds); SCIPfreeBufferArray(scip, &stats->rowncontinuouspmone); SCIPfreeBufferArray(scip, &stats->rowncontinuous); @@ -511,7 +514,7 @@ SCIP_RETCODE findImpliedIntegers( SCIP_NETMATDEC * transdec = NULL; SCIP_CALL( SCIPnetmatdecCreate(SCIPblkmem(scip),&transdec,comp->nmatrixcols,comp->nmatrixrows) ); - int nplanarcomponents = 0; + int ngoodcomponents = 0; int nbadnumerics = 0; int nbadintegrality = 0; @@ -615,40 +618,44 @@ SCIP_RETCODE findImpliedIntegers( SCIP_Bool componenttransnetwork = TRUE; /* For the transposed matrix, the situation is exactly reversed because the row/column algorithms are swapped */ - if( nrows <= ncols * presoldata->columnrowratio ) + /* We only run transposed network detection if network detection failed */ + if(!componentnetwork) { - for( int i = startrow; i < startrow + nrows && componenttransnetwork ; ++i ) + if( nrows <= ncols * presoldata->columnrowratio ) { - int row = comp->componentrows[i]; - int nrownnoz = SCIPmatrixGetRowNNonzs(matrix,row); - int* rowcols = SCIPmatrixGetRowIdxPtr(matrix,row); - SCIP_Real* rowvals = SCIPmatrixGetRowValPtr(matrix,row); - int ncontnonz = 0; - for( int j = 0; j < nrownnoz; ++j ) + for( int i = startrow; i < startrow + nrows && componenttransnetwork; ++i ) { - int col = rowcols[j]; - if( comp->coltype[col] == SCIP_VARTYPE_CONTINUOUS ) + int row = comp->componentrows[i]; + int nrownnoz = SCIPmatrixGetRowNNonzs(matrix, row); + int* rowcols = SCIPmatrixGetRowIdxPtr(matrix, row); + SCIP_Real* rowvals = SCIPmatrixGetRowValPtr(matrix, row); + int ncontnonz = 0; + for( int j = 0; j < nrownnoz; ++j ) { - tempIdxArray[ncontnonz] = col; - tempValArray[ncontnonz] = rowvals[j]; - ++ncontnonz; - assert(SCIPisEQ(scip,ABS(rowvals[j]),1.0)); + int col = rowcols[j]; + if( comp->coltype[col] == SCIP_VARTYPE_CONTINUOUS ) + { + tempIdxArray[ncontnonz] = col; + tempValArray[ncontnonz] = rowvals[j]; + ++ncontnonz; + assert(SCIPisEQ(scip, ABS(rowvals[j]), 1.0)); + } } - } - SCIP_CALL( SCIPnetmatdecTryAddCol(transdec,row,tempIdxArray,tempValArray,ncontnonz, - &componenttransnetwork) ); + SCIP_CALL(SCIPnetmatdecTryAddCol(transdec, row, tempIdxArray, tempValArray, ncontnonz, + &componenttransnetwork)); + } } - } - else - { - for( int i = startcol; i < startcol + ncols && componenttransnetwork; ++i ) + else { - int col = comp->componentcols[i]; - int ncolnnonz = SCIPmatrixGetColNNonzs(matrix,col); - int* colrows = SCIPmatrixGetColIdxPtr(matrix,col); - SCIP_Real* colvals = SCIPmatrixGetColValPtr(matrix,col); - SCIP_CALL( SCIPnetmatdecTryAddRow(transdec,col,colrows,colvals,ncolnnonz,&componenttransnetwork) ); + for( int i = startcol; i < startcol + ncols && componenttransnetwork; ++i ) + { + int col = comp->componentcols[i]; + int ncolnnonz = SCIPmatrixGetColNNonzs(matrix, col); + int* colrows = SCIPmatrixGetColIdxPtr(matrix, col); + SCIP_Real* colvals = SCIPmatrixGetColValPtr(matrix, col); + SCIP_CALL(SCIPnetmatdecTryAddRow(transdec, col, colrows, colvals, ncolnnonz, &componenttransnetwork)); + } } } @@ -663,10 +670,7 @@ SCIP_RETCODE findImpliedIntegers( continue; } ++ngoodcomponents; - if(componentnetwork && componenttransnetwork) - { - ++nplanarcomponents; - } + for( int i = startcol; i < startcol + ncols; ++i ) { int col = comp->componentcols[i]; @@ -677,9 +681,14 @@ SCIP_RETCODE findImpliedIntegers( assert(!infeasible); } } - SCIPverbMessage(scip, SCIP_VERBLEVEL_FULL, NULL, - "implied integer components: %d (%d planar) / %d (disqualified: %d by integrality, %d by numerics, %d not network) \n", - ngoodcomponents, nplanarcomponents, comp->ncomponents, nbadintegrality, nbadnumerics, nnonnetwork); + if(*nchgvartypes == 0){ + SCIPverbMessage(scip, SCIP_VERBLEVEL_FULL, NULL, "No implied integers detected \n"); + }else{ + SCIPverbMessage(scip, SCIP_VERBLEVEL_FULL, NULL, + "%d implied integers in %d / %d components (disqualified: %d by integrality, %d by numerics, %d not network) \n", + *nchgvartypes, ngoodcomponents, comp->ncomponents, nbadintegrality, nbadnumerics, nnonnetwork); + } + SCIPfreeBufferArray(scip,&tempIdxArray); SCIPfreeBufferArray(scip,&tempValArray); @@ -878,7 +887,7 @@ SCIP_DECL_PRESOLEXEC(presolExecImplint) *result = SCIP_SUCCESS; } freeMatrixStatistics(scip,&stats); - freeMatrixInfo(scip, &comp); + freeMatrixComponents(scip, &comp); SCIPmatrixFree(scip, &matrix); return SCIP_OKAY; } From e4d4147bb80207452635c13172e6a87bffb08a0d Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Tue, 8 Oct 2024 13:30:56 +0200 Subject: [PATCH 59/63] Disable implint presolver by default --- src/scip/presol_implint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index 0f007ba452..e08bce2c78 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -59,7 +59,7 @@ #define PRESOL_NAME "implint" #define PRESOL_DESC "detects implicit integer variables" #define PRESOL_PRIORITY 100 /**< priority of the presolver (>= 0: before, < 0: after constraint handlers); combined with propagators */ -#define PRESOL_MAXROUNDS 1 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */ +#define PRESOL_MAXROUNDS 0 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */ #define PRESOL_TIMING SCIP_PRESOLTIMING_EXHAUSTIVE /* timing of the presolver (fast, medium, or exhaustive) */ #define DEFAULT_CONVERTINTEGERS FALSE From 52fa7e235ee42fa935eeef68ef4d80980e04cc0c Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Wed, 9 Oct 2024 16:02:12 +0200 Subject: [PATCH 60/63] Update changelog for implied integers --- CHANGELOG | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index e3a6202ad3..a7ff1e2d06 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -46,6 +46,8 @@ Interface changes - SCIPhypergraphCreate(), SCIPhypergraphClear(), SCIPhypergraphFree(), SCIPhypergraphAddVertex(), SCIPhypergraphAddEdge(), SCIPhypergraphIsValid() and SCIPhypergraphComputeVerticesEdges() for building and deleting hypergraphs, SCIPhypergraphComputeOverlaps(), SCIPhypergraphOverlapFind(), SCIPhypergraphIntersectEdges(), SCIPhypergraphComputeOverlapsEdges(), SCIPhypergraphComputeVerticesOverlaps(), SCIPhypergraphOverlapsDisjoint() to deal with pair-wise intersections of hyperedges, SCIPhypergraphIterInit(), SCIPhypergraphIterClear(), SCIPhypergraphIterStart(), SCIPhypergraphIterValid(), SCIPhypergraphIterNext(), SCIPhypergraphIterBase(), SCIPhypergraphIterAdjacent(), SCIPhypergraphIterMinVertex(), SCIPhypergraphIterOverlap() for iterating over adjacent edges, SCIPhypergraphHasVertexEdges() SCIPhypergraphHasOverlaps(), SCIPhypergraphHasOverlapsEdges(), SCIPhypergraphHasVertexOverlaps(), SCIPhypergraphGetNVertices(), SCIPhypergraphGetNEdges(), SCIPhypergraphBlkmem(), SCIPhypergraphGetNOverlaps(), SCIPhypergraphVertexData(), SCIPhypergraphEdgeData(), SCIPhypergraphEdgeSize(), SCIPhypergraphEdgeVertices(), SCIPhypergraphVertexEdgesFirst(), SCIPhypergraphVertexEdgesBeyond(), SCIPhypergraphVertexEdgesGetAtIndex(), SCIPhypergraphOverlapData(), SCIPhypergraphOverlapSize(), SCIPhypergraphOverlapVertices() SCIPhypergraphEdgesOverlapsFirst(), SCIPhypergraphEdgesOverlapsBeyond(), SCIPhypergraphEdgesOverlapsGetAtIndex(), SCIPhypergraphOverlapsEdgesFirst(), SCIPhypergraphOverlapsEdgesBeyond(), SCIPhypergraphOverlapsEdgesGetAtIndex(), SCIPhypergraphVertexOverlapsFirst(), SCIPhypergraphVertexOverlapsBeyond() and SCIPhypergraphVertexOverlapsGetAtIndex() to query information about vertices, edges and overlaps as well as their incidences. - SCIPdebugClearSol() for clearing the debug solution - Renamed XML functions to avoid name clash with libxml2 by adding "SCIP": SCIPxmlProcess(), SCIPxmlNewNode(), SCIPxmlNewAttr(), SCIPxmlAddAttr(), SCIPxmlAppendChild(), SCIPxmlFreeNode(), SCIPxmlShowNode(), SCIPxmlGetAttrval(), SCIPxmlFirstNode(), SCIPxmlNextNode(), SCIPxmlFindNode(), SCIPxmlFindNodeMaxdepth(), SCIPxmlNextSibl(), SCIPxmlPrevSibl(), SCIPxmlFirstChild(), SCIPxmlLastChild(), SCIPxmlGetName(), SCIPxmlGetLine(), SCIPxmlGetData(), SCIPxmlFindPcdata(). +- SCIPincludePresolImplint() to include the new implied integer presolver +- SCIPnetmatdecCreate() and SCIPnetmatdecFree() for creating and deleting a network matrix decomposition. SCIPnetmatdecTryAddCol() and SCIPnetmatdecTryAddRow() are used to add columns and rows of the matrix to the decomposition. SCIPnetmatdecContainsRow() and SCIPnetmatdecContainsColumn() check if the decomposition contains the given row or columns. SCIPnetmatdecRemoveComponent() can remove connected components from the decomposition. SCIPnetmatdecCreateDiGraph() can be used to expose the underlying digraph. SCIPnetmatdecIsMinimal() and SCIPnetmatdecVerifyCycle() check if certain invariants of the decomposition are satisfied and are used in tests. ### Changes in preprocessor macros @@ -65,6 +67,9 @@ Interface changes - new parameter "propagating/symmetry/handlesignedorbitopes" to control whether special symmetry handling techniques for orbitopes whose columns can be (partially) reflected shall be applied - new parameter "propagating/symmetry/usesimplesgncomp" to control whether symmetry components all of whose variables are simultaneously reflected by a symmetry shall be handled by a special inequality - new parameter "propagating/symmetry/dispsyminfo" to control whether information about which symmetry handling methods are applied are printed +- new parameter "presolving/implint/numericslimit" to control whether rows and variables with large integral coefficients are discarded for implicit integer detection +- new parameter "presolving/implint/columnrowratio" indicates the ratio of rows/columns where the row-wise network matrix detection algorithm is used over the column-wise network matrix detection algorithm +- new parameter "presolving/implint/convertintegers" controls whether implied integrality should be detected for integer variables ### Data structures From 81f5bcf273811e00948d586b4ce39db0c1540e53 Mon Sep 17 00:00:00 2001 From: Rolf van der Hulst Date: Wed, 9 Oct 2024 09:52:13 +0200 Subject: [PATCH 61/63] Fix SCIP style and documentation; revise netMatDecDataCreateDiGraph - change non-negative long that gets converted into void* to size_t (should always have same size) - remove unused callbacks --- CHANGELOG | 4 +- doc/xternal.c | 2 +- src/scip/network.c | 133 +++++++------ src/scip/presol_implint.c | 367 +++++++++++++++--------------------- src/scip/presol_implint.h | 4 +- src/scip/type_hypergraph.h | 3 +- tests/src/network/network.c | 268 ++++++++++++++------------ 7 files changed, 372 insertions(+), 409 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a7ff1e2d06..99291c074c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -67,9 +67,9 @@ Interface changes - new parameter "propagating/symmetry/handlesignedorbitopes" to control whether special symmetry handling techniques for orbitopes whose columns can be (partially) reflected shall be applied - new parameter "propagating/symmetry/usesimplesgncomp" to control whether symmetry components all of whose variables are simultaneously reflected by a symmetry shall be handled by a special inequality - new parameter "propagating/symmetry/dispsyminfo" to control whether information about which symmetry handling methods are applied are printed -- new parameter "presolving/implint/numericslimit" to control whether rows and variables with large integral coefficients are discarded for implicit integer detection -- new parameter "presolving/implint/columnrowratio" indicates the ratio of rows/columns where the row-wise network matrix detection algorithm is used over the column-wise network matrix detection algorithm - new parameter "presolving/implint/convertintegers" controls whether implied integrality should be detected for integer variables +- new parameter "presolving/implint/columnrowratio" indicates the ratio of rows/columns where the row-wise network matrix detection algorithm is used instead of the column-wise network matrix detection algorithm +- new parameter "presolving/implint/numericslimit" determines the limit for absolute integral coefficients beyond which the corresponding rows and variables are excluded from implied integer detection ### Data structures diff --git a/doc/xternal.c b/doc/xternal.c index 8084a73992..8319684065 100644 --- a/doc/xternal.c +++ b/doc/xternal.c @@ -9056,7 +9056,7 @@ /**@defgroup NetworkMatrix Network Matrix * @ingroup DataStructures - * @brief Methods for detecting network matrices and converting them to the underlying graphs. + * @brief methods for detecting network matrices and converting them to the underlying graphs */ /**@defgroup SymGraph Symmetry Detection Graph diff --git a/src/scip/network.c b/src/scip/network.c index 822eaf5f5a..ac07a3dfaa 100644 --- a/src/scip/network.c +++ b/src/scip/network.c @@ -3118,16 +3118,11 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( SCIP_Bool createrowarcs /**< Should the row arcs be added to the created digraph? */ ) { - /* Compute the number of rows m in the network matrix decomposition. The underlying graph has m+1 vertices. */ int nrows = 0; for( int i = 0; i < dec->memRows; ++i ) - { - if( netMatDecDataContainsRow(dec, i)) - { + if( netMatDecDataContainsRow(dec, i) ) ++nrows; - } - } int nvertices = nrows + 1; SCIP_CALL( SCIPdigraphCreate(pdigraph, blkmem, nvertices) ); @@ -3135,17 +3130,14 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( /* Map decomposition nodes to graph nodes. The first node of each component is mapped to node 0 */ int largestNode = largestNodeID(dec); int* dectographnode; - SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem,&dectographnode, largestNode) ); + SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &dectographnode, largestNode) ); for( int i = 0; i < largestNode; ++i ) - { dectographnode[i] = -1; - } - SCIP_DIGRAPH* digraph = *pdigraph; /* Recursively explore the decomposition, adding the edges of each component and identifying them as needed */ - typedef struct{ + typedef struct { spqr_member member; spqr_arc arc; int graphfirstarchead; @@ -3157,50 +3149,39 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &calldata, largestmember) ); SCIP_Bool* rowprocessed; - SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &rowprocessed, dec->memRows) ); - for( int i = 0; i < dec->memRows; ++i ) - { - rowprocessed[i] = FALSE; - } + SCIP_ALLOC( BMSallocClearBlockMemoryArray(blkmem, &rowprocessed, dec->memRows) ); /* This outer loop loops over all components; this is a bit ugly unfortunately */ int nextnodeindex = 1; /* 0 is assigned to the first arc of the root members' head */ - for( int i = 0; i memRows ; ++i ) + for( int i = 0; i < dec->memRows ; ++i ) { spqr_arc rowarc = dec->rowArcs[i]; - if( SPQRarcIsInvalid(rowarc)) - { + if( SPQRarcIsInvalid(rowarc) ) continue; - } - assert(netMatDecDataContainsRow(dec, i)); + + assert(netMatDecDataContainsRow(dec, i)) ; if( rowprocessed[i] ) - { continue; - } /* Find the root member of the SPQR tree */ - spqr_member arcmember = findArcMember(dec,rowarc); + spqr_member arcmember = findArcMember(dec, rowarc); spqr_member rootmember = arcmember; do { spqr_member parent = findMemberParent(dec, rootmember); - if( SPQRmemberIsInvalid(parent)) - { + if( SPQRmemberIsInvalid(parent) ) break; - } rootmember = parent; } while( TRUE ); /*lint !e506*/ + /* We identify all the SPQR trees at node 0 */ calldata[0].member = rootmember; + calldata[0].arc = getFirstMemberArc(dec, rootmember); + calldata[0].graphfirstarchead = 0; + calldata[0].graphfirstarctail = nextnodeindex; + ++nextnodeindex; ++ncalldata; - /* We identify all the SPQR trees at node 0 */ - { - calldata[0].arc = getFirstMemberArc(dec,rootmember); - calldata[0].graphfirstarchead = 0; - calldata[0].graphfirstarctail = nextnodeindex; - ++nextnodeindex; - } while( ncalldata != 0 ) { @@ -3219,12 +3200,11 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( spqr_node exploreHead = findArcHead(dec, explore.arc); spqr_node exploreTail = findArcTail(dec, explore.arc); if( findArcSign(dec, explore.arc).reversed ) - { SCIPswapInts(&exploreTail, &exploreTail); - } dectographnode[exploreHead] = explore.graphfirstarchead; dectographnode[exploreTail] = explore.graphfirstarctail; } + /* Add all the members arcs to the graph */ spqr_arc firstArc = getFirstMemberArc(dec, explore.member); spqr_arc arc = firstArc; @@ -3233,9 +3213,8 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( spqr_node head = findArcHead(dec, arc); spqr_node tail = findArcTail(dec, arc); if( findArcSign(dec, arc).reversed ) - { SCIPswapInts(&head, &tail); - } + if( dectographnode[head] == -1 ) { dectographnode[head] = nextnodeindex; @@ -3246,14 +3225,15 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( dectographnode[tail] = nextnodeindex; ++nextnodeindex; } + /* If an arc is a marker to a child node, we add it to the call stack */ if( arcIsChildMarker(dec, arc)) { spqr_member child = findArcChildMember(dec, arc); spqr_arc toparent = markerToParent(dec, child); + /* Identify head with head and tail with tail */ /* Push member onto the processing stack (recursive call) */ - calldata[ncalldata].member = child; calldata[ncalldata].arc = toparent; calldata[ncalldata].graphfirstarchead = dectographnode[head]; @@ -3261,29 +3241,30 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( ++ncalldata; } - else if( arc != markerToParent(dec, explore.member)) + else if( arc != markerToParent(dec, explore.member) ) { - //We only realize non-virtual arcs - if( createrowarcs || !arcIsTree(dec, arc)) + /* We only realize non-virtual arcs */ + if( createrowarcs || !arcIsTree(dec, arc) ) { - spqr_element element = arcGetElement(dec,arc); - long ind; + spqr_element element = arcGetElement(dec, arc); + size_t ind; if( SPQRelementIsRow(element) ) { - ind = SPQRelementToRow(element); + ind = SPQRelementToRow(element); /*lint !e732 */ } else { - ind = (SPQRelementToColumn(element) + dec->memRows); /*lint !e776 */ + ind = SPQRelementToColumn(element) + dec->memRows; /*lint !e732 !e776 */ } SCIP_CALL( SCIPdigraphAddArc(digraph, dectographnode[tail], dectographnode[head], (void*) ind) ); } } spqr_element element = arcGetElement(dec, arc); - if( SPQRelementIsRow(element) && element != MARKER_ROW_ELEMENT) + if( SPQRelementIsRow(element) && element != MARKER_ROW_ELEMENT ) { spqr_row row = SPQRelementToRow(element); + assert(row < dec->memRows); rowprocessed[row] = TRUE; } arc = getNextMemberArc(dec, arc); @@ -3291,6 +3272,7 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( while( arc != firstArc ); break; } + case SPQR_MEMBERTYPE_LOOP: case SPQR_MEMBERTYPE_PARALLEL: { @@ -3299,12 +3281,13 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( do { /* If an edge is a marker to a child node, we add it to the call stack */ - if( arcIsChildMarker(dec, arc)) + if( arcIsChildMarker(dec, arc) ) { spqr_member child = findArcChildMember(dec, arc); spqr_arc toparent = markerToParent(dec, child); SCIP_Bool directionsmatch = arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc); + /* Identify head with head and tail with tail */ /* Push member onto the processing stack (recursive call) */ calldata[ncalldata].member = child; @@ -3321,17 +3304,17 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( /* We only realize non-virtual arcs */ if( createrowarcs || !arcIsTree(dec, arc) ) { - spqr_element element = arcGetElement(dec,arc); - long ind; + spqr_element element = arcGetElement(dec, arc); + size_t ind; if( SPQRelementIsRow(element) ) { - ind = SPQRelementToRow(element); + ind = SPQRelementToRow(element); /*lint !e732 */ } else { - ind = (SPQRelementToColumn(element) + dec->memRows); /*lint !e776 */ + ind = SPQRelementToColumn(element) + dec->memRows; /*lint !e732 !e776 */ } - if( arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc)) + if( arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc) ) { SCIP_CALL( SCIPdigraphAddArc(digraph, explore.graphfirstarctail, explore.graphfirstarchead, (void*) ind) ); @@ -3344,9 +3327,10 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( } } spqr_element element = arcGetElement(dec, arc); - if( SPQRelementIsRow(element) && element != MARKER_ROW_ELEMENT) + if( SPQRelementIsRow(element) && element != MARKER_ROW_ELEMENT ) { spqr_row row = SPQRelementToRow(element); + assert(row < dec->memRows); rowprocessed[row] = TRUE; } arc = getNextMemberArc(dec, arc); @@ -3354,6 +3338,7 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( while( arc != firstArc ); break; } + case SPQR_MEMBERTYPE_SERIES: { int count = getNumMemberArcs(dec, explore.member); @@ -3362,12 +3347,13 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( int secondnode = explore.graphfirstarctail; for( int j = 0; j < count; ++j ) { - if( arcIsChildMarker(dec, arc)) + if( arcIsChildMarker(dec, arc) ) { spqr_member child = findArcChildMember(dec, arc); spqr_arc toparent = markerToParent(dec, child); SCIP_Bool directionsmatch = arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc); + /* Identify head with head and tail with tail */ /* Push member onto the processing stack (recursive call) */ calldata[ncalldata].member = child; @@ -3377,17 +3363,20 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( ++ncalldata; } - else if( arc != markerToParent(dec, explore.member)) + else if( arc != markerToParent(dec, explore.member) ) { /* We only realize non-virtual arcs */ - if( createrowarcs || !arcIsTree(dec, arc)) + if( createrowarcs || !arcIsTree(dec, arc) ) { spqr_element element = arcGetElement(dec,arc); - long ind; - if( SPQRelementIsRow(element) ){ - ind = SPQRelementToRow(element); - }else{ - ind = (SPQRelementToColumn(element) + dec->memRows); /*lint !e776 */ + size_t ind; + if( SPQRelementIsRow(element) ) + { + ind = SPQRelementToRow(element); /*lint !e732 */ + } + else + { + ind = SPQRelementToColumn(element) + dec->memRows; /*lint !e732 !e776 */ } if( arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc) ) @@ -3423,25 +3412,29 @@ SCIP_RETCODE netMatDecDataCreateDiGraph( assert(arc == explore.arc); /* Check that we added all arcs */ break; } - case SPQR_MEMBERTYPE_UNASSIGNED:{ - SCIPerrorMessage("Can not create graph for unassigned member type, exiting. \n"); + + case SPQR_MEMBERTYPE_UNASSIGNED: + { + SCIPerrorMessage("Can not create graph for unassigned member type, exiting.\n"); SCIPABORT(); return SCIP_ERROR; } } - } } /* We cannot have more vertices then there are in the graph. - * If we processed everything right, all vertices should have been hit. */ + * If we processed everything right, all vertices should have been hit. + */ assert(nextnodeindex == nvertices); - BMSfreeBlockMemoryArray(blkmem,&rowprocessed,dec->memRows); - BMSfreeBlockMemoryArray(blkmem,&calldata,largestmember); - BMSfreeBlockMemoryArray(blkmem,&dectographnode,largestNode); + BMSfreeBlockMemoryArray(blkmem, &rowprocessed, dec->memRows); + BMSfreeBlockMemoryArray(blkmem, &calldata, largestmember); + BMSfreeBlockMemoryArray(blkmem, &dectographnode, largestNode); + return SCIP_OKAY; } + /* ---------- START functions for column addition ------------------------------------------------------------------- */ /** Path arcs are all those arcs that correspond to nonzeros of the column to be added. */ @@ -11721,5 +11714,5 @@ SCIP_RETCODE SCIPnetmatdecCreateDiGraph( SCIP_Bool createrowarcs /**< Should the row arcs be added to the created digraph? */ ) { - return netMatDecDataCreateDiGraph(dec->dec,blkmem,pdigraph,createrowarcs); + return netMatDecDataCreateDiGraph(dec->dec, blkmem, pdigraph, createrowarcs); } diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index e08bce2c78..f886ec3bd8 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -35,8 +35,6 @@ #include #include "scip/presol_implint.h" - - #include "scip/pub_matrix.h" #include "scip/pub_message.h" #include "scip/pub_misc.h" @@ -56,15 +54,15 @@ #include "scip/scip_timing.h" #include "scip/scip_var.h" -#define PRESOL_NAME "implint" -#define PRESOL_DESC "detects implicit integer variables" +#define PRESOL_NAME "implint" +#define PRESOL_DESC "detects implicit integer variables" #define PRESOL_PRIORITY 100 /**< priority of the presolver (>= 0: before, < 0: after constraint handlers); combined with propagators */ #define PRESOL_MAXROUNDS 0 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */ #define PRESOL_TIMING SCIP_PRESOLTIMING_EXHAUSTIVE /* timing of the presolver (fast, medium, or exhaustive) */ #define DEFAULT_CONVERTINTEGERS FALSE #define DEFAULT_COLUMNROWRATIO 50.0 -#define DEFAULT_NUMERICSLIMIT 1e7 +#define DEFAULT_NUMERICSLIMIT 1e7 /** presolver data */ struct SCIP_PresolData @@ -78,17 +76,18 @@ struct SCIP_PresolData }; -/** - * Struct that contains information about the blocks/components of the submatrix given by the continuous columns. - * Note that currently, the matrix is given exactly the SCIP_MATRIX created using SCIPmatrixCreate, but this may change +/** Struct that contains information about the blocks/components of the submatrix given by the continuous columns. + * + * Note that currently, the matrix represents exactly the SCIP_MATRIX created by SCIPmatrixCreate(), but this may change * once MINLP problems are also accounted for. * @todo extend the plugin to also work for MINLP problems. This changes the computed matrix. */ -typedef struct{ +struct MatrixComponents +{ int nmatrixrows; /**< Number of rows in the matrix for the linear part of the problem */ int nmatrixcols; /**< Number of columns in the matrix for the linear part of the problem */ - SCIP_VARTYPE * coltype; /**< SCIP_VARTYPE of the associated column */ + SCIP_VARTYPE* coltype; /**< SCIP_VARTYPE of the associated column */ int* rowcomponent; /**< Maps a row to the index of the component it belongs to */ int* colcomponent; /**< Maps a column to the index of the component it belongs to */ @@ -97,10 +96,28 @@ typedef struct{ int* componentcols; /**< Flattened array of arrays of columns that are in a given component. */ int* componentrowend; /**< The index of componentrows where the given component ends. */ int* componentcolend; /**< The index of componentcols where the given component ends. */ - int ncomponents; -} MATRIX_COMPONENTS; + int ncomponents; /**< The number of components. */ +}; +typedef struct MatrixComponents MATRIX_COMPONENTS; -/**< Creates the matrix components data structure */ +/** A temporary data structure that stores some statistics/data on the rows and columns. + * + * This is freed again after implied integer detection is finished. + */ +struct MatrixStatistics +{ + SCIP_Bool* rowintegral; /**< Are all row entries of non-continuous columns and the row sides integral? */ + SCIP_Bool* rowequality; /**< Is the row an equality? */ + SCIP_Bool* rowbadnumerics; /**< Does the row contain large entries that make numerics difficult? */ + int* rownnonz; /**< Number of nonzeros in the row */ + int* rowncontinuous; /**< The number of those nonzeros that are in continuous columns */ + int* rowncontinuouspmone; /**< The number of +-1 entries in continuous columns */ + SCIP_Bool* colintegralbounds; /**< Does the column have integral bounds? */ + +}; +typedef struct MatrixStatistics MATRIX_STATISTICS; + +/** Creates the matrix components data structure */ static SCIP_RETCODE createMatrixComponents( SCIP* scip, /**< SCIP data structure */ @@ -109,7 +126,7 @@ SCIP_RETCODE createMatrixComponents( ) { SCIP_CALL( SCIPallocBuffer(scip, pmatrixcomponents) ); - MATRIX_COMPONENTS * comp = *pmatrixcomponents; + MATRIX_COMPONENTS* comp = *pmatrixcomponents; int nrows = SCIPmatrixGetNRows(matrix); int ncols = SCIPmatrixGetNColumns(matrix); @@ -120,7 +137,7 @@ SCIP_RETCODE createMatrixComponents( SCIP_CALL( SCIPallocBufferArray(scip, &comp->coltype, ncols) ); for( int i = 0; i < ncols; ++i ) { - SCIP_VAR * var = SCIPmatrixGetVar(matrix,i); + SCIP_VAR* var = SCIPmatrixGetVar(matrix,i); comp->coltype[i] = SCIPvarGetType(var); } @@ -146,7 +163,7 @@ SCIP_RETCODE createMatrixComponents( return SCIP_OKAY; } -/**< Frees the matrix components data structure */ +/** Frees the matrix components data structure */ static void freeMatrixComponents( SCIP* scip, /**< SCIP data structure */ @@ -154,7 +171,8 @@ void freeMatrixComponents( ) { MATRIX_COMPONENTS* comp = *pmatrixcomponents; - /**< Make sure to free in reverse */ + + /* Make sure to free in reverse */ SCIPfreeBufferArray(scip, &comp->componentcolend); SCIPfreeBufferArray(scip, &comp->componentrowend); SCIPfreeBufferArray(scip, &comp->componentcols); @@ -166,8 +184,10 @@ void freeMatrixComponents( SCIPfreeBuffer(scip, pmatrixcomponents); } -/**< Finds the representative of an element in the disjoint set datastructure. - * Afterwards compresses the path to speed up subsequent queries. */ +/** Finds the representative of an element in the disjoint set datastructure. + * + * Afterwards compresses the path to speed up subsequent queries. + */ static int disjointSetFind( int* disjointset, /**< The array storing the disjoint set representatives */ @@ -175,6 +195,7 @@ int disjointSetFind( ) { assert(disjointset != NULL); + int current = ind; int next; /* traverse down tree */ @@ -191,11 +212,14 @@ int disjointSetFind( disjointset[current] = root; current = next; } + return root; } -/**< Merges two sets/elements into one set. Returns the index of the merged element. - * The provided elements to be merged must be representative (i.e. returned by disjointSetFind()). */ +/** Merges two sets/elements into one set. Returns the index of the merged element. + * + * The provided elements to be merged must be representative (i.e. returned by disjointSetFind()). + */ static int disjointSetMerge( int* disjointset, /**< The array storing the disjoint set representatives */ @@ -210,7 +234,8 @@ int disjointSetMerge( /* The rank is stored as a negative number: we decrement it making the negative number larger. * The rank is an upper bound on the height of the tree. We want the new root to be the one with 'largest' rank, - * so smallest number. This way, we ensure that the tree remains shallow. If they are equal, we decrement. */ + * so smallest number. This way, we ensure that the tree remains shallow. If they are equal, we decrement. + */ int firstRank = disjointset[first]; int secondRank = disjointset[second]; if( firstRank > secondRank ) @@ -223,11 +248,13 @@ int disjointSetMerge( { --disjointset[first]; } + return first; } -/**< Computes the continuous connected components, i.e. the connected components of the submatrix given by all - * continuous columns */ +/** Computes the continuous connected components, i.e. the connected components of the submatrix given by all + * continuous columns. + */ static SCIP_RETCODE computeContinuousComponents( SCIP* scip, /**< SCIP data structure */ @@ -246,29 +273,29 @@ SCIP_RETCODE computeContinuousComponents( for( int col = 0; col < comp->nmatrixcols; ++col ) { - if( comp->coltype[col] != SCIP_VARTYPE_CONTINUOUS) + if( comp->coltype[col] != SCIP_VARTYPE_CONTINUOUS ) { continue; } int colnnonzs = SCIPmatrixGetColNNonzs(matrix, col); int* colrows = SCIPmatrixGetColIdxPtr(matrix, col); - int colrep = disjointSetFind(disjointset,col); + int colrep = disjointSetFind(disjointset, col); for( int i = 0; i < colnnonzs; ++i ) { int colrow = colrows[i]; int ind = colrow + comp->nmatrixcols; - int rowrep = disjointSetFind(disjointset,ind); - if(colrep != rowrep) + int rowrep = disjointSetFind(disjointset, ind); + if( colrep != rowrep ) { - colrep = disjointSetMerge(disjointset,colrep,rowrep); + colrep = disjointSetMerge(disjointset, colrep, rowrep); } } } /** Now, fill in the relevant data. */ - int * representativecomponent; - SCIP_CALL(SCIPallocBufferArray(scip, &representativecomponent, comp->nmatrixcols + comp->nmatrixrows)); + int* representativecomponent; + SCIP_CALL( SCIPallocBufferArray(scip, &representativecomponent, comp->nmatrixcols + comp->nmatrixrows) ); for( int i = 0; i < comp->nmatrixcols + comp->nmatrixrows; ++i ) { @@ -281,9 +308,9 @@ SCIP_RETCODE computeContinuousComponents( { continue; } - int colroot = disjointSetFind(disjointset,col); + int colroot = disjointSetFind(disjointset, col); int component = representativecomponent[colroot]; - if( component < 0) + if( component < 0 ) { assert(component == -1); /* add new component */ @@ -298,12 +325,14 @@ SCIP_RETCODE computeContinuousComponents( } for( int row = 0; row < comp->nmatrixrows; ++row ) { - int rowroot = disjointSetFind(disjointset,row + comp->nmatrixcols); + int rowroot = disjointSetFind(disjointset, row + comp->nmatrixcols); int component = representativecomponent[rowroot]; - if( component < 0){ + if( component < 0 ) + { assert(component == -1); - //Any rows that have roots that we have not seen yet are rows that have no continuous columns - //We can safely skip these for finding the continuous connected components + /* Any rows that have roots that we have not seen yet are rows that have no continuous columns + * We can safely skip these for finding the continuous connected components + */ continue; } comp->rowcomponent[row] = component; @@ -331,7 +360,7 @@ SCIP_RETCODE computeContinuousComponents( for( int col = 0; col < comp->nmatrixcols; ++col ) { int component = comp->colcomponent[col]; - if(component < 0) + if( component < 0 ) { assert(component == -1); continue; @@ -343,7 +372,7 @@ SCIP_RETCODE computeContinuousComponents( for( int row = 0; row < comp->nmatrixrows; ++row ) { int component = comp->rowcomponent[row]; - if(component < 0) + if( component < 0 ) { assert(component == -1); continue; @@ -361,29 +390,17 @@ SCIP_RETCODE computeContinuousComponents( } #endif - SCIPfreeBufferArray(scip,&componentnextcolindex); - SCIPfreeBufferArray(scip,&componentnextrowindex); + SCIPfreeBufferArray(scip, &componentnextcolindex); + SCIPfreeBufferArray(scip, &componentnextrowindex); } - SCIPfreeBufferArray(scip,&representativecomponent); - SCIPfreeBufferArray(scip,&disjointset); + SCIPfreeBufferArray(scip, &representativecomponent); + SCIPfreeBufferArray(scip, &disjointset); + return SCIP_OKAY; } -/**< A temporary data structure that stores some statistics/data on the rows and columns. - * This is freed again after implied integer detection is finished. */ -typedef struct{ - SCIP_Bool* rowintegral; /**< Are all row entries of non-continuous columns and the row sides integral? */ - SCIP_Bool* rowequality; /**< Is the row an equality? */ - SCIP_Bool* rowbadnumerics; /**< Does the row contain large entries that make numerics difficult? */ - int* rownnonz; /**< Number of nonzeros in the row */ - int* rowncontinuous; /**< The number of those nonzeros that are in continuous columns */ - int* rowncontinuouspmone; /**< The number of +-1 entries in continuous columns */ - SCIP_Bool* colintegralbounds; /**< Does the column have integral bounds? */ - -} MATRIX_STATISTICS; - -/**< Creates the matrix statistics data structure */ +/** Creates the matrix statistics data structure */ static SCIP_RETCODE computeMatrixStatistics( SCIP* scip, /**< SCIP data structure */ @@ -394,7 +411,7 @@ SCIP_RETCODE computeMatrixStatistics( * to be unreliable */ ) { - SCIP_CALL( SCIPallocBuffer(scip,pstats) ); + SCIP_CALL( SCIPallocBuffer(scip, pstats) ); MATRIX_STATISTICS* stats = *pstats; int nrows = SCIPmatrixGetNRows(matrix); @@ -410,14 +427,13 @@ SCIP_RETCODE computeMatrixStatistics( SCIP_CALL( SCIPallocBufferArray(scip, &stats->colintegralbounds, ncols) ); - for( int i = 0; i < nrows; ++i ) { - SCIP_Real lhs = SCIPmatrixGetRowLhs(matrix,i); - SCIP_Real rhs = SCIPmatrixGetRowRhs(matrix,i); - int * cols = SCIPmatrixGetRowIdxPtr(matrix,i); - SCIP_Real * vals = SCIPmatrixGetRowValPtr(matrix,i); - int nnonz = SCIPmatrixGetRowNNonzs(matrix,i); + SCIP_Real lhs = SCIPmatrixGetRowLhs(matrix, i); + SCIP_Real rhs = SCIPmatrixGetRowRhs(matrix, i); + int* cols = SCIPmatrixGetRowIdxPtr(matrix, i); + SCIP_Real* vals = SCIPmatrixGetRowValPtr(matrix, i); + int nnonz = SCIPmatrixGetRowNNonzs(matrix, i); stats->rownnonz[i] = nnonz; stats->rowequality[i] = !SCIPisInfinity(scip, -lhs) && !SCIPisInfinity(scip, rhs) && SCIPisEQ(scip, lhs, rhs); @@ -434,15 +450,16 @@ SCIP_RETCODE computeMatrixStatistics( if( continuous ) { ++ncontinuous; - if(SCIPisEQ(scip,ABS(value),1.0)){ + if( SCIPisEQ(scip, ABS(value), 1.0) ) + { ++ncontinuouspmone; } } else { - integral = integral && SCIPisIntegral(scip,value); + integral = integral && SCIPisIntegral(scip, value); } - if(ABS(value) > numericslimit) + if( ABS(value) > numericslimit ) { badnumerics = TRUE; } @@ -453,23 +470,23 @@ SCIP_RETCODE computeMatrixStatistics( stats->rowintegral[i] = integral; stats->rowbadnumerics[i] = badnumerics; } + for( int i = 0; i < ncols; ++i ) { - SCIP_Real lb = SCIPmatrixGetColLb(matrix,i); - SCIP_Real ub = SCIPmatrixGetColUb(matrix,i); - stats->colintegralbounds[i] = ( SCIPisInfinity(scip, -lb) || SCIPisIntegral(scip,lb) ) - && ( SCIPisInfinity(scip, ub) || SCIPisIntegral(scip,ub) ); + SCIP_Real lb = SCIPmatrixGetColLb(matrix, i); + SCIP_Real ub = SCIPmatrixGetColUb(matrix, i); + stats->colintegralbounds[i] = ( SCIPisInfinity(scip, -lb) || SCIPisIntegral(scip, lb) ) + && ( SCIPisInfinity(scip, ub) || SCIPisIntegral(scip, ub) ); /* Check that integer variables have integer bounds, as expected. */ assert(comp->coltype[i] == SCIP_VARTYPE_CONTINUOUS || stats->colintegralbounds[i]); } - return SCIP_OKAY; } -/**< Frees the matrix statistics data structure */ +/** Frees the matrix statistics data structure */ static void freeMatrixStatistics( SCIP* scip, /**< SCIP data structure */ @@ -477,7 +494,8 @@ void freeMatrixStatistics( ) { MATRIX_STATISTICS* stats= *pstats; - /**< make sure, for performance, that these frees occur in reverse */ + + /* Make sure, for performance, that these frees occur in reverse */ SCIPfreeBufferArray(scip, &stats->colintegralbounds); SCIPfreeBufferArray(scip, &stats->rowncontinuouspmone); SCIPfreeBufferArray(scip, &stats->rowncontinuous); @@ -486,11 +504,12 @@ void freeMatrixStatistics( SCIPfreeBufferArray(scip, &stats->rowequality); SCIPfreeBufferArray(scip, &stats->rowintegral); - SCIPfreeBuffer(scip,pstats); + SCIPfreeBuffer(scip, pstats); } -/**< Given the continuous components and statistics on the matrix, detect components that have implied integer variables - * by checking if the component is a (transposed) network matrix detection and if all the bounds/rhs are integral. +/** Given the continuous components and statistics on the matrix, detect components that have implied integer variables + * by checking if the component is a (transposed) network matrix and if all the bounds/sides/coefficients are integral. + * * For every component, we detect if the associated matrix is either a network matrix or a transposed network matrix * (or both, in which case it represents a planar graph). * We choose to check if it is a (transposed) network matrix either in a row-wise or in a column-wise fashion, @@ -507,13 +526,12 @@ SCIP_RETCODE findImpliedIntegers( int* nchgvartypes /**< Pointer to count the number of changed variable types */ ) { - //TODO: some checks to prevent expensive memory initialization if not necessary (e.g. there must be some candidates) - SCIP_NETMATDEC * dec = NULL; - SCIP_CALL( SCIPnetmatdecCreate(SCIPblkmem(scip),&dec,comp->nmatrixrows,comp->nmatrixcols) ); - - SCIP_NETMATDEC * transdec = NULL; - SCIP_CALL( SCIPnetmatdecCreate(SCIPblkmem(scip),&transdec,comp->nmatrixcols,comp->nmatrixrows) ); + /* TODO: some checks to prevent expensive memory initialization if not necessary (e.g. there must be some candidates) */ + SCIP_NETMATDEC* dec = NULL; + SCIP_CALL( SCIPnetmatdecCreate(SCIPblkmem(scip), &dec, comp->nmatrixrows, comp->nmatrixcols) ); + SCIP_NETMATDEC* transdec = NULL; + SCIP_CALL( SCIPnetmatdecCreate(SCIPblkmem(scip), &transdec, comp->nmatrixcols, comp->nmatrixrows) ); int ngoodcomponents = 0; int nbadnumerics = 0; @@ -521,12 +539,12 @@ SCIP_RETCODE findImpliedIntegers( int nnonnetwork = 0; /* Because the rows may also contain non-continuous columns, we need to remove these from the array that we - * pass to the network matrix decomposition method. We use these working arrays for this purpose. */ + * pass to the network matrix decomposition method. We use these working arrays for this purpose. + */ SCIP_Real* tempValArray; int* tempIdxArray; - SCIP_CALL(SCIPallocBufferArray(scip,&tempValArray,comp->nmatrixcols)); - SCIP_CALL(SCIPallocBufferArray(scip,&tempIdxArray,comp->nmatrixcols)); - + SCIP_CALL(SCIPallocBufferArray(scip, &tempValArray, comp->nmatrixcols)); + SCIP_CALL(SCIPallocBufferArray(scip, &tempIdxArray, comp->nmatrixcols)); for( int component = 0; component < comp->ncomponents; ++component ) { @@ -536,26 +554,27 @@ SCIP_RETCODE findImpliedIntegers( for( int i = startrow; i < startrow + nrows; ++i ) { int row = comp->componentrows[i]; - if(stats->rowncontinuous[row] != stats->rowncontinuouspmone[row] ||!stats->rowintegral[row] ) + if( stats->rowncontinuous[row] != stats->rowncontinuouspmone[row] || !stats->rowintegral[row] ) { componentokay = FALSE; ++nbadintegrality; break; } - if(stats->rowbadnumerics[row]) + if( stats->rowbadnumerics[row] ) { componentokay = FALSE; ++nbadnumerics; break; } } - int startcol = (component == 0) ? 0 : comp->componentcolend[component-1]; + int startcol = ( component == 0 ) ? 0 : comp->componentcolend[component-1]; int ncols = comp->componentcolend[component] - startcol; for( int i = startcol; i < startcol + ncols && componentokay; ++i ) { int col = comp->componentcols[i]; - if( !stats->colintegralbounds[col] ){ + if( !stats->colintegralbounds[col] ) + { componentokay = FALSE; ++nbadintegrality; break; @@ -579,9 +598,9 @@ SCIP_RETCODE findImpliedIntegers( for( int i = startrow; i < startrow + nrows && componentnetwork; ++i ) { int row = comp->componentrows[i]; - int nrownnoz = SCIPmatrixGetRowNNonzs(matrix,row); - int* rowcols = SCIPmatrixGetRowIdxPtr(matrix,row); - SCIP_Real* rowvals = SCIPmatrixGetRowValPtr(matrix,row); + int nrownnoz = SCIPmatrixGetRowNNonzs(matrix, row); + int* rowcols = SCIPmatrixGetRowIdxPtr(matrix, row); + SCIP_Real* rowvals = SCIPmatrixGetRowValPtr(matrix, row); int ncontnonz = 0; for( int j = 0; j < nrownnoz; ++j ) { @@ -591,11 +610,11 @@ SCIP_RETCODE findImpliedIntegers( tempIdxArray[ncontnonz] = col; tempValArray[ncontnonz] = rowvals[j]; ++ncontnonz; - assert(SCIPisEQ(scip,ABS(rowvals[j]),1.0)); + assert(SCIPisEQ(scip, ABS(rowvals[j]), 1.0)); } } - SCIP_CALL( SCIPnetmatdecTryAddRow(dec,row,tempIdxArray,tempValArray,ncontnonz,&componentnetwork) ); + SCIP_CALL( SCIPnetmatdecTryAddRow(dec, row, tempIdxArray, tempValArray, ncontnonz, &componentnetwork) ); } } else @@ -603,23 +622,24 @@ SCIP_RETCODE findImpliedIntegers( for( int i = startcol; i < startcol + ncols && componentnetwork; ++i ) { int col = comp->componentcols[i]; - int ncolnnonz = SCIPmatrixGetColNNonzs(matrix,col); - int* colrows = SCIPmatrixGetColIdxPtr(matrix,col); - SCIP_Real* colvals = SCIPmatrixGetColValPtr(matrix,col); - SCIP_CALL( SCIPnetmatdecTryAddCol(dec,col,colrows,colvals,ncolnnonz,&componentnetwork) ); + int ncolnnonz = SCIPmatrixGetColNNonzs(matrix, col); + int* colrows = SCIPmatrixGetColIdxPtr(matrix, col); + SCIP_Real* colvals = SCIPmatrixGetColValPtr(matrix, col); + SCIP_CALL( SCIPnetmatdecTryAddCol(dec, col, colrows, colvals, ncolnnonz, &componentnetwork) ); } } if( !componentnetwork ) { - SCIPnetmatdecRemoveComponent(dec,&comp->componentrows[startrow], nrows, &comp->componentcols[startcol], ncols); + SCIPnetmatdecRemoveComponent(dec, &comp->componentrows[startrow], nrows, &comp->componentcols[startcol], ncols); } SCIP_Bool componenttransnetwork = TRUE; - /* For the transposed matrix, the situation is exactly reversed because the row/column algorithms are swapped */ - /* We only run transposed network detection if network detection failed */ - if(!componentnetwork) + /* For the transposed matrix, the situation is exactly reversed because the row/column algorithms are swapped + * We only run transposed network detection if network detection failed + */ + if( !componentnetwork ) { if( nrows <= ncols * presoldata->columnrowratio ) { @@ -642,8 +662,8 @@ SCIP_RETCODE findImpliedIntegers( } } - SCIP_CALL(SCIPnetmatdecTryAddCol(transdec, row, tempIdxArray, tempValArray, ncontnonz, - &componenttransnetwork)); + SCIP_CALL( SCIPnetmatdecTryAddCol(transdec, row, tempIdxArray, tempValArray, ncontnonz, + &componenttransnetwork) ); } } else @@ -654,18 +674,19 @@ SCIP_RETCODE findImpliedIntegers( int ncolnnonz = SCIPmatrixGetColNNonzs(matrix, col); int* colrows = SCIPmatrixGetColIdxPtr(matrix, col); SCIP_Real* colvals = SCIPmatrixGetColValPtr(matrix, col); - SCIP_CALL(SCIPnetmatdecTryAddRow(transdec, col, colrows, colvals, ncolnnonz, &componenttransnetwork)); + SCIP_CALL( SCIPnetmatdecTryAddRow(transdec, col, colrows, colvals, ncolnnonz, &componenttransnetwork) ); } } } if( !componenttransnetwork ) { - SCIPnetmatdecRemoveComponent(transdec,&comp->componentcols[startcol], - ncols, &comp->componentrows[startrow], nrows); + SCIPnetmatdecRemoveComponent(transdec, &comp->componentcols[startcol], ncols, + &comp->componentrows[startrow], nrows); } - if( !componentnetwork && !componenttransnetwork){ + if( !componentnetwork && !componenttransnetwork) + { ++nnonnetwork; continue; } @@ -674,24 +695,26 @@ SCIP_RETCODE findImpliedIntegers( for( int i = startcol; i < startcol + ncols; ++i ) { int col = comp->componentcols[i]; - SCIP_VAR * var = SCIPmatrixGetVar(matrix,col); + SCIP_VAR* var = SCIPmatrixGetVar(matrix, col); SCIP_Bool infeasible = FALSE; - SCIP_CALL(SCIPchgVarType(scip,var,SCIP_VARTYPE_IMPLINT,&infeasible)); + SCIP_CALL(SCIPchgVarType(scip, var, SCIP_VARTYPE_IMPLINT, &infeasible)); (*nchgvartypes)++; assert(!infeasible); } } - if(*nchgvartypes == 0){ + if( *nchgvartypes == 0 ) + { SCIPverbMessage(scip, SCIP_VERBLEVEL_FULL, NULL, "No implied integers detected \n"); - }else{ + } + else + { SCIPverbMessage(scip, SCIP_VERBLEVEL_FULL, NULL, "%d implied integers in %d / %d components (disqualified: %d by integrality, %d by numerics, %d not network) \n", *nchgvartypes, ngoodcomponents, comp->ncomponents, nbadintegrality, nbadnumerics, nnonnetwork); } - - SCIPfreeBufferArray(scip,&tempIdxArray); - SCIPfreeBufferArray(scip,&tempValArray); + SCIPfreeBufferArray(scip, &tempIdxArray); + SCIPfreeBufferArray(scip, &tempValArray); SCIPnetmatdecFree(&transdec); SCIPnetmatdecFree(&dec); @@ -703,26 +726,7 @@ SCIP_RETCODE findImpliedIntegers( * Callback methods of presolver */ -/* TODO: Implement all necessary presolver methods. The methods with an #if 0 ... #else #define ... are optional */ - - -/** copy method for constraint handler plugins (called when SCIP copies plugins) */ -#if 0 -static -SCIP_DECL_PRESOLCOPY(presolCopyImplint) -{ /*lint --e{715}*/ - SCIPerrorMessage("method of implint presolver not implemented yet\n"); - SCIPABORT(); /*lint --e{527}*/ - - return SCIP_OKAY; -} -#else -#define presolCopyImplint NULL -#endif - - /** destructor of presolver to free user data (called when SCIP is exiting) */ - static SCIP_DECL_PRESOLFREE(presolFreeImplint) { @@ -738,77 +742,15 @@ SCIP_DECL_PRESOLFREE(presolFreeImplint) return SCIP_OKAY; } - -/** initialization method of presolver (called after problem was transformed) */ -#if 0 -static -SCIP_DECL_PRESOLINIT(presolInitImplint) -{ /*lint --e{715}*/ - SCIPerrorMessage("method of implint presolver not implemented yet\n"); - SCIPABORT(); /*lint --e{527}*/ - - return SCIP_OKAY; -} -#else -#define presolInitImplint NULL -#endif - - -/** deinitialization method of presolver (called before transformed problem is freed) */ -#if 0 -static -SCIP_DECL_PRESOLEXIT(presolExitImplint) -{ /*lint --e{715}*/ - SCIPerrorMessage("method of implint presolver not implemented yet\n"); - SCIPABORT(); /*lint --e{527}*/ - - return SCIP_OKAY; -} -#else -#define presolExitImplint NULL -#endif - - -/** presolving initialization method of presolver (called when presolving is about to begin) */ -#if 0 -static -SCIP_DECL_PRESOLINITPRE(presolInitpreImplint) -{ /*lint --e{715}*/ - SCIPerrorMessage("method of implint presolver not implemented yet\n"); - SCIPABORT(); /*lint --e{527}*/ - - return SCIP_OKAY; -} -#else -#define presolInitpreImplint NULL -#endif - - -/** presolving deinitialization method of presolver (called after presolving has been finished) */ -#if 0 -static -SCIP_DECL_PRESOLEXITPRE(presolExitpreImplint) -{ /*lint --e{715}*/ - SCIPerrorMessage("method of implint presolver not implemented yet\n"); - SCIPABORT(); /*lint --e{527}*/ - - return SCIP_OKAY; -} -#else -#define presolExitpreImplint NULL -#endif - - /** execution method of presolver */ - static SCIP_DECL_PRESOLEXEC(presolExecImplint) { /*lint --e{715}*/ *result = SCIP_DIDNOTRUN; - //TODO: re-check these conditions again + /* TODO: re-check these conditions again */ /* Disable implicit integer detection if we are probing or in NLP context */ - if(( SCIPgetStage(scip) != SCIP_STAGE_PRESOLVING ) || SCIPinProbing(scip) || SCIPisNLPEnabled(scip)) + if( ( SCIPgetStage(scip) != SCIP_STAGE_PRESOLVING ) || SCIPinProbing(scip) || SCIPisNLPEnabled(scip) ) { return SCIP_OKAY; } @@ -822,7 +764,7 @@ SCIP_DECL_PRESOLEXEC(presolExecImplint) /* Exit early if there are no candidates variables to upgrade */ SCIP_PRESOLDATA* presoldata = SCIPpresolGetData(presol); - if(!presoldata->convertintegers && SCIPgetNContVars(scip) == 0) + if( !presoldata->convertintegers && SCIPgetNContVars(scip) == 0 ) { return SCIP_OKAY; } @@ -850,7 +792,7 @@ SCIP_DECL_PRESOLEXEC(presolExecImplint) } /*For now, we only work on pure MILP's TODO; use uplocks/downlocks */ - if( !( initialized && complete )) + if( !( initialized && complete ) ) { if( initialized ) { @@ -871,7 +813,6 @@ SCIP_DECL_PRESOLEXEC(presolExecImplint) SCIP_CALL( findImpliedIntegers(scip, presoldata, matrix, comp, stats, nchgvartypes) ); int afterchanged = *nchgvartypes; - SCIP_Real endtime = SCIPgetSolvingTime(scip); if( afterchanged == beforechanged ) { @@ -886,9 +827,10 @@ SCIP_DECL_PRESOLEXEC(presolExecImplint) *result = SCIP_SUCCESS; } - freeMatrixStatistics(scip,&stats); + freeMatrixStatistics(scip, &stats); freeMatrixComponents(scip, &comp); SCIPmatrixFree(scip, &matrix); + return SCIP_OKAY; } @@ -915,12 +857,7 @@ SCIP_RETCODE SCIPincludePresolImplint( assert(presol != NULL); /* set non fundamental callbacks via setter functions */ - SCIP_CALL( SCIPsetPresolCopy(scip, presol, presolCopyImplint) ); SCIP_CALL( SCIPsetPresolFree(scip, presol, presolFreeImplint) ); - SCIP_CALL( SCIPsetPresolInit(scip, presol, presolInitImplint) ); - SCIP_CALL( SCIPsetPresolExit(scip, presol, presolExitImplint) ); - SCIP_CALL( SCIPsetPresolInitpre(scip, presol, presolInitpreImplint) ); - SCIP_CALL( SCIPsetPresolExitpre(scip, presol, presolExitpreImplint) ); SCIP_CALL( SCIPaddBoolParam(scip, "presolving/implint/convertintegers", @@ -930,12 +867,12 @@ SCIP_RETCODE SCIPincludePresolImplint( SCIP_CALL( SCIPaddRealParam(scip, "presolving/implint/columnrowratio", - "Use the network row addition algorithm when the column to row ratio becomes larger than this threshold.", - &presoldata->columnrowratio, TRUE, DEFAULT_COLUMNROWRATIO,0.0,1e12, NULL, NULL) ); + "use the network row addition algorithm when the column to row ratio becomes larger than this threshold", + &presoldata->columnrowratio, TRUE, DEFAULT_COLUMNROWRATIO, 0.0, 1e98, NULL, NULL) ); - SCIP_CALL(SCIPaddRealParam(scip, - "presolving/implint/numericslimit", - "A row that contains variables with coefficients that are greater in absolute value than this limit is not considered for implied integrality detection.", - &presoldata->numericslimit, TRUE, DEFAULT_NUMERICSLIMIT, 1e5, 1e99, NULL, NULL) ); + SCIP_CALL( SCIPaddRealParam(scip, + "presolving/implint/numericslimit", + "a row that contains variables with coefficients that are greater in absolute value than this limit is not considered for implied integrality detection", + &presoldata->numericslimit, TRUE, DEFAULT_NUMERICSLIMIT, 1.0, 1e98, NULL, NULL) ); return SCIP_OKAY; } diff --git a/src/scip/presol_implint.h b/src/scip/presol_implint.h index e935de83ed..f0206752a3 100644 --- a/src/scip/presol_implint.h +++ b/src/scip/presol_implint.h @@ -67,7 +67,7 @@ extern "C" { * @ingroup PresolverIncludes */ SCIP_EXPORT - SCIP_RETCODE SCIPincludePresolImplint( +SCIP_RETCODE SCIPincludePresolImplint( SCIP* scip /**< SCIP data structure */ ); @@ -76,4 +76,4 @@ SCIP_EXPORT } #endif -#endif //__SCIP_PRESOL_IMPLINT_H__ +#endif /* __SCIP_PRESOL_IMPLINT_H__ */ diff --git a/src/scip/type_hypergraph.h b/src/scip/type_hypergraph.h index c2fa370434..5dffbc035d 100644 --- a/src/scip/type_hypergraph.h +++ b/src/scip/type_hypergraph.h @@ -64,8 +64,7 @@ typedef int SCIP_HYPERGRAPH_EDGE; typedef int SCIP_HYPERGRAPH_OVERLAP; -/** - * Called by \ref SCIPhypergraphOverlapFind, \ref SCIPhypergraphOverlapIntersect and \ref SCIPhypergraphComputeOverlaps +/** Called by \ref SCIPhypergraphOverlapFind, \ref SCIPhypergraphOverlapIntersectEdges and \ref SCIPhypergraphComputeOverlaps * whenever a new overlap set is created or an existing overlap is found. */ #define SCIP_DECL_HYPERGRAPH_OVERLAP(x) SCIP_RETCODE x (SCIP_HYPERGRAPH* hypergraph, SCIP_HYPERGRAPH_OVERLAP overlap, \ diff --git a/tests/src/network/network.c b/tests/src/network/network.c index f8812b1e29..930ddbfd1c 100644 --- a/tests/src/network/network.c +++ b/tests/src/network/network.c @@ -34,10 +34,10 @@ #include "include/scip_test.h" /** - * Because the algorithm and data structures used to check for network matrices are rather complex, + * Because the algorithm and data structures used for detecting network matrices are rather complex, * we extensively test them in this file. We do this by checking if the cycles of the graphs represented by * the decomposition match the nonzero entries of the columns of the matrix that we supplied, for many different graphs. - * Most of the the specific graphs tested either contain some special case or posed challenges during development. + * Most of the specific graphs tested either contain some special case or posed challenges during development. */ static SCIP* scip; @@ -46,38 +46,39 @@ static void setup(void) { /* create scip */ - SCIP_CALL(SCIPcreate(&scip)); - + SCIP_CALL( SCIPcreate(&scip) ); } static void teardown(void) { /* free scip */ - SCIP_CALL(SCIPfree(&scip)); + SCIP_CALL( SCIPfree(&scip) ); } -/** CSR/CSC matrix type to encode testing matrices **/ +/* CSR/CSC matrix type to encode testing matrices */ typedef struct { int nrows; int ncols; int nnonzs; - bool isRowWise; //True -> CSR matrix, False -> CSC matrix + bool isRowWise; /* True -> CSR matrix, False -> CSC matrix */ - int* primaryIndexStart; // - int* entrySecondaryIndex; // column with CSR and row with CSC matrix - double* entryValue; + int* firstIndex; /* Array containing the index of the first nonzero of the row (column) + * for the CSR (CSC) matrix */ + int* entryIndex; /* Array containing the entries columns (rows) for the CSR (CSC) matrix */ + double* entryValue; /* Array containing the entry values */ } DirectedTestCase; -static DirectedTestCase stringToTestCase( - const char* string, - int rows, - int cols -) +/**< Create a testcase from a string */ +static +DirectedTestCase stringToTestCase( + const char* string, /**< The string to convert to a test case */ + int rows, /**< The number of rows of the matrix to create */ + int cols /**< The number of columns of the matrix to create */ + ) { - DirectedTestCase testCase; testCase.nrows = rows; testCase.ncols = cols; @@ -85,23 +86,23 @@ static DirectedTestCase stringToTestCase( testCase.isRowWise = true; - testCase.primaryIndexStart = malloc(sizeof(int) * ( rows + 1 )); + testCase.firstIndex = malloc(sizeof(int) * ( rows + 1 )); int nonzeroArraySize = 8; - testCase.entrySecondaryIndex = malloc(sizeof(int) * nonzeroArraySize); + testCase.entryIndex = malloc(sizeof(int) * nonzeroArraySize); testCase.entryValue = malloc(sizeof(double) * nonzeroArraySize); - const char* current = &string[0]; + const char* current = string; int i = 0; - while( *current != '\0' ) + while(i < rows * cols && *current != '\0' ) { char* next = NULL; double num = strtod(current, &next); if( i % cols == 0 ) { - testCase.primaryIndexStart[i / cols] = testCase.nnonzs; + testCase.firstIndex[i / cols] = testCase.nnonzs; } if( num != 0.0 ) { @@ -109,26 +110,26 @@ static DirectedTestCase stringToTestCase( { int newSize = nonzeroArraySize * 2; testCase.entryValue = realloc(testCase.entryValue, sizeof(double) * newSize); - testCase.entrySecondaryIndex = realloc(testCase.entrySecondaryIndex, sizeof(int) * newSize); + testCase.entryIndex = realloc(testCase.entryIndex, sizeof(int) * newSize); nonzeroArraySize = newSize; } testCase.entryValue[testCase.nnonzs] = num; - testCase.entrySecondaryIndex[testCase.nnonzs] = i % cols; + testCase.entryIndex[testCase.nnonzs] = i % cols; ++testCase.nnonzs; } current = next; ++i; - if( i == rows * cols ) - { - break; - } } - testCase.primaryIndexStart[testCase.nrows] = testCase.nnonzs; + testCase.firstIndex[testCase.nrows] = testCase.nnonzs; return testCase; } -static void transposeMatrixStorage(DirectedTestCase* testCase) +/**< Transposes a testcase */ +static +void transposeMatrixStorage( + DirectedTestCase* testCase /**< The testcase to transpose (in place) */ + ) { int numPrimaryDimension = testCase->isRowWise ? testCase->nrows : testCase->ncols; int numSecondaryDimension = testCase->isRowWise ? testCase->ncols : testCase->nrows; @@ -143,7 +144,7 @@ static void transposeMatrixStorage(DirectedTestCase* testCase) } for( int i = 0; i < testCase->nnonzs; ++i ) { - ++( transposedFirstIndex[testCase->entrySecondaryIndex[i] + 1] ); + ++( transposedFirstIndex[testCase->entryIndex[i] + 1] ); } for( int i = 1; i < numSecondaryDimension; ++i ) @@ -153,11 +154,11 @@ static void transposeMatrixStorage(DirectedTestCase* testCase) for( int i = 0; i < numPrimaryDimension; ++i ) { - int first = testCase->primaryIndexStart[i]; - int beyond = testCase->primaryIndexStart[i + 1]; + int first = testCase->firstIndex[i]; + int beyond = testCase->firstIndex[i + 1]; for( int entry = first; entry < beyond; ++entry ) { - int index = testCase->entrySecondaryIndex[entry]; + int index = testCase->entryIndex[entry]; int transIndex = transposedFirstIndex[index]; transposedEntryIndex[transIndex] = i; transposedEntryValue[transIndex] = testCase->entryValue[entry]; @@ -170,18 +171,22 @@ static void transposeMatrixStorage(DirectedTestCase* testCase) } transposedFirstIndex[0] = 0; - free(testCase->entrySecondaryIndex); + free(testCase->entryIndex); free(testCase->entryValue); - free(testCase->primaryIndexStart); + free(testCase->firstIndex); - testCase->primaryIndexStart = transposedFirstIndex; - testCase->entrySecondaryIndex = transposedEntryIndex; + testCase->firstIndex = transposedFirstIndex; + testCase->entryIndex = transposedEntryIndex; testCase->entryValue = transposedEntryValue; testCase->isRowWise = !testCase->isRowWise; } -static DirectedTestCase copyTestCase(DirectedTestCase* testCase) +/**< Copies a test case */ +static +DirectedTestCase copyTestCase( + DirectedTestCase* testCase /**< The test case to copy */ + ) { DirectedTestCase copy; copy.nrows = testCase->nrows; @@ -190,75 +195,85 @@ static DirectedTestCase copyTestCase(DirectedTestCase* testCase) copy.isRowWise = testCase->isRowWise; int size = ( testCase->isRowWise ? testCase->nrows : testCase->ncols ) + 1; - copy.primaryIndexStart = malloc(sizeof(int) * size); + copy.firstIndex = malloc(sizeof(int) * size); for( int i = 0; i < size; ++i ) { - copy.primaryIndexStart[i] = testCase->primaryIndexStart[i]; + copy.firstIndex[i] = testCase->firstIndex[i]; } - copy.entrySecondaryIndex = malloc(sizeof(int) * testCase->nnonzs); + copy.entryIndex = malloc(sizeof(int) * testCase->nnonzs); copy.entryValue = malloc(sizeof(double) * testCase->nnonzs); for( int i = 0; i < testCase->nnonzs; ++i ) { - copy.entrySecondaryIndex[i] = testCase->entrySecondaryIndex[i]; + copy.entryIndex[i] = testCase->entryIndex[i]; copy.entryValue[i] = testCase->entryValue[i]; } return copy; } -static void freeTestCase(DirectedTestCase* testCase) +/**< Frees a testcase */ +static +void freeTestCase( + DirectedTestCase* testCase /**< The test case to free */ + ) { - free(testCase->primaryIndexStart); - free(testCase->entrySecondaryIndex); + free(testCase->firstIndex); + free(testCase->entryIndex); free(testCase->entryValue); - } -static SCIP_RETCODE runColumnTestCase( - DirectedTestCase* testCase, - bool isExpectedNetwork, - bool isExpectedNotNetwork -) +/**< Runs and checks whether a testcase is executed correctly with the network column addition algorithm. + * This checks the cycles of the network matrix at every step. If isExpected(Not)Network is set, we check if the + * complete matrix is (not) network, and return an error otherwise. + */ +static +SCIP_RETCODE runColumnTestCase( + DirectedTestCase* testCase, /**< The testcase to check */ + bool expectedNetwork, /**< If the complete matrix is not detected to be a network matrix, return an error */ + bool expectedNotNetwork /**< If the complete matrix is detected to be a network matrix, return an error */ + ) { if( testCase->isRowWise ) { transposeMatrixStorage(testCase); } - cr_expect(!testCase->isRowWise); + cr_assert(!testCase->isRowWise); SCIP_NETMATDEC* dec = NULL; - BMS_BLKMEM * blkmem = SCIPblkmem(scip); - BMS_BUFMEM * bufmem = SCIPbuffer(scip); - SCIP_CALL(SCIPnetmatdecCreate(blkmem, &dec, testCase->nrows, testCase->ncols)); + BMS_BLKMEM* blkmem = SCIPblkmem(scip); + BMS_BUFMEM* bufmem = SCIPbuffer(scip); + SCIP_CALL( SCIPnetmatdecCreate(blkmem, &dec, testCase->nrows, testCase->ncols) ); SCIP_Bool isNetwork = TRUE; int* tempColumnStorage; SCIP_Bool* tempSignStorage; - SCIP_CALL(SCIPallocBufferArray(scip, &tempColumnStorage, testCase->nrows)); - SCIP_CALL(SCIPallocBufferArray(scip, &tempSignStorage, testCase->nrows)); + SCIP_CALL( SCIPallocBufferArray(scip, &tempColumnStorage, testCase->nrows) ); + SCIP_CALL( SCIPallocBufferArray(scip, &tempSignStorage, testCase->nrows) ); for( int i = 0; i < testCase->ncols; ++i ) { - int colEntryStart = testCase->primaryIndexStart[i]; - int colEntryEnd = testCase->primaryIndexStart[i + 1]; - int* nonzeroRows = &testCase->entrySecondaryIndex[colEntryStart]; + int colEntryStart = testCase->firstIndex[i]; + int colEntryEnd = testCase->firstIndex[i + 1]; + int* nonzeroRows = &testCase->entryIndex[colEntryStart]; double* nonzeroValues = &testCase->entryValue[colEntryStart]; int nonzeros = colEntryEnd - colEntryStart; cr_assert(nonzeros >= 0); - //Check if adding the column preserves the network matrix - SCIP_CALL(SCIPnetmatdecTryAddCol(dec,i,nonzeroRows,nonzeroValues,nonzeros,&isNetwork)); - if(!isNetwork){ + /* Check if adding the column preserves the network matrix */ + SCIP_CALL( SCIPnetmatdecTryAddCol(dec, i, nonzeroRows, nonzeroValues, nonzeros, &isNetwork) ); + if( !isNetwork ) + { break; } cr_expect(SCIPnetmatdecIsMinimal(dec)); - //Check if the computed network matrix indeed reflects the network matrix, - //by checking if the fundamental cycles are all correct + /* Check if the computed network matrix indeed reflects the network matrix, + * by checking if the fundamental cycles are all correct + */ for( int j = 0; j <= i; ++j ) { - int jColEntryStart = testCase->primaryIndexStart[j]; - int jColEntryEnd = testCase->primaryIndexStart[j + 1]; - int* jNonzeroRows = &testCase->entrySecondaryIndex[jColEntryStart]; + int jColEntryStart = testCase->firstIndex[j]; + int jColEntryEnd = testCase->firstIndex[j + 1]; + int* jNonzeroRows = &testCase->entryIndex[jColEntryStart]; double* jNonzeroValues = &testCase->entryValue[jColEntryStart]; int jNonzeros = jColEntryEnd - jColEntryStart; SCIP_Bool cycleIsCorrect = SCIPnetmatdecVerifyCycle(bufmem, dec, j, @@ -271,88 +286,98 @@ static SCIP_RETCODE runColumnTestCase( } - if( isExpectedNetwork ) + if( expectedNetwork ) { - //We expect that the given matrix is a network matrix. If not, something went wrong. + /* We expect that the given matrix is a network matrix. If not, something went wrong */ cr_expect(isNetwork); } - if( isExpectedNotNetwork ) + if( expectedNotNetwork ) { - //We expect that the given matrix is not a network matrix. If not, something went wrong. + /* We expect that the given matrix is not a network matrix. If not, something went wrong */ cr_expect(!isNetwork); } SCIPfreeBufferArray(scip, &tempColumnStorage); SCIPfreeBufferArray(scip, &tempSignStorage); SCIPnetmatdecFree(&dec); + cr_assert(dec == NULL); + return SCIP_OKAY; } -static SCIP_RETCODE runRowTestCase( - DirectedTestCase* testCase, - bool isExpectedNetwork, - bool isExpectedNotNetwork -) +/**< Runs and checks whether a testcase is executed correctly with the network row addition algorithm. + * This checks the cycles of the network matrix at every step. If isExpected(Not)Network is set, we check if the + * complete matrix is (not) network, and return an error otherwise. + */ +static +SCIP_RETCODE runRowTestCase( + DirectedTestCase* testCase, /**< The testcase to check */ + bool expectedNetwork, /**< If the complete matrix is not detected to be a network matrix, return an error */ + bool expectedNotNetwork /**< If the complete matrix is detected to be a network matrix, return an error */ + ) { if( !testCase->isRowWise ) { transposeMatrixStorage(testCase); } - cr_expect(testCase->isRowWise); + cr_assert(testCase->isRowWise); - //We keep a column-wise copy to check the columns easily + /* We keep a column-wise copy to check the columns easily */ DirectedTestCase colWiseCase = copyTestCase(testCase); transposeMatrixStorage(&colWiseCase); - BMS_BLKMEM * blkmem = SCIPblkmem(scip); - BMS_BUFMEM * bufmem = SCIPbuffer(scip); + BMS_BLKMEM* blkmem = SCIPblkmem(scip); + BMS_BUFMEM* bufmem = SCIPbuffer(scip); SCIP_NETMATDEC* dec = NULL; - SCIP_CALL(SCIPnetmatdecCreate(blkmem, &dec, testCase->nrows, testCase->ncols)); + SCIP_CALL( SCIPnetmatdecCreate(blkmem, &dec, testCase->nrows, testCase->ncols) ); SCIP_Bool isNetwork = TRUE; int* tempColumnStorage; SCIP_Bool* tempSignStorage; - SCIP_CALL(SCIPallocBufferArray(scip, &tempColumnStorage, testCase->nrows)); - SCIP_CALL(SCIPallocBufferArray(scip, &tempSignStorage, testCase->nrows)); + SCIP_CALL( SCIPallocBufferArray(scip, &tempColumnStorage, testCase->nrows) ); + SCIP_CALL( SCIPallocBufferArray(scip, &tempSignStorage, testCase->nrows) ); for( int i = 0; i < testCase->nrows; ++i ) { - int rowEntryStart = testCase->primaryIndexStart[i]; - int rowEntryEnd = testCase->primaryIndexStart[i + 1]; - int* nonzeroCols = &testCase->entrySecondaryIndex[rowEntryStart]; + int rowEntryStart = testCase->firstIndex[i]; + int rowEntryEnd = testCase->firstIndex[i + 1]; + int* nonzeroCols = &testCase->entryIndex[rowEntryStart]; double* nonzeroValues = &testCase->entryValue[rowEntryStart]; int nonzeros = rowEntryEnd - rowEntryStart; cr_assert(nonzeros >= 0); - //Check if adding the row preserves the network matrix - SCIP_CALL(SCIPnetmatdecTryAddRow(dec,i,nonzeroCols,nonzeroValues,nonzeros,&isNetwork)); - if(!isNetwork){ + /* Check if adding the row preserves the network matrix */ + SCIP_CALL( SCIPnetmatdecTryAddRow(dec, i, nonzeroCols, nonzeroValues, nonzeros, &isNetwork) ); + if( !isNetwork ) + { break; } cr_expect(SCIPnetmatdecIsMinimal(dec)); - //Check if the computed network matrix indeed reflects the network matrix, - //by checking if the fundamental cycles are all correct + /* Check if the computed network matrix indeed reflects the network matrix, + * by checking if the fundamental cycles are all correct + */ for( int j = 0; j < colWiseCase.ncols; ++j ) { - int jColEntryStart = colWiseCase.primaryIndexStart[j]; - int jColEntryEnd = colWiseCase.primaryIndexStart[j + 1]; + int jColEntryStart = colWiseCase.firstIndex[j]; + int jColEntryEnd = colWiseCase.firstIndex[j + 1]; - //Count the number of rows in the column that should be in the current decomposition + /* Count the number of rows in the column that should be in the current decomposition */ int finalEntryIndex = jColEntryStart; for( int testEntry = jColEntryStart; testEntry < jColEntryEnd; ++testEntry ) { - if( colWiseCase.entrySecondaryIndex[testEntry] <= i ) + if( colWiseCase.entryIndex[testEntry] <= i ) { ++finalEntryIndex; - } else + } + else { break; } } - int* jNonzeroRows = &colWiseCase.entrySecondaryIndex[jColEntryStart]; + int* jNonzeroRows = &colWiseCase.entryIndex[jColEntryStart]; double* jNonzeroValues = &colWiseCase.entryValue[jColEntryStart]; int jNonzeros = finalEntryIndex - jColEntryStart; @@ -365,14 +390,14 @@ static SCIP_RETCODE runRowTestCase( } } - if( isExpectedNetwork ) + if( expectedNetwork ) { - //We expect that the given matrix is a network matrix. If not, something went wrong. + /* We expect that the given matrix is a network matrix. If not, something went wrong */ cr_expect(isNetwork); } - if( isExpectedNotNetwork ) + if( expectedNotNetwork ) { - //We expect that the given matrix is not a network matrix. If not, something went wrong. + /* We expect that the given matrix is not a network matrix. If not, something went wrong */ cr_expect(!isNetwork); } @@ -382,52 +407,61 @@ static SCIP_RETCODE runRowTestCase( SCIPfreeBufferArray(scip, &tempSignStorage); SCIPnetmatdecFree(&dec); + cr_assert(dec == NULL); + return SCIP_OKAY; } -static SCIP_RETCODE runRowTestCaseGraph( - DirectedTestCase* testCase -) +/**< Runs the network row addition, and attempts to construct the graph. + * This functions main purpose is to check if SCIPnetmatdecCreateDiGraph() correctly creates the graph without errors. + */ +static +SCIP_RETCODE runRowTestCaseGraph( + DirectedTestCase* testCase /**< The testcase to check */ + ) { if( !testCase->isRowWise ) { transposeMatrixStorage(testCase); } - cr_expect(testCase->isRowWise); + cr_assert(testCase->isRowWise); - //We keep a column-wise copy to check the columns easily + /* We keep a column-wise copy to check the columns easily */ DirectedTestCase colWiseCase = copyTestCase(testCase); transposeMatrixStorage(&colWiseCase); - BMS_BLKMEM * blkmem = SCIPblkmem(scip); + BMS_BLKMEM* blkmem = SCIPblkmem(scip); SCIP_NETMATDEC* dec = NULL; - SCIP_CALL(SCIPnetmatdecCreate(blkmem, &dec, testCase->nrows, testCase->ncols)); + SCIP_CALL( SCIPnetmatdecCreate(blkmem, &dec, testCase->nrows, testCase->ncols) ); SCIP_Bool isNetwork = TRUE; for( int i = 0; i < testCase->nrows; ++i ) { - int rowEntryStart = testCase->primaryIndexStart[i]; - int rowEntryEnd = testCase->primaryIndexStart[i + 1]; - int* nonzeroCols = &testCase->entrySecondaryIndex[rowEntryStart]; + int rowEntryStart = testCase->firstIndex[i]; + int rowEntryEnd = testCase->firstIndex[i + 1]; + int* nonzeroCols = &testCase->entryIndex[rowEntryStart]; double* nonzeroValues = &testCase->entryValue[rowEntryStart]; int nonzeros = rowEntryEnd - rowEntryStart; cr_assert(nonzeros >= 0); - //Check if adding the row preserves the network matrix - SCIP_CALL(SCIPnetmatdecTryAddRow(dec,i,nonzeroCols,nonzeroValues,nonzeros,&isNetwork)); - assert(isNetwork); + /* Check if adding the row preserves the network matrix */ + SCIP_CALL( SCIPnetmatdecTryAddRow(dec, i, nonzeroCols, nonzeroValues, nonzeros, &isNetwork) ); + cr_assert(isNetwork); } SCIP_DIGRAPH* graph; SCIP_CALL( SCIPnetmatdecCreateDiGraph(dec, blkmem, &graph, TRUE) ); - SCIPdigraphPrint(graph, SCIPgetMessagehdlr(scip),stdout); + SCIPdigraphPrint(graph, SCIPgetMessagehdlr(scip), stdout); SCIPdigraphFree(&graph); freeTestCase(&colWiseCase); SCIPnetmatdecFree(&dec); + cr_assert(dec == NULL); + return SCIP_OKAY; } + TestSuite(network, .init = setup, .fini = teardown); Test(network, coladd_single_column, .description = "Try adding a single column") @@ -1941,7 +1975,7 @@ Test(network, rowadd_singlerigid_8, .description = "Updating a single rigid memb runRowTestCase(&testCase, true, false); freeTestCase(&testCase); } -//TODO: test interleaved addition, test using random sampling + test erdos-renyi generated graphs +/* TODO: test interleaved addition, test using random sampling + test erdos-renyi generated graphs */ Test(network, rowadd_singlerigid_graph, .description = "Computing the graph for a single rigid member") { From 480397cb121623dbd3a24d79e8733dcb280f0b81 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Thu, 17 Oct 2024 10:40:45 +0200 Subject: [PATCH 62/63] Remove convertintegers parameter --- CHANGELOG | 1 - src/scip/presol_implint.c | 10 +--------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 99291c074c..7d4bdacf65 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -67,7 +67,6 @@ Interface changes - new parameter "propagating/symmetry/handlesignedorbitopes" to control whether special symmetry handling techniques for orbitopes whose columns can be (partially) reflected shall be applied - new parameter "propagating/symmetry/usesimplesgncomp" to control whether symmetry components all of whose variables are simultaneously reflected by a symmetry shall be handled by a special inequality - new parameter "propagating/symmetry/dispsyminfo" to control whether information about which symmetry handling methods are applied are printed -- new parameter "presolving/implint/convertintegers" controls whether implied integrality should be detected for integer variables - new parameter "presolving/implint/columnrowratio" indicates the ratio of rows/columns where the row-wise network matrix detection algorithm is used instead of the column-wise network matrix detection algorithm - new parameter "presolving/implint/numericslimit" determines the limit for absolute integral coefficients beyond which the corresponding rows and variables are excluded from implied integer detection diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index f886ec3bd8..ef28b4322e 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -60,14 +60,12 @@ #define PRESOL_MAXROUNDS 0 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */ #define PRESOL_TIMING SCIP_PRESOLTIMING_EXHAUSTIVE /* timing of the presolver (fast, medium, or exhaustive) */ -#define DEFAULT_CONVERTINTEGERS FALSE #define DEFAULT_COLUMNROWRATIO 50.0 #define DEFAULT_NUMERICSLIMIT 1e7 /** presolver data */ struct SCIP_PresolData { - SCIP_Bool convertintegers; /**< Should we detect implied integrality of columns that are integer? */ SCIP_Real columnrowratio; /**< Use the network row addition algorithm when the column to row ratio * becomes larger than this threshold. Otherwise, use column addition. */ SCIP_Real numericslimit; /**< A row that contains variables with coefficients that are greater in @@ -764,7 +762,7 @@ SCIP_DECL_PRESOLEXEC(presolExecImplint) /* Exit early if there are no candidates variables to upgrade */ SCIP_PRESOLDATA* presoldata = SCIPpresolGetData(presol); - if( !presoldata->convertintegers && SCIPgetNContVars(scip) == 0 ) + if( SCIPgetNContVars(scip) == 0 ) { return SCIP_OKAY; } @@ -859,12 +857,6 @@ SCIP_RETCODE SCIPincludePresolImplint( /* set non fundamental callbacks via setter functions */ SCIP_CALL( SCIPsetPresolFree(scip, presol, presolFreeImplint) ); - SCIP_CALL( SCIPaddBoolParam(scip, - "presolving/implint/convertintegers", - "should we detect implied integrality for integer variables in the problem?", - &presoldata->convertintegers, TRUE, DEFAULT_CONVERTINTEGERS, NULL, NULL) ); - - SCIP_CALL( SCIPaddRealParam(scip, "presolving/implint/columnrowratio", "use the network row addition algorithm when the column to row ratio becomes larger than this threshold", From 9b87472f0547d560827af02b66da02c13910ae90 Mon Sep 17 00:00:00 2001 From: rolfvdhulst Date: Thu, 17 Oct 2024 19:27:00 +0200 Subject: [PATCH 63/63] Change default numerics limit to 1e6 --- src/scip/presol_implint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scip/presol_implint.c b/src/scip/presol_implint.c index ef28b4322e..5fbda0ceef 100644 --- a/src/scip/presol_implint.c +++ b/src/scip/presol_implint.c @@ -61,7 +61,7 @@ #define PRESOL_TIMING SCIP_PRESOLTIMING_EXHAUSTIVE /* timing of the presolver (fast, medium, or exhaustive) */ #define DEFAULT_COLUMNROWRATIO 50.0 -#define DEFAULT_NUMERICSLIMIT 1e7 +#define DEFAULT_NUMERICSLIMIT 1e6 /** presolver data */ struct SCIP_PresolData