All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy