/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) 2008, Benedikt Huber (benedikt.huber@gmail.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jopdesign.wcet.ipet;
import com.jopdesign.common.graphutils.IDProvider;
import com.jopdesign.wcet.ipet.LinearConstraint.ConstraintType;
import lpsolve.LpSolveException;
import org.jgrapht.DirectedGraph;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;
/**
* @deprecated Use IPETBuilder and IPETSolver instead
*
* Max-Cost-Network-Flow solver with additional linear constraints
* (see Implicit Path Enumeration (IPET)).
* <p>
* A MCNF problem consists of a directed graph with a dedicated source and sink node,
* cost labels for nodes and linear edge constraints.
* </p>
* <p>
* Note: With simple (constant-per-edge) flow constraints, the problem could be solved using e.g.
* [Goldberg97]. With arbitrary flow constraints, this is an ILP problem.
* </p>
* <p>
* [Goldberg97] An efficient implementation of a scaling minimum-cost flow algorithm (1997). A. Goldberg. <br/>
* </p>
* @author Benedikt Huber (benedikt.huber@gmail.com)
*
* @param <V> the node type
* @param <E> the edge type
*/
public class MaxCostFlow<V,E> {
/**
* Data type encapsulating decision variables.<br/>
* Equality: Object Identity
*/
public static class DecisionVariable {
private int id;
private DecisionVariable(int id) {
this.id = id;
}
}
// Implies that this will only work if flow is < 10 billion for each node
private static final long BIGM = Long.MAX_VALUE;
private DirectedGraph<V, E> graph;
private Map<E,Integer> idMap;
private Map<DecisionVariable, Integer> dMap;
private int dGen;
private Map<Integer,E> revMap;
private Map<V,Long> nodeCostObjective;
private Vector<LinearConstraint<E>> flowConstraints;
private Vector<LinearConstraint<Object>> extraConstraints;
private LinearVector<Object> extraCost;
private V entry;
private V exit;
private IDProvider<Object> idProvider;
private String key;
private HashMap<Integer, DecisionVariable> dRevMap;
private File outFile = null;
/**
* if set, the problem will be dumped into a file
* @param outFile the file to dump to, or null to disable dumping
*
*/
public void setDumpILP(File outFile) {
this.outFile = outFile;
}
/**
* Initialize the MCMF problem with the given graph
* @param g the graph
* @param entry the source node
* @param exit the sink node
*/
public MaxCostFlow(String key, DirectedGraph<V,E> g, V entry, V exit) {
this.key = key;
this.graph = g;
this.entry = entry;
this.exit = exit;
this.nodeCostObjective = new HashMap<V, Long>();
this.flowConstraints = new Vector<LinearConstraint<E>>();
this.extraConstraints = new Vector<LinearConstraint<Object>>();
this.extraCost = new LinearVector<Object>();
generateMapping();
addBasicFlowConstraints();
}
/**
* Set the cost for a node <code>n</code>: for each unit of flow passing <code>n</code>,
* <code>cost</code> is added to the total cost of the solution.
* @param n
* @param cost
*/
public void setCost(V n, long cost) {
if(cost == 0) return;
this.nodeCostObjective.put(n,
(nodeCostObjective.containsKey(n) ? nodeCostObjective.get(n) : 0) + cost);
}
/**
* Add a linear flow constraint
* @param constraint of the form <code>a x <=> c</code>, where <code>x</code>
* is a vector of edges.
*/
public void addFlowConstraint(LinearConstraint<E> constraint) {
this.flowConstraints.add(constraint);
}
/**
* Solve this MCMF problem using {@link LpSolveWrapper}.
* @param flowMapOut if not null, write solution into this map, assigning a flow to each edge
* @return the cost of the solution
* @throws Exception if the ILP solver fails
*/
public double solve(Map<E,Long> flowMapOut) throws Exception {
return solve(flowMapOut,null);
}
/**
* Solve this MCMF problem using {@link LpSolveWrapper}.
* @param flowMapOut if not null, write solution into this map, assigning a flow to each edge
* @param decisionsOut if not null, write assignments to decision variable in this map
* @return the cost of the solution
* @throws Exception if the ILP solver fails
*/
public double solve(Map<E,Long> flowMapOut, Map<DecisionVariable,Boolean> decisionsOut) throws Exception {
LpSolveWrapper<Object> wrapper =
new LpSolveWrapper<Object>(dGen-1,true,this.idProvider);
for(DecisionVariable dv : dMap.keySet()) {
wrapper.setBinary(dv);
}
for(LinearConstraint<E> lc : flowConstraints) {
wrapper.addConstraint(lc);
}
for(LinearConstraint<Object> extra : extraConstraints) {
wrapper.addConstraint(extra);
}
LinearVector<Object> costVec = new LinearVector<Object>(extraCost);
// build cost objective
for(Entry<V,Long> e : this.nodeCostObjective.entrySet()) {
long costFactor = e.getValue();
for(E edge : this.graph.incomingEdgesOf(e.getKey())) {
costVec.add(edge, costFactor);
}
}
wrapper.setObjective(costVec,true);
double[] objVec = new double[dGen-1];
wrapper.freeze();
if(this.outFile != null) {
dumpILP(wrapper);
}
double sol = Math.round(wrapper.solve(objVec));
if(flowMapOut != null) {
int i = 0;
while(revMap.containsKey(i+1)) {
flowMapOut.put(revMap.get(i+1), Math.round(objVec[i]));
i++;
}
if(decisionsOut != null) {
while(dRevMap.containsKey(i+1)) {
decisionsOut.put(dRevMap.get(i+1), objVec[i] > 0.5);
i++;
}
}
}
return sol;
}
private void dumpILP(LpSolveWrapper<?> wrapper) throws LpSolveException {
wrapper.dumpToFile(outFile);
FileWriter fw = null;
try {
fw = new FileWriter(outFile,true);
} catch (IOException e1) {
throw new LpSolveException("Failed to open ILP file");
}
try {
fw.append("/* Mapping: \n");
for(Entry<E,Integer> e : this.idMap.entrySet()) {
fw.append(" "+e.getKey() + " -> C" + e.getValue() + "\n");
}
for(Entry<DecisionVariable, Integer> dv : this.dMap.entrySet()) {
fw.append(" "+dv.getKey() + " -> C" + dv.getValue() + "\n");
}
fw.append(this.toString());
fw.append("*/\n");
} catch (IOException e) {
throw new LpSolveException("Failed to write to ILP file");
} finally {
try {
fw.close();
} catch (IOException e) {
throw new LpSolveException("Failed to close ILP file");
}
}
}
/* generate a bijective mapping between edges/decision variables and integers */
private void generateMapping() {
idMap = new HashMap<E, Integer>();
this.revMap = new HashMap<Integer, E>();
for(E e : graph.edgeSet()) {
int key = idMap.size() + 1;
idMap.put(e, key);
revMap.put(key, e);
}
dMap = new HashMap<DecisionVariable, Integer>();
dRevMap = new HashMap<Integer,DecisionVariable>();
dGen = idMap.size() + 1;
/* create ID provider */
this.idProvider = new IDProvider<Object>() {
/* Note: No closures in java, so idMap/revMap have to be instance variables */
public Object fromID(int id) { return revMap.get(id); }
public int getID(Object t) {
Integer key = idMap.get(t);
if(key != null) return key.intValue();
else return dMap.get(t).intValue();
}
};
}
/*
* For all nodes but entry and exit, flow incoming = flow outgoing
* flow outgoing(source) = flow ingoing(sink) = 1
*/
private void addBasicFlowConstraints() {
for(V node : graph.vertexSet()) {
if(node.equals(this.entry)) continue;
if(node.equals(this.exit)) continue;
LinearConstraint<E> flowConstraint = new LinearConstraint<E>(ConstraintType.Equal);
for(E ingoing : graph.incomingEdgesOf(node)) {
flowConstraint.addLHS(ingoing);
}
for(E outgoing : graph.outgoingEdgesOf(node)) {
flowConstraint.addRHS(outgoing);
}
addFlowConstraint(flowConstraint);
}
}
@Override
public String toString() {
StringBuffer s = new StringBuffer();
s.append("Max-Cost-Flow problem with cost vector: ");
boolean first = true;
for(Entry<V,Long> e: nodeCostObjective.entrySet()) {
if(first) first = false;
else s.append(" + ");
s.append(e.getValue());
s.append(' ');
s.append(e.getKey());
}
s.append("\nFlow\n");
for(LinearConstraint<E> lc : flowConstraints) {
s.append(lc);
s.append('\n');
}
return s.toString();
}
}