isomorphism
Version:
Find subgraph isomorphisms with Ullman's 1976 algorithm.
200 lines (163 loc) • 8.06 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.allIsomorphismsForDigraphs = exports.allIsomorphismsForWeightedDigraphs = exports.extractAtMostOneIsomorphism = exports.flatmap = undefined;
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _ramda = require('ramda');
/* Utility */
/* Types */
var flatmap = exports.flatmap = function flatmap(fn, xs) {
return (0, _ramda.reduce)(_ramda.concat, [], (0, _ramda.map)(fn, xs));
};
/* Adjacency list graph representation */
var getIndex = function getIndex(_ref) {
var _ref2 = _slicedToArray(_ref, 2),
index = _ref2[0],
weight = _ref2[1];
return index;
};
var getWeight = function getWeight(_ref3) {
var _ref4 = _slicedToArray(_ref3, 2),
index = _ref4[0],
weight = _ref4[1];
return weight;
};
var weightOneEdge = function weightOneEdge(index) {
return [index, 1];
};
var digraphToWeighted = function digraphToWeighted(graph) {
return (0, _ramda.map)(function (neighbors) {
return (0, _ramda.map)(function (neighborIndex) {
return weightOneEdge(neighborIndex);
}, neighbors);
}, graph);
};
// In the directed graph `graph`, does `vertex` have an edge to `neighbor`?
var isAdjacent = function isAdjacent(graph, accessIndex, adjacencyPred, vertex, neighbor) {
for (var i = 0; i < graph[vertex].length; i++) {
if (accessIndex(graph[vertex][i]) > neighbor) return false; // NOTE: adjacency list must be sorted ascending
if (adjacencyPred(graph[vertex][i], neighbor)) return true;
}
return false;
};
/* Ullman */
var extractAtMostOneIsomorphism = exports.extractAtMostOneIsomorphism = function extractAtMostOneIsomorphism(pattern, target, mapping) {
var mappedTargetVertex = function mappedTargetVertex(patternVertex) {
return mapping[patternVertex].length === 1 ? mapping[patternVertex][0] : null;
};
var iso = [];
for (var _patternVertex = 0; _patternVertex < pattern.length; _patternVertex++) {
var correspondingTargetVertex = mappedTargetVertex(_patternVertex);
if (correspondingTargetVertex === null) return [];
var patternVector = pattern[_patternVertex];
var _loop = function _loop(j) {
var _patternVector$j = _slicedToArray(patternVector[j], 2),
patternNeighbor = _patternVector$j[0],
patternNeighborEdgeWeight = _patternVector$j[1];
var maybeTargetNeighbor = mappedTargetVertex(patternNeighbor);
if (maybeTargetNeighbor === null || !isAdjacent(target, getIndex, function (edge, neighborIndex) {
return getIndex(edge) === neighborIndex && getWeight(edge) >= patternNeighborEdgeWeight;
}, correspondingTargetVertex, maybeTargetNeighbor)) {
return {
v: []
};
}
};
for (var j = 0; j < patternVector.length; j++) {
var _ret = _loop(j);
if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v;
}
iso.push(correspondingTargetVertex);
}
return [iso];
};
// Given a Mapping, return a mapping in which patternVertex in mapped to targetVertex
var setMappingInPossibleMappings = function setMappingInPossibleMappings(possibleMappings, patternVertex, targetVertex) {
var eliminatePossibility = function eliminatePossibility(targetVertex, row) {
return (0, _ramda.filter)(function (possibleTarget) {
return !(possibleTarget === targetVertex);
}, row);
};
var mapping = [];
for (var i = 0; i < possibleMappings.length; i++) {
var possibleMappingsForPatternVertex = possibleMappings[i];
if (i < patternVertex) {
mapping.push(possibleMappingsForPatternVertex);
} else if (i === patternVertex) {
mapping.push([targetVertex]);
} else {
mapping.push(eliminatePossibility(targetVertex, possibleMappingsForPatternVertex));
}
}
return mapping;
};
var refine = function refine(mapping, predicate) {
var refinedMapping = [];
for (var _patternVertex2 = 0; _patternVertex2 < mapping.length; _patternVertex2++) {
refinedMapping.push([]);
var mappingVector = mapping[_patternVertex2];
if (mappingVector.length === 0) return null; // No possible mapping!
for (var i = 0; i < mappingVector.length; i++) {
var possibleTarget = mappingVector[i];
if (predicate(_patternVertex2, possibleTarget)) {
refinedMapping[_patternVertex2].push(possibleTarget);
}
}
}
return refinedMapping;
};
var degreeRefine = function degreeRefine(pattern, target, mapping) {
// What is the degree of `vertex` in `graph`?
var deg = function deg(graph, vertex) {
return graph[vertex].length;
};
return refine(mapping, function (patternVertex, targetVertex) {
return deg(pattern, patternVertex) <= deg(target, targetVertex);
});
};
var ullmanRefine = function ullmanRefine(pattern, target, mapping) {
return refine(mapping, function (patternVertex, targetVertex) {
return (0, _ramda.all)(function (_ref5) {
var _ref6 = _slicedToArray(_ref5, 2),
patternVertexNeighbor = _ref6[0],
patternNeighborEdgeWeight = _ref6[1];
return (0, _ramda.any)(function (_ref7) {
var _ref8 = _slicedToArray(_ref7, 2),
targetVertexNeighbor = _ref8[0],
targetNeighborEdgeWeight = _ref8[1];
return isAdjacent(mapping, function (x) {
return x;
}, function (edge, neighborIndex) {
return edge === neighborIndex;
}, patternVertexNeighbor, targetVertexNeighbor) && patternNeighborEdgeWeight <= targetNeighborEdgeWeight;
}, target[targetVertex]);
}, pattern[patternVertex]);
});
};
var search = function search(pattern, target, possibleMappings, scanVertex) {
var maybeRefined = ullmanRefine(pattern, target, possibleMappings);
return !maybeRefined ? [] : scanVertex < possibleMappings.length ? flatmap(function (speculativeTargetMappingForScanVertex) {
return search(pattern, target, setMappingInPossibleMappings(maybeRefined, scanVertex, speculativeTargetMappingForScanVertex), scanVertex + 1);
}, possibleMappings[scanVertex]) : extractAtMostOneIsomorphism(pattern, target, possibleMappings);
};
var allMappings = function allMappings(patternOrder, targetOrder) {
var mapping = [];
for (var i = 0; i < patternOrder; i++) {
mapping.push([]);
for (var j = 0; j < targetOrder; j++) {
mapping[i].push(j);
}
}
return mapping;
};
var allIsomorphismsForWeightedDigraphs = exports.allIsomorphismsForWeightedDigraphs = function allIsomorphismsForWeightedDigraphs(pattern, target, initialpossibleMappings) {
var maybeRefined = degreeRefine(pattern, target, initialpossibleMappings || allMappings(pattern.length, target.length));
return !maybeRefined ? [] : pattern.length > target.length ? [] : search(pattern, target, maybeRefined, 0);
};
var allIsomorphismsForDigraphs = exports.allIsomorphismsForDigraphs = function allIsomorphismsForDigraphs(pattern, target, initialpossibleMappings) {
var weightedPattern = digraphToWeighted(pattern);
var weightedTarget = digraphToWeighted(target);
return allIsomorphismsForWeightedDigraphs(weightedPattern, weightedTarget, initialpossibleMappings);
};