com.sun.electric.plugins.minarea.parallelbitmapscala.BitMapMinAreaChecker.scala Maven / Gradle / Ivy
The newest version!
/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: BitMapMinAreaChecker.scala
*
* Copyright (c) 2011, 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.plugins.minarea.parallelbitmapscala
import com.sun.electric.api.minarea.LayoutCell
import com.sun.electric.api.minarea.ManhattanOrientation
import com.sun.electric.api.minarea.MinAreaChecker
import com.sun.electric.api.minarea.geometry.Point
import java.util.BitSet
import java.util.LinkedHashMap
import java.util.Properties
import scala.actors.Futures.future
import scala.collection.immutable.TreeSet
import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.HashMap
import scala.collection.mutable.Stack
import scala.math.{min, max}
class BitMapMinAreaChecker extends MinAreaChecker {
override def getAlgorithmName = "ParallelBitMapScala"
override def getDefaultParameters = {
val parameters = new Properties()
parameters.put(MinAreaChecker.RECTS_PER_STRIPE, BitMapMinAreaChecker.DEFAULT_RECTS_PER_STRIPE)
parameters
}
/**
* @param topCell top cell of the layout
* @param minArea minimal area of valid polygon
* @param parameters algorithm parameters
* @param errorLogger an API to report violations
*/
override def check(topCell: LayoutCell, minArea: Long, parameters: Properties, errorLogger: MinAreaChecker.ErrorLogger) = {
val task = new CheckerTask(topCell, minArea, errorLogger)
val rectsPerStripe = java.lang.Long.parseLong(parameters.getProperty(MinAreaChecker.RECTS_PER_STRIPE, BitMapMinAreaChecker.DEFAULT_RECTS_PER_STRIPE))
val numRects = countRects(topCell, new HashMap[LayoutCell,Long]())
val numStripes = (numRects/rectsPerStripe + 1).asInstanceOf[Int]
if (BitMapMinAreaChecker.DEBUG >= 1) println("numStripes="+numStripes)
task.check(numStripes)
}
def countRects(t: LayoutCell, counted: HashMap[LayoutCell,Long]): Long = {
counted.get(t) match {
case Some(count) => count
case None =>
var count: Long = t.getNumRectangles
t.traverseSubcellInstances(new LayoutCell.SubcellHandler {
def apply(subCell: LayoutCell, anchorX: Int, anchorY: Int, subOrient: ManhattanOrientation) = {
count += countRects(subCell, counted)
}
})
counted.put(t, count)
count
}
}
}
object BitMapMinAreaChecker {
val PARALLEL = true
val DEBUG = 1
val DEFAULT_RECTS_PER_STRIPE = "100000"
val hugePoly = new ExtPoly
hugePoly.area = Long.MaxValue
def emptyStripeExts(x: Int) = new StripeExts(x, x, new Array[ExtSegment](0), new Array[ExtSegment](0), 0)
def connectStripes(l: StripeExts, u: StripeExts, minArea: Long, errorLogger: MinAreaChecker.ErrorLogger): StripeExts = {
assert(l.getXmax == u.getXmin)
val lmin = l.getMinSegs
val lmax = l.getMaxSegs
val umin = u.getMinSegs
val umax = u.getMaxSegs
val rmin = new Array[ExtSegment](lmin.size)
val rmax = new Array[ExtSegment](umax.size)
val polys = new LinkedHashMap[ExtPoly,Int]()
// put external polys first
addP(polys, BitMapMinAreaChecker.hugePoly)
for (s <- lmin) addP(polys, s.getP)
for (s <- umax) addP(polys, s.getP)
// put polys on connection line
val innerPolys = polys.size
for (s <- lmax) addP(polys, s.getP)
for (s <- umin) addP(polys, s.getP)
val polyInds = initInds(polys.size)
var il = 0
var iu = 0
while (il < lmax.size && iu < umin.size) {
val l = lmax(il)
val u = umin(iu)
if (l.getYmax <= u.getYmin) {
il += 1
} else if (u.getYmax <= l.getYmin) {
iu += 1
} else {
connectInds(polyInds, polys.get(l.getP), polys.get(u.getP))
if (l.getYmax <= u.getYmax) il += 1 else iu += 1
}
}
closeInds(polyInds)
val newPolys = new Array[ExtPoly](polys.size)
val it = polys.entrySet.iterator
while (it.hasNext) {
val e = it.next
val oldP = e.getKey
val i = e.getValue
if (oldP eq BitMapMinAreaChecker.hugePoly) {
assert(i == 0 && polyInds(i) == 0)
newPolys(0) = BitMapMinAreaChecker.hugePoly
} else if (polyInds(i) == i) {
val newP = new ExtPoly
newP.area = oldP.area
newP.x = oldP.x
newP.y = oldP.y
newPolys(i) = newP
} else {
val p = newPolys(polyInds(i))
if (!(p eq BitMapMinAreaChecker.hugePoly)) p.union(oldP)
}
}
assert(newPolys(0) eq BitMapMinAreaChecker.hugePoly)
for (i <- 1 until newPolys.size) {
val p = newPolys(i)
assert(!(p eq BitMapMinAreaChecker.hugePoly))
if (p != null) {
if (p.area >= minArea) {
newPolys(i) = BitMapMinAreaChecker.hugePoly
} else if (i >= innerPolys) {
errorLogger.reportMinAreaViolation(p.area, p.x, p.y)
}
}
}
for (i <- 0 until rmin.size) {
val oldS = lmin(i)
val newP = newPolys(polyInds(polys.get(oldS.getP)))
rmin(i) = new ExtSegment(oldS.getYmin, oldS.getYmax, newP)
}
for (i <- 0 until rmax.size) {
val oldS = umax(i)
val newP = newPolys(polyInds(polys.get(oldS.getP)))
rmax(i) = new ExtSegment(oldS.getYmin, oldS.getYmax, newP)
}
new StripeExts(l.getXmin, u.getXmax, rmin, rmax, l.getTotalArea + u.getTotalArea)
}
def addP(map: LinkedHashMap[ExtPoly,Int], p: ExtPoly) = {
if (!map.containsKey(p)) map.put(p, map.size)
}
def initInds(size: Int): Array[Int] = {
val inds = new Array[Int](size)
for (i <- 0 until size) inds(i) = i
inds
}
def connectInds(inds: Array[Int], i1: Int, i2: Int) = {
var m1 = i1
while (inds(m1) != m1) m1 = inds(m1)
var m2 = i2
while (inds(m2) != m2) m2 = inds(m2)
val m = if (m1 < m2) m1 else m2
m1 = i1
while (inds(m1) != m1) {
val k = m1
m1 = inds(m1)
inds(m1) = m
}
inds(m1) = m
m2 = i2
while (inds(m2) != m2) {
val k = m2
m2 = inds(m2)
inds(m2) = m
}
inds(m2) = m
}
def closeInds(inds: Array[Int]) = {
for (i <- 0 until inds.size) inds(i) = inds(inds(i))
}
}
class CheckerTask(topCell: LayoutCell, minArea: Long, errorLogger: MinAreaChecker.ErrorLogger) {
def getTopCell = topCell
def getMinArea = minArea
def reportMinAreaViolation(area: Long, x: Int, y: Int) = {
if (area < minArea) errorLogger.reportMinAreaViolation(area, x, y)
}
def check(recommendedNumStripes: Int) = {
// serial preparations
val dx = topCell.getBoundingMaxX - topCell.getBoundingMinX
val numStripes = min(recommendedNumStripes, dx)
val x0 = topCell.getBoundingMinX
val x = new Array[Int](numStripes + 1)
val dxl: Long = dx
for (i <- 0 until x.size) {
x(i) = x0 + (dxl*i/numStripes).asInstanceOf[Int]
}
if (BitMapMinAreaChecker.DEBUG >= 1) {
print("StripesX:")
for (i <- 0 until x.size) print(" "+x(i))
println
}
val exts = new Array[Function0[StripeExts]](numStripes)
for (i <- 0 until numStripes) {
val stripeChecker = new StripeChecker(this, x(i), x(i + 1))
if (BitMapMinAreaChecker.PARALLEL) {
exts(i) = future { stripeChecker() }
} else {
exts(i) = stripeChecker
}
}
// serial reduce
var result = BitMapMinAreaChecker.emptyStripeExts(topCell.getBoundingMinX)
for (i <- 0 until exts.size) {
result = BitMapMinAreaChecker.connectStripes(result, exts(i)(), minArea, errorLogger)
if (BitMapMinAreaChecker.DEBUG >= 3) result.printIt
}
result = BitMapMinAreaChecker.connectStripes(result, BitMapMinAreaChecker.emptyStripeExts(topCell.getBoundingMaxX), minArea, errorLogger)
assert(result.getMinSegs.isEmpty && result.getMaxSegs.isEmpty)
if (BitMapMinAreaChecker.DEBUG >= 1) println("Total Area "+result.getTotalArea)
}
}
/**
* @param topCell top cell of the layout
* @param minArea minimal area of valid polygon
* @param stripeMinX x-coordinate of left stripe boundary
* @param stripeMaxX x-coordinate of right stripe boundary
* @param errorLogger an API to report violations
*/
class StripeChecker(task: CheckerTask, stripeMinX: Int, stripeMaxX: Int) extends Function0[StripeExts] {
// traverse flattened rectangles
private def flattenRects(top: LayoutCell, proc: (Int, Int, Int, Int) => Unit) = {
def flatten(t: LayoutCell, x: Int, y: Int, orient: ManhattanOrientation): Unit = {
val a = new Array[Int](4)
t.traverseRectangles(new LayoutCell.RectangleHandler {
def apply(minX: Int, minY: Int, maxX: Int, maxY: Int) = {
a(0) = minX
a(1) = minY
a(2) = maxX
a(3) = maxY
orient.transformRects(a, 0, 1)
val tminX = max(a(0) + x, stripeMinX)
val tminY = a(1) + y
val tmaxX = min(a(2) + x, stripeMaxX)
val tmaxY = a(3) + y
if (tminX < tmaxX) proc(tminX, tminY, tmaxX, tmaxY)
}
})
t.traverseSubcellInstances(new LayoutCell.SubcellHandler {
def apply(subCell: LayoutCell, anchorX: Int, anchorY: Int, subOrient: ManhattanOrientation) = {
a(0) = anchorX
a(1) = anchorY
orient.transformPoints(a, 0, 1)
val newAnchorX = a(0) + x
val newanchorY = a(1) + y
val newOrient = orient.concatenate(subOrient)
a(0) = subCell.getBoundingMinX
a(1) = subCell.getBoundingMinY
a(2) = subCell.getBoundingMaxX
a(3) = subCell.getBoundingMaxY
newOrient.transformRects(a, 0, 1)
if (a(0) + newAnchorX < stripeMaxX && a(2) + newAnchorX > stripeMinX) {
flatten(subCell, newAnchorX, newanchorY, newOrient)
}
}
})
}
flatten(top, 0, 0, ManhattanOrientation.R0)
}
override def apply(): StripeExts = {
// find unique coordinates
var xcoords = new TreeSet[Int]()
var ycoords = new TreeSet[Int]()
flattenRects(task.getTopCell, (minX: Int, minY: Int, maxX: Int, maxY: Int) => {
if (BitMapMinAreaChecker.DEBUG >= 4)
println(" flat ["+minX+".."+maxX+"]x["+minY+".."+maxY+"]")
xcoords = xcoords + minX + maxX
ycoords = ycoords + minY + maxY
})
if (xcoords.isEmpty) {
return new StripeExts(stripeMinX, stripeMaxX, new Array[ExtSegment](0), new Array[ExtSegment](0), 0)
}
val xsize = xcoords.size - 1
val ysize = ycoords.size - 1
// xa,ya maps coordinate index to coordinate value
// xm,ym maps coordinate value to coordinate index
val xa = new Array[Int](xsize + 1)
val ya = new Array[Int](ysize + 1)
val xm = new HashMap[Int,Int]()
val ym = new HashMap[Int,Int]()
for (x <- xcoords) {
xa(xm.size) = x
xm.put(x, xm.size)
}
for (y <- ycoords) {
ya(ym.size) = y
ym.put(y, ym.size)
}
// fill bit map
val bitMap = new Array[BitSet](xsize)
for (i <- 0 until bitMap.length) bitMap(i) = new BitSet
flattenRects(task.getTopCell, (minX: Int, minY: Int, maxX: Int, maxY: Int) => {
val ymin = ym(minY)
val ymax = ym(maxY)
for (x <- xm(minX) until xm(maxX)) bitMap(x).set(ymin, ymax)
})
if (BitMapMinAreaChecker.DEBUG >= 4) {
println("xcoords="+xcoords)
println("ycoords="+ycoords)
printBitMap(bitMap, xsize, ysize)
}
val stripeMin = new Array[ExtPoly](ysize)
val stripeMax = new Array[ExtPoly](ysize)
// stack of tiles to fill polygon
var stack = new Stack[Point]
var polyArea: Long = 0
def pushTile(x: Int, y: Int) = {
val w: Long = xa(x + 1) - xa(x)
val h: Long = ya(y + 1) - ya(y)
polyArea += w*h
bitMap(x).clear(y)
stack.push(new Point(x,y))
}
// find polygons in reverse lexicographical order
var totalArea = 0L
var x = xsize - 1
while (x >= 0) {
var y = ysize - 1
while (y >= 0) {
if (bitMap(x).get(y)) {
polyArea = 0
// find polygon area and erase polygon from bit map
var ext: ExtPoly = null
pushTile(x, y)
while (!stack.isEmpty) {
val p = stack.top
if (p.getX == 0 && stripeMin(p.getY) == null) {
if (ext == null) ext = new ExtPoly
stripeMin(p.getY) = ext
}
if (p.getX + 1 == xsize && stripeMax(p.getY) == null) {
if (ext == null) ext = new ExtPoly
stripeMax(p.getY) = ext
}
if (p.getX - 1 >= 0 && bitMap(p.getX - 1).get(p.getY))
pushTile(p.getX - 1, p.getY)
else if (p.getX + 1 < xsize && bitMap(p.getX + 1).get(p.getY))
pushTile(p.getX + 1, p.getY)
else if (p.getY - 1 >= 0 && bitMap(p.getX).get(p.getY - 1))
pushTile(p.getX, p.getY - 1)
else if (p.getY + 1 < ysize && bitMap(p.getX).get(p.getY + 1))
pushTile(p.getX, p.getY + 1)
else
stack.pop
}
totalArea += polyArea
if (ext != null) {
ext.x = xa(x + 1)
ext.y = ya(y + 1)
ext.area = polyArea
} else {
task.reportMinAreaViolation(polyArea, xa(x + 1), ya(y + 1))
}
}
y -= 1
}
x -= 1
}
val minSegments = collectSegments(stripeMin, ya)
val maxSegments = collectSegments(stripeMax, ya)
val stripeExts = new StripeExts(stripeMinX, stripeMaxX, minSegments, maxSegments, totalArea)
if (BitMapMinAreaChecker.DEBUG >= 3) stripeExts.printIt
if (BitMapMinAreaChecker.DEBUG >= 2) println("Total Area "+totalArea)
stripeExts
}
def collectSegments(stripeExts: Array[ExtPoly], ya: Array[Int]): Array[ExtSegment] = {
val ysize = stripeExts.length
val buf = new ArrayBuffer[ExtSegment]
var curP: ExtPoly = null
var ymin = 0
for (y <- 0 until ysize) {
val p = stripeExts(y)
if (p != null) {
if (curP != null) {
assert(curP == p)
} else {
curP = p
ymin = ya(y)
}
} else if (curP != null) {
val ymax = ya(y)
buf += new ExtSegment(ymin, ymax, if (curP.area < task.getMinArea) curP else BitMapMinAreaChecker.hugePoly)
curP = null
}
}
if (curP != null) {
val ymax = ya(ysize)
buf += new ExtSegment(ymin, ymax, if (curP.area < task.getMinArea) curP else BitMapMinAreaChecker.hugePoly)
}
buf.toArray
}
def printBitMap(bitMap: Array[BitSet], xsize: Int, ysize: Int) = {
var y = ysize - 1
while (y >= 0) {
for (x <- 0 until xsize) print(if (bitMap(x).get(y)) 'X' else ' ')
println
y -= 1
}
}
}
class StripeExts(xmin: Int, xmax: Int, minSegs: Array[ExtSegment], maxSegs: Array[ExtSegment], totalArea: Long) {
assert(xmin <= xmax)
def getXmin = xmin
def getXmax = xmax
def getMinSegs = minSegs
def getMaxSegs = maxSegs
def getTotalArea = totalArea
def printIt = {
println("Lower x="+xmin+":")
for (s <- minSegs) s.printIt
println("Upper x="+xmax+":")
for (s <- maxSegs) s.printIt
}
}
class ExtSegment(ymin: Int, ymax: Int, p: ExtPoly) {
def getYmin = ymin
def getYmax = ymax
def getP = p
def printIt = {
print("y=["+ymin+","+ymax+"] ")
if (p eq BitMapMinAreaChecker.hugePoly) print("HUGE") else print("area="+p.area+" top=("+p.x+","+p.y+")")
println
}
}
class ExtPoly {
var area = 0L
var x = 0
var y = 0
def union(p: ExtPoly) = {
area += p.area
if (p.x > x || p.x == x && p.y > y) {
x = p.x
y = p.y
}
}
}