UNPKG

siafun

Version:
178 lines (177 loc) 8.22 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const math = require("mathjs"); const _ = require("lodash"); const util_1 = require("./util"); const sw_structure_1 = require("./sw-structure"); function getCachedAffinityAlignment(points, options, points2) { const file = 'sw_' + getSimOptionsString(options) + '.json'; return util_1.loadOrPerformAndCache(file, () => Object.assign(getAffinityAlignment(points, options, points2), { points2: points2 }), options); } exports.getCachedAffinityAlignment = getCachedAffinityAlignment; function getSimOptionsString(options) { return (options.minSegmentLength ? options.minSegmentLength : '') //0 == undefined + '_' + (options.similarityThreshold != null ? options.similarityThreshold : '') + '_' + (options.nLongest ? options.nLongest : '') + '_' + (options.maxGapSize != null ? options.maxGapSize : '') + '_' + (options.maxGaps ? options.maxGaps : '') + '_' + (options.maxGapRatio ? options.maxGapRatio : '') + '_' + (options.minDistance ? options.minDistance : ''); } function getAffinityAlignment(points, options, points2) { points2 = points2 || points; let matrix = getAffinityMatrix(points.map(p => p.slice(1)), points2.map(p => p.slice(1)), !options.similarityThreshold); //remove symmetry const symmetric = !points2 || _.isEqual(points2, points); if (symmetric) matrix = matrix.map((r, i) => r.map((v, j) => j > i ? v : 0)); let smoothed = smoothDiagonals(matrix, options.maxGapSize); //mask smoothed matrix smoothed = smoothed.map(r => r.map(v => v >= (options.similarityThreshold || 1) ? v : 0)); const dias = getNonzeroDiagonalSegments(smoothed); const segments = reduceSegments(dias, options, points.length, points2.length, symmetric); return { points: points, points2: points2, patterns: util_1.toPatterns(segments, points, points2), affinityMatrix: matrix, smoothedMatrix: smoothed, segmentMatrix: util_1.createPointMatrix(_.flatten(segments), points, points2, symmetric) }; } exports.getAffinityAlignment = getAffinityAlignment; function reduceSegments(segments, options, numPoints1, numPoints2, symmetric) { const padding = options.minDistance ? options.minDistance - 1 : 0; //remove short segments segments = segments.filter(s => s.length >= options.minSegmentLength); //sort, longest first segments = _.reverse(_.sortBy(segments, a => a.length)); const reduced = []; const matrix = util_1.getEmptyMatrix(numPoints1, numPoints2); if (symmetric) sw_structure_1.getPaddedArea(_.range(0, numPoints1).map(i => [i, i]), padding, symmetric, numPoints1 - 1, numPoints2 - 1).forEach(p => matrix[p[0]][p[1]] = 1); //keep longest while respecting min dist while (segments.length > 0 && (!options.nLongest || reduced.length < options.nLongest)) { const currentAlignment = segments.shift(); const nooverlap = currentAlignment.filter(p => matrix[p[0]][p[1]] == 0); //add if not covered by any previous segment if (nooverlap.length == currentAlignment.length) { reduced.push(currentAlignment); sw_structure_1.getPaddedArea(currentAlignment, padding, symmetric, numPoints1 - 1, numPoints2 - 1).forEach(p => matrix[p[0]][p[1]] = 1); //save nonoverlapping part for later } else if (nooverlap.length >= options.minSegmentLength) { const i = segments.findIndex(a => a.length == nooverlap.length); segments.splice(i, 0, nooverlap); } } return reduced; } function getNonzeroDiagonalSegments(matrix) { return _.flatten(getDiagonalIndexPairs(matrix).map(d => d.reduce((segs, [i, j], k) => { if (matrix[i][j] > 0) { k == 0 || matrix[i - 1][j - 1] == 0 ? segs.push([[i, j]]) : _.last(segs).push([i, j]); } return segs; }, []))) .filter(d => d.length > 0); } exports.getNonzeroDiagonalSegments = getNonzeroDiagonalSegments; function getDiagonalIndexPairs(matrix) { const I = matrix.length; const J = matrix[0].length; const minLength = Math.min(I, J); const belowMainDiagonal = _.reverse(_.range(1, I).map(d => _.range(0, minLength).map(j => [j + d, j]).filter(([i, j]) => i < I && j < J))); const fromMainDiagonal = _.range(0, matrix[0].length).map(d => _.range(0, minLength).map(i => [i, i + d]).filter(([i, j]) => i < I && j < J)); return belowMainDiagonal.concat(fromMainDiagonal); } exports.getDiagonalIndexPairs = getDiagonalIndexPairs; function getSelfSimilarityMatrix(vectors, equality, smoothness = 0) { return getAffinityMatrix(vectors, vectors, equality, smoothness); } exports.getSelfSimilarityMatrix = getSelfSimilarityMatrix; function getAffinityMatrix(v1, v2, equality, smoothness = 0) { return smoothDiagonals(v1.map(v => v2.map(w => equality ? (_.isEqual(v, w) ? 1 : 0) : getCosineSimilarity(v, w))), smoothness); } //median filter with median of (level*2)+1 function smoothDiagonals(matrix, level = 1) { return level > 0 ? matrix.map((x, i) => x.map((_y, j) => util_1.getMedian(getDiagonal(matrix, [i - level, j - level], (level * 2) + 1)))) : matrix; } function getDiagonal(matrix, start, length) { return _.range(0, length).map(k => [start[0] + k, start[1] + k]) .filter(([i, j]) => 0 <= i && i < matrix.length && 0 <= j && j < matrix[0].length) .map(([i, j]) => matrix[i][j]); } //adds the given successor to the predecessor of the given uri in the given sequence function addSuccessorToPredecessorOf(uri, successor, sequence, store) { var index = sequence.indexOf(uri); if (index > 0) { var predecessorUri = sequence[index - 1]; store.addSuccessor(predecessorUri, successor); } } exports.addSuccessorToPredecessorOf = addSuccessorToPredecessorOf; function addSimilaritiesAbove(store, similarities, threshold) { for (var uri1 in similarities) { for (var uri2 in similarities[uri1]) { //console.log(similarities[uri1][uri2]) if (similarities[uri1][uri2] > threshold) { store.addSimilar(uri1, uri2); store.addSimilar(uri2, uri1); } } } } exports.addSimilaritiesAbove = addSimilaritiesAbove; function addHighestSimilarities(store, similarities, count) { //gather all similarities in an array var sortedSimilarities = []; for (var uri1 in similarities) { for (var uri2 in similarities[uri1]) { sortedSimilarities.push([similarities[uri1][uri2], uri1, uri2]); } } //sort in descending order sortedSimilarities = sortedSimilarities.sort((a, b) => b[0] - a[0]); //console.log(sortedSimilarities.map(s => s[0])) //add highest ones to dymos for (var i = 0, l = Math.min(sortedSimilarities.length, count); i < l; i++) { var sim = sortedSimilarities[i]; store.addSimilar(sim[1], sim[2]); store.addSimilar(sim[2], sim[1]); } } exports.addHighestSimilarities = addHighestSimilarities; function reduce(vector) { var unitVector = Array.apply(null, Array(vector.length)).map(Number.prototype.valueOf, 1); return getCosineSimilarity(vector, unitVector); } exports.reduce = reduce; function getCosineSimilarities(vectorMap) { var similarities = {}; for (var uri1 in vectorMap) { for (var uri2 in vectorMap) { if (uri1 < uri2) { if (!similarities[uri1]) { similarities[uri1] = {}; } similarities[uri1][uri2] = getCosineSimilarity(vectorMap[uri1], vectorMap[uri2]); } } } return similarities; } exports.getCosineSimilarities = getCosineSimilarities; function getCosineSimilarity(v1, v2) { if (v1.length == v2.length) { if (v1.every(x => x == 0) && v2.every(x => x == 0)) return 1; const similarity = math.dot(v1, v2) / (math.norm(v1) * math.norm(v2)); if (similarity) return similarity; //0 if one of vectors zero-vector... } return 0; } exports.getCosineSimilarity = getCosineSimilarity;