UNPKG

siafun

Version:
156 lines (155 loc) 7.75 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const _ = require("lodash"); const arrayutils_1 = require("arrayutils"); var OPTIMIZATION; (function (OPTIMIZATION) { OPTIMIZATION[OPTIMIZATION["MINIMIZE"] = 0] = "MINIMIZE"; OPTIMIZATION[OPTIMIZATION["DIVIDE"] = 1] = "DIVIDE"; OPTIMIZATION[OPTIMIZATION["PARTITION"] = 2] = "PARTITION"; })(OPTIMIZATION = exports.OPTIMIZATION || (exports.OPTIMIZATION = {})); function minLength(input, minLength) { return { points: input.points, patterns: input.patterns.filter(p => p.points.length >= minLength), minPatternLength: minLength }; } exports.minLength = minLength; function minimize(input, heuristic, dimension, minLength) { const patterns = input.patterns.map(p => minimizePattern(p, input.points, dimension, heuristic, minLength)); return { points: input.points, patterns: patterns, minPatternLength: minLength }; } exports.minimize = minimize; function divide(input, heuristic, dimension, minLength) { let patterns = _.flatten(input.patterns.map(p => dividePattern(p, input.points, dimension, heuristic, minLength))); patterns = unitePatterns(patterns).map(p => newPattern(p.points, p.vectors)); //update occurrences return { points: input.points, patterns: patterns, minPatternLength: minLength }; } exports.divide = divide; function partition(input, heuristic, dimension, minLength) { let patterns = _.flatten(input.patterns.map(p => partitionPattern(p, input.points, dimension, heuristic, minLength))); patterns = unitePatterns(patterns).map(p => newPattern(p.points, p.vectors)); //update occurrences return { points: input.points, patterns: patterns, minPatternLength: minLength }; } exports.partition = partition; function minimizePattern(pattern, allPoints, dimension, heuristic, minLength = 1) { if (pattern.points.length > minLength) { const points = cloneAndSortPoints(pattern, dimension); if (points.length > minLength) { //all possible connected subpatterns const subPatterns = _.flatten(points.map((_, i) => points.slice(i).map((_, j) => points.length - j - i >= minLength ? //check min length newPattern(points.slice(i, points.length - j), pattern.vectors) : null))) //filter for defined ones .filter(p => p); const heuristics = subPatterns.map(p => heuristic(p, allPoints)); return subPatterns[arrayutils_1.indexOfMax(heuristics)]; } } return pattern; } function dividePattern(pattern, allPoints, dimension, heuristic, minLength = 1) { const cloned = newPattern(cloneAndSortPoints(pattern, dimension), pattern.vectors); return recursiveDividePattern(cloned, allPoints, dimension, heuristic, minLength); } exports.dividePattern = dividePattern; function recursiveDividePattern(pattern, allPoints, dimension, heuristic, minLength) { const currentHeuristicValue = heuristic(pattern, allPoints); if (pattern.points.length > minLength) { const patternPairs = pattern.points.map((_, i) => //check if both segments at least minLength pattern.points.length - i >= minLength && i >= minLength ? [newPattern(pattern.points.slice(0, i), pattern.vectors), newPattern(pattern.points.slice(i), pattern.vectors)] : null) //filter for defined ones .filter(p => p); if (patternPairs.length > 0) { const heuristics = patternPairs.map(ps => ps.map(p => heuristic(p, allPoints))); const maxes = heuristics.map(_.max); const index = arrayutils_1.indexOfMax(maxes); if (maxes[index] > currentHeuristicValue) { return recursiveDividePattern(patternPairs[index][0], allPoints, dimension, heuristic, minLength) .concat(recursiveDividePattern(patternPairs[index][1], allPoints, dimension, heuristic, minLength)); } } } return [pattern]; } /** partitions the given pattern along the given dimension */ function partitionPattern(pattern, allPoints, dimension, heuristic, minLength = 1) { const points = cloneAndSortPoints(pattern, dimension); if (pattern.vectors.length > 1 && points.length > minLength) { const vals = pattern.vectors.map(v => v[dimension]); const dists = _.flatten(vals.map((v, i) => vals.filter((_, j) => j > i).map(w => Math.abs(v - w)))); const maxLength = _.min(dists); const min = points[0][dimension]; const max = _.last(points)[dimension]; const patternLength = max - min; if (patternLength >= maxLength && patternLength >= minLength) { const partitions = _.range(0, maxLength).map(offset => points.reduce((result, p) => { const currentPartition = result.length; if (_.last(result).length && p[dimension] >= min + (currentPartition * maxLength) - offset) { result.push([p]); } else { _.last(result).push(p); } return result; }, [[]])); if (partitions.length > 0) { const patterns = partitions.map(ps => ps.map(p => newPattern(p, pattern.vectors))); const heuristics = patterns.map(ps => ps.map(p => heuristic(p, allPoints))); const maxes = heuristics.map(_.max); const i = arrayutils_1.indexOfMax(maxes); const numMaxes = maxes.reduce((n, m) => n + (m == maxes[i] ? 1 : 0), 0); //return the partition with the highest max heuristic, //and with the highest average if there are several ones let bestPartition = numMaxes == 1 ? patterns[i] : patterns[arrayutils_1.indexOfMax(heuristics.map(_.mean))]; bestPartition = bestPartition.filter(p => p.points.length >= minLength); //unite patterns with identical point sets return unitePatterns(bestPartition); } } } return [pattern]; } exports.partitionPattern = partitionPattern; function unitePatterns(patterns) { const norms = patterns.map(p => toNormalForm(p.points)); const grouped = _.groupBy(norms, n => JSON.stringify(n)); return _.values(grouped).map(g => combine(g.map(n => patterns[norms.indexOf(n)]))); } exports.unitePatterns = unitePatterns; function combine(patterns) { while (patterns.length > 1) { const distance = _.zipWith(patterns[1].points[0], patterns[0].points[0], _.subtract); patterns[0].vectors = concatUniqAndSort(patterns[0].vectors, patterns[1].vectors.map(v => _.zipWith(v, distance, _.add))); patterns[0].occurrences = concatUniqAndSort(patterns[0].occurrences, patterns[1].occurrences); patterns.splice(1, 1); } return patterns[0]; } function concatUniqAndSort(points, points2) { const result = _.uniq(points.concat(points2).map(p => JSON.stringify(p))) .map(p => JSON.parse(p)); result.sort(arrayutils_1.compareArrays); return result; } function toNormalForm(pattern) { const normalForm = _.cloneDeep(pattern); normalForm.sort(arrayutils_1.compareArrays); const offset = normalForm[0]; return normalForm.map(p => _.zipWith(p, offset, _.subtract)); } function cloneAndSortPoints(pattern, dimension) { const points = pattern.points.map(p => p.slice()); //clone points.sort((a, b) => a[dimension] - b[dimension]); return points; } function newPattern(points, vectors) { return { points: points, vectors: vectors, occurrences: vectors.map(v => points.map(pat => pat.map((p, k) => p + v[k]))) }; }