siafun
Version:
A collection of structure induction algorithms
156 lines (155 loc) • 7.75 kB
JavaScript
;
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])))
};
}