Skip to content

Commit 8e65d4f

Browse files
committed
feat: add code for rectangular assignment
1 parent 3b11277 commit 8e65d4f

File tree

11 files changed

+394
-0
lines changed

11 files changed

+394
-0
lines changed

.eslintrc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
extends: cheminfo-typescript

.gitignore

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
8+
# Runtime data
9+
pids
10+
*.pid
11+
*.seed
12+
*.pid.lock
13+
14+
# Directory for instrumented libs generated by jscoverage/JSCover
15+
lib-cov
16+
17+
# Coverage directory used by tools like istanbul
18+
coverage
19+
20+
# nyc test coverage
21+
.nyc_output
22+
23+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
24+
.grunt
25+
26+
# Bower dependency directory (https://bower.io/)
27+
bower_components
28+
29+
# node-waf configuration
30+
.lock-wscript
31+
32+
# Compiled binary addons (https://nodejs.org/api/addons.html)
33+
build/Release
34+
35+
# Dependency directories
36+
node_modules/
37+
jspm_packages/
38+
39+
# TypeScript v1 declaration files
40+
typings/
41+
42+
# Optional npm cache directory
43+
.npm
44+
45+
# Optional eslint cache
46+
.eslintcache
47+
48+
# Optional REPL history
49+
.node_repl_history
50+
51+
# Output of 'npm pack'
52+
*.tgz
53+
54+
# Yarn Integrity file
55+
.yarn-integrity
56+
57+
# dotenv environment variables file
58+
.env
59+
60+
# parcel-bundler cache (https://parceljs.org/)
61+
.cache
62+
63+
# next.js build output
64+
.next
65+
66+
# nuxt.js build output
67+
.nuxt
68+
69+
# vuepress build output
70+
.vuepress/dist
71+
72+
# Serverless directories
73+
.serverless
74+
75+
# FuseBox cache
76+
.fusebox/
77+
78+
lib
79+
lib-esm
80+
81+
.DS_Store

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package-lock=false

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022 jobo322
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

package.json

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"name": "assignRectangular2D",
3+
"version": "0.0.0",
4+
"description": "",
5+
"main": "./lib/index.js",
6+
"module": "./lib-esm/index.js",
7+
"types": "./lib/index.d.ts",
8+
"keywords": [],
9+
"author": "J Alejandro Bolanos A <jobo322>",
10+
"license": "MIT",
11+
"files": [
12+
"src",
13+
"lib",
14+
"lib-esm"
15+
],
16+
"scripts": {
17+
"clean": "rimraf lib lib-esm",
18+
"eslint": "eslint src --ext ts --cache",
19+
"eslint-fix": "npm run eslint -- --fix",
20+
"prepublishOnly": "npm run tsc",
21+
"test": "npm run test-coverage && npm run eslint",
22+
"test-coverage": "npm run test-only -- --coverage",
23+
"test-only": "jest",
24+
"tsc": "npm run clean && npm run tsc-cjs && npm run tsc-esm",
25+
"tsc-cjs": "tsc --project tsconfig.cjs.json",
26+
"tsc-esm": "tsc --project tsconfig.esm.json"
27+
},
28+
"repository": {
29+
"type": "git",
30+
"url": "git+https://github.com/jobo322/assignRectangular2D.git"
31+
},
32+
"bugs": {
33+
"url": "https://github.com/jobo322/assignRectangular2D/issues"
34+
},
35+
"homepage": "https://github.com/jobo322/assignRectangular2D#readme",
36+
"jest": {
37+
"preset": "ts-jest",
38+
"testEnvironment": "node"
39+
},
40+
"prettier": {
41+
"arrowParens": "always",
42+
"semi": true,
43+
"singleQuote": true,
44+
"tabWidth": 2,
45+
"trailingComma": "all"
46+
},
47+
"dependencies": {
48+
"cheminfo-types": "^1.1.0",
49+
"install": "^0.13.0",
50+
"ml-array-sequential-fill": "^1.1.8",
51+
"ml-matrix": "^6.10.0"
52+
},
53+
"devDependencies": {
54+
"@types/jest": "^27.5.0",
55+
"eslint": "^8.15.0",
56+
"eslint-config-cheminfo-typescript": "^10.4.0",
57+
"jest": "^28.1.0",
58+
"prettier": "^2.6.2",
59+
"rimraf": "^3.0.2",
60+
"ts-jest": "^28.0.1",
61+
"typescript": "^4.6.4"
62+
}
63+
}

src/.npmignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
__tests__
2+
.npmignore

src/__tests__/test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { assign2D } from '..';
2+
3+
describe('test myModule', () => {
4+
it('should return 42', () => {
5+
const a = [ 1 , 2 , 3, 4, 5 ];
6+
const b = [1.1, 1.9, 3.1, 3.99, 5.2];
7+
const diff = a.map((aElement) => {
8+
return b.map((bElement) => Math.abs(bElement - aElement));
9+
});
10+
console.log('diff', diff)
11+
const result = assign2D(diff, false);
12+
console.log(result)
13+
// expect(myModule()).toBe(42);
14+
});
15+
});

src/index.ts

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
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+
}

tsconfig.cjs.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"compilerOptions": {
4+
"module": "commonjs",
5+
"outDir": "lib",
6+
"declaration": true
7+
},
8+
"exclude": ["./src/**/__tests__"]
9+
}

tsconfig.esm.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "./tsconfig.cjs.json",
3+
"compilerOptions": {
4+
"module": "es2015",
5+
"outDir": "lib-esm",
6+
"declaration": false
7+
}
8+
}

0 commit comments

Comments
 (0)