/*
* (C) Copyright 2002 Arnaud Bailly (arnaud.oqube@gmail.com),
* Yves Roos (yroos@lifl.fr) and others.
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rationals.converters.algorithms;
import rationals.State;
import rationals.Transition;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* Implementation of simulated annealing for graph drawing<p>
* Transitions are spring that attracts nodes.
*
* @author Arnaud Bailly
* @version 22032002
*/
public class SimulatedAnnealing extends AbstractLayoutAlgorithm {
/** random move factor */
private double randomMove = 0.1;
/** width of display */
private double width = 200;
/** height of display */
private double height = 200;
/** maximum distance */
private double expectedDist = 70;
/** attractive force */
private double attractiveForce = 1;
/** cooling factor */
private double coolFactor = 0.005;
/** cooling threshold */
private double coolThreshold = 1;
/** temperature */
private double temperature = 70;
/** current temperature */
private double currentTemperature = temperature;
/** map of states to movement vector */
private Map movesCoord = new HashMap();
/** set of transitions */
private Set transitions;
/** automaton to display */
private rationals.Automaton automata;
/////////////////////////////////////////////////////:
// CONSTRUCTORS
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////:
// ACCESSORS AND MUTATORS
////////////////////////////////////////////////////
public double getCoolFactor() {
return coolFactor;
}
public void setCoolFactor(double cf) {
coolFactor = cf;
}
public double getCoolThreshold() {
return coolThreshold;
}
public void setCoolThreshold(double ct) {
coolThreshold = ct;
}
public double getTemperature() {
return temperature;
}
public void setTemperature(double temp) {
temperature = temp;
}
public double getWidth() {
return width;
}
public void setWidth(double temp) {
width = temp;
}
public double getHeight() {
return height;
}
public void setHeight(double temp) {
height = temp;
}
public double getExpectedDist() {
return expectedDist;
}
public void setExpectedDist(double temp) {
expectedDist = temp;
}
public double getAttractiveForce() {
return attractiveForce;
}
public void setAttractiveForce(double temp) {
attractiveForce = temp;
}
///////////////////////////////////////////////////
// PUBLIC METHODS
////////////////////////////////////////////////////
/**
* calculate display of automaton
*
* @param aut an Automaton object
*/
public void layout(rationals.Automaton aut)
throws rationals.converters.ConverterException {
automata = aut;
transitions = aut.delta();
// initialise
Iterator it = aut.states().iterator();
while (it.hasNext()) {
State s = (State) it.next();
statesCoord.put(
s,
new Coord(Math.random() * width, Math.random() * height));
movesCoord.put(s, new Coord(0, 0));
}
}
public void work() {
if (currentTemperature > coolThreshold) {
move();
currentTemperature = currentTemperature * (1 - coolFactor);
}
}
public boolean done() {
return currentTemperature < coolThreshold;
}
////////////////////////////////////////////////////
// PRIVATE METHODS
///////////////////////////////////////////////////
// returns true if the two states are connected by at least one transition
private boolean areConnected(State s1, State s2) {
Iterator it = transitions.iterator();
while (it.hasNext()) {
Transition trans = (Transition) it.next();
if ((trans.start().equals(s1) && trans.end().equals(s2))
|| (trans.start().equals(s2) && trans.end().equals(s1)))
return true;
}
return false;
}
/**
* a basic move of automaton
*/
private void move() {
Iterator it = statesCoord.keySet().iterator();
// calcul d'attraction
while (it.hasNext()) {
State s1 = (State) it.next();
Iterator it2 = statesCoord.keySet().iterator();
while (it2.hasNext()) {
State s2 = (State) it2.next();
if (s1.equals(s2))
continue;
Coord s1coord = (Coord) statesCoord.get(s1);
Coord s2coord = (Coord) statesCoord.get(s2);
Coord s1mcoord = (Coord) movesCoord.get(s1);
double dist = s1coord.distance(s2coord);
Coord vec = s1coord.vector(s2coord);
// calculate move for s1
vec.div(dist);
if (areConnected(s1, s2)) {
double attract =
attractiveForce * Math.log(dist / expectedDist);
// adjust vector
vec.mul(attract);
// System.err.println("coeff :" +coeff+",vector :"+vec);
// adjust states moves
vec.mul(currentTemperature);
s1mcoord.add(vec);
}
vec = s1coord.vector(s2coord);
vec.div(dist);
double repulse = (expectedDist * expectedDist) / (dist * dist);
// adjust vector
vec.mul(repulse);
// System.err.println("coeff :" +coeff+",vector :"+vec);
// adjust states moves
vec.mul(currentTemperature);
s1mcoord.sub(vec);
}
}
// apply moves to coordinates
it = statesCoord.keySet().iterator();
while (it.hasNext()) {
State s = (State) it.next();
Coord c = (Coord) statesCoord.get(s);
Coord m = (Coord) movesCoord.get(s);
// move towards barycenter
c.add(m);
// System.err.println(s+" : "+c+" ->"+m);
m.x = m.y = 0.0; // reset move
}
}
/**
* @return
*/
public double getRandomMove() {
return randomMove;
}
/**
* @param d
*/
public void setRandomMove(double d) {
randomMove = d;
}
}