/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: PositionIndex.java
* Written by Team 6: Sebastian Roether, Jochen Lutz
*
* This code has been developed at the Karlsruhe Institute of Technology (KIT), Germany,
* as part of the course "Multicore Programming in Practice: Tools, Models, and Languages".
* Contact instructor: Dr. Victor Pankratius (pankratius@ipd.uka.de)
*
* Copyright (c) 2010 Sun Microsystems and Static Free Software
*
* 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.placement.simulatedAnnealing2;
import com.sun.electric.database.geometry.Orientation;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* To speed up the node overlap calculation, this class provides a method
* that returns only the nodes within the proximity of a given node. For this,
* the area is partitioned into quadrants (buckets) into which the nodes are sorted.
* A Node is sorted into a bucket when a least one of its corners is within the
* boundaries of that quadrant (bucket)
*/
public class PositionIndex {
/**
* This class holds all information that is needed to evaluate the area effects
* of a perturbation. it will not be altered so the calculation is not affected
* by other threads
*
* @author Basti
*
*/
public static class AreaSnapshot {
public double maxX = -Double.MAX_VALUE;
public double minX = Double.MAX_VALUE;
public double maxY = -Double.MAX_VALUE;
public double minY = Double.MAX_VALUE;
public double sndMaxX = maxX;
public double sndMinX = minX;
public double sndMaxY = maxY;
public double sndMinY = minY;
// NOTE: there may be more than one node that has maximum coordinates. this causes the boundaries to be recalculated more often than needed
public ProxyNode node_maxX = null;
public ProxyNode node_maxY = null;
public ProxyNode node_minY = null;
public ProxyNode node_minX = null;
public ProxyNode node_sndMaxX = null;
public ProxyNode node_sndMaxY = null;
public ProxyNode node_sndMinY = null;
public ProxyNode node_sndMinX = null;
protected AreaSnapshot clone() {
AreaSnapshot clone = new AreaSnapshot();
clone.maxX = maxX;
clone.minX = minX;
clone.maxY = maxY;
clone.minY = minY;
clone.sndMaxX = sndMaxX;
clone.sndMaxY = sndMaxY;
clone.sndMinX = sndMinX;
clone.sndMinY = sndMinY;
clone.node_maxX = node_maxX;
clone.node_maxY = node_maxY;
clone.node_minX = node_minX;
clone.node_minY = node_minY;
clone.node_sndMaxX = node_sndMaxX;
clone.node_sndMaxY = node_sndMaxY;
clone.node_sndMinX = node_sndMinX;
clone.node_sndMinY = node_sndMinY;
return clone;
}
public double getArea()
{
return (maxX - minX) * (maxY - minY);
}
/**
* Method that calculates the total chip are after the node <node> has been removed
* and the node <dummy> has been placed
* @param node The actual node that is replaced by <dummy> and should be ignored
* @param dummy The dummy that replaces <node>
* @return The area after <node> has been replaced by <dummy>
*/
public double areaForDummy(ProxyNode node, ProxyNode dummy)
{
double lminX = minX;
double lmaxX = maxX;
double lminY = minY;
double lmaxY = maxY;
double ld = dummy.getPlacementX() - dummy.width/2;
double rd = dummy.getPlacementX() + dummy.width/2;
double td = dummy.getPlacementY() - dummy.height/2;
double bd = dummy.getPlacementY() + dummy.height/2;
// Remove node
// Get new boundaries for when the node to be moved was an outmost node
if(node == node_maxX) lmaxX = sndMaxX;
if(node == node_minX) lminX = sndMinX;
if(node == node_minY) lminY = sndMinY;
if(node == node_maxY) lmaxY = sndMaxY;
// Add dummy node
if(ld < lminX) { lminX = ld; }
if(rd > lmaxX) { lmaxX = rd; }
if(td < lminY) { lminY = td; }
if(bd > lmaxY) { lmaxY = bd; }
return (lmaxX-lminX) * (lmaxY-lminY);
}
/**
* Method that calculates the total chip are after two nodes have been removed
* and two nodes have been placed
* @return The area after <node1>, <node2> have been replaced by <dummy1>, <dummy2>
*/
public double areaForDummy(ProxyNode node1, ProxyNode node2, ProxyNode dummy1, ProxyNode dummy2 )
{
double lminX = minX;
double lmaxX = maxX;
double lminY = minY;
double lmaxY = maxY;
double ld1 = dummy1.getPlacementX() - dummy1.width/2;
double rd1 = dummy1.getPlacementX() + dummy1.width/2;
double td1 = dummy1.getPlacementY() - dummy1.height/2;
double bd1 = dummy1.getPlacementY() + dummy1.height/2;
double ld2 = dummy2.getPlacementX() - dummy2.width/2;
double rd2 = dummy2.getPlacementX() + dummy2.width/2;
double td2 = dummy2.getPlacementY() - dummy2.height/2;
double bd2 = dummy2.getPlacementY() + dummy2.height/2;
// Remove nodes
// Get new boundaries for when one of the nodes to be moved was an outmost node
if(node1 == node_maxX || node2 == node_maxX) lmaxX = sndMaxX;
if(node1 == node_minX || node2 == node_minX) lminX = sndMinX;
if(node1 == node_minY || node2 == node_minY) lminY = sndMinY;
if(node1 == node_maxY || node2 == node_maxY) lmaxY = sndMaxY;
// Add dummy nodes
if(ld1 < lminX) { lminX = ld1; }
if(rd1 > lmaxX) { lmaxX = rd1; }
if(td1 < lminY) { lminY = td1; }
if(bd1 > lmaxY) { lmaxY = bd1; }
if(ld2 < lminX) { lminX = ld2; }
if(rd2 > lmaxX) { lmaxX = rd2; }
if(td2 < lminY) { lminY = td2; }
if(bd2 > lmaxY) { lmaxY = bd2; }
return (lmaxX-lminX) * (lmaxY-lminY);
}
/**
* Method that stretches the boundaries so that a new node falls within the covered area
* @param node
* @param l x-coordinate of the left edge of the node
* @param r right
* @param t top
* @param b bottom
*/
public void add(ProxyNode node, double l, double r, double t, double b)
{
// Left
if ( node == node_minX || node == node_minY || node == node_maxX || node == node_maxY )
return;
if(l < sndMinX) {
sndMinX = l;
node_sndMinX = node;
if(l < minX) {
sndMinX = minX;
minX = l;
node_sndMinX = node_minX;
node_minX = node;
}
}
// Right
if(r > sndMaxX) {
sndMaxX = r;
node_sndMaxX = node;
if(r > maxX) {
sndMaxX = maxX;
maxX = r;
node_sndMaxX = node_maxX;
node_maxX = node;
}
}
// Top
if(t < sndMinY) {
sndMinY = t;
node_sndMinY = node;
if(t < minY) {
sndMinY = minY;
minY = t;
node_sndMinY = node_minY;
node_minY = node;
}
}
// Bottom
if(b > sndMaxY) {
sndMaxY = b;
node_sndMaxY = node;
if(b > maxY) {
sndMaxY = maxY;
maxY = b;
node_sndMaxY = node_maxY;
node_maxY = node;
}
}
}
}
public AreaSnapshot area = new AreaSnapshot();
private AreaSnapshot area_buffer = new AreaSnapshot();
private ArrayList<ProxyNode>[][] buckets;
private double bucketSize;
private int bucketCount;
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock read = rwl.readLock();
private final Lock write = rwl.writeLock();
/**
*
* @param chipLength the maximum chip length
* @param nodesToPlace the nodes to be sorted into the buckets
*/
PositionIndex( double chipLength, ArrayList<ProxyNode> nodesToPlace )
{
// the bucket size is twice the size of the biggest node
// (so the area is four times the area of the biggest single node)
double totalWidth = 0, totalHeight = 0;
for(ProxyNode p : nodesToPlace) {
//if(p.width > bucketSize) bucketSize = p.width;
//if(p.height > bucketSize) bucketSize = p.width;
totalWidth += p.width;
totalHeight += p.height;
}
//bucketSize *= 2;
bucketSize = (totalWidth + totalHeight) / (2 * nodesToPlace.size());
// calculate how many buckets are needed and sort nodes into buckets
bucketCount = (int) Math.ceil(chipLength / bucketSize);
buckets = new ArrayList[bucketCount][bucketCount];
for ( int i = 0; i < bucketCount; i++ )
for ( int j = 0; j < bucketCount; j++ )
buckets[i][j] = new ArrayList<ProxyNode>();
for ( ProxyNode p : nodesToPlace ) {
put( p, p.getPlacementX(), p.getPlacementY() );
}
swapAreaBuffer();
}
/**
* Adds a node to all buckets that it currently overlaps
* @param node the node to add to the bucket
*/
private void put( ProxyNode node ) {
put( node, node.getPlacementX(), node.getPlacementY());
}
/**
* Adds a node to all buckets that it overlaps when placed at the given coordinates
* @param node the node to add to the bucket
* @param posX
* @param posY
*/
private void put( ProxyNode node, double posX, double posY )
{
double l = posX - node.width/2;
double r = posX + node.width/2;
double t = posY - node.height/2;
double b = posY + node.height/2;
int minX = bucketNum( l );
int minY = bucketNum( t );
int maxX = bucketNum( r );
int maxY = bucketNum( b );
area_buffer.add(node, l, r, t, b);
for ( int x = minX; x <= maxX; x++ ) {
for ( int y = minY; y <= maxY; y++ ) {
buckets[x][y].add( node );
}
}
}
/**
* Removes a node from all buckets
* @param node the node to remove
*/
private void remove( ProxyNode node )
{
double l = node.getPlacementX() - node.width/2;
double r = node.getPlacementX() + node.width/2;
double t = node.getPlacementY() - node.height/2;
double b = node.getPlacementY() + node.height/2;
int minX = bucketNum( l );
int minY = bucketNum( t );
int maxX = bucketNum( r );
int maxY = bucketNum( b );
for ( int x = minX; x <= maxX; x++ ) {
for ( int y = minY; y <= maxY; y++ ) {
buckets[x][y].remove( node );
}
}
if(node == area_buffer.node_minX ||
node == area_buffer.node_maxX ||
node == area_buffer.node_minY ||
node == area_buffer.node_maxY ||
node == area_buffer.node_sndMaxX ||
node == area_buffer.node_sndMinX ||
node == area_buffer.node_sndMaxY ||
node == area_buffer.node_sndMinY
) {
// TODO: this is not always necessary because that node might be moved
// outwards an will therefore be added later
calcBoundaries();
}
}
/**
* Method that finds the outmost nodes and creates a new <AreaSnapshot>
*/
private void calcBoundaries()
{
// only call this while holding the write lock
area_buffer = new AreaSnapshot();
// TODO PERFORMANCE to find the boundaries we could easily search from the outer buckets
// and stop after checking all nodes in the same row/column
for ( int i = 0; i < bucketCount; i++ )
for ( int j = 0; j < bucketCount; j++ )
for(ProxyNode node: buckets[i][j]) {
double l = node.getPlacementX() - node.width/2;
double r = node.getPlacementX() + node.width/2;
double t = node.getPlacementY() - node.height/2;
double b = node.getPlacementY() + node.height/2;
area_buffer.add(node, l, r, t, b);
}
}
/**
* Swaps two nodes
* @param node1
* @param node2
*/
public void swap( ProxyNode node1, ProxyNode node2)
{
double x = node1.getPlacementX();
double y = node1.getPlacementY();
write.lock();
remove(node1);
remove(node2);
node1.setPlacement(node2.getPlacementX(), node2.getPlacementY());
node2.setPlacement(x, y);
put(node1);
put(node2);
swapAreaBuffer();
write.unlock();
}
/**
* Rotates a node
* @param node
* @param o the new orientation of that node
*/
public void rotate (ProxyNode node, Orientation o)
{
write.lock();
remove(node);
node.setPlacementOrientation(o, false);
put(node);
swapAreaBuffer();
write.unlock();
}
/**
* Moves a node to another position
* @param node
* @param newX
* @param newY
*/
public void move( ProxyNode node, double newX, double newY )
{
write.lock();
remove(node);
node.setPlacement(newX, newY);
put(node, newX, newY);
swapAreaBuffer();
write.unlock();
}
/**
* Swaps the current area buffer with the one that was created by the last perturbation
* This is for making the perturbation atomic without using a lock (but the whole idea is...)
*/
private void swapAreaBuffer()
{
area = area_buffer;
area_buffer = area.clone();
}
/**
* Returns a list with nodes that possibly overlap with a given node
* @param node
* @return List of all nodes which occupy the same buckets as /node/. Do not necessarily overlap.
*/
public List<ProxyNode> getPossibleOverlaps( ProxyNode node )
{
List<ProxyNode> result = new ArrayList<ProxyNode>();
int minX, minY, maxX, maxY;
minX = bucketNum( node.getPlacementX() - node.width/2 );
minY = bucketNum( node.getPlacementY() - node.height/2 );
maxX = bucketNum( node.getPlacementX() + node.width/2 );
maxY = bucketNum( node.getPlacementY() + node.height/2 );
read.lock();
for ( int x = minX; x <= maxX; x++ ) {
for ( int y = minY; y <= maxY; y++ ) {
result.addAll( buckets[x][y] );
// TODO Check for duplicates
/*
for ( ProxyNode p : buckets[x][y] ) {
if ( ! result.contains( p ) )
result.add( p );
}
*/
}
}
read.unlock();
return result;
}
/**
* Calculates into which bucket a coordinate falls
* @param coordinate along an axis
* @return the bucketIndex for the given coordinate
*/
private final int bucketNum( double pos )
{
int bucket = (int) Math.floor( ( pos + bucketSize * bucketCount/2 ) / bucketSize );
if ( bucket < 0 )
bucket = 0;
if ( bucket >= bucketCount )
bucket = bucketCount - 1;
return bucket;
}
}