Scippy

SCIP

Solving Constraint Integer Programs

network.c
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2/* */
3/* This file is part of the program and library */
4/* SCIP --- Solving Constraint Integer Programs */
5/* */
6/* Copyright (c) 2002-2025 Zuse Institute Berlin (ZIB) */
7/* */
8/* Licensed under the Apache License, Version 2.0 (the "License"); */
9/* you may not use this file except in compliance with the License. */
10/* You may obtain a copy of the License at */
11/* */
12/* http://www.apache.org/licenses/LICENSE-2.0 */
13/* */
14/* Unless required by applicable law or agreed to in writing, software */
15/* distributed under the License is distributed on an "AS IS" BASIS, */
16/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
17/* See the License for the specific language governing permissions and */
18/* limitations under the License. */
19/* */
20/* You should have received a copy of the Apache-2.0 license */
21/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */
22/* */
23/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24
25/**@file network.c
26 * @ingroup OTHER_CFILES
27 * @brief Methods for detecting network (sub)matrices
28 * @author Rolf van der Hulst
29 *
30 * Detecting if a matrix is a network matrix can be quite complex. Below is an introductory text, which may help with
31 * navigating the functions and datastructures in this file by giving a general overview.
32 * More details can be found in;
33 * R.P. van der Hulst and M. Walter "A row-wise algorithm for graph realization"
34 * and
35 * R.E. Bixby and D.K. Wagner "An almost linear-time algorithm for graph realization"
36 * for the column-wise algorithm.
37 *
38 * The main difficulty with detecting network matrices is that there may exist many pairs of a graph and a spanning tree
39 * that realize a matrix. The ambiguity of these graphs may be characterized in terms of \f$k\f$-separations on the
40 * graph associated to the network matrix. An edge partition \f$(E_1,E_2)\f$ is considered a \f$k\f$-separation if
41 * \f$|E_i|\geq k \f$ holds for \f$i\in\{1,2\}\f$. The ambiguity of network matrices is completely given by all
42 * the 1-separations and 2-separations of the associated graph(s). In particular, if the graph realizing a
43 * network matrix is 3-connected, then it is unique, up to inverting all edges of the graph.
44 *
45 * 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
46 * articulation node, implies that no column of the network matrix contains edges in both \f$E_1\f$ and \f$E_2\f$.
47 * Remember that each edge in a realizing graph is associated with a row or column of the network matrix. Then, we have
48 * a 1-separation exactly when the network matrix contains two (or more) disconnected blocks, where the rows and columns
49 * 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
50 * attach the graph realizing \f$E_2\f$ at any node of the graph realizing \f$E_1\f$.
51 * Thus, we store the graphs corresponding to the connected blocks of the network matrix separately.
52 * Each block has a 2-connected realization graph.
53 *
54 * 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
55 * obtain another graph representing the network matrix, by inverting the direction of all edges in \f$E_2\f$ and
56 * replacing u by v and vice-versa. One can also imagine
57 * 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
58 * \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
59 * realization we do the same thing, but first invert all the edges in \f$E_2\f$, including \f$e'\f$.
60 * An SPQR tree \f$\mathcal{T}=(\mathcal{V},\mathcal{E})\f$ is a tree data structure that represents the structure of
61 * all 2-separations in a 2-connected graph. Each member \f$\nu\in\mathcal{V}\f$ has an associated skeleton graph that
62 * has one of four different types:
63 * - (S) The member's skeleton graph is a cycle with at least 3 edges (also referred to as series or polygon)
64 * - (P) The member's skeleton graph consists of at least 3 parallel edges and 2 nodes (also referred to as bond)
65 * - (Q) The member's skeleton graph consists of at most 2 edges connecting two nodes (also referred to as loop)
66 * - (R) The member's skeleton graph is 3-connected and consists of at least 4 edges.
67 *
68 * An SPQR tree is considered minimal if it has no P-P or S-S connections. Each connected matrix has a unique minimal
69 * SPQR tree. Each edge \f$\{\nu,\mu\}\in\mathcal{E}\f$ defines a 2-separation of the underlying graph. In particular,
70 * each edge has one virtual edge in the member graph that it connects to the other member graph in the edge.
71 *
72 * We can obtain a realization of the graph underlying the network matrix by doing the following operations:
73 * 1. Permute the edges of each (S)-member arbitrarily
74 * 2. For each edge in \f$\mathcal{E}\f$, pick one of the two orientations of the virtual edges and merge the adjacent
75 * member graphs accordingly.
76 *
77 * In this way, all the graphs given by the network matrix are represented. In order to efficiently perform the merge
78 * of two member graphs, the member and node labels are given by union-find datastructures. Additionally, we also
79 * introduce a signed-union-find datastructure on the arcs of the graphs, so that we can efficiently invert the arcs
80 * of one side of a 2-separation.
81 * The 1-separations can be handled by storing an SPQR forest, with a (minimal) SPQR tree for every connected block
82 * of the network matrix.
83 *
84 * For adding a column to the network matrix, one can show that one can add a column only if the nonzeros of the column
85 * form a path with the correct signs in some graph represented by the network matrix. We solve the problem for each
86 * graph represented by the network matrix simultaneously by decomposing over the SPQR tree. First, we compute the path
87 * in each member. Then, we attempt to combine the paths by orienting the 2-separations so that the different member
88 * paths form a path in the represented graph.
89 * If some member has a set of edges that do not form a path, we can terminate.
90 * An important step is 'propagation'; when we are in a leaf node of the sub-SPQR tree containing path edges and the
91 * path in our leaf node forms a cycle with the virtual arc e connecting to the rest of the sub-tree, then we can
92 * remove the leaf node from the SPQR tree and mark the virtual arc f that is paired with e.
93 * After performing all such propagations, the sub-SPQR tree should form a path. Then, we merge these members into one,
94 * 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.
95 * Finally, we can easily join the paths of multiple SPQR trees using a series node to obtain the final path.
96 *
97 * The ideas for the row-wise algorithm have many parallels with the column-wise algorithm. One can add a row to a
98 * network matrix if and only if a node is 'splittable' with respect to a certain auxiliary graph formed by the nonzero
99 * columns indices of the row, for a graph represented by the network matrix. In particular, this auxiliary graph must
100 * be a directed bipartite graph; then, the arcs incident to the given node can be reassigned to two new nodes, so that
101 * the paths of the columns corresponding to the nonzeros of the row can be elongated to contain the new row, which is
102 * placed between the two new nodes.
103 * Similarly to the column-case, splittability of each graph represented by the network matrix can be computed at once
104 * by computing the splittability (and the corresponding bipartition) of every member graph.
105 * Similarly to the column algorithm, we can propagate; If a member is a leaf of the SPQR tree and both nodes of the
106 * 2-separation connecting it to the rest of graph are splittable, then we can remove the leaf from the reduced
107 * SPQR tree and mark the virtual edge connecting to the subtree.
108 * Finally, we are left with some minimal subtree with splittable vertices for each member graph. If we can merge all
109 * splittable vertices of the member graphs in the subtree into a single splittable vertex, then we perform this merge,
110 * and split this vertex. This yields us a new, larger node of type R (rigid).
111 *
112 * Implementation notes:
113 * 1. Quite a few algorithms used for network matrix detection are recursive in nature. However, recursive calls can
114 * cause stack overflows, particularly with large graphs. Quite frequently in the code, we need to allocate the
115 * call-data of these algorithms on the heap, instead, and use while loops to simulate the recursion.
116 *
117 * 2. In order to make the code fast in practice, a lot of emphasis is put on reusing allocated memory and avoiding
118 * allocations. In particular for the column-wise algorithm, even allocating and zeroing an array of size m+n for an
119 * m x n matrix can significantly slow down the code!
120 *
121 * 3. The graphs of the S, P and Q members do not need to be stored explicitly, as they always have the same structure.
122 * This also makes it easier to permute edges of S nodes on the fly.
123 */
124
125 /* TODO: fix tracking connectivity more cleanly, should not be left up to the algorithms ideally
126 */
127
128#include <assert.h>
129
130#include "scip/misc.h"
131#include "scip/pub_misc.h"
132#include "scip/pub_network.h"
133#include "scip/scip.h"
134#include "blockmemshell/memory.h"
135
136/* types which define matrix sizes */
140
141#define SPQR_INVALID INT_MAX
142#define SPQR_INVALID_ROW SPQR_INVALID
143#define SPQR_INVALID_COL SPQR_INVALID
144
145
146/* Only check in debug mode if the used indices are valid */
147#ifndef NDEBUG
148
149/** Determine if the row index is invalid */
150static
152 spqr_row row /**< The row to check */
153 )
154{
155 return row == SPQR_INVALID_ROW;
156}
157
158/** Determine if the column index is invalid */
159static
161 spqr_col col /**< The column to check */
162 )
163{
164 return col == SPQR_INVALID_COL;
165}
166
167/** Determine if the row index is valid */
168static
170 spqr_row row /**< The row to check */
171 )
172{
173 return !SPQRrowIsInvalid(row);
174}
175
176/** Determine if the column index is valid */
177static
179 spqr_col col /**< The column to check */
180 )
181{
182 return !SPQRcolIsInvalid(col);
183}
184
185#endif
186
187/** Columns 0..x correspond to elements 0..x and rows 0..y correspond to elements -1.. -y-1 */
188#define MARKER_ROW_ELEMENT (INT_MIN)
189#define MARKER_COLUMN_ELEMENT (INT_MAX)
190typedef int spqr_element;
191
192/** Checks if an element is a row */
193static
195 spqr_element element /**< The element to check */
196 )
197{
198 return element < 0;
199}
200
201/** Checks if an element is a column */
202static
204 spqr_element element /**< The element to check */
205 )
206{
207 return !SPQRelementIsRow(element);
208}
209
210/** Convert an element to the corresponding row index */
211static
213 spqr_element element /**< The element to convert */
214 )
215{
216 assert(SPQRelementIsRow(element));
217 return (spqr_row) ( -element - 1 );
218}
219
220/** Convert a row to the corresponding element */
221static
223 spqr_row row /**< The row to convert */
224 )
225{
226 assert(SPQRrowIsValid(row));
227 return (spqr_element) -row - 1;
228}
229
230/** Convert an element to the corresponding column index */
231static
233 spqr_element element /**< The element to convert */
234 )
235{
236 assert(SPQRelementIsColumn(element));
237 return (spqr_col) element;
238}
239
240/** Convert a column to the corresponding element */
241static
243 spqr_col column /**< The column to convert */
244 )
245{
246 assert(SPQRcolIsValid(column));
247 return (spqr_element) column;
248}
249
250/** spqr_member is an index for the members of the SPQR decomposition
251 *
252 * The members are the nodes of the SPQR tree.
253 * Each member has an associated subgraph, sometimes called a skeleton.
254 * If two members are adjacent in the SPQR tree, the two corresponding subgraphs are connected by a 2-separation in any
255 * graph represented by the matrix.
256 * For members, we reserve all negative values as invalid. We use these negative values in union-find datastructure,
257 * where we store the rank of the representative as a negative number.
258 */
259typedef int spqr_member;
260#define SPQR_INVALID_MEMBER (-1)
261
262/** Check if a member is invalid */
263static
265 spqr_member member /**< The member to check */
266 )
267{
268 return member < 0;
269}
270
271/** Check if a member is valid */
273 spqr_member member /**< The member to check */
274 )
275{
276 return !SPQRmemberIsInvalid(member);
277}
278
279/** spqr_node is an index for the nodes stored in the decomposition.
280 *
281 * The nodes are part of each member's skeleton.
282 * Similar to spqr_member, we reserve all negative values as invalid and use these in union-find.
283 */
284typedef int spqr_node;
285#define SPQR_INVALID_NODE (-1)
286
287/** Check if a node is invalid */
288static
290 spqr_node node /**< The node to check */
291 )
292{
293 return node < 0;
294}
295
296/** Check if a node is valid */
297static
299 spqr_node node /**< The node to check */
300 )
301{
302 return !SPQRnodeIsInvalid(node);
303}
304
305/** spqr_arc is an index for the arcs stored in the decomposition.
306 *
307 * The arcs are part of each member's skeleton.
308 * Similar to spqr_node and spqr_member, we reserve all negative values as invalid and use these in some union-find.
309 * However, in contrast to spqr_node and spqr_member, the union-find data structure does not represent the arcs,
310 * but rather if all arcs that have the same representative we know they are in the same member.
311 */
312typedef int spqr_arc;
313#define SPQR_INVALID_ARC (-1)
314
315/** Check if an arc is invalid */
316static
318 spqr_arc arc /**< The arc to check */
319 )
320{
321 return arc < 0;
322}
323
324/** Check if an arc is valid */
325static
327 spqr_arc arc /**< The arc to check */
328 )
329{
330 return !SPQRarcIsInvalid(arc);
331}
332
333/** The type of the member */
334typedef enum
335{
336 SPQR_MEMBERTYPE_RIGID = 0, /**< The member's skeleton is 3-connected and has at least 4 edges */
337 SPQR_MEMBERTYPE_PARALLEL = 1, /**< The member's skeleton consists of 2 nodes with at least 3 edges */
338 SPQR_MEMBERTYPE_SERIES = 2, /**< The member's skeleton is a cycle with at least 3 edges */
339 SPQR_MEMBERTYPE_LOOP = 3, /**< The member's skeleton consists of 2 nodes connected by 1 or 2 edges */
340 SPQR_MEMBERTYPE_UNASSIGNED = 4 /**< To indicate that the member is not a representative member anymore */
342
343/** Represents a single node of cyclic doubly-linked list of arc edges*/
344typedef struct
345{
349
350/** This structure stores the relevant data for a single node. */
351typedef struct
352{
353 spqr_node representativeNode; /**< Points to the next node in the union-find data structure.
354 * Stores the rank as a negative number if this node represents itself */
355 spqr_arc firstArc; /**< Points to the head node of the cyclic doubly linked list containing
356 * the arcs that are adjacent to this node. */
357 int numArcs; /**< The number of arcs adjacent to this node */
359
360/** Structure that stores the relevant data for a single arc. */
361typedef struct
362{
363 spqr_node head; /**< The head node of the arc */
364 spqr_node tail; /**< The tail node of the arc */
365 spqr_member member; /**< The member that contains the arc */
366 spqr_member childMember; /**< Stores the child member, if this arc points to one */
367 SPQRNetworkDecompositionArcListNode headArcListNode; /**< Linked-list node for iterating over the head's arcs */
368 SPQRNetworkDecompositionArcListNode tailArcListNode; /**< Linked-list node for iterating over the tail's arcs */
369 SPQRNetworkDecompositionArcListNode arcListNode; /**< Linked-list node for iterating over the member's arcs */
370
371 spqr_element element; /**< The element associated to this arc */
372
373 /** @name Signed union-find for arc directions.
374 * @{
375 *
376 * If an arc is reversed, it's head becomes its tail and vice-versa.
377 * For non-rigid members every arc is it's own representative, and the direction is simply given by the boolean.
378 * For rigid members, every arc is represented by another arc in the member,
379 * and the direction can be found by multiplying the signs along the union-find path
380 * We use this data structure to efficiently reverse all arcs in a skeleton.
381 */
382 spqr_arc representative; /**< The representative of the arc */
383 SCIP_Bool reversed; /**< Whether the arc's head and tail are reversed, or not */
384 /** @} */
386
387/** Structure that stores the relevant data for a single member */
388typedef struct
389{
390 spqr_member representativeMember; /**< The representative of this member (union-find) */
391 SPQRMemberType type; /**< The type of this member */
392
393 /** @name The SPQR tree is stored as an arborescence.
394 * @{
395 *
396 * Each member stores its parents, and each edge of the member
397 * pointing to a child member stores the associated member in childMember
398 */
399 spqr_member parentMember; /**< The parent of this member in the arborescence */
400 spqr_arc markerToParent; /**< The arc pointing to the parent */
401 spqr_arc markerOfParent; /**< The arc of the parent pointing to this member */
402 /** @} */
403
404 spqr_arc firstArc; /**< First arc of the linked list containing the member's arcs */
405 int numArcs; /**< The number of arcs associated to the member */
407
408/** Stores the SPQR forest data structure and its relevant data */
409typedef struct
410{
411 int numArcs; /**< The number of slots used in the arc data array */
412 int memArcs; /**< The amount of space allocated in the arc data array */
413 SPQRNetworkDecompositionArc* arcs; /**< Array of arcs of the SPQR forest, indexed by spqr_arc */
414 spqr_arc firstFreeArc; /**< Points to the first unused slot in the arcs array */
415
416 int memMembers; /**< The amount of space allocated in the member data array */
417 int numMembers; /**< The number of slots used in the member data array */
418 SPQRNetworkDecompositionMember* members; /**< Array of members of the SPQR forest. Indexed by spqr_member */
419
420 int memNodes; /**< The amount of space allocated in the node data array */
421 int numNodes; /**< The number of slots used in the node data array */
422 SPQRNetworkDecompositionNode* nodes; /**< Array of nodes of the SPQR forest. Indexed by spqr_node */
423
424 int memRows; /**< The (maximal) number of rows of the matrix */
425 spqr_arc* rowArcs; /**< Maps the rows of the matrix to arcs in the decomposition */
426
427 int memColumns; /**< The (maximal) number of columns of the matrix */
428 spqr_arc* columnArcs; /**< Maps the columns of the matrix to arcs in the decomposition */
429
430 BMS_BLKMEM * env; /**< used memory allocator */
431
432 int numConnectedComponents; /**< The number of disjoint SPQR trees in the SPQR forest */
434
435
436#ifndef NDEBUG
437
438/** Check if a node is a representative in the union-find data structure for nodes */
439static
441 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
442 spqr_node node /**< The node to check if it is representative */
443 )
444{
445 assert(dec);
446 assert(node < dec->memNodes);
447 assert(SPQRnodeIsValid(node));
448
449 return SPQRnodeIsInvalid(dec->nodes[node].representativeNode);
450}
451
452#endif
453
454/** Find the node its representative node in the union-find data structure */
455static
457 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
458 spqr_node node /**< The node to find the representative for */
459 )
460{
461 assert(dec);
462 assert(SPQRnodeIsValid(node));
463 assert(node < dec->memNodes);
464
465 spqr_node current = node;
466 spqr_node next;
467
468 //traverse down tree to find the root
469 while( SPQRnodeIsValid(next = dec->nodes[current].representativeNode) )
470 {
471 current = next;
472 assert(current < dec->memNodes);
473 }
474
475 spqr_node root = current;
476 current = node;
477
478 //update all pointers along path to point to root, flattening the tree
479 while( SPQRnodeIsValid(next = dec->nodes[current].representativeNode) )
480 {
481 dec->nodes[current].representativeNode = root;
482 current = next;
483 assert(current < dec->memNodes);
484 }
485 return root;
486}
487
488/** Find the node its representative node in the union-find data structure, without compressing the union-find tree.
489 *
490 * Should only be used for debugging or asserts.
491 */
492static
494 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
495 spqr_node node /**< The node to find the representative for */
496 )
497{
498 assert(dec);
499 assert(SPQRnodeIsValid(node));
500 assert(node < dec->memNodes);
501
502 spqr_node current = node;
503 spqr_node next;
504
505 //traverse down tree to find the root
506 while( SPQRnodeIsValid(next = dec->nodes[current].representativeNode) )
507 {
508 current = next;
509 assert(current < dec->memNodes);
510 }
511 spqr_node root = current;
512 return root;
513}
514
515/** Find the arc's tail node in the union find data structure of the nodes.
516 *
517 * Updates the arc's tail to point to the representative for faster future queries.
518 */
519static
521 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
522 spqr_arc arc /**< The arc whose tail we want to find */
523 )
524{
525 assert(dec);
526 assert(SPQRarcIsValid(arc));
527 assert(arc < dec->memArcs);
528
529 spqr_node representative = findNode(dec, dec->arcs[arc].tail);
530 dec->arcs[arc].tail = representative;//update the arc information
531
532 return representative;
533}
534
535/** Find the arc's head node in the union find data structure of the nodes.
536 *
537 * Updates the arc's head to point to the representative for faster future queries.
538 */
539static
541 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
542 spqr_arc arc /**< The arc whose head we want to find */
543 )
544{
545 assert(dec);
546 assert(SPQRarcIsValid(arc));
547 assert(arc < dec->memArcs);
548
549 spqr_node representative = findNode(dec, dec->arcs[arc].head);
550 dec->arcs[arc].head = representative;//update the arc information
551
552 return representative;
553}
554
555/** Find the arc's head node in the union find data structure of the nodes, without compressing the union-find tree.
556 *
557 * Should only be used in debugging statements or asserts.
558 */
559static
561 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
562 spqr_arc arc /**< The arc whose head we want to find */
563 )
564{
565 assert(dec);
566 assert(SPQRarcIsValid(arc));
567 assert(arc < dec->memArcs);
568
569 spqr_node representative = findNodeNoCompression(dec, dec->arcs[arc].head);
570 return representative;
571}
572
573/** Find the arc's tail node in the union find data structure of the nodes, without compressing the union-find tree.
574 *
575 * Should only be used in debugging statements or asserts.
576 */
577static
579 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
580 spqr_arc arc /**< The arc whose tail we want to find */
581 )
582{
583 assert(dec);
584 assert(SPQRarcIsValid(arc));
585 assert(arc < dec->memArcs);
586
587 spqr_node representative = findNodeNoCompression(dec, dec->arcs[arc].tail);
588 return representative;
589}
590
591/** Find the first arc in the list of arcs that are adjacent to the given node.
592 *
593 * These arcs form a cyclic linked-list.
594 */
595static
597 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
598 spqr_node node /**< The node to find the arc for */
599 )
600{
601 assert(dec);
602 assert(SPQRnodeIsValid(node));
603 assert(node < dec->memNodes);
604
605 return dec->nodes[node].firstArc;
606}
607
608/** Given the current arc adjacent to this node, find the next arc in the cyclic linked list of adjacent arcs to the
609 * given node.
610 *
611 * This function does not compress the union-find tree, and should only be used in debugging or asserts.
612 */
613static
615 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
616 spqr_arc arc, /**< The current arc that is adjacent to this node */
617 spqr_node node /**< The node to which the arc is adjacent */
618 )
619{
620 assert(dec);
621 assert(SPQRarcIsValid(arc));
622 assert(arc < dec->memArcs);
623 assert(nodeIsRepresentative(dec, node));
624
625 if( findArcHeadNoCompression(dec, arc) == node )
626 {
627 arc = dec->arcs[arc].headArcListNode.next;
628 }
629 else
630 {
631 assert(findArcTailNoCompression(dec, arc) == node);
632 arc = dec->arcs[arc].tailArcListNode.next;
633 }
634 return arc;
635}
636
637/** Given the current arc adjacent to this node, find the next arc in the cyclic linked list of adjacent arcs to the
638 * given node.
639 */
640static
642 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
643 spqr_arc arc, /**< The current arc that is adjacent to this node */
644 spqr_node node /**< The node to which the arc is adjacent */
645 )
646{
647 assert(dec);
648 assert(SPQRarcIsValid(arc));
649 assert(arc < dec->memArcs);
650 assert(nodeIsRepresentative(dec, node));
651
652 if( findArcHead(dec, arc) == node )
653 {
654 arc = dec->arcs[arc].headArcListNode.next;
655 }
656 else
657 {
658 assert(findArcTailNoCompression(dec, arc) == node);
659 dec->arcs[arc].tail = node; //This assignment is not necessary but speeds up future queries.
660 arc = dec->arcs[arc].tailArcListNode.next;
661 }
662 return arc;
663}
664
665/** Given the current arc adjacent to this node, find the previous arc in the cyclic linked list of adjacent arcs
666 * to the given node.
667 */
668static
670 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
671 spqr_arc arc, /**< The current arc that is adjacent to this node */
672 spqr_node node /**< The node to which the arc is adjacent */
673 )
674{
675 assert(dec);
676 assert(SPQRarcIsValid(arc));
677 assert(arc < dec->memArcs);
678 assert(nodeIsRepresentative(dec, node));
679
680 if( findArcHead(dec, arc) == node )
681 {
682 arc = dec->arcs[arc].headArcListNode.previous;
683 }
684 else
685 {
686 assert(findArcTailNoCompression(dec, arc) == node);
687 dec->arcs[arc].tail = node;//This assignment is not necessary but speeds up future queries.
688 arc = dec->arcs[arc].tailArcListNode.previous;
689 }
690 return arc;
691}
692
693/** Update the cyclic node-arc incidence data structure to move all arcs adjacent to one node to another node.
694 *
695 * We typically call this when two nodes are identified with one another, and we need to merge their adjacent arcs.
696 */
697static
699 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
700 spqr_node toMergeInto, /**< The node that we want to give all the arcs of both nodes */
701 spqr_node toRemove /**< The node whose arcs we want to remove */
702 )
703{
704 spqr_arc firstIntoArc = getFirstNodeArc(dec, toMergeInto);
705 spqr_arc firstFromArc = getFirstNodeArc(dec, toRemove);
706 if( SPQRarcIsInvalid(firstIntoArc))
707 {
708 //new node has no arcs
709 dec->nodes[toMergeInto].numArcs += dec->nodes[toRemove].numArcs;
710 dec->nodes[toRemove].numArcs = 0;
711
712 dec->nodes[toMergeInto].firstArc = dec->nodes[toRemove].firstArc;
713 dec->nodes[toRemove].firstArc = SPQR_INVALID_ARC;
714
715 return;
716 }
717 if( SPQRarcIsInvalid(firstFromArc))
718 {
719 //Old node has no arcs; we can just return
720 return;
721 }
722
723 spqr_arc lastIntoArc = getPreviousNodeArc(dec, firstIntoArc, toMergeInto);
724 assert(SPQRarcIsValid(lastIntoArc));
725 spqr_arc lastFromArc = getPreviousNodeArc(dec, firstFromArc, toRemove);
726 assert(SPQRarcIsValid(lastFromArc));
727
729 findArcHead(dec, firstIntoArc) == toMergeInto ? &dec->arcs[firstIntoArc].headArcListNode
730 : &dec->arcs[firstIntoArc].tailArcListNode;
732 findArcHead(dec, lastIntoArc) == toMergeInto ? &dec->arcs[lastIntoArc].headArcListNode
733 : &dec->arcs[lastIntoArc].tailArcListNode;
734
736 findArcHead(dec, firstFromArc) == toRemove ? &dec->arcs[firstFromArc].headArcListNode
737 : &dec->arcs[firstFromArc].tailArcListNode;
739 findArcHead(dec, lastFromArc) == toRemove ? &dec->arcs[lastFromArc].headArcListNode
740 : &dec->arcs[lastFromArc].tailArcListNode;
741
742 firstIntoNode->previous = lastFromArc;
743 lastIntoNode->next = firstFromArc;
744 firstFromNode->previous = lastIntoArc;
745 lastFromNode->next = firstIntoArc;
746
747 dec->nodes[toMergeInto].numArcs += dec->nodes[toRemove].numArcs;
748 dec->nodes[toRemove].numArcs = 0;
749 dec->nodes[toRemove].firstArc = SPQR_INVALID_ARC;
750}
751
752/** Flips the direction a given arc. */
753static
755 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
756 spqr_arc arc /**< The arc that we want to flip */
757 )
758{
759 assert(dec);
760 assert(SPQRarcIsValid(arc));
761 assert(arc < dec->memArcs);
762
763 dec->arcs[arc].reversed = !dec->arcs[arc].reversed;
764}
765
766/** Sets the direction of a given arc */
767static
769 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
770 spqr_arc arc, /**< The given arc */
771 SCIP_Bool reversed /**< Are the head and tail reversed? */
772 )
773{
774 assert(dec);
775 assert(SPQRarcIsValid(arc));
776 assert(arc < dec->memArcs);
777
778 dec->arcs[arc].reversed = reversed;
779}
780
781
782/** Sets the representative of a given arc.
783 *
784 * The arcs reversed field is given with respect to the representative.
785 * In particular, whether an arc is reversed or not is determined by the sign of the path in the signed union-find
786 * data structure for arcs, which can be computed by multiplying the signs of the individual edges (xor over bools).
787 */
788static
790 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
791 spqr_arc arc, /**< The given arc */
792 spqr_arc representative /**< The representative to set the arc to */
793 )
794{
795 assert(dec);
796 assert(SPQRarcIsValid(arc));
797 assert(arc < dec->memArcs);
798 assert(representative == SPQR_INVALID_ARC || SPQRarcIsValid(representative));
799
800 dec->arcs[arc].representative = representative;
801}
802
803/** Merge two representative nodes (Union operation) in the union-find data structure for nodes.
804 *
805 * Returns the id of the node that becomes representative for both.
806 */
807static
809 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
810 spqr_node first, /**< A node to merge */
811 spqr_node second /**< A second node to merge */
812 )
813{
814 assert(dec);
815 assert(nodeIsRepresentative(dec, first));
816 assert(nodeIsRepresentative(dec, second));
817 assert(first != second);//We cannot merge a node into itself
818 assert(first < dec->memNodes);
819 assert(second < dec->memNodes);
820
821 //The rank is stored as a negative number: we decrement it making the negative number larger.
822 //We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement.
823 spqr_node firstRank = dec->nodes[first].representativeNode;
824 spqr_node secondRank = dec->nodes[second].representativeNode;
825 if( firstRank > secondRank )
826 {
827 SCIPswapInts(&first, &second);
828 }
829 //first becomes representative; we merge all of the arcs of second into first
830 mergeNodeArcList(dec, first, second);
831 dec->nodes[second].representativeNode = first;
832 if( firstRank == secondRank )
833 {
834 --dec->nodes[first].representativeNode;
835 }
836 return first;
837}
838
839/** Check if a member is a representative in the union-find data structure for members. */
840static
842 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
843 spqr_member member /**< The member to check */
844 )
845{
846 assert(dec);
847 assert(member < dec->memMembers);
848 assert(SPQRmemberIsValid(member));
849
851}
852
853/** Find the member its representative member in the union-find data structure */
854static
856 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
857 spqr_member member /**< The member to find the representative for */
858 )
859{
860 assert(dec);
861 assert(member < dec->memMembers);
862 assert(SPQRmemberIsValid(member));
863
864 spqr_member current = member;
865 spqr_member next;
866
867 //traverse down tree to find the root
868 while( SPQRmemberIsValid(next = dec->members[current].representativeMember) )
869 {
870 current = next;
871 assert(current < dec->memMembers);
872 }
873
874 spqr_member root = current;
875 current = member;
876
877 //update all pointers along path to point to root, flattening the tree
878 while( SPQRmemberIsValid(next = dec->members[current].representativeMember) )
879 {
880 dec->members[current].representativeMember = root;
881 current = next;
882 assert(current < dec->memMembers);
883 }
884 return root;
885}
886
887/** Find the member's representative member in the union-find data structure, without compressing the union-find tree.
888 *
889 * Should only be used for debugging or asserts.
890 */
891static
893 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
894 spqr_member member /**< The member to find the representative for */
895 )
896{
897 assert(dec);
898 assert(member < dec->memMembers);
899 assert(SPQRmemberIsValid(member));
900
901 spqr_member current = member;
902 spqr_member next;
903
904 //traverse down tree to find the root
905 while( SPQRmemberIsValid(next = dec->members[current].representativeMember) )
906 {
907 current = next;
908 assert(current < dec->memMembers);
909 }
910
911 spqr_member root = current;
912 return root;
913}
914
915/** Merge two representative members (Union operation) in the union-find data structure.
916 *
917 * Returns the id of the member that becomes representative for both.
918 */
919static
921 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
922 spqr_member first, /**< The first member to merge */
923 spqr_member second /**< The second member to merge */
924 )
925{
926 assert(dec);
927 assert(memberIsRepresentative(dec, first));
928 assert(memberIsRepresentative(dec, second));
929 assert(first != second);//We cannot merge a member into itself
930 assert(first < dec->memMembers);
931 assert(second < dec->memMembers);
932
933 //The rank is stored as a negative number: we decrement it making the negative number larger.
934 // We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement.
935 spqr_member firstRank = dec->members[first].representativeMember;
936 spqr_member secondRank = dec->members[second].representativeMember;
937 if( firstRank > secondRank )
938 {
939 SCIPswapInts(&first, &second);
940 }
941 dec->members[second].representativeMember = first;
942 if( firstRank == secondRank )
943 {
944 --dec->members[first].representativeMember;
945 }
946 return first;
947}
948
949/** Finds the member in which the arc is located */
950static
952 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
953 spqr_arc arc /**< The arc to find the member for */
954 )
955{
956 assert(dec);
957 assert(SPQRarcIsValid(arc));
958 assert(arc < dec->memArcs);
959
960 spqr_member representative = findMember(dec, dec->arcs[arc].member);
961 dec->arcs[arc].member = representative;
962 return representative;
963}
964
965/** Finds the member in which the arc is located, without compressing the member union-find tree */
966static
968 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
969 spqr_arc arc /**< The arc to find the member for */
970 )
971{
972 assert(dec);
973 assert(SPQRarcIsValid(arc));
974 assert(arc < dec->memArcs);
975
976 spqr_member representative = findMemberNoCompression(dec, dec->arcs[arc].member);
977 return representative;
978}
979
980/** Find the representative parent member of the given member.
981 *
982 * Note the given member must be representative.
983 */
984static
986 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
987 spqr_member member /**< The member to find the parent for. Must be representative. */
988 )
989{
990 assert(dec);
991 assert(member < dec->memMembers);
992 assert(SPQRmemberIsValid(member));
993 assert(memberIsRepresentative(dec, member));
994
995 if( SPQRmemberIsInvalid(dec->members[member].parentMember))
996 {
997 return dec->members[member].parentMember;
998 }
999 spqr_member parent_representative = findMember(dec, dec->members[member].parentMember);
1000 dec->members[member].parentMember = parent_representative;
1001
1002 return parent_representative;
1003}
1004
1005/** Find the representative parent member of the given member.
1006 *
1007 * Note the given member must be representative.
1008 * This version does not perform compression of the union-find tree, and should only be used in debug or asserts.
1009 */
1010static
1012 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
1013 spqr_member member /**< The member to find the parent for. Must be representative. */
1014 )
1015{
1016 assert(dec);
1017 assert(member < dec->memMembers);
1018 assert(SPQRmemberIsValid(member));
1019 assert(memberIsRepresentative(dec, member));
1020
1021 if( SPQRmemberIsInvalid(dec->members[member].parentMember))
1022 {
1023 return dec->members[member].parentMember;
1024 }
1025 spqr_member parent_representative = findMemberNoCompression(dec, dec->members[member].parentMember);
1026 return parent_representative;
1027}
1028
1029/** Find the child member associated to the given arc, which must be virtual. */
1030static
1032 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
1033 spqr_arc arc /**< The arc to find the child member for */
1034 )
1035{
1036 assert(dec);
1037 assert(SPQRarcIsValid(arc));
1038 assert(arc < dec->memArcs);
1039
1040 spqr_member representative = findMember(dec, dec->arcs[arc].childMember);
1041 dec->arcs[arc].childMember = representative;
1042 return representative;
1043}
1044
1045/** Find the child member associated to the given virtual arc.
1046 *
1047 * This version does not compress the union-find tree and should only be used for debugging and asserts.
1048 */
1049static
1051 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
1052 spqr_arc arc /**< The arc to find the child member for */
1053 )
1054{
1055 assert(dec);
1056 assert(SPQRarcIsValid(arc));
1057 assert(arc < dec->memArcs);
1058
1059 spqr_member representative = findMemberNoCompression(dec, dec->arcs[arc].childMember);
1060 return representative;
1061}
1062
1063/** Checks if the arc has a child member */
1064static
1066 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
1067 spqr_arc arc /**< The arc to check */
1068 )
1069{
1070 assert(dec);
1071 assert(SPQRarcIsValid(arc));
1072 assert(arc < dec->memArcs);
1073
1074 return SPQRmemberIsValid(dec->arcs[arc].childMember);
1075}
1076
1077/** Check whether the given arc is a tree arc or not, i.e. whether it belongs to a (virtual) row or column or not */
1078static
1080 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
1081 spqr_arc arc /**< The arc to check */
1082 )
1083{
1084 assert(dec);
1085 assert(SPQRarcIsValid(arc));
1086 assert(arc < dec->memArcs);
1087
1088 return SPQRelementIsRow(dec->arcs[arc].element);
1089}
1090
1091/** Output data structure that stores both the arc's sign and representative */
1092typedef struct
1093{
1096} ArcSign;
1097
1098#ifndef NDEBUG
1099
1100/** Check if an arc is a representative in the signed union-find data structure for arc directions.
1101 *
1102 * In each member, exactly one arc is the representative.
1103 */
1104static
1106 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
1107 spqr_arc arc /**< The arc to check */
1108 )
1109{
1110 assert(dec);
1111 assert(arc < dec->memArcs);
1112 assert(SPQRarcIsValid(arc));
1113
1114 return SPQRarcIsInvalid(dec->arcs[arc].representative);
1115}
1116
1117#endif
1118
1119/** Find an arcs representative and its direction in the signed union-find data structure for arcs. */
1120static
1122 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
1123 spqr_arc arc /**< The arc to find the representative and direction for */
1124 )
1125{
1126 assert(dec);
1127 assert(arc < dec->memArcs);
1128 assert(SPQRarcIsValid(arc));
1129
1130 spqr_arc current = arc;
1131 spqr_arc next;
1132
1133 SCIP_Bool totalReversed = dec->arcs[current].reversed;
1134 //traverse down tree to find the root
1135 while( SPQRarcIsValid(next = dec->arcs[current].representative) )
1136 {
1137 current = next;
1138 assert(current < dec->memArcs);
1139 //swap boolean only if new arc is reversed
1140 totalReversed = ( totalReversed != dec->arcs[current].reversed );
1141 }
1142
1143 spqr_arc root = current;
1144 current = arc;
1145
1146 SCIP_Bool currentReversed = totalReversed != dec->arcs[root].reversed;
1147 //update all pointers along path to point to root, flattening the tree
1148
1149 while( SPQRarcIsValid(next = dec->arcs[current].representative) )
1150 {
1151 SCIP_Bool wasReversed = dec->arcs[current].reversed;
1152
1153 dec->arcs[current].reversed = currentReversed;
1154 currentReversed = ( currentReversed != wasReversed );
1155
1156 dec->arcs[current].representative = root;
1157 current = next;
1158 assert(current < dec->memArcs);
1159 }
1160
1161 ArcSign sign;
1162 sign.reversed = totalReversed;
1163 sign.representative = root;
1164 return sign;
1165}
1166
1167/** Find an arcs representative and its direction in the signed union-find data structure for arcs.
1168 *
1169 * This version does not compress the union-find tree and should only be used in debug and asserts.
1170 */
1171static
1173 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
1174 spqr_arc arc /**< The arc to find the representative and direction for */
1175 )
1176{
1177 assert(dec);
1178 assert(arc < dec->memArcs);
1179 assert(SPQRarcIsValid(arc));
1180
1181 spqr_arc current = arc;
1182 spqr_arc next;
1183
1184 SCIP_Bool totalReversed = dec->arcs[current].reversed;
1185 //traverse down tree to find the root
1186 while( SPQRarcIsValid(next = dec->arcs[current].representative))
1187 {
1188 current = next;
1189 assert(current < dec->memArcs);
1190 //swap boolean only if new arc is reversed
1191 totalReversed = ( totalReversed != dec->arcs[current].reversed );
1192 }
1193 ArcSign sign;
1194 sign.reversed = totalReversed;
1195 sign.representative = current;
1196 return sign;
1197}
1198
1199/** Finds the arcs head, taking into account whether it is reversed by the signed union-find data structure. */
1200static
1202 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
1203 spqr_arc arc /**< The arc to find the head for */
1204 )
1205{
1206 assert(dec);
1207 if( findArcSign(dec, arc).reversed )
1208 {
1209 return findArcTail(dec, arc);
1210 }
1211 else
1212 {
1213 return findArcHead(dec, arc);
1214 }
1215}
1216
1217/** Finds the arcs tail, taking into account whether it is reversed by the signed union-find data structure. */
1218static
1220 SCIP_NETMATDECDATA* dec, /**< The network decomposition */
1221 spqr_arc arc /**< The arc to find the tail for */
1222 )
1223{
1224 assert(dec);
1225 if( findArcSign(dec, arc).reversed )
1226 {
1227 return findArcHead(dec, arc);
1228 }
1229 else
1230 {
1231 return findArcTail(dec, arc);
1232 }
1233}
1234
1235/** Finds the arcs head, taking into account whether it is reversed by the signed union-find data structure.
1236 *
1237 * This version does not compress the union-find tree and should only be used in debug and asserts.
1238 */
1239static
1241 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
1242 spqr_arc arc /**< The arc to find the head for */
1243 )
1244{
1245 assert(dec);
1246
1247 if( findArcSignNoCompression(dec, arc).reversed )
1248 {
1249 return findArcTailNoCompression(dec, arc);
1250 }
1251 else
1252 {
1253 return findArcHeadNoCompression(dec, arc);
1254 }
1255}
1256
1257/** Finds the arcs tail, taking into account whether it is reversed by the signed union-find data structure.
1258 *
1259 * This version does not compress the union-find tree and should only be used in debug and asserts.
1260 */
1261static
1263 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
1264 spqr_arc arc /**< The arc to find the tail for */
1265 )
1266{
1267 assert(dec);
1268 if( findArcSignNoCompression(dec, arc).reversed )
1269 {
1270 return findArcHeadNoCompression(dec, arc);
1271 }
1272 else
1273 {
1274 return findArcTailNoCompression(dec, arc);
1275 }
1276}
1277
1278/** Merges the sign union-find structures for two arc sets.
1279 *
1280 * If reflectRelative is set to true then all arcs of the represented by the second arc are reversed
1281 * w.r.t. their current orientation. Otherwise, all arcs keep the same
1282 * reversed status with respect to the root node of the union find tree.
1283 */
1284static
1286 SCIP_NETMATDECDATA* dec, /**< The decomposition data structure */
1287 spqr_arc first, /**< Representative arc of the first arc set */
1288 spqr_arc second, /**< Representative arc of the second arc set */
1289 SCIP_Bool reflectRelative /**< Should all arcs in the second arc set be reversed?*/
1290 )
1291{
1292 assert(dec);
1293 assert(arcIsRepresentative(dec, first));
1294 assert(arcIsRepresentative(dec, second));
1295 assert(first != second);//We cannot merge a member into itself
1296 assert(first < dec->memArcs);
1297 assert(second < dec->memArcs);
1298
1299 //The rank is stored as a negative number: we decrement it making the negative number larger.
1300 //We want the new root to be the one with 'largest' rank, so smallest number. If they are equal, we decrement.
1301 spqr_member firstRank = dec->arcs[first].representative;
1302 spqr_member secondRank = dec->arcs[second].representative;
1303
1304 if( firstRank > secondRank )
1305 {
1306 SCIPswapInts(&first, &second);
1307 }
1308 dec->arcs[second].representative = first;
1309 if( firstRank == secondRank )
1310 {
1311 --dec->arcs[first].representative;
1312 }
1313 //These boolean formula's cover all 16 possible cases, such that the relative orientation of the first is not changed
1314 //but the relative orientation of the second is changed based on whether we want to reflect.
1315 SCIP_Bool equal = dec->arcs[first].reversed == dec->arcs[second].reversed;
1316 dec->arcs[second].reversed = ( equal == reflectRelative );
1317 if( firstRank > secondRank )
1318 {
1319 dec->arcs[first].reversed = ( dec->arcs[first].reversed != reflectRelative );
1320 }
1321 return first;
1322}
1323
1324/** Checks whether an arc is reversed for arcs in non-rigid members.
1325 *
1326 * For non-rigid members, we do not use the union-find datastructure, as we can get away without it.
1327 */
1328static
1330 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
1331 spqr_arc arc /**< The arc to check */
1332 )
1333{
1334 assert(dec);
1335 assert(SPQRarcIsValid(arc));
1336 assert(arc < dec->memArcs);
1337
1338 return dec->arcs[arc].reversed;
1339}
1340
1341/** Gets the element (row/column) associated to the given arc. */
1342static
1344 const SCIP_NETMATDECDATA* dec, /**< The network decomposition */
1345 spqr_arc arc /**< The arc to get the element for */
1346 )
1347{
1348 assert(dec);
1349 assert(SPQRarcIsValid(arc));
1350 assert(arc < dec->memArcs);
1351
1352 return dec->arcs[arc].element;
1353}
1354
1355/** Check whether the network matrix decomposition contains an edge for the given row index. */
1356static
1358 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1359 int row /**< The row index that is checked */
1360 )
1361{
1362 assert(SPQRrowIsValid(row) && row < dec->memRows);
1363 assert(dec);
1364
1365 return SPQRarcIsValid(dec->rowArcs[row]);
1366}
1367
1368/** Check whether the network matrix decomposition contains an edge for the given column index. */
1369static
1371 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1372 int column /**< The column index that is checked */
1373 )
1374{
1375 assert(SPQRcolIsValid(column) && column < dec->memColumns);
1376 assert(dec);
1377
1378 return SPQRarcIsValid(dec->columnArcs[column]);
1379}
1380
1381/** Associate the given arc to the given column in the network matrix decomposition. */
1382static
1384 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1385 spqr_col col, /**< The column to associate */
1386 spqr_arc arc /**< The arc to associate to the column */
1387 )
1388{
1389 assert(SPQRcolIsValid(col) && col < dec->memColumns);
1390 assert(dec);
1391 assert(SPQRarcIsValid(arc));
1392
1393 dec->columnArcs[col] = arc;
1394}
1395
1396/** Associate the given arc to the given row in the network matrix decomposition. */
1397static
1399 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1400 spqr_row row, /**< The row to associate */
1401 spqr_arc arc /**< The arc to associate to the row */
1402 )
1403{
1404 assert(SPQRrowIsValid(row) && row < dec->memRows);
1405 assert(dec);
1406 assert(SPQRarcIsValid(arc));
1407
1408 dec->rowArcs[row] = arc;
1409}
1410
1411/** Get the decomposition arc associated to the given column. */
1412static
1414 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1415 spqr_col col /**< The given column */
1416 )
1417{
1418 assert(SPQRcolIsValid(col) && col < dec->memColumns);
1419 assert(dec);
1420
1421 return dec->columnArcs[col];
1422}
1423
1424/** Get the decomposition arc associated to the given row. */
1425static
1427 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1428 spqr_row row /**< The given row */
1429 )
1430{
1431 assert(SPQRrowIsValid(row) && row < dec->memRows);
1432 assert(dec);
1433
1434 return dec->rowArcs[row];
1435}
1436
1437/** Initialize the network matrix decomposition data structure. */
1438static
1440 BMS_BLKMEM* blkmem, /**< Block memory */
1441 SCIP_NETMATDECDATA** pdec, /**< buffer to store pointer to created decomposition */
1442 int nrows, /**< The maximal number of rows that the decomposition can expect */
1443 int ncols /**< The maximal number of columns that the decomposition can expect */
1444 )
1445{
1446 assert(blkmem);
1447 assert(pdec);
1448 assert(!*pdec);
1449
1450 SCIP_ALLOC( BMSallocBlockMemory(blkmem, pdec) );
1451 SCIP_NETMATDECDATA* dec = *pdec;
1452 dec->env = blkmem;
1453
1454 //Initialize arc array data
1455 int initialMemArcs = 8;
1456 {
1457 assert(initialMemArcs > 0);
1458 dec->memArcs = initialMemArcs;
1459 dec->numArcs = 0;
1460 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &dec->arcs, dec->memArcs) );
1461 for( spqr_arc i = 0; i < dec->memArcs; ++i )
1462 {
1463 dec->arcs[i].arcListNode.next = i + 1;
1465 }
1466 dec->arcs[dec->memArcs - 1].arcListNode.next = SPQR_INVALID_ARC;
1467 dec->firstFreeArc = 0;
1468 }
1469
1470 //Initialize member array data
1471 int initialMemMembers = 8;
1472 {
1473 assert(initialMemMembers > 0);
1474 dec->memMembers = initialMemMembers;
1475 dec->numMembers = 0;
1476 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &dec->members, dec->memMembers) );
1477 }
1478
1479 //Initialize node array data
1480 int initialMemNodes = 8;
1481 {
1482 assert(initialMemNodes > 0);
1483 dec->memNodes = initialMemNodes;
1484 dec->numNodes = 0;
1485 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &dec->nodes, dec->memNodes) );
1486 }
1487
1488 //Initialize mappings for rows
1489 {
1490 dec->memRows = nrows;
1491 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &dec->rowArcs, dec->memRows) );
1492 for( int i = 0; i < dec->memRows; ++i )
1493 {
1494 dec->rowArcs[i] = SPQR_INVALID_ARC;
1495 }
1496 }
1497 //Initialize mappings for columns
1498 {
1499 dec->memColumns = ncols;
1501 for( int i = 0; i < dec->memColumns; ++i )
1502 {
1503 dec->columnArcs[i] = SPQR_INVALID_ARC;
1504 }
1505 }
1506
1507 dec->numConnectedComponents = 0;
1508 return SCIP_OKAY;
1509}
1510
1511/** Free the network matrix decomposition data structure */
1512static
1514 SCIP_NETMATDECDATA** pdec /**< pointer to the network matrix decomposition to freed */
1515 )
1516{
1517 assert(pdec);
1518 assert(*pdec);
1519
1520 SCIP_NETMATDECDATA* dec = *pdec;
1522 BMSfreeBlockMemoryArray(dec->env, &dec->rowArcs, dec->memRows);
1523 BMSfreeBlockMemoryArray(dec->env, &dec->nodes, dec->memNodes);
1524 BMSfreeBlockMemoryArray(dec->env, &dec->members, dec->memMembers);
1525 BMSfreeBlockMemoryArray(dec->env, &dec->arcs, dec->memArcs);
1526
1527 BMSfreeBlockMemory(dec->env, pdec);
1528}
1529
1530/** Get the first arc of the linked list of arcs contained in the given member. */
1531static
1533 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1534 spqr_member member /**< The given member */
1535 )
1536{
1537 assert(dec);
1538 assert(SPQRmemberIsValid(member));
1539 assert(member < dec->memMembers);
1540
1541 return dec->members[member].firstArc;
1542}
1543
1544/** Given the current arc, get the next arc of the linked list of arcs that are in the same member. */
1545static
1547 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1548 spqr_arc arc /**< The current arc */
1549 )
1550{
1551 assert(dec);
1552 assert(SPQRarcIsValid(arc));
1553 assert(arc < dec->memArcs);
1554
1555 arc = dec->arcs[arc].arcListNode.next;
1556 return arc;
1557}
1558
1559/** Given the current arc, get the previous arc of the linked list of arcs that are in the same member. */
1560static
1562 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1563 spqr_arc arc /**< The current arc */
1564 )
1565{
1566 assert(dec);
1567 assert(SPQRarcIsValid(arc));
1568 assert(arc < dec->memArcs);
1569
1570 arc = dec->arcs[arc].arcListNode.previous;
1571 return arc;
1572}
1573
1574/** Adds an arc to the linked list of arcs of the given member. */
1575static
1577 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1578 spqr_arc arc, /**< The arc to add */
1579 spqr_member member /**< The member to add the arc to */
1580 )
1581{
1582 spqr_arc firstMemberArc = getFirstMemberArc(dec, member);
1583
1584 if( SPQRarcIsValid(firstMemberArc))
1585 {
1586 spqr_arc lastMemberArc = getPreviousMemberArc(dec, firstMemberArc);
1587 dec->arcs[arc].arcListNode.next = firstMemberArc;
1588 dec->arcs[arc].arcListNode.previous = lastMemberArc;
1589 dec->arcs[firstMemberArc].arcListNode.previous = arc;
1590 dec->arcs[lastMemberArc].arcListNode.next = arc;
1591 }
1592 else
1593 {
1594 assert(dec->members[member].numArcs == 0);
1595 dec->arcs[arc].arcListNode.next = arc;
1596 dec->arcs[arc].arcListNode.previous = arc;
1597 }
1598 dec->members[member].firstArc = arc;
1599 ++( dec->members[member].numArcs );
1600}
1601
1602/** Create a new arc in the network matrix decomposition. */
1603static
1605 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1606 spqr_member member, /**< The member that contains the arc */
1607 SCIP_Bool reversed, /**< Is the arc reversed or not? */
1608 spqr_arc* pArc /**< out-pointer to the id that is assigned to the arc. */
1609 )
1610{
1611 assert(dec);
1612 assert(pArc);
1613 assert(SPQRmemberIsInvalid(member) || memberIsRepresentative(dec, member));
1614
1615 spqr_arc idx = dec->firstFreeArc;
1616 if( SPQRarcIsValid(idx))
1617 {
1618 dec->firstFreeArc = dec->arcs[idx].arcListNode.next;
1619 }
1620 else
1621 {
1622 //Enlarge array, no free nodes in arc list
1623 int newSize = 2 * dec->memArcs;
1624 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &dec->arcs, dec->memArcs, newSize) );
1625 for( int i = dec->memArcs + 1; i < newSize; ++i )
1626 {
1627 dec->arcs[i].arcListNode.next = i + 1;
1629 }
1630 dec->arcs[newSize - 1].arcListNode.next = SPQR_INVALID_ARC;
1631 dec->firstFreeArc = dec->memArcs + 1;
1632 idx = dec->memArcs;
1633 dec->memArcs = newSize;
1634 }
1635
1636 dec->arcs[idx].tail = SPQR_INVALID_NODE;
1637 dec->arcs[idx].head = SPQR_INVALID_NODE;
1638 dec->arcs[idx].member = member;
1640 dec->arcs[idx].reversed = reversed;
1641
1646
1647 dec->numArcs++;
1648
1649 *pArc = idx;
1650
1651 return SCIP_OKAY;
1652}
1653
1654/** Create a new arc in the network matrix decomposition that is associated to the given row. */
1655static
1657 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1658 spqr_member member, /**< The member that contains the arc */
1659 spqr_arc* pArc, /**< out-pointer to the id that is assigned to the arc. */
1660 spqr_row row, /**< The row associated to the arc */
1661 SCIP_Bool reversed /**< Is the arc reversed or not? */
1662 )
1663{
1664 SCIP_CALL( createArc(dec, member, reversed, pArc) );
1665 setDecompositionRowArc(dec, row, *pArc);
1666 addArcToMemberArcList(dec, *pArc, member);
1667 dec->arcs[*pArc].element = SPQRrowToElement(row);
1668
1669 return SCIP_OKAY;
1670}
1671
1672/** Create a new arc in the network matrix decomposition that is associated to the given column. */
1673static
1675 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1676 spqr_member member, /**< The member that contains the arc */
1677 spqr_arc* pArc, /**< out-pointer to the id that is assigned to the arc. */
1678 spqr_col column, /**< The column associated to the arc */
1679 SCIP_Bool reversed /**< Is the arc reversed or not? */
1680 )
1681{
1682 SCIP_CALL( createArc(dec, member, reversed, pArc) );
1683 setDecompositionColumnArc(dec, column, *pArc);
1684 addArcToMemberArcList(dec, *pArc, member);
1685 dec->arcs[*pArc].element = SPQRcolumnToElement(column);
1686
1687 return SCIP_OKAY;
1688}
1689
1690/** Create a new member in the network matrix decomposition. */
1691static
1693 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1694 SPQRMemberType type, /**< The SPQR-type of the member */
1695 spqr_member* pMember /**< out-pointer to the id that is assigned to the member */
1696 )
1697{
1698 assert(dec);
1699 assert(pMember);
1700
1701 if( dec->numMembers == dec->memMembers )
1702 {
1703 int newSize = dec->memMembers * 2;
1704 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &dec->members, dec->memMembers, newSize) );
1705 dec->memMembers = newSize;
1706 }
1710 data->firstArc = SPQR_INVALID_ARC;
1712 data->numArcs = 0;
1714 data->type = type;
1715
1716 *pMember = dec->numMembers;
1717
1718 dec->numMembers++;
1719 return SCIP_OKAY;
1720}
1721
1722/** Create a new node in the network matrix decomposition. */
1723static
1725 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1726 spqr_node* pNode /**< out-pointer to the id that is assigned to the node */
1727 )
1728{
1729 if( dec->numNodes == dec->memNodes )
1730 {
1731 int newSize = dec->memNodes * 2;
1732 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &dec->nodes, dec->memNodes, newSize) );
1733 dec->memNodes = newSize;
1734 }
1735 *pNode = dec->numNodes;
1738 dec->nodes[dec->numNodes].numArcs = 0;
1739 dec->numNodes++;
1740
1741 return SCIP_OKAY;
1742}
1743
1744/** Remove an arc from the linked list of arcs that are adjacent to a given node. */
1745static
1747 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1748 spqr_arc arc, /**< The arc to remove */
1749 spqr_node node, /**< The node to remove the arc from */
1750 SCIP_Bool nodeIsHead /**< Indicates whether the node is the arcs head, or tail */
1751 )
1752{
1753 SPQRNetworkDecompositionArcListNode* arcListNode = nodeIsHead ? &dec->arcs[arc].headArcListNode
1754 : &dec->arcs[arc].tailArcListNode;
1755
1756 if( dec->nodes[node].numArcs == 1 )
1757 {
1758 dec->nodes[node].firstArc = SPQR_INVALID_ARC;
1759 }
1760 else
1761 {
1762 spqr_arc next_arc = arcListNode->next;
1763 spqr_arc prev_arc = arcListNode->previous;
1765 findArcHead(dec, next_arc) == node ? &dec->arcs[next_arc].headArcListNode
1766 : &dec->arcs[next_arc].tailArcListNode;
1768 findArcHead(dec, prev_arc) == node ? &dec->arcs[prev_arc].headArcListNode
1769 : &dec->arcs[prev_arc].tailArcListNode;
1770
1771 nextListNode->previous = prev_arc;
1772 prevListNode->next = next_arc;
1773
1774 if( dec->nodes[node].firstArc == arc )
1775 {
1776 dec->nodes[node].firstArc = next_arc;
1777 }
1778 }
1779 --( dec->nodes[node].numArcs );
1780}
1781
1782/** Add an arc to the linked list of arcs that are adjacent to a given node. */
1783static
1785 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1786 spqr_arc arc, /**< The arc to add */
1787 spqr_node node, /**< The node to add the arc to */
1788 SCIP_Bool nodeIsHead /**< Indicates whether the node is the arcs head, or tail */
1789 )
1790{
1791 assert(nodeIsRepresentative(dec, node));
1792
1793 spqr_arc firstNodeArc = getFirstNodeArc(dec, node);
1794
1795 SPQRNetworkDecompositionArcListNode* arcListNode = nodeIsHead ? &dec->arcs[arc].headArcListNode
1796 : &dec->arcs[arc].tailArcListNode;
1797 if( SPQRarcIsValid(firstNodeArc))
1798 {
1799 SCIP_Bool nextIsHead = findArcHead(dec, firstNodeArc) == node;
1800 SPQRNetworkDecompositionArcListNode* nextListNode = nextIsHead ? &dec->arcs[firstNodeArc].headArcListNode
1801 : &dec->arcs[firstNodeArc].tailArcListNode;
1802 spqr_arc lastNodeArc = nextListNode->previous;
1803
1804 arcListNode->next = firstNodeArc;
1805 arcListNode->previous = lastNodeArc;
1806
1807 SCIP_Bool previousIsHead = findArcHead(dec, lastNodeArc) == node;
1808 SPQRNetworkDecompositionArcListNode* previousListNode = previousIsHead ? &dec->arcs[lastNodeArc].headArcListNode
1809 : &dec->arcs[lastNodeArc].tailArcListNode;
1810 previousListNode->next = arc;
1811 nextListNode->previous = arc;
1812 }
1813 else
1814 {
1815 arcListNode->next = arc;
1816 arcListNode->previous = arc;
1817 }
1818 dec->nodes[node].firstArc = arc;
1819 ++dec->nodes[node].numArcs;
1820 if( nodeIsHead )
1821 {
1822 dec->arcs[arc].head = node;
1823 }
1824 else
1825 {
1826 dec->arcs[arc].tail = node;
1827 }
1828}
1829
1830/** Initializes the data structures of the arcs head and tail nodes. */
1831static
1833 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1834 spqr_arc arc, /**< The arc to initialize */
1835 spqr_node head, /**< The arc its head node*/
1836 spqr_node tail /**< The arc its tail node*/
1837 )
1838{
1839 addArcToNodeArcList(dec, arc, head, TRUE);
1840 addArcToNodeArcList(dec, arc, tail, FALSE);
1841}
1842
1843/** Deinitializes the data structures of the arcs head and tail nodes. */
1844static
1846 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1847 spqr_arc arc /**< The arc to deinitialize */
1848 )
1849{
1850 removeArcFromNodeArcList(dec, arc, findArcHead(dec, arc), TRUE);
1851 removeArcFromNodeArcList(dec, arc, findArcTail(dec, arc), FALSE);
1852 dec->arcs[arc].head = SPQR_INVALID_NODE;
1853 dec->arcs[arc].tail = SPQR_INVALID_NODE;
1854}
1855
1856/** Change the arc's head node to a new node. */
1857static
1859 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1860 spqr_arc arc, /**< The given arc */
1861 spqr_node oldHead, /**< The current head node of the arc */
1862 spqr_node newHead /**< The new head node of the arc */
1863 )
1864{
1865 assert(nodeIsRepresentative(dec, oldHead));
1866 assert(nodeIsRepresentative(dec, newHead));
1867
1868 removeArcFromNodeArcList(dec, arc, oldHead, TRUE);
1869 addArcToNodeArcList(dec, arc, newHead, TRUE);
1870}
1871
1872/** Change the arc's head node to a new node. */
1873static
1875 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1876 spqr_arc arc, /**< The given arc */
1877 spqr_node oldTail, /**< The current tail node of the arc */
1878 spqr_node newTail /**< The new tail node of the arc */
1879 )
1880{
1881 assert(nodeIsRepresentative(dec, oldTail));
1882 assert(nodeIsRepresentative(dec, newTail));
1883
1884 removeArcFromNodeArcList(dec, arc, oldTail, FALSE);
1885 addArcToNodeArcList(dec, arc, newTail, FALSE);
1886}
1887
1888/** Returns the degree of the given node. */
1889static
1891 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1892 spqr_node node /**< The given node */
1893 )
1894{
1895 assert(dec);
1896 assert(SPQRnodeIsValid(node));
1897 assert(node < dec->memNodes);
1898
1899 return dec->nodes[node].numArcs;
1900}
1901
1902/** Get the SPQR-type of the given member. */
1903static
1905 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1906 spqr_member member /**< The given member */
1907 )
1908{
1909 assert(dec);
1910 assert(SPQRmemberIsValid(member));
1911 assert(member < dec->memMembers);
1912 assert(memberIsRepresentative(dec, member));
1913
1914 return dec->members[member].type;
1915}
1916
1917/** Update the SPQR-type of the given member. */
1918static
1920 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1921 spqr_member member, /**< The member to update the type for */
1922 SPQRMemberType type /**< The new SPQR-type of the member */
1923 )
1924{
1925 assert(dec);
1926 assert(SPQRmemberIsValid(member));
1927 assert(member < dec->memMembers);
1928 assert(memberIsRepresentative(dec, member));
1929
1930 dec->members[member].type = type;
1931}
1932
1933/** Returns the virtual arc pointing to the parent member (in the arborescence) of the given member. */
1934static
1936 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1937 spqr_member member /**< The given member */
1938 )
1939{
1940 assert(dec);
1941 assert(SPQRmemberIsValid(member));
1942 assert(member < dec->memMembers);
1943 assert(memberIsRepresentative(dec, member));
1944
1945 return dec->members[member].markerToParent;
1946}
1947
1948/** Updates the parent information of a member that is identified with another another member. */
1949static
1951 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1952 const spqr_member newMember, /**< The new large member containing both members */
1953 const spqr_member toRemove /**< The member that was merged and removed */
1954 )
1955{
1956 assert(memberIsRepresentative(dec, newMember));
1957 assert(findMemberNoCompression(dec, toRemove) == newMember);
1958
1959 dec->members[newMember].markerOfParent = dec->members[toRemove].markerOfParent;
1960 dec->members[newMember].markerToParent = dec->members[toRemove].markerToParent;
1961 dec->members[newMember].parentMember = dec->members[toRemove].parentMember;
1962
1963 dec->members[toRemove].markerOfParent = SPQR_INVALID_ARC;
1964 dec->members[toRemove].markerToParent = SPQR_INVALID_ARC;
1965 dec->members[toRemove].parentMember = SPQR_INVALID_MEMBER;
1966}
1967
1968/** Returns the virtual arc of the parent member that points to the given member. */
1969static
1971 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1972 spqr_member member /**< The given member */
1973 )
1974{
1975 assert(dec);
1976 assert(SPQRmemberIsValid(member));
1977 assert(member < dec->memMembers);
1978 assert(memberIsRepresentative(dec, member));
1979
1980 return dec->members[member].markerOfParent;
1981}
1982
1983/** Returns the number of arcs in the member. */
1984static
1986 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
1987 spqr_member member /**< The member to get the number of arcs of */
1988 )
1989{
1990 assert(dec);
1991 assert(SPQRmemberIsValid(member));
1992 assert(member < dec->memMembers);
1993 assert(memberIsRepresentative(dec, member));
1994
1995 return dec->members[member].numArcs;
1996}
1997
1998/** Returns the number of nodes in complete network matrix decomposition. */
1999static
2001 const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */
2002 )
2003{
2004 assert(dec);
2005
2006 return dec->numNodes;
2007}
2008
2009/** Returns the number of members in complete network matrix decomposition. */
2010static
2012 const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */
2013 )
2014{
2015 assert(dec);
2016
2017 return dec->numMembers;
2018}
2019
2020/** Creates a standalone parallel member with the given row and columns that is not connected to other members of the
2021 * network matrix decomposition.
2022 *
2023 * New arcs are created for the given row and columns.
2024 */
2025static
2027 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2028 spqr_col* columns, /**< The columns in the parallel member */
2029 SCIP_Bool* reversed, /**< Array indicating the direction of each column edge */
2030 int num_columns, /**< The number of columns in the parallel member */
2031 spqr_row row, /**< The row in the parallel member */
2032 spqr_member* pMember /**< out-pointer to the new parallel member id */
2033 )
2034{
2035 spqr_member member;
2036 SCIP_CALL( createMember(dec, num_columns <= 1 ? SPQR_MEMBERTYPE_LOOP : SPQR_MEMBERTYPE_PARALLEL, &member) );
2037
2038 spqr_arc row_arc;
2039 SCIP_CALL( createRowArc(dec, member, &row_arc, row, num_columns <= 1) );
2040
2041 spqr_arc col_arc;
2042 for( int i = 0; i < num_columns; ++i )
2043 {
2044 SCIP_CALL( createColumnArc(dec, member, &col_arc, columns[i], reversed[i]) );
2045 }
2046 *pMember = member;
2047
2049
2050 return SCIP_OKAY;
2051}
2052
2053/** Creates a parallel member with the given row and columns is connected to other members of the
2054 * network matrix decomposition.
2055 *
2056 * New arcs are created for the given row and columns.
2057 */
2058static
2060 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2061 spqr_col* columns, /**< The columns in the parallel member */
2062 SCIP_Bool* reversed, /**< Array indicating the direction of each column edge */
2063 int num_columns, /**< The number of columns in the parallel member */
2064 spqr_row row, /**< The row in the parallel member */
2065 spqr_member* pMember /**< out-pointer to the new parallel member id */
2066 )
2067{
2068 spqr_member member;
2070
2071 spqr_arc row_arc;
2072 SCIP_CALL( createRowArc(dec, member, &row_arc, row, FALSE) );
2073
2074 spqr_arc col_arc;
2075 for( int i = 0; i < num_columns; ++i )
2076 {
2077 SCIP_CALL( createColumnArc(dec, member, &col_arc, columns[i], reversed[i]) );
2078 }
2079 *pMember = member;
2080
2081 return SCIP_OKAY;
2082}
2083
2084/** Creates a standalone series member with the given row and columns that is not connected to other members of the
2085 * network matrix decomposition.
2086 *
2087 * New arcs are created for the given rows and column.
2088 */
2089static
2091 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2092 spqr_row* rows, /**< The rows in the series member */
2093 SCIP_Bool* reversed, /**< Array indicating the direction of each row edge */
2094 int numRows, /**< The number of rows in the parallel member */
2095 spqr_col col, /**< The column in the parallel member */
2096 spqr_member* pMember /**< out-pointer to the new series member id */
2097 )
2098{
2099 spqr_member member;
2100 SCIP_CALL( createMember(dec, numRows <= 1 ? SPQR_MEMBERTYPE_LOOP : SPQR_MEMBERTYPE_SERIES, &member) );
2101
2102 spqr_arc colArc;
2103 SCIP_CALL( createColumnArc(dec, member, &colArc, col, FALSE) );
2104
2105 spqr_arc rowArc;
2106 for( int i = 0; i < numRows; ++i )
2107 {
2108 SCIP_CALL( createRowArc(dec, member, &rowArc, rows[i], !reversed[i]) );
2109 }
2110 *pMember = member;
2112
2113 return SCIP_OKAY;
2114}
2115
2116/** Creates a series member with the given row and columns that is connected to some other member of the
2117 * network matrix decomposition.
2118 *
2119 * New arcs are created for the given rows and column.
2120 */
2121static
2123 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2124 spqr_row* rows, /**< The rows in the series member */
2125 SCIP_Bool* reversed, /**< Array indicating the direction of each row edge */
2126 int numRows, /**< The number of rows in the parallel member */
2127 spqr_col col, /**< The column in the parallel member */
2128 spqr_member* pMember /**< out-pointer to the new series member id */
2129 )
2130{
2131 spqr_member member;
2133
2134 spqr_arc colArc;
2135 SCIP_CALL( createColumnArc(dec, member, &colArc, col, FALSE) );
2136
2137 spqr_arc rowArc;
2138 for( int i = 0; i < numRows; ++i )
2139 {
2140 SCIP_CALL( createRowArc(dec, member, &rowArc, rows[i], !reversed[i]) );
2141 }
2142 *pMember = member;
2143
2144 return SCIP_OKAY;
2145}
2146
2147/** Remove an arc from the linked list containing all arcs of a single member. */
2148static
2150 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2151 spqr_arc arc, /**< The arc to remove */
2152 spqr_member member /**< The member to remove it from */
2153 )
2154{
2155 assert(findArcMemberNoCompression(dec, arc) == member);
2156 assert(memberIsRepresentative(dec, member));
2157
2158 if( dec->members[member].numArcs == 1 )
2159 {
2160 dec->members[member].firstArc = SPQR_INVALID_ARC;
2161 }
2162 else
2163 {
2164 spqr_arc nextArc = dec->arcs[arc].arcListNode.next;
2165 spqr_arc prevArc = dec->arcs[arc].arcListNode.previous;
2166
2167 dec->arcs[nextArc].arcListNode.previous = prevArc;
2168 dec->arcs[prevArc].arcListNode.next = nextArc;
2169
2170 if( dec->members[member].firstArc == arc )
2171 {
2172 dec->members[member].firstArc = nextArc;
2173 }
2174 }
2175
2176 --( dec->members[member].numArcs );
2177}
2178
2179/** Data structure for the algorithms to find fundamental cycle within the network matrix decomposition */
2180typedef struct
2181{
2185
2186/** Processes a single arc for the algorithm to find cycles in the network matrix decomposition.
2187 *
2188 * if virtual, pushes it on the callstack, if non-virtual, adds it to the found cycle
2189 */
2190static
2192 spqr_row* cyclearcs, /**< The found cycle so far */
2193 int* num_cycle_arcs, /**< The number of arcs in the cycle so far */
2194 FindCycleCall* callStack, /**< The call stack of virtual edges to process still */
2195 int* callStackSize, /**< The number of virtual edges on the callstack */
2196 spqr_arc arc, /**< The current arc to process */
2197 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2198 SCIP_Bool* cycledir, /**< Whether the current arc is reversed w.r.t to the cycle/path */
2199 SCIP_Bool arcIsReversed /**< The arcIsReversed status from the network matrix decomposition */
2200 )
2201{
2202 assert(arcIsTree(dec, arc));
2203
2204 if( !arcIsChildMarker(dec, arc))
2205 {
2206 spqr_member current_member = findArcMemberNoCompression(dec, arc);
2207 if( markerToParent(dec, current_member) == arc )
2208 {
2209 spqr_arc other_arc = markerOfParent(dec, current_member);
2210 assert(!arcIsTree(dec, other_arc));
2211 callStack[*callStackSize].arc = other_arc;
2212 callStack[*callStackSize].reversed = arcIsReversed;
2213 ++( *callStackSize );
2214 }
2215 else
2216 {
2217 spqr_element element = arcGetElement(dec, arc);
2218 assert(SPQRelementIsRow(element));
2219 spqr_row row = SPQRelementToRow(element);
2220 cyclearcs[*num_cycle_arcs] = row;
2221 cycledir[*num_cycle_arcs] = arcIsReversed;
2222 ++( *num_cycle_arcs );
2223 }
2224 }
2225 else
2226 {
2227 spqr_member child_member = findArcChildMemberNoCompression(dec, arc);
2228 spqr_arc other_arc = markerToParent(dec, child_member);
2229 assert(!arcIsTree(dec, other_arc));
2230 callStack[*callStackSize].arc = other_arc;
2231 callStack[*callStackSize].reversed = arcIsReversed;
2232 ++( *callStackSize );
2233 }
2234}
2235
2236/** Find the fundamental path of a cycle.
2237 *
2238 * This is a slow method and only intended for debugging and testing.
2239 */
2240static
2242 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2243 spqr_col column, /**< The column to find the fundamental path for */
2244 spqr_row* output, /**< preallocated array to store fundamental path in. Must have at least
2245 * the number of rows in the decomposition allocated */
2246 SCIP_Bool* computedSignStorage /**< Boolean array for storage of whether the path occurs forwards or
2247 * backwards. Must have at least the same size as output array. */
2248 )
2249{
2250 /*Basic idea; for each component, do a dfs over the tree formed by the row arcs to find the relevant edges.
2251 This is easy for SPQ-nodes, for R nodes we need to search */
2252 spqr_arc arc = getDecompositionColumnArc(dec, column);
2253 if( SPQRarcIsInvalid(arc))
2254 {
2255 return 0;
2256 }
2257 int num_rows = 0;
2258
2259 FindCycleCall* callStack;
2260 if( BMSallocBlockMemoryArray(dec->env, &callStack, dec->memRows) == NULL )
2261 {
2262 return -1;
2263 }
2264 int callStackSize = 1;
2265 callStack[0].arc = arc;
2266 callStack[0].reversed = FALSE;
2267
2268 SCIP_Bool* nodeVisited;
2269 if( BMSallocBlockMemoryArray(dec->env, &nodeVisited, dec->numNodes) == NULL )
2270 {
2271 return -1;
2272 }
2273 for( int i = 0; i < dec->numNodes; ++i )
2274 {
2275 nodeVisited[i] = FALSE;
2276 }
2277
2278 typedef struct
2279 {
2280 spqr_node node; /* cppcheck-suppress unusedStructMember */
2281 spqr_arc nodeArc; /* cppcheck-suppress unusedStructMember */
2282 } DFSCallData;
2283 DFSCallData* pathSearchCallStack;
2284 if( BMSallocBlockMemoryArray(dec->env, &pathSearchCallStack, dec->numNodes) == NULL )
2285 {
2286 return -1;
2287 }
2288 int pathSearchCallStackSize = 0;
2289
2290 while( callStackSize > 0 )
2291 {
2292 spqr_arc column_arc = callStack[callStackSize - 1].arc;
2293 SCIP_Bool reverseEverything = callStack[callStackSize - 1].reversed;
2294 --callStackSize;
2295 spqr_member column_arc_member = findArcMemberNoCompression(dec, column_arc);
2296 switch( getMemberType(dec, column_arc_member))
2297 {
2299 {
2300 spqr_node source = findEffectiveArcTailNoCompression(dec, column_arc);
2301 spqr_node target = findEffectiveArcHeadNoCompression(dec, column_arc);
2302
2303 assert(pathSearchCallStackSize == 0);
2304 pathSearchCallStack[0].node = source;
2305 pathSearchCallStack[0].nodeArc = getFirstNodeArc(dec, source);
2306 pathSearchCallStackSize++;
2307 while( pathSearchCallStackSize > 0 )
2308 {
2309 DFSCallData* dfsData = &pathSearchCallStack[pathSearchCallStackSize - 1];
2310 nodeVisited[dfsData->node] = TRUE;
2311 //cannot be a tree arc which is its parent
2312 if( arcIsTree(dec, dfsData->nodeArc) &&
2313 ( pathSearchCallStackSize <= 1 ||
2314 dfsData->nodeArc != pathSearchCallStack[pathSearchCallStackSize - 2].nodeArc ))
2315 {
2318 spqr_node other = head == dfsData->node ? tail : head;
2319 assert(other != dfsData->node);
2320 assert(!nodeVisited[other]);
2321 if( other == target )
2322 {
2323 break;
2324 }
2325 //We go up a level: add new node to the call stack
2326
2327 pathSearchCallStack[pathSearchCallStackSize].node = other;
2328 pathSearchCallStack[pathSearchCallStackSize].nodeArc = getFirstNodeArc(dec, other);
2329 ++pathSearchCallStackSize;
2330 continue;
2331 }
2332 do
2333 {
2334 dfsData->nodeArc = getNextNodeArcNoCompression(dec, dfsData->nodeArc, dfsData->node);
2335 if( dfsData->nodeArc == getFirstNodeArc(dec, dfsData->node))
2336 {
2337 --pathSearchCallStackSize;
2338 dfsData = &pathSearchCallStack[pathSearchCallStackSize - 1];
2339 }
2340 else
2341 {
2342 break;
2343 }
2344 }
2345 while( pathSearchCallStackSize > 0 );
2346 }
2347 for( int i = 0; i < pathSearchCallStackSize; ++i )
2348 {
2349 if( arcIsTree(dec, pathSearchCallStack[i].nodeArc))
2350 {
2351 SCIP_Bool arcReversedInPath =
2352 findEffectiveArcHeadNoCompression(dec, pathSearchCallStack[i].nodeArc) ==
2353 pathSearchCallStack[i].node;
2354 process_arc(output, &num_rows, callStack, &callStackSize, pathSearchCallStack[i].nodeArc, dec,
2355 computedSignStorage, arcReversedInPath != reverseEverything);
2356 }
2357 }
2358
2359 pathSearchCallStackSize = 0;
2360 break;
2361 }
2363 {
2364 SCIP_Bool columnReversed = arcIsReversedNonRigid(dec, column_arc);
2365
2366 spqr_arc first_arc = getFirstMemberArc(dec, column_arc_member);
2367 spqr_arc iter_arc = first_arc;
2368 int tree_count = 0;
2369 do
2370 {
2371 if( arcIsTree(dec, iter_arc))
2372 {
2373 SCIP_Bool treeIsReversed = arcIsReversedNonRigid(dec, iter_arc);
2374 process_arc(output, &num_rows, callStack, &callStackSize, iter_arc, dec,
2375 computedSignStorage, ( columnReversed != treeIsReversed ) != reverseEverything);
2376 tree_count++;
2377 }
2378 iter_arc = getNextMemberArc(dec, iter_arc);
2379 }
2380 while( iter_arc != first_arc );
2381 if( tree_count != 1 )
2382 {
2383 return -1;
2384 }
2385 break;
2386 }
2389 {
2390 SCIP_Bool columnReversed = arcIsReversedNonRigid(dec, column_arc);
2391 spqr_arc first_arc = getFirstMemberArc(dec, column_arc_member);
2392 spqr_arc iter_arc = first_arc;
2393 int nontree_count = 0;
2394 do
2395 {
2396 if( arcIsTree(dec, iter_arc))
2397 {
2398 SCIP_Bool treeIsReversed = arcIsReversedNonRigid(dec, iter_arc);
2399 process_arc(output, &num_rows, callStack, &callStackSize, iter_arc, dec,
2400 computedSignStorage, ( columnReversed == treeIsReversed ) != reverseEverything);
2401 }
2402 else
2403 {
2404 nontree_count++;
2405 }
2406 iter_arc = getNextMemberArc(dec, iter_arc);
2407 }
2408 while( iter_arc != first_arc );
2409 if( nontree_count != 1 )
2410 {
2411 return -1;
2412 }
2413 break;
2414 }
2416 SCIPABORT();
2417 return -1;
2418 }
2419 }
2420 }
2421 BMSfreeBlockMemoryArray(dec->env, &pathSearchCallStack, dec->numNodes);
2422 BMSfreeBlockMemoryArray(dec->env, &nodeVisited, dec->numNodes);
2423 BMSfreeBlockMemoryArray(dec->env, &callStack, dec->memRows);
2424
2425 return num_rows;
2426}
2427
2428/** Given a cycle (e.g. a matrix column), checks if the column's cycle matches the cycle in the
2429 * network matrix decomposition.
2430 */
2431static
2433 BMS_BUFMEM* bufmem, /**< Buffer memory */
2434 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2435 int column, /**< The column to check */
2436 const int* nonzrowidx, /**< Array with the nonzero row indices of the column */
2437 const double* nonzvals, /**< Array with the nonzero entry values of the column's row indices */
2438 int num_rows, /**< The number of nonzeros in the column */
2439 int* pathrowstorage, /**< Temporary storage vector for storing the fundamental path. Must have
2440 * at least as many entries allocated as the number of rows in dec. */
2441 SCIP_Bool* pathsignstorage /**< Temporary storage for the fundamental path directions. Must have
2442 * at least as many entries allocated as the number of rows in dec. */
2443 )
2444{
2445 int num_found_rows = decompositionGetFundamentalCycleRows(dec, column, pathrowstorage, pathsignstorage);
2446
2447 if( num_found_rows != num_rows )
2448 {
2449 return FALSE;
2450 }
2451 if( num_rows == 0 )
2452 {
2453 return TRUE;
2454 }
2455 spqr_row * pathRow;
2456 int * pathRowReversed;
2457
2458 if( BMSallocBufferMemoryArray(bufmem, &pathRow, num_rows) == NULL )
2459 {
2460 return FALSE;
2461 }
2462
2463 if( BMSallocBufferMemoryArray(bufmem, &pathRowReversed, num_rows) == NULL )
2464 {
2465 BMSfreeBufferMemoryArray(bufmem, &pathRow);
2466 return FALSE;
2467 }
2468 for( int i = 0; i < num_rows; ++i )
2469 {
2470 pathRow[i] = pathrowstorage[i];
2471 pathRowReversed[i] = pathsignstorage[i] ? 1 : 0;
2472 }
2473 SCIPsortIntInt(pathRow, pathRowReversed, num_rows);
2474
2475 spqr_row * secondPathRow;
2476 int * secondPathRowReversed;
2477 if( BMSallocBufferMemoryArray(bufmem, &secondPathRow, num_rows) == NULL )
2478 {
2479 BMSfreeBufferMemoryArray(bufmem, &pathRow);
2480 BMSfreeBufferMemoryArray(bufmem, &pathRowReversed);
2481 return FALSE;
2482 }
2483
2484 if( BMSallocBufferMemoryArray(bufmem, &secondPathRowReversed, num_rows) == NULL )
2485 {
2486 BMSfreeBufferMemoryArray(bufmem, &pathRow);
2487 BMSfreeBufferMemoryArray(bufmem, &pathRowReversed);
2488 BMSfreeBufferMemoryArray(bufmem, &secondPathRow);
2489 return FALSE;
2490 }
2491 for( int i = 0; i < num_rows; ++i )
2492 {
2493 secondPathRow[i] = nonzrowidx[i];
2494 secondPathRowReversed[i] = nonzvals[i] < 0.0 ? 1 : 0;
2495 }
2496
2497 SCIPsortIntInt(secondPathRow, secondPathRowReversed, num_rows);
2498
2499 SCIP_Bool good = TRUE;
2500 for( int i = 0; i < num_rows; ++i )
2501 {
2502 if( pathRow[i] != secondPathRow[i] || pathRowReversed[i] != secondPathRowReversed[i] )
2503 {
2504 good = FALSE;
2505 break;
2506 }
2507 }
2508 BMSfreeBufferMemoryArray(bufmem, &secondPathRowReversed);
2509 BMSfreeBufferMemoryArray(bufmem, &secondPathRow);
2510 BMSfreeBufferMemoryArray(bufmem, &pathRowReversed);
2511 BMSfreeBufferMemoryArray(bufmem, &pathRow);
2512
2513 return good;
2514}
2515
2516/** Returns the largest member id that is currently in the decomposition. */
2517static
2519 const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */
2520 )
2521{
2522 return dec->numMembers;
2523}
2524
2525/** Returns the largest arc id that is currently in the decomposition. */
2526static
2528 const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */
2529 )
2530{
2531 return dec->numArcs;
2532}
2533
2534/** Returns the largest node id that is currently in the decomposition. */
2535static
2537 const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */
2538 )
2539{
2540 return dec->numNodes;
2541}
2542
2543/** Returns the number of SPQR trees in the SPQR forest, i.e. the number of connected components. */
2544static
2546 const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */
2547 )
2548{
2549 return dec->numConnectedComponents;
2550}
2551
2552/** Creates a child marker in the network decomposition. */
2553static
2555 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2556 spqr_member member, /**< The member to create the arc in */
2557 spqr_member child, /**< The member that the arc points to */
2558 SCIP_Bool isTree, /**< Indicates if the new arc is a tree arc */
2559 spqr_arc* pArc, /**< Out-pointer to store the new arc's id */
2560 SCIP_Bool reversed /**< Sets the reversed field of the arc */
2561 )
2562{
2563 SCIP_CALL( createArc(dec, member, reversed, pArc) );
2564 dec->arcs[*pArc].element = isTree ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT;
2565 dec->arcs[*pArc].childMember = child;
2566
2567 addArcToMemberArcList(dec, *pArc, member);
2568
2569 return SCIP_OKAY;
2570}
2571
2572/** Creates a parent marker in the network decomposition. */
2573static
2575 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2576 spqr_member member, /**< The member to create the arc in */
2577 SCIP_Bool isTree, /**< Indicates if the new arc is a tree arc */
2578 spqr_member parent, /**< The member that the arc points to */
2579 spqr_arc parentMarker, /**< The parent arc in the parent that points to this member */
2580 spqr_arc* arc, /**< Out-pointer to store the new arc's id */
2581 SCIP_Bool reversed /**< Sets the reversed field of the arc */
2582 )
2583{
2584 SCIP_CALL( createArc(dec, member, reversed, arc) );
2585 dec->arcs[*arc].element = isTree ? MARKER_ROW_ELEMENT : MARKER_COLUMN_ELEMENT;
2586
2587 addArcToMemberArcList(dec, *arc, member);
2588
2589 dec->members[member].parentMember = parent;
2590 dec->members[member].markerOfParent = parentMarker;
2591 dec->members[member].markerToParent = *arc;
2592
2593 return SCIP_OKAY;
2594}
2595
2596/** Creates a child-marker parent-marker pair in the network decomposition. */
2597static
2599 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2600 spqr_member parentMember, /**< The parent member */
2601 spqr_member childMember, /**< The child member */
2602 SCIP_Bool parentIsTree, /**< Is the edge in the parent member (the child marker) a tree edge? */
2603 SCIP_Bool childMarkerReversed, /**< Is the child marker arc reversed?*/
2604 SCIP_Bool parentMarkerReversed /**< IS the parent marker arc reversed? */
2605 )
2606{
2607 spqr_arc parentToChildMarker = SPQR_INVALID_ARC;
2608 SCIP_CALL( createChildMarker(dec, parentMember, childMember, parentIsTree, &parentToChildMarker, childMarkerReversed) );
2609
2610 spqr_arc childToParentMarker = SPQR_INVALID_ARC;
2611 SCIP_CALL( createParentMarker(dec, childMember, !parentIsTree, parentMember, parentToChildMarker,
2612 &childToParentMarker, parentMarkerReversed) );
2613
2614 return SCIP_OKAY;
2615}
2616
2617/** Creates a child-marker parent-marker pair in the network decomposition, and returns the assigned arc id's. */
2618static
2620 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2621 spqr_member parentMember, /**< The parent member */
2622 spqr_member childMember, /**< The child member */
2623 SCIP_Bool parentIsTree, /**< Is the edge in the parent member (the child marker) a tree edge? */
2624 SCIP_Bool childMarkerReversed, /**< Is the child marker arc reversed?*/
2625 SCIP_Bool parentMarkerReversed, /**< IS the parent marker arc reversed? */
2626 spqr_arc* parentToChild, /**< Output-pointer containing arc id of the arc in the parent member */
2627 spqr_arc* childToParent /**< Output-pointer containing arc id of the arc in the child member */
2628 )
2629{
2630 SCIP_CALL( createChildMarker(dec, parentMember, childMember, parentIsTree, parentToChild, childMarkerReversed) );
2631 SCIP_CALL( createParentMarker(dec, childMember, !parentIsTree, parentMember, *parentToChild, childToParent,
2632 parentMarkerReversed) );
2633
2634 return SCIP_OKAY;
2635}
2636
2637/** Moves a given arc from one member to another, updating the linked lists it is contained in and the parent/child
2638 * information of the relevant members.
2639 */
2640static
2642 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2643 spqr_arc arc, /**< The arc to move */
2644 spqr_member oldMember, /**< The member that currently contains the arc */
2645 spqr_member newMember /**< The member to move the arc to */
2646 )
2647{
2648 assert(SPQRarcIsValid(arc));
2649 assert(arc < dec->memArcs);
2650 assert(dec);
2651
2652 assert(memberIsRepresentative(dec, oldMember));
2653 assert(memberIsRepresentative(dec, newMember));
2654 //Need to change the arc's member, remove it from the current member list and add it to the new member list
2655 assert(findArcMemberNoCompression(dec, arc) == oldMember);
2656
2657 removeArcFromMemberArcList(dec, arc, oldMember);
2658 addArcToMemberArcList(dec, arc, newMember);
2659
2660 dec->arcs[arc].member = newMember;
2661
2662 //If this arc has a childMember, update the information correctly!
2663 spqr_member childMember = dec->arcs[arc].childMember;
2664 if( SPQRmemberIsValid(childMember))
2665 {
2666 spqr_member childRepresentative = findArcChildMember(dec, arc);
2667 dec->members[childRepresentative].parentMember = newMember;
2668 }
2669 //If this arc is a marker to the parent, update the child arc marker of the parent to reflect the move
2670 if( dec->members[oldMember].markerToParent == arc )
2671 {
2672 dec->members[newMember].markerToParent = arc;
2673 dec->members[newMember].parentMember = dec->members[oldMember].parentMember;
2674 dec->members[newMember].markerOfParent = dec->members[oldMember].markerOfParent;
2675
2676 assert(findArcChildMemberNoCompression(dec, dec->members[oldMember].markerOfParent) == oldMember);
2677 dec->arcs[dec->members[oldMember].markerOfParent].childMember = newMember;
2678 }
2679}
2680
2681/** Merges the arc linked list of two members into one linked list. */
2682static
2684 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2685 spqr_member toMergeInto, /**< The member id that gets the new large linked list containing both */
2686 spqr_member toRemove /**< The member id that is invalidated, whose linked list is moved away. */
2687 )
2688{
2689 spqr_arc firstIntoArc = getFirstMemberArc(dec, toMergeInto);
2690 spqr_arc firstFromArc = getFirstMemberArc(dec, toRemove);
2691 assert(SPQRarcIsValid(firstIntoArc));
2692 assert(SPQRarcIsValid(firstFromArc));
2693
2694 spqr_arc lastIntoArc = getPreviousMemberArc(dec, firstIntoArc);
2695 spqr_arc lastFromArc = getPreviousMemberArc(dec, firstFromArc);
2696
2697 //Relink linked lists to merge them effectively
2698 dec->arcs[firstIntoArc].arcListNode.previous = lastFromArc;
2699 dec->arcs[lastIntoArc].arcListNode.next = firstFromArc;
2700 dec->arcs[firstFromArc].arcListNode.previous = lastIntoArc;
2701 dec->arcs[lastFromArc].arcListNode.next = firstIntoArc;
2702
2703 //Clean up old
2704 dec->members[toMergeInto].numArcs += dec->members[toRemove].numArcs;
2705 dec->members[toRemove].numArcs = 0;
2706 dec->members[toRemove].firstArc = SPQR_INVALID_ARC;
2707}
2708
2709/** Changes the type of a member from loop to series. */
2710static
2712 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2713 spqr_member member /**< The member whose type is changed */
2714 )
2715{
2716 assert(SPQRmemberIsValid(member));
2717 assert(member < dec->memMembers);
2718 assert(dec);
2719 assert(( getMemberType(dec, member) == SPQR_MEMBERTYPE_PARALLEL ||
2720 getMemberType(dec, member) == SPQR_MEMBERTYPE_SERIES ||
2721 getMemberType(dec, member) == SPQR_MEMBERTYPE_LOOP ) &&
2722 getNumMemberArcs(dec, member) == 2);
2723 assert(memberIsRepresentative(dec, member));
2724 dec->members[member].type = SPQR_MEMBERTYPE_SERIES;
2725}
2726
2727/** Changes the type of a member from loop to parallel. */
2728static
2730 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2731 spqr_member member /**< The member whose type is changed */
2732 )
2733{
2734 assert(SPQRmemberIsValid(member));
2735 assert(member < dec->memMembers);
2736 assert(dec);
2737 assert(( getMemberType(dec, member) == SPQR_MEMBERTYPE_PARALLEL ||
2738 getMemberType(dec, member) == SPQR_MEMBERTYPE_SERIES ||
2739 getMemberType(dec, member) == SPQR_MEMBERTYPE_LOOP ) &&
2740 getNumMemberArcs(dec, member) == 2);
2741 assert(memberIsRepresentative(dec, member));
2742 dec->members[member].type = SPQR_MEMBERTYPE_PARALLEL;
2743}
2744
2745/** Checks if the network decomposition is minimal, i.e. if it does not contain two adjacent parallel or series
2746 * members.
2747 */
2748static
2750 const SCIP_NETMATDECDATA* dec /**< The network matrix decomposition */
2751 )
2752{
2753 //Relies on parents/children etc. being set correctly in the tree
2754 SCIP_Bool isMinimal = TRUE;
2755 for( spqr_member member = 0; member < dec->numMembers; ++member )
2756 {
2757 if( !memberIsRepresentative(dec, member) || getMemberType(dec, member) == SPQR_MEMBERTYPE_UNASSIGNED )
2758 {
2759 continue;
2760 }
2761 spqr_member memberParent = findMemberParentNoCompression(dec, member);
2762 if( SPQRmemberIsValid(memberParent))
2763 {
2764 assert(memberIsRepresentative(dec, memberParent));
2765 SPQRMemberType memberType = getMemberType(dec, member);
2766 SPQRMemberType parentType = getMemberType(dec, memberParent);
2767 if( memberType == parentType && memberType != SPQR_MEMBERTYPE_RIGID )
2768 {
2769 isMinimal = FALSE;
2770 break;
2771 }
2772 }
2773 }
2774 return isMinimal;
2775}
2776
2777/** Decreases the count of the number of connected components in the network matrix decomposition. */
2778static
2780 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2781 int by /**< The number of connected components to remove. */
2782 )
2783{
2784 dec->numConnectedComponents -= by;
2785 assert(dec->numConnectedComponents >= 1);
2786}
2787
2788/** Reorders the arborescence of the SPQR that contains a given member so that the given member becomes the new root
2789 * of the arborescence.
2790 */
2791static
2793 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2794 spqr_member newRoot /**< The member to make the new root */
2795 )
2796{
2797 assert(dec);
2798 assert(memberIsRepresentative(dec, newRoot));
2799 //If the newRoot has no parent, it is already the root, so then there's no need to reorder.
2800 if( SPQRmemberIsValid(dec->members[newRoot].parentMember))
2801 {
2802 spqr_member member = findMemberParent(dec, newRoot);
2803 spqr_member newParent = newRoot;
2804 spqr_arc newMarkerToParent = dec->members[newRoot].markerOfParent;
2805 spqr_arc markerOfNewParent = dec->members[newRoot].markerToParent;
2806
2807 //Recursively update the parent
2808 do
2809 {
2810 assert(SPQRmemberIsValid(member));
2811 assert(SPQRmemberIsValid(newParent));
2812 spqr_member oldParent = findMemberParent(dec, member);
2813 spqr_arc oldMarkerToParent = dec->members[member].markerToParent;
2814 spqr_arc oldMarkerOfParent = dec->members[member].markerOfParent;
2815
2816 dec->members[member].markerToParent = newMarkerToParent;
2817 dec->members[member].markerOfParent = markerOfNewParent;
2818 dec->members[member].parentMember = newParent;
2819 dec->arcs[markerOfNewParent].childMember = member;
2820 dec->arcs[newMarkerToParent].childMember = SPQR_INVALID_MEMBER;
2821
2822 if( SPQRmemberIsValid(oldParent))
2823 {
2824 newParent = member;
2825 member = oldParent;
2826 newMarkerToParent = oldMarkerOfParent;
2827 markerOfNewParent = oldMarkerToParent;
2828 }
2829 else
2830 {
2831 break;
2832 }
2833 }
2834 while( TRUE ); /*lint !e506*/
2836 dec->members[newRoot].markerToParent = SPQR_INVALID_ARC;
2837 dec->members[newRoot].markerOfParent = SPQR_INVALID_ARC;
2838 }
2839}
2840
2841/** Deletes the SPQR tree (connected component) containing the given component rows and columns.
2842 *
2843 * Note that the implementation of this method does not actually modify the SPQR tree, but rather unlinks the rows and
2844 * columns from the relevant arcs. Thus, this method is a bit hacky and should be used with care.
2845 */
2846static
2848 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2849 const int* componentRows, /**< The rows of the connected component*/
2850 int numRows, /**< The number of rows */
2851 const int* componentCols, /**< The columns of the connected component*/
2852 int numCols /**< The number of columns */
2853 )
2854{
2855 //The below just removes the 'link' but not the internal datastructures.
2856 //This is sufficient for our purposes, as long as we do not re-introduce any of the 'negated' rows/columns back into the decomposition.
2857
2858 for( int i = 0; i < numRows; ++i )
2859 {
2860 spqr_row row = componentRows[i];
2861 if( SPQRarcIsValid(dec->rowArcs[row]) )
2862 {
2863 dec->rowArcs[row] = SPQR_INVALID_ARC;
2864 }
2865 }
2866
2867 for( int i = 0; i < numCols; ++i )
2868 {
2869 spqr_col col = componentCols[i];
2870 if( SPQRarcIsValid(dec->columnArcs[col]) )
2871 {
2872 dec->columnArcs[col] = SPQR_INVALID_ARC;
2873 }
2874 }
2875}
2876
2877#ifdef SCIP_DEBUG
2878/** Converts the members type to an associated character */ //Debugging functions to print the decomposition
2879static
2880char typeToChar(
2881 SPQRMemberType type /**< The member type */
2882 )
2883{
2884 switch (type)
2885 {
2887 return 'R';
2889 return 'P';
2891 return 'S';
2893 return 'L';
2894 default:
2895 return '?';
2896 }
2897}
2898
2899/** Prints an arc in DOT format */
2900static
2901void arcToDot(
2902 FILE* stream, /**< The stream to write the arc to */
2903 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2904 spqr_arc arc, /**< The arc to write */
2905 unsigned long dot_head, /**< The head id of the arc */
2906 unsigned long dot_tail, /**< The tail id of the arc */
2907 SCIP_Bool useElementNames /**< If TRUE, prints the corresponding row/column index. If FALSE,
2908 **< prints the spqr_arc id instead. */
2909 )
2910{
2911 assert(SPQRarcIsValid(arc));
2912 spqr_member member = findArcMemberNoCompression(dec, arc);
2913 SPQRMemberType member_type = getMemberType(dec, member);
2914 char type = typeToChar(member_type);
2915 const char *color = arcIsTree(dec, arc) ? ",color=red" : ",color=blue";
2916
2917 int arc_name = arc;
2918
2919 if( markerToParent(dec, member) == arc )
2920 {
2921 if( useElementNames )
2922 {
2923 arc_name = -1;
2924 }
2925 fprintf(stream, " %c_%d_%lu -> %c_p_%d [label=\"%d\",style=dashed%s];\n", type, member, dot_tail, type, member, arc_name, color);
2926 fprintf(stream, " %c_p_%d -> %c_%d_%lu [label=\"%d\",style=dashed%s];\n", type, member, type, member, dot_head, arc_name, color);
2927 fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_tail);
2928 fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_head);
2929 fprintf(stream, " %c_p_%d [style=dashed];\n", type, member);
2930 }
2931 else if( arcIsChildMarker(dec, arc) )
2932 {
2934 char childType = typeToChar(getMemberType(dec, child));
2935 if( useElementNames )
2936 {
2937 arc_name = -1;
2938 }
2939 fprintf(stream, " %c_%d_%lu -> %c_c_%d [label=\"%d\",style=dotted%s];\n", type, member, dot_tail, type, child, arc_name, color);
2940 fprintf(stream, " %c_c_%d -> %c_%d_%lu [label=\"%d\",style=dotted%s];\n", type, child, type, member, dot_head, arc_name, color);
2941 fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_tail);
2942 fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_head);
2943 fprintf(stream, " %c_c_%d [style=dotted];\n", type, child);
2944 fprintf(stream, " %c_p_%d -> %c_c_%d [style=dashed,dir=forward];\n", childType, child, type, child);
2945 }
2946 else
2947 {
2948 if( useElementNames )
2949 {
2950 spqr_element element = dec->arcs[arc].element;
2951 if( SPQRelementIsRow(element) )
2952 {
2953 arc_name = (int) SPQRelementToRow(element);
2954 }
2955 else
2956 {
2957 arc_name = (int) SPQRelementToColumn(element);
2958 }
2959 }
2960
2961 fprintf(stream, " %c_%d_%lu -> %c_%d_%lu [label=\"%d \",style=bold%s];\n", type, member, dot_tail, type, member, dot_head,
2962 arc_name, color);
2963 fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_tail);
2964 fprintf(stream, " %c_%d_%lu [shape=box];\n", type, member, dot_head);
2965 }
2966}
2967
2968/** Outputs the network decomposition as a DOT file. */
2969static
2970void decompositionToDot(
2971 FILE* stream, /**< The stream to write to */
2972 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
2973 SCIP_Bool useElementNames /**< If TRUE, prints the corresponding row/column index. If FALSE,
2974 * prints the spqr_arc id instead. */
2975 )
2976{
2977 fprintf(stream, "//decomposition\ndigraph decomposition{\n compound = TRUE;\n");
2978 for( spqr_member member = 0; member < dec->numMembers; ++member )
2979 {
2980 if( !memberIsRepresentative(dec, member) )
2981 continue;
2982 fprintf(stream, " subgraph member_%d{\n", member);
2983 switch( getMemberType(dec, member) )
2984 {
2986 {
2987 spqr_arc first_arc = getFirstMemberArc(dec, member);
2988 spqr_arc arc = first_arc;
2989 do
2990 {
2991 unsigned long arcHead = (unsigned long) findEffectiveArcHeadNoCompression(dec, arc);
2992 unsigned long arcTail = (unsigned long) findEffectiveArcTailNoCompression(dec, arc);
2993 arcToDot(stream, dec, arc, arcHead, arcTail, useElementNames);
2994 arc = getNextMemberArc(dec, arc);
2995 }
2996 while( arc != first_arc );
2997 break;
2998 }
3001 {
3002 spqr_arc first_arc = getFirstMemberArc(dec, member);
3003 spqr_arc arc = first_arc;
3004 do
3005 {
3006 if( arcIsReversedNonRigid(dec, arc) )
3007 {
3008 arcToDot(stream, dec, arc, 1, 0, useElementNames);
3009 }
3010 else
3011 {
3012 arcToDot(stream, dec, arc, 0, 1, useElementNames);
3013 }
3014 arc = getNextMemberArc(dec, arc);
3015 }
3016 while( arc != first_arc );
3017 break;
3018 }
3020 {
3021 unsigned long i = 0;
3022 unsigned long num_member_arcs = (unsigned long) getNumMemberArcs(dec, member);
3023 spqr_arc first_arc = getFirstMemberArc(dec, member);
3024 spqr_arc arc = first_arc;
3025 do
3026 {
3027 unsigned long head = i;
3028 unsigned long tail = (i + 1) % num_member_arcs;
3029 if( arcIsReversedNonRigid(dec, arc) )
3030 {
3031 unsigned long temp = head;
3032 head = tail;
3033 tail = temp;
3034 }
3035 arcToDot(stream, dec, arc, head, tail, useElementNames);
3036 arc = getNextMemberArc(dec, arc);
3037 i++;
3038 }
3039 while( arc != first_arc );
3040 break;
3041 }
3043 break;
3044 }
3045 fprintf(stream, " }\n");
3046 }
3047 fprintf(stream, "}\n");
3048}
3049#endif
3050
3051static
3053 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
3054 spqr_member member, /**< The member to merge */
3055 spqr_member parent, /**< The parent of the member to merge into */
3056 spqr_arc parentToChild, /**< The arc from the parent pointing to the member */
3057 spqr_arc childToParent, /**< The arc in the child pointing to the parent */
3058 SCIP_Bool headToHead, /**< Identify the head of parentToChild with the head of childToParent?*/
3059 spqr_member* mergedMember /**< Out-pointer to the member id of the final member */
3060 )
3061{
3062 assert(dec);
3063 assert(SPQRmemberIsValid(member));
3064 assert(memberIsRepresentative(dec, member));
3065 assert(SPQRmemberIsValid(parent));
3066 assert(memberIsRepresentative(dec, parent));
3067 assert(findMemberParentNoCompression(dec, member) == parent);
3068 assert(markerOfParent(dec, member) == parentToChild);
3069 assert(markerToParent(dec, member) == childToParent);
3070
3071 removeArcFromMemberArcList(dec, parentToChild, parent);
3072 removeArcFromMemberArcList(dec, childToParent, member);
3073
3074 spqr_node parentTail = findEffectiveArcTail(dec, parentToChild) ;
3075 spqr_node parentHead = findEffectiveArcHead(dec, parentToChild);
3076 spqr_node childTail = findEffectiveArcTail(dec, childToParent);
3077 spqr_node childHead = findEffectiveArcHead(dec, childToParent);
3078 spqr_node parentArcNodes[2] = { parentTail, parentHead };
3079 spqr_node childArcNodes[2] = { childTail, childHead };
3080
3081 clearArcHeadAndTail(dec, parentToChild);
3082 clearArcHeadAndTail(dec, childToParent);
3083
3084 spqr_node first = childArcNodes[headToHead ? 0 : 1];
3085 spqr_node second = childArcNodes[headToHead ? 1 : 0];
3086 {
3087 spqr_node newNode = mergeNodes(dec, parentArcNodes[0], first);
3088 spqr_node toRemoveFrom = newNode == first ? parentArcNodes[0] : first;
3089 mergeNodeArcList(dec, newNode, toRemoveFrom);
3090 }
3091 {
3092 spqr_node newNode = mergeNodes(dec, parentArcNodes[1], second);
3093 spqr_node toRemoveFrom = newNode == second ? parentArcNodes[1] : second;
3094 mergeNodeArcList(dec, newNode, toRemoveFrom);
3095 }
3096
3097 spqr_member newMember = mergeMembers(dec, member, parent);
3098 spqr_member toRemoveFrom = newMember == member ? parent : member;
3099 mergeMemberArcList(dec, newMember, toRemoveFrom);
3100 if( toRemoveFrom == parent )
3101 {
3102 updateMemberParentInformation(dec, newMember, toRemoveFrom);
3103 }
3104 updateMemberType(dec, newMember, SPQR_MEMBERTYPE_RIGID);
3105 *mergedMember = newMember;
3106
3107 return SCIP_OKAY;
3108}
3109
3110static
3112 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
3113 BMS_BLKMEM * blkmem, /**< The block memory to use for the created digraph */
3114 SCIP_DIGRAPH** pdigraph, /**< Pointer to the pointer to store the created digraph */
3115 SCIP_Bool createrowarcs /**< Should the row arcs be added to the created digraph? */
3116 )
3117{
3118 /* Compute the number of rows m in the network matrix decomposition. The underlying graph has m+1 vertices. */
3119 int nrows = 0;
3120 for( int i = 0; i < dec->memRows; ++i )
3121 if( netMatDecDataContainsRow(dec, i) )
3122 ++nrows;
3123
3124 int nvertices = nrows + 1;
3125 SCIP_CALL( SCIPdigraphCreate(pdigraph, blkmem, nvertices) );
3126
3127 /* Map decomposition nodes to graph nodes. The first node of each component is mapped to node 0 */
3128 int largestNode = largestNodeID(dec);
3129 int* dectographnode;
3130 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &dectographnode, largestNode) );
3131 for( int i = 0; i < largestNode; ++i )
3132 dectographnode[i] = -1;
3133
3134 SCIP_DIGRAPH* digraph = *pdigraph;
3135 /* Recursively explore the decomposition, adding the edges of each component and identifying them as needed */
3136
3137 typedef struct {
3138 spqr_member member;
3139 spqr_arc arc;
3140 int graphfirstarchead;
3141 int graphfirstarctail;
3142 } CallData;
3143 spqr_member largestmember = largestMemberID(dec);
3144 CallData* calldata;
3145 int ncalldata = 0;
3146 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &calldata, largestmember) );
3147
3148 SCIP_Bool* rowprocessed;
3149 SCIP_ALLOC( BMSallocClearBlockMemoryArray(blkmem, &rowprocessed, dec->memRows) );
3150
3151 /* This outer loop loops over all components; this is a bit ugly unfortunately */
3152 int nextnodeindex = 1; /* 0 is assigned to the first arc of the root members' head */
3153 for( int i = 0; i < dec->memRows ; ++i )
3154 {
3155 spqr_arc rowarc = dec->rowArcs[i];
3156 if( SPQRarcIsInvalid(rowarc) )
3157 continue;
3158
3159 assert(netMatDecDataContainsRow(dec, i)) ;
3160 if( rowprocessed[i] )
3161 continue;
3162
3163 /* Find the root member of the SPQR tree */
3164 spqr_member arcmember = findArcMember(dec, rowarc);
3165 spqr_member rootmember = arcmember;
3166 do
3167 {
3168 spqr_member parent = findMemberParent(dec, rootmember);
3169 if( SPQRmemberIsInvalid(parent) )
3170 break;
3171 rootmember = parent;
3172 }
3173 while( TRUE ); /*lint !e506*/
3174
3175 /* We identify all the SPQR trees at node 0 */
3176 calldata[0].member = rootmember;
3177 calldata[0].arc = getFirstMemberArc(dec, rootmember);
3178 calldata[0].graphfirstarchead = 0;
3179 calldata[0].graphfirstarctail = nextnodeindex;
3180 ++nextnodeindex;
3181 ++ncalldata;
3182
3183 while( ncalldata != 0 )
3184 {
3185 /* Pop the next member to process of the call stack */
3186 CallData explore = calldata[ncalldata-1];
3187 --ncalldata;
3188
3189 switch( getMemberType(dec, explore.member) )
3190 {
3192 {
3193 /* First, we set the initial arc's nodes */
3194 {
3195 assert(dectographnode[findEffectiveArcHeadNoCompression(dec, explore.arc)] == -1);
3196 assert(dectographnode[findEffectiveArcTailNoCompression(dec, explore.arc)] == -1);
3197 spqr_node exploreHead = findArcHead(dec, explore.arc);
3198 spqr_node exploreTail = findArcTail(dec, explore.arc);
3199 if( findArcSign(dec, explore.arc).reversed )
3200 SCIPswapInts(&exploreTail, &exploreTail);
3201 dectographnode[exploreHead] = explore.graphfirstarchead;
3202 dectographnode[exploreTail] = explore.graphfirstarctail;
3203 }
3204
3205 /* Add all the members arcs to the graph */
3206 spqr_arc firstArc = getFirstMemberArc(dec, explore.member);
3207 spqr_arc arc = firstArc;
3208 do
3209 {
3210 spqr_node head = findArcHead(dec, arc);
3211 spqr_node tail = findArcTail(dec, arc);
3212 if( findArcSign(dec, arc).reversed )
3213 SCIPswapInts(&head, &tail);
3214
3215 if( dectographnode[head] == -1 )
3216 {
3217 dectographnode[head] = nextnodeindex;
3218 ++nextnodeindex;
3219 }
3220 if( dectographnode[tail] == -1 )
3221 {
3222 dectographnode[tail] = nextnodeindex;
3223 ++nextnodeindex;
3224 }
3225
3226 /* If an arc is a marker to a child node, we add it to the call stack */
3227 if( arcIsChildMarker(dec, arc))
3228 {
3229 spqr_member child = findArcChildMember(dec, arc);
3230 spqr_arc toparent = markerToParent(dec, child);
3231
3232 /* Identify head with head and tail with tail */
3233 /* Push member onto the processing stack (recursive call) */
3234 calldata[ncalldata].member = child;
3235 calldata[ncalldata].arc = toparent;
3236 calldata[ncalldata].graphfirstarchead = dectographnode[head];
3237 calldata[ncalldata].graphfirstarctail = dectographnode[tail];
3238
3239 ++ncalldata;
3240 }
3241 else if( arc != markerToParent(dec, explore.member) )
3242 {
3243 /* We only realize non-virtual arcs */
3244 if( createrowarcs || !arcIsTree(dec, arc) )
3245 {
3246 spqr_element element = arcGetElement(dec, arc);
3247 size_t ind;
3248 if( SPQRelementIsRow(element) )
3249 {
3250 ind = SPQRelementToRow(element); /*lint !e732 */
3251 }
3252 else
3253 {
3254 ind = SPQRelementToColumn(element) + dec->memRows; /*lint !e732 !e776 */
3255 }
3256 SCIP_CALL( SCIPdigraphAddArc(digraph, dectographnode[tail], dectographnode[head],
3257 (void*) ind) );
3258 }
3259 }
3260 spqr_element element = arcGetElement(dec, arc);
3261 if( SPQRelementIsRow(element) && element != MARKER_ROW_ELEMENT )
3262 {
3263 spqr_row row = SPQRelementToRow(element);
3264 assert(row < dec->memRows);
3265 rowprocessed[row] = TRUE;
3266 }
3267 arc = getNextMemberArc(dec, arc);
3268 }
3269 while( arc != firstArc );
3270 break;
3271 }
3272
3275 {
3276 spqr_arc firstArc = getFirstMemberArc(dec, explore.member);
3277 spqr_arc arc = firstArc;
3278 do
3279 {
3280 /* If an edge is a marker to a child node, we add it to the call stack */
3281 if( arcIsChildMarker(dec, arc) )
3282 {
3283 spqr_member child = findArcChildMember(dec, arc);
3284 spqr_arc toparent = markerToParent(dec, child);
3285 SCIP_Bool directionsmatch =
3286 arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc);
3287
3288 /* Identify head with head and tail with tail */
3289 /* Push member onto the processing stack (recursive call) */
3290 calldata[ncalldata].member = child;
3291 calldata[ncalldata].arc = toparent;
3292 calldata[ncalldata].graphfirstarchead = directionsmatch ? explore.graphfirstarchead
3293 : explore.graphfirstarctail;
3294 calldata[ncalldata].graphfirstarctail = directionsmatch ? explore.graphfirstarctail
3295 : explore.graphfirstarchead;
3296
3297 ++ncalldata;
3298 }
3299 else if( arc != markerToParent(dec, explore.member) )
3300 {
3301 /* We only realize non-virtual arcs */
3302 if( createrowarcs || !arcIsTree(dec, arc) )
3303 {
3304 spqr_element element = arcGetElement(dec, arc);
3305 size_t ind;
3306 if( SPQRelementIsRow(element) )
3307 {
3308 ind = SPQRelementToRow(element); /*lint !e732 */
3309 }
3310 else
3311 {
3312 ind = SPQRelementToColumn(element) + dec->memRows; /*lint !e732 !e776 */
3313 }
3314 if( arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc) )
3315 {
3316 SCIP_CALL( SCIPdigraphAddArc(digraph, explore.graphfirstarctail,
3317 explore.graphfirstarchead, (void*) ind) );
3318 }
3319 else
3320 {
3321 SCIP_CALL( SCIPdigraphAddArc(digraph, explore.graphfirstarchead,
3322 explore.graphfirstarctail, (void*) ind) );
3323 }
3324 }
3325 }
3326 spqr_element element = arcGetElement(dec, arc);
3327 if( SPQRelementIsRow(element) && element != MARKER_ROW_ELEMENT )
3328 {
3329 spqr_row row = SPQRelementToRow(element);
3330 assert(row < dec->memRows);
3331 rowprocessed[row] = TRUE;
3332 }
3333 arc = getNextMemberArc(dec, arc);
3334 }
3335 while( arc != firstArc );
3336 break;
3337 }
3338
3340 {
3341 int count = getNumMemberArcs(dec, explore.member);
3342 spqr_arc arc = explore.arc;
3343 int firstnode = explore.graphfirstarchead;
3344 int secondnode = explore.graphfirstarctail;
3345 for( int j = 0; j < count; ++j )
3346 {
3347 if( arcIsChildMarker(dec, arc) )
3348 {
3349 spqr_member child = findArcChildMember(dec, arc);
3350 spqr_arc toparent = markerToParent(dec, child);
3351 SCIP_Bool directionsmatch =
3352 arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc);
3353
3354 /* Identify head with head and tail with tail */
3355 /* Push member onto the processing stack (recursive call) */
3356 calldata[ncalldata].member = child;
3357 calldata[ncalldata].arc = toparent;
3358 calldata[ncalldata].graphfirstarchead = directionsmatch ? firstnode : secondnode;
3359 calldata[ncalldata].graphfirstarctail = directionsmatch ? secondnode : firstnode;
3360
3361 ++ncalldata;
3362 }
3363 else if( arc != markerToParent(dec, explore.member) )
3364 {
3365 /* We only realize non-virtual arcs */
3366 if( createrowarcs || !arcIsTree(dec, arc) )
3367 {
3368 spqr_element element = arcGetElement(dec,arc);
3369 size_t ind;
3370 if( SPQRelementIsRow(element) )
3371 {
3372 ind = SPQRelementToRow(element); /*lint !e732 */
3373 }
3374 else
3375 {
3376 ind = SPQRelementToColumn(element) + dec->memRows; /*lint !e732 !e776 */
3377 }
3378
3379 if( arcIsReversedNonRigid(dec, explore.arc) == arcIsReversedNonRigid(dec, arc) )
3380 {
3381 SCIP_CALL( SCIPdigraphAddArc(digraph, secondnode, firstnode, (void*) ind) );
3382 }
3383 else
3384 {
3385 SCIP_CALL( SCIPdigraphAddArc(digraph, firstnode, secondnode, (void*) ind) );
3386 }
3387 }
3388 }
3389
3390 spqr_element element = arcGetElement(dec, arc);
3391 if( SPQRelementIsRow(element) && element != MARKER_ROW_ELEMENT )
3392 {
3393 spqr_row row = SPQRelementToRow(element);
3394 rowprocessed[row] = TRUE;
3395 }
3396
3397 arc = getNextMemberArc(dec, arc);
3398 secondnode = firstnode;
3399 if( j >= count - 2 )
3400 {
3401 firstnode = explore.graphfirstarctail;
3402 }
3403 else
3404 {
3405 firstnode = nextnodeindex;
3406 ++nextnodeindex;
3407 }
3408 }
3409 assert(arc == explore.arc); /* Check that we added all arcs */
3410 break;
3411 }
3412
3414 {
3415 SCIPerrorMessage("Can not create graph for unassigned member type, exiting.\n");
3416 SCIPABORT();
3417 return SCIP_ERROR;
3418 }
3419 }
3420 }
3421 }
3422
3423 /* We cannot have more vertices then there are in the graph.
3424 * If we processed everything right, all vertices should have been hit.
3425 */
3426 assert(nextnodeindex == nvertices);
3427
3428 BMSfreeBlockMemoryArray(blkmem, &rowprocessed, dec->memRows);
3429 BMSfreeBlockMemoryArray(blkmem, &calldata, largestmember);
3430 BMSfreeBlockMemoryArray(blkmem, &dectographnode, largestNode);
3431
3432 return SCIP_OKAY;
3433}
3434
3435/* ---------- START functions for column addition ------------------------------------------------------------------- */
3436
3437/** Path arcs are all those arcs that correspond to nonzeros of the column to be added. */
3438typedef int path_arc_id;
3439#define INVALID_PATH_ARC (-1)
3440
3441/** Returns true if the path arc is invalid. */
3442static
3444 const path_arc_id arc /**< The path arc to check */
3445 )
3446{
3447 return arc < 0;
3448}
3449
3450/** Returns true if the path arc is valid. */
3451static
3453 const path_arc_id arc /**< The path arc to check */
3454 )
3455{
3456 return !pathArcIsInvalid(arc);
3457}
3458
3459/** A forward linked list-node for the path arcs.
3460 *
3461 * Contains a pointer to the next path arc in the member graph and the
3462 * next path arc in the SPQR tree. We additionally store copies of the corresponding arc's node indices.
3463 */
3464typedef struct
3465{
3466 spqr_arc arc; /**< The arc corresponding to the path ar c*/
3467 spqr_node arcHead; /**< A copy of the arc's head node index */
3468 spqr_node arcTail; /**< A copy of the arc's tail node index */
3469 path_arc_id nextMember; /**< Array index of the next path arc in the member path arc linked list */
3470 path_arc_id nextOverall; /**< Array index of the next path arc in the total path arc linked list */
3471 SCIP_Bool reversed; /**< Is the path arc occuring forwards or backwards in the path?
3472 * Corresponds to the sign of the nonzero */
3474
3475/** Index for the reduced members, which are the members of the sub-SPQR tree containing all path arcs.
3476 *
3477 * Note that these are indexed separately from the members of the SPQR tree!
3478 */
3480#define INVALID_REDUCED_MEMBER (-1)
3481
3482/** Returns true if the reduced member is invalid. */
3483static
3485 const reduced_member_id id /**< The reduced member to check */
3486 )
3487{
3488 return id < 0;
3489}
3490
3491/** Returns true if the reduced member is valid. */
3492static
3494 const reduced_member_id id /**< The reduced member to check */
3495 )
3496{
3497 return !reducedMemberIsInvalid(id);
3498}
3499
3500/** Array index for the children of a reduced member */
3501typedef int children_idx;
3502
3503/** Type of the member, signifies to what degree we processed the member and how to treat with it when updating the
3504 * graph.
3505 */
3506typedef enum
3507{
3508 REDUCEDMEMBER_TYPE_UNASSIGNED = 0, /**< We have not yet decided if the reduced member contains a valid path */
3509 REDUCEDMEMBER_TYPE_CYCLE = 1, /**< The reduced member is a leaf node of the SPQR tree and contains a path
3510 * that forms a cycle with the corresponding virtual edge, i.e.
3511 * it is propagated */
3512 REDUCEDMEMBER_TYPE_MERGED = 2, /**< The reduced member contains a path and is not propagated */
3513 REDUCEDMEMBER_TYPE_NOT_NETWORK = 3 /**< The reduced member does not have a valid path or can not be oriented
3514 * to form a valid path with the other members. */
3516
3517/** Defines the structure of the path in the reduced member */
3518typedef enum
3519{
3520 INTO_HEAD = 0, /**< The directed path goes into the head of the virtual arc */
3521 INTO_TAIL = 1, /**< The directed path goes into the tail of the virtual arc */
3522 OUT_HEAD = 2, /**< The directed path goes out of the head of the virtual arc */
3523 OUT_TAIL = 3 /**< The directed path goes out of the tail of the virtual arc */
3525
3526/** Check if the path type is into. */
3527static
3529 MemberPathType type /**< The path type to check */
3530 )
3531{
3532 return type == INTO_HEAD || type == INTO_TAIL;
3533}
3534
3535/** Check if the path end node is the head or tail node of the corresponding arc. */
3536static
3538 MemberPathType type /**< The path type to check */
3539 )
3540{
3541 return type == INTO_HEAD || type == OUT_HEAD;
3542}
3543
3544/** A struct that keeps track of the relevant data for the members that are in the subtree given by the specified row
3545 * arcs.
3546 *
3547 * We typically call these 'reduced' members (members of the reduced tree).
3548 */
3549typedef struct
3550{
3551 spqr_member member; /**< The id of the member */
3552 spqr_member rootMember; /**< The root member of the arborescence that contains this member */
3553 int depth; /**< The depth of this member in the arborescence */
3554 ReducedMemberType type; /**< The type of the member */
3555 reduced_member_id parent; /**< The reduced member id of the parent of this reduced member */
3556
3557 children_idx firstChild; /**< The index of the first child in the children array. */
3558 children_idx numChildren; /**< The number of children in the arborescence of this reduced member */
3559
3560 path_arc_id firstPathArc; /**< Head of the linked list containing the path arcs */
3561 int numPathArcs; /**< The number of path arcs in the linked list */
3562
3563 SCIP_Bool reverseArcs; /**< Will the arcs in this component be reversed? */
3564 spqr_node rigidPathStart; /**< The start node of the path. Only used for Rigid/3-connected nodes */
3565 spqr_node rigidPathEnd; /**< The end node of the path. Only used for Rigid/3-connected nodes */
3566
3567 SCIP_Bool pathBackwards; /**< Indicates if the path direction is reversed with respect to the
3568 * default orientation within series and parallel members. */
3569
3570 int numPropagatedChildren; /**< Counts the number of children that are cycles that propagated to this
3571 * reduced member */
3572 int componentIndex; /**< Stores the index of the component of the SPQR forest that this reduced
3573 * member is contained in. */
3574
3575 MemberPathType pathType; /**< The type of the path with respect to the virtual arc */
3576 reduced_member_id nextPathMember; /**< Indicates the id of the next reduced member in the path during merging.
3577 * During merging, the SPQR tree must be a path itself. */
3578 SCIP_Bool nextPathMemberIsParent; /**< Indicates if the next reduced member in the path is a parent of
3579 * this member */
3580 spqr_arc pathSourceArc; /**< The virtual arc from where the path originates */
3581 spqr_arc pathTargetArc; /**< The virtual arc where the path has to go */
3583
3584/** Keeps track of the data relevant for each SPQR tree in the SPQR forest. */
3585typedef struct
3586{
3587 int rootDepth; /**< The depth of the root node of the subtree in the arborescence */
3588 reduced_member_id root; /**< The reduced member id of the root */
3589
3590 reduced_member_id pathEndMembers[2]; /**< The reduced members that contain the ends of the path */
3591 int numPathEndMembers; /**< The number of reduced members that contain an end of the path */
3593
3594/** Keeps track of the reduced member and the reduced member that is the root of the arborescence for a single member */
3595typedef struct
3596{
3597 reduced_member_id reducedMember; /**< The ID of the associated reduced member */
3598 reduced_member_id rootDepthMinimizer; /**< The ID of the reduced member that is the root of the arborescence this
3599 * reduced member is contained in. */
3600} MemberInfo;
3601
3602/** Data to be used for the recursive algorithms that creates the sub-SPQR trees that contain the path edges */
3603typedef struct
3604{
3605 spqr_member member; /**< The current member */
3607
3608/** The main datastructure that manages all the data for column-addition in network matrices */
3609typedef struct
3610{
3611 SCIP_Bool remainsNetwork; /**< Does the addition of the current column give a network matrix? */
3612
3613 SPQRColReducedMember* reducedMembers; /**< The array of reduced members, that form the subtree containing the
3614 * rows of the current column. */
3615 int memReducedMembers; /**< Number of allocated slots in the reduced member array */
3616 int numReducedMembers; /**< Number of used slots in the reduced member array */
3617
3618 SPQRColReducedComponent* reducedComponents;/**< The array of reduced components,
3619 * that represent the SPQR trees in the SPQR forest */
3620 int memReducedComponents;/**< Number of allocated slots in the reduced component array */
3621 int numReducedComponents;/**< Number of used slots in the reduced component array */
3622
3623 MemberInfo* memberInformation; /**< Array with member information; tracks the reduced member id that
3624 * corresponds to every member in the decomposition. */
3625 int memMemberInformation;/**< Number of allocated slots in the member information array */
3626 int numMemberInformation;/**< Number of used slots in the member information array */
3627
3628 reduced_member_id* childrenStorage; /**< Array that stores the children of the reduced member arborescences.
3629 * Each reduced member has a 'firstChild' field and a length, that points
3630 * to the subarray within this array with its children. This array is
3631 * shared here in order to minimize allocations across iterations. */
3632 int memChildrenStorage; /**< Number of allocated slots for the children storage array */
3633 int numChildrenStorage; /**< Number of used slots for the children storage array */
3634
3635 PathArcListNode* pathArcs; /**< Array that contains the linked-list nodes of the path arcs, that
3636 * correspond to the rows of the current column. */
3637 int memPathArcs; /**< Number of allocated slots for the path arc array */
3638 int numPathArcs; /**< Number of used slots for the path arc array */
3639 path_arc_id firstOverallPathArc;/**< Head node of the linked list containing all path arcs */
3640
3641 int* nodeInPathDegree; /**< Array that contains the in degree of all nodes */
3642 int* nodeOutPathDegree; /**< Array that contains the out degree of all nodes */
3643 int memNodePathDegree; /**< The number of allocated slots for the node-degree arrays */
3644
3645 SCIP_Bool* arcInPath; /**< Is the given arc in the path? */
3646 SCIP_Bool* arcInPathReversed; /**< Is the given arc's direction reversed in the path? */
3647 int memArcsInPath; /**< The number of allocated slots for the arcInPath(Reversed) arrays */
3648
3649 CreateReducedMembersCallstack* createReducedMembersCallStack; /**< Callstack for createReducedMembers() */
3650 int memCreateReducedMembersCallStack; /**< Allocated memory for callstack for createReducedMembers() */
3651
3652 spqr_col newColIndex; /**< The index of the new column to be added */
3653
3654 spqr_row* newRowArcs; /**< The row indices of the nonzeros of the column to be added, that are
3655 * not yet in the decomposition. */
3656 SCIP_Bool* newRowArcReversed; /**< True if the nonzero corresponding to the row index is -1,
3657 * false otherwise */
3658 int memNewRowArcs; /**< Number of allocated slots in newRowArcs(Reversed) */
3659 int numNewRowArcs; /**< Number of new rows in the column to be added */
3660
3661 spqr_arc* decompositionRowArcs; /**< For each row nonzero that is in the decomposition,
3662 * stores the corresponding decomposition arc */
3663 SCIP_Bool* decompositionArcReversed; /**< For each row nonzero that is in the decomposition,
3664 * stores whether the corresponding decomposition arc is reversed */
3665 int memDecompositionRowArcs; /**< Number of allocated slots in decompositionRowArcs(Reversed) */
3666 int numDecompositionRowArcs; /**< Number of used slots in decompositionRowArcs(Reversed) */
3667
3668 spqr_member* leafMembers; /**< Array that stores the leaf members of the SPQR forest */
3669 int numLeafMembers; /**< Number of used slots in leafMembers array */
3670 int memLeafMembers; /**< Number of allocated slots in leafMembers array */
3672
3673/** Clean up internal temporary data structures that were used in the previous column iteration. */
3674static
3676 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
3677 SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */
3678 )
3679{
3680 assert(dec);
3681 assert(newCol);
3682
3683 path_arc_id pathArc = newCol->firstOverallPathArc;
3684 while( pathArcIsValid(pathArc) )
3685 {
3686 spqr_node head = newCol->pathArcs[pathArc].arcHead;
3687 spqr_node tail = newCol->pathArcs[pathArc].arcTail;
3688 if( SPQRnodeIsValid(head) )
3689 {
3690 newCol->nodeInPathDegree[head] = 0;
3691 }
3692 if( SPQRnodeIsValid(tail) )
3693 {
3694 newCol->nodeOutPathDegree[tail] = 0;
3695 }
3696
3697 spqr_arc arc = newCol->pathArcs[pathArc].arc;
3698 if( arc < newCol->memArcsInPath )
3699 {
3700 newCol->arcInPath[arc] = FALSE;
3701 newCol->arcInPathReversed[arc] = FALSE;
3702 }
3703 pathArc = newCol->pathArcs[pathArc].nextOverall;
3704 }
3705#ifndef NDEBUG
3706 for( int i = 0; i < newCol->memArcsInPath; ++i )
3707 {
3708 assert(newCol->arcInPath[i] == FALSE);
3709 assert(newCol->arcInPathReversed[i] == FALSE);
3710 }
3711
3712 for( int i = 0; i < newCol->memNodePathDegree; ++i )
3713 {
3714 assert(newCol->nodeInPathDegree[i] == 0);
3715 assert(newCol->nodeOutPathDegree[i] == 0);
3716 }
3717#endif
3718
3720 newCol->numPathArcs = 0;
3721}
3722
3723/** Create a new network column addition datastructure. */
3724static
3726 BMS_BLKMEM* blkmem, /**< Block memory */
3727 SCIP_NETCOLADD** pcoladd /**< Out-pointer for the created network column addition datastructure */
3728 )
3729{
3730 assert(blkmem);
3731
3732 SCIP_ALLOC( BMSallocBlockMemory(blkmem, pcoladd) );
3733 SCIP_NETCOLADD* newCol = *pcoladd;
3734
3735 newCol->remainsNetwork = FALSE;
3736 newCol->reducedMembers = NULL;
3737 newCol->memReducedMembers = 0;
3738 newCol->numReducedMembers = 0;
3739
3740 newCol->reducedComponents = NULL;
3741 newCol->memReducedComponents = 0;
3742 newCol->numReducedComponents = 0;
3743
3744 newCol->memberInformation = NULL;
3745 newCol->memMemberInformation = 0;
3746 newCol->numMemberInformation = 0;
3747
3748 newCol->childrenStorage = NULL;
3749 newCol->memChildrenStorage = 0;
3750 newCol->numChildrenStorage = 0;
3751
3752 newCol->pathArcs = NULL;
3753 newCol->memPathArcs = 0;
3754 newCol->numPathArcs = 0;
3756
3757 newCol->nodeInPathDegree = NULL;
3758 newCol->nodeOutPathDegree = NULL;
3759 newCol->memNodePathDegree = 0;
3760
3761 newCol->arcInPath = NULL;
3762 newCol->arcInPathReversed = NULL;
3763 newCol->memArcsInPath = 0;
3764
3767
3768 newCol->newColIndex = SPQR_INVALID_COL;
3769
3770 newCol->newRowArcs = NULL;
3771 newCol->newRowArcReversed = NULL;
3772 newCol->memNewRowArcs = 0;
3773 newCol->numNewRowArcs = 0;
3774
3775 newCol->decompositionRowArcs = NULL;
3777 newCol->memDecompositionRowArcs = 0;
3778 newCol->numDecompositionRowArcs = 0;
3779
3780 newCol->leafMembers = NULL;
3781 newCol->numLeafMembers = 0;
3782 newCol->memLeafMembers = 0;
3783
3784 return SCIP_OKAY;
3785}
3786
3787/** Free a network column addition datastructure */
3788static
3790 BMS_BLKMEM* blkmem, /**< Block memory */
3791 SCIP_NETCOLADD** pcoladd /**< Pointer to the network column addition datastructure to be freed */
3792 )
3793{
3794 assert(blkmem);
3795
3796 SCIP_NETCOLADD* newCol = *pcoladd;
3799 BMSfreeBlockMemoryArray(blkmem, &newCol->newRowArcs, newCol->memNewRowArcs);
3800 BMSfreeBlockMemoryArray(blkmem, &newCol->newRowArcReversed, newCol->memNewRowArcs);
3802 BMSfreeBlockMemoryArray(blkmem, &newCol->arcInPath, newCol->memArcsInPath);
3803 BMSfreeBlockMemoryArray(blkmem, &newCol->arcInPathReversed, newCol->memArcsInPath);
3806 BMSfreeBlockMemoryArray(blkmem, &newCol->pathArcs, newCol->memPathArcs);
3810 BMSfreeBlockMemoryArray(blkmem, &newCol->reducedMembers, newCol->memReducedMembers);
3811 BMSfreeBlockMemoryArray(blkmem, &newCol->leafMembers, newCol->memLeafMembers);
3812
3813 BMSfreeBlockMemory(blkmem, pcoladd);
3814}
3815
3816/** Adds members to the reduced member tree, by starting at the given member, jumping up to the parent, repeating this
3817 * procedure until the root has been added.
3818 */
3819static
3821 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
3822 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
3823 const spqr_member firstMember /**< The member to create a reduced member for */
3824 )
3825{
3826 assert(SPQRmemberIsValid(firstMember));
3827
3828 /* Originally a recursive algorithm, but for large matrices we need to unroll recursion to prevent stack overflows
3829 * from too many recursive calls */
3831 callstack[0].member = firstMember;
3832 int callDepth = 0;
3833
3834 while( callDepth >= 0 )
3835 {
3836 spqr_member member = callstack[callDepth].member;
3837 reduced_member_id reducedMember = newCol->memberInformation[member].reducedMember;
3838
3839 SCIP_Bool reducedValid = reducedMemberIsValid(reducedMember);
3840 if( !reducedValid )
3841 {
3842 //reduced member was not yet created; we create it
3843 reducedMember = newCol->numReducedMembers;
3844
3845 SPQRColReducedMember* reducedMemberData = &newCol->reducedMembers[reducedMember];
3846 ++newCol->numReducedMembers;
3847
3848 reducedMemberData->member = member;
3849 reducedMemberData->numChildren = 0;
3850
3851 reducedMemberData->type = REDUCEDMEMBER_TYPE_UNASSIGNED;
3852 reducedMemberData->numPropagatedChildren = 0;
3853 reducedMemberData->firstPathArc = INVALID_PATH_ARC;
3854 reducedMemberData->numPathArcs = 0;
3855 reducedMemberData->rigidPathStart = SPQR_INVALID_NODE;
3856 reducedMemberData->rigidPathEnd = SPQR_INVALID_NODE;
3857
3858 reducedMemberData->componentIndex = -1;
3859 //The children are set later
3860
3861 newCol->memberInformation[member].reducedMember = reducedMember;
3862 assert(memberIsRepresentative(dec, member));
3863 spqr_member parentMember = findMemberParent(dec, member);
3864
3865 if( SPQRmemberIsValid(parentMember) )
3866 {
3867 //recursive call to parent member
3868 ++callDepth;
3869 assert(callDepth < newCol->memCreateReducedMembersCallStack);
3870 callstack[callDepth].member = parentMember;
3871 continue;
3872 }
3873 else
3874 {
3875 //we found a new reduced decomposition component
3876
3877 reducedMemberData->parent = INVALID_REDUCED_MEMBER;
3878 reducedMemberData->depth = 0;
3879 reducedMemberData->rootMember = member;
3880 reducedMemberData->componentIndex = newCol->numReducedComponents;
3881
3882 assert(newCol->numReducedComponents < newCol->memReducedComponents);
3883 newCol->reducedComponents[newCol->numReducedComponents].root = reducedMember;
3887 ++newCol->numReducedComponents;
3888 }
3889 }
3890 if( reducedValid )
3891 {
3892 assert(reducedMember < newCol->numReducedMembers);
3893 //Reduced member was already created in earlier call
3894 //update the depth of the root if appropriate
3895 reduced_member_id* depthMinimizer = &newCol->memberInformation[newCol->reducedMembers[reducedMember].rootMember].rootDepthMinimizer;
3896 if( reducedMemberIsInvalid(*depthMinimizer) ||
3897 newCol->reducedMembers[reducedMember].depth < newCol->reducedMembers[*depthMinimizer].depth )
3898 {
3899 *depthMinimizer = reducedMember;
3900 }
3901 }
3902 while( TRUE ) /*lint !e716*/
3903 {
3904 --callDepth;
3905 if( callDepth < 0 )
3906 break;
3907 spqr_member parentMember = callstack[callDepth + 1].member;
3908 reduced_member_id parentReducedMember = newCol->memberInformation[parentMember].reducedMember;
3909 spqr_member currentMember = callstack[callDepth].member;
3910 reduced_member_id currentReducedMember = newCol->memberInformation[currentMember].reducedMember;
3911
3912 SPQRColReducedMember* parentReducedMemberData = &newCol->reducedMembers[parentReducedMember];
3913 SPQRColReducedMember* reducedMemberData = &newCol->reducedMembers[currentReducedMember];
3914
3915 reducedMemberData->parent = parentReducedMember;
3916 reducedMemberData->depth = parentReducedMemberData->depth + 1;
3917 reducedMemberData->rootMember = parentReducedMemberData->rootMember;
3918 //ensure that all newly created reduced members are pointing to the correct component
3919 assert(parentReducedMemberData->componentIndex >= 0);
3920 reducedMemberData->componentIndex = parentReducedMemberData->componentIndex;
3921
3922 newCol->reducedMembers[parentReducedMember].numChildren++;
3923 }
3924 }
3925
3926 reduced_member_id returnedMember = newCol->memberInformation[callstack[0].member].reducedMember;
3927 return returnedMember;
3928}
3929
3930/** Construct the reduced decomposition, which is the smallest subtree containing all members path arcs. */
3931static
3933 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
3934 SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */
3935 )
3936{
3937 assert(dec);
3938 assert(newCol);
3939#ifndef NDEBUG
3940 for( int i = 0; i < newCol->memMemberInformation; ++i )
3941 {
3943 }
3944#endif
3945
3946 newCol->numReducedComponents = 0;
3947 newCol->numReducedMembers = 0;
3948 if( newCol->numDecompositionRowArcs == 0 )
3949 {//Early return in case the reduced decomposition will be empty
3950 return SCIP_OKAY;
3951 }
3952 assert(newCol->numReducedMembers == 0);
3953 assert(newCol->numReducedComponents == 0);
3954
3955 int newSize = largestMemberID(dec);//Is this sufficient?
3956 if( newSize > newCol->memReducedMembers )
3957 {
3958 int newArraySize = MAX(2 * newCol->memReducedMembers, newSize);
3959 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->reducedMembers, newCol->memReducedMembers, newArraySize) );
3960 newCol->memReducedMembers = newArraySize;
3961 }
3962 if( newSize > newCol->memMemberInformation )
3963 {
3964 int updatedSize = MAX(2 * newCol->memMemberInformation, newSize);
3965 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->memberInformation, newCol->memMemberInformation, updatedSize) );
3966 for( int i = newCol->memMemberInformation; i < updatedSize; ++i )
3967 {
3970 }
3971 newCol->memMemberInformation = updatedSize;
3972 }
3973
3974 int numComponents = numConnectedComponents(dec);
3975 if( numComponents > newCol->memReducedComponents )
3976 {
3977 int updatedSize = MAX(2 * newCol->memReducedComponents, numComponents);
3978 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->reducedComponents, newCol->memReducedComponents, updatedSize) );
3979 newCol->memReducedComponents = updatedSize;
3980 }
3981
3982 int numMembers = getNumMembers(dec);
3983 if( newCol->memCreateReducedMembersCallStack < numMembers )
3984 {
3985 int updatedSize = MAX(2 * newCol->memCreateReducedMembersCallStack, numMembers);
3987 newCol->memCreateReducedMembersCallStack, updatedSize) );
3988 newCol->memCreateReducedMembersCallStack = updatedSize;
3989 }
3990
3991 //Create the reduced members (recursively)
3992 for( int i = 0; i < newCol->numDecompositionRowArcs; ++i )
3993 {
3994 assert(i < newCol->memDecompositionRowArcs);
3995 spqr_arc arc = newCol->decompositionRowArcs[i];
3996 spqr_member arcMember = findArcMember(dec, arc);
3997 reduced_member_id reducedMember = createReducedMembersToRoot(dec, newCol, arcMember);
3998 reduced_member_id* depthMinimizer = &newCol->memberInformation[newCol->reducedMembers[reducedMember].rootMember].rootDepthMinimizer;
3999 if( reducedMemberIsInvalid(*depthMinimizer) )
4000 {
4001 *depthMinimizer = reducedMember;
4002 }
4003 }
4004
4005 //Set the reduced roots according to the root depth minimizers
4006 for( int i = 0; i < newCol->numReducedComponents; ++i )
4007 {
4008 SPQRColReducedComponent* component = &newCol->reducedComponents[i];
4009 spqr_member rootMember = newCol->reducedMembers[component->root].member;
4010 reduced_member_id reducedMinimizer = newCol->memberInformation[rootMember].rootDepthMinimizer;
4011 component->rootDepth = newCol->reducedMembers[reducedMinimizer].depth;
4012 component->root = reducedMinimizer;
4013
4014 //This simplifies code further down which does not need to be component-aware; just pretend that the reduced member is the new root.
4015 newCol->reducedMembers[component->root].parent = INVALID_REDUCED_MEMBER;
4016 assert(memberIsRepresentative(dec, rootMember));
4017 }
4018
4019 //update the children array
4020 int numTotalChildren = 0;
4021 for( int i = 0; i < newCol->numReducedMembers; ++i )
4022 {
4023 SPQRColReducedMember* reducedMember = &newCol->reducedMembers[i];
4024 reduced_member_id minimizer = newCol->memberInformation[reducedMember->rootMember].rootDepthMinimizer;
4025 if( reducedMember->depth >= newCol->reducedMembers[minimizer].depth )
4026 {
4027 reducedMember->firstChild = numTotalChildren;
4028 numTotalChildren += reducedMember->numChildren;
4029 reducedMember->numChildren = 0;
4030 }
4031 }
4032
4033 if( newCol->memChildrenStorage < numTotalChildren )
4034 {
4035 int newMemSize = MAX(newCol->memChildrenStorage * 2, numTotalChildren);
4036 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->childrenStorage, newCol->memChildrenStorage, newMemSize) );
4037 newCol->memChildrenStorage = newMemSize;
4038 }
4039 newCol->numChildrenStorage = numTotalChildren;
4040
4041 //Fill up the children array`
4042 for( reduced_member_id reducedMember = 0; reducedMember < newCol->numReducedMembers; ++reducedMember )
4043 {
4044 SPQRColReducedMember* reducedMemberData = &newCol->reducedMembers[reducedMember];
4045 if( reducedMemberData->depth <=
4046 newCol->reducedMembers[newCol->memberInformation[reducedMemberData->rootMember].rootDepthMinimizer].depth )
4047 {
4048 continue;
4049 }
4050 spqr_member parentMember = findMemberParent(dec, reducedMemberData->member);
4051 reduced_member_id parentReducedMember = SPQRmemberIsValid(parentMember)
4052 ? newCol->memberInformation[parentMember].reducedMember
4054 if( reducedMemberIsValid(parentReducedMember))
4055 {
4056 SPQRColReducedMember* parentReducedMemberData = &newCol->reducedMembers[parentReducedMember];
4057 newCol->childrenStorage[parentReducedMemberData->firstChild +
4058 parentReducedMemberData->numChildren] = reducedMember;
4059 ++parentReducedMemberData->numChildren;
4060 }
4061 }
4062
4063 //Clean up the root depth minimizers.
4064 for( int i = 0; i < newCol->numReducedMembers; ++i )
4065 {
4066 SPQRColReducedMember* reducedMember = &newCol->reducedMembers[i];
4067 assert(reducedMember);
4068 spqr_member rootMember = reducedMember->rootMember;
4069 assert(rootMember >= 0);
4070 assert(rootMember < dec->memMembers);
4072 }
4073
4074 return SCIP_OKAY;
4075}
4076
4077/** Clean up the memberinformation array at the end of an iteration. */
4078static
4080 SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */
4081 )
4082{
4083 //This loop is at the end as memberInformation is also used to assign the cut arcs during propagation
4084 //Clean up the memberInformation array
4085 for( int i = 0; i < newCol->numReducedMembers; ++i )
4086 {
4088 }
4089#ifndef NDEBUG
4090 for( int i = 0; i < newCol->memMemberInformation; ++i )
4091 {
4093 }
4094#endif
4095}
4096
4097/** Marks the given arc as a path arc and adds it to the relevant data structures. */
4098static
4100 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
4101 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
4102 const spqr_arc arc, /**< The arc to mark as a path arc */
4103 const reduced_member_id reducedMember, /**< The reduced member containing the arc */
4104 SCIP_Bool reversed /**< Indicates if the new column entry has +1 or -1 (TRUE) for the arc */
4105 )
4106{
4107 assert(dec);
4108 assert(newCol);
4109
4110 path_arc_id path_arc = newCol->numPathArcs;
4111 PathArcListNode* listNode = &newCol->pathArcs[path_arc];
4112 listNode->arc = arc;
4113
4114 listNode->nextMember = newCol->reducedMembers[reducedMember].firstPathArc;
4115 newCol->reducedMembers[reducedMember].firstPathArc = path_arc;
4116 newCol->reducedMembers[reducedMember].numPathArcs += 1;
4117
4118 listNode->nextOverall = newCol->firstOverallPathArc;
4119 newCol->firstOverallPathArc = path_arc;
4120
4121 ++newCol->numPathArcs;
4122 assert(newCol->numPathArcs <= newCol->memPathArcs);
4123
4124 assert(arc < newCol->memArcsInPath);
4125 newCol->arcInPath[arc] = TRUE;
4126 newCol->arcInPathReversed[arc] = reversed;
4127 assert(memberIsRepresentative(dec, newCol->reducedMembers[reducedMember].member));
4128 if( getMemberType(dec, newCol->reducedMembers[reducedMember].member) == SPQR_MEMBERTYPE_RIGID )
4129 {
4130 listNode->arcHead = findEffectiveArcHead(dec, arc);
4131 listNode->arcTail = findEffectiveArcTail(dec, arc);
4132 if( reversed )
4133 {
4134 SCIPswapInts(&listNode->arcHead, &listNode->arcTail);
4135 }
4136 assert(SPQRnodeIsValid(listNode->arcHead) && SPQRnodeIsValid(listNode->arcTail));
4137 assert(listNode->arcHead < newCol->memNodePathDegree && listNode->arcTail < newCol->memNodePathDegree);
4138 ++newCol->nodeInPathDegree[listNode->arcHead];
4139 ++newCol->nodeOutPathDegree[listNode->arcTail];
4140 }
4141 else
4142 {
4143 listNode->arcHead = SPQR_INVALID_NODE;
4144 listNode->arcTail = SPQR_INVALID_NODE;
4145 }
4146 listNode->reversed = reversed;
4147}
4148
4149/** Mark all the row indices of the new column as path arcs */
4150static
4152 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
4153 SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */
4154 )
4155{
4156 int maxNumPathArcs = newCol->numDecompositionRowArcs + getNumMembers(dec);
4157 if( newCol->memPathArcs < maxNumPathArcs )
4158 {
4159 int newMaxNumArcs = 2 * maxNumPathArcs;//safety factor to prevent very frequent reallocations
4160 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->pathArcs, newCol->memPathArcs, newMaxNumArcs) );
4161 newCol->memPathArcs = newMaxNumArcs;
4162 }
4163 int maxPathArcIndex = largestArcID(dec);
4164 if( newCol->memArcsInPath < maxPathArcIndex )
4165 {
4166 int newSize = 2 * maxPathArcIndex;//safety factor to prevent very frequent reallocations
4167 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->arcInPath, newCol->memArcsInPath, newSize) );
4168 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->arcInPathReversed, newCol->memArcsInPath, newSize) );
4169
4170 for( int i = newCol->memArcsInPath; i < newSize; ++i )
4171 {
4172 newCol->arcInPath[i] = FALSE;
4173 newCol->arcInPathReversed[i] = FALSE;
4174 }
4175 newCol->memArcsInPath = newSize;
4176 }
4177 int maxNumNodes = largestNodeID(dec);
4178 if( newCol->memNodePathDegree < maxNumNodes )
4179 {
4180 int newSize = 2 * maxNumNodes;//safety factor to prevent very frequent reallocations
4181 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->nodeInPathDegree, newCol->memNodePathDegree, newSize) );
4183 for( int i = newCol->memNodePathDegree; i < newSize; ++i )
4184 {
4185 newCol->nodeInPathDegree[i] = 0;
4186 newCol->nodeOutPathDegree[i] = 0;
4187 }
4188 newCol->memNodePathDegree = newSize;
4189 }
4190 for( int i = 0; i < newCol->numDecompositionRowArcs; ++i )
4191 {
4192 spqr_arc arc = newCol->decompositionRowArcs[i];
4193 spqr_member member = findArcMember(dec, arc);
4194 reduced_member_id reducedMember = newCol->memberInformation[member].reducedMember;
4195 createPathArc(dec, newCol, arc, reducedMember, newCol->decompositionArcReversed[i]);
4196 }
4197
4198 return SCIP_OKAY;
4199}
4200
4201
4202/** Saves the information of the current row and partitions it based on whether or not the given columns are
4203 * already part of the decomposition.
4204 */
4205static
4207 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
4208 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
4209 spqr_col column, /**< The column that is checked */
4210 const spqr_row* nonzeroRows, /**< The column's row indices */
4211 const double* nonzeroValues, /**< The column's nonzero values */
4212 int numNonzeros /**< The number of nonzeros in the column */
4213 )
4214{
4215 newCol->newColIndex = column;
4216
4217 newCol->numDecompositionRowArcs = 0;
4218 newCol->numNewRowArcs = 0;
4219
4220 for( int i = 0; i < numNonzeros; ++i )
4221 {
4222 spqr_arc rowArc = getDecompositionRowArc(dec, nonzeroRows[i]);
4223 SCIP_Bool reversed = nonzeroValues[i] < 0.0;
4224 if( SPQRarcIsValid(rowArc) )
4225 {//If the arc is the current decomposition: save it in the array
4226 if( newCol->numDecompositionRowArcs == newCol->memDecompositionRowArcs )
4227 {
4228 int newNumArcs = newCol->memDecompositionRowArcs == 0 ? 8 : 2 * newCol->memDecompositionRowArcs;
4230 newCol->memDecompositionRowArcs, newNumArcs) );
4232 newCol->memDecompositionRowArcs, newNumArcs) );
4233 newCol->memDecompositionRowArcs = newNumArcs;
4234 }
4235 newCol->decompositionRowArcs[newCol->numDecompositionRowArcs] = rowArc;
4236 newCol->decompositionArcReversed[newCol->numDecompositionRowArcs] = reversed;
4237 ++newCol->numDecompositionRowArcs;
4238 }
4239 else
4240 {
4241 //Not in the decomposition: add it to the set of arcs which are newly added with this row.
4242 if( newCol->numNewRowArcs == newCol->memNewRowArcs )
4243 {
4244 int newNumArcs = newCol->memNewRowArcs == 0 ? 8 : 2 * newCol->memNewRowArcs;
4246 newCol->memNewRowArcs, newNumArcs) );
4248 newCol->memNewRowArcs, newNumArcs) );
4249 newCol->memNewRowArcs = newNumArcs;
4250 }
4251 newCol->newRowArcs[newCol->numNewRowArcs] = nonzeroRows[i];
4252 newCol->newRowArcReversed[newCol->numNewRowArcs] = reversed;
4253 newCol->numNewRowArcs++;
4254 }
4255 }
4256
4257 return SCIP_OKAY;
4258}
4259
4260/** Compute and store the leaf members of the reduced SPQR forest */
4261static
4263 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
4264 SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */
4265 )
4266{
4267 if( newCol->numReducedMembers > newCol->memLeafMembers )
4268 {
4269 int newSize = MAX(newCol->numReducedMembers, 2 * newCol->memLeafMembers);
4270 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newCol->leafMembers, newCol->memLeafMembers, newSize) );
4271 newCol->memLeafMembers = newSize;
4272 }
4273 newCol->numLeafMembers = 0;
4274
4275 for( reduced_member_id reducedMember = 0; reducedMember < newCol->numReducedMembers; ++reducedMember )
4276 {
4277 if( newCol->reducedMembers[reducedMember].numChildren == 0 )
4278 {
4279 newCol->leafMembers[newCol->numLeafMembers] = reducedMember;
4280 ++newCol->numLeafMembers;
4281 }
4282 }
4283
4284 return SCIP_OKAY;
4285}
4286
4287/** Checks if the path arcs in the given rigid member form a path */
4288static
4290 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
4291 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
4292 SPQRColReducedMember* redMem /**< The rigid reduced member to determine the path in */
4293 )
4294{
4295 assert(dec);
4296 assert(newCol);
4297 assert(redMem);
4298
4299 /* 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
4300 * and only if the head of the tail of the path are the only nodes with 1 adjacent edge, and all other nodes
4301 * have 2 adjacent edges*/
4302 SCIP_Bool isValidPath = TRUE;
4305 for( path_arc_id pathArc = redMem->firstPathArc; pathArcIsValid(pathArc); pathArc = newCol->pathArcs[pathArc].nextMember )
4306 {
4307 spqr_node head = newCol->pathArcs[pathArc].arcHead;
4308 spqr_node tail = newCol->pathArcs[pathArc].arcTail;
4309 assert(nodeIsRepresentative(dec, head) && nodeIsRepresentative(dec, tail));
4310
4311 if( newCol->nodeInPathDegree[head] > 1 || newCol->nodeOutPathDegree[tail] > 1 )
4312 {
4313 //not network -> stop
4314 isValidPath = FALSE;
4315 break;
4316 }
4317 if( newCol->nodeOutPathDegree[head] == 0 )
4318 {
4319 //found end node
4320 //If this is the second, stop
4321 if( SPQRnodeIsValid(redMem->rigidPathEnd) )
4322 {
4323 isValidPath = FALSE;
4324 break;
4325 }
4326 redMem->rigidPathEnd = head;
4327 }
4328 if( newCol->nodeInPathDegree[tail] == 0 )
4329 {
4330 //Found start node.
4331 //If this is the second, stop.
4332 if( SPQRnodeIsValid(redMem->rigidPathStart) )
4333 {
4334 isValidPath = FALSE;
4335 break;
4336 }
4337 redMem->rigidPathStart = tail;
4338 }
4339 }
4340 //assert that both a start and end node have been found
4341 assert(!isValidPath || ( SPQRnodeIsValid(redMem->rigidPathStart) && SPQRnodeIsValid(redMem->rigidPathEnd)));
4342 if( !isValidPath )
4343 {
4347 newCol->remainsNetwork = FALSE;
4348 }
4349}
4350
4351/** Determines the member's type for the case where the reduced tree consists of a single rigid member. */
4352static
4354 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
4355 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
4356 reduced_member_id reducedMember /**< The reduced member to check */
4357 )
4358{
4359 assert(dec);
4360 assert(newCol);
4361 assert(reducedMemberIsValid(reducedMember));
4362
4363 SPQRColReducedMember* redMem = &newCol->reducedMembers[reducedMember];
4364 assert(pathArcIsValid(redMem->firstPathArc));
4365 determineRigidPath(dec, newCol, redMem);
4366 if( redMem->type != REDUCEDMEMBER_TYPE_NOT_NETWORK )
4367 {
4369 }
4370}
4371
4372/** Determines the member's type for the case where the reduced tree consists of a single member. */
4373static
4375 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
4376 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
4377 reduced_member_id reducedMember /**< The reduced member to check */
4378 )
4379{
4380 assert(dec);
4381 assert(newCol);
4382
4383 int numNonPropagatedAdjacent =
4384 newCol->reducedMembers[reducedMember].numChildren - newCol->reducedMembers[reducedMember].numPropagatedChildren;
4385 if( reducedMemberIsValid(newCol->reducedMembers[reducedMember].parent) &&
4386 newCol->reducedMembers[newCol->reducedMembers[reducedMember].parent].type != REDUCEDMEMBER_TYPE_CYCLE )
4387 {
4388 ++numNonPropagatedAdjacent;
4389 }
4390
4391 if( numNonPropagatedAdjacent > 2 )
4392 {
4393 newCol->reducedMembers[reducedMember].type = REDUCEDMEMBER_TYPE_NOT_NETWORK;
4394 newCol->remainsNetwork = FALSE;
4395 return;
4396 }
4397
4398 spqr_member member = findMember(dec, newCol->reducedMembers[reducedMember].member);
4399 SPQRMemberType type = getMemberType(dec, member);
4400 switch( type )
4401 {
4403 {
4404 determineSingleRigidType(dec, newCol, reducedMember);
4405 break;
4406 }
4408 {
4409 SPQRColReducedMember* redMem = &newCol->reducedMembers[reducedMember];
4410 assert(pathArcIsValid(redMem->firstPathArc));
4411 SCIP_Bool pathForwards = newCol->pathArcs[redMem->firstPathArc].reversed ==
4412 arcIsReversedNonRigid(dec, newCol->pathArcs[redMem->firstPathArc].arc);
4413 redMem->pathBackwards = !pathForwards;
4415 break;
4416 }
4419 {
4420 SPQRColReducedMember* redMem = &newCol->reducedMembers[reducedMember];
4421 int countedPathArcs = 0;
4422 SCIP_Bool good = TRUE;
4423 SCIP_Bool passesForwards = TRUE;
4424 for( path_arc_id pathArc = redMem->firstPathArc; pathArcIsValid(pathArc);
4425 pathArc = newCol->pathArcs[pathArc].nextMember )
4426 {
4427 if( countedPathArcs == 0 )
4428 {
4429 passesForwards =
4430 newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc);
4431 }
4432 else if(
4433 (newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc)) !=
4434 passesForwards )
4435 {
4436 good = FALSE;
4437 break;
4438 }
4439 ++countedPathArcs;
4440 }
4441 if( !good )
4442 {
4444 newCol->remainsNetwork = FALSE;
4445 break;
4446 }
4447
4448 redMem->pathBackwards = !passesForwards;
4449 if( countedPathArcs == getNumMemberArcs(dec, findMember(dec, redMem->member)) - 1 )
4450 {
4451 //Type -> Cycle;
4452 //Propagate arc
4454 }
4455 else
4456 {
4457 //Type -> single_end
4459 }
4460 break;
4461 }
4463 {
4464 SCIPABORT();
4465 newCol->reducedMembers[reducedMember].type = REDUCEDMEMBER_TYPE_NOT_NETWORK;
4466 break;
4467 }
4468 }
4469}
4470
4471/** Determines the path type of a series member. */
4472static
4474 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
4475 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
4476 reduced_member_id reducedMember, /**< The reduced member to check */
4477 spqr_member member, /**< The reduced member's member id */
4478 MemberPathType previousType, /**< Type of the previous reduced member in the path */
4479 spqr_arc source, /**< The marker connecting to the previous reduced member in the path */
4480 spqr_arc target /**< The marker connecting to the next reduced member in the path */
4481 )
4482{
4483 assert(dec);
4484 assert(newCol);
4485 assert(reducedMemberIsValid(reducedMember));
4486 assert(SPQRmemberIsValid(member) && memberIsRepresentative(dec, member));
4487 assert(getMemberType(dec, member) == SPQR_MEMBERTYPE_SERIES);
4488
4489 SPQRColReducedMember* redMem = &newCol->reducedMembers[reducedMember];
4490 int countedPathArcs = 0;
4491
4492 SCIP_Bool good = TRUE;
4493 SCIP_Bool passesForwards = TRUE;
4494 for( path_arc_id pathArc = redMem->firstPathArc; pathArcIsValid(pathArc);
4495 pathArc = newCol->pathArcs[pathArc].nextMember )
4496 {
4497 if( countedPathArcs == 0 )
4498 {
4499 passesForwards =
4500 newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc);
4501 }
4502 else if(( newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc)) !=
4503 passesForwards )
4504 {
4505 good = FALSE;
4506 break;
4507 }
4508 ++countedPathArcs;
4509 }
4510 //If the internal directions of the arcs do not agree, we have no way to realize
4511 if( !good )
4512 {
4514 newCol->remainsNetwork = FALSE;
4515 return;
4516 }
4517 //If we are in the first skeleton processed, ignore the previous member type
4518 if( !SPQRarcIsValid(source))
4519 {
4520 assert(countedPathArcs > 0);
4521 assert(SPQRarcIsValid(target));
4522 SCIP_Bool firstReversed = arcIsReversedNonRigid(dec, newCol->pathArcs[redMem->firstPathArc].arc);
4523 SCIP_Bool targetReversed = arcIsReversedNonRigid(dec, target);
4524 SCIP_Bool reversePath = newCol->pathArcs[redMem->firstPathArc].reversed;
4525
4526 if(( firstReversed == targetReversed ) == reversePath )
4527 {
4528 redMem->pathType = INTO_HEAD;
4529 }
4530 else
4531 {
4532 redMem->pathType = OUT_HEAD;
4533 }
4534 redMem->pathBackwards = !passesForwards;
4535 return;
4536 }
4537 SCIP_Bool sourceReversed = arcIsReversedNonRigid(dec, source);
4538 if( countedPathArcs > 0 )
4539 {
4540 SCIP_Bool isIntoHeadOrOutTail = isInto(previousType) == isHead(previousType);
4541 SCIP_Bool isGood = isIntoHeadOrOutTail == ( sourceReversed == passesForwards );
4542 if( !isGood )
4543 {
4545 newCol->remainsNetwork = FALSE;
4546 return;
4547 }
4548 }
4549 redMem->pathBackwards = !passesForwards;
4550 if( SPQRarcIsValid(target) )
4551 {
4552 SCIP_Bool targetReversed = arcIsReversedNonRigid(dec, target);
4553 SCIP_Bool inSameDirection = sourceReversed == targetReversed;
4554
4555 MemberPathType currentType;
4556 switch( previousType )
4557 {
4558 case INTO_HEAD:
4559 {
4560 currentType = inSameDirection ? INTO_TAIL : INTO_HEAD;
4561 break;
4562 }
4563 case INTO_TAIL:
4564 {
4565 currentType = inSameDirection ? INTO_HEAD : INTO_TAIL;
4566 break;
4567 }
4568 case OUT_HEAD:
4569 {
4570 currentType = inSameDirection ? OUT_TAIL : OUT_HEAD;
4571 break;
4572 }
4573 case OUT_TAIL:
4574 default:
4575 {
4576 assert(previousType == OUT_TAIL);
4577 currentType = inSameDirection ? OUT_HEAD : OUT_TAIL;
4578 break;
4579 }
4580 }
4581 redMem->pathType = currentType;
4582 return;
4583 }
4584 //If we are in the last skeleton, we only have a source, so nothing further to do
4585 assert(countedPathArcs > 0);
4586 //Strictly speaking below are no-ops within the algorithm, but help with debugging
4587 if( isInto(previousType))
4588 {
4589 redMem->pathType = INTO_HEAD;
4590 }
4591 else
4592 {
4593 redMem->pathType = OUT_HEAD;
4594 }
4595}
4596
4597/** Determines the path type of a parallel member. */
4598static
4600 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
4601 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
4602 reduced_member_id reducedMember, /**< The reduced member to check */
4603 spqr_member member, /**< The reduced member's member id */
4604 MemberPathType previousType, /**< Type of the previous reduced member in the path */
4605 spqr_arc source, /**< The marker connecting to the previous reduced member in the path */
4606 spqr_arc target /**< The marker connecting to the next reduced member in the path */
4607 )
4608{
4609 assert(dec);
4610 assert(newCol);
4611 assert(reducedMemberIsValid(reducedMember));
4612 assert(SPQRmemberIsValid(member) && memberIsRepresentative(dec, member));
4613 assert(getMemberType(dec, member) == SPQR_MEMBERTYPE_PARALLEL);
4614
4615 //Parallel members must always be of degree two; if they are a leaf, they must contain an edge, which could have been propagated
4616 assert(SPQRarcIsValid(source) && SPQRarcIsValid(target));
4617 SCIP_Bool sourceReversed = arcIsReversedNonRigid(dec, source);
4618 SCIP_Bool targetReversed = arcIsReversedNonRigid(dec, target);
4619 SCIP_Bool inSameDirection = sourceReversed == targetReversed;
4620
4621 SPQRColReducedMember* redMem = &newCol->reducedMembers[reducedMember];
4622
4623 path_arc_id pathArc = redMem->firstPathArc;
4624
4625 SCIP_Bool arcPresent = FALSE;
4626 if( pathArcIsValid(pathArc))
4627 {
4628 SCIP_Bool pathArcReversed =
4629 newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc);
4630 SCIP_Bool intoHeadOrOutTail = isInto(previousType) == isHead(previousType);
4631 SCIP_Bool good = intoHeadOrOutTail == ( pathArcReversed != sourceReversed );
4632 if( !good )
4633 {
4635 newCol->remainsNetwork = FALSE;
4636 return;
4637 }
4638 arcPresent = TRUE;
4639 }
4640
4641 SCIP_Bool swapHeadTail = arcPresent != inSameDirection;
4642 switch( previousType )
4643 {
4644 case INTO_HEAD:
4645 {
4646 redMem->pathType = swapHeadTail ? INTO_HEAD : INTO_TAIL;
4647 break;
4648 }
4649 case INTO_TAIL:
4650 {
4651 redMem->pathType = swapHeadTail ? INTO_TAIL : INTO_HEAD;
4652 break;
4653 }
4654 case OUT_HEAD:
4655 {
4656 redMem->pathType = swapHeadTail ? OUT_HEAD : OUT_TAIL;
4657 break;
4658 }
4659 case OUT_TAIL:
4660 {
4661 redMem->pathType = swapHeadTail ? OUT_TAIL : OUT_HEAD;
4662 break;
4663 }
4664 }
4665}
4666
4667/** Determines the path type of a rigid member. */
4668static
4670 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
4671 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
4672 reduced_member_id reducedMember, /**< The reduced member to check */
4673 MemberPathType previousType, /**< Type of the previous reduced member in the path */
4674 spqr_arc source, /**< The marker connecting to the previous reduced member in the path */
4675 spqr_arc target /**< The marker connecting to the next reduced member in the path */
4676 )
4677{
4678 SPQRColReducedMember* redMem = &newCol->reducedMembers[reducedMember];
4679 if( pathArcIsInvalid(redMem->firstPathArc))
4680 {
4681 assert(SPQRarcIsValid(source));
4682 assert(SPQRarcIsValid(target));
4683 //In this case, we need to check if the source and target are adjacent in any node
4684
4685 spqr_node sourceTail = findEffectiveArcTail(dec, source);
4686 spqr_node sourceHead = findEffectiveArcHead(dec, source);
4687 spqr_node targetTail = findEffectiveArcTail(dec, target);
4688 spqr_node targetHead = findEffectiveArcHead(dec, target);
4689 SCIP_Bool sourceHeadIsTargetHead = sourceHead == targetHead;
4690 SCIP_Bool sourceTailIsTargetHead = sourceTail == targetHead;
4691 SCIP_Bool sourceHeadIsTargetTail = sourceHead == targetTail;
4692 SCIP_Bool sourceTailIsTargetTail = sourceTail == targetTail;
4693
4694 if( !(sourceHeadIsTargetHead || sourceTailIsTargetHead || sourceHeadIsTargetTail || sourceTailIsTargetTail) )
4695 {
4697 newCol->remainsNetwork = FALSE;
4698 return;
4699 }
4700 assert(
4701 ( sourceHeadIsTargetHead ? 1 : 0 ) + ( sourceHeadIsTargetTail ? 1 : 0 ) + ( sourceTailIsTargetHead ? 1 : 0 ) +
4702 ( sourceTailIsTargetTail ? 1 : 0 ) == 1);
4703 //assert simplicity; they can be adjacent in only exactly one node
4704 SCIP_Bool isSourceHead = sourceHeadIsTargetHead || sourceHeadIsTargetTail;
4705 SCIP_Bool isTargetTail = sourceHeadIsTargetTail || sourceTailIsTargetTail;
4706 if( isHead(previousType) == isSourceHead )
4707 {
4708 //no need to reflect
4709 redMem->reverseArcs = FALSE;
4710 if( isInto(previousType))
4711 {
4712 if( isTargetTail )
4713 {
4714 redMem->pathType = INTO_TAIL;
4715 }
4716 else
4717 {
4718 redMem->pathType = INTO_HEAD;
4719 }
4720 }
4721 else
4722 {
4723 if( isTargetTail )
4724 {
4725 redMem->pathType = OUT_TAIL;
4726 }
4727 else
4728 {
4729 redMem->pathType = OUT_HEAD;
4730 }
4731 }
4732 }
4733 else
4734 {
4735 redMem->reverseArcs = TRUE;
4736 //because of the reversal, all the heads/tails are switched below
4737 if( isInto(previousType))
4738 {
4739 if( isTargetTail )
4740 {
4741 redMem->pathType = INTO_HEAD;
4742 }
4743 else
4744 {
4745 redMem->pathType = INTO_TAIL;
4746 }
4747 }
4748 else
4749 {
4750 if( isTargetTail )
4751 {
4752 redMem->pathType = OUT_HEAD;
4753 }
4754 else
4755 {
4756 redMem->pathType = OUT_TAIL;
4757 }
4758 }
4759 }
4760 assert(isInto(redMem->pathType) == isInto(previousType));
4761 return;
4762 }
4763 determineRigidPath(dec, newCol, redMem);
4764 if( redMem->type == REDUCEDMEMBER_TYPE_NOT_NETWORK )
4765 {
4766 return;
4767 }
4768 if( SPQRarcIsInvalid(source) )
4769 {
4770 assert(SPQRarcIsValid(target));
4771 spqr_node targetTail = findEffectiveArcTail(dec, target);
4772 spqr_node targetHead = findEffectiveArcHead(dec, target);
4773 redMem->reverseArcs = FALSE;
4774 if( redMem->rigidPathEnd == targetHead )
4775 {
4776 redMem->pathType = INTO_HEAD;
4777 }
4778 else if( redMem->rigidPathEnd == targetTail )
4779 {
4780 redMem->pathType = INTO_TAIL;
4781 }
4782 else if( redMem->rigidPathStart == targetHead )
4783 {
4784 redMem->pathType = OUT_HEAD;
4785 }
4786 else if( redMem->rigidPathStart == targetTail )
4787 {
4788 redMem->pathType = OUT_TAIL;
4789 }
4790 else
4791 {
4793 newCol->remainsNetwork = FALSE;
4794 }
4795 return;
4796 }
4797 assert(SPQRarcIsValid(source));
4798 spqr_node sourceTail = findEffectiveArcTail(dec, source);
4799 spqr_node sourceHead = findEffectiveArcHead(dec, source);
4800
4801 SCIP_Bool startsAtHead = sourceHead == redMem->rigidPathStart;
4802 SCIP_Bool endsAtTail = sourceTail == redMem->rigidPathEnd;
4803 SCIP_Bool startsAtTail = sourceTail == redMem->rigidPathStart;
4804 SCIP_Bool endsAtHead = sourceHead == redMem->rigidPathEnd;
4805
4806 SCIP_Bool isIntoHeadOrOutTail = isInto(previousType) == isHead(previousType);
4807 if( isIntoHeadOrOutTail )
4808 {//into head or outTail
4809 //Check if path starts at head or ends at tail
4810 if( !startsAtHead && !endsAtTail )
4811 {
4813 newCol->remainsNetwork = FALSE;
4814 return;
4815 }
4816 assert(startsAtHead || endsAtTail);//both can hold; they can form cycle but other components can not be reduced
4817 // redMem->reverseArcs = isInto(previousType) != startsAtHead; //Reverse only if there is no path starting at head
4818 }
4819 else
4820 {//Into tail or outHead
4821 //Check if path starts at tail or ends at head
4822 if( !startsAtTail && !endsAtHead )
4823 {
4825 newCol->remainsNetwork = FALSE;
4826 return;
4827 }
4828 assert(startsAtTail || endsAtHead);// both can hold; they can form cycle but other components can not be reduced
4829 // redMem->reverseArcs = isInto(previousType) != startsAtTail; //Reverse only if there is no path starting at tail
4830 }
4831
4832 if( SPQRarcIsValid(target) )
4833 {
4834 spqr_node targetTail = findEffectiveArcTail(dec, target);
4835 spqr_node targetHead = findEffectiveArcHead(dec, target);
4836
4837 //Check if they are not parallel; (below logic relies on this fact)
4838 assert(!(( targetHead == sourceHead && targetTail == sourceTail ) ||
4839 ( targetHead == sourceTail && targetTail == sourceHead )));
4840
4841 SCIP_Bool startsAtTargetHead = redMem->rigidPathStart == targetHead;
4842 SCIP_Bool startsAtTargetTail = redMem->rigidPathStart == targetTail;
4843 SCIP_Bool endsAtTargetHead = redMem->rigidPathEnd == targetHead;
4844 SCIP_Bool endsAtTargetTail = redMem->rigidPathEnd == targetTail;
4845
4846 if( !(startsAtTargetHead || startsAtTargetTail || endsAtTargetHead || endsAtTargetTail) )
4847 {
4849 newCol->remainsNetwork = FALSE;
4850 return;
4851 }
4852 SCIP_Bool outReverse = FALSE;
4853 SCIP_Bool outHead = FALSE;
4854
4855 if( isInto(previousType) == isHead(previousType) )
4856 {
4857 if( startsAtHead && endsAtTail )
4858 {
4859 outReverse = ( startsAtTargetHead || startsAtTargetTail ) == isInto(previousType);
4860 }
4861 else if( startsAtHead )
4862 {
4863 outReverse = !isInto(previousType);
4864 }
4865 else
4866 {
4867 assert(endsAtTail);
4868 outReverse = isInto(previousType);
4869 }
4870 }
4871 else
4872 {
4873 if( startsAtTail && endsAtHead )
4874 {
4875 outReverse = ( startsAtTargetHead || startsAtTargetTail ) == isInto(previousType);
4876 }
4877 else if( startsAtTail )
4878 {
4879 outReverse = !isInto(previousType);
4880 }
4881 else
4882 {
4883 assert(endsAtHead);
4884 outReverse = isInto(previousType);
4885 }
4886 }
4887
4888 //TODO: this if-else tree to compute two booleans can probably be simplified significantly
4889 SCIP_Bool isBad = FALSE;
4890 if( isInto(previousType) == isHead(previousType) )
4891 {
4892 if( startsAtHead && endsAtTail )
4893 {
4894 outHead = ( startsAtTargetTail || endsAtTargetHead ) == isInto(previousType);
4895 }
4896 else if( startsAtHead )
4897 {
4898 if( endsAtTargetHead )
4899 {
4900 outHead = isInto(previousType);
4901 }
4902 else if( endsAtTargetTail )
4903 {
4904 outHead = !isInto(previousType);
4905 }
4906 else
4907 {
4908 isBad = TRUE;
4909 }
4910 }
4911 else
4912 {
4913 assert(endsAtTail);
4914 if( startsAtTargetTail )
4915 {
4916 outHead = isInto(previousType);
4917 }
4918 else if( startsAtTargetHead )
4919 {
4920 outHead = !isInto(previousType);
4921 }
4922 else
4923 {
4924 isBad = TRUE;
4925 }
4926 }
4927 }
4928 else
4929 {
4930 if( startsAtTail && endsAtHead )
4931 {
4932 outHead = ( startsAtTargetTail || endsAtTargetHead ) == isInto(previousType);
4933 }
4934 else if( startsAtTail )
4935 {
4936 if( endsAtTargetHead )
4937 {
4938 outHead = isInto(previousType);
4939 }
4940 else if( endsAtTargetTail )
4941 {
4942 outHead = !isInto(previousType);
4943 }
4944 else
4945 {
4946 isBad = TRUE;
4947 }
4948 }
4949 else
4950 {
4951 assert(endsAtHead);
4952 if( startsAtTargetTail )
4953 {
4954 outHead = isInto(previousType);
4955 }
4956 else if( startsAtTargetHead )
4957 {
4958 outHead = !isInto(previousType);
4959 }
4960 else
4961 {
4962 isBad = TRUE;
4963 }
4964 }
4965 }
4966 if( isBad )
4967 {
4969 newCol->remainsNetwork = FALSE;
4970 return;
4971 }
4972
4973 redMem->reverseArcs = outReverse;
4974 if( isInto(previousType) )
4975 {
4976 redMem->pathType = outHead ? INTO_HEAD : INTO_TAIL;
4977 }
4978 else
4979 {
4980 redMem->pathType = outHead ? OUT_HEAD : OUT_TAIL;
4981 }
4982 return;
4983 }
4984
4985 //TODO: is this simplifyable?
4986 if( isInto(previousType) == isHead(previousType) )
4987 {
4988 redMem->reverseArcs = startsAtHead != isInto(previousType);
4989 }
4990 else
4991 {
4992 redMem->reverseArcs = startsAtTail != isInto(previousType);
4993 }
4994 //last member of the path. Since we already checked the source,
4995 //Below is technically no op, but helps with debugging
4996 if( isInto(previousType) )
4997 {
4998 redMem->pathType = INTO_HEAD;
4999 }
5000 else
5001 {
5002 redMem->pathType = OUT_HEAD;
5003 }
5004}
5005
5006/** Determines the path type of a single member. */
5007static
5009 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
5010 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
5011 reduced_member_id reducedMember, /**< The reduced member to check */
5012 spqr_member member, /**< The reduced member's member id */
5013 MemberPathType previousType, /**< Type of the previous reduced member in the path */
5014 spqr_arc source, /**< The marker connecting to the previous reduced member in the path */
5015 spqr_arc target /**< The marker connecting to the next reduced member in the path */
5016 )
5017{
5018 newCol->reducedMembers[reducedMember].pathSourceArc = source;
5019 newCol->reducedMembers[reducedMember].pathTargetArc = target;
5020 //Check if the marked edges with the given signs
5021 //form a (reverse) directed path from one of the source's end nodes to one of the target's end nodes
5022 switch( getMemberType(dec, member) )
5023 {
5025 {
5026 determinePathRigidType(dec, newCol, reducedMember, previousType, source, target);
5027 break;
5028 }
5030 {
5031 determinePathParallelType(dec, newCol, reducedMember, member, previousType, source, target);
5032 break;
5033 }
5035 {
5036 determinePathSeriesType(dec, newCol, reducedMember, member, previousType, source, target);
5037 break;
5038 }
5041 {
5042 //In release
5043 newCol->remainsNetwork = FALSE;
5044 SCIPABORT();
5045 break;
5046 }
5047 }
5048}
5049
5050/** Determines the path types of all reduced members.
5051 *
5052 * The reduced members themselves also form a path in the reduced decomposition.
5053 */
5054static
5056 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
5057 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
5058 SPQRColReducedComponent* component /**< The component to determine the path types for */
5059 )
5060{
5061 assert(dec);
5062 assert(newCol);
5063 assert(component);
5064 assert(component->numPathEndMembers == 2);
5065
5066 assert(component->pathEndMembers[0] != component->root);
5067
5068 //We check the path by going from end to end. We start at the leaf and process every component,
5069 //walking down until we hit the root.
5070 //Then, we walk up from the root and process each component in the same manner.
5071 reduced_member_id reducedStart = component->pathEndMembers[0];
5072 spqr_arc toPrevious = SPQR_INVALID_ARC;
5073 spqr_arc toNext = SPQR_INVALID_ARC;
5074 MemberPathType previousType = INTO_HEAD;//Arbitrary, is ignored in the first call
5075
5076 spqr_member member = newCol->reducedMembers[reducedStart].member;
5077 reduced_member_id reducedMember = reducedStart;
5078 reduced_member_id previousReducedMember = reducedStart;
5079 while( reducedMember != component->root )
5080 {
5081 toNext = markerToParent(dec, member);
5082 determinePathMemberType(dec, newCol, reducedMember, member, previousType, toPrevious, toNext);
5083 if( !newCol->remainsNetwork )
5084 {
5085 return;
5086 }
5087 previousType = newCol->reducedMembers[reducedMember].pathType;
5088 toPrevious = markerOfParent(dec, member);
5089 member = findMemberParent(dec, newCol->reducedMembers[reducedMember].member);
5090 previousReducedMember = reducedMember;
5091 reducedMember = newCol->memberInformation[member].reducedMember;
5092 newCol->reducedMembers[previousReducedMember].nextPathMember = reducedMember;
5093 newCol->reducedMembers[previousReducedMember].nextPathMemberIsParent = TRUE;
5094 }
5095
5096 while( reducedMember != component->pathEndMembers[1] )
5097 {
5098 //Search the (other) child node
5100 //a bit ugly linear search, but not a problem for time complexity
5101 for( children_idx i = newCol->reducedMembers[reducedMember].firstChild;
5102 i <
5103 newCol->reducedMembers[reducedMember].firstChild + newCol->reducedMembers[reducedMember].numChildren; ++i )
5104 {
5105 reduced_member_id childReduced = newCol->childrenStorage[i];
5106 if( newCol->reducedMembers[childReduced].type != REDUCEDMEMBER_TYPE_CYCLE &&
5107 childReduced != previousReducedMember )
5108 {
5109 child = childReduced;
5110 break;
5111 }
5112 }
5113 assert(reducedMemberIsValid(child));
5114
5115 spqr_member childMember = newCol->reducedMembers[child].member;
5116 toNext = markerOfParent(dec, childMember);
5117
5118 determinePathMemberType(dec, newCol, reducedMember, member, previousType, toPrevious, toNext);
5119 if( !newCol->remainsNetwork )
5120 {
5121 return;
5122 }
5123 previousType = newCol->reducedMembers[reducedMember].pathType;
5124 toPrevious = markerToParent(dec, childMember);
5125 member = childMember;
5126 previousReducedMember = reducedMember;
5127 reducedMember = child;
5128 newCol->reducedMembers[previousReducedMember].nextPathMember = reducedMember;
5129 newCol->reducedMembers[previousReducedMember].nextPathMemberIsParent = FALSE;
5130 }
5131 //The last iteration is not performed by the loops above.
5132 //We explicitly set the target arc to invalid in order to indicate that this is the last iteration.
5133 toNext = SPQR_INVALID_ARC;
5134 determinePathMemberType(dec, newCol, reducedMember, member, previousType, toPrevious, toNext);
5135 newCol->reducedMembers[reducedMember].nextPathMember = INVALID_REDUCED_MEMBER;
5136 //since we return anyways, no need to check newCol->remainsNetwork explicitly
5137}
5138
5139/** Check if a rigid leaf closes a cycle with its child.
5140 *
5141 * If so, we can propagate this cycle to a virtual arc of the child node member and shrink the decomposition.
5142 */
5143static
5145 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
5146 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
5147 reduced_member_id leaf, /**< The leaf node of the reduced SPQR tree */
5148 spqr_arc toParent, /**< The virtual arc to the leaf node's neighbour */
5149 reduced_member_id parent, /**< The neighbouring member of the leaf node. */
5150 spqr_arc toChild /**< The virtual arc from the neighbouring member pointing to the leaf. */
5151 )
5152{
5153 SPQRColReducedMember* leafMember = &newCol->reducedMembers[leaf];
5154 determineRigidPath(dec, newCol, leafMember);
5155 if( leafMember->type == REDUCEDMEMBER_TYPE_NOT_NETWORK )
5156 {
5157 return;
5158 }
5159 spqr_node targetHead = findEffectiveArcHead(dec, toParent);
5160 spqr_node targetTail = findEffectiveArcTail(dec, toParent);
5161 SCIP_Bool matches = leafMember->rigidPathStart == targetTail && leafMember->rigidPathEnd == targetHead;
5162 SCIP_Bool opposite = leafMember->rigidPathStart == targetHead && leafMember->rigidPathEnd == targetTail;
5163 if( matches || opposite )
5164 {
5165 leafMember->type = REDUCEDMEMBER_TYPE_CYCLE;
5166 createPathArc(dec, newCol, toChild, parent, opposite);
5167 return;
5168 }
5169 leafMember->type = REDUCEDMEMBER_TYPE_MERGED;
5170}
5171
5172/** Check if a leaf node closes a cycle with its child.
5173 *
5174 * If so, we can propagate this cycle to a virtual arc of the child node member and shrink the decomposition.
5175 */
5176static
5178 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
5179 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
5180 reduced_member_id leaf, /**< The leaf node of the reduced SPQR tree */
5181 spqr_arc toParent, /**< The virtual arc to the leaf node's neighbour */
5182 reduced_member_id parent, /**< The neighbouring member of the leaf node. */
5183 spqr_arc toChild /**< The virtual arc from the neighbouring member pointing to the leaf. */
5184 )
5185{
5186 assert(dec);
5187 assert(newCol);
5188 assert(SPQRarcIsValid(toParent));
5189 assert(SPQRarcIsValid(toChild));
5190 assert(!arcIsTree(dec, toParent));
5191 assert(reducedMemberIsValid(leaf));
5192 assert(reducedMemberIsValid(parent));
5193
5194 switch( getMemberType(dec, newCol->reducedMembers[leaf].member) )
5195 {
5197 {
5198 checkRigidLeaf(dec, newCol, leaf, toParent, parent, toChild);
5199 break;
5200 }
5202 {
5203 SPQRColReducedMember* reducedMember = &newCol->reducedMembers[leaf];
5204 assert(pathArcIsValid(reducedMember->firstPathArc));
5205 reducedMember->type = REDUCEDMEMBER_TYPE_CYCLE;
5206
5207 SCIP_Bool pathArcReversed = newCol->pathArcs[reducedMember->firstPathArc].reversed;
5208 SCIP_Bool arcPathArcIsReverse = arcIsReversedNonRigid(dec, newCol->pathArcs[reducedMember->firstPathArc].arc);
5209 SCIP_Bool parentReversed = arcIsReversedNonRigid(dec, toParent);
5210 createPathArc(dec, newCol, toChild, parent, ( arcPathArcIsReverse == parentReversed ) == pathArcReversed);
5211 break;
5212 }
5215 {
5216 SPQRColReducedMember* reducedMember = &newCol->reducedMembers[leaf];
5217 int countedPathArcs = 0;
5218 SCIP_Bool good = TRUE;
5219 SCIP_Bool passesForwards = TRUE;
5220 for( path_arc_id pathArc = reducedMember->firstPathArc; pathArcIsValid(pathArc);
5221 pathArc = newCol->pathArcs[pathArc].nextMember )
5222 {
5223 if( countedPathArcs == 0 )
5224 {
5225 passesForwards =
5226 newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc);
5227 }
5228 else if(
5229 (newCol->pathArcs[pathArc].reversed != arcIsReversedNonRigid(dec, newCol->pathArcs[pathArc].arc)) !=
5230 passesForwards )
5231 {
5232 good = FALSE;
5233 break;
5234 }
5235 ++countedPathArcs;
5236 }
5237 if( !good )
5238 {
5239 reducedMember->type = REDUCEDMEMBER_TYPE_NOT_NETWORK;
5240 newCol->remainsNetwork = FALSE;
5241 break;
5242 }
5243
5244 reducedMember->pathBackwards = !passesForwards;
5245 if( countedPathArcs == getNumMemberArcs(dec, findMember(dec, reducedMember->member)) - 1 )
5246 {
5247 //Type -> Cycle;
5248 //Propagate arc
5249 reducedMember->type = REDUCEDMEMBER_TYPE_CYCLE;
5250
5251 SCIP_Bool firstArcReversed = arcIsReversedNonRigid(dec, newCol->pathArcs[reducedMember->firstPathArc].arc);
5252 SCIP_Bool firstArcInPathReverse = newCol->pathArcs[reducedMember->firstPathArc].reversed;
5253 SCIP_Bool parentReversed = arcIsReversedNonRigid(dec, toParent);
5254 createPathArc(dec, newCol, toChild, parent,
5255 ( parentReversed == firstArcReversed ) != firstArcInPathReverse);
5256 }
5257 else
5258 {
5259 //Type -> single_end
5260 reducedMember->type = REDUCEDMEMBER_TYPE_MERGED;
5261 }
5262
5263 break;
5264 }
5266 {
5267 SCIPABORT();
5269 break;
5270 }
5271 }
5272 return newCol->reducedMembers[leaf].type;
5273}
5274
5275/** Recursively removes leaf nodes whose path forms cycles with the virtual arc to its children. */
5276static
5278 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
5279 SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */
5280 )
5281{
5282 assert(dec);
5283 assert(newCol);
5284
5285 int leafArrayIndex = 0;
5286 while( leafArrayIndex != newCol->numLeafMembers )
5287 {
5288 reduced_member_id leaf = newCol->leafMembers[leafArrayIndex];
5289 //next is invalid if the member is not in the reduced decomposition.
5290 reduced_member_id next = newCol->reducedMembers[leaf].parent;
5291 spqr_arc parentMarker = markerToParent(dec, newCol->reducedMembers[leaf].member);
5292 if( reducedMemberIsValid(next) && !arcIsTree(dec, parentMarker))
5293 {
5294 assert(reducedMemberIsValid(next));
5295 assert(SPQRarcIsValid(parentMarker));
5296 ReducedMemberType type = checkLeaf(dec, newCol, leaf, parentMarker, next,
5297 markerOfParent(dec, newCol->reducedMembers[leaf].member));
5298 if( type == REDUCEDMEMBER_TYPE_CYCLE )
5299 {
5300 ++newCol->reducedMembers[next].numPropagatedChildren;
5301 if( newCol->reducedMembers[next].numPropagatedChildren == newCol->reducedMembers[next].numChildren )
5302 {
5303 newCol->leafMembers[leafArrayIndex] = next;
5304 }
5305 else
5306 {
5307 ++leafArrayIndex;
5308 }
5309 }
5310 else if( type == REDUCEDMEMBER_TYPE_NOT_NETWORK )
5311 {
5312 return;
5313 }
5314 else
5315 {
5316 assert(type == REDUCEDMEMBER_TYPE_MERGED);
5317 ++leafArrayIndex;
5318 int component = newCol->reducedMembers[leaf].componentIndex;
5319 if( newCol->reducedComponents[component].numPathEndMembers >= 2 )
5320 {
5321 newCol->remainsNetwork = FALSE;
5322 return;
5323 }
5324 assert(newCol->reducedComponents[component].numPathEndMembers < 2);
5325 newCol->reducedComponents[component].pathEndMembers[newCol->reducedComponents[component].numPathEndMembers] = leaf;
5326 ++newCol->reducedComponents[component].numPathEndMembers;
5327 }
5328 }
5329 else
5330 {
5331 ++leafArrayIndex;
5332 int component = newCol->reducedMembers[leaf].componentIndex;
5333 if( newCol->reducedComponents[component].numPathEndMembers >= 2 )
5334 {
5335 newCol->remainsNetwork = FALSE;
5336 return;
5337 }
5338 assert(newCol->reducedComponents[component].numPathEndMembers < 2);
5339 newCol->reducedComponents[component].pathEndMembers[newCol->reducedComponents[component].numPathEndMembers] = leaf;
5340 ++newCol->reducedComponents[component].numPathEndMembers;
5341 }
5342 }
5343
5344 for( int j = 0; j < newCol->numReducedComponents; ++j )
5345 {
5346 //The reduced root might be a leaf as well: we propagate it last
5347 reduced_member_id root = newCol->reducedComponents[j].root;
5348
5349 while( TRUE ) /*lint !e716*/
5350 {
5351 if( newCol->reducedMembers[root].numPropagatedChildren != newCol->reducedMembers[root].numChildren - 1 )
5352 {
5353 break;
5354 }
5355 //TODO: bit ugly, have to do a linear search for the child
5357 spqr_arc markerToChild = SPQR_INVALID_ARC;
5358 for( children_idx i = newCol->reducedMembers[root].firstChild;
5359 i < newCol->reducedMembers[root].firstChild + newCol->reducedMembers[root].numChildren; ++i )
5360 {
5361 reduced_member_id childReduced = newCol->childrenStorage[i];
5362 if( newCol->reducedMembers[childReduced].type != REDUCEDMEMBER_TYPE_CYCLE )
5363 {
5364 child = childReduced;
5365 markerToChild = markerOfParent(dec, newCol->reducedMembers[child].member);
5366 break;
5367 }
5368 }
5369 assert(SPQRmemberIsValid(newCol->reducedMembers[child].member));
5370 assert(SPQRarcIsValid(markerToChild));
5371 if( !arcIsTree(dec, markerToChild) )
5372 {
5373 ReducedMemberType type = checkLeaf(dec, newCol, root, markerToChild, child,
5374 markerToParent(dec, newCol->reducedMembers[child].member));
5375 if( type == REDUCEDMEMBER_TYPE_CYCLE )
5376 {
5377 root = child;
5378 continue;
5379 }
5380 else if( type == REDUCEDMEMBER_TYPE_NOT_NETWORK )
5381 {
5382 return;
5383 }
5384 }
5385 //If the root has exactly one neighbour and is not contained, it is also considered a path end member
5386 int component = newCol->reducedMembers[root].componentIndex;
5387 SCIP_Bool rootPresent = FALSE;
5388 for( int i = 0; i < newCol->reducedComponents[component].numPathEndMembers; ++i )
5389 {
5390 rootPresent = rootPresent || ( newCol->reducedComponents[component].pathEndMembers[i] == root );
5391 }
5392 if( !rootPresent )
5393 {
5394 if( newCol->reducedComponents[component].numPathEndMembers >= 2 )
5395 {
5396 newCol->remainsNetwork = FALSE;
5397 return;
5398 }
5399 newCol->reducedComponents[component].pathEndMembers[newCol->reducedComponents[component].numPathEndMembers] = root;
5400 ++newCol->reducedComponents[component].numPathEndMembers;
5401 }
5402 break;
5403 }
5404
5405 newCol->reducedComponents[j].root = root;
5407 }
5408}
5409
5410/** Determine the type of a single component. */
5411static
5413 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
5414 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
5415 SPQRColReducedComponent* component /**< The component to determine the types for */
5416 )
5417{
5418 assert(dec);
5419 assert(newCol);
5420 assert(component);
5421
5422 if( component->numPathEndMembers == 1 )
5423 {
5424 assert(component->root == component->pathEndMembers[0]);
5425 determineSingleComponentType(dec, newCol, component->root);
5426 }
5427 else
5428 {
5429 assert(component->numPathEndMembers == 2);
5430 determinePathTypes(dec, newCol, component);
5431 }
5432}
5433
5434/** Checks if the given column can be added to the network matrix decomposition.
5435 *
5436 * See header for more info.
5437 */
5438static
5440 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
5441 SCIP_NETCOLADD* coladd, /**< The network matrix column addition data structure */
5442 int column, /**< The column to add */
5443 const int* nonzrows, /**< The column's nonzero row indices */
5444 const double* nonzvals, /**< The column's nonzero entries */
5445 int nnonzs /**< The number of nonzeros in the column */
5446 )
5447{
5448 assert(dec);
5449 assert(coladd);
5450 assert(nnonzs == 0 || ( nonzrows && nonzvals ));
5451
5452 /* A column can only be added once */
5453 if( netMatDecDataContainsColumn(dec,column) )
5454 {
5455 return SCIP_INVALIDDATA;
5456 }
5457 coladd->remainsNetwork = TRUE;
5458 cleanupPreviousIteration(dec, coladd);
5459 //assert that previous iteration was cleaned up
5460
5461 //Store call data
5462 SCIP_CALL( newColUpdateColInformation(dec, coladd, column, nonzrows, nonzvals, nnonzs) );
5463
5464 //compute reduced decomposition
5466 //initialize path arcs in reduced decomposition
5467 SCIP_CALL( createPathArcs(dec, coladd) );
5468 SCIP_CALL( computeLeafMembers(dec, coladd) );
5469 propagateCycles(dec, coladd);
5470 //determine types
5471 if( coladd->remainsNetwork )
5472 {
5473 for( int i = 0; i < coladd->numReducedComponents; ++i )
5474 {
5475 determineComponentTypes(dec, coladd, &coladd->reducedComponents[i]);
5476 }
5477 }
5478 //clean up memberInformation
5480
5481 return SCIP_OKAY;
5482}
5483
5484/** Struct that contains the data that tells us how to add the new column after the graph has been modified.
5485 *
5486 * In the case the member is a series or parallel node, the new column and rows are placed in series or parallel,
5487 * respectively. Otherwise, the edge can be added between head and tail in a rigid member.
5488 */
5489typedef struct
5490{
5491 spqr_member member; /**< The member where the new column should be added */
5492 spqr_node head; /**< The head node of the new column (rigid members only) */
5493 spqr_node tail; /**< The tail node of the new column (rigid members only) */
5494 spqr_arc representative; /**< The representative arc of the new column */
5495 SCIP_Bool reversed; /**< Is the new column reversed? */
5497
5498#define NEWCOLINFORMATION_EMPTY { SPQR_INVALID_MEMBER, SPQR_INVALID_NODE, SPQR_INVALID_NODE, SPQR_INVALID_ARC, FALSE }
5499
5500/** Set the head node of the new column edge to be added. */
5501static
5503 NewColInformation* info, /**< The new column information */
5504 spqr_node node /**< The head node of the new column edge */
5505 )
5506{
5507 assert(SPQRnodeIsValid(node));
5508 assert(SPQRnodeIsInvalid(info->head));
5509 assert(info);
5510
5511 info->head = node;
5512}
5513
5514/** Set the tail node of the new column edge to be added. */
5515static
5517 NewColInformation* info, /**< The new column information */
5518 spqr_node node /**< The tail node of the new column edge */
5519 )
5520{
5521 assert(SPQRnodeIsValid(node));
5522 assert(info);
5523 assert(SPQRnodeIsInvalid(info->tail));
5524
5525 info->tail = node;
5526}
5527
5528/** Set whether the new column arc should be reversed. */
5529static
5531 NewColInformation* info, /**< The new column information */
5532 SCIP_Bool reversed /**< Should the new column arc be reversed? */
5533 )
5534{
5535 assert(info);
5536
5537 info->reversed = reversed;
5538}
5539
5540/** Set the member that contains the new column arc. */
5541static
5543 NewColInformation* info, /**< The new column information */
5544 spqr_member member /**< The decomposition member that contains the new column arc */
5545 )
5546{
5547 assert(info);
5548
5549 info->member = member;
5550}
5551
5552/** Set the representative arc of the new column arc. */
5553static
5555 NewColInformation* info, /**< The new column information */
5556 spqr_arc representative /**< The representative arc of the new column arc */
5557 )
5558{
5559 assert(info);
5560
5561 info->representative = representative;
5562}
5563
5564/** Splits a parallel member into two adjacent parallel members connected by a virtual edge pair.
5565 *
5566 * One member keeps the two arcs passed to this function, the other member gets all other arcs.
5567 */
5568static
5570 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
5571 spqr_member parallel, /**< The parallel member */
5572 spqr_arc arc1, /**< First arc to keep. */
5573 spqr_arc arc2, /**< Second arc to keep. */
5574 spqr_member* childParallel /**< Out pointer to the new child parallel member. */
5575 )
5576{
5577 assert(dec);
5578 assert(SPQRarcIsValid(arc1));
5579 assert(SPQRarcIsValid(arc2));
5580 assert(SPQRmemberIsValid(parallel));
5581
5582 SCIP_Bool childContainsTree = arcIsTree(dec, arc1) || arcIsTree(dec, arc2);
5583 spqr_arc toParent = markerToParent(dec, parallel);
5584 SCIP_Bool parentMoved = toParent == arc1 || toParent == arc2;
5585 SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, childParallel) );
5586
5587 moveArcToNewMember(dec, arc1, parallel, *childParallel);
5588 moveArcToNewMember(dec, arc2, parallel, *childParallel);
5589
5590 if( parentMoved )
5591 {
5592 SCIP_CALL( createMarkerPair(dec, *childParallel, parallel, !childContainsTree, FALSE, FALSE) );
5593 }
5594 else
5595 {
5596 SCIP_CALL( createMarkerPair(dec, parallel, *childParallel, childContainsTree, FALSE, FALSE) );
5597 }
5598
5599 return SCIP_OKAY;
5600}
5601
5602/** Very elaborate function that splits a series member into multiple members based on the structure of the path arcs.
5603 *
5604 * The updated member reflects the structure of the updated SPQR tree after the new column arc is added.
5605 */
5606static
5608 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
5609 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
5610 SPQRColReducedMember* reducedMember, /**< The reduced member of the series member to split. */
5611 spqr_member member, /**< The series member to split. */
5612 spqr_member* loopMember, /**< Out-pointer to a new loop member that may be created */
5613 NewColInformation* newColInfo /**< New column information struct */
5614 )
5615{
5616 assert(dec);
5617 assert(reducedMember);
5618 assert(SPQRmemberIsValid(member));
5619 assert(memberIsRepresentative(dec, member));
5620
5621 assert(reducedMember->numPathArcs != 0);
5622 SCIP_Bool createPathSeries = reducedMember->numPathArcs > 1;
5623 SCIP_Bool convertOriginal = reducedMember->numPathArcs == getNumMemberArcs(dec, member) - 1;
5624
5625 //TODO: for now very elaborate to first get logic correct. Can probably merge some branches later...
5626 if( !createPathSeries && !convertOriginal )
5627 {
5628 spqr_member parallel;
5630 path_arc_id pathArcId = reducedMember->firstPathArc;
5631 assert(pathArcIsValid(pathArcId));
5632 spqr_arc marked = newCol->pathArcs[pathArcId].arc;
5633 assert(marked != markerToParent(dec, member));//TODO: handle this case later
5634 moveArcToNewMember(dec, marked, member, parallel);
5635 SCIP_Bool reversed = arcIsReversedNonRigid(dec, marked);
5636 SCIP_CALL( createMarkerPair(dec, member, parallel, TRUE, reversed, reversed) );
5637 *loopMember = parallel;
5638
5639 if( reversed == reducedMember->pathBackwards )
5640 {
5641 setTerminalReversed(newColInfo, !reversed);
5642 }
5643 else
5644 {
5645 setTerminalReversed(newColInfo, reversed);
5646 }
5647
5648 setTerminalMember(newColInfo, *loopMember);
5649 return SCIP_OKAY;
5650 }
5651 if( !createPathSeries && convertOriginal )
5652 {
5653 //only one path arc; we are in a loop; no need to change anything
5654 assert(getNumMemberArcs(dec, member) == 2);
5655 assert(reducedMember->numPathArcs == 1);
5656 *loopMember = member;
5657 changeLoopToParallel(dec, member);
5658
5659 path_arc_id pathArcId = reducedMember->firstPathArc;
5660 spqr_arc marked = newCol->pathArcs[pathArcId].arc;
5661 //The 'reversed' field has different meaning for parallels, so we need to change orientation when converting to a parallel
5662 arcFlipReversed(dec, marked);
5663
5664 setTerminalReversed(newColInfo, reducedMember->pathBackwards);
5665 setTerminalMember(newColInfo, *loopMember);
5666 return SCIP_OKAY;
5667 }
5668 if( createPathSeries && !convertOriginal )
5669 {
5670 spqr_member pathMember;
5671 SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember) );
5672
5673 path_arc_id pathArcId = reducedMember->firstPathArc;
5674 SCIP_Bool parentMoved = FALSE;
5675 while( pathArcIsValid(pathArcId))
5676 {
5677 spqr_arc pathArc = newCol->pathArcs[pathArcId].arc;
5678 pathArcId = newCol->pathArcs[pathArcId].nextMember;
5679 if( pathArc == markerToParent(dec, member))
5680 {
5681 parentMoved = TRUE;
5682 }
5683 moveArcToNewMember(dec, pathArc, member, pathMember);
5684 }
5685
5686 SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, loopMember) );
5687
5688 if( !parentMoved )
5689 {
5690 SCIP_CALL( createMarkerPair(dec, member, *loopMember, TRUE, FALSE, FALSE) );
5691 SCIP_CALL( createMarkerPair(dec, *loopMember, pathMember, TRUE, FALSE, TRUE) );
5692 }
5693 else
5694 {
5695 SCIP_CALL( createMarkerPair(dec, pathMember, *loopMember, FALSE, FALSE, TRUE) );
5696 SCIP_CALL( createMarkerPair(dec, *loopMember, member, FALSE, FALSE, FALSE) );
5697 }
5698
5699 setTerminalReversed(newColInfo, !reducedMember->pathBackwards);
5700 setTerminalMember(newColInfo, *loopMember);
5701 return SCIP_OKAY;
5702 }
5703 assert(createPathSeries && convertOriginal);
5704 //There's one exception in this case
5705 //if the single unmarked (column) marker is a parent or child marker to a parallel member, we add the edge there
5706 {
5707 spqr_member adjacentMember = SPQR_INVALID_MEMBER;
5708 spqr_arc adjacentMarker = SPQR_INVALID_ARC;
5709 spqr_arc memberMarker = SPQR_INVALID_ARC;
5710 spqr_arc firstArc = getFirstMemberArc(dec, reducedMember->member);
5711 spqr_arc arc = firstArc;
5712 do
5713 {
5714 if( !newCol->arcInPath[arc] )
5715 {
5716 if( arc == markerToParent(dec, reducedMember->member))
5717 {
5718 adjacentMember = findMemberParent(dec, reducedMember->member);
5719 adjacentMarker = markerOfParent(dec, reducedMember->member);
5720 memberMarker = arc;
5721 }
5722 else if( arcIsChildMarker(dec, arc))
5723 {
5724 adjacentMember = findArcChildMember(dec, arc);
5725 adjacentMarker = markerToParent(dec, adjacentMember);
5726 memberMarker = arc;
5727 }
5728
5729 break;//There is only a singular such arc
5730 }
5731 arc = getNextMemberArc(dec, arc);
5732 }
5733 while( arc != firstArc );
5734
5735 if( SPQRmemberIsValid(adjacentMember))
5736 {
5737 SPQRMemberType adjacentType = getMemberType(dec, adjacentMember);
5738 if( adjacentType == SPQR_MEMBERTYPE_PARALLEL )
5739 {
5740 //Figure out if the markers are the same or opposite orientations
5741 //If they are the same, we can proceed as normal, otherwise, we need to flip the placed edge
5742 SCIP_Bool markersHaveSameOrientation =
5743 arcIsReversedNonRigid(dec, adjacentMarker) == arcIsReversedNonRigid(dec, memberMarker);
5744 setTerminalReversed(newColInfo, reducedMember->pathBackwards == markersHaveSameOrientation);
5745 setTerminalMember(newColInfo, adjacentMember);
5746 return SCIP_OKAY;
5747 }
5748 }
5749 }
5750
5751 spqr_member pathMember;
5752 SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember) );
5753
5754 path_arc_id pathArcId = reducedMember->firstPathArc;
5755 SCIP_Bool parentMoved = FALSE;
5756 while( pathArcIsValid(pathArcId))
5757 {
5758 spqr_arc pathArc = newCol->pathArcs[pathArcId].arc;
5759 pathArcId = newCol->pathArcs[pathArcId].nextMember;
5760 if( pathArc == markerToParent(dec, member))
5761 {
5762 parentMoved = TRUE;
5763 }
5764 moveArcToNewMember(dec, pathArc, member, pathMember);
5765 }
5766 if( parentMoved )
5767 {
5768 SCIP_CALL( createMarkerPair(dec, pathMember, member, FALSE, FALSE, FALSE) );
5769 }
5770 else
5771 {
5772 SCIP_CALL( createMarkerPair(dec, member, pathMember, TRUE, FALSE, FALSE) );
5773 }
5774
5775 changeLoopToParallel(dec, member);
5776
5777 *loopMember = member;
5778 setTerminalReversed(newColInfo, reducedMember->pathBackwards);
5779 setTerminalMember(newColInfo, *loopMember);
5780 return SCIP_OKAY;
5781}
5782
5783/** Very elaborate function that splits a series member into multiple members based on the structure of the path arcs.
5784 *
5785 * The updated member reflects the structure of the updated SPQR tree after the new column arc is added.
5786 * This variant is only used on series members that are part of a reduced tree that is not a single member.
5787 */
5788static
5790 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
5791 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
5792 SPQRColReducedMember* reducedMember, /**< The reduced member of the series member to split. */
5793 spqr_member member, /**< The series member to split. */
5794 spqr_arc* pathRepresentative, /**< Out pointer pointing to the tree arc in the final series node */
5795 spqr_arc* nonPathRepresentative, /**< Out pointer pointing to the non-tree arc in the final series node*/
5796 spqr_arc exceptionArc1, /**< The first exception arc. Set to SPQR_INVALID_ARC to ignore */
5797 spqr_arc exceptionArc2 /**< The second exception arc. Set to SPQR_INVALID_ARC to ignore */
5798 )
5799{
5800 assert(dec);
5801 assert(reducedMember);
5802 assert(SPQRmemberIsValid(member));
5803 assert(memberIsRepresentative(dec, member));
5804
5805 int numExceptionArcs = ( exceptionArc1 == SPQR_INVALID_ARC ? 0 : 1 ) + ( exceptionArc2 == SPQR_INVALID_ARC ? 0 : 1 );
5806 int numNonPathArcs = getNumMemberArcs(dec, member) - reducedMember->numPathArcs - numExceptionArcs;
5807 SCIP_Bool createPathSeries = reducedMember->numPathArcs > 1;
5808 //If this holds, there are 2 or more non-parent marker non-path arcs
5809 SCIP_Bool createNonPathSeries = numNonPathArcs > 1;
5810 assert(exceptionArc1 == SPQR_INVALID_ARC || !newCol->arcInPath[exceptionArc1]);
5811 assert(exceptionArc2 == SPQR_INVALID_ARC || !newCol->arcInPath[exceptionArc2]);
5812
5813 if( createPathSeries )
5814 {
5815 spqr_member pathMember;
5816 SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_SERIES, &pathMember) );
5817
5818 path_arc_id pathArcId = reducedMember->firstPathArc;
5819 SCIP_Bool parentMoved = FALSE;
5820 while( pathArcIsValid(pathArcId) )
5821 {
5822 spqr_arc pathArc = newCol->pathArcs[pathArcId].arc;
5823 pathArcId = newCol->pathArcs[pathArcId].nextMember;
5824 assert(pathArc != exceptionArc1 && pathArc != exceptionArc2);
5825 parentMoved = parentMoved || markerToParent(dec, member) == pathArc;
5826 moveArcToNewMember(dec, pathArc, member, pathMember);
5827 }
5828 assert(getNumMemberArcs(dec, pathMember) >= 2);
5829
5830 spqr_arc ignored;
5831 SCIP_Bool inOldReversed = TRUE;
5832 SCIP_Bool inNewReversed = FALSE;
5833 if( parentMoved )
5834 {
5835 SCIP_CALL( createMarkerPairWithReferences(dec, pathMember, member, FALSE, inNewReversed, inOldReversed,
5836 &ignored, pathRepresentative) );
5837 }
5838 else
5839 {
5840 SCIP_CALL( createMarkerPairWithReferences(dec, member, pathMember, TRUE, inOldReversed, inNewReversed,
5841 pathRepresentative, &ignored) );
5842 }
5843 }
5844 else
5845 {
5846 if( pathArcIsValid(reducedMember->firstPathArc) )
5847 {
5848 *pathRepresentative = newCol->pathArcs[reducedMember->firstPathArc].arc;
5849 }
5850 else
5851 {
5852 *pathRepresentative = SPQR_INVALID_ARC;
5853 }
5854 }
5855
5856 if( createNonPathSeries )
5857 {
5858 spqr_member nonPathMember;
5859 SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_SERIES, &nonPathMember) );
5860
5861 spqr_arc arc = getFirstMemberArc(dec, member);
5862 SCIP_Bool parentMoved = FALSE;
5863 SCIP_Bool canStop = FALSE;//hack when the first arc is moved in the below loop to prevent that we immediately terminate
5864 do
5865 {
5866 spqr_arc nextArc = getNextMemberArc(dec, arc);
5867 if( arc != *pathRepresentative && arc != exceptionArc1 && arc != exceptionArc2 )
5868 {
5869 parentMoved = parentMoved || markerToParent(dec, member) == arc;
5870 moveArcToNewMember(dec, arc, member, nonPathMember);
5871 }
5872 else
5873 {
5874 canStop = TRUE;
5875 }
5876 arc = nextArc;
5877 if( canStop && arc == getFirstMemberArc(dec, member))
5878 {
5879 break;
5880 }
5881 }
5882 while( TRUE ); /*lint !e506*/
5883 assert(getNumMemberArcs(dec, nonPathMember) >= 2);
5884 SCIP_Bool representativeIsTree = !arcIsTree(dec, exceptionArc1);
5885 if( SPQRarcIsValid(exceptionArc2) )
5886 {
5887 representativeIsTree = representativeIsTree || !arcIsTree(dec, exceptionArc2);
5888 }
5889 spqr_arc ignored;
5890 SCIP_Bool inOldReversed = TRUE;
5891 SCIP_Bool inNewReversed = FALSE;
5892 if( parentMoved )
5893 {
5894 SCIP_CALL( createMarkerPairWithReferences(dec, nonPathMember, member, !representativeIsTree,
5895 inNewReversed, inOldReversed, &ignored, nonPathRepresentative) );
5896 }
5897 else
5898 {
5899 SCIP_CALL( createMarkerPairWithReferences(dec, member, nonPathMember, representativeIsTree,
5900 inOldReversed, inNewReversed, nonPathRepresentative, &ignored) );
5901 }
5902 }
5903 else
5904 {
5905 *nonPathRepresentative = SPQR_INVALID_ARC;
5906 if( numNonPathArcs != 0 )
5907 {
5908 spqr_arc firstArc = getFirstMemberArc(dec, member);
5909 spqr_arc arc = firstArc;
5910 do
5911 {
5912 if( arc != *pathRepresentative && arc != exceptionArc1 && arc != exceptionArc2 )
5913 {
5914 *nonPathRepresentative = arc;
5915 break;
5916 }
5917 arc = getNextMemberArc(dec, arc);
5918 }
5919 while( arc != firstArc );
5920 assert(*nonPathRepresentative != SPQR_INVALID_ARC);
5921 }
5922 }
5923
5924 return SCIP_OKAY;
5925}
5926
5927/** Transforms the first member in the path of members to reflect the new column update. */
5928static
5930 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
5931 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
5932 reduced_member_id reducedMember, /**< The reduced member to transform */
5933 NewColInformation* newColInfo, /**< The new column information */
5934 spqr_arc* representativeArc, /**< Pointer to the representative of the member, needed for merging.*/
5935 spqr_member* mergedMember /**< Pointer to the merged member */
5936 )
5937{
5938 spqr_member member = newCol->reducedMembers[reducedMember].member;
5939 SPQRMemberType type = getMemberType(dec, member);
5940 if( type == SPQR_MEMBERTYPE_RIGID )
5941 {
5942 //The nodes are already created, we only need to assign the correct start/end node
5943 switch( newCol->reducedMembers[reducedMember].pathType )
5944 {
5945 case INTO_HEAD:
5946 case INTO_TAIL:
5947 setTerminalTail(newColInfo, newCol->reducedMembers[reducedMember].rigidPathStart);
5948 break;
5949 case OUT_HEAD:
5950 case OUT_TAIL:
5951 setTerminalHead(newColInfo, newCol->reducedMembers[reducedMember].rigidPathEnd);
5952 break;
5953 }
5954 *representativeArc = findArcSign(dec, newCol->reducedMembers[reducedMember].pathTargetArc).representative;
5955 *mergedMember = member;
5956
5957 return SCIP_OKAY;
5958 }
5959 assert(type == SPQR_MEMBERTYPE_SERIES);
5960 //Split off sets of multiple path non-path edges so that the series has exactly 3 edges
5961
5962 spqr_arc target = newCol->reducedMembers[reducedMember].pathTargetArc;
5963 SPQRColReducedMember* redMem = &newCol->reducedMembers[reducedMember];
5964 spqr_arc pathRepresentative = SPQR_INVALID_ARC;
5965 spqr_arc nonPathRepresentative = SPQR_INVALID_ARC;
5966 SCIP_CALL( splitSeriesMerging(dec, newCol, redMem, member, &pathRepresentative, &nonPathRepresentative, target,
5968
5969 assert(
5970 target != pathRepresentative && target != nonPathRepresentative && pathRepresentative != nonPathRepresentative);
5971 assert(SPQRarcIsValid(pathRepresentative) && SPQRarcIsValid(nonPathRepresentative) && SPQRarcIsValid(target));
5972 assert(getNumMemberArcs(dec, member) == 3);
5973
5974 //Create nodes
5978 SCIP_CALL( createNode(dec, &a) );
5979 SCIP_CALL( createNode(dec, &b) );
5980 SCIP_CALL( createNode(dec, &c) );
5981
5982 // a -- b
5983 // \ /
5984 // c
5985
5986 //Set arc nodes
5987 //Set target from b to c,
5988 SCIP_Bool targetReversed = arcIsReversedNonRigid(dec, target);
5989 setArcHeadAndTail(dec, target, c, b);
5990
5991 MemberPathType pathType = newCol->reducedMembers[reducedMember].pathType;
5992 assert(pathType == INTO_HEAD || pathType == OUT_HEAD);
5993 if( arcIsReversedNonRigid(dec, pathRepresentative) == targetReversed )
5994 {
5995 setArcHeadAndTail(dec, pathRepresentative, a, c);
5996 }
5997 else
5998 {
5999 setArcHeadAndTail(dec, pathRepresentative, c, a);
6000 }
6001 if( arcIsReversedNonRigid(dec, nonPathRepresentative) == targetReversed )
6002 {
6003 setArcHeadAndTail(dec, nonPathRepresentative, b, a);
6004 }
6005 else
6006 {
6007 setArcHeadAndTail(dec, nonPathRepresentative, a, b);
6008 }
6009 //setup signed union find; all arcs are placed are not reversed. We pick an arbitrary arc as 'root' arc for this skeleton
6010 arcSetReversed(dec, target, FALSE);
6011 arcSetReversed(dec, pathRepresentative, FALSE);
6012 arcSetReversed(dec, nonPathRepresentative, FALSE);
6014 arcSetRepresentative(dec, pathRepresentative, target);
6015 arcSetRepresentative(dec, nonPathRepresentative, target);
6016 *representativeArc = target;
6017
6018 if( pathType == INTO_HEAD )
6019 {
6020 setTerminalTail(newColInfo, a);
6021 }
6022 else
6023 {
6024 setTerminalHead(newColInfo, a);
6025 }
6026
6027 *mergedMember = member;
6028
6029 return SCIP_OKAY;
6030}
6031
6032/** Transforms the next parallel member in the path of members and merge it into the current member. */
6033static
6035 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
6036 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
6037 reduced_member_id current, /**< The current reduced member id */
6038 reduced_member_id next, /**< The next reduced member */
6039 spqr_member nextMember, /**< The member of the next reduced member in the path */
6040 SCIP_Bool nextIsParent, /**< Is the next reduced member the parent of the current member? */
6041 spqr_arc* representativeArc, /**< Pointer to the representative of the member, needed for merging.*/
6042 spqr_member* mergedMember /**< Pointer to the merged member */
6043 )
6044{
6045 //Split off edges not in current subtree to form one parallel (or one edge)
6046 spqr_member childParallel = INVALID_REDUCED_MEMBER;
6047 spqr_arc source = newCol->reducedMembers[next].pathSourceArc;
6048 spqr_arc target = newCol->reducedMembers[next].pathTargetArc;
6049 if( getNumMemberArcs(dec, nextMember) > 3 )
6050 {
6051 SCIP_CALL( splitParallel(dec, nextMember, source, target, &childParallel) );
6052 nextMember = childParallel;
6053 newCol->reducedMembers[next].member = childParallel;
6054 }
6055 assert(getNumMemberArcs(dec, nextMember) == 3);
6056
6057 spqr_node sourceHead = SPQR_INVALID_NODE;
6058 spqr_node sourceTail = SPQR_INVALID_NODE;
6059 SCIP_CALL( createNode(dec, &sourceHead) );
6060 SCIP_CALL( createNode(dec, &sourceTail) );
6061
6062 //set edge nodes and arc union-find data
6063 {
6064 spqr_arc firstArc = getFirstMemberArc(dec, nextMember);
6065 spqr_arc arc = firstArc;
6066
6067 SCIP_Bool sourceReversed = arcIsReversedNonRigid(dec, source);
6068 do
6069 {
6070 if( arcIsReversedNonRigid(dec, arc) == sourceReversed )
6071 {
6072 setArcHeadAndTail(dec, arc, sourceHead, sourceTail);
6073 }
6074 else
6075 {
6076 setArcHeadAndTail(dec, arc, sourceTail, sourceHead);
6077 }
6078 arcSetRepresentative(dec, arc, source);
6079 arcSetReversed(dec, arc, FALSE);
6080
6081 arc = getNextMemberArc(dec, arc);
6082 }
6083 while( arc != firstArc );
6084
6086 }
6087
6088 //fix arc orientations of members; we cannot reflect for parallels.
6089 *representativeArc = mergeArcSigns(dec, *representativeArc, source, FALSE);
6090
6091 spqr_member newMergedMember = SPQR_INVALID_MEMBER;
6092 if( nextIsParent )
6093 {
6094 SCIP_CALL( mergeGivenMemberIntoParent(dec, *mergedMember, nextMember,
6095 source, newCol->reducedMembers[current].pathTargetArc, TRUE,
6096 &newMergedMember) );
6097 }
6098 else
6099 {
6100 SCIP_CALL( mergeGivenMemberIntoParent(dec, nextMember, *mergedMember,
6101 newCol->reducedMembers[current].pathTargetArc, source, TRUE,
6102 &newMergedMember) );
6103 }
6104 *mergedMember = newMergedMember;
6105
6106 return SCIP_OKAY;
6107}
6108
6109/** Transforms the next series member in the path of members and merge it into the current member. */
6110static
6112 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
6113 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
6114 reduced_member_id current, /**< The current reduced member id */
6115 reduced_member_id next, /**< The next reduced member */
6116 spqr_member nextMember, /**< The member of the next reduced member in the path */
6117 SCIP_Bool nextIsParent, /**< Is the next reduced member the parent of the current member? */
6118 spqr_arc* representativeArc, /**< Pointer to the representative of the member, needed for merging.*/
6119 spqr_member* mergedMember, /**< Pointer to the merged member */
6120 NewColInformation* info /**< The new column information */
6121 )
6122{
6123 SPQRColReducedMember* redMem = &newCol->reducedMembers[next];
6124 spqr_arc source = redMem->pathSourceArc;
6125 spqr_arc target = redMem->pathTargetArc;
6126
6127 spqr_arc pathRepresentative = SPQR_INVALID_ARC;
6128 spqr_arc nonPathRepresentative = SPQR_INVALID_ARC;
6129 SCIP_CALL(
6130 splitSeriesMerging(dec, newCol, redMem, nextMember, &pathRepresentative, &nonPathRepresentative, source, target) );
6131 //After splitting there is the following possibilities for nodes a-d:
6132 //(a)-source-(b)-path-(c)-target-(d)-nonpath-(a)
6133 //(a)-source-(b)-path-(c)-target-(d==a)
6134 //(a)-source-(b)=(c)-target-(d)-nonpath-(a)
6135 //(a)-source-(b)-path-(c)=(d) -nonpath-(a)
6136 //Note that the given arc is always between the same nodes
6137 assert(getNumMemberArcs(dec, nextMember) == 3 || getNumMemberArcs(dec, nextMember) == 4);
6138 assert(pathRepresentative != source && nonPathRepresentative != source &&
6139 ( SPQRarcIsInvalid(target) || ( target != pathRepresentative && target != nonPathRepresentative )));
6144 SCIP_CALL( createNode(dec, &a) );
6145 SCIP_CALL( createNode(dec, &b) );
6146 if( SPQRarcIsValid(pathRepresentative) )
6147 {
6148 SCIP_CALL( createNode(dec, &c) );
6149 }
6150 else
6151 {
6152 c = b;
6153 }
6154 SCIP_Bool hasNonPath = SPQRarcIsValid(nonPathRepresentative);
6155 SCIP_Bool hasTarget = SPQRarcIsValid(target);
6156 if( hasNonPath && hasTarget )
6157 {
6158 SCIP_CALL( createNode(dec, &d) );
6159 }
6160 else
6161 {
6162 if( hasNonPath )
6163 {
6164 d = c;
6165 }
6166 else
6167 {
6168 d = a;
6169 }
6170 }
6171
6172 SCIP_Bool sourceReversed = arcIsReversedNonRigid(dec, source);
6173 SCIP_Bool pathStartInHead = isHead(newCol->reducedMembers[current].pathType);
6174 if( pathStartInHead )
6175 {
6176 setArcHeadAndTail(dec, source, b, a);
6177 }
6178 else
6179 {
6180 setArcHeadAndTail(dec, source, a, b);
6181 }
6182 if( SPQRarcIsValid(pathRepresentative))
6183 {
6184 if( (arcIsReversedNonRigid(dec, pathRepresentative) == sourceReversed) == pathStartInHead )
6185 {
6186 setArcHeadAndTail(dec, pathRepresentative, c, b);
6187 }
6188 else
6189 {
6190 setArcHeadAndTail(dec, pathRepresentative, b, c);
6191 }
6192 arcSetReversed(dec, pathRepresentative, FALSE);
6193 arcSetRepresentative(dec, pathRepresentative, source);
6194 }
6195 if( hasTarget )
6196 {
6197 if( (arcIsReversedNonRigid(dec, target) == sourceReversed) == pathStartInHead )
6198 {
6199 setArcHeadAndTail(dec, target, d, c);
6200 }
6201 else
6202 {
6203 setArcHeadAndTail(dec, target, c, d);
6204 }
6205 arcSetReversed(dec, target, FALSE);
6206 arcSetRepresentative(dec, target, source);
6207 }
6208 if( hasNonPath )
6209 {
6210 if( (arcIsReversedNonRigid(dec, nonPathRepresentative) == sourceReversed) == pathStartInHead )
6211 {
6212 setArcHeadAndTail(dec, nonPathRepresentative, a, d);
6213 }
6214 else
6215 {
6216 setArcHeadAndTail(dec, nonPathRepresentative, d, a);
6217 }
6218 arcSetReversed(dec, nonPathRepresentative, FALSE);
6219 arcSetRepresentative(dec, nonPathRepresentative, source);
6220 }
6221
6222 arcSetReversed(dec, source, FALSE);
6224
6225 //fix arc orientations of members; we cannot reflect for series
6226
6227 spqr_member newMergedMember = SPQR_INVALID_MEMBER;
6228 if( nextIsParent )
6229 {
6230 SCIP_CALL( mergeGivenMemberIntoParent(dec, *mergedMember, nextMember,
6231 source, newCol->reducedMembers[current].pathTargetArc, TRUE,
6232 &newMergedMember) );
6233 }
6234 else
6235 {
6236 SCIP_CALL( mergeGivenMemberIntoParent(dec, nextMember, *mergedMember,
6237 newCol->reducedMembers[current].pathTargetArc, source, TRUE,
6238 &newMergedMember) );
6239 }
6240 *mergedMember = newMergedMember;
6241
6242 *representativeArc = mergeArcSigns(dec, *representativeArc, source, FALSE);
6243 if( !hasTarget )
6244 {
6245 //We are in the last node; finish the path
6247 if( isInto(newCol->reducedMembers[current].pathType))
6248 {
6249 setTerminalHead(info, c);
6250 }
6251 else
6252 {
6253 setTerminalTail(info, c);
6254 }
6255 setTerminalMember(info, *mergedMember);
6256 setTerminalRepresentative(info, *representativeArc);
6257 }
6258
6259 return SCIP_OKAY;
6260}
6261
6262/** Transforms the next rigid member in the path of members and merge it into the current member. */
6263static
6265 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
6266 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
6267 reduced_member_id current, /**< The current reduced member id */
6268 reduced_member_id next, /**< The next reduced member */
6269 spqr_member nextMember, /**< The member of the next reduced member in the path */
6270 SCIP_Bool nextIsParent, /**< Is the next reduced member the parent of the current member? */
6271 spqr_arc* representativeArc, /**< Pointer to the representative of the member, needed for merging.*/
6272 spqr_member* mergedMember, /**< Pointer to the merged member */
6273 NewColInformation* info /**< The new column information */
6274 )
6275{
6276 SPQRColReducedMember* redMem = &newCol->reducedMembers[next];
6277 spqr_arc source = redMem->pathSourceArc;
6278 spqr_arc sourceRepresentative = findArcSign(dec, source).representative;
6279
6280 spqr_member newMergedMember = SPQR_INVALID_MEMBER;
6281
6282 if( nextIsParent )
6283 {
6284 SCIP_CALL( mergeGivenMemberIntoParent(dec, *mergedMember, nextMember,
6285 source, newCol->reducedMembers[current].pathTargetArc, !redMem->reverseArcs,
6286 &newMergedMember) );
6287 }
6288 else
6289 {
6290 SCIP_CALL( mergeGivenMemberIntoParent(dec, nextMember, *mergedMember,
6291 newCol->reducedMembers[current].pathTargetArc, source, !redMem->reverseArcs,
6292 &newMergedMember) );
6293 }
6294
6295 *mergedMember = newMergedMember;
6296
6297 *representativeArc = mergeArcSigns(dec, *representativeArc, sourceRepresentative, redMem->reverseArcs);
6298
6299 if( SPQRarcIsInvalid(redMem->pathTargetArc) )
6300 {
6301 //We are in the last node; finish the path
6303 if( isInto(newCol->reducedMembers[current].pathType) )
6304 {
6305 if( redMem->reverseArcs )
6306 {
6307 setTerminalHead(info, redMem->rigidPathStart);
6308 }
6309 else
6310 {
6311 setTerminalHead(info, redMem->rigidPathEnd);
6312 }
6313 }
6314 else
6315 {
6316 if( redMem->reverseArcs )
6317 {
6318 setTerminalTail(info, redMem->rigidPathEnd);
6319 }
6320 else
6321 {
6322 setTerminalTail(info, redMem->rigidPathStart);
6323 }
6324 }
6325 setTerminalMember(info, *mergedMember);
6326 setTerminalRepresentative(info, *representativeArc);
6327 }
6328
6329 return SCIP_OKAY;
6330}
6331
6332/** Transforms the next member in the path of members and merge it into the current member. */
6333static
6335 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
6336 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
6337 reduced_member_id current, /**< The current reduced member id */
6338 reduced_member_id next, /**< The next reduced member */
6339 spqr_arc* representativeArc, /**< Pointer to the representative of the member, needed for merging.*/
6340 spqr_member* mergedMember, /**< Pointer to the merged member */
6341 SCIP_Bool nextIsParent, /**< Is the next reduced member the parent of the current member? */
6342 NewColInformation* info /**< The new column information */
6343 )
6344{
6345 spqr_member nextMember = newCol->reducedMembers[next].member;
6346 switch( getMemberType(dec, nextMember) )
6347 {
6349 {
6350 SCIP_CALL( transformAndMergeRigid(dec, newCol, current, next, nextMember, nextIsParent,
6351 representativeArc, mergedMember, info) );
6352 break;
6353 }
6355 {
6356 SCIP_CALL( transformAndMergeParallel(dec, newCol, current, next, nextMember, nextIsParent,
6357 representativeArc, mergedMember) );
6358 break;
6359 }
6361 {
6362 SCIP_CALL( transformAndMergeSeries(dec, newCol, current, next, nextMember, nextIsParent,
6363 representativeArc, mergedMember, info) );
6364 break;
6365 }
6368 {
6369 SCIPABORT();
6370 return SCIP_ERROR;
6371 }
6372 }
6373 return SCIP_OKAY;
6374}
6375
6376/** Transforms a single component when it contains multiple reduced members. */
6377static
6379 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
6380 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
6381 SPQRColReducedComponent* component, /**< The component to transform */
6382 NewColInformation* newColInfo /**< The new column information */
6383 )
6384{
6385 //Realize first member
6386 reduced_member_id firstPathMember = component->pathEndMembers[0];
6387
6388 spqr_arc representativeArc = SPQR_INVALID_ARC;
6389 spqr_member mergedMember = SPQR_INVALID_MEMBER;
6390 SCIP_CALL( transformFirstPathMember(dec, newCol, firstPathMember, newColInfo, &representativeArc, &mergedMember) );
6391 //Iteratively call function which realizes next member and merges them together.
6392 reduced_member_id current = firstPathMember;
6393 reduced_member_id next = newCol->reducedMembers[current].nextPathMember;
6394 SCIP_Bool nextIsParent = newCol->reducedMembers[current].nextPathMemberIsParent;
6395 while( reducedMemberIsValid(next) )
6396 {
6397 SCIP_CALL(
6398 transformAndMerge(dec, newCol, current, next, &representativeArc, &mergedMember, nextIsParent, newColInfo));
6399 current = next;
6400 next = newCol->reducedMembers[current].nextPathMember;
6401 nextIsParent = newCol->reducedMembers[current].nextPathMemberIsParent;
6402 }
6403
6404 return SCIP_OKAY;
6405}
6406
6407/** Transform a single parallel member to add the new column. */
6408static
6410 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
6411 reduced_member_id reducedMemberId, /**< The reduced member to transform */
6412 spqr_member member, /**< The member to transform */
6413 NewColInformation* newColInfo /**< The new column information */
6414 )
6415{
6416 SPQRColReducedMember* reducedMember = &newCol->reducedMembers[reducedMemberId];
6417 assert(pathArcIsValid(reducedMember->firstPathArc) && reducedMember->numPathArcs == 1);
6418 //The new arc can be placed in parallel; just add it to this member
6419 setTerminalReversed(newColInfo, reducedMember->pathBackwards);
6420 setTerminalMember(newColInfo, member);
6421
6422 return SCIP_OKAY;
6423}
6424
6425/** Transform a single series member to add the new column. */
6426static
6428 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
6429 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
6430 reduced_member_id reducedMemberId, /**< The reduced member to transform */
6431 spqr_member member, /**< The member to transform */
6432 NewColInformation* newColInfo /**< The new column information */
6433 )
6434{
6435 if( getNumMemberArcs(dec, member) == 1 )
6436 {
6437 newColInfo->member = member;
6438 newColInfo->reversed = newCol->arcInPathReversed[getFirstMemberArc(dec, member)];
6439 return SCIP_OKAY;
6440 }
6441 //Isolated single cycle
6442 spqr_member loopMember;
6443 SPQRColReducedMember* reducedMember = &newCol->reducedMembers[reducedMemberId];
6444 SCIP_CALL( splitSeries(dec, newCol, reducedMember, member, &loopMember, newColInfo) );
6445
6446 return SCIP_OKAY;
6447}
6448
6449/** Transform a single rigid member to add the new column. */
6450static
6452 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
6453 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
6454 reduced_member_id reducedMemberId, /**< The reduced member to transform */
6455 spqr_member member, /**< The member to transform */
6456 NewColInformation* newColInfo /**< The new column information */
6457 )
6458{
6459 assert(dec);
6460 assert(newCol);
6461 assert(newColInfo);
6462 assert(reducedMemberIsValid(reducedMemberId));
6463
6464 //The path is already computed, so we can simply take the start and end nodes.
6465 //However, there is one exception, which is that an arc connecting these two nodes already exists in the member
6466 //If so, we create a new parallel member with the new arc and this member, unless the existing arc already points
6467 //to a parallel member
6468 SPQRColReducedMember* reducedMember = &newCol->reducedMembers[reducedMemberId];
6469 assert(SPQRnodeIsValid(reducedMember->rigidPathStart) && SPQRnodeIsValid(reducedMember->rigidPathEnd));
6470 {
6471 spqr_arc existingArcWithPath = SPQR_INVALID_ARC;
6472 spqr_arc firstArc = getFirstNodeArc(dec, reducedMember->rigidPathStart);
6473 spqr_arc arc = firstArc;
6474 SCIP_Bool pathInSameDirection = FALSE;
6475 do
6476 {
6477 spqr_node head = findArcHead(dec, arc);
6478 spqr_node tail = findArcTail(dec, arc);
6479 spqr_node other = head == reducedMember->rigidPathStart ? tail : head;
6480 if( other == reducedMember->rigidPathEnd )
6481 {
6482 existingArcWithPath = arc;
6483 pathInSameDirection = ( head == other ) != findArcSign(dec, existingArcWithPath).reversed;
6484 break;
6485 }
6486 arc = getNextNodeArc(dec, arc, reducedMember->rigidPathStart);
6487 }
6488 while( arc != firstArc );
6489 if( SPQRarcIsValid(existingArcWithPath) )
6490 {
6491 SCIP_Bool isParent = FALSE;
6492 spqr_member adjacentMember = arcIsChildMarker(dec, existingArcWithPath) ?
6493 findArcChildMember(dec, existingArcWithPath) : SPQR_INVALID_MEMBER;
6494 if( existingArcWithPath == markerToParent(dec, member) )
6495 {
6496 adjacentMember = findMemberParent(dec, member);
6497 isParent = TRUE;
6498 }
6499 if( SPQRmemberIsValid(adjacentMember) && getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_PARALLEL )
6500 {
6501 spqr_arc parallelMarker = isParent ? markerOfParent(dec, member) : markerToParent(dec, adjacentMember);
6502 SCIP_Bool markerReversed = arcIsReversedNonRigid(dec, parallelMarker);
6503 setTerminalMember(newColInfo, adjacentMember);
6504 setTerminalReversed(newColInfo, markerReversed == pathInSameDirection);
6505 }
6506 else
6507 {
6508 //create a new parallel and move the edge there
6509 //This is a bit painful, because we cannot actually remove edges because of the union-find data structure
6510 //So what we do instead, is convert the current edge to a marker edge, and 'duplicate'
6511 //it in the new parallel member, and add the new marker there too, manually
6512 spqr_member adjacentParallel = SPQR_INVALID_MEMBER;
6513 SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &adjacentParallel) );
6514 //'duplicate' a new arc in the parallel to be the current arc
6515 spqr_arc duplicate = SPQR_INVALID_ARC;
6516 spqr_element element = arcGetElement(dec, existingArcWithPath);
6517 if( element != MARKER_COLUMN_ELEMENT && element != MARKER_ROW_ELEMENT )
6518 {
6519 if( SPQRelementIsColumn(element) )
6520 {
6521 SCIP_CALL( createColumnArc(dec, adjacentParallel, &duplicate, SPQRelementToColumn(element), FALSE) );
6522 }
6523 else
6524 {
6525 SCIP_CALL( createRowArc(dec, adjacentParallel, &duplicate, SPQRelementToRow(element), FALSE) );
6526 }
6527 }
6528 else if( isParent )
6529 {
6530 SCIP_CALL( createParentMarker(dec, adjacentParallel, arcIsTree(dec, existingArcWithPath), adjacentMember,
6531 markerOfParent(dec, member), &duplicate, FALSE) );
6532 }
6533 else
6534 {
6535 SCIP_CALL( createChildMarker(dec, adjacentParallel, adjacentMember, arcIsTree(dec, existingArcWithPath),
6536 &duplicate, FALSE) );
6537 dec->members[adjacentMember].parentMember = adjacentParallel;
6538 dec->members[adjacentMember].markerOfParent = duplicate;
6539 }
6540 //Create the other marker edge
6541 spqr_arc parallelMarker = SPQR_INVALID_ARC;
6542 if( isParent )
6543 {
6544 SCIP_CALL( createChildMarker(dec, adjacentParallel, member, !arcIsTree(dec, existingArcWithPath),
6545 &parallelMarker, FALSE) );
6546 }
6547 else
6548 {
6549 SCIP_CALL( createParentMarker(dec, adjacentParallel, !arcIsTree(dec, existingArcWithPath),
6550 member, existingArcWithPath, &parallelMarker, FALSE) );
6551 }
6552
6553 //Change the existing edge to a marker
6554 if( isParent )
6555 {
6556 assert(markerToParent(dec, member) == existingArcWithPath);
6557 dec->arcs[markerOfParent(dec, member)].childMember = adjacentParallel;
6558 dec->members[member].parentMember = adjacentParallel;
6559 dec->members[member].markerToParent = existingArcWithPath;
6560 dec->members[member].markerOfParent = parallelMarker;
6561 dec->arcs[existingArcWithPath].element = arcIsTree(dec, existingArcWithPath) ? MARKER_ROW_ELEMENT
6563 dec->arcs[existingArcWithPath].childMember = adjacentParallel;
6564 }
6565 else
6566 {
6567 dec->arcs[existingArcWithPath].element = arcIsTree(dec, existingArcWithPath) ? MARKER_ROW_ELEMENT
6569 dec->arcs[existingArcWithPath].childMember = adjacentParallel;
6570 }
6571
6572 setTerminalMember(newColInfo, adjacentParallel);
6573 setTerminalReversed(newColInfo, !pathInSameDirection);
6574 }
6575 return SCIP_OKAY;
6576 }
6577 }
6578
6579 setTerminalMember(newColInfo, member);
6580 setTerminalReversed(newColInfo, FALSE);
6581 setTerminalTail(newColInfo, reducedMember->rigidPathStart);
6582 setTerminalHead(newColInfo, reducedMember->rigidPathEnd);
6583 setTerminalRepresentative(newColInfo,
6584 findArcSign(dec, newCol->pathArcs[reducedMember->firstPathArc].arc).representative);
6585
6586 return SCIP_OKAY;
6587}
6588
6589/** Transform a component to reflect the new column. */
6590static
6592 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
6593 SCIP_NETCOLADD* newCol, /**< The network matrix column addition data structure */
6594 SPQRColReducedComponent* component, /**< The component to transform */
6595 NewColInformation* newColInfo /**< The new column information */
6596 )
6597{
6598 assert(dec);
6599 assert(newCol);
6600 assert(component);
6601 assert(newColInfo);
6602
6603 if( newCol->reducedMembers[component->root].numChildren ==
6604 newCol->reducedMembers[component->root].numPropagatedChildren )
6605 {
6606 //No merging necessary, only a single component
6607 reduced_member_id reducedMember = component->root;
6608 assert(reducedMemberIsValid(reducedMember));
6609 spqr_member member = newCol->reducedMembers[reducedMember].member;
6610 SPQRMemberType type = getMemberType(dec, member);
6611
6612 switch( type )
6613 {
6615 {
6616 SCIP_CALL( columnTransformSingleRigid(dec, newCol, reducedMember, member, newColInfo) );
6617 break;
6618 }
6620 {
6621 SCIP_CALL( columnTransformSingleParallel(newCol, reducedMember, member, newColInfo) );
6622 break;
6623 }
6626 {
6627 SCIP_CALL( columnTransformSingleSeries(dec, newCol, reducedMember, member, newColInfo) );
6628 break;
6629 }
6631 default:
6632 {
6633 SCIPABORT();
6634 return SCIP_ERROR;
6635 }
6636 }
6637 return SCIP_OKAY;
6638 }
6639 // Otherwise, the reduced members form a path which can be merged into a single component of type R
6640 SCIP_CALL( transformPath(dec, newCol, component, newColInfo) );
6641
6642 return SCIP_OKAY;
6643}
6644
6645/** Check if the submatrix stored remains a network matrix with the new column update. */
6646static
6648 const SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */
6649 )
6650{
6651 return newCol->remainsNetwork;
6652}
6653
6654/** Add the new column to the network decomposition as an arc.
6655 *
6656 * Only use this function after SCIPnetcoladdCheck() has been called.
6657 */
6658static
6660 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
6661 SCIP_NETCOLADD* newCol /**< The network matrix column addition data structure */
6662 )
6663{
6664 assert(dec);
6665 assert(newCol);
6666 assert(netcoladdRemainsNetwork(newCol));
6667
6668 if( newCol->numReducedComponents == 0 )
6669 {
6670 spqr_member member;
6672 newCol->numNewRowArcs, newCol->newColIndex, &member) );
6673 }
6674 else if( newCol->numReducedComponents == 1 )
6675 {
6677 SCIP_CALL( transformComponent(dec, newCol, &newCol->reducedComponents[0], &information) );
6678 assert(memberIsRepresentative(dec, information.member));
6679 if( newCol->numNewRowArcs == 0 )
6680 {
6681 spqr_arc colArc = SPQR_INVALID_ARC;
6682 SCIP_CALL( createColumnArc(dec, information.member, &colArc, newCol->newColIndex, information.reversed) );
6683 if( SPQRnodeIsValid(information.head) )
6684 {
6685 assert(SPQRnodeIsValid(information.tail));
6686 assert(SPQRarcIsValid(information.representative));
6687 setArcHeadAndTail(dec, colArc, findNode(dec, information.head), findNode(dec, information.tail));
6688 arcSetRepresentative(dec, colArc, information.representative);
6689 arcSetReversed(dec, colArc, information.reversed != arcIsReversedNonRigid(dec, information.representative));
6690 }
6691 }
6692 else
6693 {
6694 spqr_member newSeries = SPQR_INVALID_MEMBER;
6696 newCol->newColIndex, &newSeries) );
6697 spqr_arc markerArc = SPQR_INVALID_ARC;
6698 spqr_arc ignore = SPQR_INVALID_ARC;
6699 SCIP_CALL( createMarkerPairWithReferences(dec, information.member, newSeries, FALSE, information.reversed, TRUE,
6700 &markerArc, &ignore) );
6701 if( SPQRnodeIsValid(information.head) )
6702 {
6703 assert(SPQRnodeIsValid(information.tail));
6704 assert(SPQRarcIsValid(information.representative));
6705 setArcHeadAndTail(dec, markerArc, findNode(dec, information.head), findNode(dec, information.tail));
6706 arcSetRepresentative(dec, markerArc, information.representative);
6707 arcSetReversed(dec, markerArc,
6708 information.reversed != arcIsReversedNonRigid(dec, information.representative));
6709 }
6710 }
6711 if( getMemberType(dec, information.member) == SPQR_MEMBERTYPE_LOOP )
6712 {
6713 assert(getNumMemberArcs(dec, information.member) == 2 || getNumMemberArcs(dec, information.member) == 3);
6714 if( getNumMemberArcs(dec, information.member) == 3 )
6715 {
6716 changeLoopToParallel(dec, information.member);
6717 }
6718 }
6719 }
6720 else
6721 {
6722#ifndef NDEBUG
6723 int numDecComponentsBefore = numConnectedComponents(dec);
6724#endif
6725 spqr_member newSeries = SPQR_INVALID_MEMBER;
6727 newCol->numNewRowArcs, newCol->newColIndex, &newSeries) );
6728 for( int i = 0; i < newCol->numReducedComponents; ++i )
6729 {
6731 SCIP_CALL( transformComponent(dec, newCol, &newCol->reducedComponents[i], &information) );
6732 if( getMemberType(dec, information.member) == SPQR_MEMBERTYPE_LOOP )
6733 {
6734 assert(getNumMemberArcs(dec, information.member) == 1);
6735 spqr_arc arc = getFirstMemberArc(dec, information.member);
6736 assert(newCol->arcInPath[arc]);
6737 moveArcToNewMember(dec, arc, information.member, newSeries);
6738 arcSetReversed(dec, arc, !newCol->arcInPathReversed[arc]);
6739 dec->members[information.member].type = SPQR_MEMBERTYPE_UNASSIGNED;
6740 }
6741 else
6742 {
6743 reorderComponent(dec, information.member);//reorder the subtree so that the newly series member is a parent
6744 spqr_arc markerArc = SPQR_INVALID_ARC;
6745 spqr_arc ignore = SPQR_INVALID_ARC;
6746 SCIP_CALL( createMarkerPairWithReferences(dec, newSeries, information.member, TRUE, information.reversed,
6747 TRUE, &ignore, &markerArc) );
6748 if( SPQRnodeIsValid(information.head) )
6749 {
6750 assert(SPQRnodeIsValid(information.tail));
6751 assert(SPQRarcIsValid(information.representative));
6752 setArcHeadAndTail(dec, markerArc, findNode(dec, information.head), findNode(dec, information.tail));
6753 arcSetRepresentative(dec, markerArc, information.representative);
6754 arcSetReversed(dec, markerArc,
6755 information.reversed == arcIsReversedNonRigid(dec, information.representative));
6756 }
6757 }
6758 }
6760 assert(numConnectedComponents(dec) == ( numDecComponentsBefore - newCol->numReducedComponents + 1 ));
6761 }
6762
6763 return SCIP_OKAY;
6764}
6765
6766/* ---------- END functions for column addition --------------------------------------------------------------------- */
6767
6768/* ---------- START functions for row addition ---------------------------------------------------------------------- */
6769
6770/** Index type for the nonzeros of the new row, e.g. the columns whose tree path must be elongated with the new row */
6771typedef int cut_arc_id;
6772#define INVALID_CUT_ARC (-1)
6773
6774/** Checks if the given cut arc is invalid (negative). */
6775static
6777 const cut_arc_id arc /**< The cut arc to check */
6778 )
6779{
6780 return arc < 0;
6781}
6782
6783/** Checks if the given cut arc is valid (nonnegative). */
6785 const cut_arc_id arc /**< The cut arc to check */
6786 )
6787{
6788 return !cutArcIsInvalid(arc);
6789}
6790
6791/** Linked list node for the cut arcs.
6792 *
6793 * Contains links to the next cut arc in the whole decomposition and the next cut arc in the current member.
6794 */
6795typedef struct
6796{
6797 spqr_arc arc; /**< The arc id */
6798 spqr_node arcHead; /**< The arc's head node */
6799 spqr_node arcTail; /**< The arc's tail node */
6800 cut_arc_id nextMember; /**< Index to next linked list node of the linked list containing the cut arcs
6801 * of the arcs member.*/
6802 cut_arc_id nextOverall; /**< Index to next linked list node of the linked list containing the cut arcs
6803 * of the complete decomposition.*/
6804 SCIP_Bool arcReversed; /**< Should the new row have reverse direction in the cut arcs path? */
6806
6807/** Type of the reduced member
6808 *
6809 * Indicates whether the member is processed, and whether it should be merged or left the same.
6810 */
6811typedef enum
6812{
6813 TYPE_UNDETERMINED = 0, /**< The type of this member has not yet been determined */
6814 TYPE_PROPAGATED = 1, /**< The member contains cut arcs, but should not be merged */
6815 TYPE_MERGED = 2, /**< This member should be merged into a rigid member */
6816 TYPE_NOT_NETWORK = 3 /**< This member implies that the addition of the new row
6817 * does not create a network matrix */
6819
6820/** Stores for every vertex information needed for computing articulation nodes in rigid members. */
6821typedef struct
6822{
6823 int low; /**< What is the lowest discovery time vertex that can be reached from this
6824 * subtree?*/
6825 int discoveryTime; /**< When was the vertex first discovered? */
6827
6828/** Call stack data structure for performing DFS on the rigid member graphs. */
6829typedef struct
6830{
6831 spqr_node node; /**< The current node of the DFS call */
6832 spqr_arc nodeArc; /**< The current arc of the node that is being processed */
6833} DFSCallData;
6834
6835/** Call stack data structure for merging the SPQR tree into a single rigid member. */
6836typedef struct
6837{
6838 children_idx currentChild; /**< The index of the current child that is being merged */
6839 reduced_member_id id; /**< The index of the current member */
6841
6842/** Call stack data structure that determines whether a bipartition of the nodes exists that satisfies the requirements. */
6843typedef struct
6844{
6845 spqr_node node; /**< The current node of the call */
6846 spqr_arc arc; /**< The current arc that is being processed */
6848
6849/** Call stack data structure for recursive algorithm to find articulation point in rigid member graphs (Tarjan). */
6850typedef struct
6851{
6852 spqr_arc arc; /**< The arc that is currently being processed */
6853 spqr_node node; /**< The node that is currently being processed */
6854 spqr_node parent; /**< The node's parent */
6855 SCIP_Bool isAP; /**< Is the current node an articulation point? */
6857
6858/** Colors assigned to the node in the partitioning algorithm.
6859 *
6860 * It must either be in the source or the sink partition.
6861 */
6862typedef enum
6863{
6864 UNCOLORED = 0, /**< The current node has not yet been processed */
6865 COLOR_SOURCE = 1, /**< The current node belongs to the source partition */
6866 COLOR_SINK = 2 /**< The current node belongs to the sink partition */
6868
6869/** Struct that stores the data of a single reduced member. */
6870typedef struct
6871{
6872 spqr_member member; /**< The id of the decomposition member */
6873 spqr_member rootMember; /**< The decomposition member that is the root node of the arborescence
6874 * containing this member */
6875 int depth; /**< The depth of this member in the arborescence */
6876 RowReducedMemberType type; /**< The type of the member */
6877 reduced_member_id parent; /**< The reduced member id of the parent of this reduced member */
6878
6879 children_idx firstChild; /**< The index of the first child in the children array. */
6880 children_idx numChildren; /**< The number of children in the arborescence of this reduced member */
6881 children_idx numPropagatedChildren; /**< Counts the number of children that are propagated to this reduced
6882 * member */
6883
6884 cut_arc_id firstCutArc; /**< Head of the linked list containing the cut arcs */
6885 int numCutArcs; /**< The number of cut arcs in the linked list */
6886
6887 spqr_arc splitArc; /**< An arc adjacent to the split node. */
6888 SCIP_Bool splitHead; /**< Is the head or the tail of the split arc split in the realization? */
6889 SCIP_Bool otherIsSource; /**< Is the nonsplit node of the split arc in the source or the sink
6890 * partition? In rigid members this refers to the articulation arc. */
6891
6892 /* Rigid member fields */
6893 spqr_node otherNode; /**< The other nonsplit node adjacent to the virtual edge */
6894 spqr_node splitNode; /**< The node to be split */
6895 SCIP_Bool allHaveCommonNode; /**< Do all cut edges share a common node? */
6896 SCIP_Bool otherNodeSplit; /**< Is the other node a split node, too? */
6897 SCIP_Bool willBeReversed; /**< Will all the arcs in this component be reversed? */
6898 spqr_arc articulationArc; /**< Indicates the arc between the split node and the other ndoe, if both
6899 * are splittable. */
6900 spqr_node coloredNode; /**< Points to a colored node so that we can efficiently zero out colors
6901 * by backtracking our DFS */
6903
6904/** Keeps track of the data relevant for each SPQR tree in the SPQR forest. */
6905typedef struct
6906{
6907 int rootDepth; /**< The depth of the root node of the subtree in the arborescence */
6908 reduced_member_id root; /**< The reduced member id of the root */
6910
6911/** The main datastructure that manages all the data for row-addition in network matrices */
6912typedef struct
6913{
6914 SCIP_Bool remainsNetwork; /**< Does the addition of the current row give a network matrix? */
6915
6916 SPQRRowReducedMember* reducedMembers; /**< The array of reduced members, that form the subtree containing the
6917 * rows of the current column.*/
6918 int memReducedMembers; /**< Number of allocated slots in the reduced member array */
6919 int numReducedMembers; /**< Number of used slots in the reduced member array */
6920
6921 SPQRRowReducedComponent* reducedComponents; /**< The array of reduced components,
6922 * that represent the SPQR trees in the SPQR forest */
6923 int memReducedComponents; /**< Number of allocated slots in the reduced component array */
6924 int numReducedComponents; /**< Number of used slots in the reduced component array */
6925
6926 MemberInfo* memberInformation; /**< Array with member information; tracks the reduced member id that
6927 * corresponds to every member in the decomposition. */
6928 int memMemberInformation; /**< Number of allocated slots in the member information array */
6929 int numMemberInformation; /**< Number of used slots in the member information array */
6930
6931 reduced_member_id* childrenStorage; /**< Array that stores the children of the reduced member arborescences.
6932 * Each reduced member has a 'firstChild' field and a length, that points
6933 * to the subarray within this array with its children. This array is
6934 * shared here in order to minimize allocations across iterations. */
6935 int memChildrenStorage; /**< Number of allocated slots for the children storage array */
6936 int numChildrenStorage; /**< Number of used slots for the children storage array */
6937
6938 CutArcListNode* cutArcs; /**< Array containing the linked list nodes of the cut arcs */
6939 int memCutArcs; /**< Number of allocated entries in cutArcs */
6940 int numCutArcs; /**< Number of used entries in cutArcs */
6941 cut_arc_id firstOverallCutArc; /**< Index of the head node of the linked list containing all cut arcs */
6942
6943 spqr_row newRowIndex; /**< The index of the new row to be added */
6944
6945 spqr_col* newColumnArcs; /**< The nonzero columns in the new row that do not yet occur in the
6946 * decomposition */
6947 SCIP_Bool* newColumnReversed; /**< True if the nonzero entry of the new column is -1, False otherwise */
6948 int memColumnArcs; /**< Number of allocated slots in newColumnArcs/newColumnReversed */
6949 int numColumnArcs; /**< Number of new columns in the row to be added */
6950
6951 reduced_member_id* leafMembers; /**< Array that stores the leaf members of the SPQR forest */
6952 int numLeafMembers; /**< Number of used slots in leafMembers array */
6953 int memLeafMembers; /**< Number of allocated slots in leafMembers array */
6954
6955 spqr_arc* decompositionColumnArcs; /**< For each nonzero column of the new row that is in the decomposition,
6956 * stores the corresponding decomposition arc */
6957 SCIP_Bool* decompositionColumnArcReversed; /**< For each nonzero column of the new row that is in the decomposition,
6958 * stores whether the corresponding decomposition arc is reversed */
6959 int memDecompositionColumnArcs; /**< Number of allocated slots in decompositionColumnArcs(Reversed) */
6960 int numDecompositionColumnArcs; /**< Number of used slots in decompositionColumnArcs(Reversed) */
6961
6962 SCIP_Bool* isArcCut; /**< Stores for each arc, if the arc a cut arc? */
6963 SCIP_Bool* isArcCutReversed; /**< Is the new row in reverse direction on the arcs cycle? */
6964 int memIsArcCut; /**< The allocated size of the isArcCut(Reversed) arrays */
6965
6966 COLOR_STATUS* nodeColors; /**< Stores the color of each node */
6967 int memNodeColors; /**< The allocated size of the nodeColors array */
6968
6969 spqr_node* articulationNodes; /**< Temp. array for storing articulation nodes of member graph-cut arcs */
6970 int numArticulationNodes; /**< Number of used slots in articulation nodes array */
6971 int memArticulationNodes; /**< Number of allocated slots in articulation nodes array */
6972
6973 ArticulationNodeInformation* articulationNodeSearchInfo; /**< Stores for each node information necessary to find
6974 * articulation nodes. */
6975 int memNodeSearchInfo; /**< The number of allocated entries in articulationNodeSearchInfo array*/
6976
6977 int* crossingPathCount; /**< Stores for each arc, how many cut arc cycles contain it */
6978 int memCrossingPathCount; /**< The number of allocated entries for the crossingPathCount array */
6979
6980 DFSCallData* intersectionDFSData; /**< Call stack for computing the intersection of all cut arc paths */
6981 int memIntersectionDFSData; /**< Number of allocated entries for intersectionDFSData */
6982
6983 ColorDFSCallData* colorDFSData; /**< Call stack for computing source/sink coloring */
6984 int memColorDFSData; /**< Number of allocated entries for colorDFSData */
6985
6986 ArticulationPointCallStack* artDFSData; /**< Call stack for computing articulation points */
6987 int memArtDFSData; /**< Number of allocated entries for artDFSData */
6988
6989 CreateReducedMembersCallstack* createReducedMembersCallstack; /**< Call stack for createReducedMembers() */
6990 int memCreateReducedMembersCallstack; /**< Number of allocated entries for createReducedMembersCallStack */
6991
6992 int* intersectionPathDepth; /**< Tracks depth of each node in the intersection of all paths algorithm */
6993 int memIntersectionPathDepth; /**< Number of allocated entries in intersectionPathDepth array */
6994
6995 spqr_node* intersectionPathParent; /**< Tracks the parents of each node in the intersection of all paths
6996 * algorithm. */
6997 int memIntersectionPathParent; /**< Number of allocated entries in intersectionPathParent array */
6998
6999 MergeTreeCallData* mergeTreeCallData; /**< Call stack for mergeTree */
7000 int memMergeTreeCallData; /**< Number of allocated elements for mergeTreeCallData */
7001
7002 COLOR_STATUS* temporaryColorArray; /**< A temporary array used for saving some colors */
7003 int memTemporaryColorArray; /**< The number of allocated elements in temporaryColorArray */
7005
7006/** Struct that contains information on how to place the new row arc in the decomposition */
7007typedef struct
7008{
7009 spqr_member member; /**< The member to place the new row in */
7010 spqr_node head; /**< The head node of the new row arc (only used for rigid members) */
7011 spqr_node tail; /**< The tail node of the new row arc (only used for rigid members) */
7012 spqr_arc representative; /**< The representative arc of the new row arc */
7013 SCIP_Bool reversed; /**< Orientation of the arc w.r.t. the representative */
7015
7016#define NEWROWINFORMATION_EMPTY { SPQR_INVALID_MEMBER, SPQR_INVALID_NODE, SPQR_INVALID_NODE, SPQR_INVALID_ARC, FALSE }
7017
7018/** Saves the information of the current row and partitions it based on whether or not the given columns are
7019 * already part of the decomposition.
7020 */
7021static
7023 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
7024 SCIP_NETROWADD* newRow, /**< The network matrix row addition datastructure */
7025 const spqr_row row, /**< The row to check */
7026 const spqr_col* columns, /**< The column indices of the nonzeros in the row */
7027 const double* columnValues, /**< The matrix entries of the nonzeros in the row */
7028 const int numColumns /**< The number of nonzeros in the row */
7029 )
7030{
7031 newRow->newRowIndex = row;
7032
7033 newRow->numDecompositionColumnArcs = 0;
7034 newRow->numColumnArcs = 0;
7035
7036 for( int i = 0; i < numColumns; ++i )
7037 {
7038 spqr_arc columnArc = getDecompositionColumnArc(dec, columns[i]);
7039 SCIP_Bool reversed = columnValues[i] < 0.0;
7040 if( SPQRarcIsValid(columnArc) )
7041 {//If the arc is the current decomposition: save it in the array
7043 {
7044 int newNumArcs = newRow->memDecompositionColumnArcs == 0 ? 8 : 2 * newRow->memDecompositionColumnArcs;
7046 newRow->memDecompositionColumnArcs, newNumArcs) );
7048 newRow->memDecompositionColumnArcs, newNumArcs) );
7049 newRow->memDecompositionColumnArcs = newNumArcs;
7050 }
7051 newRow->decompositionColumnArcs[newRow->numDecompositionColumnArcs] = columnArc;
7054 }
7055 else
7056 {
7057 //Not in the decomposition: add it to the set of arcs which are newly added with this row.
7058 if( newRow->numColumnArcs == newRow->memColumnArcs )
7059 {
7060 int newNumArcs = newRow->memColumnArcs == 0 ? 8 : 2 * newRow->memColumnArcs;
7062 newRow->memColumnArcs, newNumArcs) );
7064 newRow->memColumnArcs, newNumArcs) );
7065 newRow->memColumnArcs = newNumArcs;
7066 }
7067 newRow->newColumnArcs[newRow->numColumnArcs] = columns[i];
7068 newRow->newColumnReversed[newRow->numColumnArcs] = reversed;
7069 newRow->numColumnArcs++;
7070 }
7071 }
7072
7073 return SCIP_OKAY;
7074}
7075
7076/** Adds members to the reduced member tree, by starting at the given member, jumping up to the parent, repeating this
7077 * procedure until the root has been added.
7078 */
7079static
7081 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
7082 SCIP_NETROWADD* newRow, /**< The network matrix row addition datastructure */
7083 const spqr_member firstMember /**< The member to create a reduced member for */
7084 )
7085{
7086 assert(SPQRmemberIsValid(firstMember));
7087
7089 callstack[0].member = firstMember;
7090 int callDepth = 0;
7091
7092 while( callDepth >= 0 )
7093 {
7094 spqr_member member = callstack[callDepth].member;
7095 reduced_member_id reducedMember = newRow->memberInformation[member].reducedMember;
7096
7097 SCIP_Bool reducedValid = reducedMemberIsValid(reducedMember);
7098 if( !reducedValid )
7099 {
7100 //reduced member was not yet created; we create it
7101 reducedMember = newRow->numReducedMembers;
7102
7103 SPQRRowReducedMember* reducedMemberData = &newRow->reducedMembers[reducedMember];
7104 ++newRow->numReducedMembers;
7105
7106 reducedMemberData->member = member;
7107 reducedMemberData->numChildren = 0;
7108 reducedMemberData->numCutArcs = 0;
7109 reducedMemberData->firstCutArc = INVALID_CUT_ARC;
7110 reducedMemberData->type = TYPE_UNDETERMINED;
7111 reducedMemberData->numPropagatedChildren = 0;
7112 reducedMemberData->articulationArc = SPQR_INVALID_ARC;
7113 reducedMemberData->splitNode = SPQR_INVALID_NODE;
7114 reducedMemberData->otherNode = SPQR_INVALID_NODE;
7115 reducedMemberData->splitArc = SPQR_INVALID_ARC;
7116 reducedMemberData->splitHead = FALSE;
7117 reducedMemberData->allHaveCommonNode = FALSE;
7118 reducedMemberData->otherNodeSplit = FALSE;
7119 reducedMemberData->willBeReversed = FALSE;
7120 reducedMemberData->coloredNode = SPQR_INVALID_NODE;
7121
7122 newRow->memberInformation[member].reducedMember = reducedMember;
7123 assert(memberIsRepresentative(dec, member));
7124 spqr_member parentMember = findMemberParent(dec, member);
7125
7126 if( SPQRmemberIsValid(parentMember) )
7127 {
7128 //recursive call to parent member
7129 ++callDepth;
7130 assert(callDepth < newRow->memCreateReducedMembersCallstack);
7131 callstack[callDepth].member = parentMember;
7132 continue;
7133 }
7134 else
7135 {
7136 //we found a new reduced decomposition component
7137
7138 reducedMemberData->parent = INVALID_REDUCED_MEMBER;
7139 reducedMemberData->depth = 0;
7140 reducedMemberData->rootMember = member;
7141
7142 assert(newRow->numReducedComponents < newRow->memReducedComponents);
7143 newRow->reducedComponents[newRow->numReducedComponents].root = reducedMember;
7144 ++newRow->numReducedComponents;
7145 }
7146 }
7147 if( reducedValid )
7148 {
7149 assert(reducedMember < newRow->numReducedMembers);
7150 //Reduced member was already created in earlier call
7151 //update the depth of the root if appropriate
7152 reduced_member_id* depthMinimizer = &newRow->memberInformation[newRow->reducedMembers[reducedMember].rootMember].rootDepthMinimizer;
7153 if( reducedMemberIsInvalid(*depthMinimizer) ||
7154 newRow->reducedMembers[reducedMember].depth < newRow->reducedMembers[*depthMinimizer].depth )
7155 {
7156 *depthMinimizer = reducedMember;
7157 }
7158 }
7159 while( TRUE ) /*lint !e716*/
7160 {
7161 --callDepth;
7162 if( callDepth < 0 )
7163 break;
7164 spqr_member parentMember = callstack[callDepth + 1].member;
7165 reduced_member_id parentReducedMember = newRow->memberInformation[parentMember].reducedMember;
7166 spqr_member currentMember = callstack[callDepth].member;
7167 reduced_member_id currentReducedMember = newRow->memberInformation[currentMember].reducedMember;
7168
7169 SPQRRowReducedMember* parentReducedMemberData = &newRow->reducedMembers[parentReducedMember];
7170 SPQRRowReducedMember* reducedMemberData = &newRow->reducedMembers[currentReducedMember];
7171
7172 reducedMemberData->parent = parentReducedMember;
7173 reducedMemberData->depth = parentReducedMemberData->depth + 1;
7174 reducedMemberData->rootMember = parentReducedMemberData->rootMember;
7175
7176 newRow->reducedMembers[parentReducedMember].numChildren++;
7177 }
7178 }
7179
7180 reduced_member_id returnedMember = newRow->memberInformation[callstack[0].member].reducedMember;
7181 return returnedMember;
7182}
7183
7184
7185/** Construct the reduced decomposition, which is the smallest subtree containing all members cut arcs. */
7186static
7188 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
7189 SCIP_NETROWADD* newRow /**< The network matrix row addition datastructure */
7190 )
7191{
7192#ifndef NDEBUG
7193 for( int i = 0; i < newRow->memMemberInformation; ++i )
7194 {
7196 }
7197#endif
7198
7199 newRow->numReducedComponents = 0;
7200 newRow->numReducedMembers = 0;
7201 if( newRow->numDecompositionColumnArcs == 0 )
7202 {//Early return in case the reduced decomposition will be empty
7203 return SCIP_OKAY;
7204 }
7205 assert(newRow->numReducedMembers == 0);
7206 assert(newRow->numReducedComponents == 0);
7207
7208 int newSize = largestMemberID(dec);//Is this sufficient?
7209 if( newSize > newRow->memReducedMembers )
7210 {
7211 int updatedSize = MAX(2 * newRow->memReducedMembers, newSize);
7212 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->reducedMembers, newRow->memReducedMembers, updatedSize) );
7213 newRow->memReducedMembers = updatedSize;
7214 }
7215 if( newSize > newRow->memMemberInformation )
7216 {
7217 int updatedSize = MAX(2 * newRow->memMemberInformation, newSize);
7218 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->memberInformation, newRow->memMemberInformation, updatedSize) );
7219 for( int i = newRow->memMemberInformation; i < updatedSize; ++i )
7220 {
7223 }
7224 newRow->memMemberInformation = updatedSize;
7225 }
7226
7227 int numComponents = numConnectedComponents(dec);
7228 if( numComponents > newRow->memReducedComponents )
7229 {
7230 int updatedSize = MAX(2 * newRow->memReducedComponents, numComponents);
7231 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->reducedComponents, newRow->memReducedComponents, updatedSize) );
7232 newRow->memReducedComponents = updatedSize;
7233 }
7234
7235 int numMembers = getNumMembers(dec);
7236 if( newRow->memCreateReducedMembersCallstack < numMembers )
7237 {
7238 int updatedSize = MAX(2 * newRow->memCreateReducedMembersCallstack, numMembers);
7240 newRow->memCreateReducedMembersCallstack, updatedSize) );
7241 newRow->memCreateReducedMembersCallstack = updatedSize;
7242 }
7243
7244 //Create the reduced members (recursively)
7245 for( int i = 0; i < newRow->numDecompositionColumnArcs; ++i )
7246 {
7247 assert(i < newRow->memDecompositionColumnArcs);
7248 spqr_arc arc = newRow->decompositionColumnArcs[i];
7249 spqr_member arcMember = findArcMember(dec, arc);
7250 reduced_member_id reducedMember = createRowReducedMembersToRoot(dec, newRow, arcMember);
7251 reduced_member_id* depthMinimizer = &newRow->memberInformation[newRow->reducedMembers[reducedMember].rootMember].rootDepthMinimizer;
7252 if( reducedMemberIsInvalid(*depthMinimizer) )
7253 {
7254 *depthMinimizer = reducedMember;
7255 }
7256 }
7257
7258 //Set the reduced roots according to the root depth minimizers
7259 for( int i = 0; i < newRow->numReducedComponents; ++i )
7260 {
7261 SPQRRowReducedComponent* component = &newRow->reducedComponents[i];
7262 spqr_member rootMember = newRow->reducedMembers[component->root].member;
7263 reduced_member_id reducedMinimizer = newRow->memberInformation[rootMember].rootDepthMinimizer;
7264 component->rootDepth = newRow->reducedMembers[reducedMinimizer].depth;
7265 component->root = reducedMinimizer;
7266
7267 //This simplifies code further down which does not need to be component-aware; just pretend that the reduced member is the new root.
7268 newRow->reducedMembers[component->root].parent = INVALID_REDUCED_MEMBER;
7269 assert(memberIsRepresentative(dec, rootMember));
7270 }
7271
7272 //update the children array
7273 int numTotalChildren = 0;
7274 for( int i = 0; i < newRow->numReducedMembers; ++i )
7275 {
7276 SPQRRowReducedMember* reducedMember = &newRow->reducedMembers[i];
7277 reduced_member_id minimizer = newRow->memberInformation[reducedMember->rootMember].rootDepthMinimizer;
7278 if( reducedMember->depth >= newRow->reducedMembers[minimizer].depth )
7279 {
7280 reducedMember->firstChild = numTotalChildren;
7281 numTotalChildren += reducedMember->numChildren;
7282 reducedMember->numChildren = 0;
7283 }
7284 }
7285
7286 if( newRow->memChildrenStorage < numTotalChildren )
7287 {
7288 int newMemSize = MAX(newRow->memChildrenStorage * 2, numTotalChildren);
7290 newMemSize) );
7291 newRow->memChildrenStorage = newMemSize;
7292 }
7293 newRow->numChildrenStorage = numTotalChildren;
7294
7295 //Fill up the children array`
7296 for( reduced_member_id reducedMember = 0; reducedMember < newRow->numReducedMembers; ++reducedMember )
7297 {
7298 SPQRRowReducedMember* reducedMemberData = &newRow->reducedMembers[reducedMember];
7299 if( reducedMemberData->depth <=
7300 newRow->reducedMembers[newRow->memberInformation[reducedMemberData->rootMember].rootDepthMinimizer].depth )
7301 {
7302 continue;
7303 }
7304 spqr_member parentMember = findMemberParent(dec, reducedMemberData->member);
7305 reduced_member_id parentReducedMember = SPQRmemberIsValid(parentMember)
7306 ? newRow->memberInformation[parentMember].reducedMember
7308 if( reducedMemberIsValid(parentReducedMember) )
7309 {
7310 SPQRRowReducedMember* parentReducedMemberData = &newRow->reducedMembers[parentReducedMember];
7311 newRow->childrenStorage[parentReducedMemberData->firstChild +
7312 parentReducedMemberData->numChildren] = reducedMember;
7313 ++parentReducedMemberData->numChildren;
7314 }
7315 }
7316
7317 //Clean up the root depth minimizers.
7318 for( int i = 0; i < newRow->numReducedMembers; ++i )
7319 {
7320 SPQRRowReducedMember* reducedMember = &newRow->reducedMembers[i];
7321 assert(reducedMember);
7322 spqr_member rootMember = reducedMember->rootMember;
7323 assert(rootMember >= 0);
7324 assert(rootMember < dec->memMembers);
7326 }
7327
7328 return SCIP_OKAY;
7329}
7330
7331/** Marks an arc as 'cut'.
7332 *
7333 * This implies that its cycle in the decomposition must be elongated.
7334 */
7335static
7337 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
7338 SCIP_NETROWADD* newRow, /**< The network matrix row addition datastructure */
7339 const spqr_arc arc, /**< The arc to mark as a cut arc */
7340 const reduced_member_id reducedMember, /**< The reduced member containing the arc */
7341 SCIP_Bool reversed /**< Indicates if the new row entry has +1 or -1 (TRUE) for the arc */
7342 )
7343{
7344 cut_arc_id cut_arc = newRow->numCutArcs;
7345
7346 CutArcListNode* listNode = &newRow->cutArcs[cut_arc];
7347 listNode->arc = arc;
7348
7349 listNode->nextMember = newRow->reducedMembers[reducedMember].firstCutArc;
7350 newRow->reducedMembers[reducedMember].firstCutArc = cut_arc;
7351
7352 listNode->nextOverall = newRow->firstOverallCutArc;
7353 newRow->firstOverallCutArc = cut_arc;
7354
7355 newRow->numCutArcs++;
7356 newRow->reducedMembers[reducedMember].numCutArcs++;
7357 assert(newRow->numCutArcs <= newRow->memCutArcs);
7358
7359 assert(arc < newRow->memIsArcCut);
7360 newRow->isArcCut[arc] = TRUE;
7361 newRow->isArcCutReversed[arc] = reversed;
7362
7363 assert(memberIsRepresentative(dec, newRow->reducedMembers[reducedMember].member));
7364 if( getMemberType(dec, newRow->reducedMembers[reducedMember].member) == SPQR_MEMBERTYPE_RIGID )
7365 {
7366 listNode->arcHead = findEffectiveArcHead(dec, arc);
7367 listNode->arcTail = findEffectiveArcTail(dec, arc);
7368 if( reversed )
7369 {
7370 SCIPswapInts(&listNode->arcHead, &listNode->arcTail);
7371 }
7372 assert(SPQRnodeIsValid(listNode->arcHead) && SPQRnodeIsValid(listNode->arcTail));
7373 }
7374 else
7375 {
7376 listNode->arcHead = SPQR_INVALID_NODE;
7377 listNode->arcTail = SPQR_INVALID_NODE;
7378 }
7379
7380 listNode->arcReversed = reversed;
7381}
7382
7383/** Creates all cut arcs within the decomposition for the new row.
7384 *
7385 * Note this preallocates memory for cut arcs which may be created by propagation.
7386 */
7387static
7389 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
7390 SCIP_NETROWADD* newRow /**< The network matrix row addition datastructure */
7391 )
7392{
7393 //Allocate memory for cut arcs
7394 spqr_arc maxArcID = largestArcID(dec);
7395 if( maxArcID > newRow->memIsArcCut )
7396 {
7397 int newSize = MAX(maxArcID, 2 * newRow->memIsArcCut);
7398 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->isArcCut, newRow->memIsArcCut, newSize) );
7399 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->isArcCutReversed, newRow->memIsArcCut, newSize) );
7400 for( int i = newRow->memIsArcCut; i < newSize; ++i )
7401 {
7402 newRow->isArcCut[i] = FALSE;
7403 newRow->isArcCutReversed[i] = FALSE;
7404 }
7405 newRow->memIsArcCut = newSize;
7406 }
7407#ifndef NDEBUG
7408 for( int i = 0; i < newRow->memIsArcCut; ++i )
7409 {
7410 assert(!newRow->isArcCut[i]);
7411 assert(!newRow->isArcCutReversed[i]);
7412 }
7413#endif
7414
7415 int numNeededArcs = newRow->numDecompositionColumnArcs * 4;
7416 if( numNeededArcs > newRow->memCutArcs )
7417 {
7418 int newSize = MAX(newRow->memCutArcs * 2, numNeededArcs);
7419 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->cutArcs, newRow->memCutArcs, newSize) );
7420 newRow->memCutArcs = newSize;
7421 }
7422 newRow->numCutArcs = 0;
7424 for( int i = 0; i < newRow->numDecompositionColumnArcs; ++i )
7425 {
7426 spqr_arc arc = newRow->decompositionColumnArcs[i];
7427 spqr_member member = findArcMember(dec, arc);
7428 reduced_member_id reduced_member = newRow->memberInformation[member].reducedMember;
7429 assert(reducedMemberIsValid(reduced_member));
7430 createCutArc(dec, newRow, arc, reduced_member, newRow->decompositionColumnArcReversed[i]);
7431 }
7432
7433 return SCIP_OKAY;
7434}
7435
7436/** Determines the members of the reduced decomposition which are leafs.
7437 *
7438 * This is used in propagation to ensure
7439 * propagation is only checked for components which have at most one neighbour that is not propagated.
7440 */
7441static
7443 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
7444 SCIP_NETROWADD* newRow /**< The network matrix row addition datastructure */
7445 )
7446{
7447 if( newRow->numDecompositionColumnArcs > newRow->memLeafMembers )
7448 {
7449 int updatedSize = MAX(newRow->numDecompositionColumnArcs, 2 * newRow->memLeafMembers);
7450 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->leafMembers, newRow->memLeafMembers, updatedSize) );
7451 newRow->memLeafMembers = updatedSize;
7452 }
7453 newRow->numLeafMembers = 0;
7454
7455 for( reduced_member_id reducedMember = 0; reducedMember < newRow->numReducedMembers; ++reducedMember )
7456 {
7457 if( newRow->reducedMembers[reducedMember].numChildren == 0 )
7458 {
7459 newRow->leafMembers[newRow->numLeafMembers] = reducedMember;
7460 ++newRow->numLeafMembers;
7461 }
7462 }
7463
7464 return SCIP_OKAY;
7465}
7466
7467/** Preallocates memory arrays necessary for searching rigid components. */
7468static
7470 const SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
7471 SCIP_NETROWADD* newRow /**< The network matrix row addition datastructure */
7472 )
7473{
7474 int totalNumNodes = getNumNodes(dec);
7475 int maxNumNodes = 2 * dec->numArcs;
7476 if( maxNumNodes > newRow->memNodeColors )
7477 {
7478 int newSize = MAX(2 * newRow->memNodeColors, maxNumNodes);
7479 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->nodeColors, newRow->memNodeColors, newSize) );
7480 for( int i = newRow->memNodeColors; i < newSize; ++i )
7481 {
7482 newRow->nodeColors[i] = UNCOLORED;
7483 }
7484 newRow->memNodeColors = newSize;
7485 }
7486
7487 if( totalNumNodes > newRow->memArticulationNodes )
7488 {
7489 int newSize = MAX(2 * newRow->memArticulationNodes, totalNumNodes);
7491 newRow->memArticulationNodes = newSize;
7492 }
7493 if( totalNumNodes > newRow->memNodeSearchInfo )
7494 {
7495 int newSize = MAX(2 * newRow->memNodeSearchInfo, totalNumNodes);
7497 newSize) );
7498 newRow->memNodeSearchInfo = newSize;
7499 }
7500 if( totalNumNodes > newRow->memCrossingPathCount )
7501 {
7502 int newSize = MAX(2 * newRow->memCrossingPathCount, totalNumNodes);
7504 newRow->memCrossingPathCount = newSize;
7505 }
7506 if( totalNumNodes > newRow->memTemporaryColorArray )
7507 {
7508 int newSize = MAX(2 * newRow->memTemporaryColorArray, totalNumNodes);
7510 newRow->memTemporaryColorArray, newSize) );
7511 newRow->memTemporaryColorArray = newSize;
7512 }
7513
7514 //TODO: see if tradeoff for performance bound by checking max # of nodes of rigid is worth it to reduce size
7515 //of the following allocations
7516 int largestID = largestNodeID(dec);
7517 //TODO: only update the stack sizes of the following when needed? The preallocation may not be worth it.
7518 if( largestID > newRow->memIntersectionDFSData )
7519 {
7520 int newSize = MAX(2 * newRow->memIntersectionDFSData, largestID);
7522 newRow->memIntersectionDFSData = newSize;
7523 }
7524 if( largestID > newRow->memColorDFSData )
7525 {
7526 int newSize = MAX(2 * newRow->memColorDFSData, largestID);
7527 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->colorDFSData, newRow->memColorDFSData, newSize) );
7528 newRow->memColorDFSData = newSize;
7529 }
7530 if( largestID > newRow->memArtDFSData )
7531 {
7532 int newSize = MAX(2 * newRow->memArtDFSData, largestID);
7533 SCIP_ALLOC( BMSreallocBlockMemoryArray(dec->env, &newRow->artDFSData, newRow->memArtDFSData, newSize) );
7534 newRow->memArtDFSData = newSize;
7535 }
7536
7537 for( int i = 0; i < newRow->memIntersectionPathDepth; ++i )
7538 {
7539 newRow->intersectionPathDepth[i] = -1;
7540 }
7541
7542 if( largestID > newRow->memIntersectionPathDepth )
7543 {
7544 int newSize = MAX(2 * newRow->memIntersectionPathDepth, largestID);
7546 newSize) );
7547 for( int i = newRow->memIntersectionPathDepth; i < newSize; ++i )
7548 {
7549 newRow->intersectionPathDepth[i] = -1;
7550 }
7551 newRow->memIntersectionPathDepth = newSize;
7552 }
7553 for( int i = 0; i < newRow->memIntersectionPathParent; ++i )
7554 {
7556 }
7557 if( largestID > newRow->memIntersectionPathParent )
7558 {
7559 int newSize = MAX(2 * newRow->memIntersectionPathParent, largestID);
7561 newSize) );
7562 for( int i = newRow->memIntersectionPathParent; i < newSize; ++i )
7563 {
7565 }
7566 newRow->memIntersectionPathParent = newSize;
7567 }
7568
7569 return SCIP_OKAY;
7570}
7571
7572/** Clears the colors for all nodes.
7573 *
7574 * We do DFS in reverse (reverse exploration order), as zeroing out the entire
7575 * array is more expensive.
7576 */
7577static
7579 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
7580 SCIP_NETROWADD* newRow, /**< The network matrix row addition datastructure */
7581 const spqr_node firstRemoveNode /**< The first node that was colored in the original DFS */
7582 )
7583{
7584 assert(firstRemoveNode < newRow->memNodeColors);
7585
7586 newRow->nodeColors[firstRemoveNode] = UNCOLORED;
7587 ColorDFSCallData* data = newRow->colorDFSData;
7588 if( newRow->colorDFSData == NULL )
7589 {
7590 return;
7591 }
7592
7593 data[0].node = firstRemoveNode;
7594 data[0].arc = getFirstNodeArc(dec, firstRemoveNode);
7595 if( SPQRarcIsInvalid(data[0].arc) )
7596 {
7597 return;
7598 }
7599
7600 int depth = 0;
7601
7602 while( depth >= 0 )
7603 {
7604 assert(depth < newRow->memColorDFSData);
7605 ColorDFSCallData* callData = &data[depth];
7606 spqr_node head = findArcHead(dec, callData->arc);
7607 spqr_node tail = findArcTail(dec, callData->arc);
7608 spqr_node otherNode = callData->node == head ? tail : head;
7609 assert(otherNode < newRow->memNodeColors);
7610 if( newRow->nodeColors[otherNode] != UNCOLORED )
7611 {
7612 callData->arc = getNextNodeArc(dec, callData->arc, callData->node);
7613
7614 newRow->nodeColors[otherNode] = UNCOLORED;
7615 ++depth;
7616 data[depth].node = otherNode;
7617 data[depth].arc = getFirstNodeArc(dec, otherNode);
7618 continue;
7619 }
7620
7621 callData->arc = getNextNodeArc(dec, callData->arc, callData->node);
7622 while( depth >= 0 && data[depth].arc == getFirstNodeArc(dec, data[depth].node) )
7623 {
7624 --depth;
7625 }
7626 }
7627}
7628
7629/** Cleans up various arrays used in previous iterations.
7630 *
7631 * This is cheaper than reallocating empty memory.
7632 */
7633static
7635 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
7636 SCIP_NETROWADD* newRow /**< The network matrix row addition datastructure */
7637 )
7638{
7639 //zero out coloring information from previous check
7640 for( int i = 0; i < newRow->numReducedMembers; ++i )
7641 {
7643 {
7644 zeroOutColors(dec, newRow, newRow->reducedMembers[i].coloredNode);
7645 }
7646 }
7647
7648#ifndef NDEBUG
7649 for( int i = 0; i < newRow->memNodeColors; ++i )
7650 {
7651 assert(newRow->nodeColors[i] == UNCOLORED);
7652 }
7653#endif
7654
7655 //For cut arcs: clear them from the array from previous iteration
7656 cut_arc_id cutArcIdx = newRow->firstOverallCutArc;
7657 while( cutArcIsValid(cutArcIdx) )
7658 {
7659 spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc;
7660 cutArcIdx = newRow->cutArcs[cutArcIdx].nextOverall;
7661 newRow->isArcCut[cutArc] = FALSE;
7662 newRow->isArcCutReversed[cutArc] = FALSE;
7663 }
7664#ifndef NDEBUG
7665 for( int i = 0; i < newRow->memIsArcCut; ++i )
7666 {
7667 assert(!newRow->isArcCut[i]);
7668 assert(!newRow->isArcCutReversed[i]);
7669 }
7670#endif
7671}
7672
7673/** Finds all the star nodes, i.e. nodes that are adjacent to all cut arcs, in a rigid member. */
7674static
7676 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
7677 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
7678 const reduced_member_id toCheck /**< The reduced member to check */
7679 )
7680{
7681 //4 cases:
7682 //Only a single edge; both head/tail are okay => network
7683 //All are adjacent to a single node, but do not have it as head or tail => not network
7684 //All are adjacent to a single node, and have it as head or tail => network
7685 //Not all are adjacent to a single node => check articulation nodes
7686 assert(newRow->reducedMembers[toCheck].numCutArcs > 0);//calling this function otherwise is nonsensical
7687
7688 cut_arc_id cutArcIdx = newRow->reducedMembers[toCheck].firstCutArc;
7689 spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc;
7690 spqr_node head = findArcHead(dec, cutArc);
7691 spqr_node tail = findArcTail(dec, cutArc);
7692
7693 SCIP_Bool reverse = findArcSign(dec, cutArc).reversed != newRow->cutArcs[cutArcIdx].arcReversed;
7694 spqr_node cutArcsHead = reverse ? tail : head;
7695 spqr_node cutArcsTail = reverse ? head : tail;
7696
7697 if( newRow->reducedMembers[toCheck].numCutArcs == 1 )
7698 {
7699 newRow->reducedMembers[toCheck].articulationArc = cutArc;
7700 newRow->reducedMembers[toCheck].splitNode = cutArcsHead;
7701 newRow->reducedMembers[toCheck].otherNode = cutArcsTail;
7702 newRow->reducedMembers[toCheck].otherIsSource = TRUE;
7703 newRow->reducedMembers[toCheck].allHaveCommonNode = TRUE;
7704 return;// Only a single cut arc
7705 }
7706
7707 spqr_node intersectionNodes[2] = {head, tail};
7708
7709 while( cutArcIsValid(newRow->cutArcs[cutArcIdx].nextMember) )
7710 {
7711 cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember;
7712 cutArc = newRow->cutArcs[cutArcIdx].arc;
7713 head = findArcHead(dec, cutArc);
7714 tail = findArcTail(dec, cutArc);
7715 reverse = findArcSign(dec, cutArc).reversed != newRow->cutArcs[cutArcIdx].arcReversed;
7716 spqr_node effectiveHead = reverse ? tail : head;
7717 spqr_node effectiveTail = reverse ? head : tail;
7718 if( effectiveHead != cutArcsHead )
7719 {
7720 cutArcsHead = SPQR_INVALID_NODE;
7721 }
7722 if( effectiveTail != cutArcsTail )
7723 {
7724 cutArcsTail = SPQR_INVALID_NODE;
7725 }
7726
7727 //intersection between intersectionNodes and head and tail
7728 for( int i = 0; i < 2; ++i )
7729 {
7730 if( intersectionNodes[i] != head && intersectionNodes[i] != tail )
7731 {
7732 intersectionNodes[i] = SPQR_INVALID_NODE;
7733 }
7734 }
7735 if( SPQRnodeIsInvalid(intersectionNodes[0]) && SPQRnodeIsInvalid(intersectionNodes[1]) )
7736 {
7737 newRow->reducedMembers[toCheck].splitNode = SPQR_INVALID_NODE;
7738 newRow->reducedMembers[toCheck].allHaveCommonNode = FALSE;
7739 return;//not all arcs are adjacent to a single node, need to check articulation nodes
7740 }
7741 }
7742 if( SPQRnodeIsInvalid(cutArcsHead) && SPQRnodeIsInvalid(cutArcsTail) )
7743 {
7744 //All arcs adjacent to a single node, but not in same direction; not network
7745 newRow->remainsNetwork = FALSE;
7746 newRow->reducedMembers[toCheck].type = TYPE_NOT_NETWORK;
7747 return;
7748 }
7749 SCIP_Bool headSplittable = SPQRnodeIsValid(cutArcsHead);
7750 //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
7751 //By virtue of the spanning tree, this arc must be a tree arc.
7752 spqr_node splitNode = headSplittable ? cutArcsHead : cutArcsTail;
7753 newRow->reducedMembers[toCheck].splitNode = splitNode;
7754 newRow->reducedMembers[toCheck].otherIsSource = headSplittable;
7755 newRow->reducedMembers[toCheck].allHaveCommonNode = TRUE;
7756 if( newRow->reducedMembers[toCheck].numCutArcs == nodeDegree(dec, splitNode) - 1 )
7757 {
7758 spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode);
7759 spqr_arc neighbourArc = firstNodeArc;
7760 do
7761 {
7762 if( arcIsTree(dec, neighbourArc))
7763 {
7764 break;
7765 }
7766 neighbourArc = getNextNodeArc(dec, neighbourArc, splitNode);
7767 } while( neighbourArc != firstNodeArc );
7768
7769 newRow->reducedMembers[toCheck].articulationArc = neighbourArc;
7770 spqr_arc arcHead = findArcHead(dec, neighbourArc);
7771 newRow->reducedMembers[toCheck].otherNode = arcHead == splitNode ? findArcTail(dec, neighbourArc) : arcHead;
7772 }
7773}
7774
7775/** Clears the colors for all nodes, but the neighbours.
7776 *
7777 * We do DFS in reverse (reverse exploration order), as zeroing out the entire array is more expensive.
7778 */
7779static
7781 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
7782 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
7783 const spqr_node articulationNode, /**< The node whose neighbours we want to keep */
7784 const spqr_node startRemoveNode /**< The node where DFS started */
7785 )
7786{
7787 COLOR_STATUS* neighbourColors = newRow->temporaryColorArray;
7788 assert(nodeDegree(dec, articulationNode) <= newRow->memTemporaryColorArray);
7789 {
7790 int i = 0;
7791 spqr_arc artFirstArc = getFirstNodeArc(dec, articulationNode);
7792 spqr_arc artItArc = artFirstArc;
7793 do
7794 {
7795 spqr_node head = findArcHead(dec, artItArc);
7796 spqr_node tail = findArcTail(dec, artItArc);
7797 spqr_node otherNode = articulationNode == head ? tail : head;
7798 neighbourColors[i] = newRow->nodeColors[otherNode];
7799 i++;
7800 assert(i <= nodeDegree(dec, articulationNode));
7801 artItArc = getNextNodeArc(dec, artItArc, articulationNode);
7802 }
7803 while( artItArc != artFirstArc );
7804 }
7805 zeroOutColors(dec, newRow, startRemoveNode);
7806
7807 {
7808 int i = 0;
7809 spqr_arc artFirstArc = getFirstNodeArc(dec, articulationNode);
7810 spqr_arc artItArc = artFirstArc;
7811 do
7812 {
7813 spqr_node head = findArcHead(dec, artItArc);
7814 spqr_node tail = findArcTail(dec, artItArc);
7815 spqr_node otherNode = articulationNode == head ? tail : head;
7816 newRow->nodeColors[otherNode] = neighbourColors[i];
7817 i++;
7818 assert(i <= nodeDegree(dec, articulationNode));
7819 artItArc = getNextNodeArc(dec, artItArc, articulationNode);
7820 }
7821 while( artItArc != artFirstArc );
7822 }
7823}
7824
7825/** Find the intersection of all cut arc paths in the given rigid member.
7826 *
7827 * Theoretically, there is a faster algorithm using lowest common ancestor queries, but it is quite complicated.
7828 * Thus, we stick with the 'simple' version below, which is typically also faster in practice.
7829 */
7830static
7832 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
7833 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
7834 const reduced_member_id toCheck, /**< The rigid member to check */
7835 int* const nodeNumPaths /**< Tracks for each node, how many cut arc paths cross it */
7836 )
7837{
7838 int* intersectionPathDepth = newRow->intersectionPathDepth;
7839 spqr_node* intersectionPathParent = newRow->intersectionPathParent;
7840
7841 //First do a dfs over the tree, storing all the tree-parents and depths for each node
7842 //TODO: maybe cache this tree and also update it so we can prevent this DFS call?
7843
7844 //pick an arbitrary node as root; we just use the first cutArc here
7845 {
7846 spqr_node root = findArcHead(dec, newRow->cutArcs[newRow->reducedMembers[toCheck].firstCutArc].arc);
7847 DFSCallData* pathSearchCallStack = newRow->intersectionDFSData;
7848
7849 assert(intersectionPathDepth[root] == -1);
7850 assert(intersectionPathParent[root] == SPQR_INVALID_NODE);
7851
7852 int pathSearchCallStackSize = 0;
7853
7854 intersectionPathDepth[root] = 0;
7855 intersectionPathParent[root] = SPQR_INVALID_NODE;
7856
7857 pathSearchCallStack[0].node = root;
7858 pathSearchCallStack[0].nodeArc = getFirstNodeArc(dec, root);
7859 pathSearchCallStackSize++;
7860 while( pathSearchCallStackSize > 0 )
7861 {
7862 assert(pathSearchCallStackSize <= newRow->memIntersectionDFSData);
7863 DFSCallData* dfsData = &pathSearchCallStack[pathSearchCallStackSize - 1];
7864 //cannot be a tree arc which is its parent
7865 if( arcIsTree(dec, dfsData->nodeArc) &&
7866 ( pathSearchCallStackSize <= 1 ||
7867 dfsData->nodeArc != pathSearchCallStack[pathSearchCallStackSize - 2].nodeArc ) )
7868 {
7869 spqr_node head = findArcHeadNoCompression(dec, dfsData->nodeArc);
7870 spqr_node tail = findArcTailNoCompression(dec, dfsData->nodeArc);
7871 spqr_node other = head == dfsData->node ? tail : head;
7872 assert(other != dfsData->node);
7873
7874 //We go up a level: add new node to the call stack
7875 pathSearchCallStack[pathSearchCallStackSize].node = other;
7876 pathSearchCallStack[pathSearchCallStackSize].nodeArc = getFirstNodeArc(dec, other);
7877 //Every time a new node is discovered/added, we update its parent and depth information
7878 assert(intersectionPathDepth[other] == -1);
7879 assert(intersectionPathParent[other] == SPQR_INVALID_NODE);
7880 intersectionPathParent[other] = dfsData->node;
7881 intersectionPathDepth[other] = pathSearchCallStackSize;
7882 ++pathSearchCallStackSize;
7883 continue;
7884 }
7885 do
7886 {
7887 dfsData->nodeArc = getNextNodeArc(dec, dfsData->nodeArc, dfsData->node);
7888 if( dfsData->nodeArc == getFirstNodeArc(dec, dfsData->node) )
7889 {
7890 --pathSearchCallStackSize;
7891 dfsData = &pathSearchCallStack[pathSearchCallStackSize - 1];
7892 }
7893 else
7894 {
7895 break;
7896 }
7897 }
7898 while( pathSearchCallStackSize > 0 );
7899 }
7900 }
7901
7902 //For each cut arc, trace back both ends until they meet
7903 cut_arc_id cutArc = newRow->reducedMembers[toCheck].firstCutArc;
7904 do
7905 {
7906 spqr_arc arc = newRow->cutArcs[cutArc].arc;
7907 cutArc = newRow->cutArcs[cutArc].nextMember;
7908
7909 //Iteratively jump up to the parents until they reach a common parent
7910 spqr_node source = findArcHead(dec, arc);
7911 spqr_node target = findArcTail(dec, arc);
7912 int sourceDepth = intersectionPathDepth[source];
7913 int targetDepth = intersectionPathDepth[target];
7914 nodeNumPaths[source]++;
7915 nodeNumPaths[target]++;
7916
7917 while( sourceDepth > targetDepth )
7918 {
7919 assert(source != target);
7920 source = intersectionPathParent[source];
7921 nodeNumPaths[source]++;
7922 --sourceDepth;
7923 }
7924 while( targetDepth > sourceDepth )
7925 {
7926 assert(source != target);
7927 target = intersectionPathParent[target];
7928 nodeNumPaths[target]++;
7929 --targetDepth;
7930 }
7931 while( source != target && targetDepth >= 0 )
7932 {
7933 source = intersectionPathParent[source];
7934 target = intersectionPathParent[target];
7935 nodeNumPaths[source]++;
7936 nodeNumPaths[target]++;
7937 --targetDepth;
7938 }
7939 //In all the above, the lowest common ancestor is increased twice, so we correct for it ad-hoc
7940 nodeNumPaths[source]--;
7941 assert(SPQRnodeIsValid(source) && SPQRnodeIsValid(target));
7942 assert(source == target);
7943 }
7944 while( cutArcIsValid(cutArc));
7945}
7946
7947/** Add a node to array of articulation nodes. */
7948static
7950 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
7951 spqr_node articulationNode /**< The node to add */
7952 )
7953{
7954#ifndef NDEBUG
7955 for( int i = 0; i < newRow->numArticulationNodes; ++i )
7956 {
7957 assert(newRow->articulationNodes[i] != articulationNode);
7958 }
7959#endif
7960 newRow->articulationNodes[newRow->numArticulationNodes] = articulationNode;
7961 ++newRow->numArticulationNodes;
7962}
7963
7964/** Find all the articulation points of the rigid member graph where the cut arcs are removed.
7965 *
7966 * Articulation points are nodes whose removal disconnects the remaining graph.
7967 */
7968static
7970 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
7971 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
7972 ArticulationNodeInformation* nodeInfo, /**< Stores information about the found articulation nodes */
7973 reduced_member_id reducedMember /**< The reduced member to find the articulation nodes for */
7974 )
7975{
7976 const SCIP_Bool* arcRemoved = newRow->isArcCut;
7977
7978 int rootChildren = 0;
7979 spqr_node root_node = findArcHead(dec, getFirstMemberArc(dec, newRow->reducedMembers[reducedMember].member));
7980
7981 ArticulationPointCallStack* callStack = newRow->artDFSData;
7982
7983 int depth = 0;
7984 int time = 1;
7985
7986 callStack[depth].arc = getFirstNodeArc(dec, root_node);
7987 callStack[depth].node = root_node;
7988 callStack[depth].parent = SPQR_INVALID_NODE;
7989 callStack[depth].isAP = FALSE;
7990
7991 nodeInfo[root_node].low = time;
7992 nodeInfo[root_node].discoveryTime = time;
7993
7994 while( depth >= 0 )
7995 {
7996 if( !arcRemoved[callStack[depth].arc] )
7997 {
7998 spqr_node node = callStack[depth].node;
7999 spqr_node head = findArcHead(dec, callStack[depth].arc);
8000 spqr_node tail = findArcTail(dec, callStack[depth].arc);
8001 spqr_node otherNode = node == head ? tail : head;
8002 if( otherNode != callStack[depth].parent )
8003 {
8004 if( nodeInfo[otherNode].discoveryTime == 0 )
8005 {
8006 if( depth == 0 )
8007 {
8008 rootChildren++;
8009 }
8010 //recursive call
8011 ++depth;
8012 assert(depth < newRow->memArtDFSData);
8013 callStack[depth].parent = node;
8014 callStack[depth].node = otherNode;
8015 callStack[depth].arc = getFirstNodeArc(dec, otherNode);
8016 callStack[depth].isAP = FALSE;
8017
8018 ++time;
8019 nodeInfo[otherNode].low = time;
8020 nodeInfo[otherNode].discoveryTime = time;
8021 continue;
8022 }
8023 else
8024 {
8025 nodeInfo[node].low = MIN(nodeInfo[node].low, nodeInfo[otherNode].discoveryTime);
8026 }
8027 }
8028 }
8029
8030 while( TRUE ) /*lint !e716*/
8031 {
8032 callStack[depth].arc = getNextNodeArc(dec, callStack[depth].arc, callStack[depth].node);
8033 if( callStack[depth].arc != getFirstNodeArc(dec, callStack[depth].node)) break;
8034 --depth;
8035 if( depth < 0 ) break;
8036
8037 spqr_node current_node = callStack[depth].node;
8038 spqr_node other_node = callStack[depth + 1].node;
8039 nodeInfo[current_node].low = MIN(nodeInfo[current_node].low, nodeInfo[other_node].low);
8040 if( depth != 0 &&
8041 !callStack[depth].isAP &&
8042 nodeInfo[current_node].discoveryTime <= nodeInfo[other_node].low )
8043 {
8044 addArticulationNode(newRow, current_node);
8045 callStack[depth].isAP = TRUE;
8046 }
8047 }
8048 }
8049 if( rootChildren > 1 )
8050 {
8051 addArticulationNode(newRow, root_node);
8052 }
8053}
8054
8055/** For a given articulation node, partitions the rigid member's graph where it is removed into a SOURCE and SINK
8056 * partition.
8057 *
8058 * All cut arcs must point (after reversal) from the source partition to the sink partition. This is akin
8059 * to finding a 2-coloring, and uses a DFS to do so. This function specifically executes the DFS/recursive part.
8060 */
8061static
8063 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
8064 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
8065 spqr_node articulationNode, /**< The articulation node to obtain the coloring for */
8066 spqr_node firstProcessNode, /**< The first node to process for the DFS */
8067 COLOR_STATUS firstColor, /**< The partition that the first node lies in. */
8068 SCIP_Bool* isGood /**< Returns whether the coloring was consistent */
8069 )
8070{
8071 const SCIP_Bool* isArcCut = newRow->isArcCut;
8072 COLOR_STATUS* nodeColors = newRow->nodeColors;
8073 ColorDFSCallData* data = newRow->colorDFSData;
8074
8075 data[0].node = firstProcessNode;
8076 data[0].arc = getFirstNodeArc(dec, firstProcessNode);
8077 newRow->nodeColors[firstProcessNode] = firstColor;
8078
8079 int depth = 0;
8080 while( depth >= 0 )
8081 {
8082 assert(depth < newRow->memColorDFSData);
8083 assert(newRow->nodeColors[articulationNode] == UNCOLORED);
8084
8085 ColorDFSCallData* callData = &data[depth];
8086 spqr_node head = findArcHead(dec, callData->arc);
8087 spqr_node tail = findArcTail(dec, callData->arc);
8088 spqr_node otherNode = callData->node == head ? tail : head;
8089 COLOR_STATUS currentColor = nodeColors[callData->node];
8090 COLOR_STATUS otherColor = nodeColors[otherNode];
8091 //Checks the direction of the arc; in the rest of the algorithm, we just need to check partition
8092 if( isArcCut[callData->arc] && currentColor != otherColor )
8093 {
8094 SCIP_Bool otherIsTail = callData->node == head;
8095 SCIP_Bool arcReversed = findArcSign(dec, callData->arc).reversed != newRow->isArcCutReversed[callData->arc];
8096 SCIP_Bool good = ( currentColor == COLOR_SOURCE ) == ( otherIsTail == arcReversed );
8097 if( !good )
8098 {
8099 *isGood = FALSE;
8100 break;
8101 }
8102 }
8103 if( otherNode != articulationNode )
8104 {
8105 if( otherColor == UNCOLORED )
8106 {
8107 if( isArcCut[callData->arc] )
8108 {
8109 nodeColors[otherNode] = currentColor == COLOR_SOURCE ? COLOR_SINK : COLOR_SOURCE;//reverse the colors
8110 }
8111 else
8112 {
8113 nodeColors[otherNode] = currentColor;
8114 }
8115 callData->arc = getNextNodeArc(dec, callData->arc, callData->node);
8116
8117 depth++;
8118 assert(depth < newRow->memColorDFSData);
8119 data[depth].node = otherNode;
8120 data[depth].arc = getFirstNodeArc(dec, otherNode);
8121 continue;
8122 }
8123 if( isArcCut[callData->arc] != ( currentColor != otherColor ) )
8124 {
8125 *isGood = FALSE;
8126 break;
8127 }
8128 }
8129 callData->arc = getNextNodeArc(dec, callData->arc, callData->node);
8130 while( depth >= 0 && data[depth].arc == getFirstNodeArc(dec, data[depth].node) )
8131 {
8132 --depth;
8133 }
8134 }
8135}
8136
8137/** For a given articulation node, partitions the rigid member's graph where it is removed into a SOURCE and SINK
8138 * partition.
8139 *
8140 * All cut arcs must point (after reversal) from the source partition to the sink partition. This is akin
8141 * to finding a 2-coloring, and uses a DFS to do so.
8142 */
8143static
8145 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
8146 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
8147 const reduced_member_id reducedMember, /**< The rigid member whose graph to color */
8148 const spqr_node node, /**< The articulation node to obtain the coloring for */
8149 SCIP_Bool* const isGood /**< Returns whether the coloring was consistent */
8150 )
8151{
8152 //we should only perform this function if there's more than one cut arc
8153 assert(newRow->reducedMembers[reducedMember].numCutArcs > 1);
8154#ifndef NDEBUG
8155 {
8156 spqr_member member = newRow->reducedMembers[reducedMember].member;
8157 spqr_arc firstArc = getFirstMemberArc(dec, member);
8158 spqr_arc memberArc = firstArc;
8159 do
8160 {
8161 assert(newRow->nodeColors[findArcHeadNoCompression(dec, memberArc)] == UNCOLORED);
8162 assert(newRow->nodeColors[findArcTailNoCompression(dec, memberArc)] == UNCOLORED);
8163 memberArc = getNextMemberArc(dec, memberArc);
8164 }
8165 while( firstArc != memberArc );
8166 }
8167#endif
8168
8169 spqr_node firstProcessNode;
8170 COLOR_STATUS firstColor;
8171 {
8172 cut_arc_id cutArc = newRow->reducedMembers[reducedMember].firstCutArc;
8173 spqr_arc arc = newRow->cutArcs[cutArc].arc;
8174 assert(SPQRarcIsValid(arc));
8175 spqr_node head = findArcHead(dec, arc);
8176 spqr_node tail = findArcTail(dec, arc);
8177 if( findArcSign(dec, arc).reversed != newRow->cutArcs[cutArc].arcReversed )
8178 {
8179 spqr_node temp = head;
8180 head = tail;
8181 tail = temp;
8182 }
8183 if( tail != node )
8184 {
8185 firstProcessNode = tail;
8186 firstColor = COLOR_SOURCE;
8187 }
8188 else
8189 {
8190 assert(head != node);
8191 firstProcessNode = head;
8192 firstColor = COLOR_SINK;
8193 }
8194 }
8195 assert(SPQRnodeIsValid(firstProcessNode) && firstProcessNode != node);
8196 *isGood = TRUE;
8197 newRow->reducedMembers[reducedMember].coloredNode = firstProcessNode;
8198 rigidConnectedColoringRecursive(dec, newRow, node, firstProcessNode, firstColor, isGood);
8199
8200 // Need to zero all colors for next attempts if we failed
8201 if( !( *isGood ) )
8202 {
8203 zeroOutColors(dec, newRow, firstProcessNode);
8204 newRow->reducedMembers[reducedMember].coloredNode = SPQR_INVALID_NODE;
8205 }
8206 else
8207 {
8208 //Otherwise, we zero out all colors but the ones which we need
8209 zeroOutColorsExceptNeighbourhood(dec, newRow, node, firstProcessNode);
8210 newRow->reducedMembers[reducedMember].coloredNode = node;
8211 }
8212}
8213
8214/** Given a coloring for an articulation node, we can prove that a neighbouring node is splittable if and only if it is
8215 * the unique node in the neighbourhood of the articulation node within the SOURCE or SINK partition. In this function,
8216 * we check for a splittable articulation node if such an adjacent node exists, and return it if possible.
8217 */
8218static
8220 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
8221 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
8222 const spqr_node articulationNode, /**< The articulation whose neighbours are to be checked */
8223 spqr_arc* const adjacentSplittingArc/**< The arc connecting the two splittable nodes. */
8224 )
8225{
8226 spqr_node firstSideCandidate = SPQR_INVALID_NODE;
8227 spqr_node secondSideCandidate = SPQR_INVALID_NODE;
8228 spqr_arc firstSideArc = SPQR_INVALID_ARC;
8229 spqr_arc secondSideArc = SPQR_INVALID_ARC;
8230 int numFirstSide = 0;
8231 int numSecondSide = 0;
8232
8233 spqr_arc firstArc = getFirstNodeArc(dec, articulationNode);
8234 spqr_arc moveArc = firstArc;
8235 do
8236 {
8237 spqr_node head = findArcHead(dec, moveArc);
8238 spqr_node tail = findArcTail(dec, moveArc);
8239 spqr_node otherNode = articulationNode == head ? tail : head;
8240 assert(newRow->nodeColors[otherNode] != UNCOLORED);
8241 if(( newRow->nodeColors[otherNode] == COLOR_SOURCE ) != newRow->isArcCut[moveArc] )
8242 {
8243 if( numFirstSide == 0 && arcIsTree(dec, moveArc))
8244 {
8245 firstSideCandidate = otherNode;
8246 firstSideArc = moveArc;
8247 }
8248 else if( numFirstSide > 0 )
8249 {
8250 firstSideCandidate = SPQR_INVALID_NODE;
8251 }
8252 ++numFirstSide;
8253 }
8254 else
8255 {
8256 if( numSecondSide == 0 && arcIsTree(dec, moveArc))
8257 {
8258 secondSideCandidate = otherNode;
8259 secondSideArc = moveArc;
8260 }
8261 else if( numSecondSide > 0 )
8262 {
8263 secondSideCandidate = SPQR_INVALID_NODE;
8264 }
8265 ++numSecondSide;
8266 }
8267 moveArc = getNextNodeArc(dec, moveArc, articulationNode);
8268 }
8269 while( moveArc != firstArc );
8270
8271 if( numFirstSide == 1 )
8272 {
8273 *adjacentSplittingArc = firstSideArc;
8274 return firstSideCandidate;
8275 }
8276 else if( numSecondSide == 1 )
8277 {
8278 *adjacentSplittingArc = secondSideArc;
8279 return secondSideCandidate;
8280 }
8281 return SPQR_INVALID_NODE;
8282}
8283
8284/** Find all the splittable articulation points that lie on the intersection of all cut arc cycles.
8285 *
8286 * firstNode and secondNode can be set to limit the nodes that this function checks to the given nodes.
8287 */
8288static
8290 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
8291 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
8292 const reduced_member_id toCheck, /**< The rigid member to check */
8293 spqr_node firstNode, /**< Optional: the first given node that should be checked */
8294 spqr_node secondNode /**< Optional: the second given node that should be checked */
8295 )
8296{
8297 assert(newRow->reducedMembers[toCheck].numCutArcs > 1);
8298
8299 int totalNumNodes = getNumNodes(dec);
8300 int* nodeNumPaths = newRow->crossingPathCount;
8301
8302 for( int i = 0; i < totalNumNodes; ++i )
8303 {
8304 nodeNumPaths[i] = 0;
8305 }
8306
8307 intersectionOfAllPaths(dec, newRow, toCheck, nodeNumPaths);
8308
8309 newRow->numArticulationNodes = 0;
8310
8312 //TODO: ugly; we clean up over all decomposition nodes for every component
8313 //clean up can not easily be done in the search, unfortunately
8314 for( int i = 0; i < totalNumNodes; ++i )
8315 {
8316 artNodeInfo[i].low = 0;
8317 artNodeInfo[i].discoveryTime = 0;
8318 }
8319
8320 articulationPoints(dec, newRow, artNodeInfo, toCheck);
8321
8322 int numCutArcs = newRow->reducedMembers[toCheck].numCutArcs;
8323 for( int i = 0; i < newRow->numArticulationNodes; i++ )
8324 {
8325 spqr_node articulationNode = newRow->articulationNodes[i];
8326 assert(nodeIsRepresentative(dec, articulationNode));
8327 SCIP_Bool isOnPath = nodeNumPaths[articulationNode] == numCutArcs;
8328 if( isOnPath &&
8329 (( SPQRnodeIsInvalid(firstNode) && SPQRnodeIsInvalid(secondNode)) || articulationNode == firstNode ||
8330 articulationNode == secondNode ) )
8331 {
8332 SCIP_Bool isGood = TRUE;
8333 rigidConnectedColoring(dec, newRow, toCheck, articulationNode, &isGood);
8334 if( !isGood )
8335 {
8336 continue;
8337 }
8338 newRow->reducedMembers[toCheck].splitNode = articulationNode;
8339
8340 //Once we have found one node, we can (possibly) find another by looking at the coloring of the neighbourhood of it.
8341
8342 spqr_arc adjacentSplittingArc = SPQR_INVALID_ARC;
8343 spqr_node adjacentSplittingNode = checkNeighbourColoringArticulationNode(dec, newRow, articulationNode,
8344 &adjacentSplittingArc);
8345
8346 //If the neighbour-coloring works, we still need to check that the adjacent node
8347 //is also an articulation node
8348 if( SPQRnodeIsValid(adjacentSplittingNode) &&
8349 (( SPQRnodeIsInvalid(firstNode) && SPQRnodeIsInvalid(secondNode)) || adjacentSplittingNode == firstNode ||
8350 adjacentSplittingNode == secondNode ) )
8351 {
8352 SCIP_Bool isArticulationNode = FALSE;
8353 for( int j = 0; j < newRow->numArticulationNodes; ++j )
8354 {
8355 if( newRow->articulationNodes[j] == adjacentSplittingNode )
8356 {
8357 isArticulationNode = TRUE;
8358 break;
8359 }
8360 }
8361 if( isArticulationNode )
8362 {
8363 newRow->reducedMembers[toCheck].articulationArc = adjacentSplittingArc;
8364 newRow->reducedMembers[toCheck].otherNode = adjacentSplittingNode;
8365 newRow->reducedMembers[toCheck].otherIsSource =
8366 newRow->nodeColors[adjacentSplittingNode] == COLOR_SOURCE;
8367
8368 //Cleaning up the colors
8369 {
8370 spqr_arc firstNodeArc = getFirstNodeArc(dec, articulationNode);
8371 spqr_arc itArc = firstNodeArc;
8372 do
8373 {
8374 spqr_node head = findArcHead(dec, itArc);
8375 spqr_node tail = findArcTail(dec, itArc);
8376 spqr_node otherNode = articulationNode == head ? tail : head;
8377 newRow->nodeColors[otherNode] = UNCOLORED;
8378 itArc = getNextNodeArc(dec, itArc, articulationNode);
8379 }
8380 while( itArc != firstNodeArc );
8381 }
8382 }
8383 }
8384 return;
8385 }
8386 }
8387
8388 newRow->reducedMembers[toCheck].type = TYPE_NOT_NETWORK;
8389 newRow->remainsNetwork = FALSE;
8390}
8391
8392/** Checks if a leaf parallel member can be propagated away from the reduced decomposition. */
8393static
8395 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
8396 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
8397 const reduced_member_id toCheckMember, /**< The parallel leaf member to check */
8398 const spqr_arc markerToOther, /**< Marker to the non-leaf member */
8399 const reduced_member_id otherMember, /**< The connected non-leaf member */
8400 const spqr_arc markerToCheck /**< Marker from the non-leaf to the leaf parallel member */
8401 )
8402{
8403 SPQRRowReducedMember* member = &newRow->reducedMembers[toCheckMember];
8404 assert(cutArcIsValid(member->firstCutArc));
8405
8406 SCIP_Bool good = TRUE;
8407 SCIP_Bool isReversed = TRUE;
8408 int countedCutArcs = 0;
8409 for( cut_arc_id cutArc = member->firstCutArc; cutArcIsValid(cutArc);
8410 cutArc = newRow->cutArcs[cutArc].nextMember )
8411 {
8412 spqr_arc arc = newRow->cutArcs[cutArc].arc;
8413 SCIP_Bool arcIsReversed = arcIsReversedNonRigid(dec, arc) != newRow->cutArcs[cutArc].arcReversed;
8414 if( countedCutArcs == 0 )
8415 {
8416 isReversed = arcIsReversed;
8417 }
8418 else if( arcIsReversed != isReversed )
8419 {
8420 good = FALSE;
8421 break;
8422 }
8423 ++countedCutArcs;
8424 }
8425 if( !good )
8426 {
8427 member->type = TYPE_NOT_NETWORK;
8428 newRow->remainsNetwork = FALSE;
8429 return;
8430 }
8431 //we can only propagate if the marker arc is a tree arc and all other arcs are cut
8432 if( !arcIsTree(dec, markerToOther) ||
8433 countedCutArcs != ( getNumMemberArcs(dec, newRow->reducedMembers[toCheckMember].member) - 1 ) )
8434 {
8435 //In all other cases, the bond can be split so that the result will be okay!
8436 newRow->reducedMembers[toCheckMember].type = TYPE_MERGED;
8437 }
8438 else
8439 {
8440 SCIP_Bool markerIsReversed = arcIsReversedNonRigid(dec, markerToOther);
8441 createCutArc(dec, newRow, markerToCheck, otherMember, markerIsReversed != isReversed);
8442 newRow->reducedMembers[toCheckMember].type = TYPE_PROPAGATED;
8443 }
8444}
8445
8446/** Checks if a leaf series member can be propagated away from the reduced decomposition. */
8447static
8449 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
8450 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
8451 const reduced_member_id toCheckMember, /**< The series leaf member to check */
8452 const spqr_arc markerToOther, /**< Marker to the non-leaf member */
8453 const reduced_member_id otherMember, /**< The connected non-leaf member */
8454 const spqr_arc markerToCheck /**< Marker from the non-leaf to the leaf series member */
8455 )
8456{
8457 //Propagation only calls this function if the arc is tree already, so we do not check it here.
8458 assert(arcIsTree(dec, markerToOther));
8459 assert(newRow->reducedMembers[toCheckMember].numCutArcs == 1);
8460 assert(cutArcIsValid(newRow->reducedMembers[toCheckMember].firstCutArc));
8461 spqr_arc cutArc = newRow->reducedMembers[toCheckMember].firstCutArc;
8462 spqr_arc arc = newRow->cutArcs[cutArc].arc;
8463 newRow->reducedMembers[toCheckMember].type = TYPE_PROPAGATED;
8464 createCutArc(dec, newRow, markerToCheck, otherMember,
8465 ( arcIsReversedNonRigid(dec, arc) == arcIsReversedNonRigid(dec, markerToOther)) !=
8466 newRow->cutArcs[cutArc].arcReversed);
8467}
8468
8469/** Checks if a leaf rigid member can be propagated away from the reduced decomposition. */
8470static
8472 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
8473 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
8474 const reduced_member_id toCheckMember, /**< The rigid leaf member to check */
8475 const spqr_arc markerToOther, /**< Marker to the non-leaf member */
8476 const reduced_member_id otherMember, /**< The connected non-leaf member */
8477 const spqr_arc markerToCheck /**< Marker from the non-leaf to the rigid leaf member */
8478 )
8479{
8480 //Checking for propagation only makes sense if there is at least one cut arc
8481 assert(newRow->reducedMembers[toCheckMember].numCutArcs > 0);
8482
8483 rigidFindStarNodes(dec, newRow, toCheckMember);
8484 if( !newRow->remainsNetwork )
8485 {
8486 return;
8487 }
8488 spqr_node markerHead = findArcHead(dec, markerToOther);
8489 spqr_node markerTail = findArcTail(dec, markerToOther);
8490 if( findArcSign(dec, markerToOther).reversed )
8491 {
8492 spqr_node temp = markerHead;
8493 markerHead = markerTail;
8494 markerTail = temp;
8495 }
8496 if( SPQRnodeIsInvalid(newRow->reducedMembers[toCheckMember].splitNode) )
8497 {
8498 //not a star => attempt to find splittable nodes using articulation node algorithms
8499 //We save some work by telling the methods that only the marker nodes should be checked
8500 rigidGetSplittableArticulationPointsOnPath(dec, newRow, toCheckMember, markerHead, markerTail);
8501 }
8502 if( !newRow->remainsNetwork )
8503 {
8504 return;
8505 }
8506
8507 if( SPQRarcIsValid(newRow->reducedMembers[toCheckMember].articulationArc) &&
8508 newRow->reducedMembers[toCheckMember].articulationArc == markerToOther )
8509 {
8510 newRow->reducedMembers[toCheckMember].type = TYPE_PROPAGATED;
8511 SCIP_Bool reverse = ( markerHead == newRow->reducedMembers[toCheckMember].splitNode ) !=
8512 newRow->reducedMembers[toCheckMember].otherIsSource;
8513
8514 createCutArc(dec, newRow, markerToCheck, otherMember, reverse);
8515 }
8516 else if( newRow->reducedMembers[toCheckMember].splitNode == markerHead ||
8517 newRow->reducedMembers[toCheckMember].splitNode == markerTail )
8518 {
8519 newRow->reducedMembers[toCheckMember].type = TYPE_MERGED;
8520 }
8521 else if( SPQRarcIsValid(newRow->reducedMembers[toCheckMember].articulationArc) &&
8522 ( newRow->reducedMembers[toCheckMember].otherNode == markerHead ||
8523 newRow->reducedMembers[toCheckMember].otherNode == markerTail ) )
8524 {
8525 newRow->reducedMembers[toCheckMember].type = TYPE_MERGED;
8526 }
8527 else
8528 {
8529 //Found source or sinks, but not adjacent to the marker
8530 newRow->reducedMembers[toCheckMember].type = TYPE_NOT_NETWORK;
8531 newRow->remainsNetwork = FALSE;
8532 }
8533}
8534
8535/** Checks if a leaf member can be propagated away from the reduced decomposition. */
8536static
8538 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
8539 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
8540 const reduced_member_id toCheckMember, /**< The leaf member to check */
8541 const spqr_arc markerToOther, /**< Marker to the non-leaf member */
8542 const reduced_member_id otherMember, /**< The connected non-leaf member */
8543 const spqr_arc markerToCheck /**< Marker from the non-leaf to the leaf member */
8544 )
8545{
8546 assert(newRow->reducedMembers[toCheckMember].type == TYPE_UNDETERMINED);
8547 switch( getMemberType(dec, newRow->reducedMembers[toCheckMember].member) )
8548 {
8550 {
8551 determineRigidType(dec, newRow, toCheckMember, markerToOther, otherMember, markerToCheck);
8552 break;
8553 }
8556 {
8557 determineParallelType(dec, newRow, toCheckMember, markerToOther, otherMember, markerToCheck);
8558 break;
8559 }
8561 {
8562 determineSeriesType(dec, newRow, toCheckMember, markerToOther, otherMember, markerToCheck);
8563 break;
8564 }
8566 default:
8567 newRow->reducedMembers[toCheckMember].type = TYPE_NOT_NETWORK;
8568 SCIPABORT();
8569 }
8570
8571 return newRow->reducedMembers[toCheckMember].type;
8572}
8573
8574/** Propagates away all leaf members that are propagatable to shrink the reduced decomposition. */
8575static
8577 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
8578 SCIP_NETROWADD* newRow /**< The network matrix row addition data structure */
8579 )
8580{
8581 int leafArrayIndex = 0;
8582
8583 reduced_member_id leaf;
8584 reduced_member_id next;
8585
8586 while( leafArrayIndex != newRow->numLeafMembers )
8587 {
8588 leaf = newRow->leafMembers[leafArrayIndex];
8589 //next is invalid if the member is not in the reduced decomposition.
8590 next = newRow->reducedMembers[leaf].parent;
8591 spqr_arc parentMarker = markerToParent(dec, newRow->reducedMembers[leaf].member);
8592 if( next != INVALID_REDUCED_MEMBER && arcIsTree(dec, parentMarker) )
8593 {
8594 assert(reducedMemberIsValid(next));
8595 assert(SPQRarcIsValid(parentMarker));
8596 RowReducedMemberType type = determineType(dec, newRow, leaf, parentMarker, next,
8597 markerOfParent(dec, newRow->reducedMembers[leaf].member));
8598 if( type == TYPE_PROPAGATED )
8599 {
8600 ++newRow->reducedMembers[next].numPropagatedChildren;
8601 if( newRow->reducedMembers[next].numPropagatedChildren == newRow->reducedMembers[next].numChildren )
8602 {
8603 newRow->leafMembers[leafArrayIndex] = next;
8604 }
8605 else
8606 {
8607 ++leafArrayIndex;
8608 }
8609 }
8610 else if( type == TYPE_NOT_NETWORK )
8611 {
8612 return;
8613 }
8614 else
8615 {
8616 assert(type == TYPE_MERGED);
8617 ++leafArrayIndex;
8618 }
8619 }
8620 else
8621 {
8622 ++leafArrayIndex;
8623 }
8624 }
8625
8626 for( int j = 0; j < newRow->numReducedComponents; ++j )
8627 {
8628 //The reduced root might be a leaf as well: we propagate it last
8629 reduced_member_id root = newRow->reducedComponents[j].root;
8630
8631 while( TRUE ) /*lint !e716*/
8632 {
8633 if( newRow->reducedMembers[root].numPropagatedChildren == newRow->reducedMembers[root].numChildren - 1 )
8634 {
8635 //TODO: bit ugly, have to do a linear search for the child
8637 spqr_arc markerToChild = SPQR_INVALID_ARC;
8638 for( children_idx i = newRow->reducedMembers[root].firstChild;
8639 i < newRow->reducedMembers[root].firstChild + newRow->reducedMembers[root].numChildren; ++i )
8640 {
8641 reduced_member_id childReduced = newRow->childrenStorage[i];
8642 if( newRow->reducedMembers[childReduced].type != TYPE_PROPAGATED )
8643 {
8644 child = childReduced;
8645 markerToChild = markerOfParent(dec, newRow->reducedMembers[child].member);
8646 break;
8647 }
8648 }
8649 assert(SPQRmemberIsValid(newRow->reducedMembers[child].member));
8650 assert(SPQRarcIsValid(markerToChild));
8651 if( !arcIsTree(dec, markerToChild) )
8652 {
8653 break;
8654 }
8655 RowReducedMemberType type = determineType(dec, newRow, root, markerToChild, child,
8656 markerToParent(dec, newRow->reducedMembers[child].member));
8657 if( type == TYPE_PROPAGATED )
8658 {
8659 root = child;
8660 }
8661 else if( type == TYPE_NOT_NETWORK )
8662 {
8663 return;
8664 }
8665 else
8666 {
8667 break;
8668 }
8669 }
8670 else
8671 {
8672 break;
8673 }
8674 }
8675 newRow->reducedComponents[j].root = root;
8677 }
8678}
8679
8680/** A data structure representing a node pair. */
8681typedef struct
8682{
8685} Nodes;
8686
8687/** Computes the intersection nodes of all markers that point to reduced tree members.
8688 *
8689 * These are the only nodes that may become Y-splittable, after propagation.
8690 */
8691static
8693 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
8694 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
8695 const reduced_member_id toCheck /**< The rigid member to check */
8696 )
8697{
8698 Nodes pair;
8699 pair.first = SPQR_INVALID_NODE;
8701
8702 //take union of children's arcs nodes to find one or two candidates
8703 for( children_idx i = newRow->reducedMembers[toCheck].firstChild;
8704 i < newRow->reducedMembers[toCheck].firstChild + newRow->reducedMembers[toCheck].numChildren; ++i )
8705 {
8706 reduced_member_id reducedChild = newRow->childrenStorage[i];
8707 if( newRow->reducedMembers[reducedChild].type != TYPE_PROPAGATED )
8708 {
8709 spqr_arc arc = markerOfParent(dec, newRow->reducedMembers[reducedChild].member);
8710 spqr_node head = findArcHead(dec, arc);
8711 spqr_node tail = findArcTail(dec, arc);
8712 if( SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second) )
8713 {
8714 pair.first = head;
8715 pair.second = tail;
8716 }
8717 else
8718 {
8719 if( pair.first != head && pair.first != tail )
8720 {
8721 pair.first = SPQR_INVALID_NODE;
8722 }
8723 if( pair.second != head && pair.second != tail )
8724 {
8726 }
8727 }
8728 if( SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second) )
8729 {
8730 return pair;
8731 }
8732 }
8733 }
8734 if( reducedMemberIsValid(newRow->reducedMembers[toCheck].parent) &&
8735 newRow->reducedMembers[newRow->reducedMembers[toCheck].parent].type != TYPE_PROPAGATED )
8736 {
8737 spqr_arc arc = markerToParent(dec, newRow->reducedMembers[toCheck].member);
8738 spqr_node head = findArcHead(dec, arc);
8739 spqr_node tail = findArcTail(dec, arc);
8740 if( SPQRnodeIsInvalid(pair.first) && SPQRnodeIsInvalid(pair.second) )
8741 {
8742 pair.first = head;
8743 pair.second = tail;
8744 }
8745 else
8746 {
8747 if( pair.first != head && pair.first != tail )
8748 {
8749 pair.first = SPQR_INVALID_NODE;
8750 }
8751 if( pair.second != head && pair.second != tail )
8752 {
8754 }
8755 }
8756 }
8757 return pair;
8758}
8759
8760/** Allocates memory for various procedures that need to do tree-search for rigid members (e.g. DFS or BFS). */
8761static
8763 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
8764 SCIP_NETROWADD* newRow /**< The network matrix row addition data structure */
8765 )
8766{
8767 int necessarySpace = newRow->numReducedMembers;
8768 if( necessarySpace > newRow->memMergeTreeCallData )
8769 {
8770 int updatedSize = MAX(2 * newRow->memMergeTreeCallData, necessarySpace);
8772 updatedSize) );
8773 newRow->memMergeTreeCallData = updatedSize;
8774 }
8775 return SCIP_OKAY;
8776}
8777
8778/** Determine the type of a rigid member when it is the only member in the reduced decomposition. */
8779static
8781 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
8782 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
8783 reduced_member_id reducedMember /**< The rigid member to determine the type for */
8784 )
8785{
8786 //Checking for propagation only makes sense if there is at least one cut arc
8787 assert(newRow->reducedMembers[reducedMember].numCutArcs > 0);
8788
8789 rigidFindStarNodes(dec, newRow, reducedMember);
8790 if( !newRow->remainsNetwork )
8791 {
8792 return;
8793 }
8794
8795 if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedMember].splitNode) )
8796 {
8797 //not a star => attempt to find splittable nodes using articulation node algorithms
8799 }
8800 if( SPQRnodeIsValid(newRow->reducedMembers[reducedMember].splitNode) )
8801 {
8802 newRow->reducedMembers[reducedMember].type = TYPE_MERGED;
8803 }
8804 else
8805 {
8806 newRow->reducedMembers[reducedMember].type = TYPE_NOT_NETWORK;
8807 newRow->remainsNetwork = FALSE;
8808 }
8809}
8810
8811/** Determine the type of a parallel member when it is the only member in the reduced decomposition. */
8812static
8814 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
8815 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
8816 reduced_member_id reducedMember /**< The parallel member to determine the type for */
8817 )
8818{
8819 SPQRRowReducedMember* redMember = &newRow->reducedMembers[reducedMember];
8820 assert(cutArcIsValid(redMember->firstCutArc));
8821
8822 SCIP_Bool good = TRUE;
8823 SCIP_Bool isReversed = TRUE;
8824 int countedCutArcs = 0;
8825 for( cut_arc_id cutArc = redMember->firstCutArc; cutArcIsValid(cutArc);
8826 cutArc = newRow->cutArcs[cutArc].nextMember )
8827 {
8828 spqr_arc arc = newRow->cutArcs[cutArc].arc;
8829 SCIP_Bool arcIsReversed = arcIsReversedNonRigid(dec, arc) != newRow->cutArcs[cutArc].arcReversed;
8830 if( countedCutArcs == 0 )
8831 {
8832 isReversed = arcIsReversed;
8833 }
8834 else if( arcIsReversed != isReversed )
8835 {
8836 good = FALSE;
8837 break;
8838 }
8839 ++countedCutArcs;
8840 }
8841 if( !good )
8842 {
8843 redMember->type = TYPE_NOT_NETWORK;
8844 newRow->remainsNetwork = FALSE;
8845 }
8846 else
8847 {
8848 redMember->type = TYPE_MERGED;
8849 }
8850}
8851
8852/** Determine the type of a series member when it is the only member in the reduced decomposition. */
8853static
8855 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
8856 reduced_member_id reducedMember /**< The series member to determine the type for */
8857 )
8858{
8859 assert(newRow->reducedMembers[reducedMember].numCutArcs == 1);
8860 assert(cutArcIsValid(newRow->reducedMembers[reducedMember].firstCutArc));
8861 newRow->reducedMembers[reducedMember].type = TYPE_MERGED;
8862}
8863
8864/** Sets the given split nodes; performs bookkeeping so that we have an easier time updating the graph in many
8865 * edge cases.
8866 *
8867 * Algorithmically speaking, does nothing important.
8868 */
8869static
8871 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
8872 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
8873 reduced_member_id id, /**< The reduced member that we compute the split node for */
8874 spqr_node candidateOne, /**< The first candidate node */
8875 spqr_node candidateTwo /**< The second candidate node */
8876 )
8877{
8879 {
8880 return SPQR_INVALID_NODE;
8881 }
8882 spqr_node splitNode = newRow->reducedMembers[id].splitNode;
8883 if( splitNode == candidateOne || splitNode == candidateTwo )
8884 {
8885 if( newRow->reducedMembers[id].allHaveCommonNode )
8886 {
8887 spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode);
8888 spqr_arc iterArc = firstNodeArc;
8890 do
8891 {
8892 spqr_node head = findArcHead(dec, iterArc);
8893 spqr_node other = head == splitNode ? findArcTail(dec, iterArc) : head;
8894 newRow->nodeColors[other] = color;
8895 iterArc = getNextNodeArc(dec, iterArc, splitNode);
8896 }
8897 while( iterArc != firstNodeArc );
8898 newRow->reducedMembers[id].coloredNode = splitNode;
8899 }
8900 return splitNode;
8901 }
8902 splitNode = newRow->reducedMembers[id].otherNode;
8903 if( SPQRnodeIsInvalid(splitNode) || ( splitNode != candidateOne && splitNode != candidateTwo ) )
8904 {
8905 return SPQR_INVALID_NODE;
8906 }
8907 assert(SPQRarcIsValid(newRow->reducedMembers[id].articulationArc));
8908 if( newRow->reducedMembers[id].allHaveCommonNode )
8909 {
8910 spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode);
8911 spqr_arc iterArc = firstNodeArc;
8912 COLOR_STATUS color;
8913 if( newRow->reducedMembers[id].numCutArcs == 1 )
8914 {
8915 color = newRow->reducedMembers[id].otherIsSource ? COLOR_SINK : COLOR_SOURCE;
8916 }
8917 else
8918 {
8919 color = newRow->reducedMembers[id].otherIsSource ? COLOR_SOURCE : COLOR_SINK;
8920 }
8921 do
8922 {
8923 spqr_node head = findArcHead(dec, iterArc);
8924 spqr_node other = head == splitNode ? findArcTail(dec, iterArc) : head;
8925 newRow->nodeColors[other] = color;
8926 iterArc = getNextNodeArc(dec, iterArc, splitNode);
8927 }
8928 while( iterArc != firstNodeArc );
8929 newRow->nodeColors[newRow->reducedMembers[id].splitNode] = newRow->reducedMembers[id].otherIsSource ? COLOR_SINK
8930 : COLOR_SOURCE;
8931 }
8932 else
8933 {
8934 COLOR_STATUS splitColor = newRow->nodeColors[splitNode];
8935 spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode);
8936 spqr_arc iterArc = firstNodeArc;
8937 do
8938 {
8939 spqr_node head = findArcHead(dec, iterArc);
8940 spqr_node other = head == splitNode ? findArcTail(dec, iterArc) : head;
8941 newRow->nodeColors[other] = splitColor;
8942 iterArc = getNextNodeArc(dec, iterArc, splitNode);
8943 }
8944 while( iterArc != firstNodeArc );
8945 newRow->nodeColors[newRow->reducedMembers[id].splitNode] = splitColor == COLOR_SOURCE ? COLOR_SINK : COLOR_SOURCE;
8946 newRow->nodeColors[splitNode] = UNCOLORED;
8947 }
8948
8949 newRow->reducedMembers[id].coloredNode = splitNode;
8950 return splitNode;
8951}
8952
8953/** After propagation, determines the split type of the first leaf node of the reduced decomposition.
8954 *
8955 * The leaf nodes of the decomposition after propagation can only be of type P or R.
8956 */
8957static
8959 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
8960 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
8961 reduced_member_id reducedId /**< The member to determine the split type for */
8962 )
8963{
8964 spqr_member member = newRow->reducedMembers[reducedId].member;
8965 SPQRMemberType type = getMemberType(dec, member);
8966 assert(type == SPQR_MEMBERTYPE_PARALLEL || type == SPQR_MEMBERTYPE_RIGID);
8967 assert(cutArcIsValid(newRow->reducedMembers[reducedId].firstCutArc));
8968 SPQRRowReducedMember* redMember = &newRow->reducedMembers[reducedId];
8969
8970 if( type == SPQR_MEMBERTYPE_PARALLEL )
8971 {
8972 //TODO: duplicate-ish
8973
8974 SCIP_Bool good = TRUE;
8975 SCIP_Bool isReversed = TRUE;
8976 int countedCutArcs = 0;
8977 for( cut_arc_id cutArc = redMember->firstCutArc; cutArcIsValid(cutArc);
8978 cutArc = newRow->cutArcs[cutArc].nextMember )
8979 {
8980 spqr_arc arc = newRow->cutArcs[cutArc].arc;
8981 SCIP_Bool arcIsReversed = arcIsReversedNonRigid(dec, arc) != newRow->cutArcs[cutArc].arcReversed;
8982 if( countedCutArcs == 0 )
8983 {
8984 isReversed = arcIsReversed;
8985 }
8986 else if( arcIsReversed != isReversed )
8987 {
8988 good = FALSE;
8989 break;
8990 }
8991 ++countedCutArcs;
8992 }
8993 if( !good )
8994 {
8995 redMember->type = TYPE_NOT_NETWORK;
8996 newRow->remainsNetwork = FALSE;
8997 }
8998 else
8999 {
9000 spqr_arc marker = markerToParent(dec, member);
9001 redMember->type = TYPE_MERGED;
9002 redMember->splitArc = marker;
9003 redMember->splitHead = TRUE;
9004 redMember->otherIsSource = arcIsReversedNonRigid(dec, marker) == isReversed;
9005 }
9006 return;
9007 }
9008 assert(type == SPQR_MEMBERTYPE_RIGID);
9009
9010 spqr_arc marker = markerToParent(dec, member);
9011 spqr_node markerHead = findArcHead(dec, marker);
9012 spqr_node markerTail = findArcTail(dec, marker);
9013 if( findArcSign(dec, marker).reversed )
9014 {
9015 spqr_node temp = markerHead;
9016 markerHead = markerTail;
9017 markerTail = temp;
9018 }
9019
9020 if( !SPQRnodeIsValid(newRow->reducedMembers[reducedId].splitNode) )
9021 {
9022 //Checking for propagation only makes sense if there is at least one cut arc
9023 assert(newRow->reducedMembers[reducedId].numCutArcs > 0);
9024
9025 rigidFindStarNodes(dec, newRow, reducedId);
9026 if( !newRow->remainsNetwork )
9027 {
9028 return;
9029 }
9030
9031 if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode) )
9032 {
9033 //not a star => attempt to find splittable nodes using articulation node algorithms
9034 //We save some work by telling the methods that only the marker nodes should be checked
9035 rigidGetSplittableArticulationPointsOnPath(dec, newRow, reducedId, markerHead, markerTail);
9036 }
9037 if( !newRow->remainsNetwork )
9038 {
9039 return;
9040 }
9041 if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode) )
9042 {
9043 redMember->type = TYPE_NOT_NETWORK;
9044 newRow->remainsNetwork = FALSE;
9045 return;
9046 }
9047 }
9048
9049 spqr_node splitNode = newRow->reducedMembers[reducedId].splitNode;
9050#ifndef NDEBUG
9051 spqr_node otherNode = newRow->reducedMembers[reducedId].otherNode;
9052#endif
9053 //We cannot have both splittable (should have been propagated)
9054 assert(!(( splitNode == markerTail || splitNode == markerHead ) &&
9055 ( otherNode == markerTail || otherNode == markerHead )));
9056
9057 splitNode = determineAndColorSplitNode(dec, newRow, reducedId, markerHead, markerTail);
9058 if( SPQRnodeIsInvalid(splitNode) )
9059 {
9060 redMember->type = TYPE_NOT_NETWORK;
9061 newRow->remainsNetwork = FALSE;
9062 return;
9063 }
9064 assert(splitNode == markerHead || splitNode == markerTail);
9065
9066 newRow->reducedMembers[reducedId].splitNode = splitNode;
9067 newRow->reducedMembers[reducedId].willBeReversed = FALSE;
9068 redMember->type = TYPE_MERGED;
9069}
9070
9071/** 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
9072 * source or the sink partition.
9073 */
9074typedef struct
9075{
9076 SCIP_Bool headSplit; /**< Is the head or tail of the marked arc split?*/
9077 SCIP_Bool otherIsSource; /**< Is the non-split node in the source or sink partition? */
9079
9080/** Get the split orientation for a given rigid member and marked arc. */
9081static
9083 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
9084 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
9085 reduced_member_id reducedId, /**< The member to determine the split orientation for */
9086 spqr_arc arcToNext /**< The marked arc to determine the orientation for */
9087 )
9088{
9089 assert(findArcMemberNoCompression(dec, arcToNext) == newRow->reducedMembers[reducedId].member);
9090 assert(SPQRnodeIsValid(newRow->reducedMembers[reducedId].splitNode));
9091
9092 SplitOrientation orientation;
9093 if( newRow->reducedMembers[reducedId].numCutArcs == 0 )
9094 {
9095 spqr_node splitNode = newRow->reducedMembers[reducedId].splitNode;
9096 spqr_node head = findEffectiveArcHead(dec, arcToNext);
9097
9098 assert(head == splitNode || splitNode == findEffectiveArcTailNoCompression(dec, arcToNext));
9099
9100 orientation.headSplit = newRow->reducedMembers[reducedId].willBeReversed == ( head != splitNode );
9101 orientation.otherIsSource = newRow->reducedMembers[reducedId].otherIsSource;
9102 return orientation;
9103 }
9104 spqr_node splitNode = newRow->reducedMembers[reducedId].splitNode;
9105 spqr_node arcHead = findArcHead(dec, arcToNext);
9106 spqr_node arcTail = findArcTail(dec, arcToNext);
9107 if( findArcSign(dec, arcToNext).reversed )
9108 {
9109 spqr_node temp = arcHead;
9110 arcHead = arcTail;
9111 arcTail = temp;
9112 }
9113 assert(arcHead == splitNode || arcTail == splitNode);
9114 spqr_node other = arcHead == splitNode ? arcTail : arcHead;
9115
9116 assert(newRow->nodeColors[other] == COLOR_SOURCE || newRow->nodeColors[other] == COLOR_SINK);
9117
9118 if( newRow->reducedMembers[reducedId].willBeReversed )
9119 {
9120 orientation.headSplit = arcHead != splitNode;
9121 orientation.otherIsSource = newRow->nodeColors[other] != COLOR_SOURCE;
9122 }
9123 else
9124 {
9125 orientation.headSplit = arcHead == splitNode;
9126 orientation.otherIsSource = newRow->nodeColors[other] == COLOR_SOURCE;
9127 }
9128 return orientation;
9129}
9130
9131/** Get the split orientation for a given parallel member and marked arc. */
9132static
9134 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
9135 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
9136 reduced_member_id reducedId, /**< The member to determine the split orientation for */
9137 spqr_arc arcToNext /**< The marked arc to determine the orientaiton for */
9138 )
9139{
9140 assert(findArcMemberNoCompression(dec, arcToNext) == newRow->reducedMembers[reducedId].member);
9141 assert(SPQRarcIsValid(newRow->reducedMembers[reducedId].splitArc) && SPQRarcIsValid(arcToNext));
9142
9143 SplitOrientation orientation;
9144 orientation.otherIsSource = newRow->reducedMembers[reducedId].otherIsSource;
9145 if( arcIsReversedNonRigid(dec, arcToNext) == arcIsReversedNonRigid(dec, newRow->reducedMembers[reducedId].splitArc))
9146 {
9147 orientation.headSplit = newRow->reducedMembers[reducedId].splitHead;
9148 }
9149 else
9150 {
9151 orientation.headSplit = !newRow->reducedMembers[reducedId].splitHead;
9152 }
9153 return orientation;
9154}
9155
9156/** Get the split orientation for a given series member and marked arc. */
9157static
9159 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
9160 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
9161 reduced_member_id reducedId, /**< The member to determine the split orientation for */
9162 spqr_arc arcToNext /**< The marked arc to determine the orientaiton for */
9163 )
9164{
9165 assert(findArcMemberNoCompression(dec, arcToNext) == newRow->reducedMembers[reducedId].member);
9166 assert(SPQRarcIsValid(newRow->reducedMembers[reducedId].splitArc) && SPQRarcIsValid(arcToNext));
9167
9168 SplitOrientation orientation;
9169 orientation.otherIsSource = newRow->reducedMembers[reducedId].otherIsSource;
9170 if( arcIsReversedNonRigid(dec, arcToNext) == arcIsReversedNonRigid(dec, newRow->reducedMembers[reducedId].splitArc))
9171 {
9172 orientation.headSplit = !newRow->reducedMembers[reducedId].splitHead;
9173 }
9174 else
9175 {
9176 orientation.headSplit = newRow->reducedMembers[reducedId].splitHead;
9177 }
9178 return orientation;
9179}
9180
9181/** Get the split orientation for a given member and marked arc. */
9182static
9184 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
9185 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
9186 reduced_member_id reducedId, /**< The member to determine the split orientation for */
9187 spqr_arc arcToNext /**< The marked arc to determine the orientaiton for */
9188 )
9189{
9190 switch( getMemberType(dec, newRow->reducedMembers[reducedId].member) )
9191 {
9193 return getRelativeOrientationRigid(dec, newRow, reducedId, arcToNext);
9195 return getRelativeOrientationParallel(dec, newRow, reducedId, arcToNext);
9197 return getRelativeOrientationSeries(dec, newRow, reducedId, arcToNext);
9200 default:
9201 {
9202 SCIPABORT();
9203 SplitOrientation orientation; /*lint !e527*/
9204 orientation.headSplit = FALSE; /*lint !e527*/
9205 orientation.otherIsSource = FALSE;
9206 return orientation;
9207 }
9208 }
9209}
9210
9211/** Determine the split type of a series node when the SPQR tree is not a singular member. */
9212static
9214 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
9215 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
9216 reduced_member_id reducedId, /**< The member to determine the split orientation for */
9217 spqr_arc marker, /**< The marker to the previous member whose type was already determined */
9218 SplitOrientation previousOrientation /**< The orientation to the previous member */
9219 )
9220{
9221 int numAdjacentMembers =
9222 newRow->reducedMembers[reducedId].numChildren - newRow->reducedMembers[reducedId].numPropagatedChildren;
9223 if( reducedMemberIsValid(newRow->reducedMembers[reducedId].parent) &&
9224 newRow->reducedMembers[newRow->reducedMembers[reducedId].parent].type != TYPE_PROPAGATED )
9225 {
9226 ++numAdjacentMembers;
9227 }
9228 assert(numAdjacentMembers > 1);
9229 if( numAdjacentMembers > 2 )
9230 {
9231 newRow->remainsNetwork = FALSE;
9232 newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK;
9233 return;
9234 }
9235 cut_arc_id cutArc = newRow->reducedMembers[reducedId].firstCutArc;
9236 if( cutArcIsValid(cutArc) )
9237 {
9238 spqr_arc arc = newRow->cutArcs[cutArc].arc;
9239 SCIP_Bool good = ((( arcIsReversedNonRigid(dec, arc) == arcIsReversedNonRigid(dec, marker)) ==
9240 newRow->cutArcs[cutArc].arcReversed ) == previousOrientation.headSplit ) ==
9241 previousOrientation.otherIsSource;
9242 if( !good )
9243 {
9244 newRow->remainsNetwork = FALSE;
9245 newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK;
9246 return;
9247 }
9248 newRow->reducedMembers[reducedId].splitArc = marker;
9249 newRow->reducedMembers[reducedId].splitHead = previousOrientation.headSplit;
9250 newRow->reducedMembers[reducedId].otherIsSource = !previousOrientation.otherIsSource;
9251 newRow->reducedMembers[reducedId].type = TYPE_MERGED;
9252 return;
9253 }
9254
9255 newRow->reducedMembers[reducedId].splitArc = marker;
9256 newRow->reducedMembers[reducedId].splitHead = previousOrientation.headSplit;
9257 newRow->reducedMembers[reducedId].otherIsSource = previousOrientation.otherIsSource;
9258 newRow->reducedMembers[reducedId].type = TYPE_MERGED;
9259}
9260
9261/** Determine the split type of a parallel node when the SPQR tree is not a singular member. */
9262static
9264 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
9265 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
9266 reduced_member_id reducedId, /**< The member to determine the split orientation for */
9267 spqr_arc marker, /**< The marker to the previous member whose type was already determined */
9268 SplitOrientation previousOrientation /**< The orientation to the previous member */
9269 )
9270{
9271 SPQRRowReducedMember* redMember = &newRow->reducedMembers[reducedId];
9272
9273 SCIP_Bool good = TRUE;
9274 SCIP_Bool isReversed = TRUE;
9275 int countedCutArcs = 0;
9276 for( cut_arc_id cutArc = redMember->firstCutArc; cutArcIsValid(cutArc);
9277 cutArc = newRow->cutArcs[cutArc].nextMember )
9278 {
9279 spqr_arc arc = newRow->cutArcs[cutArc].arc;
9280 SCIP_Bool arcIsReversed = arcIsReversedNonRigid(dec, arc) != newRow->cutArcs[cutArc].arcReversed;
9281 if( countedCutArcs == 0 )
9282 {
9283 isReversed = arcIsReversed;
9284 }
9285 else if( arcIsReversed != isReversed )
9286 {
9287 good = FALSE;
9288 break;
9289 }
9290 ++countedCutArcs;
9291 }
9292 if( !good )
9293 {
9294 redMember->type = TYPE_NOT_NETWORK;
9295 newRow->remainsNetwork = FALSE;
9296 return;
9297 }
9298 if( countedCutArcs == 0 )
9299 {
9300 redMember->splitArc = marker;
9301 redMember->splitHead = previousOrientation.headSplit;
9302 redMember->otherIsSource = previousOrientation.otherIsSource;
9303 redMember->type = TYPE_MERGED;
9304 return;
9305 }
9306 SCIP_Bool isHeadSourceOrTailTarget = previousOrientation.headSplit == previousOrientation.otherIsSource;
9307 SCIP_Bool isOkay = isHeadSourceOrTailTarget == ( isReversed == arcIsReversedNonRigid(dec, marker));
9308 if( !isOkay )
9309 {
9310 redMember->type = TYPE_NOT_NETWORK;
9311 newRow->remainsNetwork = FALSE;
9312 return;
9313 }
9314 redMember->splitArc = marker;
9315 redMember->splitHead = previousOrientation.headSplit;
9316 redMember->otherIsSource = previousOrientation.otherIsSource;
9317 redMember->type = TYPE_MERGED;
9318}
9319
9320/** Determine the split type of a rigid node when the SPQR tree is not a singular member. */
9321static
9323 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
9324 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
9325 reduced_member_id reducedId, /**< The reduced member to determine the split orientation for */
9326 spqr_member member, /**< The member belonging to the reduced member */
9327 spqr_arc marker, /**< The marker to the previous member whose type was already determined */
9328 SplitOrientation previousOrientation /**< The orientation to the previous member */
9329 )
9330{
9331 assert(dec);
9332 assert(newRow);
9333 assert(getMemberType(dec, member) == SPQR_MEMBERTYPE_RIGID);
9334
9335 Nodes nodes = rigidDetermineCandidateNodesFromAdjacentComponents(dec, newRow, reducedId);
9336 if( SPQRnodeIsInvalid(nodes.first) && SPQRnodeIsInvalid(nodes.second) )
9337 {
9338 newRow->remainsNetwork = FALSE;
9339 newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK;
9340 return;
9341 }
9342 if( SPQRnodeIsInvalid(nodes.first) && SPQRnodeIsValid(nodes.second) )
9343 {
9344 nodes.first = nodes.second;
9345 nodes.second = SPQR_INVALID_NODE;
9346 }
9347
9348 spqr_node markerHead = findArcHead(dec, marker);
9349 spqr_node markerTail = findArcTail(dec, marker);
9350 if( findArcSign(dec, marker).reversed )
9351 {
9352 spqr_node temp = markerHead;
9353 markerHead = markerTail;
9354 markerTail = temp;
9355 }
9356
9357 if( newRow->reducedMembers[reducedId].numCutArcs == 0 )
9358 {
9359 assert(SPQRnodeIsInvalid(nodes.second));//There must be at least two adjacent components
9360 if( nodes.first != markerHead && nodes.first != markerTail )
9361 {
9362 newRow->remainsNetwork = FALSE;
9363 newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK;
9364 return;
9365 }
9366 SCIP_Bool reverse = previousOrientation.headSplit == ( nodes.first == markerTail );
9367 newRow->reducedMembers[reducedId].splitNode = nodes.first;
9368 newRow->reducedMembers[reducedId].otherIsSource = previousOrientation.otherIsSource;
9369 newRow->reducedMembers[reducedId].willBeReversed = reverse;
9370 newRow->reducedMembers[reducedId].type = TYPE_MERGED;
9371 return;
9372 }
9373 if( !SPQRnodeIsValid(newRow->reducedMembers[reducedId].splitNode) )
9374 {
9375 //Checking for propagation only makes sense if there is at least one cut arc
9376 assert(newRow->reducedMembers[reducedId].numCutArcs > 0);
9377
9378 rigidFindStarNodes(dec, newRow, reducedId);
9379 if( !newRow->remainsNetwork )
9380 {
9381 return;
9382 }
9383
9384 if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode) )
9385 {
9386 //not a star => attempt to find splittable nodes using articulation node algorithms
9387 //We save some work by telling the methods that only the marker nodes should be checked
9388 rigidGetSplittableArticulationPointsOnPath(dec, newRow, reducedId, nodes.first, nodes.second);
9389 }
9390 if( !newRow->remainsNetwork )
9391 {
9392 return;
9393 }
9394 if( SPQRnodeIsInvalid(newRow->reducedMembers[reducedId].splitNode) )
9395 {
9396 newRow->remainsNetwork = FALSE;
9397 newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK;
9398 return;
9399 }
9400 }
9401 spqr_node splitNode = determineAndColorSplitNode(dec, newRow, reducedId, nodes.first, nodes.second);
9402
9403 if( SPQRnodeIsInvalid(splitNode) )
9404 {
9405 newRow->remainsNetwork = FALSE;
9406 newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK;
9407 return;
9408 }
9409 assert(splitNode == nodes.first || splitNode == nodes.second);
9410 assert(splitNode == markerHead || splitNode == markerTail);
9411
9412 spqr_node otherNode = splitNode == markerHead ? markerTail : markerHead;
9413 SCIP_Bool headsMatch = previousOrientation.headSplit == ( splitNode == markerHead );
9414
9415 COLOR_STATUS otherColor = newRow->nodeColors[otherNode];
9416 assert(otherColor == COLOR_SOURCE || otherColor == COLOR_SINK);
9417 SCIP_Bool otherIsSource = otherColor == COLOR_SOURCE;
9418
9419 SCIP_Bool good = headsMatch == ( previousOrientation.otherIsSource == otherIsSource );
9420
9421 if( !good )
9422 {
9423 newRow->remainsNetwork = FALSE;
9424 newRow->reducedMembers[reducedId].type = TYPE_NOT_NETWORK;
9425 return;
9426 }
9427
9428 newRow->reducedMembers[reducedId].splitNode = splitNode;
9429 newRow->reducedMembers[reducedId].willBeReversed = !headsMatch;
9430 newRow->reducedMembers[reducedId].type = TYPE_MERGED;
9431}
9432
9433/** Determine the split type of a node when the SPQR tree is not a singular member.
9434 *
9435 * This function is used for all the nodes that are not first in the given ordering.
9436 */
9437static
9439 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
9440 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
9441 reduced_member_id current, /**< The current node, whose type has already been determined */
9442 reduced_member_id next, /**< The next node to determine the type of, adjacent to the current node */
9443 SCIP_Bool currentIsParent /**< Is the current node a parent of the next node? */
9444 )
9445{
9446 spqr_member member = newRow->reducedMembers[next].member;
9447 SPQRMemberType type = getMemberType(dec, member);
9448 spqr_arc nextArc = currentIsParent ? markerToParent(dec, member) : markerOfParent(dec,
9449 newRow->reducedMembers[current].member);
9450 spqr_arc currentArc = currentIsParent ? markerOfParent(dec, member) : markerToParent(dec,
9451 newRow->reducedMembers[current].member);
9452 SplitOrientation orientation = getRelativeOrientation(dec, newRow, current, currentArc);
9453 assert(type == SPQR_MEMBERTYPE_PARALLEL || type == SPQR_MEMBERTYPE_RIGID || type == SPQR_MEMBERTYPE_SERIES);
9454 switch( type )
9455 {
9457 {
9458 determineSplitTypeRigid(dec, newRow, next, member, nextArc, orientation);
9459 break;
9460 }
9462 {
9463 determineSplitTypeParallel(dec, newRow, next, nextArc, orientation);
9464 break;
9465 }
9467 {
9468 determineSplitTypeSeries(dec, newRow, next, nextArc, orientation);
9469 break;
9470 }
9471
9474 default:
9475 newRow->remainsNetwork = FALSE;
9476 SCIPABORT();
9477 }
9478}
9479
9480/** For the given root reduced member of a component, determine if the component is updateable/transformable. */
9481static
9483 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
9484 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
9485 reduced_member_id root /**< The root of the given component */
9486 )
9487{
9488 assert(newRow->numReducedMembers <= newRow->memMergeTreeCallData);
9489 if( newRow->reducedMembers[root].numPropagatedChildren == newRow->reducedMembers[root].numChildren )
9490 {
9491 //Determine single component;
9492 if( newRow->reducedMembers[root].type == TYPE_UNDETERMINED )
9493 {
9494 spqr_member member = newRow->reducedMembers[root].member;
9495 switch( getMemberType(dec, member) )
9496 {
9498 determineSingleRowRigidType(dec, newRow, root);
9499 break;
9501 determineSingleParallelType(dec, newRow, root);
9502 break;
9505 determineSingleSeriesType( newRow, root);
9506 break;
9508 default:
9509 newRow->remainsNetwork = FALSE;
9510 SCIPABORT();
9511 }
9512 }
9513 return;
9514 }
9515
9516 //Go to a leaf. We need to start in a leaf to avoid the ambiguity of choosing an orientation
9517 //in members which have no cut arcs; otherwise, we might choose the wrong one
9518 reduced_member_id leaf = root;
9519 while( newRow->reducedMembers[leaf].numChildren != newRow->reducedMembers[leaf].numPropagatedChildren )
9520 {
9521 for( int i = 0; i < newRow->reducedMembers[leaf].numChildren; ++i )
9522 {
9523 children_idx idx = newRow->reducedMembers[leaf].firstChild + i;
9524 reduced_member_id child = newRow->childrenStorage[idx];
9525 if( newRow->reducedMembers[child].type != TYPE_PROPAGATED )
9526 {
9527 leaf = child;
9528 break;
9529 }
9530 }
9531 }
9532 determineSplitTypeFirstLeaf(dec, newRow, leaf);
9533
9534 if( !newRow->remainsNetwork )
9535 {
9536 return;
9537 }
9538 reduced_member_id baseNode = leaf;
9539 reduced_member_id nextNode = newRow->reducedMembers[baseNode].parent;
9540
9541 while( reducedMemberIsValid(nextNode) )
9542 {
9543 //check this node
9544 determineSplitTypeNext(dec, newRow, baseNode, nextNode, FALSE);
9545 if( !newRow->remainsNetwork )
9546 {
9547 return;
9548 }
9549 //Add other nodes in the subtree
9550 //use a while loop to avoid recursion; we may get stack overflows for large graphs
9551 MergeTreeCallData* data = newRow->mergeTreeCallData;
9552
9553 data[0].id = nextNode;
9554 data[0].currentChild = newRow->reducedMembers[nextNode].firstChild ;
9555 int depth = 0;
9556 while( depth >= 0 )
9557 {
9558 reduced_member_id id = data[depth].id;
9559 children_idx childidx = data[depth].currentChild;
9560 if( childidx == newRow->reducedMembers[id].firstChild + newRow->reducedMembers[id].numChildren )
9561 {
9562 --depth;
9563 continue;
9564 }
9565 reduced_member_id currentchild = newRow->childrenStorage[childidx];
9566 data[depth].currentChild += 1;
9567 //skip this child if we already processed it or it is not merged
9568 if( currentchild == baseNode || newRow->reducedMembers[currentchild].type == TYPE_PROPAGATED )
9569 {
9570 continue;
9571 }
9572 determineSplitTypeNext(dec,newRow,id,currentchild,TRUE);
9573 if( !newRow->remainsNetwork )
9574 {
9575 return;
9576 }
9577 //recursively process the child
9578 depth += 1;
9579 assert(depth < newRow->memMergeTreeCallData);
9580 data[depth].id = currentchild;
9581 data[depth].currentChild = newRow->reducedMembers[currentchild].firstChild;
9582 }
9583 //Move up one layer
9584 baseNode = nextNode;
9585 nextNode = newRow->reducedMembers[nextNode].parent;
9586 }
9587}
9588
9589/** Empty the new member information array. */
9590static
9592 SCIP_NETROWADD* newRow /**< The network matrix row addition data structure */
9593 )
9594{
9595 //This loop is at the end as memberInformation is also used to assign the cut arcs during propagation
9596 //Clean up the memberInformation array
9597 for( int i = 0; i < newRow->numReducedMembers; ++i )
9598 {
9600 }
9601#ifndef NDEBUG
9602 for( int i = 0; i < newRow->memMemberInformation; ++i )
9603 {
9605 }
9606#endif
9607}
9608
9609/** Transforms a rigid arc by putting it in series with the new column arc.
9610 *
9611 * We need to do some magic to keep our the internal datastructures consistent in this case.
9612 */
9613static
9615 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
9616 const spqr_member member, /**< The rigid member that contains the arc that is moved */
9617 const spqr_arc arc, /**< The arc that is moved to a new series member */
9618 const SCIP_Bool reverseArcDirection,/**< Is the given arc reversed? */
9619 NewRowInformation* const newRowInfo /**< Stores information on how to add the new row */
9620 )
9621{
9622 //If a cycle already exists, just expand it with the new arc.
9623 spqr_member markerCycleMember = SPQR_INVALID_MEMBER;
9624 spqr_arc markerCycleArc = SPQR_INVALID_ARC;
9625 SCIP_Bool isParent = arc == markerToParent(dec, member);
9626 spqr_member adjacentMember = SPQR_INVALID_MEMBER;
9627 if( isParent )
9628 {
9629 adjacentMember = findMemberParent(dec, member);
9630 if( getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_SERIES )
9631 {
9632 markerCycleMember = adjacentMember;
9633 markerCycleArc = markerOfParent(dec, member);
9634 }
9635 }
9636 else if( arcIsChildMarker(dec, arc) )
9637 {
9638 adjacentMember = findArcChildMember(dec, arc);
9639 if( getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_SERIES )
9640 {
9641 markerCycleMember = adjacentMember;
9642 markerCycleArc = markerToParent(dec, adjacentMember);
9643 }
9644 }
9645 if( markerCycleMember != SPQR_INVALID_MEMBER )
9646 {
9647 newRowInfo->member = markerCycleMember;
9648 if( arcIsReversedNonRigid(dec, markerCycleArc) )
9649 {
9650 newRowInfo->reversed = reverseArcDirection;
9651 }
9652 else
9653 {
9654 newRowInfo->reversed = !reverseArcDirection;
9655 }
9656
9657 return SCIP_OKAY;
9658 }
9659
9660 //Otherwise, we create a new cycle
9661 spqr_member newCycle;
9662 SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_SERIES, &newCycle) );
9663 //We would like to move the edge but unfortunately cannot do so without destroying the arc union-find datastructure.
9664 //Thus, we 'convert' the arc into a marker and add the new series
9665
9666 spqr_arc duplicate = SPQR_INVALID_ARC;
9667 spqr_element element = arcGetElement(dec, arc);
9668 if( element != MARKER_COLUMN_ELEMENT && element != MARKER_ROW_ELEMENT )
9669 {
9670 if( SPQRelementIsColumn(element) )
9671 {
9672 SCIP_CALL( createColumnArc(dec, newCycle, &duplicate, SPQRelementToColumn(element), TRUE) );
9673 }
9674 else
9675 {
9676 SCIP_CALL( createRowArc(dec, newCycle, &duplicate, SPQRelementToRow(element), TRUE) );
9677 }
9678 }
9679 else if( isParent )
9680 {
9681 //create parent marker
9682 SCIP_CALL( createParentMarker(dec, newCycle, arcIsTree(dec, arc), adjacentMember,
9683 markerOfParent(dec, member), &duplicate, TRUE) );
9684 }
9685 else
9686 {
9687 //create child marker
9688 SCIP_CALL( createChildMarker(dec, newCycle, adjacentMember, arcIsTree(dec, arc), &duplicate, TRUE) );
9689 dec->members[adjacentMember].parentMember = newCycle;
9690 dec->members[adjacentMember].markerOfParent = duplicate;
9691 }
9692 //Create the other marker edge
9693 spqr_arc cycleMarker = SPQR_INVALID_ARC;
9694 if( isParent )
9695 {
9696 SCIP_CALL( createChildMarker(dec, newCycle, member, !arcIsTree(dec, arc),
9697 &cycleMarker, FALSE) );
9698 }
9699 else
9700 {
9701 SCIP_CALL( createParentMarker(dec, newCycle, !arcIsTree(dec, arc),
9702 member, arc, &cycleMarker, FALSE) );
9703 }
9704 //Change the existing edge to a marker
9705 if( isParent )
9706 {
9707 assert(markerToParent(dec, member) == arc);
9708 dec->arcs[markerOfParent(dec, member)].childMember = newCycle;
9709 dec->members[member].parentMember = newCycle;
9710 dec->members[member].markerToParent = arc;
9711 dec->members[member].markerOfParent = cycleMarker;
9714 }
9715 else
9716 {
9718 dec->arcs[arc].childMember = newCycle;
9719 }
9720 newRowInfo->member = newCycle;
9721 newRowInfo->reversed = !reverseArcDirection;
9722
9723 return SCIP_OKAY;
9724}
9725
9726/** Updates a single rigid member to reflect the new row. */
9727static
9729 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
9730 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
9731 const reduced_member_id reducedMember, /**< The reduced member to transform */
9732 const spqr_member member, /**< The member belonging to the reduced member */
9733 NewRowInformation* const newRowInfo /**< Stores information about the new row placement */
9734 )
9735{
9736 if( SPQRarcIsValid(newRow->reducedMembers[reducedMember].articulationArc) )
9737 {
9738 spqr_arc arc = newRow->reducedMembers[reducedMember].articulationArc;
9739 //Cut arc is propagated into a cycle with new arc
9740 assert(newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcHeadNoCompression(dec, arc) ||
9741 newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcTailNoCompression(dec, arc));
9742
9743 SCIP_Bool reversed;
9744
9745 if( newRow->isArcCut[arc] )
9746 {
9747 reversed = ( newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcHead(dec, arc)) ==
9748 newRow->reducedMembers[reducedMember].otherIsSource;
9749 }
9750 else
9751 {
9752 reversed = ( newRow->reducedMembers[reducedMember].splitNode == findEffectiveArcHead(dec, arc)) !=
9753 newRow->reducedMembers[reducedMember].otherIsSource;
9754 }
9755
9756 SCIP_CALL( rigidTransformArcIntoCycle(dec, member, newRow->reducedMembers[reducedMember].articulationArc,
9757 reversed, newRowInfo) );
9758
9759 return SCIP_OKAY;
9760 }
9761 //Single splittable node
9762 assert(SPQRnodeIsValid(newRow->reducedMembers[reducedMember].splitNode));
9763
9764 spqr_node splitNode = newRow->reducedMembers[reducedMember].splitNode;
9765 if( newRow->reducedMembers[reducedMember].allHaveCommonNode )
9766 {
9767 //Create a new node; move all cut arcs end of split node to it and add new arc between new node and split node
9768 spqr_node newNode = SPQR_INVALID_NODE;
9769 SCIP_CALL( createNode(dec, &newNode) );
9770
9771 cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc;
9772 do
9773 {
9774 spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc;
9775 spqr_node arcHead = findArcHead(dec, cutArc);
9776 if( arcHead == splitNode )
9777 {
9778 changeArcHead(dec, cutArc, arcHead, newNode);
9779 }
9780 else
9781 {
9782 changeArcTail(dec, cutArc, findArcTail(dec, cutArc), newNode);
9783 }
9784
9785 cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember;
9786 }
9787 while( cutArcIsValid(cutArcIdx) );
9788
9789 newRowInfo->member = member;
9790 if( newRow->reducedMembers[reducedMember].otherIsSource )
9791 {
9792 newRowInfo->head = newNode;
9793 newRowInfo->tail = splitNode;
9794 }
9795 else
9796 {
9797 newRowInfo->head = splitNode;
9798 newRowInfo->tail = newNode;
9799 }
9800 newRowInfo->representative = findArcSign(dec,
9801 newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc).representative;
9802 newRowInfo->reversed = FALSE;
9803
9804 return SCIP_OKAY;
9805 }
9806 //Articulation point was split (based on coloring)
9807
9808 spqr_node newNode = SPQR_INVALID_NODE;
9809 SCIP_CALL( createNode(dec, &newNode) );
9810
9811 spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode);
9812 spqr_arc iterArc = firstNodeArc;
9813
9814 do
9815 {
9816 SCIP_Bool isCut = newRow->isArcCut[iterArc];
9817 spqr_node otherHead = findArcHead(dec, iterArc);
9818 spqr_node otherTail = findArcTail(dec, iterArc);
9819 spqr_node otherEnd = otherHead == splitNode ? otherTail : otherHead;
9820 SCIP_Bool isMoveColor = newRow->nodeColors[otherEnd] == COLOR_SOURCE;
9821 spqr_arc nextArc = getNextNodeArc(dec, iterArc, splitNode);//Need to do this before we modify the arc :)
9822
9823 SCIP_Bool changeArcEnd = isCut == isMoveColor;
9824 if( changeArcEnd )
9825 {
9826 if( otherHead == splitNode )
9827 {
9828 changeArcHead(dec, iterArc, otherHead, newNode);
9829 }
9830 else
9831 {
9832 changeArcTail(dec, iterArc, otherTail, newNode);
9833 }
9834 }
9835 newRow->nodeColors[otherEnd] = UNCOLORED;//Clean up
9836
9837 //Ugly hack to make sure we can iterate neighbourhood whilst changing arc ends.
9838 spqr_arc previousArc = iterArc;
9839 iterArc = nextArc;
9840 if( iterArc == firstNodeArc )
9841 {
9842 break;
9843 }
9844 if( changeArcEnd && previousArc == firstNodeArc )
9845 {
9846 firstNodeArc = iterArc;
9847 }
9848 }
9849 while( TRUE ); /*lint !e506*/
9850 newRow->reducedMembers[reducedMember].coloredNode = SPQR_INVALID_NODE;
9851
9852 newRowInfo->member = member;
9853 newRowInfo->head = newNode;
9854 newRowInfo->tail = splitNode;
9855 newRowInfo->representative = findArcSign(dec,
9856 newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc).representative;
9857 newRowInfo->reversed = FALSE;
9858
9859 return SCIP_OKAY;
9860}
9861
9862/** Splits a single parallel member into two adjacent ones, where the cut arcs and non-cut arcs get their own member. */
9863static
9865 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
9866 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
9867 const reduced_member_id reducedMember, /**< The reduced member to transform */
9868 const spqr_member member, /**< The member belonging to the reduced member */
9869 NewRowInformation* const newRowInfo /**< Stores information about the new row placement */
9870 )
9871{
9872 assert(newRow->reducedMembers[reducedMember].numCutArcs > 0);
9873
9874 int numCutArcs = newRow->reducedMembers[reducedMember].numCutArcs;
9875 int numParallelArcs = getNumMemberArcs(dec, member);
9876
9877 SCIP_Bool createCutParallel = numCutArcs > 1;
9878 SCIP_Bool convertOriginalParallel = ( numCutArcs + 1 ) == numParallelArcs;
9879
9880 //Do linear search to find non-marked arc
9881 spqr_arc treeArc = getFirstMemberArc(dec, member);
9882 do
9883 {
9884 if( arcIsTree(dec, treeArc))
9885 {
9886 break;
9887 }
9888 treeArc = getNextMemberArc(dec, treeArc);
9889 }
9890 while( treeArc != getFirstMemberArc(dec, member) );
9891 assert(arcIsTree(dec, treeArc));
9892
9893 SCIP_Bool treeReversed = arcIsReversedNonRigid(dec, treeArc);
9894
9895 assert(!( !createCutParallel &&
9896 convertOriginalParallel ));//This can only happen if the parallel member is actually a loop, which means it is mislabeled
9897 if( createCutParallel )
9898 {
9899 if( convertOriginalParallel )
9900 {
9901 spqr_member adjacentMember = SPQR_INVALID_MEMBER;
9902 spqr_arc adjacentArc = SPQR_INVALID_ARC;
9903 if( treeArc == markerToParent(dec, member) )
9904 {
9905 adjacentMember = findMemberParent(dec, member);
9906 adjacentArc = markerOfParent(dec, member);
9907 }
9908 else if( arcIsChildMarker(dec, treeArc) )
9909 {
9910 adjacentMember = findArcChildMember(dec, treeArc);
9911 adjacentArc = markerToParent(dec, adjacentMember);
9912 assert(markerOfParent(dec, adjacentMember) == treeArc);
9913 }
9914 cut_arc_id firstCut = newRow->reducedMembers[reducedMember].firstCutArc;
9915 SCIP_Bool firstReversed =
9916 newRow->cutArcs[firstCut].arcReversed != arcIsReversedNonRigid(dec, newRow->cutArcs[firstCut].arc);
9917
9918 if( SPQRmemberIsValid(adjacentMember) && getMemberType(dec, adjacentMember) == SPQR_MEMBERTYPE_SERIES )
9919 {
9920 newRowInfo->member = adjacentMember;
9921 if( arcIsReversedNonRigid(dec, treeArc) == arcIsReversedNonRigid(dec, adjacentArc) )
9922 {
9923 newRowInfo->reversed = !firstReversed;
9924 }
9925 else
9926 {
9927 newRowInfo->reversed = firstReversed;
9928 }
9929 return SCIP_OKAY;
9930 }
9931 spqr_member cutMember = SPQR_INVALID_MEMBER;
9932 SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember) );
9933
9934 cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc;
9935 assert(cutArcIsValid(cutArcIdx));
9936 SCIP_Bool parentCut = FALSE;
9937
9938 while( cutArcIsValid(cutArcIdx) )
9939 {
9940 spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc;
9941 cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember;
9942 moveArcToNewMember(dec, cutArc, member, cutMember);
9943 if( cutArc == markerToParent(dec, member) )
9944 {
9945 parentCut = TRUE;
9946 }
9947 }
9948 if( parentCut )
9949 {
9950 SCIP_CALL( createMarkerPair(dec, cutMember, member, TRUE, FALSE, TRUE) );
9951 }
9952 else
9953 {
9954 SCIP_CALL( createMarkerPair(dec, member, cutMember, FALSE, TRUE, FALSE) );
9955 }
9956 changeLoopToSeries(dec, member);
9957 newRowInfo->member = member;
9958 if( treeReversed )
9959 {
9960 newRowInfo->reversed = firstReversed == arcIsReversedNonRigid(dec, treeArc);
9961 }
9962 else
9963 {
9964 newRowInfo->reversed = firstReversed != arcIsReversedNonRigid(dec, treeArc);
9965 }
9966 }
9967 else
9968 {
9969 spqr_member cutMember = SPQR_INVALID_MEMBER;
9970 SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember) );
9971
9972 cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc;
9973 assert(cutArcIsValid(cutArcIdx));
9974 SCIP_Bool parentCut = FALSE;
9975
9976 while( cutArcIsValid(cutArcIdx) )
9977 {
9978 spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc;
9979 cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember;
9980 moveArcToNewMember(dec, cutArc, member, cutMember);
9981 if( cutArc == markerToParent(dec, member) )
9982 {
9983 parentCut = TRUE;
9984 }
9985 }
9986 spqr_member newSeries;
9987 SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_SERIES, &newSeries) );
9988 if( parentCut )
9989 {
9990 SCIP_CALL( createMarkerPair(dec, newSeries, member, TRUE, FALSE, FALSE) );
9991 SCIP_CALL( createMarkerPair(dec, cutMember, newSeries, TRUE, FALSE, TRUE) );
9992 }
9993 else
9994 {
9995 SCIP_CALL( createMarkerPair(dec, member, newSeries, FALSE, FALSE, FALSE) );
9996 SCIP_CALL( createMarkerPair(dec, newSeries, cutMember, FALSE, TRUE, FALSE) );
9997 }
9998 newRowInfo->member = newSeries;
9999 cut_arc_id firstCut = newRow->reducedMembers[reducedMember].firstCutArc;
10000 SCIP_Bool firstReversed =
10001 newRow->cutArcs[firstCut].arcReversed != arcIsReversedNonRigid(dec, newRow->cutArcs[firstCut].arc);
10002
10003 newRowInfo->reversed = firstReversed;
10004 }
10005
10006 return SCIP_OKAY;
10007 }
10008
10009 assert(!createCutParallel && !convertOriginalParallel);
10010 assert(numCutArcs == 1);
10011#ifndef NDEBUG
10012 spqr_arc arc = newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc;
10013 spqr_member adjacentMember = SPQR_INVALID_MEMBER;
10014 if( arc == markerToParent(dec, member) )
10015 {
10016 adjacentMember = findMemberParent(dec, member);
10017 }
10018 else if( arcIsChildMarker(dec, arc) )
10019 {
10020 adjacentMember = findArcChildMember(dec, arc);
10021 }
10022 if( SPQRmemberIsValid(adjacentMember) )
10023 {
10024 assert(getMemberType(dec, adjacentMember) != SPQR_MEMBERTYPE_SERIES);
10025 }
10026#endif
10027 spqr_member newSeries;
10028 SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_SERIES, &newSeries) );
10029 cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc;
10030 assert(cutArcIsValid(cutArcIdx));
10031 SCIP_Bool parentCut = FALSE;
10032
10033 while( cutArcIsValid(cutArcIdx) )
10034 {
10035 spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc;
10036 cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember;
10037 moveArcToNewMember(dec, cutArc, member, newSeries);
10038 if( cutArc == markerToParent(dec, member) )
10039 {
10040 parentCut = TRUE;
10041 }
10042 }
10043 if( parentCut )
10044 {
10045 SCIP_CALL( createMarkerPair(dec, newSeries, member, TRUE, TRUE, FALSE) );
10046 }
10047 else
10048 {
10049 SCIP_CALL( createMarkerPair(dec, member, newSeries, FALSE, FALSE, TRUE) );
10050 }
10051 newRowInfo->member = newSeries;
10052 cut_arc_id cutArcId = newRow->reducedMembers[reducedMember].firstCutArc;
10053 newRowInfo->reversed =
10054 newRow->cutArcs[cutArcId].arcReversed == arcIsReversedNonRigid(dec, newRow->cutArcs[cutArcId].arc);
10055
10056 return SCIP_OKAY;
10057}
10058
10059/** Updates a single rigid member to reflect the new row. */
10060static
10062 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
10063 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
10064 const reduced_member_id reducedMember, /**< The reduced member to transform */
10065 const spqr_member member, /**< The member belonging to the reduced member */
10066 NewRowInformation* info /**< Stores information about the new row placement */
10067 )
10068{
10069 SCIP_CALL( splitParallelRowAddition(dec, newRow, reducedMember, member, info) );
10070 return SCIP_OKAY;
10071}
10072
10073/** Split a series member into multiple series members for merging. */
10074static
10076 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
10077 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
10078 const reduced_member_id reducedMember, /**< The reduced series member */
10079 const spqr_member member, /**< The member belonging to the reduced member */
10080 spqr_member* const mergingMember, /**< The member that contains the arcs that are to be merged */
10081 SCIP_Bool* const isCut, /**< Array that contains whether each arc is Cut */
10082 spqr_arc* representativeEdge /**< Pointer to the representative arc for the split off arcs */
10083 )
10084{
10085 assert(getNumMemberArcs(dec, member) >= 3);
10086 *isCut = newRow->reducedMembers[reducedMember].numCutArcs > 0;
10087
10088 if( getNumMemberArcs(dec, member) == 3 )
10089 {
10090 spqr_arc splitArc = newRow->reducedMembers[reducedMember].splitArc;
10091 spqr_arc otherArc = SPQR_INVALID_ARC;
10092 for( children_idx i = newRow->reducedMembers[reducedMember].firstChild;
10093 i <
10094 newRow->reducedMembers[reducedMember].firstChild + newRow->reducedMembers[reducedMember].numChildren; ++i )
10095 {
10096 reduced_member_id child = newRow->childrenStorage[i];
10097 if( newRow->reducedMembers[child].type == TYPE_MERGED )
10098 {
10099 spqr_arc testArc = markerOfParent(dec, findMember(dec, newRow->reducedMembers[child].member));
10100 if( testArc != splitArc )
10101 {
10102 otherArc = testArc;
10103 break;
10104 }
10105 }
10106 }
10107 if( SPQRarcIsInvalid(otherArc) )
10108 {
10109#ifndef NDEBUG
10110 reduced_member_id parent = newRow->reducedMembers[reducedMember].parent;
10111#endif
10112 assert(newRow->reducedMembers[parent].type == TYPE_MERGED ||
10113 newRow->reducedMembers[parent].type == TYPE_PROPAGATED);
10114 assert(reducedMemberIsValid(parent) && newRow->reducedMembers[parent].type == TYPE_MERGED);
10115 otherArc = markerToParent(dec, member);
10116 }
10117 spqr_arc firstArc = getFirstMemberArc(dec, member);
10118 spqr_arc arc = firstArc;
10119 do
10120 {
10121 if( arc != splitArc && arc != otherArc )
10122 {
10123 *representativeEdge = arc;
10124 break;
10125 }
10126 arc = getNextMemberArc(dec, arc);
10127 }
10128 while( arc != firstArc );
10129 *mergingMember = member;
10130 return SCIP_OKAY;
10131 }
10132 //Split off the relevant part of the series member
10133 spqr_member mergingSeries = SPQR_INVALID_MEMBER;
10134 SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_SERIES, &mergingSeries) );
10135 //Move all marker arcs which point to another component in the reduced decomposition to the new member
10136 //This should be exactly 2, as with 3 the result is not network anymore
10137 //move all mergeable children and parent arcs to the mergingMember
10138
10139 SCIP_Bool coTreeToMergingMember = FALSE;
10140 SCIP_Bool parentToMergingMember = FALSE;
10141 for( children_idx i = newRow->reducedMembers[reducedMember].firstChild;
10142 i < newRow->reducedMembers[reducedMember].firstChild + newRow->reducedMembers[reducedMember].numChildren; ++i )
10143 {
10144 reduced_member_id child = newRow->childrenStorage[i];
10145 assert(
10146 newRow->reducedMembers[child].type == TYPE_MERGED || newRow->reducedMembers[child].type == TYPE_PROPAGATED);
10147 if( newRow->reducedMembers[child].type == TYPE_MERGED )
10148 {
10149 spqr_arc moveArc = markerOfParent(dec, findMember(dec, newRow->reducedMembers[child].member));
10150 moveArcToNewMember(dec, moveArc, member, mergingSeries);
10151 if( !arcIsTree(dec, moveArc) )
10152 {
10153 coTreeToMergingMember = TRUE;
10154 }
10155 }
10156 }
10157
10158 reduced_member_id parent = newRow->reducedMembers[reducedMember].parent;
10159 assert(reducedMemberIsInvalid(parent) || ( newRow->reducedMembers[parent].type == TYPE_MERGED ||
10160 newRow->reducedMembers[parent].type == TYPE_PROPAGATED ));
10161
10162 if( reducedMemberIsValid(parent) &&
10163 newRow->reducedMembers[parent].type == TYPE_MERGED )
10164 {
10165 spqr_arc moveArc = markerToParent(dec, member);
10166 moveArcToNewMember(dec, moveArc, member, mergingSeries);
10167 parentToMergingMember = TRUE;
10168 if( !arcIsTree(dec, moveArc) )
10169 {
10170 coTreeToMergingMember = TRUE;
10171 }
10172 }
10173 spqr_arc ignoreArc = SPQR_INVALID_ARC;
10174 if( parentToMergingMember )
10175 {
10176 SCIP_CALL( createMarkerPairWithReferences(dec, mergingSeries, member, coTreeToMergingMember, TRUE, FALSE,
10177 representativeEdge, &ignoreArc) );
10178 }
10179 else
10180 {
10181 SCIP_CALL(
10182 createMarkerPairWithReferences(dec, member, mergingSeries, !coTreeToMergingMember, FALSE, TRUE, &ignoreArc,
10183 representativeEdge) );
10184 }
10185
10186 *mergingMember = mergingSeries;
10187 assert(getNumMemberArcs(dec, mergingSeries) == 3);
10188 assert(getNumMemberArcs(dec, member) >= 3);
10189
10190 return SCIP_OKAY;
10191}
10192
10193/** Split a parallel member into multiple parallel members for merging. */
10194static
10196 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
10197 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
10198 reduced_member_id reducedMember, /**< The reduced parallel member */
10199 spqr_member member, /**< The member belonging to the reduced member */
10200 spqr_member* const pMergeMember, /**< The member that contains the arcs that are to be merged */
10201 spqr_arc* const cutRepresentative /**< Pointer to the representative arc for all cut arcs */
10202 )
10203{
10204 //When merging, we cannot have propagated members;
10205 assert(newRow->reducedMembers[reducedMember].numCutArcs < ( getNumMemberArcs(dec, member) - 1 ));
10206
10207 int numMergeableAdjacent =
10208 newRow->reducedMembers[reducedMember].numChildren - newRow->reducedMembers[reducedMember].numPropagatedChildren;
10209 if( reducedMemberIsValid(newRow->reducedMembers[reducedMember].parent) &&
10210 newRow->reducedMembers[newRow->reducedMembers[reducedMember].parent].type == TYPE_MERGED )
10211 {
10212 numMergeableAdjacent++;
10213 }
10214
10215 int numCutArcs = newRow->reducedMembers[reducedMember].numCutArcs;
10216 //All arcs which are not in the mergeable decomposition or cut
10217 int numBaseSplitAwayArcs = getNumMemberArcs(dec, member) - numMergeableAdjacent - numCutArcs;
10218
10219 SCIP_Bool createCutParallel = numCutArcs > 1;
10220 SCIP_Bool keepOriginalParallel = numBaseSplitAwayArcs <= 1;
10221
10222 spqr_member cutMember = SPQR_INVALID_MEMBER;
10223 //The below cases can probably be aggregated in some way, but for now we first focus on the correct logic
10224 if( createCutParallel && keepOriginalParallel )
10225 {
10226 SCIP_Bool parentCut = FALSE;
10227 SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember) );
10228
10229 cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc;
10230 assert(cutArcIsValid(cutArcIdx));
10231
10232 while( cutArcIsValid(cutArcIdx) )
10233 {
10234 spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc;
10235 cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember;
10236 moveArcToNewMember(dec, cutArc, member, cutMember);
10237 if( cutArc == markerToParent(dec, member) )
10238 {
10239 parentCut = TRUE;
10240 }
10241 }
10242 spqr_arc ignoreArc = SPQR_INVALID_ARC;
10243 if( parentCut )
10244 {
10245 SCIP_CALL(
10246 createMarkerPairWithReferences(dec, cutMember, member, TRUE, FALSE, FALSE, &ignoreArc, cutRepresentative) );
10247 }
10248 else
10249 {
10250 SCIP_CALL(
10251 createMarkerPairWithReferences(dec, member, cutMember, FALSE, FALSE, FALSE, cutRepresentative, &ignoreArc) );
10252 }
10253
10254 *pMergeMember = member;
10255 }
10256 else if( createCutParallel )
10257 {
10258 assert(!keepOriginalParallel);
10259
10260 SCIP_Bool parentCut = FALSE;
10261 SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &cutMember) );
10262
10263 cut_arc_id cutArcIdx = newRow->reducedMembers[reducedMember].firstCutArc;
10264 assert(cutArcIsValid(cutArcIdx));
10265
10266 while( cutArcIsValid(cutArcIdx) )
10267 {
10268 spqr_arc cutArc = newRow->cutArcs[cutArcIdx].arc;
10269 cutArcIdx = newRow->cutArcs[cutArcIdx].nextMember;
10270 moveArcToNewMember(dec, cutArc, member, cutMember);
10271 if( cutArc == markerToParent(dec, member) )
10272 {
10273 parentCut = TRUE;
10274 }
10275 }
10276 spqr_arc ignoreArc = SPQR_INVALID_ARC;
10277 if( parentCut )
10278 {
10279 SCIP_CALL(
10280 createMarkerPairWithReferences(dec, cutMember, member, TRUE, FALSE, FALSE, &ignoreArc, cutRepresentative) );
10281 }
10282 else
10283 {
10284 SCIP_CALL(
10285 createMarkerPairWithReferences(dec, member, cutMember, FALSE, FALSE, FALSE, cutRepresentative, &ignoreArc) );
10286 }
10287
10288 spqr_arc noCutRepresentative = SPQR_INVALID_ARC;
10289 spqr_member mergingMember = member;
10290 SCIP_Bool parentToMergingMember = FALSE;
10291 SCIP_Bool treeToMergingMember = FALSE;
10292 SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &mergingMember) );
10293 //move all mergeable children and parent arcs to the mergingMember
10294 for( children_idx i = newRow->reducedMembers[reducedMember].firstChild;
10295 i < newRow->reducedMembers[reducedMember].firstChild +
10296 newRow->reducedMembers[reducedMember].numChildren;
10297 ++i )
10298 {
10299 reduced_member_id child = newRow->childrenStorage[i];
10300 assert(
10301 newRow->reducedMembers[child].type == TYPE_MERGED || newRow->reducedMembers[child].type == TYPE_PROPAGATED);
10302 if( newRow->reducedMembers[child].type == TYPE_MERGED )
10303 {
10304 spqr_arc moveArc = markerOfParent(dec, findMember(dec, newRow->reducedMembers[child].member));
10305 moveArcToNewMember(dec, moveArc, member, mergingMember);
10306 if( arcIsTree(dec, moveArc) )
10307 {
10308 treeToMergingMember = TRUE;
10309 }
10310 }
10311 }
10312 reduced_member_id parent = newRow->reducedMembers[reducedMember].parent;
10313 if( reducedMemberIsValid(parent) &&
10314 newRow->reducedMembers[parent].type == TYPE_MERGED )
10315 {
10316 spqr_arc moveArc = markerToParent(dec, member);
10317 moveArcToNewMember(dec, moveArc, member, mergingMember);
10318 parentToMergingMember = TRUE;
10319 if( arcIsTree(dec, moveArc) )
10320 {
10321 treeToMergingMember = TRUE;
10322 }
10323 }
10324 //If there is only one cut arc, we also move it.
10325 if( SPQRarcIsValid(*cutRepresentative) )
10326 {
10327 if( *cutRepresentative == markerToParent(dec, member) )
10328 {
10329 parentToMergingMember = TRUE;
10330 }
10331 moveArcToNewMember(dec, *cutRepresentative, member, mergingMember);
10332 }
10333 spqr_arc ignoreArgument = SPQR_INVALID_ARC;
10334 if( parentToMergingMember || parentCut )
10335 {
10336 SCIP_CALL( createMarkerPairWithReferences(dec, mergingMember, member, !treeToMergingMember, FALSE, FALSE,
10337 &ignoreArgument, &noCutRepresentative) );
10338 }
10339 else
10340 {
10341 SCIP_CALL( createMarkerPairWithReferences(dec, member, mergingMember, treeToMergingMember, FALSE, FALSE,
10342 &noCutRepresentative, &ignoreArgument) );
10343 }
10344 *pMergeMember = mergingMember;
10345 }
10346 else if( keepOriginalParallel )
10347 {
10348 assert(!createCutParallel);
10349 if( cutArcIsValid(newRow->reducedMembers[reducedMember].firstCutArc) )
10350 {
10351 *cutRepresentative = newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc;
10352 }
10353 *pMergeMember = member;
10354 }
10355 else
10356 {
10357 assert(!keepOriginalParallel && !createCutParallel);
10358
10359 if( cutArcIsValid(newRow->reducedMembers[reducedMember].firstCutArc) )
10360 {
10361 *cutRepresentative = newRow->cutArcs[newRow->reducedMembers[reducedMember].firstCutArc].arc;
10362 }
10363
10364 spqr_arc noCutRepresentative = SPQR_INVALID_ARC;
10365 spqr_member mergingMember = member;
10366 SCIP_Bool parentToMergingMember = FALSE;
10367 SCIP_Bool treeToMergingMember = FALSE;
10368 SCIP_CALL( createMember(dec, SPQR_MEMBERTYPE_PARALLEL, &mergingMember) );
10369 //move all mergeable children and parent arcs to the mergingMember
10370 for( children_idx i = newRow->reducedMembers[reducedMember].firstChild;
10371 i < newRow->reducedMembers[reducedMember].firstChild +
10372 newRow->reducedMembers[reducedMember].numChildren;
10373 ++i )
10374 {
10375 reduced_member_id child = newRow->childrenStorage[i];
10376 assert(
10377 newRow->reducedMembers[child].type == TYPE_MERGED || newRow->reducedMembers[child].type == TYPE_PROPAGATED);
10378 if( newRow->reducedMembers[child].type == TYPE_MERGED )
10379 {
10380 spqr_arc moveArc = markerOfParent(dec, findMember(dec, newRow->reducedMembers[child].member));
10381 moveArcToNewMember(dec, moveArc, member, mergingMember);
10382 if( arcIsTree(dec, moveArc) )
10383 {
10384 treeToMergingMember = TRUE;
10385 }
10386 }
10387 }
10388 reduced_member_id parent = newRow->reducedMembers[reducedMember].parent;
10389 if( reducedMemberIsValid(parent) &&
10390 newRow->reducedMembers[parent].type == TYPE_MERGED )
10391 {
10392 spqr_arc moveArc = markerToParent(dec, member);
10393 moveArcToNewMember(dec, moveArc, member, mergingMember);
10394 parentToMergingMember = TRUE;
10395 if( arcIsTree(dec, moveArc) )
10396 {
10397 treeToMergingMember = TRUE;
10398 }
10399 }
10400 //If there is only one cut arc, we also move it.
10401 if( SPQRarcIsValid(*cutRepresentative) )
10402 {
10403 if( *cutRepresentative == markerToParent(dec, member) )
10404 {
10405 parentToMergingMember = TRUE;
10406 }
10407 moveArcToNewMember(dec, *cutRepresentative, member, mergingMember);
10408 }
10409 spqr_arc ignoreArgument = SPQR_INVALID_ARC;
10410 if( parentToMergingMember )
10411 {
10412 SCIP_CALL( createMarkerPairWithReferences(dec, mergingMember, member, !treeToMergingMember, FALSE, FALSE,
10413 &ignoreArgument, &noCutRepresentative) );
10414 }
10415 else
10416 {
10417 SCIP_CALL( createMarkerPairWithReferences(dec, member, mergingMember, treeToMergingMember, FALSE, FALSE,
10418 &noCutRepresentative, &ignoreArgument) );
10419 }
10420 *pMergeMember = mergingMember;
10421 }
10422
10423 return SCIP_OKAY;
10424}
10425
10426/** Update the first leaf to reflect the addition of the new row */
10427static
10429 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
10430 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
10431 reduced_member_id leaf, /**< The leaf to split the first node for */
10432 NewRowInformation* const newRowInfo /**< Stores how to add the new row */
10433 )
10434{
10435 assert(cutArcIsValid(newRow->reducedMembers[leaf].firstCutArc));
10436 assert(newRow->reducedMembers[leaf].numCutArcs > 0);
10437 spqr_member member = newRow->reducedMembers[leaf].member;
10438 if( getMemberType(dec, member) == SPQR_MEMBERTYPE_PARALLEL )
10439 {
10440 spqr_member mergeMember = SPQR_INVALID_MEMBER;
10441 spqr_arc cutRepresentative = SPQR_INVALID_ARC;
10442 SCIP_CALL( splitParallelMerging(dec, newRow, leaf, member, &mergeMember, &cutRepresentative) );
10443 newRow->reducedMembers[leaf].member = mergeMember;
10444
10445 spqr_node firstNode = SPQR_INVALID_NODE;
10446 spqr_node secondNode = SPQR_INVALID_NODE;
10447 spqr_node thirdNode = SPQR_INVALID_NODE;
10448 SCIP_CALL( createNode(dec, &firstNode) );
10449 SCIP_CALL( createNode(dec, &secondNode) );
10450 SCIP_CALL( createNode(dec, &thirdNode) );
10451
10452 spqr_arc splitArc = newRow->reducedMembers[leaf].splitArc;
10453 assert(findArcMemberNoCompression(dec, splitArc) == mergeMember);
10454 SCIP_Bool splitArcReversed = arcIsReversedNonRigid(dec, splitArc);
10455 SCIP_Bool splitHead = newRow->reducedMembers[leaf].splitHead;
10456 spqr_node splitArcHead = splitArcReversed ? secondNode : firstNode;
10457 spqr_node splitArcTail = splitArcReversed ? firstNode : secondNode;
10458 spqr_node splitNode = splitHead ? splitArcHead : splitArcTail;
10459 spqr_node otherNode = splitHead ? splitArcTail : splitArcHead;
10460
10461 spqr_arc first_arc = getFirstMemberArc(dec, mergeMember);
10462 spqr_arc arc = first_arc;
10463
10464 do
10465 {
10466 if( arc != cutRepresentative )
10467 {
10468 if( arcIsReversedNonRigid(dec, arc) )
10469 {
10470 setArcHeadAndTail(dec, arc, secondNode, firstNode);
10471 }
10472 else
10473 {
10474 setArcHeadAndTail(dec, arc, firstNode, secondNode);
10475 }
10476 }
10477 else
10478 {
10479 if( (arcIsReversedNonRigid(dec, arc) == splitArcReversed) == splitHead )
10480 {
10481 setArcHeadAndTail(dec, arc, thirdNode, otherNode);
10482 }
10483 else
10484 {
10485 setArcHeadAndTail(dec, arc, otherNode, thirdNode);
10486 }
10487 }
10488 arcSetReversed(dec, arc, FALSE);
10489 if( arc == splitArc )
10490 {
10492 }
10493 else
10494 {
10495 arcSetRepresentative(dec, arc, splitArc);
10496 }
10497 arc = getNextMemberArc(dec, arc);
10498 }
10499 while( arc != first_arc );
10500
10501 updateMemberType(dec, mergeMember, SPQR_MEMBERTYPE_RIGID);
10502 newRowInfo->member = mergeMember;
10503 if( newRow->reducedMembers[leaf].otherIsSource )
10504 {
10505 newRowInfo->head = thirdNode;
10506 newRowInfo->tail = splitNode;
10507 }
10508 else
10509 {
10510 newRowInfo->head = splitNode;
10511 newRowInfo->tail = thirdNode;
10512 }
10513
10514 newRowInfo->reversed = FALSE;
10515 newRowInfo->representative = splitArc;
10516
10517 return SCIP_OKAY;
10518 }
10519 assert(getMemberType(dec, member) == SPQR_MEMBERTYPE_RIGID);
10520
10521 spqr_node newNode = SPQR_INVALID_NODE;//Sink node
10522 SCIP_CALL( createNode(dec, &newNode) );
10523
10524 spqr_node splitNode = newRow->reducedMembers[leaf].splitNode;
10525
10526 spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode);
10527 spqr_arc iterArc = firstNodeArc;
10528 do
10529 {
10530 spqr_node otherHead = findArcHead(dec, iterArc);
10531 spqr_node otherTail = findArcTail(dec, iterArc);
10532 spqr_node otherEnd = otherHead == splitNode ? otherTail : otherHead;
10533 spqr_arc nextArc = getNextNodeArc(dec, iterArc, splitNode);//Need to do this before we modify the arc
10534
10535 SCIP_Bool isCut = newRow->isArcCut[iterArc];
10536 SCIP_Bool isMoveColor = newRow->nodeColors[otherEnd] == COLOR_SOURCE;
10537 SCIP_Bool changeArcEnd = isCut == isMoveColor;
10538 if( changeArcEnd )
10539 {
10540 if( otherHead == splitNode )
10541 {
10542 changeArcHead(dec, iterArc, otherHead, newNode);
10543 }
10544 else
10545 {
10546 changeArcTail(dec, iterArc, otherTail, newNode);
10547 }
10548 }
10549 //Ugly hack to make sure we can iterate neighbourhood whilst changing arc ends.
10550 newRow->nodeColors[otherEnd] = UNCOLORED;
10551 spqr_arc previousArc = iterArc;
10552 iterArc = nextArc;
10553 if( iterArc == firstNodeArc )
10554 {
10555 break;
10556 }
10557 if( changeArcEnd && previousArc == firstNodeArc )
10558 {
10559 firstNodeArc = iterArc;
10560 }
10561 }
10562 while( TRUE ); /*lint !e506*/
10563 newRowInfo->head = newNode;
10564 newRowInfo->tail = splitNode;
10565 newRowInfo->member = member;
10566 newRowInfo->reversed = FALSE;
10567 newRowInfo->representative = findArcSign(dec, iterArc).representative;
10568
10569 return SCIP_OKAY;
10570}
10571
10572/** Merge an (updated) member into its parent.
10573 *
10574 * This function is mainly there to prevent duplication.
10575 */
10576static
10578 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
10579 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
10580 spqr_member member, /**< The member to merge */
10581 spqr_member parent, /**< The member's parent */
10582 spqr_arc parentToChild, /**< The marker pointing from the parent to child */
10583 spqr_arc childToParent, /**< The marker pointing from the child to the parent */
10584 SCIP_Bool headToHead, /**< Should the marker heads be identified with one another? */
10585 spqr_node parentNode, /**< A node in the parent that is not adjacent to the marker */
10586 spqr_node childNode, /**< A node in the child that is not adjacent to the marker */
10587 spqr_member* mergedMember, /**< Pointer to the member that contains both members after merging */
10588 spqr_node* arcNodeOne, /**< The first identified node of the two marker arcs */
10589 spqr_node* arcNodeTwo, /**< The second identified node of the two marker arcs */
10590 spqr_node* thirdNode /**< The node that parentNode and childNode are identified into */
10591 )
10592{
10593 assert(dec);
10594 assert(SPQRmemberIsValid(member));
10595 assert(memberIsRepresentative(dec, member));
10596 assert(SPQRmemberIsValid(parent));
10597 assert(memberIsRepresentative(dec, parent));
10598 assert(findMemberParentNoCompression(dec, member) == parent);
10599 assert(markerOfParent(dec, member) == parentToChild);
10600 assert(markerToParent(dec, member) == childToParent);
10601
10602 removeArcFromMemberArcList(dec, parentToChild, parent);
10603 removeArcFromMemberArcList(dec, childToParent, member);
10604
10605 int parentTail = findEffectiveArcTail(dec, parentToChild);
10606 int parentHead = findEffectiveArcHead(dec, parentToChild);
10607 int childTail = findEffectiveArcTail(dec, childToParent);
10608 int childHead = findEffectiveArcHead(dec, childToParent);
10609 spqr_node parentArcNodes[2] = { parentTail, parentHead };
10610 spqr_node childArcNodes[2] = { childTail, childHead };
10611
10612 clearArcHeadAndTail(dec, parentToChild);
10613 clearArcHeadAndTail(dec, childToParent);
10614
10615 spqr_node first = childArcNodes[headToHead ? 0 : 1];
10616 spqr_node second = childArcNodes[headToHead ? 1 : 0];
10617 {
10618 spqr_node newNode = mergeNodes(dec, parentArcNodes[0], first);
10619 spqr_node toRemoveFrom = newNode == first ? parentArcNodes[0] : first;
10620 mergeNodeArcList(dec, newNode, toRemoveFrom);
10621 *arcNodeOne = newNode;
10622 newRow->nodeColors[toRemoveFrom] = UNCOLORED;
10623 }
10624 {
10625 spqr_node newNode = mergeNodes(dec, parentArcNodes[1], second);
10626 spqr_node toRemoveFrom = newNode == second ? parentArcNodes[1] : second;
10627 mergeNodeArcList(dec, newNode, toRemoveFrom);
10628 *arcNodeTwo = newNode;
10629 newRow->nodeColors[toRemoveFrom] = UNCOLORED;
10630 }
10631 {
10632 spqr_node newNode = mergeNodes(dec, parentNode, childNode);
10633 spqr_node toRemoveFrom = newNode == childNode ? parentNode : childNode;
10634 mergeNodeArcList(dec, newNode, toRemoveFrom);
10635 *thirdNode = newNode;
10636 newRow->nodeColors[toRemoveFrom] = UNCOLORED;
10637 }
10638
10639 spqr_member newMember = mergeMembers(dec, member, parent);
10640 spqr_member toRemoveFrom = newMember == member ? parent : member;
10641 mergeMemberArcList(dec, newMember, toRemoveFrom);
10642 if( toRemoveFrom == parent )
10643 {
10644 updateMemberParentInformation(dec, newMember, toRemoveFrom);
10645 }
10646 updateMemberType(dec, newMember, SPQR_MEMBERTYPE_RIGID);
10647 *mergedMember = newMember;
10648
10649 return SCIP_OKAY;
10650}
10651
10652/** Update a series member to reflect the addition of the new row, and merge it into the previous members that were
10653 * updated.
10654 */
10655static
10657 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
10658 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
10659 reduced_member_id smallMember, /**< The reduced series member */
10660 SCIP_Bool largeIsParent, /**< Whether the large member containing previously updated members is
10661 * the parent of this member */
10662 NewRowInformation* const newRowInfo, /**< Stores the information on how to add the new row */
10663 spqr_member member /**< The member belonging to the reduced series member */
10664 )
10665{
10666 SCIP_Bool isCut = FALSE;
10667 spqr_member mergingMember = SPQR_INVALID_MEMBER;
10668 spqr_arc nonVirtualArc = SPQR_INVALID_ARC;
10669
10670 SCIP_CALL( splitSeriesMergingRowAddition(dec, newRow, smallMember, member, &mergingMember, &isCut, &nonVirtualArc) );
10671 assert(getNumMemberArcs(dec, mergingMember) == 3);
10672
10673 //create the split series. There's two possible configurations, based on whether it contains a cut edge or not
10678 SCIP_CALL( createNode(dec, &a) );
10679 SCIP_CALL( createNode(dec, &b) );
10680 SCIP_CALL( createNode(dec, &c) );
10681 SCIP_CALL( createNode(dec, &d) );
10682
10683 spqr_arc splitArc = newRow->reducedMembers[smallMember].splitArc;
10684
10685 {
10686 SCIP_Bool splitHead = newRow->reducedMembers[smallMember].splitHead;
10687 spqr_arc firstArc = getFirstMemberArc(dec, mergingMember);
10688 spqr_arc arc = firstArc;
10689 SCIP_Bool splitReversed = arcIsReversedNonRigid(dec, splitArc);
10690 do
10691 {
10692 if( arc == splitArc )
10693 {
10694 if( splitHead )
10695 {
10696 setArcHeadAndTail(dec, splitArc, b, a);
10697 }
10698 else
10699 {
10700 setArcHeadAndTail(dec, splitArc, a, b);
10701 }
10702 }
10703 else if( arc == nonVirtualArc )
10704 {
10705 if( (arcIsReversedNonRigid(dec, arc) == splitReversed) == splitHead )
10706 {
10707 setArcHeadAndTail(dec, arc, a, d);
10708 }
10709 else
10710 {
10711 setArcHeadAndTail(dec, arc, d, a);
10712 }
10713 }
10714 else
10715 {
10716 spqr_node otherNode = cutArcIsValid(newRow->reducedMembers[smallMember].firstCutArc) ? c : b;
10717 if( (arcIsReversedNonRigid(dec, arc) == splitReversed) == splitHead )
10718 {
10719 setArcHeadAndTail(dec, arc, d, otherNode);
10720 }
10721 else
10722 {
10723 setArcHeadAndTail(dec, arc, otherNode, d);
10724 }
10725 }
10726 arcSetReversed(dec, arc, FALSE);
10727 arcSetRepresentative(dec, arc, splitArc);
10728 arc = getNextMemberArc(dec, arc);
10729 }
10730 while( arc != firstArc );
10731 arcSetRepresentative(dec, splitArc, SPQR_INVALID_ARC);
10732 }
10733
10734 spqr_member otherMember = newRowInfo->member;
10735 spqr_arc otherMarker = largeIsParent ? markerOfParent(dec, mergingMember) : markerToParent(dec, otherMember);
10736
10737 assert(nodeIsRepresentative(dec, newRowInfo->tail));
10738 assert(nodeIsRepresentative(dec, newRowInfo->head));
10739 spqr_node splitNode = newRow->reducedMembers[smallMember].splitHead ? findEffectiveArcHead(dec, otherMarker)
10740 : findEffectiveArcTail(dec, otherMarker);
10741
10742 spqr_node otherNode = splitNode == newRowInfo->head ? newRowInfo->tail : newRowInfo->head;
10743 assert(splitNode == newRowInfo->head || splitNode == newRowInfo->tail);
10744 newRowInfo->representative = mergeArcSigns(dec, newRowInfo->representative, splitArc, FALSE);
10745
10746 spqr_member mergedMember = SPQR_INVALID_MEMBER;
10747 spqr_node arcNodeOne;
10748 spqr_node arcNodeTwo;
10749 spqr_node thirdNode;
10750 if( largeIsParent )
10751 {
10752 SCIP_CALL( mergeSplitMemberIntoParent(dec, newRow, mergingMember, otherMember, otherMarker, splitArc, TRUE,
10753 otherNode, c, &mergedMember,
10754 &arcNodeOne,
10755 &arcNodeTwo,
10756 &thirdNode) );
10757 }
10758 else
10759 {
10760 SCIP_CALL( mergeSplitMemberIntoParent(dec, newRow, otherMember, mergingMember, splitArc, otherMarker, TRUE,
10761 c, otherNode, &mergedMember,
10762 &arcNodeOne,
10763 &arcNodeTwo,
10764 &thirdNode) );
10765 }
10766 newRow->reducedMembers[smallMember].member = mergedMember;
10767
10768 newRowInfo->member = mergedMember;
10769 SCIP_Bool splitIsReferenceHead = newRow->reducedMembers[smallMember].splitHead;
10770 SCIP_Bool splitIsNewRowHead = splitNode == newRowInfo->head;
10771 if( !splitIsReferenceHead && !splitIsNewRowHead )
10772 {
10773 newRowInfo->head = thirdNode;
10774 newRowInfo->tail = arcNodeOne;
10775 }
10776 else if( !splitIsReferenceHead && splitIsNewRowHead )
10777 {
10778 newRowInfo->head = arcNodeOne;
10779 newRowInfo->tail = thirdNode;
10780 }
10781 else if( splitIsReferenceHead && !splitIsNewRowHead )
10782 {
10783 newRowInfo->head = thirdNode;
10784 newRowInfo->tail = arcNodeTwo;
10785 }
10786 else if( splitIsReferenceHead && splitIsNewRowHead )
10787 {
10788 newRowInfo->head = arcNodeTwo;
10789 newRowInfo->tail = thirdNode;
10790 }
10791
10792 return SCIP_OKAY;
10793}
10794
10795/** Update a parallel member to reflect the addition of the new row, and merge it into the previous members that were
10796 * updated.
10797 */
10798static
10800 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
10801 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
10802 reduced_member_id smallMember, /**< The reduced parallel member */
10803 SCIP_Bool largeIsParent, /**< Whether the large member containing previously updated members is
10804 * the parent of this member */
10805 NewRowInformation* const newRowInfo, /**< Stores the information on how to add the new row */
10806 spqr_member member /**< The member belonging to the reduced parallel member */
10807 )
10808{
10809 spqr_member mergeMember = SPQR_INVALID_MEMBER;
10810 spqr_arc cutRepresentative = SPQR_INVALID_ARC;
10811 SCIP_CALL( splitParallelMerging(dec, newRow, smallMember, member, &mergeMember, &cutRepresentative) );
10812 newRow->reducedMembers[smallMember].member = mergeMember;
10813
10814 spqr_node firstNode = SPQR_INVALID_NODE;
10815 spqr_node secondNode = SPQR_INVALID_NODE;
10816 spqr_node thirdNode = SPQR_INVALID_NODE;
10817 SCIP_CALL( createNode(dec, &firstNode) );
10818 SCIP_CALL( createNode(dec, &secondNode) );
10819 SCIP_CALL( createNode(dec, &thirdNode) );
10820
10821 spqr_arc splitArc = newRow->reducedMembers[smallMember].splitArc;
10822 assert(findArcMemberNoCompression(dec, splitArc) == mergeMember);
10823 SCIP_Bool splitArcReversed = arcIsReversedNonRigid(dec, splitArc);
10824 SCIP_Bool splitHead = newRow->reducedMembers[smallMember].splitHead;
10825
10826 spqr_node splitArcHead = splitArcReversed ? secondNode : firstNode;
10827 spqr_node splitArcTail = splitArcReversed ? firstNode : secondNode;
10828 spqr_node otherNode = splitHead ? splitArcTail : splitArcHead;
10829
10830 spqr_arc first_arc = getFirstMemberArc(dec, mergeMember);
10831 spqr_arc arc = first_arc;
10832
10833 do
10834 {
10835 if( arc != cutRepresentative )
10836 {
10837 if( arcIsReversedNonRigid(dec, arc) )
10838 {
10839 setArcHeadAndTail(dec, arc, secondNode, firstNode);
10840 }
10841 else
10842 {
10843 setArcHeadAndTail(dec, arc, firstNode, secondNode);
10844 }
10845 }
10846 else
10847 {
10848 if( (arcIsReversedNonRigid(dec, arc) == splitArcReversed) == splitHead )
10849 {
10850 setArcHeadAndTail(dec, arc, thirdNode, otherNode);
10851 }
10852 else
10853 {
10854 setArcHeadAndTail(dec, arc, otherNode, thirdNode);
10855 }
10856 }
10857 arcSetReversed(dec, arc, FALSE);
10858 arcSetRepresentative(dec, arc, splitArc);
10859 arc = getNextMemberArc(dec, arc);
10860 }
10861 while( arc != first_arc );
10862 arcSetRepresentative(dec, splitArc, SPQR_INVALID_ARC);
10863
10864 spqr_member otherMember = newRowInfo->member;
10865 spqr_arc otherMarker = largeIsParent ? markerOfParent(dec, mergeMember) : markerToParent(dec, otherMember);
10866
10867 assert(nodeIsRepresentative(dec, newRowInfo->tail));
10868 assert(nodeIsRepresentative(dec, newRowInfo->head));
10869 spqr_node largeSplitNode = newRow->reducedMembers[smallMember].splitHead ? findEffectiveArcHead(dec, otherMarker)
10870 : findEffectiveArcTail(dec, otherMarker);
10871
10872 spqr_node largeOtherNode =
10873 largeSplitNode == newRowInfo->head ? newRowInfo->tail : newRowInfo->head;
10874 assert(largeSplitNode == newRowInfo->head || largeSplitNode == newRowInfo->tail);
10875
10876 newRowInfo->representative = mergeArcSigns(dec, newRowInfo->representative, splitArc, FALSE);
10877
10878 spqr_member mergedMember = SPQR_INVALID_MEMBER;
10879 spqr_node arcNodeOne;
10880 spqr_node arcNodeTwo;
10881 spqr_node mergeNodeThree;
10882 if( largeIsParent )
10883 {
10884 SCIP_CALL( mergeSplitMemberIntoParent(dec, newRow, mergeMember, otherMember, otherMarker, splitArc, TRUE,
10885 largeOtherNode, thirdNode, &mergedMember,
10886 &arcNodeOne,
10887 &arcNodeTwo,
10888 &mergeNodeThree) );
10889 }
10890 else
10891 {
10892 SCIP_CALL( mergeSplitMemberIntoParent(dec, newRow, otherMember, mergeMember, splitArc, otherMarker, TRUE,
10893 thirdNode, largeOtherNode, &mergedMember,
10894 &arcNodeOne,
10895 &arcNodeTwo,
10896 &mergeNodeThree) );
10897 }
10898
10899 newRowInfo->member = mergedMember;
10900
10901 SCIP_Bool splitIsReferenceHead = newRow->reducedMembers[smallMember].splitHead;
10902 SCIP_Bool splitIsNewRowHead = largeSplitNode == newRowInfo->head;
10903 if( !splitIsReferenceHead && !splitIsNewRowHead )
10904 {
10905 newRowInfo->head = mergeNodeThree;
10906 newRowInfo->tail = arcNodeOne;
10907 }
10908 else if( !splitIsReferenceHead && splitIsNewRowHead )
10909 {
10910 newRowInfo->head = arcNodeOne;
10911 newRowInfo->tail = mergeNodeThree;
10912 }
10913 else if( splitIsReferenceHead && !splitIsNewRowHead )
10914 {
10915 newRowInfo->head = mergeNodeThree;
10916 newRowInfo->tail = arcNodeTwo;
10917 }
10918 else if( splitIsReferenceHead && splitIsNewRowHead )
10919 {
10920 newRowInfo->head = arcNodeTwo;
10921 newRowInfo->tail = mergeNodeThree;
10922 }
10923
10924 return SCIP_OKAY;
10925}
10926
10927/** Update a rigid member to reflect the addition of the new row, and merge it into the previous members that were
10928 * updated.
10929 */
10930static
10932 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
10933 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
10934 reduced_member_id smallMember, /**< The reduced rigid member */
10935 SCIP_Bool largeIsParent, /**< Whether the large member containing previously updated members is
10936 * the parent of this member */
10937 NewRowInformation* const newRowInfo, /**< Stores the information on how to add the new row */
10938 spqr_member member /**< The member belonging to the reduced rigid member */
10939 )
10940{
10941 spqr_node newNode = SPQR_INVALID_NODE;//Sink node
10942 SCIP_CALL( createNode(dec, &newNode) );
10943
10944 spqr_member smallMemberMember = member;
10945 spqr_member largeMemberMember = newRowInfo->member;
10946
10947 spqr_arc smallMarker = largeIsParent ? markerToParent(dec, smallMemberMember) : markerOfParent(dec,
10948 largeMemberMember);
10949 spqr_arc largeMarker = largeIsParent ? markerOfParent(dec, smallMemberMember) : markerToParent(dec,
10950 largeMemberMember);
10951
10952 spqr_node splitNode = newRow->reducedMembers[smallMember].splitNode;
10953 spqr_node smallOtherNode = newNode;
10954
10955 if( newRow->reducedMembers[smallMember].numCutArcs != 0 )
10956 {
10957 spqr_arc firstNodeArc = getFirstNodeArc(dec, splitNode);
10958 spqr_arc iterArc = firstNodeArc;
10959 do
10960 {
10961 spqr_node otherHead = findArcHead(dec, iterArc);
10962 spqr_node otherTail = findArcTail(dec, iterArc);
10963 spqr_node otherEnd = otherHead == splitNode ? otherTail : otherHead;
10964 spqr_arc nextArc = getNextNodeArc(dec, iterArc, splitNode);//Need to do this before we modify the arc
10965
10966 SCIP_Bool isCut = newRow->isArcCut[iterArc];
10967 SCIP_Bool isMoveColor = newRow->nodeColors[otherEnd] == COLOR_SOURCE;
10968 SCIP_Bool changeArcEnd = isCut == isMoveColor;
10969 if( changeArcEnd )
10970 {
10971 if( otherHead == splitNode )
10972 {
10973 changeArcHead(dec, iterArc, otherHead, newNode);
10974 }
10975 else
10976 {
10977 changeArcTail(dec, iterArc, otherTail, newNode);
10978 }
10979 if( iterArc == smallMarker )
10980 {
10981 smallOtherNode = splitNode;
10982 }
10983 }
10984 newRow->nodeColors[otherEnd] = UNCOLORED;
10985 //Ugly hack to make sure we can iterate neighbourhood whilst changing arc ends.
10986 spqr_arc previousArc = iterArc;
10987 iterArc = nextArc;
10988 if( iterArc == firstNodeArc )
10989 {
10990 break;
10991 }
10992 if( changeArcEnd && previousArc == firstNodeArc )
10993 {
10994 firstNodeArc = iterArc;
10995 }
10996 }
10997 while( TRUE ); /*lint !e506*/
10998 }
10999
11000 spqr_arc representative = findArcSign(dec, smallMarker).representative;
11001
11002 newRowInfo->representative = mergeArcSigns(dec, newRowInfo->representative, representative,
11003 newRow->reducedMembers[smallMember].willBeReversed);
11004
11005 spqr_node largeMarkerHead = findArcHead(dec, largeMarker);
11006 spqr_node largeMarkerTail = findArcTail(dec, largeMarker);
11007 if( findArcSign(dec, largeMarker).reversed )
11008 {
11009 spqr_node temp = largeMarkerHead;
11010 largeMarkerHead = largeMarkerTail;
11011 largeMarkerTail = temp;
11012 }
11013 assert(newRowInfo->head == largeMarkerHead || newRowInfo->head == largeMarkerTail ||
11014 newRowInfo->tail == largeMarkerHead || newRowInfo->tail == largeMarkerTail);
11015 spqr_node largeOtherNode = ( newRowInfo->head == largeMarkerHead || newRowInfo->head == largeMarkerTail )
11016 ? newRowInfo->tail : newRowInfo->head;
11017
11018 spqr_member mergedMember = SPQR_INVALID_MEMBER;
11019 spqr_node arcNodeOne;
11020 spqr_node arcNodeTwo;
11021 spqr_node mergeNodeThree;
11022 if( largeIsParent )
11023 {
11024 SCIP_CALL(
11025 mergeSplitMemberIntoParent(dec, newRow, smallMemberMember, largeMemberMember, largeMarker, smallMarker, TRUE,
11026 largeOtherNode, smallOtherNode, &mergedMember,
11027 &arcNodeOne,
11028 &arcNodeTwo,
11029 &mergeNodeThree) );
11030 }
11031 else
11032 {
11033 SCIP_CALL(
11034 mergeSplitMemberIntoParent(dec, newRow, largeMemberMember, smallMemberMember, smallMarker, largeMarker, TRUE,
11035 smallOtherNode, largeOtherNode, &mergedMember,
11036 &arcNodeOne,
11037 &arcNodeTwo,
11038 &mergeNodeThree) );
11039 }
11040 newRowInfo->member = mergedMember;
11041
11042 SCIP_Bool otherIsHead = largeOtherNode == newRowInfo->head;
11043 SCIP_Bool adjacentToMarkerHead = ( newRowInfo->tail == largeMarkerHead ||
11044 newRowInfo->head == largeMarkerHead );
11045 if( adjacentToMarkerHead )
11046 {
11047 if( otherIsHead )
11048 {
11049 newRowInfo->head = mergeNodeThree;
11050 newRowInfo->tail = arcNodeTwo;
11051 }
11052 else
11053 {
11054 newRowInfo->head = arcNodeTwo;
11055 newRowInfo->tail = mergeNodeThree;
11056 }
11057 }
11058 else
11059 {
11060 if( otherIsHead )
11061 {
11062 newRowInfo->head = mergeNodeThree;
11063 newRowInfo->tail = arcNodeOne;
11064 }
11065 else
11066 {
11067 newRowInfo->head = arcNodeOne;
11068 newRowInfo->tail = mergeNodeThree;
11069 }
11070 }
11071
11072 return SCIP_OKAY;
11073}
11074
11075/** Update a member to reflect the addition of the new row, and merge it into the previous members that were updated. */
11076static
11078 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
11079 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
11080 reduced_member_id smallMember, /**< The reduced rigid member */
11081 SCIP_Bool largeIsParent, /**< Whether the large member containing previously updated members is
11082 * the parent of this member */
11083 NewRowInformation* const newRowInfo /**< Stores the information on how to add the new row */
11084 )
11085{
11086 spqr_member member = newRow->reducedMembers[smallMember].member;
11087 switch( getMemberType(dec, member))
11088 {
11090 {
11091 SCIP_CALL( splitAndMergeRigid(dec, newRow, smallMember, largeIsParent, newRowInfo, member) );
11092 break;
11093 }
11095 {
11096 SCIP_CALL( splitAndMergeParallel(dec, newRow, smallMember, largeIsParent, newRowInfo, member) );
11097 break;
11098 }
11100 {
11101 SCIP_CALL( splitAndMergeSeries(dec, newRow, smallMember, largeIsParent, newRowInfo, member) );
11102 break;
11103 }
11106 default:
11107 {
11108 newRow->remainsNetwork = FALSE;
11109 SCIPABORT();
11110 return SCIP_ERROR;
11111 }
11112 }
11113 return SCIP_OKAY;
11114}
11115
11116/** Update an SPQR tree with multiple members to reflect the addition of a new row,
11117 * merging it into one big rigid node.
11118 */
11119static
11121 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
11122 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
11123 reduced_member_id root, /**< The root node of the SPQR tree that is to be merged */
11124 NewRowInformation* const newRowInfo /**< Stores the information on how to add the new row */
11125 )
11126{
11127 //We use the same ordering as when finding
11128 //go to a leaf. We need to start in a leaf to avoid the ambiguity of choosing an orientation
11129 //in members which have no cut arcs; otherwise, we might choose the wrong one
11130 reduced_member_id leaf = root;
11131 while( newRow->reducedMembers[leaf].numChildren != newRow->reducedMembers[leaf].numPropagatedChildren )
11132 {
11133 for( int i = 0; i < newRow->reducedMembers[leaf].numChildren; ++i )
11134 {
11135 children_idx idx = newRow->reducedMembers[leaf].firstChild + i;
11136 reduced_member_id child = newRow->childrenStorage[idx];
11137 if( newRow->reducedMembers[child].type != TYPE_PROPAGATED )
11138 {
11139 leaf = child;
11140 break;
11141 }
11142 }
11143 }
11144 SCIP_CALL( splitFirstLeaf(dec, newRow, leaf, newRowInfo) );
11145
11146 reduced_member_id baseNode = leaf;
11147 reduced_member_id nextNode = newRow->reducedMembers[baseNode].parent;
11148
11149 while( reducedMemberIsValid(nextNode) )
11150 {
11151 //check this node
11152 SCIP_CALL( splitAndMerge(dec, newRow, nextNode, FALSE, newRowInfo) );
11153
11154 //Recursively merge the children
11155 //use a while loop to avoid recursion; we may get stack overflows for large graphs
11156 MergeTreeCallData * data = newRow->mergeTreeCallData;
11157
11158 data[0].id = nextNode;
11159 data[0].currentChild = newRow->reducedMembers[nextNode].firstChild ;
11160 int depth = 0;
11161 while( depth >= 0 )
11162 {
11163 reduced_member_id id = data[depth].id;
11164 children_idx childidx = data[depth].currentChild;
11165 if( childidx == newRow->reducedMembers[id].firstChild + newRow->reducedMembers[id].numChildren )
11166 {
11167 --depth;
11168 continue;
11169 }
11170 reduced_member_id currentchild = newRow->childrenStorage[childidx];
11171 data[depth].currentChild += 1;
11172 //skip this child if we already processed it or it is not merged
11173 if( currentchild == baseNode || newRow->reducedMembers[currentchild].type == TYPE_PROPAGATED )
11174 {
11175 continue;
11176 }
11177 SCIP_CALL( splitAndMerge(dec, newRow, currentchild, TRUE, newRowInfo) );
11178
11179 //recursively process the child
11180 depth += 1;
11181 assert(depth < newRow->memMergeTreeCallData);
11182 data[depth].id = currentchild;
11183 data[depth].currentChild = newRow->reducedMembers[currentchild].firstChild;
11184 }
11185 //Move up one layer
11186 baseNode = nextNode;
11187 nextNode = newRow->reducedMembers[nextNode].parent;
11188 }
11189
11190 return SCIP_OKAY;
11191}
11192
11193/** Update an SPQR tree (a single component of the SPQR forest) to reflect addition of a new row. */
11194static
11196 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
11197 SCIP_NETROWADD* newRow, /**< The network matrix row addition data structure */
11198 SPQRRowReducedComponent* component, /**< The component to transform */
11199 NewRowInformation* const newRowInfo /**< Stores the information on how to add the new row */
11200 )
11201{
11202 assert(component);
11203 if( newRow->reducedMembers[component->root].numChildren ==
11204 newRow->reducedMembers[component->root].numPropagatedChildren )
11205 {
11206 //No merging necessary, only a single component
11207 reduced_member_id reducedMember = component->root;
11208 assert(reducedMemberIsValid(reducedMember));
11209 spqr_member member = newRow->reducedMembers[reducedMember].member;
11210 SPQRMemberType type = getMemberType(dec, member);
11211
11212 switch( type )
11213 {
11215 SCIP_CALL( transformSingleRigid(dec, newRow, reducedMember, member, newRowInfo) );
11216 break;
11218 {
11219 SCIP_CALL( transformSingleParallel(dec, newRow, reducedMember, member, newRowInfo) );
11220 break;
11221 }
11224 {
11225 newRowInfo->member = member;
11226 cut_arc_id cutArc = newRow->reducedMembers[reducedMember].firstCutArc;
11227 spqr_arc arc = newRow->cutArcs[cutArc].arc;
11228 newRowInfo->reversed = arcIsReversedNonRigid(dec, arc) == newRow->cutArcs[cutArc].arcReversed;
11229 if( type == SPQR_MEMBERTYPE_LOOP )
11230 {
11231 if( getNumMemberArcs(dec, member) == 2 )
11232 {
11233 changeLoopToSeries(dec, member);
11234 }
11235 }
11236 break;
11237 }
11239 default:
11240 {
11241 SCIPABORT();
11242 return SCIP_ERROR;
11243 }
11244 }
11245
11246 return SCIP_OKAY;
11247 }
11248
11249 SCIP_CALL( mergeTree(dec, newRow, component->root, newRowInfo) );
11250
11251 return SCIP_OKAY;
11252}
11253
11254/** Create the network row addition data structure. */
11255static
11257 BMS_BLKMEM* blkmem, /**< Block memory */
11258 SCIP_NETROWADD** prowadd /**< Pointer to store the new row addition struct at */
11259 )
11260{
11261 assert(blkmem);
11262 SCIP_ALLOC( BMSallocBlockMemory(blkmem, prowadd) );
11263 SCIP_NETROWADD* newRow = *prowadd;
11264
11265 newRow->remainsNetwork = TRUE;
11266
11267 newRow->reducedMembers = NULL;
11268 newRow->memReducedMembers = 0;
11269 newRow->numReducedMembers = 0;
11270
11271 newRow->reducedComponents = NULL;
11272 newRow->memReducedComponents = 0;
11273 newRow->numReducedComponents = 0;
11274
11275 newRow->memberInformation = NULL;
11276 newRow->memMemberInformation = 0;
11277 newRow->numMemberInformation = 0;
11278
11279 newRow->cutArcs = NULL;
11280 newRow->memCutArcs = 0;
11281 newRow->numCutArcs = 0;
11283
11284 newRow->childrenStorage = NULL;
11285 newRow->memChildrenStorage = 0;
11286 newRow->numChildrenStorage = 0;
11287
11288 newRow->newRowIndex = SPQR_INVALID_ROW;
11289
11290 newRow->newColumnArcs = NULL;
11291 newRow->newColumnReversed = NULL;
11292 newRow->memColumnArcs = 0;
11293 newRow->numColumnArcs = 0;
11294
11295 newRow->leafMembers = NULL;
11296 newRow->numLeafMembers = 0;
11297 newRow->memLeafMembers = 0;
11298
11299 newRow->decompositionColumnArcs = NULL;
11301 newRow->memDecompositionColumnArcs = 0;
11302 newRow->numDecompositionColumnArcs = 0;
11303
11304 newRow->isArcCut = NULL;
11305 newRow->isArcCutReversed = NULL;
11306 newRow->memIsArcCut = 0;
11307
11308 newRow->nodeColors = NULL;
11309 newRow->memNodeColors = 0;
11310
11311 newRow->articulationNodes = NULL;
11312 newRow->memArticulationNodes = 0;
11313 newRow->numArticulationNodes = 0;
11314
11316 newRow->memNodeSearchInfo = 0;
11317
11318 newRow->crossingPathCount = NULL;
11319 newRow->memCrossingPathCount = 0;
11320
11321 newRow->intersectionDFSData = NULL;
11322 newRow->memIntersectionDFSData = 0;
11323
11324 newRow->colorDFSData = NULL;
11325 newRow->memColorDFSData = 0;
11326
11327 newRow->artDFSData = NULL;
11328 newRow->memArtDFSData = 0;
11329
11332
11333 newRow->intersectionPathDepth = NULL;
11334 newRow->memIntersectionPathDepth = 0;
11335
11336 newRow->intersectionPathParent = NULL;
11337 newRow->memIntersectionPathParent = 0;
11338
11339 newRow->mergeTreeCallData = NULL;
11340 newRow->memMergeTreeCallData = 0;
11341
11342 newRow->temporaryColorArray = NULL;
11343 newRow->memTemporaryColorArray = 0;
11344
11345 return SCIP_OKAY;
11346}
11347
11348/** Frees the network row addition data structure. */
11349static
11351 BMS_BLKMEM* blkmem, /**< Block memory */
11352 SCIP_NETROWADD** prowadd /**< Pointer to row addition struct to be freed */
11353 )
11354{
11355 assert(*prowadd);
11356
11357 SCIP_NETROWADD* newRow = *prowadd;
11358
11361 BMSfreeBlockMemoryArray(blkmem, &newRow->artDFSData, newRow->memArtDFSData);
11362 BMSfreeBlockMemoryArray(blkmem, &newRow->colorDFSData, newRow->memColorDFSData);
11370 BMSfreeBlockMemoryArray(blkmem, &newRow->nodeColors, newRow->memNodeColors);
11371 BMSfreeBlockMemoryArray(blkmem, &newRow->isArcCut, newRow->memIsArcCut);
11372 BMSfreeBlockMemoryArray(blkmem, &newRow->isArcCutReversed, newRow->memIsArcCut);
11375 BMSfreeBlockMemoryArray(blkmem, &newRow->newColumnArcs, newRow->memColumnArcs);
11376 BMSfreeBlockMemoryArray(blkmem, &newRow->newColumnReversed, newRow->memColumnArcs);
11378 BMSfreeBlockMemoryArray(blkmem, &newRow->cutArcs, newRow->memCutArcs);
11381 BMSfreeBlockMemoryArray(blkmem, &newRow->reducedMembers, newRow->memReducedMembers);
11382 BMSfreeBlockMemoryArray(blkmem, &newRow->leafMembers, newRow->memLeafMembers);
11383 BMSfreeBlockMemory(blkmem, prowadd);
11384}
11385
11386/** Checks if the given row can be added to the network decomposition. */
11387static
11389 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
11390 SCIP_NETROWADD* rowadd, /**< The network matrix row addition data structure */
11391 int row, /**< The row to be checked */
11392 const int* nonzcols, /**< The column indices of the row's nonzero values */
11393 const double* nonzvals, /**< The matrix entries of the row's nonzeroes */
11394 int nnonzs /**< The number of nonzeroes in the row */
11395 )
11396{
11397 assert(dec);
11398 assert(rowadd);
11399 assert(nnonzs == 0 || nonzcols);
11400
11401 /* A row can only be added once */
11402 if( netMatDecDataContainsRow(dec,row) )
11403 {
11404 return SCIP_INVALIDDATA;
11405 }
11406
11407 rowadd->remainsNetwork = TRUE;
11408 cleanUpPreviousIteration(dec, rowadd);
11409
11410 SCIP_CALL( newRowUpdateRowInformation(dec, rowadd, row, nonzcols, nonzvals, nnonzs) );
11413
11414 SCIP_CALL( determineLeafReducedMembers(dec, rowadd) );
11415 SCIP_CALL( allocateRigidSearchMemory(dec, rowadd) );
11416 SCIP_CALL( allocateTreeSearchMemory(dec, rowadd) );
11417 //Check for each component if the cut arcs propagate through a row tree marker to a cut arc in another component
11418 //From the leafs inward.
11419 propagateComponents(dec, rowadd);
11420 //It can happen that we are not graphic by some of the checked components.
11421 //In that case, further checking may lead to errors as some invariants that the code assumes will be broken.
11422 if( rowadd->remainsNetwork )
11423 {
11424 for( int i = 0; i < rowadd->numReducedComponents; ++i )
11425 {
11426 determineMergeableTypes(dec, rowadd, rowadd->reducedComponents[i].root);
11427 //exit early if one is not graphic
11428 if( !rowadd->remainsNetwork )
11429 {
11430 break;
11431 }
11432 }
11433 }
11434
11436
11437 return SCIP_OKAY;
11438}
11439
11440/** Adds the last checked row to the network decomposition. */
11441static
11443 SCIP_NETMATDECDATA* dec, /**< The network matrix decomposition */
11444 SCIP_NETROWADD* rowadd /**< The network matrix row addition data structure */
11445 )
11446{
11447 assert(rowadd->remainsNetwork);
11448 if( rowadd->numReducedComponents == 0 )
11449 {
11450 spqr_member newMember = SPQR_INVALID_MEMBER;
11452 rowadd->numColumnArcs, rowadd->newRowIndex, &newMember) );
11453 }
11454 else if( rowadd->numReducedComponents == 1 )
11455 {
11457 SCIP_CALL( transformComponentRowAddition(dec, rowadd, &rowadd->reducedComponents[0], &information) );
11458
11459 if( rowadd->numColumnArcs == 0 )
11460 {
11461 spqr_arc rowArc = SPQR_INVALID_ARC;
11462 SCIP_CALL( createRowArc(dec, information.member, &rowArc, rowadd->newRowIndex, information.reversed) );
11463 if( SPQRnodeIsValid(information.head))
11464 {
11465 assert(SPQRnodeIsValid(information.tail));
11466 assert(SPQRarcIsValid(information.representative));
11467 setArcHeadAndTail(dec, rowArc, findNode(dec, information.head), findNode(dec, information.tail));
11468 arcSetRepresentative(dec, rowArc, information.representative);
11469 arcSetReversed(dec, rowArc, information.reversed != arcIsReversedNonRigid(dec, information.representative));
11470 }
11471 }
11472 else
11473 {
11474 spqr_member new_row_parallel = SPQR_INVALID_MEMBER;
11476 rowadd->newRowIndex, &new_row_parallel) );
11477 spqr_arc markerArc = SPQR_INVALID_ARC;
11478 spqr_arc ignore = SPQR_INVALID_ARC;
11479 SCIP_CALL( createMarkerPairWithReferences(dec, information.member, new_row_parallel, TRUE,
11480 information.reversed, FALSE,
11481 &markerArc, &ignore) );
11482 if( SPQRnodeIsValid(information.head))
11483 {
11484 assert(SPQRnodeIsValid(information.tail));
11485 assert(SPQRarcIsValid(information.representative));
11486 setArcHeadAndTail(dec, markerArc, findNode(dec, information.head), findNode(dec, information.tail));
11487 arcSetRepresentative(dec, markerArc, information.representative);
11488 arcSetReversed(dec, markerArc,
11489 information.reversed != arcIsReversedNonRigid(dec, information.representative));
11490 }
11491 }
11492 if( getMemberType(dec, information.member) == SPQR_MEMBERTYPE_LOOP )
11493 {
11494 assert(getNumMemberArcs(dec, information.member) == 2 || getNumMemberArcs(dec, information.member) == 3);
11495 if( getNumMemberArcs(dec, information.member) == 3 )
11496 {
11497 changeLoopToSeries(dec, information.member);
11498 }
11499 }
11500 }
11501 else
11502 {
11503#ifndef NDEBUG
11504 int numDecComponentsBefore = numConnectedComponents(dec);
11505#endif
11506 spqr_member new_row_parallel = SPQR_INVALID_MEMBER;
11508 rowadd->newRowIndex, &new_row_parallel) );
11509 for( int i = 0; i < rowadd->numReducedComponents; ++i )
11510 {
11512
11513 SCIP_CALL( transformComponentRowAddition(dec, rowadd, &rowadd->reducedComponents[i], &information) );
11514 if( getMemberType(dec, information.member) == SPQR_MEMBERTYPE_LOOP )
11515 {
11516 assert(getNumMemberArcs(dec, information.member) == 1);
11517 spqr_arc arc = getFirstMemberArc(dec, information.member);
11518 assert(rowadd->isArcCut[arc]);
11519 moveArcToNewMember(dec, arc, information.member, new_row_parallel);
11520 arcSetReversed(dec, arc, rowadd->isArcCutReversed[arc]);
11521 dec->members[information.member].type = SPQR_MEMBERTYPE_UNASSIGNED;
11522 }
11523 else
11524 {
11525 reorderComponent(dec, information.member);//Make sure the new component is the root of the local decomposition tree
11526 spqr_arc markerArc = SPQR_INVALID_ARC;
11527 spqr_arc ignore = SPQR_INVALID_ARC;
11528 SCIP_CALL( createMarkerPairWithReferences(dec, new_row_parallel, information.member, FALSE,
11529 FALSE, information.reversed,
11530 &ignore, &markerArc) );
11531 if( SPQRnodeIsValid(information.head))
11532 {
11533 assert(SPQRnodeIsValid(information.tail));
11534 assert(SPQRarcIsValid(information.representative));
11535 setArcHeadAndTail(dec, markerArc, findNode(dec, information.head), findNode(dec, information.tail));
11536 arcSetRepresentative(dec, markerArc, information.representative);
11537 arcSetReversed(dec, markerArc,
11538 information.reversed != arcIsReversedNonRigid(dec, information.representative));
11539 }
11540 }
11541 }
11543 assert(numConnectedComponents(dec) == ( numDecComponentsBefore - rowadd->numReducedComponents + 1 ));
11544 }
11545
11546 return SCIP_OKAY;
11547}
11548
11549/** Returns whether the last checked row can be added to the network decomposition. */
11550static
11552 const SCIP_NETROWADD* rowadd /**< The network matrix row addition data structure */
11553 )
11554{
11555 return rowadd->remainsNetwork;
11556}
11557
11558/** A generic data structure that stores a decomposition and can perform both column and row additions. */
11560{
11564};
11565
11567 BMS_BLKMEM* blkmem, /**< Block memory */
11568 SCIP_NETMATDEC** pdec, /**< buffer to store pointer to created decomposition */
11569 int nrows, /**< The maximal number of rows that the decomposition can expect */
11570 int ncols /**< The maximal number of columns that the decomposition can expect */
11571 )
11572{
11573 SCIP_ALLOC( BMSallocBlockMemory(blkmem,pdec) );
11574 SCIP_NETMATDEC* dec = *pdec;
11575
11576 dec->dec = NULL;
11577 SCIP_CALL( netMatDecDataCreate(blkmem, &dec->dec, nrows, ncols) );
11578 dec->rowadd = NULL;
11579 dec->coladd = NULL;
11580
11581 return SCIP_OKAY;
11582}
11583
11585 SCIP_NETMATDEC** pdec /**< pointer to the network matrix decomposition to freed */
11586 )
11587{
11588 SCIP_NETMATDEC* dec = *pdec;
11589 BMS_BLKMEM* blkmem = dec->dec->env;
11590
11591 if( dec->coladd != NULL )
11592 {
11593 netcoladdFree(blkmem, &dec->coladd);
11594 }
11595 if( dec->rowadd != NULL )
11596 {
11597 netrowaddFree(blkmem, &dec->rowadd);
11598 }
11599 netMatDecDataFree(&dec->dec);
11600 BMSfreeBlockMemory(blkmem,pdec);
11601}
11602
11604 SCIP_NETMATDEC* dec, /**< Network matrix decomposition */
11605 int column, /**< The column to add */
11606 int* nonzrows, /**< The column's nonzero row indices */
11607 double* nonzvals, /**< The column's nonzero entries */
11608 int nnonzs, /**< The number of nonzeros in the column */
11609 SCIP_Bool* success /**< Buffer to store whether the column was added */
11610 )
11611{
11612 if( dec->coladd == NULL )
11613 {
11614 SCIP_CALL( netcoladdCreate(dec->dec->env, &dec->coladd) );
11615 }
11616
11617 SCIP_CALL( netcoladdCheck(dec->dec, dec->coladd, column, nonzrows, nonzvals, nnonzs) );
11618 *success = netcoladdRemainsNetwork(dec->coladd);
11619 if( *success )
11620 {
11621 SCIP_CALL( netcoladdAdd(dec->dec, dec->coladd) );
11622 }
11623
11624 return SCIP_OKAY;
11625}
11626
11628 SCIP_NETMATDEC* dec, /**< Network matrix decomposition */
11629 int row, /**< The row to add */
11630 int* nonzcols, /**< The row's nonzero column indices */
11631 double* nonzvals, /**< The row's nonzero entries */
11632 int nnonzs, /**< The number of nonzeros in the row */
11633 SCIP_Bool* success /**< Buffer to store whether the row was added */
11634 )
11635{
11636 if( dec->rowadd == NULL )
11637 {
11638 SCIP_CALL( netrowaddCreate(dec->dec->env, &dec->rowadd) );
11639 }
11640
11641 SCIP_CALL( netrowaddCheck(dec->dec, dec->rowadd, row, nonzcols, nonzvals, nnonzs) );
11642 *success = netrowaddRemainsNetwork(dec->rowadd);
11643 if( *success )
11644 {
11645 SCIP_CALL( netrowaddAdd(dec->dec, dec->rowadd) );
11646 }
11647
11648 return SCIP_OKAY;
11649}
11650
11652 SCIP_NETMATDEC* dec, /**< The network matrix decomposition */
11653 int row /**< The row index that is checked */
11654 )
11655{
11656 return netMatDecDataContainsRow(dec->dec, row);
11657}
11658
11660 SCIP_NETMATDEC* dec, /**< The network matrix decomposition */
11661 int column /**< The column index that is checked */
11662 )
11663{
11664 return netMatDecDataContainsColumn(dec->dec, column);
11665}
11666
11668 SCIP_NETMATDEC* dec, /**< The network matrix decomposition */
11669 int* componentrows, /**< Pointer to the array of rows to delete */
11670 int nrows, /**< The number of rows to delete */
11671 int* componentcols, /**< Pointer to the array of columns to delete */
11672 int ncols /**< The number of columns to delete */
11673 )
11674{
11675 netMatDecDataRemoveComponent(dec->dec, componentrows, nrows, componentcols, ncols);
11676}
11677
11679 SCIP_NETMATDEC* dec /**< The network matrix decomposition */
11680 )
11681{
11682 return netMatDecDataIsMinimal(dec->dec);
11683}
11684
11686 BMS_BUFMEM* bufmem, /**< Buffer memory */
11687 SCIP_NETMATDEC* dec, /**< The network matrix decomposition */
11688 int column, /**< The column to check */
11689 int* nonzrowidx, /**< Array with the column's nonzero row indices */
11690 double* nonzvals, /**< Array with the column's nonzero values */
11691 int nnonzs, /**< Number of nonzeros in the column */
11692 int* pathrowstorage, /**< A buffer to hold the computed path's rows. Should have size equal or
11693 * greater than the number of rows in the decomposition. */
11694 SCIP_Bool* pathsignstorage /**< A buffer to store the computed path's row signs. Should have size
11695 * equal or greater than the number of rows in the decomposition. */
11696 )
11697{
11698 return netMatDecDataVerifyCycle(bufmem, dec->dec, column, nonzrowidx, nonzvals, nnonzs, pathrowstorage, pathsignstorage);
11699}
11700
11702 SCIP_NETMATDEC* dec, /**< The network matrix decomposition */
11703 BMS_BLKMEM * blkmem, /**< The block memory to use for the created digraph */
11704 SCIP_DIGRAPH** pdigraph, /**< Pointer to the pointer to store the created digraph */
11705 SCIP_Bool createrowarcs /**< Should the row arcs be added to the created digraph? */
11706 )
11707{
11708 return netMatDecDataCreateDiGraph(dec->dec, blkmem, pdigraph, createrowarcs);
11709}
SCIP_VAR * a
Definition: circlepacking.c:66
SCIP_VAR ** b
Definition: circlepacking.c:65
#define NULL
Definition: def.h:248
#define SCIP_Bool
Definition: def.h:91
#define MIN(x, y)
Definition: def.h:224
#define SCIP_ALLOC(x)
Definition: def.h:366
#define TRUE
Definition: def.h:93
#define FALSE
Definition: def.h:94
#define MAX(x, y)
Definition: def.h:220
#define SCIPABORT()
Definition: def.h:327
#define SCIP_CALL(x)
Definition: def.h:355
SCIP_RETCODE SCIPdigraphAddArc(SCIP_DIGRAPH *digraph, int startnode, int endnode, void *data)
Definition: misc.c:7739
SCIP_Bool SCIPnetmatdecVerifyCycle(BMS_BUFMEM *bufmem, SCIP_NETMATDEC *dec, int column, int *nonzrowidx, double *nonzvals, int nnonzs, int *pathrowstorage, SCIP_Bool *pathsignstorage)
Definition: network.c:11685
SCIP_RETCODE SCIPnetmatdecCreateDiGraph(SCIP_NETMATDEC *dec, BMS_BLKMEM *blkmem, SCIP_DIGRAPH **pdigraph, SCIP_Bool createrowarcs)
Definition: network.c:11701
SCIP_Bool SCIPnetmatdecContainsRow(SCIP_NETMATDEC *dec, int row)
Definition: network.c:11651
void SCIPnetmatdecRemoveComponent(SCIP_NETMATDEC *dec, int *componentrows, int nrows, int *componentcols, int ncols)
Definition: network.c:11667
SCIP_Bool SCIPnetmatdecContainsColumn(SCIP_NETMATDEC *dec, int column)
Definition: network.c:11659
SCIP_RETCODE SCIPnetmatdecTryAddRow(SCIP_NETMATDEC *dec, int row, int *nonzcols, double *nonzvals, int nnonzs, SCIP_Bool *success)
Definition: network.c:11627
SCIP_RETCODE SCIPnetmatdecCreate(BMS_BLKMEM *blkmem, SCIP_NETMATDEC **pdec, int nrows, int ncols)
Definition: network.c:11566
SCIP_RETCODE SCIPnetmatdecTryAddCol(SCIP_NETMATDEC *dec, int column, int *nonzrows, double *nonzvals, int nnonzs, SCIP_Bool *success)
Definition: network.c:11603
SCIP_Bool SCIPnetmatdecIsMinimal(SCIP_NETMATDEC *dec)
Definition: network.c:11678
void SCIPnetmatdecFree(SCIP_NETMATDEC **pdec)
Definition: network.c:11584
void SCIPswapInts(int *value1, int *value2)
Definition: misc.c:10485
void SCIPsortIntInt(int *intarray1, int *intarray2, int len)
memory allocation routines
#define BMSfreeBlockMemory(mem, ptr)
Definition: memory.h:465
#define BMSallocBlockMemory(mem, ptr)
Definition: memory.h:451
#define BMSfreeBufferMemoryArray(mem, ptr)
Definition: memory.h:742
#define BMSallocBlockMemoryArray(mem, ptr, num)
Definition: memory.h:454
#define BMSfreeBlockMemoryArray(mem, ptr, num)
Definition: memory.h:467
#define BMSreallocBlockMemoryArray(mem, ptr, oldnum, newnum)
Definition: memory.h:458
#define BMSallocClearBlockMemoryArray(mem, ptr, num)
Definition: memory.h:455
#define BMSallocBufferMemoryArray(mem, ptr, num)
Definition: memory.h:731
struct BMS_BlkMem BMS_BLKMEM
Definition: memory.h:437
SCIP_RETCODE SCIPdigraphCreate(SCIP_DIGRAPH **digraph, BMS_BLKMEM *blkmem, int nnodes)
Definition: misc.c:7454
internal miscellaneous methods
static SCIP_RETCODE splitAndMergeRigid(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id smallMember, SCIP_Bool largeIsParent, NewRowInformation *const newRowInfo, spqr_member member)
Definition: network.c:10931
static reduced_member_id createReducedMembersToRoot(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, const spqr_member firstMember)
Definition: network.c:3820
static spqr_node findNode(SCIP_NETMATDECDATA *dec, spqr_node node)
Definition: network.c:456
static spqr_arc getPreviousNodeArc(SCIP_NETMATDECDATA *dec, spqr_arc arc, spqr_node node)
Definition: network.c:669
static SCIP_RETCODE transformPath(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, SPQRColReducedComponent *component, NewColInformation *newColInfo)
Definition: network.c:6378
static void mergeMemberArcList(SCIP_NETMATDECDATA *dec, spqr_member toMergeInto, spqr_member toRemove)
Definition: network.c:2683
static SCIP_Bool SPQRelementIsRow(spqr_element element)
Definition: network.c:194
int spqr_matrix_size
Definition: network.c:137
static SplitOrientation getRelativeOrientationRigid(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id reducedId, spqr_arc arcToNext)
Definition: network.c:9082
static SCIP_Bool SPQRcolIsValid(spqr_col col)
Definition: network.c:178
static void setTerminalMember(NewColInformation *info, spqr_member member)
Definition: network.c:5542
static SCIP_RETCODE netrowaddCheck(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *rowadd, int row, const int *nonzcols, const double *nonzvals, int nnonzs)
Definition: network.c:11388
static void netMatDecDataRemoveComponent(SCIP_NETMATDECDATA *dec, const int *componentRows, int numRows, const int *componentCols, int numCols)
Definition: network.c:2847
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)
Definition: network.c:10195
int spqr_arc
Definition: network.c:312
static SCIP_Bool nodeIsRepresentative(const SCIP_NETMATDECDATA *dec, spqr_node node)
Definition: network.c:440
static SCIP_Bool SPQRnodeIsValid(spqr_node node)
Definition: network.c:298
static SCIP_RETCODE splitParallel(SCIP_NETMATDECDATA *dec, spqr_member parallel, spqr_arc arc1, spqr_arc arc2, spqr_member *childParallel)
Definition: network.c:5569
static void determinePathTypes(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, SPQRColReducedComponent *component)
Definition: network.c:5055
static void articulationPoints(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, ArticulationNodeInformation *nodeInfo, reduced_member_id reducedMember)
Definition: network.c:7969
ReducedMemberType
Definition: network.c:3507
@ REDUCEDMEMBER_TYPE_NOT_NETWORK
Definition: network.c:3513
@ REDUCEDMEMBER_TYPE_MERGED
Definition: network.c:3512
@ REDUCEDMEMBER_TYPE_UNASSIGNED
Definition: network.c:3508
@ REDUCEDMEMBER_TYPE_CYCLE
Definition: network.c:3509
static SCIP_RETCODE newColUpdateColInformation(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, spqr_col column, const spqr_row *nonzeroRows, const double *nonzeroValues, int numNonzeros)
Definition: network.c:4206
static SCIP_Bool cutArcIsInvalid(const cut_arc_id arc)
Definition: network.c:6776
static void determineSingleSeriesType(SCIP_NETROWADD *newRow, reduced_member_id reducedMember)
Definition: network.c:8854
#define INVALID_PATH_ARC
Definition: network.c:3439
static void moveArcToNewMember(SCIP_NETMATDECDATA *dec, spqr_arc arc, spqr_member oldMember, spqr_member newMember)
Definition: network.c:2641
static SCIP_Bool SPQRarcIsValid(spqr_arc arc)
Definition: network.c:326
static SCIP_Bool SPQRmemberIsInvalid(spqr_member member)
Definition: network.c:264
static int decompositionGetFundamentalCycleRows(const SCIP_NETMATDECDATA *dec, spqr_col column, spqr_row *output, SCIP_Bool *computedSignStorage)
Definition: network.c:2241
static spqr_member findArcChildMember(SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:1031
RowReducedMemberType
Definition: network.c:6812
@ TYPE_MERGED
Definition: network.c:6815
@ TYPE_PROPAGATED
Definition: network.c:6814
@ TYPE_NOT_NETWORK
Definition: network.c:6816
@ TYPE_UNDETERMINED
Definition: network.c:6813
static SCIP_RETCODE netMatDecDataCreateDiGraph(SCIP_NETMATDECDATA *dec, BMS_BLKMEM *blkmem, SCIP_DIGRAPH **pdigraph, SCIP_Bool createrowarcs)
Definition: network.c:3111
static ReducedMemberType checkLeaf(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, reduced_member_id leaf, spqr_arc toParent, reduced_member_id parent, spqr_arc toChild)
Definition: network.c:5177
static void determineSingleParallelType(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id reducedMember)
Definition: network.c:8813
static spqr_element SPQRrowToElement(spqr_row row)
Definition: network.c:222
static SCIP_RETCODE createMarkerPair(SCIP_NETMATDECDATA *dec, spqr_member parentMember, spqr_member childMember, SCIP_Bool parentIsTree, SCIP_Bool childMarkerReversed, SCIP_Bool parentMarkerReversed)
Definition: network.c:2598
static void cleanUpPreviousIteration(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow)
Definition: network.c:7634
static void determineMergeableTypes(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id root)
Definition: network.c:9482
static SCIP_Bool pathArcIsValid(const path_arc_id arc)
Definition: network.c:3452
static void determineSplitTypeRigid(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id reducedId, spqr_member member, spqr_arc marker, SplitOrientation previousOrientation)
Definition: network.c:9322
static void rigidGetSplittableArticulationPointsOnPath(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, const reduced_member_id toCheck, spqr_node firstNode, spqr_node secondNode)
Definition: network.c:8289
#define SPQR_INVALID_ARC
Definition: network.c:313
static SCIP_RETCODE createConnectedSeries(SCIP_NETMATDECDATA *dec, spqr_row *rows, SCIP_Bool *reversed, int numRows, spqr_col col, spqr_member *pMember)
Definition: network.c:2122
#define MARKER_ROW_ELEMENT
Definition: network.c:188
static SCIP_RETCODE createStandaloneParallel(SCIP_NETMATDECDATA *dec, spqr_col *columns, SCIP_Bool *reversed, int num_columns, spqr_row row, spqr_member *pMember)
Definition: network.c:2026
int spqr_node
Definition: network.c:284
static void zeroOutColors(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, const spqr_node firstRemoveNode)
Definition: network.c:7578
static SCIP_RETCODE transformComponentRowAddition(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, SPQRRowReducedComponent *component, NewRowInformation *const newRowInfo)
Definition: network.c:11195
static SCIP_RETCODE allocateTreeSearchMemory(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow)
Definition: network.c:8762
int reduced_member_id
Definition: network.c:3479
#define SPQR_INVALID_COL
Definition: network.c:143
static spqr_member findMemberParent(SCIP_NETMATDECDATA *dec, spqr_member member)
Definition: network.c:985
static SCIP_RETCODE splitAndMergeParallel(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id smallMember, SCIP_Bool largeIsParent, NewRowInformation *const newRowInfo, spqr_member member)
Definition: network.c:10799
static SCIP_RETCODE createPathArcs(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol)
Definition: network.c:4151
static SCIP_RETCODE columnTransformSingleRigid(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, reduced_member_id reducedMemberId, spqr_member member, NewColInformation *newColInfo)
Definition: network.c:6451
static spqr_member findArcMemberNoCompression(const SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:967
static void determineSplitTypeNext(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id current, reduced_member_id next, SCIP_Bool currentIsParent)
Definition: network.c:9438
static spqr_arc markerOfParent(const SCIP_NETMATDECDATA *dec, spqr_member member)
Definition: network.c:1970
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)
Definition: network.c:10075
static SCIP_RETCODE splitSeries(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, SPQRColReducedMember *reducedMember, spqr_member member, spqr_member *loopMember, NewColInformation *newColInfo)
Definition: network.c:5607
static spqr_member findMember(SCIP_NETMATDECDATA *dec, spqr_member member)
Definition: network.c:855
static spqr_arc markerToParent(const SCIP_NETMATDECDATA *dec, spqr_member member)
Definition: network.c:1935
static void mergeNodeArcList(SCIP_NETMATDECDATA *dec, spqr_node toMergeInto, spqr_node toRemove)
Definition: network.c:698
static void updateMemberType(const SCIP_NETMATDECDATA *dec, spqr_member member, SPQRMemberType type)
Definition: network.c:1919
static void determineSingleRigidType(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, reduced_member_id reducedMember)
Definition: network.c:4353
#define NEWROWINFORMATION_EMPTY
Definition: network.c:7016
static int numConnectedComponents(const SCIP_NETMATDECDATA *dec)
Definition: network.c:2545
static void determinePathRigidType(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, reduced_member_id reducedMember, MemberPathType previousType, spqr_arc source, spqr_arc target)
Definition: network.c:4669
static void addArcToMemberArcList(SCIP_NETMATDECDATA *dec, spqr_arc arc, spqr_member member)
Definition: network.c:1576
static void arcFlipReversed(SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:754
static spqr_node findEffectiveArcTailNoCompression(const SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:1262
#define INVALID_REDUCED_MEMBER
Definition: network.c:3480
static void determineRigidPath(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, SPQRColReducedMember *redMem)
Definition: network.c:4289
static void removeArcFromNodeArcList(SCIP_NETMATDECDATA *dec, spqr_arc arc, spqr_node node, SCIP_Bool nodeIsHead)
Definition: network.c:1746
#define SPQR_INVALID_MEMBER
Definition: network.c:260
static spqr_member findArcChildMemberNoCompression(const SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:1050
static SCIP_Bool netMatDecDataVerifyCycle(BMS_BUFMEM *bufmem, const SCIP_NETMATDECDATA *dec, int column, const int *nonzrowidx, const double *nonzvals, int num_rows, int *pathrowstorage, SCIP_Bool *pathsignstorage)
Definition: network.c:2432
static SCIP_RETCODE rigidTransformArcIntoCycle(SCIP_NETMATDECDATA *dec, const spqr_member member, const spqr_arc arc, const SCIP_Bool reverseArcDirection, NewRowInformation *const newRowInfo)
Definition: network.c:9614
static int nodeDegree(SCIP_NETMATDECDATA *dec, spqr_node node)
Definition: network.c:1890
static int getNumMembers(const SCIP_NETMATDECDATA *dec)
Definition: network.c:2011
static SCIP_RETCODE splitFirstLeaf(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id leaf, NewRowInformation *const newRowInfo)
Definition: network.c:10428
static void setTerminalRepresentative(NewColInformation *info, spqr_arc representative)
Definition: network.c:5554
static spqr_member findMemberNoCompression(const SCIP_NETMATDECDATA *dec, spqr_member member)
Definition: network.c:892
static void decreaseNumConnectedComponents(SCIP_NETMATDECDATA *dec, int by)
Definition: network.c:2779
static SplitOrientation getRelativeOrientationParallel(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id reducedId, spqr_arc arcToNext)
Definition: network.c:9133
static void cleanUpMemberInformation(SCIP_NETCOLADD *newCol)
Definition: network.c:4079
static SCIP_RETCODE netcoladdCreate(BMS_BLKMEM *blkmem, SCIP_NETCOLADD **pcoladd)
Definition: network.c:3725
static spqr_node findEffectiveArcHead(SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:1201
static spqr_element arcGetElement(const SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:1343
static SCIP_Bool SPQRrowIsValid(spqr_row row)
Definition: network.c:169
static spqr_node mergeNodes(SCIP_NETMATDECDATA *dec, spqr_node first, spqr_node second)
Definition: network.c:808
static spqr_arc getFirstNodeArc(const SCIP_NETMATDECDATA *dec, spqr_node node)
Definition: network.c:596
static SCIP_RETCODE transformComponent(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, SPQRColReducedComponent *component, NewColInformation *newColInfo)
Definition: network.c:6591
static void netrowaddFree(BMS_BLKMEM *blkmem, SCIP_NETROWADD **prowadd)
Definition: network.c:11350
static void setTerminalTail(NewColInformation *info, spqr_node node)
Definition: network.c:5516
static void setArcHeadAndTail(SCIP_NETMATDECDATA *dec, spqr_arc arc, spqr_node head, spqr_node tail)
Definition: network.c:1832
static SCIP_RETCODE columnTransformSingleParallel(SCIP_NETCOLADD *newCol, reduced_member_id reducedMemberId, spqr_member member, NewColInformation *newColInfo)
Definition: network.c:6409
static spqr_arc getNextNodeArcNoCompression(const SCIP_NETMATDECDATA *dec, spqr_arc arc, spqr_node node)
Definition: network.c:614
#define INVALID_CUT_ARC
Definition: network.c:6772
static SCIP_Bool isHead(MemberPathType type)
Definition: network.c:3537
static void addArcToNodeArcList(SCIP_NETMATDECDATA *dec, spqr_arc arc, spqr_node node, SCIP_Bool nodeIsHead)
Definition: network.c:1784
static void setDecompositionRowArc(SCIP_NETMATDECDATA *dec, spqr_row row, spqr_arc arc)
Definition: network.c:1398
int cut_arc_id
Definition: network.c:6771
static SCIP_RETCODE createStandaloneSeries(SCIP_NETMATDECDATA *dec, spqr_row *rows, SCIP_Bool *reversed, int numRows, spqr_col col, spqr_member *pMember)
Definition: network.c:2090
static void updateMemberParentInformation(SCIP_NETMATDECDATA *dec, const spqr_member newMember, const spqr_member toRemove)
Definition: network.c:1950
static void netcoladdFree(BMS_BLKMEM *blkmem, SCIP_NETCOLADD **pcoladd)
Definition: network.c:3789
static SCIP_Bool SPQRmemberIsValid(spqr_member member)
Definition: network.c:272
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)
Definition: network.c:2619
static SPQRMemberType getMemberType(const SCIP_NETMATDECDATA *dec, spqr_member member)
Definition: network.c:1904
static void determinePathSeriesType(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, reduced_member_id reducedMember, spqr_member member, MemberPathType previousType, spqr_arc source, spqr_arc target)
Definition: network.c:4473
static SCIP_RETCODE columnTransformSingleSeries(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, reduced_member_id reducedMemberId, spqr_member member, NewColInformation *newColInfo)
Definition: network.c:6427
static SCIP_RETCODE netrowaddAdd(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *rowadd)
Definition: network.c:11442
static spqr_node findArcHead(SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:540
int children_idx
Definition: network.c:3501
static void reorderComponent(SCIP_NETMATDECDATA *dec, spqr_member newRoot)
Definition: network.c:2792
static SCIP_RETCODE transformSingleParallel(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, const reduced_member_id reducedMember, const spqr_member member, NewRowInformation *info)
Definition: network.c:10061
static SCIP_Bool arcIsChildMarker(const SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:1065
static SplitOrientation getRelativeOrientationSeries(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id reducedId, spqr_arc arcToNext)
Definition: network.c:9158
static spqr_node findArcTailNoCompression(const SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:578
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)
Definition: network.c:3052
static SCIP_Bool isInto(MemberPathType type)
Definition: network.c:3528
static void changeLoopToSeries(SCIP_NETMATDECDATA *dec, spqr_member member)
Definition: network.c:2711
static spqr_node largestNodeID(const SCIP_NETMATDECDATA *dec)
Definition: network.c:2536
static void removeArcFromMemberArcList(SCIP_NETMATDECDATA *dec, spqr_arc arc, spqr_member member)
Definition: network.c:2149
static SCIP_RETCODE netrowaddCreate(BMS_BLKMEM *blkmem, SCIP_NETROWADD **prowadd)
Definition: network.c:11256
static spqr_member findArcMember(SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:951
MemberPathType
Definition: network.c:3519
@ OUT_HEAD
Definition: network.c:3522
@ INTO_TAIL
Definition: network.c:3521
@ OUT_TAIL
Definition: network.c:3523
@ INTO_HEAD
Definition: network.c:3520
int spqr_member
Definition: network.c:259
static SCIP_Bool reducedMemberIsValid(const reduced_member_id id)
Definition: network.c:3493
static SplitOrientation getRelativeOrientation(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id reducedId, spqr_arc arcToNext)
Definition: network.c:9183
static spqr_node findArcTail(SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:520
static spqr_member findMemberParentNoCompression(const SCIP_NETMATDECDATA *dec, spqr_member member)
Definition: network.c:1011
static void determineSingleComponentType(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, reduced_member_id reducedMember)
Definition: network.c:4374
static spqr_node findArcHeadNoCompression(const SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:560
static SCIP_RETCODE transformFirstPathMember(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, reduced_member_id reducedMember, NewColInformation *newColInfo, spqr_arc *representativeArc, spqr_member *mergedMember)
Definition: network.c:5929
static void changeLoopToParallel(SCIP_NETMATDECDATA *dec, spqr_member member)
Definition: network.c:2729
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)
Definition: network.c:6111
static SCIP_RETCODE createColumnArc(SCIP_NETMATDECDATA *dec, spqr_member member, spqr_arc *pArc, spqr_col column, SCIP_Bool reversed)
Definition: network.c:1674
static SCIP_Bool netrowaddRemainsNetwork(const SCIP_NETROWADD *rowadd)
Definition: network.c:11551
static void process_arc(spqr_row *cyclearcs, int *num_cycle_arcs, FindCycleCall *callStack, int *callStackSize, spqr_arc arc, const SCIP_NETMATDECDATA *dec, SCIP_Bool *cycledir, SCIP_Bool arcIsReversed)
Definition: network.c:2191
static void propagateComponents(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow)
Definition: network.c:8576
static SCIP_Bool reducedMemberIsInvalid(const reduced_member_id id)
Definition: network.c:3484
static void zeroOutColorsExceptNeighbourhood(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, const spqr_node articulationNode, const spqr_node startRemoveNode)
Definition: network.c:7780
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)
Definition: network.c:8471
static spqr_element SPQRcolumnToElement(spqr_col column)
Definition: network.c:242
static SCIP_Bool memberIsRepresentative(const SCIP_NETMATDECDATA *dec, spqr_member member)
Definition: network.c:841
static SCIP_RETCODE createMember(SCIP_NETMATDECDATA *dec, SPQRMemberType type, spqr_member *pMember)
Definition: network.c:1692
static SCIP_Bool netMatDecDataContainsColumn(SCIP_NETMATDECDATA *dec, int column)
Definition: network.c:1370
#define SPQR_INVALID_ROW
Definition: network.c:142
static void createCutArc(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, const spqr_arc arc, const reduced_member_id reducedMember, SCIP_Bool reversed)
Definition: network.c:7336
static spqr_node findNodeNoCompression(const SCIP_NETMATDECDATA *dec, spqr_node node)
Definition: network.c:493
static void determinePathParallelType(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, reduced_member_id reducedMember, spqr_member member, MemberPathType previousType, spqr_arc source, spqr_arc target)
Definition: network.c:4599
static void setTerminalReversed(NewColInformation *info, SCIP_Bool reversed)
Definition: network.c:5530
static spqr_node findEffectiveArcTail(SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:1219
static SCIP_RETCODE createReducedDecompositionCutArcs(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow)
Definition: network.c:7388
static SCIP_RETCODE createRowArc(SCIP_NETMATDECDATA *dec, spqr_member member, spqr_arc *pArc, spqr_row row, SCIP_Bool reversed)
Definition: network.c:1656
static Nodes rigidDetermineCandidateNodesFromAdjacentComponents(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, const reduced_member_id toCheck)
Definition: network.c:8692
#define NEWCOLINFORMATION_EMPTY
Definition: network.c:5498
static SCIP_RETCODE netMatDecDataCreate(BMS_BLKMEM *blkmem, SCIP_NETMATDECDATA **pdec, int nrows, int ncols)
Definition: network.c:1439
spqr_matrix_size spqr_col
Definition: network.c:139
static void determineSingleRowRigidType(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id reducedMember)
Definition: network.c:8780
static void addArticulationNode(SCIP_NETROWADD *newRow, spqr_node articulationNode)
Definition: network.c:7949
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)
Definition: network.c:8448
static SCIP_RETCODE createArc(SCIP_NETMATDECDATA *dec, spqr_member member, SCIP_Bool reversed, spqr_arc *pArc)
Definition: network.c:1604
static void createPathArc(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, const spqr_arc arc, const reduced_member_id reducedMember, SCIP_Bool reversed)
Definition: network.c:4099
int path_arc_id
Definition: network.c:3438
static void netMatDecDataFree(SCIP_NETMATDECDATA **pdec)
Definition: network.c:1513
static void arcSetRepresentative(SCIP_NETMATDECDATA *dec, spqr_arc arc, spqr_arc representative)
Definition: network.c:789
static SCIP_RETCODE constructRowReducedDecomposition(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow)
Definition: network.c:7187
static SCIP_RETCODE createChildMarker(SCIP_NETMATDECDATA *dec, spqr_member member, spqr_member child, SCIP_Bool isTree, spqr_arc *pArc, SCIP_Bool reversed)
Definition: network.c:2554
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)
Definition: network.c:6334
static spqr_node findEffectiveArcHeadNoCompression(const SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:1240
#define SPQR_INVALID_NODE
Definition: network.c:285
static void determineSplitTypeParallel(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id reducedId, spqr_arc marker, SplitOrientation previousOrientation)
Definition: network.c:9263
static spqr_arc getNextMemberArc(const SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:1546
static spqr_node determineAndColorSplitNode(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id id, spqr_node candidateOne, spqr_node candidateTwo)
Definition: network.c:8870
static int getNumMemberArcs(const SCIP_NETMATDECDATA *dec, spqr_member member)
Definition: network.c:1985
#define MARKER_COLUMN_ELEMENT
Definition: network.c:189
static spqr_member mergeMembers(SCIP_NETMATDECDATA *dec, spqr_member first, spqr_member second)
Definition: network.c:920
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)
Definition: network.c:2574
static SCIP_RETCODE mergeTree(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id root, NewRowInformation *const newRowInfo)
Definition: network.c:11120
static spqr_member largestMemberID(const SCIP_NETMATDECDATA *dec)
Definition: network.c:2518
static void rigidConnectedColoring(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, const reduced_member_id reducedMember, const spqr_node node, SCIP_Bool *const isGood)
Definition: network.c:8144
static void changeArcHead(SCIP_NETMATDECDATA *dec, spqr_arc arc, spqr_node oldHead, spqr_node newHead)
Definition: network.c:1858
static SCIP_RETCODE createConnectedParallel(SCIP_NETMATDECDATA *dec, spqr_col *columns, SCIP_Bool *reversed, int num_columns, spqr_row row, spqr_member *pMember)
Definition: network.c:2059
static SCIP_RETCODE determineLeafReducedMembers(const SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow)
Definition: network.c:7442
static spqr_arc mergeArcSigns(SCIP_NETMATDECDATA *dec, spqr_arc first, spqr_arc second, SCIP_Bool reflectRelative)
Definition: network.c:1285
int spqr_element
Definition: network.c:190
static SCIP_Bool SPQRarcIsInvalid(spqr_arc arc)
Definition: network.c:317
static int getNumNodes(const SCIP_NETMATDECDATA *dec)
Definition: network.c:2000
static void rigidConnectedColoringRecursive(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, spqr_node articulationNode, spqr_node firstProcessNode, COLOR_STATUS firstColor, SCIP_Bool *isGood)
Definition: network.c:8062
static void checkRigidLeaf(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, reduced_member_id leaf, spqr_arc toParent, reduced_member_id parent, spqr_arc toChild)
Definition: network.c:5144
static SCIP_RETCODE transformSingleRigid(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, const reduced_member_id reducedMember, const spqr_member member, NewRowInformation *const newRowInfo)
Definition: network.c:9728
static SCIP_Bool pathArcIsInvalid(const path_arc_id arc)
Definition: network.c:3443
static void intersectionOfAllPaths(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, const reduced_member_id toCheck, int *const nodeNumPaths)
Definition: network.c:7831
static void determineComponentTypes(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, SPQRColReducedComponent *component)
Definition: network.c:5412
static SCIP_Bool SPQRnodeIsInvalid(spqr_node node)
Definition: network.c:289
static spqr_row SPQRelementToRow(spqr_element element)
Definition: network.c:212
static void arcSetReversed(SCIP_NETMATDECDATA *dec, spqr_arc arc, SCIP_Bool reversed)
Definition: network.c:768
static SCIP_Bool netMatDecDataIsMinimal(const SCIP_NETMATDECDATA *dec)
Definition: network.c:2749
static void clearArcHeadAndTail(SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:1845
static SCIP_Bool arcIsReversedNonRigid(const SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:1329
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)
Definition: network.c:6264
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)
Definition: network.c:7022
static void propagateCycles(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol)
Definition: network.c:5277
static spqr_node checkNeighbourColoringArticulationNode(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, const spqr_node articulationNode, spqr_arc *const adjacentSplittingArc)
Definition: network.c:8219
static ArcSign findArcSign(SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:1121
static SCIP_RETCODE createNode(SCIP_NETMATDECDATA *dec, spqr_node *pNode)
Definition: network.c:1724
static SCIP_RETCODE allocateRigidSearchMemory(const SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow)
Definition: network.c:7469
static SCIP_RETCODE netcoladdCheck(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *coladd, int column, const int *nonzrows, const double *nonzvals, int nnonzs)
Definition: network.c:5439
static SCIP_Bool netMatDecDataContainsRow(SCIP_NETMATDECDATA *dec, int row)
Definition: network.c:1357
static SCIP_Bool netcoladdRemainsNetwork(const SCIP_NETCOLADD *newCol)
Definition: network.c:6647
static spqr_arc getFirstMemberArc(const SCIP_NETMATDECDATA *dec, spqr_member member)
Definition: network.c:1532
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)
Definition: network.c:8394
static void changeArcTail(SCIP_NETMATDECDATA *dec, spqr_arc arc, spqr_node oldTail, spqr_node newTail)
Definition: network.c:1874
static SCIP_RETCODE splitAndMergeSeries(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id smallMember, SCIP_Bool largeIsParent, NewRowInformation *const newRowInfo, spqr_member member)
Definition: network.c:10656
static spqr_arc getPreviousMemberArc(const SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:1561
static spqr_arc getDecompositionColumnArc(const SCIP_NETMATDECDATA *dec, spqr_col col)
Definition: network.c:1413
static void cleanupPreviousIteration(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol)
Definition: network.c:3675
spqr_matrix_size spqr_row
Definition: network.c:138
static spqr_arc getNextNodeArc(SCIP_NETMATDECDATA *dec, spqr_arc arc, spqr_node node)
Definition: network.c:641
static spqr_arc getDecompositionRowArc(const SCIP_NETMATDECDATA *dec, spqr_row row)
Definition: network.c:1426
static ArcSign findArcSignNoCompression(const SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:1172
static SCIP_Bool SPQRelementIsColumn(spqr_element element)
Definition: network.c:203
static SCIP_RETCODE netcoladdAdd(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol)
Definition: network.c:6659
static SCIP_RETCODE computeLeafMembers(const SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol)
Definition: network.c:4262
static void determineSplitTypeFirstLeaf(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id reducedId)
Definition: network.c:8958
static reduced_member_id createRowReducedMembersToRoot(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, const spqr_member firstMember)
Definition: network.c:7080
static void rigidFindStarNodes(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, const reduced_member_id toCheck)
Definition: network.c:7675
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)
Definition: network.c:8537
static spqr_col SPQRelementToColumn(spqr_element element)
Definition: network.c:232
static SCIP_Bool cutArcIsValid(const cut_arc_id arc)
Definition: network.c:6784
COLOR_STATUS
Definition: network.c:6863
@ COLOR_SOURCE
Definition: network.c:6865
@ COLOR_SINK
Definition: network.c:6866
@ UNCOLORED
Definition: network.c:6864
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)
Definition: network.c:6034
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)
Definition: network.c:5789
static SCIP_Bool SPQRrowIsInvalid(spqr_row row)
Definition: network.c:151
static void setDecompositionColumnArc(SCIP_NETMATDECDATA *dec, spqr_col col, spqr_arc arc)
Definition: network.c:1383
static void cleanUpRowMemberInformation(SCIP_NETROWADD *newRow)
Definition: network.c:9591
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)
Definition: network.c:10577
static SCIP_RETCODE splitParallelRowAddition(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, const reduced_member_id reducedMember, const spqr_member member, NewRowInformation *const newRowInfo)
Definition: network.c:9864
static SCIP_Bool arcIsRepresentative(const SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:1105
static void determinePathMemberType(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol, reduced_member_id reducedMember, spqr_member member, MemberPathType previousType, spqr_arc source, spqr_arc target)
Definition: network.c:5008
static spqr_arc largestArcID(const SCIP_NETMATDECDATA *dec)
Definition: network.c:2527
static SCIP_Bool arcIsTree(const SCIP_NETMATDECDATA *dec, spqr_arc arc)
Definition: network.c:1079
static void determineSplitTypeSeries(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id reducedId, spqr_arc marker, SplitOrientation previousOrientation)
Definition: network.c:9213
static void setTerminalHead(NewColInformation *info, spqr_node node)
Definition: network.c:5502
static SCIP_RETCODE constructReducedDecomposition(SCIP_NETMATDECDATA *dec, SCIP_NETCOLADD *newCol)
Definition: network.c:3932
static SCIP_Bool SPQRcolIsInvalid(spqr_col col)
Definition: network.c:160
static SCIP_RETCODE splitAndMerge(SCIP_NETMATDECDATA *dec, SCIP_NETROWADD *newRow, reduced_member_id smallMember, SCIP_Bool largeIsParent, NewRowInformation *const newRowInfo)
Definition: network.c:11077
SPQRMemberType
Definition: network.c:335
@ SPQR_MEMBERTYPE_RIGID
Definition: network.c:336
@ SPQR_MEMBERTYPE_PARALLEL
Definition: network.c:337
@ SPQR_MEMBERTYPE_SERIES
Definition: network.c:338
@ SPQR_MEMBERTYPE_UNASSIGNED
Definition: network.c:340
@ SPQR_MEMBERTYPE_LOOP
Definition: network.c:339
#define SCIPerrorMessage
Definition: pub_message.h:64
public data structures and miscellaneous methods
Methods for detecting network matrices.
SCIP callable library.
SCIP_Bool reversed
Definition: network.c:1095
spqr_arc representative
Definition: network.c:1094
spqr_arc arc
Definition: network.c:6846
spqr_node node
Definition: network.c:6845
spqr_node arcHead
Definition: network.c:6798
cut_arc_id nextMember
Definition: network.c:6800
SCIP_Bool arcReversed
Definition: network.c:6804
spqr_node arcTail
Definition: network.c:6799
cut_arc_id nextOverall
Definition: network.c:6802
spqr_arc arc
Definition: network.c:6797
spqr_node node
Definition: network.c:6831
spqr_arc nodeArc
Definition: network.c:6832
spqr_arc arc
Definition: network.c:2182
SCIP_Bool reversed
Definition: network.c:2183
reduced_member_id reducedMember
Definition: network.c:3597
reduced_member_id rootDepthMinimizer
Definition: network.c:3598
children_idx currentChild
Definition: network.c:6838
reduced_member_id id
Definition: network.c:6839
spqr_arc representative
Definition: network.c:5494
spqr_node tail
Definition: network.c:5493
SCIP_Bool reversed
Definition: network.c:5495
spqr_node head
Definition: network.c:5492
spqr_member member
Definition: network.c:5491
spqr_node head
Definition: network.c:7010
spqr_node tail
Definition: network.c:7011
SCIP_Bool reversed
Definition: network.c:7013
spqr_member member
Definition: network.c:7009
spqr_arc representative
Definition: network.c:7012
spqr_node first
Definition: network.c:8683
spqr_node second
Definition: network.c:8684
path_arc_id nextOverall
Definition: network.c:3470
spqr_arc arc
Definition: network.c:3466
spqr_node arcHead
Definition: network.c:3467
spqr_node arcTail
Definition: network.c:3468
path_arc_id nextMember
Definition: network.c:3469
SCIP_Bool reversed
Definition: network.c:3471
int * nodeInPathDegree
Definition: network.c:3641
SPQRColReducedMember * reducedMembers
Definition: network.c:3613
int memChildrenStorage
Definition: network.c:3632
int numReducedMembers
Definition: network.c:3616
int numDecompositionRowArcs
Definition: network.c:3666
int memLeafMembers
Definition: network.c:3670
spqr_col newColIndex
Definition: network.c:3652
int memMemberInformation
Definition: network.c:3625
spqr_member * leafMembers
Definition: network.c:3668
SCIP_Bool * arcInPath
Definition: network.c:3645
int numChildrenStorage
Definition: network.c:3633
int memReducedMembers
Definition: network.c:3615
PathArcListNode * pathArcs
Definition: network.c:3635
MemberInfo * memberInformation
Definition: network.c:3623
SCIP_Bool * arcInPathReversed
Definition: network.c:3646
int memCreateReducedMembersCallStack
Definition: network.c:3650
int memArcsInPath
Definition: network.c:3647
SCIP_Bool remainsNetwork
Definition: network.c:3611
int memNewRowArcs
Definition: network.c:3658
int memDecompositionRowArcs
Definition: network.c:3665
int * nodeOutPathDegree
Definition: network.c:3642
int memReducedComponents
Definition: network.c:3620
int numNewRowArcs
Definition: network.c:3659
CreateReducedMembersCallstack * createReducedMembersCallStack
Definition: network.c:3649
path_arc_id firstOverallPathArc
Definition: network.c:3639
SCIP_Bool * decompositionArcReversed
Definition: network.c:3663
spqr_arc * decompositionRowArcs
Definition: network.c:3661
int memNodePathDegree
Definition: network.c:3643
int numMemberInformation
Definition: network.c:3626
int numLeafMembers
Definition: network.c:3669
SCIP_Bool * newRowArcReversed
Definition: network.c:3656
SPQRColReducedComponent * reducedComponents
Definition: network.c:3618
reduced_member_id * childrenStorage
Definition: network.c:3628
int numReducedComponents
Definition: network.c:3621
spqr_row * newRowArcs
Definition: network.c:3654
SPQRNetworkDecompositionArc * arcs
Definition: network.c:413
spqr_arc * columnArcs
Definition: network.c:428
SPQRNetworkDecompositionMember * members
Definition: network.c:418
spqr_arc firstFreeArc
Definition: network.c:414
BMS_BLKMEM * env
Definition: network.c:430
SPQRNetworkDecompositionNode * nodes
Definition: network.c:422
int numConnectedComponents
Definition: network.c:432
spqr_arc * rowArcs
Definition: network.c:425
int memCreateReducedMembersCallstack
Definition: network.c:6990
ArticulationNodeInformation * articulationNodeSearchInfo
Definition: network.c:6973
int memMergeTreeCallData
Definition: network.c:7000
int * crossingPathCount
Definition: network.c:6977
COLOR_STATUS * temporaryColorArray
Definition: network.c:7002
SCIP_Bool * isArcCut
Definition: network.c:6962
MemberInfo * memberInformation
Definition: network.c:6926
spqr_col * newColumnArcs
Definition: network.c:6945
int memArtDFSData
Definition: network.c:6987
SCIP_Bool * newColumnReversed
Definition: network.c:6947
int numArticulationNodes
Definition: network.c:6970
reduced_member_id * childrenStorage
Definition: network.c:6931
int memReducedComponents
Definition: network.c:6923
int memLeafMembers
Definition: network.c:6953
SCIP_Bool * decompositionColumnArcReversed
Definition: network.c:6957
int memDecompositionColumnArcs
Definition: network.c:6959
int memMemberInformation
Definition: network.c:6928
ColorDFSCallData * colorDFSData
Definition: network.c:6983
ArticulationPointCallStack * artDFSData
Definition: network.c:6986
DFSCallData * intersectionDFSData
Definition: network.c:6980
int memReducedMembers
Definition: network.c:6918
spqr_row newRowIndex
Definition: network.c:6943
int memIntersectionDFSData
Definition: network.c:6981
int memTemporaryColorArray
Definition: network.c:7003
SPQRRowReducedMember * reducedMembers
Definition: network.c:6916
int * intersectionPathDepth
Definition: network.c:6992
spqr_arc * decompositionColumnArcs
Definition: network.c:6955
int numReducedMembers
Definition: network.c:6919
int numLeafMembers
Definition: network.c:6952
COLOR_STATUS * nodeColors
Definition: network.c:6966
int numChildrenStorage
Definition: network.c:6936
int numMemberInformation
Definition: network.c:6929
int numColumnArcs
Definition: network.c:6949
int memColumnArcs
Definition: network.c:6948
int memNodeColors
Definition: network.c:6967
int memNodeSearchInfo
Definition: network.c:6975
int memIntersectionPathParent
Definition: network.c:6997
int memIntersectionPathDepth
Definition: network.c:6993
SPQRRowReducedComponent * reducedComponents
Definition: network.c:6921
CutArcListNode * cutArcs
Definition: network.c:6938
SCIP_Bool * isArcCutReversed
Definition: network.c:6963
spqr_node * articulationNodes
Definition: network.c:6969
int memCrossingPathCount
Definition: network.c:6978
int memArticulationNodes
Definition: network.c:6971
int memChildrenStorage
Definition: network.c:6935
MergeTreeCallData * mergeTreeCallData
Definition: network.c:6999
int numDecompositionColumnArcs
Definition: network.c:6960
cut_arc_id firstOverallCutArc
Definition: network.c:6941
SCIP_Bool remainsNetwork
Definition: network.c:6914
int numReducedComponents
Definition: network.c:6924
CreateReducedMembersCallstack * createReducedMembersCallstack
Definition: network.c:6989
int memColorDFSData
Definition: network.c:6984
reduced_member_id * leafMembers
Definition: network.c:6951
spqr_node * intersectionPathParent
Definition: network.c:6995
SCIP_NETROWADD * rowadd
Definition: network.c:11562
SCIP_NETMATDECDATA * dec
Definition: network.c:11561
SCIP_NETCOLADD * coladd
Definition: network.c:11563
reduced_member_id pathEndMembers[2]
Definition: network.c:3590
reduced_member_id root
Definition: network.c:3588
reduced_member_id nextPathMember
Definition: network.c:3576
SCIP_Bool reverseArcs
Definition: network.c:3563
children_idx firstChild
Definition: network.c:3557
spqr_node rigidPathEnd
Definition: network.c:3565
spqr_member member
Definition: network.c:3551
spqr_member rootMember
Definition: network.c:3552
reduced_member_id parent
Definition: network.c:3555
ReducedMemberType type
Definition: network.c:3554
path_arc_id firstPathArc
Definition: network.c:3560
children_idx numChildren
Definition: network.c:3558
spqr_arc pathTargetArc
Definition: network.c:3581
SCIP_Bool pathBackwards
Definition: network.c:3567
MemberPathType pathType
Definition: network.c:3575
spqr_arc pathSourceArc
Definition: network.c:3580
SCIP_Bool nextPathMemberIsParent
Definition: network.c:3578
spqr_node rigidPathStart
Definition: network.c:3564
SPQRNetworkDecompositionArcListNode arcListNode
Definition: network.c:369
SPQRNetworkDecompositionArcListNode headArcListNode
Definition: network.c:367
SPQRNetworkDecompositionArcListNode tailArcListNode
Definition: network.c:368
spqr_member representativeMember
Definition: network.c:390
reduced_member_id root
Definition: network.c:6908
RowReducedMemberType type
Definition: network.c:6876
children_idx numPropagatedChildren
Definition: network.c:6881
SCIP_Bool splitHead
Definition: network.c:6888
SCIP_Bool allHaveCommonNode
Definition: network.c:6895
spqr_node coloredNode
Definition: network.c:6900
SCIP_Bool willBeReversed
Definition: network.c:6897
children_idx numChildren
Definition: network.c:6880
spqr_member member
Definition: network.c:6872
cut_arc_id firstCutArc
Definition: network.c:6884
spqr_node splitNode
Definition: network.c:6894
SCIP_Bool otherNodeSplit
Definition: network.c:6896
spqr_member rootMember
Definition: network.c:6873
SCIP_Bool otherIsSource
Definition: network.c:6889
reduced_member_id parent
Definition: network.c:6877
spqr_arc articulationArc
Definition: network.c:6898
spqr_node otherNode
Definition: network.c:6893
children_idx firstChild
Definition: network.c:6879
SCIP_Bool otherIsSource
Definition: network.c:9077
SCIP_Bool headSplit
Definition: network.c:9076
@ SCIP_INVALIDDATA
Definition: type_retcode.h:52
@ SCIP_OKAY
Definition: type_retcode.h:42
@ SCIP_ERROR
Definition: type_retcode.h:43
enum SCIP_Retcode SCIP_RETCODE
Definition: type_retcode.h:63