/* XXL: The eXtensible and fleXible Library for data processing
Copyright (C) 2000-2013 Prof. Dr. Bernhard Seeger
Head of the Database Research Group
Department of Mathematics and Computer Science
University of Marburg
Germany
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; If not, see <http://www.gnu.org/licenses/>.
http://code.google.com/p/xxl/
*/
package xxl.core.spatial.histograms.utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Arrays;
import xxl.core.cursors.Cursor;
import xxl.core.functions.Functional.UnaryFunction;
import xxl.core.indexStructures.rtrees.GenericPartitioner;
import xxl.core.indexStructures.rtrees.GenericPartitioner.CostFunctionArrayProcessor;
import xxl.core.spatial.SpaceFillingCurves;
import xxl.core.spatial.points.DoublePoint;
import xxl.core.spatial.rectangles.DoublePointRectangle;
/**
*
* This class contains cost function for spatial histogram computation
*
* @see MHistograms
* @see GenericPartitioner
*
*/
public class PartitionerUtils {
public static enum ProcessorType{
VOLUME,
GRID_SSE,
RK_HIST,
}
public static UnaryFunction<double[], int[]> getSFCFunction(final int sfcPrecision){
return new UnaryFunction<double[], int[]>() {
@Override
public int[] invoke(double[] from) {
int[] to = new int[from.length];
for (int i = 0; i < to.length; i++) {
to[i] = (int) (from[i] * sfcPrecision);
}
return to;
}
};
}
public static Map<Long, Integer> computeGrid(
Cursor<DoublePointRectangle> rectangles, int bitsPerDim,
int dimensions) {
Map<Long, Integer> grid = new HashMap<Long, Integer>();
UnaryFunction<double[], int[]> convertToFillingCurveValues = getSFCFunction( (1 << (bitsPerDim-1))) ;
for (; rectangles.hasNext();) {
DoublePointRectangle actRectangle = rectangles.next();
double[] lowleft = (double[]) actRectangle.getCorner(false)
.getPoint();
double[] upright = (double[]) actRectangle.getCorner(true)
.getPoint();
int[] intLowLeft = convertToFillingCurveValues.invoke(lowleft);
int[] intUpRight = convertToFillingCurveValues.invoke(upright);
try {
List<long[]> zcodesList = SpaceFillingCurves.computeZBoxRanges(
intLowLeft, intUpRight, bitsPerDim, dimensions);
for (long[] zcodes : zcodesList) {
for (long zcode = zcodes[0]; zcode <= zcodes[1]; zcode++) {
Integer value = 1;
if (grid.containsKey(zcode)) {
value = grid.get(zcode);
value++;
}
grid.put(zcode, value);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
return grid;
}
public static class SpatialSkewProcessor implements CostFunctionArrayProcessor<DoublePointRectangle>{
protected double[][] arrayCosts;
protected boolean useStorage;
protected int B;
protected int n;
protected int bitsPerDim;
protected Map<Long, Integer> grid;
protected int dimension;
protected UnaryFunction<double[], int[]> getSfcKey;
public SpatialSkewProcessor(Cursor<DoublePointRectangle> gridConsIterator,
int precision, int dimension, int B, int storageSlots, boolean useStorage) {
super();
if (useStorage){
arrayCosts = new double[storageSlots][B];
Arrays.fill(arrayCosts, null);
}
this.B = B;
this.n = storageSlots;
this.useStorage = useStorage;
this.bitsPerDim= precision;
this.dimension = dimension;
this.getSfcKey = getSFCFunction((1 << (bitsPerDim-1)));
this.grid = computeGrid(gridConsIterator, precision, dimension);
}
@Override
public double[] processList(
DoublePointRectangle[] rectangles,
int b,
int B,
int startIndex) {
if (arrayCosts[startIndex] == null ){
double[] array = new double[B];
DoublePointRectangle universe = null;
for(int i = 0, j = startIndex; j >=0 && i < B ; i++, j--){
if(universe == null)
universe = new DoublePointRectangle(rectangles[j]);
else
universe.union(rectangles[j]);
if (i >=b-1 ){
array[i] = computeSkew (universe);
}
}
arrayCosts[startIndex] = array;
return array;
}
return arrayCosts[startIndex];
}
public void reset(){
if (useStorage){
arrayCosts = new double[n][B];
Arrays.fill(arrayCosts, null);
}
}
protected double computeSkew(DoublePointRectangle rectangle ){
//
double skew = 0;
double average = 0;
double cells = 0;
// compute
DoublePointRectangle actRectangle = rectangle;
double[] lowleft = (double[]) actRectangle.getCorner(false)
.getPoint();
double[] upright = (double[]) actRectangle.getCorner(true)
.getPoint();
int[] intLowLeft = getSfcKey.invoke(lowleft);
int[] intUpRight = getSfcKey.invoke(upright);
List<long[]> zcodesList = SpaceFillingCurves.computeZBoxRanges(
intLowLeft, intUpRight, bitsPerDim, dimension);
for(long[] zcodes : zcodesList){
cells +=( zcodes[1] - zcodes[0] + 1);
}
double sum = 0;
for (long[] zcodes : zcodesList) {
for (long zcode = zcodes[0]; zcode <= zcodes[1]; zcode++) {
if (grid.containsKey(zcode)) {
double value = grid.get(zcode);
sum +=value;
}
}
}
average = sum / cells; // x^
for (long[] zcodes : zcodesList) {
for (long zcode = zcodes[0]; zcode <= zcodes[1]; zcode++) {
if (grid.containsKey(zcode)) {
double value = grid.get(zcode);
skew += Math.pow((value - average), 2);
}
}
}
return skew;
}
public double[] processInitialList( DoublePointRectangle[] rectangles, int b, int B){
double[] array = new double[B];
DoublePointRectangle universe = null;
for(int i = 0;i < B ; i++ ){
if(universe == null)
universe = new DoublePointRectangle(rectangles[i]);
else
universe.union(rectangles[i]);
array[i] = computeSkew(universe);
}
return array;
}
@Override
public double[][] precomputeAllCosts(DoublePointRectangle[] rectangles)
throws UnsupportedOperationException {
double[][] costs = new double[rectangles.length][rectangles.length];
for(int i = 0; i < rectangles.length; i++){
DoublePointRectangle rec = null;
for(int j = i ; j <rectangles.length; j++){
if(rec == null){
rec = new DoublePointRectangle(rectangles[j]);
}
else{
rec.union(rectangles[j]);
}
costs[i][j] = computeSkew(rec);
}
}
return costs;
}
}
/**
*
* @author RK-Hist Partitioner
*
*/
public static class RKHistMetrikProcessor implements CostFunctionArrayProcessor<DoublePointRectangle> {
double[][] arrayCosts;
boolean useStorage;
int B;
int n;
public RKHistMetrikProcessor (int B, int storageSlots, boolean useStorage) {
super();
if (useStorage){
arrayCosts = new double[storageSlots][B];
Arrays.fill(arrayCosts, null);
}
this.B = B;
this.n = storageSlots;
this.useStorage = useStorage;
}
@Override
public double[] processList(
DoublePointRectangle[] rectangles,
int b,
int B,
int startIndex) {
if (arrayCosts[startIndex] == null ){
double[] array = new double[B];
DoublePointRectangle universe = null;
List<DoublePoint> asList = new LinkedList<DoublePoint>();
for(int i = 0, j = startIndex; j >=0 && i < B ; i++, j--){
asList.add(rectangles[j].getCenter());
if(universe == null)
universe = new DoublePointRectangle(rectangles[j]);
else
universe.union(rectangles[j]);
if (i >=b-1 ){
array[i] = computeRKMetric(asList, universe);
}
}
arrayCosts[startIndex] = array;
return array;
}
return arrayCosts[startIndex];
}
public void reset(){
if (useStorage){
arrayCosts = new double[n][B];
Arrays.fill(arrayCosts, null);
}
}
protected double computeRKMetric(List<DoublePoint> recs, DoublePointRectangle uni){
return RKhist.kMetricCost(recs, uni);
}
public double[] processInitialList( DoublePointRectangle[] rectangles, int b, int B){
double[] array = new double[B];
DoublePointRectangle universe = null;
List<DoublePoint> asList = new LinkedList<DoublePoint>();
for(int i = 0;i < B ; i++ ){
asList.add(rectangles[i].getCenter());
if(universe == null)
universe = new DoublePointRectangle(rectangles[i]);
else
universe.union(rectangles[i]);
array[i] = computeRKMetric(asList, universe);;
}
return array;
}
@Override
public double[][] precomputeAllCosts(DoublePointRectangle[] rectangles)
throws UnsupportedOperationException {
return null;
}
}
/**
*
* one dim SSE processor see Thorsten Suel et. Al.
*
* computes the sfc value of a multidim object
* and then process the list of scfs with SSE Metric
*
*/
public static class SSEArrayProcessor implements CostFunctionArrayProcessor<DoublePointRectangle> {
private final UnaryFunction<DoublePointRectangle, Long> sfcFunction;
public SSEArrayProcessor(
UnaryFunction<DoublePointRectangle, Long> sfcFunction) {
super();
this.sfcFunction = sfcFunction;
}
@Override
public double[] processList(
DoublePointRectangle[] rectangles,
int b,
int B,
int startIndex) {
double[] array = new double[B];
double count = 0;
double sum = 0;
double qSum = 0;
double avg = 0;
double sse = 0;
for(int i = 0, j = startIndex; j >=0 && i < B ; i++, j-- ){
double key = sfcFunction.invoke(rectangles[j]);
qSum += key * key;
sum +=key;
count++;
avg = count/sum;
sse = qSum -(count)*avg*avg;
array[i] = sse;
}
return array;
}
@Override
public double[] processInitialList(DoublePointRectangle[] rectangles,
int b, int B) {
double[] array = new double[B];
double count = 0;
double sum = 0;
double qSum = 0;
double avg = 0;
double sse = 0;
for(int i = 0; i < B ; i++){
double key = sfcFunction.invoke(rectangles[i]);
qSum += key * key;
sum +=key;
count++;
avg = count/sum;
sse = qSum -(count)*avg*avg;
array[i] = sse;
}
return array;
}
@Override
public double[][] precomputeAllCosts(DoublePointRectangle[] rectangles)
throws UnsupportedOperationException {
double[][] costs = new double[rectangles.length][rectangles.length];
for(int i = 0; i < rectangles.length; i++){
double count = 0;
double sum = 0;
double qSum = 0;
double avg = 0;
double sse = 0;
for(int j = i ; j <rectangles.length; j++){
double key = sfcFunction.invoke(rectangles[j]);
qSum += key * key;
sum +=key;
count++;
avg = count/sum;
sse = qSum -(count)*avg*avg;
costs[i][j] = sse;
}
}
return costs;
}
@Override
public void reset() {
// TODO Auto-generated method stub
}
}
/**
*
* Default generic OPT list processor. Parameterized with a UnarayFunction DoublePointRectangle Double
*
*/
public static class DeafultWeightedArrayProcessor implements CostFunctionArrayProcessor<SpatialHistogramBucket>{
final UnaryFunction< DoublePointRectangle, Double> costFunction;
public DeafultWeightedArrayProcessor(
UnaryFunction<DoublePointRectangle, Double> costFunction) {
super();
this.costFunction = costFunction;
}
@Override
public double[] processList(
SpatialHistogramBucket[] rectangles,
int minWeight,
int maxWeight,
int startIndex
) {
List<Double> list = new ArrayList<Double>(100);
DoublePointRectangle universe = null;
int weight = 0;
for(int j = startIndex; j >=0 && weight < maxWeight ; j-- ){
weight += rectangles[j].getWeight();
if(universe == null)
universe = new DoublePointRectangle(rectangles[j]);
else
universe.union(rectangles[j]);
if( weight >= minWeight)
list.add( costFunction.invoke(universe));
else list.add(0d);
}
double[] array = new double[list.size()];
for(int i = 0 ; i < array.length; i++){
array[i] = list.get(i);
}
return array;
}
@Override
public double[] processInitialList(
SpatialHistogramBucket[] rectangles, int b, int B) {
List<Double> list = new ArrayList<Double>(100);
DoublePointRectangle universe = null;
int weight = 0;
for(int i = 0; i < rectangles.length && weight <= B ; i++ ){
weight += rectangles[i].getWeight();
if(universe == null)
universe = new DoublePointRectangle(rectangles[i]);
else
universe.union(rectangles[i]);
if( weight >= b)
list.add( costFunction.invoke(universe));
else list.add(0d);
}
double[] array = new double[list.size()];
for(int i = 0 ; i < array.length; i++){
array[i] = list.get(i);
}
return array;
}
@Override
public double[][] precomputeAllCosts(
SpatialHistogramBucket[] rectangles)
throws UnsupportedOperationException {
// TODO Auto-generated method stub
return null;
}
@Override
public void reset() {
// TODO Auto-generated method stub
}
}
/**
*
* one dim SSE processor see Thorsten Suel et. Al.
*
* computes the sfc value of a multidim object
* and then process the list of scfs with SSE Metric
*
*/
public static class SSEWeightedArrayProcessor implements CostFunctionArrayProcessor<SpatialHistogramBucket> {
private final UnaryFunction<DoublePointRectangle, Long> sfcFunction;
public SSEWeightedArrayProcessor(
UnaryFunction<DoublePointRectangle, Long> sfcFunction) {
super();
this.sfcFunction = sfcFunction;
}
@Override
public double[] processList(
SpatialHistogramBucket[] rectangles,
int minWeight,
int maxWeight,
int startIndex
) {
List<Double> list = new ArrayList<Double>(100);
int weight = 0;
double count = 0;
double sum = 0;
double qSum = 0;
double avg = 0;
double sse = 0;
for(int i = 0, j = startIndex; j >=0 && weight <= maxWeight ; i++, j-- ){
double key = sfcFunction.invoke(rectangles[j]);
qSum += key * key;
sum +=key;
count++;
avg = count/sum;
sse = qSum -(count)*avg*avg;
if( weight >= minWeight)
list.add( sse);
else list.add(0d);
}
double[] array = new double[list.size()];
for(int i = 0 ; i < array.length; i++){
array[i] = list.get(i);
}
return array;
}
@Override
public double[] processInitialList(
SpatialHistogramBucket[] rectangles, int b, int B) {
// TODO Auto-generated method stub
return null;
}
@Override
public double[][] precomputeAllCosts(
SpatialHistogramBucket[] rectangles)
throws UnsupportedOperationException {
// TODO Auto-generated method stub
return null;
}
@Override
public void reset() {
// TODO Auto-generated method stub
}
}
}