1
1
import 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' ;
2
7
3
8
// Adapted from http://jorisvr.nl/maximummatching.html
4
9
// All credit for the implementation goes to Joris van Rantwijk [http://jorisvr.nl].
@@ -18,12 +23,6 @@ import assert from 'assert';
18
23
// A C program for maximum weight matching by Ed Rothberg was used extensively
19
24
// to validate this new code.
20
25
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
-
27
26
export default function blossom ( CHECK_OPTIMUM , CHECK_DELTA ) {
28
27
// Check delta2/delta3 computation after every substage;
29
28
// 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) {
32
31
// Check optimality of solution before returning; only works on integer weights.
33
32
if ( CHECK_OPTIMUM === undefined ) CHECK_OPTIMUM = true ;
34
33
35
- const maxWeightMatching = function ( edges , maxcardinality = false ) {
34
+ const maxWeightMatching = function ( edges , maxCardinality = false ) {
36
35
let i ;
37
36
let j ;
38
37
let k ;
@@ -43,7 +42,7 @@ export default function blossom(CHECK_OPTIMUM, CHECK_DELTA) {
43
42
/**
44
43
*
45
44
* 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,
47
46
* only maximum-cardinality matchings are considered as solutions.
48
47
*
49
48
* Edges is a sequence of tuples (i, j, wt) describing an undirected
@@ -638,13 +637,6 @@ export default function blossom(CHECK_OPTIMUM, CHECK_DELTA) {
638
637
unusedblossoms . push ( b ) ;
639
638
} ;
640
639
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
-
648
640
// Swap matched/unmatched edges over an alternating path through blossom b
649
641
// between vertex v and the base vertex. Keep blossom bookkeeping consistent.
650
642
const augmentBlossom = function ( b , v ) {
@@ -772,165 +764,6 @@ export default function blossom(CHECK_OPTIMUM, CHECK_DELTA) {
772
764
} ) ;
773
765
} ;
774
766
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
-
934
767
let b ;
935
768
let d ;
936
769
let t ;
@@ -1073,12 +906,31 @@ export default function blossom(CHECK_OPTIMUM, CHECK_DELTA) {
1073
906
1074
907
// Verify data structures for delta2/delta3 computation.
1075
908
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
+ } ) ;
1078
930
}
1079
931
1080
932
// Compute delta1: the minumum value of any vertex dual.
1081
- if ( ! maxcardinality ) {
933
+ if ( ! maxCardinality ) {
1082
934
deltatype = 1 ;
1083
935
delta = min ( dualvar , 0 , nvertex ) ;
1084
936
}
@@ -1128,7 +980,7 @@ export default function blossom(CHECK_OPTIMUM, CHECK_DELTA) {
1128
980
// No further improvement possible; max-cardinality optimum
1129
981
// reached. Do a final delta update to make the optimum
1130
982
// verifyable.
1131
- assert ( maxcardinality ) ;
983
+ assert ( maxCardinality ) ;
1132
984
deltatype = 1 ;
1133
985
delta = Math . max ( 0 , min ( dualvar , 0 , nvertex ) ) ;
1134
986
}
@@ -1206,7 +1058,19 @@ export default function blossom(CHECK_OPTIMUM, CHECK_DELTA) {
1206
1058
}
1207
1059
1208
1060
// 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
+ } ) ;
1210
1074
1211
1075
// Transform mate[] such that mate[v] is the vertex to which v is paired.
1212
1076
for ( v = 0 ; v < nvertex ; ++ v ) {
0 commit comments