/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: AStarWorker.java
* Written by: Christian Julg, Jonas Thedering (Team 1)
*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) 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.
*
* Electric(tm) 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 Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.routing.experimentalAStar1;
import java.util.List;
import java.util.concurrent.Callable;
/**
* Does the actual A* routing for the given net
*
* @author Jonas Thedering
* @author Christian Jülg
*/
public class AStarWorker implements Callable<Net> {
private boolean DEBUG = false;
private Storage storage;
// only used by run(), use nodePool instead
private List<ObjectPool<Node>> nodePools;
private List<ObjectPool<Storage>> storagePools;
private ObjectPool<Node> nodePool;
private ObjectPool<Storage> storagePool;
private Net net; // contains paths
private final Map map;
private Goal goal;
private long shutdownTime;
public AStarWorker(Net net, List<ObjectPool<Node>> nodePools,
List<ObjectPool<Storage>> storagePools, Map map, Goal goal, long shutdownTime) {
this.nodePools = nodePools;
this.storagePools = storagePools;
this.net = net;
this.map = map;
this.goal = goal;
this.shutdownTime = shutdownTime;
DEBUG &= AStarRoutingFrame.getInstance().isOutputEnabled();
}
/** Connects a child for the given position to the parent, if passable */
private void linkChild(int x, int y, int z, Node parent) {
if (x < 0 || x >= map.getWidth() || y < 0 || y >= map.getHeight() || z < 0 || z >= map.getLayers())
return;
if (goal.isTileOK(parent.x, parent.y, parent.z, x, y, z)) {
int g = parent.g + goal.getTileCost(parent.x, parent.y, parent.z, x, y, z);
Node child = storage.get(x, y, z);
if (child != null) {
parent.children[parent.childCount++] = child;
if (g < child.g) {
child.parent = parent;
child.g = g;
if(storage.isNodeInOpen(child))
storage.decreaseCost(child, g + child.h);
else
updateChildren(child);
}
return;
}
// No node existing for that position, so create new one
child = nodePool.alloc();
child.initialize(x, y, z);
child.parent = parent;
child.g = g;
child.h = goal.distanceToGoal(x, y, z);
child.f = (g + child.h);
parent.children[parent.childCount++] = child;
storage.addToOpen(child);
}
}
/** Walks through the children of the given node to update their cost */
private void updateChildren(Node parent) {
for (int i = 0; i < parent.childCount; ++i) {
Node child = parent.children[i];
int g = parent.g
+ goal.getTileCost(parent.x, parent.y, parent.z, child.x, child.y, child.z);
if (g < child.g) {
child.g = g;
child.parent = parent;
if(storage.isNodeInOpen(child))
storage.decreaseCost(child, g + child.h);
else
updateChildren(child);
}
}
}
public Net call() throws Exception {
//sync on nodepools, is unsynced list
synchronized (nodePools) {
storagePool = storagePools.remove(0);
assert nodePools.size() > 0;
this.nodePool = nodePools.remove(0);
}
storage = storagePool.alloc();
int index;
// Skip paths already routed
for (index=0; index<net.pathDone.length; index++){
if (!net.pathDone[index]){
break;
}
}
boolean shutdown = System.currentTimeMillis() > shutdownTime;
// Iterates over the not yet routed paths of the net
while(!goal.isRoutingComplete() && !shutdown) {
storage.initialize(map.getWidth(), map.getHeight(), map.getLayers());
Node startNode = nodePool.alloc();
int[] startLoc = goal.getNextStart(); // returns an int[] {x,y,z}
startNode.initialize(startLoc[0], startLoc[1], startLoc[2]);
startNode.g = 0;
startNode.h = goal.distanceToGoal(startNode.x, startNode.y, startNode.z);
startNode.f = startNode.h;
// If the start node is invalid, don't start the algorithm
int status = map.getStatus(startNode.x, startNode.y, startNode.z);
assert status != Map.CLEAR : "Start position not marked!";
if (status == net.getNetID())
storage.addToOpen(startNode);
Node finishNode = null;
int count = 0;
long startTime=0;
if(DEBUG)
startTime = System.currentTimeMillis();
// run A* until finish is found or all nodes inspected
while (!storage.isOpenEmpty()) {
++count;
Node current = storage.shiftCheapestNode();
int x = current.x;
int y = current.y;
int z = current.z;
if (goal.isFinishPosition(x, y, z)) {
finishNode = current;
break;
}
linkChild(x, y, z - 1, current);
linkChild(x, y, z + 1, current);
int spaceAround = map.getSpaceAround(x, y, z);
if(spaceAround == 0) {
// We are in unknown territory, move conservatively
linkChild(x - 1, y, z, current);
linkChild(x + 1, y, z, current);
linkChild(x, y - 1, z, current);
linkChild(x, y + 1, z, current);
}
else {
int spaceDiff = spaceAround-1;
int offX = x % spaceAround;
int offY = y % spaceAround;
boolean xInside = offX > 0 && offX < spaceDiff;
boolean yInside = offY > 0 && offY < spaceDiff;
if(xInside && yInside) {
// We are in the middle of clear space, move to the margins
linkChild(x - offX, y, z, current);
linkChild(x - offX + spaceDiff, y, z, current);
linkChild(x, y - offY, z, current);
linkChild(x, y - offY + spaceDiff, z, current);
}
else {
// We are at a margin, move by 1 along the margin and to the outside,
// jump to the margin on the other side
if(offX == spaceDiff && yInside)
linkChild(x - spaceDiff, y, z, current);
else
linkChild(x - 1, y, z, current);
if(offX == 0 && yInside)
linkChild(x + spaceDiff, y, z, current);
else
linkChild(x + 1, y, z, current);
if(offY == spaceDiff && xInside)
linkChild(x, y - spaceDiff, z, current);
else
linkChild(x, y - 1, z, current);
if(offY == 0 && xInside)
linkChild(x, y + spaceDiff, z, current);
else
linkChild(x, y + 1, z, current);
}
}
}
// get Path to enter routing result
Path path = net.getPaths().get(index);
path.initialize(); // only segment set before initialize
if(DEBUG) {
long time = System.currentTimeMillis()-startTime;
long speed = -2;
if(time > 0){
speed = count / time;
} else{
speed = (count>0)? 0 : -1;
}
System.out.printf("AStarWorker: %8d iterations in %4d ms (%4d it/ms) for path in \"%s\"\n", count, time, speed, path.segment.getNetName());
}
if (finishNode != null) {
// Path was routed successfully, create the nodesX/Y/Z arrays by backtracking from the finish node
int pathLength = 1;
{
Node current = finishNode;
while (current != startNode) {
pathLength += Math.abs(current.x - current.parent.x)
+ Math.abs(current.y - current.parent.y)
+ Math.abs(current.z - current.parent.z);
current = current.parent;
}
}
path.totalCost = finishNode.g;
path.nodesX = new int[pathLength];
path.nodesY = new int[pathLength];
path.nodesZ = new int[pathLength];
Node current = finishNode;
int i = pathLength - 1;
while (current != startNode) {
if(current.x < current.parent.x)
for(int x = current.x; x < current.parent.x; ++x) {
path.nodesX[i] = x;
path.nodesY[i] = current.y;
path.nodesZ[i] = current.z;
--i;
}
else if(current.x > current.parent.x)
for(int x = current.x; x > current.parent.x; --x) {
path.nodesX[i] = x;
path.nodesY[i] = current.y;
path.nodesZ[i] = current.z;
--i;
}
else if(current.y < current.parent.y)
for(int y = current.y; y < current.parent.y; ++y) {
path.nodesX[i] = current.x;
path.nodesY[i] = y;
path.nodesZ[i] = current.z;
--i;
}
else if(current.y > current.parent.y)
for(int y = current.y; y > current.parent.y; --y) {
path.nodesX[i] = current.x;
path.nodesY[i] = y;
path.nodesZ[i] = current.z;
--i;
}
else if(current.z < current.parent.z)
for(int z = current.z; z < current.parent.z; ++z) {
path.nodesX[i] = current.x;
path.nodesY[i] = current.y;
path.nodesZ[i] = z;
--i;
}
else if(current.z > current.parent.z)
for(int z = current.z; z > current.parent.z; --z) {
path.nodesX[i] = current.x;
path.nodesY[i] = current.y;
path.nodesZ[i] = z;
--i;
}
else
assert false;
// nodesX/Y will be translated to cellGrid in runRouting
// translating here confuses AStarMaster
current = current.parent;
}
path.nodesX[0] = startNode.x;
path.nodesY[0] = startNode.y;
path.nodesZ[0] = startNode.z;
} else {
// path was unroutable, set path.totalCost to improve error messages in Master
path.totalCost = count;
int finishStatus = goal.getFinishPositionStatus();
if (finishStatus != net.getNetID()){
//finish is marked incorrectly
path.totalCost = -2;
}
}
// Return "borrowed" node objects
storage.freeNodes(nodePool);
// Skip paths already routed
for (index=index+1; index<net.getPaths().size(); index++){
if (!net.pathDone[index]){
break;
}
}
shutdown = System.currentTimeMillis() > shutdownTime;
}
if (DEBUG && shutdown) {
System.out.printf("AStarWorker: shutdown now\n");
}
storagePool.free(storage);
// sync on nodepools, is unsynced list
synchronized (nodePools) {
nodePools.add(nodePool);
storagePools.add(storagePool);
}
return net;
}
}