/* XXL: The eXtensible and fleXible Library for data processing
Copyright (C) 2000-2011 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.math.functions;
import java.util.Arrays;
import java.util.List;
import xxl.core.functions.AbstractFunction;
import xxl.core.functions.Function;
/**
* This class provides an adaptive online aggregation strategy by
* performing a convex merging of two objects. This is typically
* realized with a
* {@link xxl.core.math.functions.LinearCombination convex-linear combination}
* of the last returned and every new
* function delivered by the wrapped aggregation function, i.e. in each step
* functions are provided and merged.
* But the user can also realize his own merge procedure based on the current objects
* and weights.
* <BR>
* For the weighting of the functions different strategies exist that allow a time-dependent emphasis
* of the functions. The strategies in turn are realized with
* {@link xxl.core.math.functions.AdaptiveWeightFunctions adaptive weight functions}.
* <BR>
* This function has two phases during runtime (exemplified with functions):<BR>
* 1) Initialize: As far as the internal used (wrapped) aggregation function
* returns null-objects, the adaptive aggregation function will do this as well.
* The first non-null object returned by the wrapped function will be stored internally
* and passed directly to the consumer, i.e., it will be directly returned.
* 2) Working: For every NEW function returned by the wrapped function, a counter
* is incremented, the corresponding weight is computed and a linear
* combination of the old function and the new return function
* is returned. The code for determining a NEW function looks like <BR>
* <PRE>
* <CODE>
if( lastF == newF){
return lastF;
}
else{
// COMPUTE new function
}
* </CODE>
* </PRE>
*
* Generally, each aggregation function must support a function call of the following type:<br>
* <tt>agg_n = f (agg_n-1, next)</tt>. <br>
* There, <tt>agg_n</tt> denotes the computed aggregation value after <tt>n</tt> steps,
* <tt>f</tt> represents the aggregation function,
* <tt>agg_n-1</tt> the computed aggregation value after <tt>n-1</tt> steps
* and <tt>next</tt> the next object to use for computation.
* An aggregation function delivers <tt>null</tt> as aggregation result as long as the aggregation
* function has not yet been fully initialized.
* <br>
*
* For a more detailed discussion concerning the idea of online aggregation
* we refer to [HHW97]:P. Haas, J. Hellerstein, H. Wang, Online Aggregation, 1997.
*
* @see xxl.core.math.functions.RealFunction
* @see xxl.core.math.functions.LinearCombination
* @see xxl.core.math.functions.AdaptiveWeightFunctions
* @see xxl.core.functions.Function
*/
public class AdaptiveAggregationFunction extends AggregationFunction<Object,Object> {
/** internally used (wrapped) aggregation function providing every k-th step a
* NEW estimator
*/
protected AggregationFunction function;
/** RealFunction that consumes the current step and returns the corresponding
* weight
*/
protected RealFunction weights;
/** current step, i.e., current number of already combined estimators */
protected int step;
/** internally stored aggregation value of the previous aggregation step performed
* by the function
*/
private Object internalOld;
/** Indicates whether the adaptive estimator is in 'RealFunction' mode, i.e.,
* the functions returned from the wrapped aggregation function will be treated as
* objects of type {@link xxl.core.math.functions.RealFunction}. Otherwise objects of type
* {@link xxl.core.functions.Function Function}
* consuming objects of type <TT>Number</TT> will be assumed.
*/
protected boolean realMode;
/**
* Internal factory for the convex-linear merge of two objects.
* Typically, this is realized by building a convex linear combination of two
* RealFunctions.
*/
protected Function<Object,? extends Object> convexMergeFunction;
/** Constructs a new object of this type. The estimator is assumed not to be in the real mode.
*
* @param function wrapped aggregation function
* @param weights delivering weights for every adaptive step
* @param convexMergeFunction internal factory for the convex-linear merge of two objects
*/
public AdaptiveAggregationFunction(AggregationFunction function, RealFunction weights, Function<Object,? extends Object> convexMergeFunction) {
this.function = function;
this.weights = weights;
this.convexMergeFunction = convexMergeFunction;
step = 0;
internalOld = null;
}
/** Constructs a new object of this type.
*
* @param function wrapped aggregation function
* @param weights delivering weights for every adaptive step
* @param realMode indicates whether the estimator is in real mode,i.e.,
* the wrapped aggregations functions will return objects of
* type {@link RealFunction}
* @param convexMergeFunction internal factory for the convex merge of two objects
*/
public AdaptiveAggregationFunction(
AggregationFunction function,
RealFunction weights,
boolean realMode,
Function<Object,? extends Object> convexMergeFunction) {
this.function = function;
this.weights = weights;
this.convexMergeFunction = convexMergeFunction;
step = 0;
internalOld = null;
this.realMode = realMode;
}
/** Constructs a new object of this type. Initially, convex linear combinations of two Functions
* respectively RealFunctions are built.
*
* @param function wrapped aggregation function
* @param weights delivering weights for every adaptive step
* @param realMode indicates whether the estimator is in real mode,i.e.,
* the wrapped aggregations functions will return objects of
* type {@link RealFunction}
*/
public AdaptiveAggregationFunction(AggregationFunction function, RealFunction weights, final boolean realMode) {
this(function, weights, false, new AbstractFunction<Object,LinearCombination>() {
public LinearCombination invoke(List<? extends Object> list) {
if (realMode) {
return new LinearCombination(
(RealFunction) list.get(0),
((Double) list.get(1)).doubleValue(),
(RealFunction) list.get(2),
((Double) list.get(3)).doubleValue());
}
return new LinearCombination(
(Function) list.get(0),
((Double) list.get(1)).doubleValue(),
(Function) list.get(2),
((Double) list.get(3)).doubleValue());
}
});
}
/** Constructs a new object of this type. The estimator is assumed not to be in the real mode.
* Initially, convex linear combinations of two functions
* are built.
*
* @param function wrapped aggregation function
* @param weights delivering weights for every adaptive step
*/
public AdaptiveAggregationFunction(AggregationFunction function, RealFunction weights) {
this(function, weights, false, new AbstractFunction<Object,LinearCombination>() {
public LinearCombination invoke(List<? extends Object> list) {
return new LinearCombination(
(Function) list.get(0),
((Double) list.get(1)).doubleValue(),
(Function) list.get(2),
((Double) list.get(3)).doubleValue());
}
});
}
/** Constructs a new object of this type using an arithmetic weighting.
* The estimator is assumed not to be in the real mode. Initially, convex linear combinations of two functions
* are built.
*
* @param function wrapped aggregation function
*/
public AdaptiveAggregationFunction(AggregationFunction function) {
this(function, new AdaptiveWeightFunctions.ArithmeticWeights(), false, new AbstractFunction<Object,LinearCombination>() {
public LinearCombination invoke(List<? extends Object> list) {
return new LinearCombination(
(Function) list.get(0),
((Double) list.get(1)).doubleValue(),
(Function) list.get(2),
((Double) list.get(3)).doubleValue());
}
});
}
/** Two-figured function call for supporting aggregation by this function.
* Each aggregation function must support a function call like <tt>agg_n = f (agg_n-1, next)</tt>.
* There, <tt>agg_n</tt> denotes the computed aggregation value after <tt>n</tt> steps, <tt>f</tt>
* the aggregation function, <tt>agg_n-1</tt> the computed aggregation value after <tt>n-1</tt> steps
* and <tt>next</tt> the next object to use for computation.
* This method delivers only <tt>null</tt> as aggregation result as long as the aggregation
* has not yet been initialized.
*
* @param old result of the aggregation function in the previous computation step
* @param next next object used for computation
* @return aggregation value after n steps
*/
public Object invoke(Object old, Object next) {
Object na = function.invoke(internalOld, next);
if (na == null)
return null;
// The first one?
if (old == null) {
internalOld = na;
step = 1;
return na;
}
else { // no, not the first yet
// new result of wrapped aggregation?
if (internalOld == na) { // no
return old; // return "old" result
}
else { // yes, new internal estimator
internalOld = na;
step++;
double w = weights.eval(step);
return convexMergeFunction.invoke(Arrays.asList(old, new Double((1.0 - w)), na, new Double(w)));
}
}
}
}