UNPKG

isomorphism

Version:

Find subgraph isomorphisms with Ullman's 1976 algorithm.

200 lines (163 loc) 8.06 kB
'use strict'; 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); };