Skip to content

Commit

Permalink
Merge operator before pseudocode change
Browse files Browse the repository at this point in the history
  • Loading branch information
vidyaap committed Jan 6, 2020
1 parent 4dfc7f9 commit 655c08b
Show file tree
Hide file tree
Showing 4 changed files with 301 additions and 62 deletions.
6 changes: 4 additions & 2 deletions lib/andOperator.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,12 @@ function addToFinalCategories(finalCategories, inCommon, text1, text2) {
let orderOne = text1 + '_' + text2;
let orderTwo = text2 + '_' + text1;
if (orderOne in finalCategories) {
finalCategories[orderOne] += inCommon;
finalCategories[orderOne].ids.concat(inCommon.ids);
finalCategories[orderOne].roles.concat(inCommon.roles);
ret = orderOne;
} else if (orderTwo in finalCategories) {
finalCategories[orderTwo] += inCommon;
finalCategories[orderTwo].ids.concat(inCommon.ids);
finalCategories[orderTwo].roles.concat(inCommon.roles);
ret = orderTwo;
} else { // if not, make a new entry for this edge
finalCategories[orderOne] = inCommon;
Expand Down
53 changes: 42 additions & 11 deletions lib/combineGraphs.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const ATOM = 'atom'; // Denotes a GOLDBAR atom
const ACCEPT = 'accept'; // Denotes an end node/a global leaf
const ROOT = 'root'; // Denotes the unique root node


const util = require('util');
/**
* Combines graphs sequentially with specified method
* @param combineMethod String specifying which method of AND or MERGE
Expand All @@ -23,10 +23,10 @@ function combineGraphs(combineMethod, stateGraphObj, categories, tolerance) {
let combined;
// call the correct handler method on the stateGraphs
if (combineMethod === AND) {
combined = handleAnd(stateGraphObj[0], stateGraphObj[1], categories[0], categories[1], tolerance);
combined = linearCall(handleAnd, stateGraphObj, categories, tolerance);

} else if (combineMethod === MERGE) {
combined = handleMerge(stateGraphObj[0], stateGraphObj[1], categories[0], categories[1], tolerance);
combined = linearCall(handleMerge, stateGraphObj, categories, tolerance);

} else {
throw new Error('Invalid combine method');
Expand All @@ -36,11 +36,32 @@ function combineGraphs(combineMethod, stateGraphObj, categories, tolerance) {
if (JSON.stringify(combined.graph ) === JSON.stringify({})) {
return {graph: {}, categories: {}, paths: []};
}

console.log(util.inspect(combined.graph, {showHidden: false, depth: null}));
return {graph: combined.graph, categories: combined.categories};
}


/**
* Calls whichever handler function on the graphs sequentially
* @param handleFunc Function: the function to call on each item in the object
* @param graphObj Object: the stateGraphs of all the submitted designs
* @param categories Object: original list of categories per stateGraph
* @param tolerance Tolerance level of combined method (0-2)
* @return {{categories: Object, graph: Object}}
*/
function linearCall(handleFunc, graphObj, categories, tolerance) {
// do one iteration before the loop
let firstIter = handleFunc(graphObj[0], graphObj[1], categories[0], categories[1], tolerance);
let finalGraph = firstIter.graph;
let finalCategories = firstIter.categories;
// all subsequent and/merges must happen in relation to the first and/merge
for (let i = 2; i < Object.keys(graphObj).length; i++) {
finalGraph = handleFunc(finalGraph, graphObj[i], finalCategories, categories[i], tolerance);
}
return {graph: finalGraph, categories: finalCategories};
}


/**
* Called if combine method is AND; and's one pair of graphs
* @param graph1 Object representing the left side of the and
Expand All @@ -61,14 +82,19 @@ function handleAnd(graph1, graph2, categories1, categories2, tolerance) {
// remove nodes that do not have any edges coming out of it (except accepts)
// must be done multiple times to account for epsilon nodes that point to other epsilon nodes
let updated = JSON.parse(JSON.stringify(newGraph));
removeEmptyEdgeNodes(newGraph);
removeNoEdgeNodes(newGraph);
while (JSON.stringify(updated) !== JSON.stringify(newGraph)) {
removeEmptyEdgeNodes(newGraph);
removeNoEdgeNodes(newGraph);
updated = JSON.parse(JSON.stringify(newGraph));
}

// remove nodes that don't have any edges pointing to them
updated = JSON.parse(JSON.stringify(newGraph));
removeNonConnectedNodes(newGraph);
while (JSON.stringify(updated) !== JSON.stringify(newGraph)) {
removeNonConnectedNodes(newGraph);
updated = JSON.parse(JSON.stringify(newGraph));
}

return {graph: newGraph, categories: finalCategories};

Expand All @@ -95,14 +121,19 @@ function handleMerge(graph1, graph2, categories1, categories2, tolerance) {
// remove nodes that do not have any edges coming out of it (except accepts)
// must be done multiple times to account for epsilon nodes that point to other epsilon nodes
let updated = JSON.parse(JSON.stringify(newGraph));
removeEmptyEdgeNodes(newGraph);
removeNoEdgeNodes(newGraph);
while (JSON.stringify(updated) !== JSON.stringify(newGraph)) {
removeEmptyEdgeNodes(newGraph);
removeNoEdgeNodes(newGraph);
updated = JSON.parse(JSON.stringify(newGraph));
}

// remove nodes that don't have any edges pointing to them
updated = JSON.parse(JSON.stringify(newGraph));
removeNonConnectedNodes(newGraph);
while (JSON.stringify(updated) !== JSON.stringify(newGraph)) {
removeNonConnectedNodes(newGraph);
updated = JSON.parse(JSON.stringify(newGraph));
}

return {graph: newGraph, categories: finalCategories};
}
Expand Down Expand Up @@ -168,7 +199,7 @@ function cartesianNodes(graph1, graph2) {
* Removes all nodes that do not have edges going out from them
* @param graph
*/
function removeEmptyEdgeNodes(graph) {
function removeNoEdgeNodes(graph) {
for (let id in graph) {
let node = graph[id];
if (node.edges.length === 0 && node.type !== ACCEPT) {
Expand Down Expand Up @@ -254,7 +285,7 @@ function findRoot(graph) {
for (let id in graph) {
let node = graph[id];
if (node.text === ROOT) {
return id;
return node.id;
}
}
}
Expand All @@ -269,7 +300,7 @@ function findAccepts(graph) {
for (let id in graph) {
let node = graph[id];
if (node.type === ACCEPT) {
accepts.push(id);
accepts.push(node.id);
}
}
return accepts
Expand Down
77 changes: 60 additions & 17 deletions lib/graphDataOnEdges.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ graphGOLDBAR = require('./graphGOLDBAR');
combineGraphs = require('./combineGraphs');
uuidv4 = require('uuidv4');

const util = require('util');

/* * * * * * * * * * */
/* NODE HANDLING */
/* * * * * * * * * * */
Expand Down Expand Up @@ -376,6 +378,7 @@ function enumeratePaths(root, stateGraph, maxCycles) {
const eMap = getEpsilonMap(stateGraph);
const nep = getNonEpsilonParents(stateGraph, eMap);
collapseEpsilons(stateGraph, eMap, nep);
root = combineGraphs.findRoot(stateGraph);

let dummyEdge = {'src': 'dummy',
'dest': root,
Expand Down Expand Up @@ -517,24 +520,52 @@ function collapseEpsilons(stateGraph, eMap, nep) {
const srcNode = stateGraph[srcs[0]];
const destNode = stateGraph[dest];

if (destNode.text === handleOp.ROOT && srcNode.edges.length === 1) {
// cases for when the src node is an accept node
if (srcNode.type === handleOp.ACCEPT) {
if (destNode.type === handleOp.ROOT) { // if dest is root, continue
continue;
} else {
let flag = false;
for (let e of destNode.edges) {
if (e.dest === srcNode.id) {
flag = true;
break;
}
}
if (flag) { // if dest node points to src node, continue
continue;
}
}
}

// if there is a floating node with an epsilon edge pointing to the root, delete it (can result from combining graphs)
if (destNode.text === handleOp.ROOT && !isPointedTo(stateGraph, srcNode)) {
delete stateGraph[srcs[0]];
continue;
}

// if there is an accept that has an epsilon edge pointing to it, but it is to construct a zero-or-one, leave that edge
if (destNode.type === handleOp.ACCEPT && checkZeroOrOne(srcNode, dest)) {
continue;
}

// if the destNode is a root, make the srcNode a root
if (destNode.text === handleOp.ROOT) {
srcNode.text = handleOp.ROOT;
srcNode.type = handleOp.ROOT;
}

// if the destNode is an accept, make the srcNode an accept
if (destNode.type === handleOp.ACCEPT) {
srcNode.type = handleOp.ACCEPT;
}

for (let edge of destNode.edges) {
// Transfer children
edge.src = srcs[0];
srcNode.edges.push(edge);

if (destNode.type === handleOp.ACCEPT) {
srcNode.type = handleOp.ACCEPT;
}

// if any of the destNode's children are in the eMap, update their parent to srcNode
if (edge.dest in eMap) {
let index = Array.from(eMap[edge.dest]).indexOf(dest);
if (index > -1) {
Expand All @@ -552,6 +583,7 @@ function collapseEpsilons(stateGraph, eMap, nep) {
srcNode.operator.push(destNode.operator[i]);
}
}
srcNode.operator = [...new Set(srcNode.operator)];
}
// Transfer loops
for (let dE of stateGraph[edge.dest].edges) {
Expand All @@ -560,13 +592,10 @@ function collapseEpsilons(stateGraph, eMap, nep) {
}
}
}
// remove the epsilon edge that pointed from srcNode to destNode
let edgeIndex = srcNode.edges.indexOf(srcNode.edges.find(obj => obj.dest === dest));
if (edgeIndex > -1) {
srcNode.edges.splice(edgeIndex, 1);
if (destNode.type === handleOp.ACCEPT) {
srcNode.type = handleOp.ACCEPT;
srcNode.text = handleOp.ACCEPT;
}
}

//re assign any non-epsilon parent nodes to the epsilon parent node
Expand All @@ -583,12 +612,6 @@ function collapseEpsilons(stateGraph, eMap, nep) {
}
}

// update neps to reflect the correct parent node(s)
for (let nepDest in nep) {
nep[nepDest].delete(dest);
nep[nepDest].add(srcs[0]);
}

removeDuplicateEdges(srcNode);
delete stateGraph[dest];
}
Expand All @@ -603,14 +626,34 @@ function removeDuplicateEdges(node) {
for (let j = i+1; j < node.edges.length; j++) {
let edge1 = node.edges[i];
let edge2 = node.edges[j];
if (JSON.stringify(edge1) === JSON.stringify(edge2)) {
let edge1Info = [edge1.src, edge1.dest, edge1.component];
let edge2Info = [edge2.src, edge2.dest, edge2.component];
if (JSON.stringify(edge1Info) === JSON.stringify(edge2Info)) {
let idx = node.edges.indexOf(edge2);
node.edges.splice(idx, 1);
}
}
}
}

/**
* States whether the node 'compare' is pointed to by any edge in stateGraph
* @param stateGraph
* @param compare
* @return {boolean}
*/
function isPointedTo(stateGraph, compare) {
for (let id in stateGraph) {
let node = stateGraph[id];
for (let edge of node.edges) {
if (edge.dest === compare.id) {
return true;
}
}
}
return false;
}

/**
* Checks if source node has multiple edges to the destID and if any of them are non-epsilon
* @param srcNode Object: src of destID
Expand Down Expand Up @@ -645,7 +688,7 @@ function getEpsilonMap(stateGraph) {
if (stateGraph[srcNode].edges.length === 1) {
let edge = stateGraph[srcNode].edges[0];
if (edge.type === handleOp.EPSILON) {
if (stateGraph[srcNode].operator.includes(handleOp.OR) &&
if (stateGraph[srcNode].operator.includes(handleOp.OR) ||
(stateGraph[edge.dest].operator.includes(handleOp.ONE_MORE) || stateGraph[edge.dest].operator.includes(handleOp.ZERO_MORE))) {
continue;
}
Expand Down

0 comments on commit 655c08b

Please sign in to comment.