1+ import { DoubleArray } from 'cheminfo-types' ;
2+ import Matrix from 'ml-matrix' ;
3+ import sequentialFill from 'ml-array-sequential-fill' ;
4+
5+ let matrix = [ [ 0.1 , 1.1 , 2.1 ] , [ 1.1 , 0.1 , 1.1 ] , [ 2.1 , 1.1 , 0.1 ] ]
6+ let assignment = assign2D ( matrix , false ) ;
7+ console . log ( assignment )
8+ export function assign2D ( input : DoubleArray [ ] , maximaze = true ) {
9+ if ( input [ 0 ] . length > input . length ) {
10+ throw new Error ( 'the matrix should have at least less rows than columns' ) ;
11+ }
12+
13+ let matrix = Matrix . checkMatrix ( input ) ;
14+
15+ let didFlip = false ;
16+ if ( matrix . columns > matrix . rows ) {
17+ didFlip = true ;
18+ matrix = matrix . transpose ( ) ;
19+ }
20+
21+ let nbRows = matrix . rows ;
22+ let nbColumns = matrix . columns ;
23+
24+ let matrixDelta = maximaze ? matrix . max ( ) : matrix . min ( ) ;
25+ matrix = matrix . subtract ( matrixDelta ) ;
26+
27+ let col4row : DoubleArray = new Float64Array ( nbRows ) ;
28+ let row4col : DoubleArray = new Float64Array ( nbColumns ) ;
29+ let dualVariableForColumns : DoubleArray = new Float64Array ( nbColumns ) ;
30+ let dualVariableForRows : DoubleArray = new Float64Array ( nbRows ) ;
31+
32+ for ( let currUnAssCol = 0 ; currUnAssCol < nbColumns ; currUnAssCol ++ ) {
33+ let currentAugmenting = getShortestPath ( {
34+ matrix,
35+ currUnAssCol,
36+ dualVariableForColumns,
37+ dualVariableForRows,
38+ col4row,
39+ row4col,
40+ } ) ;
41+
42+ let { sink, pred } = currentAugmenting ;
43+ console . log ( 'pred' , pred )
44+ if ( sink === - 1 ) {
45+ return {
46+ col4row : [ ] ,
47+ row4col : [ ] ,
48+ gain : - 1 ,
49+ }
50+ }
51+
52+ dualVariableForColumns = currentAugmenting . dualVariableForColumns ;
53+ dualVariableForRows = currentAugmenting . dualVariableForRows ;
54+
55+ console . log ( dualVariableForColumns , dualVariableForRows )
56+ let j = sink ;
57+ console . log ( JSON . stringify ( sink ) ) ;
58+ console . log ( `j ${ j } , currUnAssCol ${ currUnAssCol } ` )
59+ for ( let i = pred [ j ] ; true ; i = pred [ j ] ) {
60+ col4row [ j ] = i ;
61+ let h = row4col [ i ] ;
62+ row4col [ i ] = j ;
63+ j = h ;
64+ if ( i === currUnAssCol ) break ;
65+ }
66+ console . log ( JSON . stringify ( { j, sink} ) ) ;
67+ }
68+
69+ let gain = 0 ;
70+ for ( let curCol = 0 ; curCol < nbColumns ; curCol ++ ) {
71+ gain += matrix . get ( row4col [ curCol ] , curCol ) ;
72+ }
73+
74+ gain = ( ( maximaze ? - 1 : 1 ) * gain ) + ( matrixDelta * nbColumns ) ;
75+
76+ if ( didFlip ) {
77+ [ row4col , col4row ] = [ col4row , row4col ] ;
78+ [ dualVariableForColumns , dualVariableForRows ] = [ dualVariableForRows , dualVariableForColumns ] ;
79+ }
80+
81+ return {
82+ col4row,
83+ row4col,
84+ gain,
85+ dualVariableForColumns,
86+ dualVariableForRows
87+ }
88+ }
89+
90+ interface GetShortestPathOptions {
91+ currUnAssCol : number ,
92+ dualVariableForColumns : DoubleArray ,
93+ dualVariableForRows : DoubleArray ,
94+ col4row : DoubleArray ,
95+ row4col : DoubleArray ,
96+ matrix : Matrix ,
97+ }
98+
99+ function getShortestPath ( options : GetShortestPathOptions ) {
100+ let {
101+ currUnAssCol,
102+ dualVariableForColumns,
103+ dualVariableForRows,
104+ col4row,
105+ row4col,
106+ matrix,
107+ } = options ;
108+
109+ let nbRows = matrix . rows
110+ let nbColumns = matrix . columns ;
111+
112+ let pred = new Float64Array ( nbColumns ) ;
113+ let scannedColumns = new Float64Array ( nbColumns ) ;
114+ let scannedRows = new Float64Array ( nbRows ) ;
115+
116+ let rows2Scan = sequentialFill ( { from : 0 , to : nbRows - 1 , size : nbRows } ) ;
117+ let numRows2Scan = nbRows ;
118+
119+ let sink = - 1 ;
120+ let delta = 0 ;
121+ let curColumn = currUnAssCol ;
122+ let shortestPathCost = getArrayOfInfinity ( nbRows ) ;
123+
124+ for ( ; sink === - 1 ; ) {
125+ scannedColumns [ curColumn ] = 1 ;
126+ let minVal = Number . POSITIVE_INFINITY ;
127+ let closestRowScan = - 1 ;
128+ for ( let curRowScan = 0 ; curRowScan < numRows2Scan ; curRowScan ++ ) {
129+ let curRow = rows2Scan [ curRowScan ] ;
130+ // console.log(`curRow ${curRow}, ${curRowScan}`)
131+ let reducedCost = delta + matrix . get ( curRow , curColumn ) - dualVariableForColumns [ curColumn ] - dualVariableForRows [ curRow ] ;
132+ console . log ( 'reduced cost' , reducedCost , curColumn , shortestPathCost [ curRow ] )
133+ if ( reducedCost < shortestPathCost [ curRow ] ) {
134+ pred [ curRow ] = curColumn ;
135+ shortestPathCost [ curRow ] = reducedCost
136+ }
137+
138+ if ( shortestPathCost [ curRow ] < minVal ) {
139+ minVal = shortestPathCost [ curRow ] ;
140+ closestRowScan = curRowScan ;
141+ }
142+ }
143+ if ( ! Number . isFinite ( minVal ) ) {
144+ return { dualVariableForColumns, dualVariableForRows, sink, pred } ;
145+ }
146+ let closestRow = rows2Scan [ closestRowScan ] ;
147+ scannedRows [ closestRow ] = 1 ;
148+ numRows2Scan -= 1 ;
149+ rows2Scan . splice ( closestRowScan , 1 ) ;
150+ delta = shortestPathCost [ closestRow ] ;
151+
152+ if ( col4row [ closestRow ] === 0 ) {
153+ sink = closestRow ;
154+ } else {
155+ curColumn = col4row [ closestRow ] ;
156+ }
157+ console . log ( `sink ${ sink } ` ) ;
158+ }
159+ console . log ( 'sale loop sink' )
160+ dualVariableForColumns [ currUnAssCol ] += delta ;
161+
162+ for ( let sel = 0 ; sel < nbColumns ; sel ++ ) {
163+ if ( scannedColumns [ sel ] === 0 ) continue ;
164+ if ( sel === currUnAssCol ) continue ;
165+ dualVariableForColumns [ sel ] += delta - shortestPathCost [ row4col [ sel ] ] ;
166+ }
167+ for ( let sel = 0 ; sel < nbRows ; sel ++ ) {
168+ if ( scannedRows [ sel ] === 0 ) continue ;
169+ dualVariableForRows [ sel ] -= ( delta - shortestPathCost [ sel ] ) ;
170+ }
171+ console . log ( 'return' )
172+ return {
173+ sink, pred, dualVariableForColumns, dualVariableForRows
174+ }
175+ }
176+
177+ function getArrayOfInfinity ( nbElements = 1 , value = Number . POSITIVE_INFINITY ) {
178+ const array = new Array ( nbElements ) ;
179+ for ( let i = 0 ; i < nbElements ; i ++ ) {
180+ array [ i ] = value ;
181+ }
182+ return array ;
183+ }
0 commit comments