11import assert from 'assert' ;
2+ import min from './min' ;
3+ import rotate from './rotate' ;
4+ import verifyOptimum from './verifyOptimum' ;
5+ import checkDelta2 from './checkDelta2' ;
6+ import checkDelta3 from './checkDelta3' ;
27
38// Adapted from http://jorisvr.nl/maximummatching.html
49// All credit for the implementation goes to Joris van Rantwijk [http://jorisvr.nl].
@@ -18,12 +23,6 @@ import assert from 'assert';
1823// A C program for maximum weight matching by Ed Rothberg was used extensively
1924// to validate this new code.
2025
21- const min = ( a , i , j ) => {
22- let o = a [ i ] ;
23- for ( ++ i ; i < j ; ++ i ) if ( a [ i ] < o ) o = a [ i ] ;
24- return o ;
25- } ;
26-
2726export default function blossom ( CHECK_OPTIMUM , CHECK_DELTA ) {
2827 // Check delta2/delta3 computation after every substage;
2928 // only works on integer weights, slows down the algorithm to O(n^4).
@@ -32,7 +31,7 @@ export default function blossom(CHECK_OPTIMUM, CHECK_DELTA) {
3231 // Check optimality of solution before returning; only works on integer weights.
3332 if ( CHECK_OPTIMUM === undefined ) CHECK_OPTIMUM = true ;
3433
35- const maxWeightMatching = function ( edges , maxcardinality = false ) {
34+ const maxWeightMatching = function ( edges , maxCardinality = false ) {
3635 let i ;
3736 let j ;
3837 let k ;
@@ -43,7 +42,7 @@ export default function blossom(CHECK_OPTIMUM, CHECK_DELTA) {
4342 /**
4443 *
4544 * Compute a maximum-weighted matching in the general undirected
46- * weighted graph given by "edges". If "maxcardinality " is true,
45+ * weighted graph given by "edges". If "maxCardinality " is true,
4746 * only maximum-cardinality matchings are considered as solutions.
4847 *
4948 * Edges is a sequence of tuples (i, j, wt) describing an undirected
@@ -638,13 +637,6 @@ export default function blossom(CHECK_OPTIMUM, CHECK_DELTA) {
638637 unusedblossoms . push ( b ) ;
639638 } ;
640639
641- const rotate = function ( a , n ) {
642- const head = a . splice ( 0 , n ) ;
643- for ( let i = 0 ; i < n ; ++ i ) {
644- a . push ( head [ i ] ) ;
645- }
646- } ;
647-
648640 // Swap matched/unmatched edges over an alternating path through blossom b
649641 // between vertex v and the base vertex. Keep blossom bookkeeping consistent.
650642 const augmentBlossom = function ( b , v ) {
@@ -772,165 +764,6 @@ export default function blossom(CHECK_OPTIMUM, CHECK_DELTA) {
772764 } ) ;
773765 } ;
774766
775- // Verify that the optimum solution has been reached.
776- const verifyOptimum = function ( ) {
777- let i ;
778- let j ;
779- let wt ;
780- let v ;
781- let b ;
782- let p ;
783- let k ;
784- let s ;
785- let vdualoffset ;
786- let iblossoms ;
787- let jblossoms ;
788- if ( maxcardinality ) {
789- // Vertices may have negative dual;
790- // find a constant non-negative number to add to all vertex duals.
791- vdualoffset = Math . max ( 0 , - min ( dualvar , 0 , nvertex ) ) ;
792- } else vdualoffset = 0 ;
793- // 0. all dual variables are non-negative
794- assert ( min ( dualvar , 0 , nvertex ) + vdualoffset >= 0 ) ;
795- assert ( min ( dualvar , nvertex , 2 * nvertex ) >= 0 ) ;
796- // 0. all edges have non-negative slack and
797- // 1. all matched edges have zero slack;
798- for ( k = 0 ; k < nedge ; ++ k ) {
799- i = edges [ k ] [ 0 ] ;
800- j = edges [ k ] [ 1 ] ;
801- wt = edges [ k ] [ 2 ] ;
802-
803- s = dualvar [ i ] + dualvar [ j ] - 2 * wt ;
804- iblossoms = [ i ] ;
805- jblossoms = [ j ] ;
806- while ( blossomparent [ iblossoms [ iblossoms . length - 1 ] ] !== - 1 )
807- iblossoms . push ( blossomparent [ iblossoms [ iblossoms . length - 1 ] ] ) ;
808- while ( blossomparent [ jblossoms [ jblossoms . length - 1 ] ] !== - 1 )
809- jblossoms . push ( blossomparent [ jblossoms [ jblossoms . length - 1 ] ] ) ;
810- iblossoms . reverse ( ) ;
811- jblossoms . reverse ( ) ;
812- const length = Math . min ( iblossoms . length , jblossoms . length ) ;
813- for ( let x = 0 ; x < length ; ++ x ) {
814- const bi = iblossoms [ x ] ;
815- const bj = jblossoms [ x ] ;
816- if ( bi !== bj ) break ;
817- s += 2 * dualvar [ bi ] ;
818- }
819-
820- assert ( s >= 0 ) ;
821- if ( Math . floor ( mate [ i ] / 2 ) === k || Math . floor ( mate [ j ] / 2 ) === k ) {
822- assert (
823- Math . floor ( mate [ i ] / 2 ) === k && Math . floor ( mate [ j ] / 2 ) === k
824- ) ;
825- assert ( s === 0 ) ;
826- }
827- }
828-
829- // 2. all single vertices have zero dual value;
830- for ( v = 0 ; v < nvertex ; ++ v )
831- assert ( mate [ v ] >= 0 || dualvar [ v ] + vdualoffset === 0 ) ;
832- // 3. all blossoms with positive dual value are full.
833- for ( b = nvertex ; b < 2 * nvertex ; ++ b ) {
834- if ( blossombase [ b ] >= 0 && dualvar [ b ] > 0 ) {
835- assert ( blossomendps [ b ] . length % 2 === 1 ) ;
836- for ( i = 1 ; i < blossomendps [ b ] . length ; i += 2 ) {
837- p = blossomendps [ b ] [ i ] ;
838- assert ( ( mate [ endpoint [ p ] ] === p ) ^ 1 ) ;
839- assert ( mate [ endpoint [ p ^ 1 ] ] === p ) ;
840- }
841- }
842- }
843- // Ok.
844- } ;
845-
846- // Check optimized delta2 against a trivial computation.
847- const checkDelta2 = function ( ) {
848- for ( let v = 0 ; v < nvertex ; ++ v ) {
849- if ( label [ inblossom [ v ] ] === 0 ) {
850- let bd = null ;
851- let bk = - 1 ;
852- for ( let i = 0 ; i < neighbend [ v ] . length ; ++ i ) {
853- const p = neighbend [ v ] [ i ] ;
854- const k = Math . floor ( p / 2 ) ;
855- const w = endpoint [ p ] ;
856- if ( label [ inblossom [ w ] ] === 1 ) {
857- const d = slack ( k ) ;
858- if ( bk === - 1 || d < bd ) {
859- bk = k ;
860- bd = d ;
861- }
862- }
863- }
864-
865- if (
866- ( bestedge [ v ] !== - 1 || bk !== - 1 ) &&
867- ( bestedge [ v ] === - 1 || bd !== slack ( bestedge [ v ] ) )
868- ) {
869- console . debug (
870- 'v=' +
871- v +
872- ' bk=' +
873- bk +
874- ' bd=' +
875- bd +
876- ' bestedge=' +
877- bestedge [ v ] +
878- ' slack=' +
879- slack ( bestedge [ v ] )
880- ) ;
881- }
882-
883- assert (
884- ( bk === - 1 && bestedge [ v ] === - 1 ) ||
885- ( bestedge [ v ] !== - 1 && bd === slack ( bestedge [ v ] ) )
886- ) ;
887- }
888- }
889- } ;
890-
891- // Check optimized delta3 against a trivial computation.
892- const checkDelta3 = function ( ) {
893- let bk = - 1 ;
894- let bd = null ;
895- let tbk = - 1 ;
896- let tbd = null ;
897- for ( let b = 0 ; b < 2 * nvertex ; ++ b ) {
898- if ( blossomparent [ b ] === - 1 && label [ b ] === 1 ) {
899- blossomLeaves ( b , function ( v ) {
900- for ( let x = 0 ; x < neighbend [ v ] . length ; ++ x ) {
901- const p = neighbend [ v ] [ x ] ;
902- const k = Math . floor ( p / 2 ) ;
903- const w = endpoint [ p ] ;
904- if ( inblossom [ w ] !== b && label [ inblossom [ w ] ] === 1 ) {
905- const d = slack ( k ) ;
906- if ( bk === - 1 || d < bd ) {
907- bk = k ;
908- bd = d ;
909- }
910- }
911- }
912- } ) ;
913-
914- if ( bestedge [ b ] !== - 1 ) {
915- const i = edges [ bestedge [ b ] ] [ 0 ] ;
916- const j = edges [ bestedge [ b ] ] [ 1 ] ;
917-
918- assert ( inblossom [ i ] === b || inblossom [ j ] === b ) ;
919- assert ( inblossom [ i ] !== b || inblossom [ j ] !== b ) ;
920- assert ( label [ inblossom [ i ] ] === 1 && label [ inblossom [ j ] ] === 1 ) ;
921- if ( tbk === - 1 || slack ( bestedge [ b ] ) < tbd ) {
922- tbk = bestedge [ b ] ;
923- tbd = slack ( bestedge [ b ] ) ;
924- }
925- }
926- }
927- }
928-
929- if ( bd !== tbd )
930- console . debug ( 'bk=' + bk + ' tbk=' + tbk + ' bd=' + bd + ' tbd=' + tbd ) ;
931- assert ( bd === tbd ) ;
932- } ;
933-
934767 let b ;
935768 let d ;
936769 let t ;
@@ -1073,12 +906,31 @@ export default function blossom(CHECK_OPTIMUM, CHECK_DELTA) {
1073906
1074907 // Verify data structures for delta2/delta3 computation.
1075908 if ( CHECK_DELTA ) {
1076- checkDelta2 ( ) ;
1077- checkDelta3 ( ) ;
909+ checkDelta2 ( {
910+ nvertex,
911+ neighbend,
912+ label,
913+ endpoint,
914+ bestedge,
915+ slack,
916+ inblossom
917+ } ) ;
918+ checkDelta3 ( {
919+ nvertex,
920+ edges,
921+ blossomparent,
922+ blossomLeaves,
923+ neighbend,
924+ label,
925+ endpoint,
926+ bestedge,
927+ slack,
928+ inblossom
929+ } ) ;
1078930 }
1079931
1080932 // Compute delta1: the minumum value of any vertex dual.
1081- if ( ! maxcardinality ) {
933+ if ( ! maxCardinality ) {
1082934 deltatype = 1 ;
1083935 delta = min ( dualvar , 0 , nvertex ) ;
1084936 }
@@ -1128,7 +980,7 @@ export default function blossom(CHECK_OPTIMUM, CHECK_DELTA) {
1128980 // No further improvement possible; max-cardinality optimum
1129981 // reached. Do a final delta update to make the optimum
1130982 // verifyable.
1131- assert ( maxcardinality ) ;
983+ assert ( maxCardinality ) ;
1132984 deltatype = 1 ;
1133985 delta = Math . max ( 0 , min ( dualvar , 0 , nvertex ) ) ;
1134986 }
@@ -1206,7 +1058,19 @@ export default function blossom(CHECK_OPTIMUM, CHECK_DELTA) {
12061058 }
12071059
12081060 // Verify that we reached the optimum solution.
1209- if ( CHECK_OPTIMUM ) verifyOptimum ( ) ;
1061+ if ( CHECK_OPTIMUM )
1062+ verifyOptimum ( {
1063+ nvertex,
1064+ edges,
1065+ maxCardinality,
1066+ nedge,
1067+ blossomparent,
1068+ mate,
1069+ endpoint,
1070+ dualvar,
1071+ blossombase,
1072+ blossomendps
1073+ } ) ;
12101074
12111075 // Transform mate[] such that mate[v] is the vertex to which v is paired.
12121076 for ( v = 0 ; v < nvertex ; ++ v ) {
0 commit comments