import java.io.*;
import java.util.*;
public class GraphSolve {
public static BufferedReader graphsFile;
public static FileReader graphsFr;
public static BufferedWriter satInputFile;
public static FileWriter satInputFw;
public static BufferedReader satOutputFile;
public static FileReader satOutputFw;
public static BufferedWriter outputMappingFile;
public static FileWriter outputMappinngFw;
public static int numSatVars = 0;
public static long numClauses = 0;
public static String newLine = System.getProperty("line.separator");
public static class CNFClauseGenerator
{
private int sizeBigGraph;
private int sizeSmallGraph;
private int numSatVars = 0;
private long numClauses = 0;
private long numVarsInCurrentClause = 0;
private BufferedWriter satInputFile;
//public static StringBuilder satString;
public CNFClauseGenerator(int sizeBigGraph, int sizeSmallGraph, BufferedWriter satInputFile)
{
this.sizeBigGraph = sizeBigGraph;
this.sizeSmallGraph = sizeSmallGraph;
this.numSatVars = this.sizeBigGraph * this.sizeSmallGraph;
this.numClauses = 1000;
//satString = new StringBuilder(1024 * 1024 * 32);
this.satInputFile = satInputFile;
try {
satInputFile.write("c auto generated file" + newLine);
satInputFile.write("p cnf " + numSatVars + " " + numClauses + GraphSolve.newLine);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// add condition
public void addConditionInExistingClause(int smallGraphIndex, int bigGraphIndex, boolean varState) throws IOException
{
int varNum = generateVarNum(smallGraphIndex, bigGraphIndex);
if (varNum > numSatVars)
{
numSatVars = varNum;
}
if (!varState)
{
varNum = -1 * varNum;
}
//satString.append(varNum);
//satString.append(' ');
this.satInputFile.write(Integer.toString(varNum));
this.satInputFile.write(' ');
numVarsInCurrentClause++;
}
public void addConditionInNewClause(int smallGraphIndex, int bigGraphIndex, boolean varState) throws IOException
{
addNewClause();
this.addConditionInExistingClause(smallGraphIndex, bigGraphIndex, varState);
}
public void addNewClause() throws IOException
{
// append 0 to mark end of clause
if (numVarsInCurrentClause > 0)
{
//satString.append("0");
//satString.append(GraphSolve.newLine);
this.satInputFile.write('0');
this.satInputFile.newLine();
numClauses++;
numVarsInCurrentClause = 0;
}
}
public int generateVarNum(int smallGraphIndex, int bigGraphIndex) {
return sizeBigGraph * (smallGraphIndex - 1) + bigGraphIndex;
}
public void writeOutSatFile() throws IOException
{
System.out.println("NumVars,Clauses: " + numSatVars + "," + numClauses);
this.addNewClause();
satInputFile.close();
}
}
public static class Node implements Comparable<Node>
{
int id;
int sortedId;
LinkedList<Node> edges;
SortedSet<Node> qEdges;
LinkedList<Node> notEdges;
LinkedList<Node> candidatesFromBigGraph;
LinkedList<Node> notCandidatesFromBigGraph;
int outGoingEdges = 0;
int incomingEdges = 0;
public Node(int idIn)
{
this.id = idIn;
this.sortedId = idIn;
this.edges = new LinkedList<GraphSolve.Node>();
this.notEdges = new LinkedList<GraphSolve.Node>();
this.qEdges = new TreeSet<GraphSolve.Node>();
this.candidatesFromBigGraph = new LinkedList<GraphSolve.Node>();
this.notCandidatesFromBigGraph = new LinkedList<GraphSolve.Node>();
}
@Override
public int compareTo(Node n)
{
if (this.id < n.id)
{
return -1;
}
else if (this.id > n.id)
{
return 1;
}
else
{
return 0;
}
}
@Override
public boolean equals (Object other)
{
Node otherNode = (Node) other;
return this.id == otherNode.id;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("ID:");
sb.append(id + "(" + this.sortedId + ")");
sb.append(" E:");
Iterator<Node> it = edges.iterator();
while (it.hasNext())
{
Node currNode = it.next();
sb.append(currNode.sortedId);
if (it.hasNext())
{
sb.append(", ");
}
}
sb.append(" NE:");
it = notEdges.iterator();
while (it.hasNext())
{
Node currNode = it.next();
sb.append(currNode.sortedId);
if (it.hasNext())
{
sb.append(", ");
}
}
sb.append(" P(x):");
it = this.candidatesFromBigGraph.iterator();
while (it.hasNext())
{
Node currNode = it.next();
sb.append(currNode.sortedId);
if (it.hasNext())
{
sb.append(",");
}
}
sb.append(" (" + this.outGoingEdges + "," + this.incomingEdges + ")");
return sb.toString();
}
}
public static SortedMap<Integer, Node> bigGraph;
public static SortedMap<Integer, Node> smallGraph;
public static boolean isDebug = false;
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
if (args.length < 3 || args.length > 4)
{
// print usage
printUsage();
}
else
{
if (args[0].equalsIgnoreCase("solve"))
{
if (args.length != 3)
{
System.out.println("Need 2 arguments for solve");
printUsage();
}
else
{
generateNodes(args[1]);
generateSatVariables(args[2]);
}
}
else if (args[0].equalsIgnoreCase("outputMapping"))
{
if (args.length != 4)
{
System.out.println("Need 4 arguments for outputMapping");
printUsage();
}
else
{
generateNodes(args[1]);
readSatResult(args[2], args[3]);
}
}
else
{
System.out.println("Can only have solve or outputMapping");
printUsage();
}
}
}
private static void generateNodes(String graphsFileName)
throws FileNotFoundException, IOException, Exception
{
graphsFr = new FileReader(graphsFileName);
graphsFile = new BufferedReader(graphsFr);
String line = graphsFile.readLine();
bigGraph = new TreeMap<Integer, GraphSolve.Node>();
smallGraph = new TreeMap<Integer, GraphSolve.Node>();
SortedMap<Integer, Node> currentGraph = bigGraph;
while (line != null)
{
String splits[] = line.split(" ");
if (line.trim().length() > 0 && splits.length == 2)
{
int node1 = Integer.parseInt(splits[0]);
int node2 = Integer.parseInt(splits[1]);
Node n1;
Node n2;
if (node1 == 0 && node2 == 0)
{
currentGraph = smallGraph;
line = graphsFile.readLine();
continue;
}
if (currentGraph.containsKey(node2))
{
n2 = currentGraph.get(node2);
}
else
{
n2 = new Node(node2);
currentGraph.put(node2, n2);
}
if (currentGraph.containsKey(node1))
{
n1 = currentGraph.get(node1);
}
else
{
n1 = new Node(node1);
currentGraph.put(node1, n1);
}
if (n1.edges!=null && n1.edges.contains(n2))
{
//throw new Exception ("there is already a link from " + n1.id + " to " + n2.id);
}
else
{
n1.edges.add(n2);
n1.qEdges.add(n2);
n1.outGoingEdges++;
n2.incomingEdges++;
}
}
else
{
throw new Exception("bad input in graphs file: " + line);
}
line = graphsFile.readLine();
}
updatedSortedIds(bigGraph);
updateSortedIdsForNoConnects(smallGraph);
generatePossibleMappings();
printNodes(bigGraph);
printNodes(smallGraph);
}
private static void generatePossibleMappings()
{
for (Node i: smallGraph.values())
{
for (Node j: bigGraph.values())
{
if (j.incomingEdges >= i.incomingEdges && j.outGoingEdges >= i.outGoingEdges)
{
i.candidatesFromBigGraph.add(j);
}
else
{
i.notCandidatesFromBigGraph.add(j);
//i.candidatesFromBigGraph.add(j);
}
}
}
}
private static void generateSatVariables(String satFileName) throws Exception
{
HashSet<Integer> definitelyFalseMappings = new HashSet<Integer>(smallGraph.size() * bigGraph.size() / 5);
int numEstablished=0;
try {
satInputFw = new FileWriter(satFileName, false /*append*/);
} catch (IOException e) {
System.out.println("unable to create sat file:" + satFileName);
e.printStackTrace();
return;
}
satInputFile = new BufferedWriter(satInputFw);
CNFClauseGenerator cg = new CNFClauseGenerator(bigGraph.size(),
smallGraph.size(), GraphSolve.satInputFile);
// generate initial list of variables
for (int i = 1; i <= smallGraph.size(); i++)
{
for (int j = 1; j <= bigGraph.size(); j++)
{
cg.addConditionInExistingClause(i, j, true);
}
cg.addNewClause();
}
// add restriction that a node from the small graph will map to the big graph
for (int i = 1; i <= smallGraph.size(); i++)
{
for (int j = 1; j <= bigGraph.size(); j++)
{
for (int k = 1; k <= bigGraph.size(); k++)
{
if (k != j)
{
cg.addNewClause();
cg.addConditionInExistingClause(i, j, false);
cg.addConditionInExistingClause(i, k, false);
}
}
}
}
// add restriction that no two nodes from the small graph can map to the same
// node in the bigger graph
for (int i = 1; i <= smallGraph.size(); i++)
{
for (int j = 1; j <= bigGraph.size(); j++)
{
for (int k = 1; k <= smallGraph.size(); k++)
{
if (k != i)
{
cg.addNewClause();
cg.addConditionInExistingClause(i, j, false);
cg.addConditionInExistingClause(k, j, false);
}
}
}
}
for (Node i: smallGraph.values())
{
if (i.candidatesFromBigGraph.size() == 1)
{
numEstablished++;
cg.addConditionInNewClause(i.sortedId, i.candidatesFromBigGraph.element().sortedId, true);
}
// go through the list of nodes that you possibly can't map to and specify them
for (Node jNot: i.notCandidatesFromBigGraph)
{
cg.addNewClause();
numEstablished++;
cg.addConditionInExistingClause(i.sortedId, jNot.sortedId, false);
int varNum = cg.generateVarNum(i.sortedId, jNot.sortedId);
definitelyFalseMappings.add(new Integer(varNum));
}
}
// generate edge mappings
for (Node i: smallGraph.values())
{
if (i.candidatesFromBigGraph.size() < 1)
{
// assume that we'll always have a proper subgraph to work with
// this below should not happen
System.out.println ("** The smaller graph is not a subset of the larger graph");
}
for (Node j: i.candidatesFromBigGraph)
{
// for each edge
for (Node iEdgeNode : i.edges)
{
cg.addNewClause();
cg.addConditionInExistingClause(i.sortedId, j.sortedId, false);
for (Node jEdgeNode: j.edges)
{
int varNum = cg.generateVarNum(iEdgeNode.sortedId, jEdgeNode.sortedId);
// there is no value for adding a clause to an OR condition that we already know
// is false. A | B | false | D, is really A | B | D
// this will help shorten the clauses
if (!definitelyFalseMappings.contains(new Integer(varNum)));
{
cg.addConditionInExistingClause(iEdgeNode.sortedId, jEdgeNode.sortedId, true);
}
}
}
// for each non-edge
for (Node iNonEdge : i.notEdges)
{
for (Node jEdgeNode: j.edges)
{
int varNum = cg.generateVarNum(iNonEdge.sortedId, jEdgeNode.sortedId);
// same situation, no value in adding a clause that doesn't give us any more information
// i.e. A | True = true
// it isn't affected by the value of A and doesn't help solve the equations
if (!definitelyFalseMappings.contains(new Integer(varNum)))
{
cg.addNewClause();
cg.addConditionInExistingClause(i.sortedId, j.sortedId, false);
cg.addConditionInExistingClause(iNonEdge.sortedId, jEdgeNode.sortedId, false);
}
}
}
}
}
for (Node i: smallGraph.values())
{
if (i.edges.size() + i.notEdges.size() != smallGraph.size() - 1)
{
System.out.println("***" + i);
}
}
System.out.println("Found fix vars: " + numEstablished);
// generate conditions for non-edges
cg.writeOutSatFile();
}
private static void printNodes(SortedMap<Integer, Node> graph) {
if (isDebug)
{
System.out.println("*Graph*");
for (Node n : graph.values())
{
System.out.println(n.toString());
}
System.out.println("*EndGraph*");
}
}
private static void updatedSortedIds(SortedMap<Integer, Node> graph) {
int count = 1;
for (Node n : graph.values())
{
n.sortedId = count;
count++;
}
}
private static void updateSortedIdsForNoConnects(SortedMap<Integer, Node> graph) {
int count = 1;
int graphSize = graph.size();
for (Node n : graph.values())
{
n.sortedId = count;
count++;
}
count--;
Node[] tempNodeArray = graph.values().toArray(new Node[graphSize]);
for (Node n : graph.values())
{
Iterator <Node> it = n.qEdges.iterator();
int lastNodeProcessed = 0;
Node currNode;
while (it.hasNext())
{
currNode = it.next();
// fill up the "non existent edges"
for (int i = lastNodeProcessed; i < currNode.sortedId - 1; i++)
{
if (tempNodeArray[i].sortedId != n.sortedId)
{
n.notEdges.add(tempNodeArray[i]);
}
}
lastNodeProcessed = currNode.sortedId;
}
// fill up the "non existent edges" for any last rows
for (int i = lastNodeProcessed; i < count; i++)
{
if (tempNodeArray[i].sortedId != n.sortedId)
{
n.notEdges.add(tempNodeArray[i]);
}
}
}
}
private static void readSatResult(String satResultPath, String outputMappingFilePath) throws Exception
{
try {
satOutputFw = new FileReader(satResultPath);
satOutputFile = new BufferedReader(satOutputFw);
outputMappinngFw = new FileWriter(outputMappingFilePath, false /*append*/);
outputMappingFile = new BufferedWriter(outputMappinngFw);
String line = satOutputFile.readLine().trim();
SortedSet<Integer> validAnswers = new TreeSet<Integer>();
if (line.equalsIgnoreCase("SAT"))
{
String[] splits = satOutputFile.readLine().trim().split(" ");
if (Integer.parseInt(splits[splits.length-1]) == 0)
{
for (int i=0; i < splits.length-1; i++)
{
int varNum = Integer.parseInt(splits[i]);
if (varNum > 0)
{
validAnswers.add(varNum);
}
}
Iterator<Integer> smallIterator = smallGraph.keySet().iterator();
for (int i=1; i <= smallGraph.size(); i++)
{
Node small = smallGraph.get(smallIterator.next());
Iterator<Integer> bigIterator = bigGraph.keySet().iterator();
for (int j=1; j <= bigGraph.size(); j++)
{
Node big = bigGraph.get(bigIterator.next());
if (validAnswers.contains(bigGraph.size() * (i-1) + j))
{
outputMappingFile.write(small.id + " " + big.id + newLine);
}
}
}
}
else
{
throw new Exception("sat output file has invalid format. doesn't end in 0");
}
}
else
{
outputMappingFile.write("0");
}
outputMappingFile.close();
} catch (FileNotFoundException e) {
System.out.println("Unable to open sat output file");
e.printStackTrace();
} catch (IOException e) {
System.out.println("Unable to read sat output file line");
e.printStackTrace();
}
}
private static void printUsage() {
System.out.println("Please enter three arguments\n" +
"java GraphSolve solve foo.graphs foo.satinput\n" +
"java GraphSolve outputMapping foo.graphs foo.satoutput foo.mapping");
}
}