UNPKG

@qudtlib/core

Version:

Data model for QUDTLib

1 lines 6.54 kB
{"version":3,"sources":["assignmentProblem.ts"],"names":[],"mappings":"AACA,qBAAa,iBAAiB;IAC5B,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IAYnC,OAAO,CAAC,MAAM,CAAC,IAAI;CAKpB;AAED,8BAAsB,QAAQ;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,OAAO,EAAE,MAAM,EAAE,EAAE;IASxB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS;aAanD,KAAK,IAAI,QAAQ;CAClC;AAED,qBAAa,QAAQ;IACnB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;gBAEhB,QAAQ,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE;IAMrD,UAAU,IAAI,OAAO;IAIrB,OAAO,IAAI,OAAO;IAIlB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ;IAO5C,oBAAoB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO;CAY/C;AAED,qBAAa,cAAc;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAIzC;AAED,qBAAa,sBAAuB,SAAQ,QAAQ;IAClD,OAAO,CAAC,mBAAmB,CAAC,CAAW;gBAE3B,OAAO,EAAE,MAAM,EAAE,EAAE;IAI/B,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAapD,4BAA4B,CAAC,SAAS,EAAE,QAAQ;IAShD,KAAK,IAAI,QAAQ;IAQjB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ;IAwBvC,OAAO,CAAC,SAAS;IAiBjB,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,kBAAkB;CAa3B","file":"assignmentProblem.d.ts","sourcesContent":["import { arrayContains } from \"./utils.js\";\nexport class AssignmentProblem {\n static instance(weights: number[][]) {\n const rows = weights.length;\n if (!rows) {\n throw \"Cannot create instance with 0x0 weights matrix\";\n }\n const cols = weights[0].length;\n if (rows > cols) {\n throw \"The weights matrix may not have more rows than columns\";\n }\n return new NaiveAlgorithmInstance(AssignmentProblem.copy(weights));\n }\n\n private static copy(weights: number[][]): number[][] {\n const ret: number[][] = [];\n weights.forEach((row) => ret.push(row));\n return ret;\n }\n}\n\nexport abstract class Instance {\n readonly weights: number[][];\n readonly rows: number;\n readonly cols: number;\n\n constructor(weights: number[][]) {\n if (!weights?.length || !weights[0]?.length) {\n throw \"Not a valid weights matrix: \" + weights;\n }\n this.weights = weights;\n this.rows = weights.length;\n this.cols = weights[0].length;\n }\n\n public weightOfAssignment(assignment: number[]): number | undefined {\n if (!assignment) {\n throw \"Not a valid assignment: \" + assignment;\n }\n if (assignment?.length === 0) {\n return undefined;\n }\n const sum = assignment\n .map((col, row) => this.weights[row][col])\n .reduce((a, b) => a + b);\n return sum;\n }\n\n public abstract solve(): Solution;\n}\n\nexport class Solution {\n readonly assignment: number[];\n readonly weight: number | undefined;\n readonly instance: Instance;\n\n constructor(instance: Instance, assignment?: number[]) {\n this.instance = instance;\n this.assignment = assignment || [];\n this.weight = instance.weightOfAssignment(this.assignment);\n }\n\n isComplete(): boolean {\n return this.assignment.length >= this.instance.rows;\n }\n\n isEmpty(): boolean {\n return this.assignment.length === 0;\n }\n\n assignColumnInNextRow(col: number): Solution {\n if (this.isComplete()) {\n throw \"Solution is already complete\";\n }\n return new Solution(this.instance, [...this.assignment, col]);\n }\n\n isBetterSolutionThan(other: Solution): boolean {\n if (!(this.isComplete() && other.isComplete())) {\n throw \"Cannot compare incomplete solutions\";\n }\n if (\n typeof this.weight === \"undefined\" ||\n typeof other.weight === \"undefined\"\n ) {\n throw \"Cannot compare empty solutions\";\n }\n return this.weight < other.weight;\n }\n}\n\nexport class ValueWithIndex {\n readonly value: number;\n readonly index: number;\n\n constructor(value: number, index: number) {\n this.value = value;\n this.index = index;\n }\n}\n\nexport class NaiveAlgorithmInstance extends Instance {\n private currentBestSolution?: Solution;\n\n constructor(weights: number[][]) {\n super(weights);\n }\n\n isLowerThanBestWeight(weightToTest: number): boolean {\n if (!this.currentBestSolution) {\n return true;\n }\n if (!this.currentBestSolution.isComplete()) {\n return true;\n }\n if (!this.currentBestSolution.weight) {\n return true;\n }\n return this.currentBestSolution.weight > weightToTest;\n }\n\n updateBestSolutionIfPossible(candidate: Solution) {\n if (\n !this.currentBestSolution ||\n candidate.isBetterSolutionThan(this.currentBestSolution)\n ) {\n this.currentBestSolution = candidate;\n }\n }\n\n solve(): Solution {\n this.doSolve(0, new Solution(this));\n if (!this.currentBestSolution) {\n return new Solution(this);\n }\n return this.currentBestSolution;\n }\n\n doSolve(row: number, solution: Solution) {\n if (row >= this.rows) {\n this.updateBestSolutionIfPossible(solution);\n return;\n }\n if (solution.weight) {\n const bestAttainableScore = this.sum(\n this.minPerRow(row, solution.assignment)\n );\n if (!this.isLowerThanBestWeight(solution.weight + bestAttainableScore)) {\n return;\n }\n }\n const nMin = this.rowSortedAscending(row, solution.assignment);\n for (let i = 0; i < nMin.length; i++) {\n if (\n !solution.weight ||\n this.isLowerThanBestWeight(solution.weight + nMin[i].value)\n ) {\n this.doSolve(row + 1, solution.assignColumnInNextRow(nMin[i].index));\n }\n }\n }\n\n private minPerRow(startRow: number, skipCols: number[]): number[] {\n const ret = [];\n for (let r = startRow; r < this.rows; r++) {\n let min = Number.MAX_VALUE;\n for (let c = 0; c < this.cols; c++) {\n if (!arrayContains(skipCols, c)) {\n const val = this.weights[r][c];\n if (min > val) {\n min = val;\n }\n }\n }\n ret.push(min);\n }\n return ret;\n }\n\n private sum(arr: number[]) {\n return arr.reduce((a, b) => a + b);\n }\n\n private rowSortedAscending(\n row: number,\n skipCols: number[]\n ): ValueWithIndex[] {\n const sorted: ValueWithIndex[] = [];\n for (let i = 0; i < this.cols; i++) {\n if (!arrayContains(skipCols, i)) {\n sorted.push(new ValueWithIndex(this.weights[row][i], i));\n }\n }\n sorted.sort((l, r) => l.value - r.value);\n return sorted;\n }\n}\n"]}