You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@systemml.apache.org by lr...@apache.org on 2015/12/03 19:46:15 UTC

[44/78] [abbrv] [partial] incubator-systemml git commit: Move files to new package folder structure

http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/276d9257/src/main/java/com/ibm/bi/dml/hops/AggUnaryOp.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/ibm/bi/dml/hops/AggUnaryOp.java b/src/main/java/com/ibm/bi/dml/hops/AggUnaryOp.java
deleted file mode 100644
index a0be584..0000000
--- a/src/main/java/com/ibm/bi/dml/hops/AggUnaryOp.java
+++ /dev/null
@@ -1,735 +0,0 @@
-/**
- * (C) Copyright IBM Corp. 2010, 2015
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * 
- */
-
-package com.ibm.bi.dml.hops;
-
-import com.ibm.bi.dml.hops.AggBinaryOp.SparkAggType;
-import com.ibm.bi.dml.hops.Hop.MultiThreadedHop;
-import com.ibm.bi.dml.hops.rewrite.HopRewriteUtils;
-import com.ibm.bi.dml.lops.Aggregate;
-import com.ibm.bi.dml.lops.Aggregate.OperationTypes;
-import com.ibm.bi.dml.lops.Binary;
-import com.ibm.bi.dml.lops.Group;
-import com.ibm.bi.dml.lops.Lop;
-import com.ibm.bi.dml.lops.LopsException;
-import com.ibm.bi.dml.lops.PartialAggregate;
-import com.ibm.bi.dml.lops.PartialAggregate.DirectionTypes;
-import com.ibm.bi.dml.lops.TernaryAggregate;
-import com.ibm.bi.dml.lops.UAggOuterChain;
-import com.ibm.bi.dml.lops.UnaryCP;
-import com.ibm.bi.dml.lops.LopProperties.ExecType;
-import com.ibm.bi.dml.parser.Expression.DataType;
-import com.ibm.bi.dml.parser.Expression.ValueType;
-import com.ibm.bi.dml.runtime.controlprogram.context.SparkExecutionContext;
-import com.ibm.bi.dml.runtime.matrix.MatrixCharacteristics;
-
-
-/* Aggregate unary (cell) operation: Sum (aij), col_sum, row_sum
- * 		Properties: 
- * 			Symbol: +, min, max, ...
- * 			1 Operand
- * 	
- * 		Semantic: generate indices, align, aggregate
- */
-
-public class AggUnaryOp extends Hop implements MultiThreadedHop
-{
-	
-	private static final boolean ALLOW_UNARYAGG_WO_FINAL_AGG = true;
-	
-	private AggOp _op;
-	private Direction _direction;
-
-	private int _maxNumThreads = -1; //-1 for unlimited
-	
-	private AggUnaryOp() {
-		//default constructor for clone
-	}
-	
-	public AggUnaryOp(String l, DataType dt, ValueType vt, AggOp o, Direction idx, Hop inp) 
-	{
-		super(l, dt, vt);
-		_op = o;
-		_direction = idx;
-		getInput().add(0, inp);
-		inp.getParent().add(this);
-	}
-	
-	public AggOp getOp()
-	{
-		return _op;
-	}
-	
-	public void setOp(AggOp op)
-	{
-		_op = op;
-	}
-	
-	public Direction getDirection()
-	{
-		return _direction;
-	}
-	
-	public void setDirection(Direction direction)
-	{
-		_direction = direction;
-	}
-
-	@Override
-	public void setMaxNumThreads( int k ) {
-		_maxNumThreads = k;
-	}
-	
-	@Override
-	public int getMaxNumThreads() {
-		return _maxNumThreads;
-	}
-	
-	@Override
-	public Lop constructLops()
-		throws HopsException, LopsException 
-	{	
-		//return already created lops
-		if( getLops() != null )
-			return getLops();
-
-		try 
-		{
-			ExecType et = optFindExecType();
-			Hop input = getInput().get(0);
-			
-			if ( et == ExecType.CP ) 
-			{
-				Lop agg1 = null;
-				if( isTernaryAggregateRewriteApplicable() ) {
-					agg1 = constructLopsTernaryAggregateRewrite(et);
-				}
-				else if( isUnaryAggregateOuterCPRewriteApplicable() )
-				{
-					OperationTypes op = HopsAgg2Lops.get(_op);
-					DirectionTypes dir = HopsDirection2Lops.get(_direction);
-
-					BinaryOp binput = (BinaryOp)getInput().get(0);
-					agg1 = new UAggOuterChain( binput.getInput().get(0).constructLops(), 
-							binput.getInput().get(1).constructLops(), op, dir, 
-							HopsOpOp2LopsB.get(binput.getOp()), DataType.MATRIX, getValueType(), ExecType.CP);
-					PartialAggregate.setDimensionsBasedOnDirection(agg1, getDim1(), getDim2(), input.getRowsInBlock(), input.getColsInBlock(), dir);
-				
-					if (getDataType() == DataType.SCALAR) {
-						UnaryCP unary1 = new UnaryCP(agg1, HopsOpOp1LopsUS.get(OpOp1.CAST_AS_SCALAR),
-								                    getDataType(), getValueType());
-						unary1.getOutputParameters().setDimensions(0, 0, 0, 0, -1);
-						setLineNumbers(unary1);
-						setLops(unary1);
-					}
-				
-				}				
-				else { //general case		
-					int k = OptimizerUtils.getConstrainedNumThreads(_maxNumThreads);
-					agg1 = new PartialAggregate(input.constructLops(), 
-							HopsAgg2Lops.get(_op), HopsDirection2Lops.get(_direction), getDataType(),getValueType(), et, k);
-				}
-				
-				setOutputDimensions(agg1);
-				setLineNumbers(agg1);
-				setLops(agg1);
-				
-				if (getDataType() == DataType.SCALAR) {
-					agg1.getOutputParameters().setDimensions(1, 1, getRowsInBlock(), getColsInBlock(), getNnz());
-				}
-			}
-			else if( et == ExecType.MR )
-			{
-				OperationTypes op = HopsAgg2Lops.get(_op);
-				DirectionTypes dir = HopsDirection2Lops.get(_direction);
-				
-				//unary aggregate operation
-				Lop transform1 = null;
-				if( isUnaryAggregateOuterRewriteApplicable() ) 
-				{
-					BinaryOp binput = (BinaryOp)getInput().get(0);
-					transform1 = new UAggOuterChain( binput.getInput().get(0).constructLops(), 
-							binput.getInput().get(1).constructLops(), op, dir, 
-							HopsOpOp2LopsB.get(binput.getOp()), DataType.MATRIX, getValueType(), ExecType.MR);
-					PartialAggregate.setDimensionsBasedOnDirection(transform1, getDim1(), getDim2(), input.getRowsInBlock(), input.getColsInBlock(), dir);
-				}
-				else //default
-				{
-					transform1 = new PartialAggregate(input.constructLops(), op, dir, DataType.MATRIX, getValueType());
-					((PartialAggregate) transform1).setDimensionsBasedOnDirection(getDim1(), getDim2(), input.getRowsInBlock(), input.getColsInBlock());
-				}
-				setLineNumbers(transform1);
-				
-				//aggregation if required
-				Lop aggregate = null;
-				Group group1 = null; 
-				Aggregate agg1 = null;
-				if( requiresAggregation(input, _direction) || transform1 instanceof UAggOuterChain )
-				{
-					group1 = new Group(transform1, Group.OperationTypes.Sort, DataType.MATRIX, getValueType());
-					group1.getOutputParameters().setDimensions(getDim1(), getDim2(), input.getRowsInBlock(), input.getColsInBlock(), getNnz());
-					setLineNumbers(group1);
-					
-					agg1 = new Aggregate(group1, HopsAgg2Lops.get(_op), DataType.MATRIX, getValueType(), et);
-					agg1.getOutputParameters().setDimensions(getDim1(), getDim2(), input.getRowsInBlock(), input.getColsInBlock(), getNnz());
-					agg1.setupCorrectionLocation(PartialAggregate.getCorrectionLocation(op,dir));
-					setLineNumbers(agg1);
-					
-					aggregate = agg1;
-				}
-				else
-				{
-					((PartialAggregate) transform1).setDropCorrection();
-					aggregate = transform1;
-				}
-				
-				setLops(aggregate);
-				
-				//cast if required
-				if (getDataType() == DataType.SCALAR) {
-
-					// Set the dimensions of PartialAggregate LOP based on the
-					// direction in which aggregation is performed
-					PartialAggregate.setDimensionsBasedOnDirection(transform1, input.getDim1(), input.getDim2(),
-							input.getRowsInBlock(), input.getColsInBlock(), dir);
-					
-					if( group1 != null && agg1 != null ) { //if aggregation required
-						group1.getOutputParameters().setDimensions(input.getDim1(), input.getDim2(), 
-								input.getRowsInBlock(), input.getColsInBlock(), getNnz());
-						agg1.getOutputParameters().setDimensions(1, 1, 
-								input.getRowsInBlock(), input.getColsInBlock(), getNnz());
-					}
-					
-					UnaryCP unary1 = new UnaryCP(
-							aggregate, HopsOpOp1LopsUS.get(OpOp1.CAST_AS_SCALAR),
-							getDataType(), getValueType());
-					unary1.getOutputParameters().setDimensions(0, 0, 0, 0, -1);
-					setLineNumbers(unary1);
-					setLops(unary1);
-				} 
-			}
-			else if( et == ExecType.SPARK )
-			{
-				OperationTypes op = HopsAgg2Lops.get(_op);
-				DirectionTypes dir = HopsDirection2Lops.get(_direction);
-
-				//unary aggregate
-				if( isTernaryAggregateRewriteApplicable() ) 
-				{
-					Lop aggregate = constructLopsTernaryAggregateRewrite(et);
-					setOutputDimensions(aggregate); //0x0 (scalar)
-					setLineNumbers(aggregate);
-					setLops(aggregate);
-				}
-				else if( isUnaryAggregateOuterSPRewriteApplicable() ) 
-				{
-					BinaryOp binput = (BinaryOp)getInput().get(0);
-					Lop transform1 = new UAggOuterChain( binput.getInput().get(0).constructLops(), 
-							binput.getInput().get(1).constructLops(), op, dir, 
-							HopsOpOp2LopsB.get(binput.getOp()), DataType.MATRIX, getValueType(), ExecType.SPARK);
-					PartialAggregate.setDimensionsBasedOnDirection(transform1, getDim1(), getDim2(), input.getRowsInBlock(), input.getColsInBlock(), dir);
-					setLineNumbers(transform1);
-					setLops(transform1);
-				
-					if (getDataType() == DataType.SCALAR) {
-						UnaryCP unary1 = new UnaryCP(transform1, HopsOpOp1LopsUS.get(OpOp1.CAST_AS_SCALAR),
-								                    getDataType(), getValueType());
-						unary1.getOutputParameters().setDimensions(0, 0, 0, 0, -1);
-						setLineNumbers(unary1);
-						setLops(unary1);
-					}
-				
-				}				
-				else //default
-				{
-					boolean needAgg = requiresAggregation(input, _direction);
-					SparkAggType aggtype = getSparkUnaryAggregationType(needAgg);
-					
-					PartialAggregate aggregate = new PartialAggregate(input.constructLops(), 
-							HopsAgg2Lops.get(_op), HopsDirection2Lops.get(_direction), DataType.MATRIX, getValueType(), aggtype, et);
-					aggregate.setDimensionsBasedOnDirection(getDim1(), getDim2(), input.getRowsInBlock(), input.getColsInBlock());
-					setLineNumbers(aggregate);
-					setLops(aggregate);
-				
-					if (getDataType() == DataType.SCALAR) {
-						UnaryCP unary1 = new UnaryCP(aggregate, HopsOpOp1LopsUS.get(OpOp1.CAST_AS_SCALAR),
-								                    getDataType(), getValueType());
-						unary1.getOutputParameters().setDimensions(0, 0, 0, 0, -1);
-						setLineNumbers(unary1);
-						setLops(unary1);
-					}
-				}
-			}
-		} 
-		catch (Exception e) {
-			throw new HopsException(this.printErrorLocation() + "In AggUnary Hop, error constructing Lops " , e);
-		}
-		
-		//add reblock/checkpoint lops if necessary
-		constructAndSetLopsDataFlowProperties();
-		
-		//return created lops
-		return getLops();
-	}
-
-	
-	
-	@Override
-	public String getOpString() {
-		//ua - unary aggregate, for consistency with runtime
-		String s = "ua(" + 
-				HopsAgg2String.get(_op) + 
-				HopsDirection2String.get(_direction) + ")";
-		return s;
-	}
-
-	public void printMe() throws HopsException {
-		if (LOG.isDebugEnabled()){
-			if (getVisited() != VisitStatus.DONE) {
-				super.printMe();
-				LOG.debug("  Operation: " + _op);
-				LOG.debug("  Direction: " + _direction);
-				for (Hop h : getInput()) {
-					h.printMe();
-				}
-			}
-			setVisited(VisitStatus.DONE);
-		}
-	}
-	
-	@Override
-	public boolean allowsAllExecTypes()
-	{
-		return true;
-	}
-
-	@Override
-	protected double computeOutputMemEstimate( long dim1, long dim2, long nnz )
-	{		
-		double sparsity = OptimizerUtils.getSparsity(dim1, dim2, nnz);
-		return OptimizerUtils.estimateSizeExactSparsity(dim1, dim2, sparsity);
-	}
-	
-	@Override
-	protected double computeIntermediateMemEstimate( long dim1, long dim2, long nnz )
-	{
-		 //default: no additional memory required
-		double val = 0;
-		
-		double sparsity = OptimizerUtils.getSparsity(dim1, dim2, nnz);
-		
-		switch( _op ) //see MatrixAggLib for runtime operations
-		{
-			case MAX:
-			case MIN:
-				//worst-case: column-wise, sparse (temp int count arrays)
-				if( _direction == Direction.Col )
-					val = dim2 * OptimizerUtils.INT_SIZE;
-				break;
-			case SUM:
-			case SUM_SQ:
-				//worst-case correction LASTROW / LASTCOLUMN 
-				if( _direction == Direction.Col ) //(potentially sparse)
-					val = OptimizerUtils.estimateSizeExactSparsity(1, dim2, sparsity);
-				else if( _direction == Direction.Row ) //(always dense)
-					val = OptimizerUtils.estimateSizeExactSparsity(dim1, 1, 1.0);
-				break;
-			case MEAN:
-				//worst-case correction LASTTWOROWS / LASTTWOCOLUMNS 
-				if( _direction == Direction.Col ) //(potentially sparse)
-					val = OptimizerUtils.estimateSizeExactSparsity(2, dim2, sparsity);
-				else if( _direction == Direction.Row ) //(always dense)
-					val = OptimizerUtils.estimateSizeExactSparsity(dim1, 2, 1.0);
-				break;
-			case MAXINDEX:
-			case MININDEX:
-				Hop hop = getInput().get(0);
-				if(isUnaryAggregateOuterCPRewriteApplicable())
-					val = 3 * OptimizerUtils.estimateSizeExactSparsity(1, hop._dim2, 1.0);
-				else
-					//worst-case correction LASTCOLUMN 
-					val = OptimizerUtils.estimateSizeExactSparsity(dim1, 1, 1.0);
-				break;
-			default:
-				//no intermediate memory consumption
-				val = 0;				
-		}
-		
-		return val;
-	}
-	
-	@Override
-	protected long[] inferOutputCharacteristics( MemoTable memo )
-	{
-		long[] ret = null;
-	
-		Hop input = getInput().get(0);
-		MatrixCharacteristics mc = memo.getAllInputStats(input);
-		if( _direction == Direction.Col && mc.colsKnown() ) 
-			ret = new long[]{1, mc.getCols(), -1};
-		else if( _direction == Direction.Row && mc.rowsKnown() )
-			ret = new long[]{mc.getRows(), 1, -1};
-		
-		return ret;
-	}
-	
-
-	@Override
-	protected ExecType optFindExecType() throws HopsException {
-		
-		checkAndSetForcedPlatform();
-		
-		ExecType REMOTE = OptimizerUtils.isSparkExecutionMode() ? ExecType.SPARK : ExecType.MR;
-		
-		//forced / memory-based / threshold-based decision
-		if( _etypeForced != null ) 			
-		{
-			_etype = _etypeForced;
-		}
-		else
-		{
-			if ( OptimizerUtils.isMemoryBasedOptLevel() ) 
-			{
-				_etype = findExecTypeByMemEstimate();
-			}
-			// Choose CP, if the input dimensions are below threshold or if the input is a vector
-			else if ( getInput().get(0).areDimsBelowThreshold() || getInput().get(0).isVector() )
-			{
-				_etype = ExecType.CP;
-			}
-			else 
-			{
-				_etype = REMOTE;
-			}
-			
-			//check for valid CP dimensions and matrix size
-			checkAndSetInvalidCPDimsAndSize();
-		}
-
-		//spark-specific decision refinement (execute unary aggregate w/ spark input and 
-		//single parent also in spark because it's likely cheap and reduces data transfer)
-		if( _etype == ExecType.CP && _etypeForced != ExecType.CP
-			&& !(getInput().get(0) instanceof DataOp)  //input is not checkpoint
-			&& getInput().get(0).getParent().size()==1 //uagg is only parent
-			&& getInput().get(0).optFindExecType() == ExecType.SPARK )					
-		{
-			//pull unary aggregate into spark 
-			_etype = ExecType.SPARK;
-		}
-		
-		//mark for recompile (forever)
-		if( OptimizerUtils.ALLOW_DYN_RECOMPILATION && !dimsKnown(true) && _etype==REMOTE ) {
-			setRequiresRecompile();
-		}
-		
-		return _etype;
-	}
-	
-	/**
-	 * 
-	 * @param input
-	 * @param dir
-	 * @return
-	 */
-	private boolean requiresAggregation( Hop input, Direction dir ) 
-	{
-		if( !ALLOW_UNARYAGG_WO_FINAL_AGG )
-			return false; //customization not allowed
-		
-		boolean noAggRequired = 
-				  ( input.getDim1()>1 && input.getDim1()<=input.getRowsInBlock() && dir==Direction.Col ) //e.g., colSums(X) with nrow(X)<=1000
-				||( input.getDim2()>1 && input.getDim2()<=input.getColsInBlock() && dir==Direction.Row ); //e.g., rowSums(X) with ncol(X)<=1000
-	
-		return !noAggRequired;
-	}
-	
-
-	/**
-	 * 
-	 * @param agg
-	 * @return
-	 */
-	private SparkAggType getSparkUnaryAggregationType( boolean agg )
-	{
-		if( !agg )
-			return SparkAggType.NONE;
-		
-		if(   getDataType()==DataType.SCALAR //in case of scalars the block dims are not set
-		   || dimsKnown() && getDim1()<=getRowsInBlock() && getDim2()<=getColsInBlock() )
-			return SparkAggType.SINGLE_BLOCK;
-		else
-			return SparkAggType.MULTI_BLOCK;
-	}
-	
-	/**
-	 * 
-	 * @return
-	 */
-	private boolean isTernaryAggregateRewriteApplicable() 
-	{
-		boolean ret = false;
-		
-		//currently we support only sum over binary multiply but potentially 
-		//it can be generalized to any RC aggregate over two common binary operations
-		if( _direction == Direction.RowCol 
-			&& _op == AggOp.SUM ) 
-		{
-			Hop input1 = getInput().get(0);
-			if( input1 instanceof BinaryOp && ((BinaryOp)input1).getOp()==OpOp2.MULT )
-			{
-				Hop input11 = input1.getInput().get(0);
-				Hop input12 = input1.getInput().get(1);
-				
-				if( input11 instanceof BinaryOp && ((BinaryOp)input11).getOp()==OpOp2.MULT )
-				{
-					//ternary, arbitrary matrices but no mv/outer operations.
-					ret = HopRewriteUtils.isEqualSize(input11.getInput().get(0), input1)
-						&& HopRewriteUtils.isEqualSize(input11.getInput().get(1), input1)	
-						&& HopRewriteUtils.isEqualSize(input12, input1);
-				}
-				else if( input12 instanceof BinaryOp && ((BinaryOp)input12).getOp()==OpOp2.MULT )
-				{
-					//ternary, arbitrary matrices but no mv/outer operations.
-					ret = HopRewriteUtils.isEqualSize(input12.getInput().get(0), input1)
-							&& HopRewriteUtils.isEqualSize(input12.getInput().get(1), input1)	
-							&& HopRewriteUtils.isEqualSize(input11, input1);
-				}
-				else
-				{
-					//binary, arbitrary matrices but no mv/outer operations.
-					ret = HopRewriteUtils.isEqualSize(input11, input12);
-				}
-			}
-		}
-		
-		return ret;
-	}
-	
-	private static boolean isCompareOperator(OpOp2 opOp2)
-	{
-		return (opOp2 == OpOp2.LESS || opOp2 == OpOp2.LESSEQUAL 
-			|| opOp2 == OpOp2.GREATER || opOp2 == OpOp2.GREATEREQUAL
-			|| opOp2 == OpOp2.EQUAL || opOp2 == OpOp2.NOTEQUAL);
-	}
-	
-	/**
-	 * 
-	 * @return
-	 */
-	private boolean isUnaryAggregateOuterRewriteApplicable() 
-	{
-		boolean ret = false;
-		Hop input = getInput().get(0);
-		
-		if( input instanceof BinaryOp && ((BinaryOp)input).isOuterVectorOperator() )
-		{
-			//for special cases, we need to hold the broadcast twice in order to allow for
-			//an efficient binary search over a plain java array
-			double factor = (isCompareOperator(((BinaryOp)input).getOp())
-					&& (_direction == Direction.Row || _direction == Direction.Col || _direction == Direction.RowCol)
-					&& (_op == AggOp.SUM)) ? 2.0 : 1.0;
-			
-			factor += (isCompareOperator(((BinaryOp)input).getOp())
-					&& (_direction == Direction.Row || _direction == Direction.Col)
-					&& (_op == AggOp.MAXINDEX || _op == AggOp.MININDEX))
-					? 1.0 : 0.0;
-
-			//note: memory constraint only needs to take the rhs into account because the output
-			//is guaranteed to be an aggregate of <=16KB
-			Hop right = input.getInput().get(1);
-			if(  (right.dimsKnown() && factor*OptimizerUtils.estimateSize(right.getDim1(), right.getDim2())
-				  < OptimizerUtils.getRemoteMemBudgetMap(true)) //dims known and estimate fits
-			   ||(!right.dimsKnown() && factor*right.getOutputMemEstimate()
-				  <	OptimizerUtils.getRemoteMemBudgetMap(true)))//dims unknown but worst-case estimate fits 
-			{
-				ret = true;
-			}
-		}
-		
-		return ret;
-	}
-	
-	/**
-	 * This will check if there is sufficient memory locally (twice the size of second matrix, for original and sort data), and remotely (size of second matrix (sorted data)).  
-	 * @return
-	 */
-	private boolean isUnaryAggregateOuterSPRewriteApplicable() 
-	{
-		boolean ret = false;
-		Hop input = getInput().get(0);
-		
-		if( input instanceof BinaryOp && ((BinaryOp)input).isOuterVectorOperator() )
-		{
-			//note: both cases (partitioned matrix, and sorted double array), require to
-			//fit the broadcast twice into the local memory budget. Also, the memory 
-			//constraint only needs to take the rhs into account because the output is 
-			//guaranteed to be an aggregate of <=16KB
-			
-			Hop right = input.getInput().get(1);
-			
-			double size = right.dimsKnown() ? 
-					OptimizerUtils.estimateSize(right.getDim1(), right.getDim2()) : //dims known and estimate fits
-					right.getOutputMemEstimate();                      //dims unknown but worst-case estimate fits
-			
-			if(_op == AggOp.MAXINDEX || _op == AggOp.MININDEX){
-				double memBudgetExec = SparkExecutionContext.getBroadcastMemoryBudget();
-				double memBudgetLocal = OptimizerUtils.getLocalMemBudget();
-
-				//basic requirement: the broadcast needs to to fit twice in the remote broadcast memory 
-				//and local memory budget because we have to create a partitioned broadcast
-				//memory and hand it over to the spark context as in-memory object
-				ret = ( 2*size < memBudgetExec && 2*size < memBudgetLocal );
-			
-			} else {
-				if( OptimizerUtils.checkSparkBroadcastMemoryBudget(size) ) {
-					ret = true;
-				}
-			}
-				
-		}
-		
-		return ret;
-	}
-	
-	
-	
-	/**
-	 * This will check if this is one of the operator from supported LibMatrixOuterAgg library.
-	 * It needs to be Outer, aggregator type SUM, RowIndexMin, RowIndexMax and 6 operators <, <=, >, >=, == and !=
-	 *   
-	 *   
-	 * @return
-	 */
-	private boolean isUnaryAggregateOuterCPRewriteApplicable() 
-	{
-		boolean ret = false;
-		Hop input = getInput().get(0);
-		
-		if(( input instanceof BinaryOp && ((BinaryOp)input).isOuterVectorOperator() )
-			&& (_op == AggOp.MAXINDEX || _op == AggOp.MININDEX || _op == AggOp.SUM)
-			&& (isCompareOperator(((BinaryOp)input).getOp())))
-			ret = true;
-
-		return ret;
-	}
-	
-	
-	/**
-	 * 
-	 * @return
-	 * @throws HopsException
-	 * @throws LopsException
-	 */
-	private Lop constructLopsTernaryAggregateRewrite(ExecType et) 
-		throws HopsException, LopsException
-	{
-		Hop input1 = getInput().get(0);
-		Hop input11 = input1.getInput().get(0);
-		Hop input12 = input1.getInput().get(1);
-		
-		Lop ret = null;
-		Lop in1 = null;
-		Lop in2 = null;
-		Lop in3 = null;
-		
-		if( input11 instanceof BinaryOp && ((BinaryOp)input11).getOp()==OpOp2.MULT )
-		{
-			in1 = input11.getInput().get(0).constructLops();
-			in2 = input11.getInput().get(1).constructLops();
-			in3 = input12.constructLops();
-		}
-		else if( input12 instanceof BinaryOp && ((BinaryOp)input12).getOp()==OpOp2.MULT )
-		{
-			in1 = input11.constructLops();
-			in2 = input12.getInput().get(0).constructLops();
-			in3 = input12.getInput().get(1).constructLops();
-		}
-		else
-		{
-			in1 = input11.constructLops();
-			in2 = input12.constructLops();
-			in3 = new LiteralOp(1).constructLops();
-		}
-
-		//create new ternary aggregate operator 
-		int k = OptimizerUtils.getConstrainedNumThreads( _maxNumThreads );
-		ret = new TernaryAggregate(in1, in2, in3, Aggregate.OperationTypes.KahanSum, 
-				Binary.OperationTypes.MULTIPLY, DataType.SCALAR, ValueType.DOUBLE, et, k);
-		
-		return ret;
-	}
-	
-	@Override
-	public void refreshSizeInformation()
-	{
-		if (getDataType() != DataType.SCALAR)
-		{
-			Hop input = getInput().get(0);
-			if ( _direction == Direction.Col ) //colwise computations
-			{
-				setDim1(1);
-				setDim2(input.getDim2());
-			}
-			else if ( _direction == Direction.Row )
-			{
-				setDim1(input.getDim1());
-				setDim2(1);	
-			}
-		}
-	}
-	
-	@Override
-	public boolean isTransposeSafe()
-	{
-		boolean ret = (_direction == Direction.RowCol) && //full aggregate
-		              (_op == AggOp.SUM || _op == AggOp.SUM_SQ || //valid aggregration functions
-		               _op == AggOp.MIN || _op == AggOp.MAX ||
-		               _op == AggOp.PROD || _op == AggOp.MEAN);
-		//note: trace and maxindex are not transpose-safe.
-		
-		return ret;	
-	}
-	
-	@Override
-	public Object clone() throws CloneNotSupportedException 
-	{
-		AggUnaryOp ret = new AggUnaryOp();	
-		
-		//copy generic attributes
-		ret.clone(this, false);
-		
-		//copy specific attributes
-		ret._op = _op;
-		ret._direction = _direction;
-		ret._maxNumThreads = _maxNumThreads;
-		
-		return ret;
-	}
-	
-	@Override
-	public boolean compare( Hop that )
-	{
-		if( !(that instanceof AggUnaryOp) )
-			return false;
-		
-		AggUnaryOp that2 = (AggUnaryOp)that;		
-		return (   _op == that2._op
-				&& _direction == that2._direction
-				&& _maxNumThreads == that2._maxNumThreads
-				&& getInput().get(0) == that2.getInput().get(0));
-	}
-}

http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/276d9257/src/main/java/com/ibm/bi/dml/hops/BinaryOp.java
----------------------------------------------------------------------
diff --git a/src/main/java/com/ibm/bi/dml/hops/BinaryOp.java b/src/main/java/com/ibm/bi/dml/hops/BinaryOp.java
deleted file mode 100644
index 9d6d6fb..0000000
--- a/src/main/java/com/ibm/bi/dml/hops/BinaryOp.java
+++ /dev/null
@@ -1,1565 +0,0 @@
-/**
- * (C) Copyright IBM Corp. 2010, 2015
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * 
- */
-
-package com.ibm.bi.dml.hops;
-
-import com.ibm.bi.dml.hops.rewrite.HopRewriteUtils;
-import com.ibm.bi.dml.lops.Aggregate;
-import com.ibm.bi.dml.lops.AppendGAlignedSP;
-import com.ibm.bi.dml.lops.AppendM;
-import com.ibm.bi.dml.lops.AppendCP;
-import com.ibm.bi.dml.lops.AppendG;
-import com.ibm.bi.dml.lops.AppendR;
-import com.ibm.bi.dml.lops.Binary;
-import com.ibm.bi.dml.lops.BinaryScalar;
-import com.ibm.bi.dml.lops.BinaryM;
-import com.ibm.bi.dml.lops.BinaryUAggChain;
-import com.ibm.bi.dml.lops.CentralMoment;
-import com.ibm.bi.dml.lops.CoVariance;
-import com.ibm.bi.dml.lops.CombineBinary;
-import com.ibm.bi.dml.lops.CombineUnary;
-import com.ibm.bi.dml.lops.Data;
-import com.ibm.bi.dml.lops.DataPartition;
-import com.ibm.bi.dml.lops.Group;
-import com.ibm.bi.dml.lops.Lop;
-import com.ibm.bi.dml.lops.LopsException;
-import com.ibm.bi.dml.lops.PartialAggregate;
-import com.ibm.bi.dml.lops.PickByCount;
-import com.ibm.bi.dml.lops.RepMat;
-import com.ibm.bi.dml.lops.SortKeys;
-import com.ibm.bi.dml.lops.Unary;
-import com.ibm.bi.dml.lops.UnaryCP;
-import com.ibm.bi.dml.lops.CombineBinary.OperationTypes;
-import com.ibm.bi.dml.lops.LopProperties.ExecType;
-import com.ibm.bi.dml.parser.Expression.DataType;
-import com.ibm.bi.dml.parser.Expression.ValueType;
-import com.ibm.bi.dml.runtime.controlprogram.ParForProgramBlock.PDataPartitionFormat;
-import com.ibm.bi.dml.runtime.matrix.MatrixCharacteristics;
-import com.ibm.bi.dml.runtime.matrix.mapred.DistributedCacheInput;
-
-
-/* Binary (cell operations): aij + bij
- * 		Properties: 
- * 			Symbol: *, -, +, ...
- * 			2 Operands
- * 		Semantic: align indices (sort), then perform operation
- */
-
-public class BinaryOp extends Hop 
-{
-	
-	//we use the full remote memory budget (but reduced by sort buffer), 
-	public static final double APPEND_MEM_MULTIPLIER = 1.0;
-	
-	private Hop.OpOp2 op;
-	private boolean outer = false;
-	
-	public static AppendMethod FORCED_APPEND_METHOD = null;
-	public enum AppendMethod { 
-		CP_APPEND, //in-memory general case append
-		MR_MAPPEND, //map-only append (rhs must be vector and fit in mapper mem)
-		MR_RAPPEND, //reduce-only append (output must have at most one column block)
-		MR_GAPPEND, //map-reduce general case append (map-extend, aggregate)
-		SP_GAlignedAppend // special case for general case in Spark where left.getCols() % left.getColsPerBlock() == 0
-	};
-	
-	private enum MMBinaryMethod{
-		CP_BINARY,
-		MR_BINARY_R, //both mm, mv 
-		MR_BINARY_M, //only mv (mr/spark)
-		MR_BINARY_OUTER_M,
-		MR_BINARY_OUTER_R, //only vv 
-		MR_BINARY_UAGG_CHAIN, //(mr/spark)
-	}
-	
-	private BinaryOp() {
-		//default constructor for clone
-	}
-	
-	public BinaryOp(String l, DataType dt, ValueType vt, Hop.OpOp2 o,
-			Hop inp1, Hop inp2) {
-		super(l, dt, vt);
-		op = o;
-		getInput().add(0, inp1);
-		getInput().add(1, inp2);
-
-		inp1.getParent().add(this);
-		inp2.getParent().add(this);
-		
-		//compute unknown dims and nnz
-		refreshSizeInformation();
-	}
-
-	public OpOp2 getOp() {
-		return op;
-	}
-	
-	public void setOp(OpOp2 iop) {
-		 op = iop;
-	}
-	
-	public void setOuterVectorOperation(boolean flag) {
-		outer = flag;
-	}
-	
-	public boolean isOuterVectorOperator(){
-		return outer;
-	}
-	
-	@Override
-	public Lop constructLops() 
-		throws HopsException, LopsException 
-	{	
-		//return already created lops
-		if( getLops() != null )
-			return getLops();
-
-		//select the execution type
-		ExecType et = optFindExecType();
-		
-		switch(op) 
-		{
-			case IQM: {
-				constructLopsIQM(et);
-				break;
-			}
-			case CENTRALMOMENT: {
-				constructLopsCentralMoment(et);
-				break;
-			}	
-			case COVARIANCE: {
-				constructLopsCovariance(et);
-				break;
-			}
-			case QUANTILE:
-			case INTERQUANTILE: {
-				constructLopsQuantile(et);
-				break;
-			}
-			case MEDIAN: {
-				constructLopsMedian(et);
-				break;
-			}
-			case CBIND: 
-			case RBIND: {
-				constructLopsAppend(et);
-				break;
-			}
-			default:
-				constructLopsBinaryDefault();	
-		}
-
-		//add reblock/checkpoint lops if necessary
-		constructAndSetLopsDataFlowProperties();
-		
-		return getLops();
-	}
-	
-	private void constructLopsIQM(ExecType et) throws HopsException, LopsException {
-		if ( et == ExecType.MR ) {
-			CombineBinary combine = CombineBinary.constructCombineLop(
-					OperationTypes.PreSort, (Lop) getInput().get(0)
-							.constructLops(), (Lop) getInput().get(1)
-							.constructLops(), DataType.MATRIX,
-					getValueType());
-			combine.getOutputParameters().setDimensions(
-					getInput().get(0).getDim1(),
-					getInput().get(0).getDim2(),
-					getInput().get(0).getRowsInBlock(),
-					getInput().get(0).getColsInBlock(), 
-					getInput().get(0).getNnz());
-
-			SortKeys sort = SortKeys.constructSortByValueLop(
-					combine,
-					SortKeys.OperationTypes.WithWeights,
-					DataType.MATRIX, ValueType.DOUBLE, ExecType.MR);
-
-			// Sort dimensions are same as the first input
-			sort.getOutputParameters().setDimensions(
-					getInput().get(0).getDim1(),
-					getInput().get(0).getDim2(),
-					getInput().get(0).getRowsInBlock(),
-					getInput().get(0).getColsInBlock(), 
-					getInput().get(0).getNnz());
-
-			Data lit = Data.createLiteralLop(ValueType.DOUBLE, Double.toString(0.25));
-			setLineNumbers(lit);
-	
-			PickByCount pick = new PickByCount(
-					sort, lit, DataType.MATRIX, getValueType(),
-					PickByCount.OperationTypes.RANGEPICK);
-
-			pick.getOutputParameters().setDimensions(-1, -1, 
-					getRowsInBlock(), getColsInBlock(), -1);
-			setLineNumbers(pick);
-			
-			PartialAggregate pagg = new PartialAggregate(pick,
-					HopsAgg2Lops.get(Hop.AggOp.SUM),
-					HopsDirection2Lops.get(Hop.Direction.RowCol),
-					DataType.MATRIX, getValueType());
-			setLineNumbers(pagg);
-			
-			// Set the dimensions of PartialAggregate LOP based on the
-			// direction in which aggregation is performed
-			pagg.setDimensionsBasedOnDirection(getDim1(), getDim2(),
-					getRowsInBlock(), getColsInBlock());
-
-			Group group1 = new Group(pagg, Group.OperationTypes.Sort,
-					DataType.MATRIX, getValueType());
-			setOutputDimensions(group1);
-			setLineNumbers(group1);
-
-			Aggregate agg1 = new Aggregate(group1, HopsAgg2Lops
-					.get(Hop.AggOp.SUM), DataType.MATRIX,
-					getValueType(), ExecType.MR);
-			setOutputDimensions(agg1);
-			agg1.setupCorrectionLocation(pagg.getCorrectionLocation());
-			setLineNumbers(agg1);
-
-			UnaryCP unary1 = new UnaryCP(agg1, HopsOpOp1LopsUS
-					.get(OpOp1.CAST_AS_SCALAR), DataType.SCALAR,
-					getValueType());
-			unary1.getOutputParameters().setDimensions(0, 0, 0, 0, -1);
-			setLineNumbers(unary1);
-			
-			Unary iqm = new Unary(sort, unary1, Unary.OperationTypes.MR_IQM, DataType.SCALAR, ValueType.DOUBLE, ExecType.CP);
-			iqm.getOutputParameters().setDimensions(0, 0, 0, 0, -1);
-			setLineNumbers(iqm);			
-			setLops(iqm);
-		}
-		else 
-		{
-			SortKeys sort = SortKeys.constructSortByValueLop(
-					getInput().get(0).constructLops(), 
-					getInput().get(1).constructLops(), 
-					SortKeys.OperationTypes.WithWeights, 
-					getInput().get(0).getDataType(), getInput().get(0).getValueType(), et);
-			sort.getOutputParameters().setDimensions(
-					getInput().get(0).getDim1(),
-					getInput().get(0).getDim2(), 
-					getInput().get(0).getRowsInBlock(), 
-					getInput().get(0).getColsInBlock(), 
-					getInput().get(0).getNnz());
-			PickByCount pick = new PickByCount(
-					sort,
-					null,
-					getDataType(),
-					getValueType(),
-					PickByCount.OperationTypes.IQM, et, true);
-			
-			setOutputDimensions(pick);
-			setLineNumbers(pick);
-			setLops(pick);
-		}
-	}
-	
-	private void constructLopsMedian(ExecType et) throws HopsException, LopsException {
-		if ( et == ExecType.MR ) {
-			CombineBinary combine = CombineBinary
-					.constructCombineLop(
-							OperationTypes.PreSort,
-							getInput().get(0).constructLops(),
-							getInput().get(1).constructLops(),
-							DataType.MATRIX, getValueType());
-
-			SortKeys sort = SortKeys
-					.constructSortByValueLop(
-							combine,
-							SortKeys.OperationTypes.WithWeights,
-							DataType.MATRIX, getValueType(), et);
-
-			combine.getOutputParameters().setDimensions(getDim1(),
-					getDim2(), getRowsInBlock(), getColsInBlock(), getNnz());
-
-			// Sort dimensions are same as the first input
-			sort.getOutputParameters().setDimensions(
-					getInput().get(0).getDim1(),
-					getInput().get(0).getDim2(),
-					getInput().get(0).getRowsInBlock(),
-					getInput().get(0).getColsInBlock(), 
-					getInput().get(0).getNnz());
-
-			ExecType et_pick = ExecType.CP;
-			
-			PickByCount pick = new PickByCount(
-					sort,
-					Data.createLiteralLop(ValueType.DOUBLE, Double.toString(0.5)),
-					getDataType(),
-					getValueType(),
-					PickByCount.OperationTypes.MEDIAN, et_pick, false);
-
-			pick.getOutputParameters().setDimensions(getDim1(),
-					getDim2(), getRowsInBlock(), getColsInBlock(), getNnz());
-			
-			pick.setAllPositions(this.getBeginLine(), this.getBeginColumn(), this.getEndLine(), this.getEndColumn());
-
-			setLops(pick);
-		}
-		else {
-			SortKeys sort = SortKeys.constructSortByValueLop(
-					getInput().get(0).constructLops(), 
-					getInput().get(1).constructLops(), 
-					SortKeys.OperationTypes.WithWeights, 
-					getInput().get(0).getDataType(), getInput().get(0).getValueType(), et);
-			sort.getOutputParameters().setDimensions(
-					getInput().get(0).getDim1(),
-					getInput().get(0).getDim2(),
-					getInput().get(0).getRowsInBlock(),
-					getInput().get(0).getColsInBlock(), 
-					getInput().get(0).getNnz());
-			PickByCount pick = new PickByCount(
-					sort,
-					Data.createLiteralLop(ValueType.DOUBLE, Double.toString(0.5)),
-					getDataType(),
-					getValueType(),
-					PickByCount.OperationTypes.MEDIAN, et, true);
-
-			pick.getOutputParameters().setDimensions(getDim1(),
-					getDim2(), getRowsInBlock(), getColsInBlock(), getNnz());
-			
-			pick.setAllPositions(this.getBeginLine(), this.getBeginColumn(), this.getEndLine(), this.getEndColumn());
-
-			setLops(pick);
-		}
-	}
-	
-	private void constructLopsCentralMoment(ExecType et) 
-		throws HopsException, LopsException 
-	{
-		// The output data type is a SCALAR if central moment 
-		// gets computed in CP/SPARK, and it will be MATRIX otherwise.
-		DataType dt = (et == ExecType.MR ? DataType.MATRIX : DataType.SCALAR );
-		CentralMoment cm = new CentralMoment(
-				getInput().get(0).constructLops(), 
-				getInput().get(1).constructLops(),
-				dt, getValueType(), et);
-
-		setLineNumbers(cm);
-		
-		if ( et == ExecType.MR ) {
-			cm.getOutputParameters().setDimensions(1, 1, 0, 0, -1);
-			UnaryCP unary1 = new UnaryCP(cm, HopsOpOp1LopsUS
-					.get(OpOp1.CAST_AS_SCALAR), getDataType(),
-					getValueType());
-			unary1.getOutputParameters().setDimensions(0, 0, 0, 0, -1);
-			setLineNumbers(unary1);
-			setLops(unary1);
-		}
-		else {
-			cm.getOutputParameters().setDimensions(0, 0, 0, 0, -1);
-			setLops(cm);
-		}
-	}
-	
-	/**
-	 * 
-	 * @param et
-	 * @throws LopsException
-	 * @throws HopsException
-	 */
-	private void constructLopsCovariance(ExecType et) 
-		throws LopsException, HopsException 
-	{
-		if ( et == ExecType.MR ) {
-			// combineBinary -> CoVariance -> CastAsScalar
-			CombineBinary combine = CombineBinary.constructCombineLop(
-					OperationTypes.PreCovUnweighted, getInput().get(
-							0).constructLops(), getInput().get(1)
-							.constructLops(), DataType.MATRIX,
-					getValueType());
-
-			combine.getOutputParameters().setDimensions(
-					getInput().get(0).getDim1(),
-					getInput().get(0).getDim2(),
-					getInput().get(0).getRowsInBlock(),
-					getInput().get(0).getColsInBlock(), 
-					getInput().get(0).getNnz());
-
-			CoVariance cov = new CoVariance(combine, DataType.MATRIX,
-					getValueType(), et);
-			cov.getOutputParameters().setDimensions(1, 1, 0, 0, -1);
-			setLineNumbers(cov);
-
-			UnaryCP unary1 = new UnaryCP(cov, HopsOpOp1LopsUS
-					.get(OpOp1.CAST_AS_SCALAR), getDataType(),
-					getValueType());
-			unary1.getOutputParameters().setDimensions(0, 0, 0, 0, -1);
-			setLineNumbers(unary1);	
-			setLops(unary1);
-		}
-		else //CP/SPARK
-		{
-			CoVariance cov = new CoVariance(
-					getInput().get(0).constructLops(), 
-					getInput().get(1).constructLops(), 
-					getDataType(), getValueType(), et);
-			cov.getOutputParameters().setDimensions(0, 0, 0, 0, -1);
-			setLineNumbers(cov);
-			setLops(cov);
-		}
-	}
-	
-	private void constructLopsQuantile(ExecType et) throws HopsException, LopsException {
-		// 1st arguments needs to be a 1-dimensional matrix
-		// For QUANTILE: 2nd argument is scalar or 1-dimensional matrix
-		// For INTERQUANTILE: 2nd argument is always a scalar
-
-		PickByCount.OperationTypes pick_op = null;
-		if(op == Hop.OpOp2.QUANTILE)
-			pick_op = PickByCount.OperationTypes.VALUEPICK;
-		else
-			pick_op = PickByCount.OperationTypes.RANGEPICK;
-
-		if ( et == ExecType.MR ) 
-		{
-			CombineUnary combine = CombineUnary.constructCombineLop(
-					getInput().get(0).constructLops(),
-					getDataType(), getValueType());
-
-			SortKeys sort = SortKeys.constructSortByValueLop(
-					combine, SortKeys.OperationTypes.WithoutWeights,
-					DataType.MATRIX, ValueType.DOUBLE, et);
-
-			combine.getOutputParameters().setDimensions(getDim1(),
-					getDim2(), getRowsInBlock(), getColsInBlock(), getNnz());
-
-			// Sort dimensions are same as the first input
-			sort.getOutputParameters().setDimensions(
-					getInput().get(0).getDim1(),
-					getInput().get(0).getDim2(),
-					getInput().get(0).getRowsInBlock(),
-					getInput().get(0).getColsInBlock(), 
-					getInput().get(0).getNnz());
-
-			// If only a single quantile is computed, then "pick" operation executes in CP.
-			ExecType et_pick = (getInput().get(1).getDataType() == DataType.SCALAR ? ExecType.CP : ExecType.MR);
-			
-			PickByCount pick = new PickByCount(
-					sort,
-					getInput().get(1).constructLops(),
-					getDataType(),
-					getValueType(),
-					pick_op, et_pick, false);
-
-			pick.getOutputParameters().setDimensions(getDim1(),
-					getDim2(), getRowsInBlock(), getColsInBlock(), getNnz());
-			
-			pick.setAllPositions(this.getBeginLine(), this.getBeginColumn(), this.getEndLine(), this.getEndColumn());
-
-			setLops(pick);
-		}
-		else //CP/SPARK 
-		{
-			SortKeys sort = SortKeys.constructSortByValueLop(
-								getInput().get(0).constructLops(), 
-								SortKeys.OperationTypes.WithoutWeights, 
-								DataType.MATRIX, ValueType.DOUBLE, et );
-			sort.getOutputParameters().setDimensions(
-					getInput().get(0).getDim1(),
-					getInput().get(0).getDim2(),
-					getInput().get(0).getRowsInBlock(),
-					getInput().get(0).getColsInBlock(), 
-					getInput().get(0).getNnz());
-			PickByCount pick = new PickByCount( sort, getInput().get(1).constructLops(),
-					getDataType(), getValueType(), pick_op, et, true);
-
-			setOutputDimensions(pick);
-			setLineNumbers(pick);
-			setLops(pick);
-		}
-	}
-	
-	/**
-	 * 
-	 * @param et
-	 * @throws HopsException
-	 * @throws LopsException
-	 */
-	private void constructLopsAppend(ExecType et) 
-		throws HopsException, LopsException 
-	{
-		DataType dt1 = getInput().get(0).getDataType();
-		DataType dt2 = getInput().get(1).getDataType();
-		ValueType vt1 = getInput().get(0).getValueType();
-		ValueType vt2 = getInput().get(1).getValueType();
-		boolean cbind = op==OpOp2.CBIND;
-		
-		//sanity check for input data types
-		if( !((dt1==DataType.MATRIX && dt2==DataType.MATRIX)
-			 ||(dt1==DataType.SCALAR && dt2==DataType.SCALAR
-			   && vt1==ValueType.STRING && vt2==ValueType.STRING )) )
-		{
-			throw new HopsException("Append can only apply to two matrices or two scalar strings!");
-		}
-				
-		Lop append = null;
-		if( dt1==DataType.MATRIX && dt2==DataType.MATRIX )
-		{
-			long rlen = cbind ? getInput().get(0).getDim1() : (getInput().get(0).dimsKnown() && getInput().get(1).dimsKnown()) ?
-				getInput().get(0).getDim1()+getInput().get(1).getDim1() : -1;
-			long clen = cbind ? ((getInput().get(0).dimsKnown() && getInput().get(1).dimsKnown()) ?
-				getInput().get(0).getDim2()+getInput().get(1).getDim2() : -1) : getInput().get(0).getDim2();			
-		
-			if( et == ExecType.MR )
-			{
-				append = constructMRAppendLop(getInput().get(0), getInput().get(1), getDataType(), getValueType(), cbind, this);				
-			}
-			else if(et == ExecType.SPARK) 
-			{
-				append = constructSPAppendLop(getInput().get(0), getInput().get(1), getDataType(), getValueType(), cbind, this);
-				append.getOutputParameters().setDimensions(rlen, clen, getRowsInBlock(), getColsInBlock(), getNnz());
-			}
-			else //CP
-			{
-				Lop offset = createOffsetLop( getInput().get(0), cbind ); //offset 1st input
-				append = new AppendCP(getInput().get(0).constructLops(), getInput().get(1).constructLops(), offset, getDataType(), getValueType(), cbind);
-				append.getOutputParameters().setDimensions(rlen, clen, getRowsInBlock(), getColsInBlock(), getNnz());
-			}
-		}
-		else //SCALAR-STRING and SCALAR-STRING (always CP)
-		{
-			append = new AppendCP(getInput().get(0).constructLops(), getInput().get(1).constructLops(), 
-				     Data.createLiteralLop(ValueType.INT, "-1"), getDataType(), getValueType(), cbind);
-			append.getOutputParameters().setDimensions(0,0,-1,-1,-1);
-		}
-		
-		setLineNumbers(append);
-		setLops(append);
-	}
-	
-	/**
-	 * 
-	 * @throws HopsException
-	 * @throws LopsException
-	 */
-	private void constructLopsBinaryDefault() 
-		throws HopsException, LopsException 
-	{
-		/* Default behavior for BinaryOp */
-		// it depends on input data types
-		DataType dt1 = getInput().get(0).getDataType();
-		DataType dt2 = getInput().get(1).getDataType();
-		
-		if (dt1 == dt2 && dt1 == DataType.SCALAR) {
-
-			// Both operands scalar
-			BinaryScalar binScalar1 = new BinaryScalar(getInput().get(0)
-					.constructLops(),
-					getInput().get(1).constructLops(), HopsOpOp2LopsBS
-							.get(op), getDataType(), getValueType());
-			binScalar1.getOutputParameters().setDimensions(0, 0, 0, 0, -1);
-			setLineNumbers(binScalar1);
-			setLops(binScalar1);
-
-		} 
-		else if ((dt1 == DataType.MATRIX && dt2 == DataType.SCALAR)
-				   || (dt1 == DataType.SCALAR && dt2 == DataType.MATRIX)) {
-
-			// One operand is Matrix and the other is scalar
-			ExecType et = optFindExecType();
-			
-			//select specific operator implementations
-			Unary.OperationTypes ot = null;
-			Hop right = getInput().get(1);
-			if( op==OpOp2.POW && right instanceof LiteralOp && ((LiteralOp)right).getDoubleValue()==2.0  )
-				ot = Unary.OperationTypes.POW2;
-			else if( op==OpOp2.MULT && right instanceof LiteralOp && ((LiteralOp)right).getDoubleValue()==2.0  )
-				ot = Unary.OperationTypes.MULTIPLY2;
-			else //general case
-				ot = HopsOpOp2LopsU.get(op);
-			
-			
-			Unary unary1 = new Unary(getInput().get(0).constructLops(),
-						   getInput().get(1).constructLops(), ot, getDataType(), getValueType(), et);
-		
-			setOutputDimensions(unary1);
-			setLineNumbers(unary1);
-			setLops(unary1);
-			
-		} 
-		else 
-		{
-			// Both operands are Matrixes
-			ExecType et = optFindExecType();
-			if ( et == ExecType.CP ) 
-			{
-				Binary binary = new Binary(getInput().get(0).constructLops(), getInput().get(1).constructLops(), HopsOpOp2LopsB.get(op),
-						getDataType(), getValueType(), et);
-				
-				setOutputDimensions(binary);
-				setLineNumbers(binary);
-				setLops(binary);
-			}
-			else if(et == ExecType.SPARK)
-			{
-				Hop left = getInput().get(0);
-				Hop right = getInput().get(1);
-				MMBinaryMethod mbin = optFindMMBinaryMethodSpark(left, right);
-				
-				Lop  binary = null;
-				if( mbin == MMBinaryMethod.MR_BINARY_UAGG_CHAIN ) {
-					AggUnaryOp uRight = (AggUnaryOp)right;
-					binary = new BinaryUAggChain(left.constructLops(), HopsOpOp2LopsB.get(op),
-							HopsAgg2Lops.get(uRight.getOp()), HopsDirection2Lops.get(uRight.getDirection()),
-							getDataType(), getValueType(), et);
-				}
-				else if (mbin == MMBinaryMethod.MR_BINARY_M) {
-					boolean partitioned = false;
-					boolean isColVector = (right.getDim2()==1 && left.getDim1()==right.getDim1());
-					
-					binary = new BinaryM(left.constructLops(), right.constructLops(),
-							HopsOpOp2LopsB.get(op), getDataType(), getValueType(), et, partitioned, isColVector); 
-				}
-				else {
-					binary = new Binary(left.constructLops(), right.constructLops(), 
-							HopsOpOp2LopsB.get(op), getDataType(), getValueType(), et);
-				}
-				
-				setOutputDimensions(binary);
-				setLineNumbers(binary);
-				setLops(binary);
-			}
-			else //MR
-			{
-				Hop left = getInput().get(0);
-				Hop right = getInput().get(1);
-				MMBinaryMethod mbin = optFindMMBinaryMethod(left, right);
-				
-				if( mbin == MMBinaryMethod.MR_BINARY_M )
-				{
-					boolean needPart = requiresPartitioning(right);
-					Lop dcInput = right.constructLops();
-					if( needPart ) {
-						//right side in distributed cache
-						ExecType etPart = (OptimizerUtils.estimateSizeExactSparsity(right.getDim1(), right.getDim2(), OptimizerUtils.getSparsity(right.getDim1(), right.getDim2(), right.getNnz())) 
-						          < OptimizerUtils.getLocalMemBudget()) ? ExecType.CP : ExecType.MR; //operator selection
-						dcInput = new DataPartition(dcInput, DataType.MATRIX, ValueType.DOUBLE, etPart, (right.getDim2()==1)?PDataPartitionFormat.ROW_BLOCK_WISE_N:PDataPartitionFormat.COLUMN_BLOCK_WISE_N);
-						dcInput.getOutputParameters().setDimensions(right.getDim1(), right.getDim2(), right.getRowsInBlock(), right.getColsInBlock(), right.getNnz());
-						dcInput.setAllPositions(right.getBeginLine(), right.getBeginColumn(), right.getEndLine(), right.getEndColumn());
-					}					
-					
-					BinaryM binary = new BinaryM(left.constructLops(), dcInput, HopsOpOp2LopsB.get(op),
-							getDataType(), getValueType(), ExecType.MR, needPart, (right.getDim2()==1 && left.getDim1()==right.getDim1()));
-					setOutputDimensions(binary);
-					setLineNumbers(binary);
-					setLops(binary);
-				}
-				else if( mbin == MMBinaryMethod.MR_BINARY_UAGG_CHAIN )
-				{
-					AggUnaryOp uRight = (AggUnaryOp)right;
-					BinaryUAggChain bin = new BinaryUAggChain(left.constructLops(), HopsOpOp2LopsB.get(op),
-							HopsAgg2Lops.get(uRight.getOp()), HopsDirection2Lops.get(uRight.getDirection()),
-							getDataType(), getValueType(), et);
-					setOutputDimensions(bin);
-					setLineNumbers(bin);
-					setLops(bin);
-				}
-				else if( mbin == MMBinaryMethod.MR_BINARY_OUTER_R )
-				{
-					boolean requiresRepLeft = (!right.dimsKnown() || right.getDim2() > right.getColsInBlock());
-					boolean requiresRepRight = (!left.dimsKnown() || left.getDim1() > right.getRowsInBlock());
-					
-					Lop leftLop = left.constructLops();
-					Lop rightLop = right.constructLops();
-					
-					if( requiresRepLeft ) {
-						Lop offset = createOffsetLop(right, true); //ncol of right determines rep of left
-						leftLop = new RepMat(leftLop, offset, true, left.getDataType(), left.getValueType());
-						setOutputDimensions(leftLop);
-						setLineNumbers(leftLop);
-					}
-					
-					if( requiresRepRight ) {
-						Lop offset = createOffsetLop(left, false); //nrow of right determines rep of right
-						rightLop = new RepMat(rightLop, offset, false, right.getDataType(), right.getValueType());
-						setOutputDimensions(rightLop);
-						setLineNumbers(rightLop);
-					}
-				
-					Group group1 = new Group( leftLop, Group.OperationTypes.Sort, getDataType(), getValueType());
-					setLineNumbers(group1);
-					setOutputDimensions(group1);
-					
-					Group group2 = new Group( rightLop, Group.OperationTypes.Sort, getDataType(), getValueType());
-					setLineNumbers(group2);
-					setOutputDimensions(group2);
-					
-					Binary binary = new Binary(group1, group2, HopsOpOp2LopsB.get(op), getDataType(), getValueType(), et);
-					setOutputDimensions(binary);
-					setLineNumbers(binary);
-					
-					setLops(binary);
-				}
-				else //MMBinaryMethod.MR_BINARY_R
-				{
-					boolean requiresRep = requiresReplication(left, right);
-					
-					Lop rightLop = right.constructLops();
-					if( requiresRep ) {
-						Lop offset = createOffsetLop(left, (right.getDim2()<=1)); //ncol of left input (determines num replicates)
-						rightLop = new RepMat(rightLop, offset, (right.getDim2()<=1), right.getDataType(), right.getValueType());
-						setOutputDimensions(rightLop);
-						setLineNumbers(rightLop);	
-					}
-				
-					Group group1 = new Group(getInput().get(0).constructLops(), Group.OperationTypes.Sort, getDataType(), getValueType());
-					setLineNumbers(group1);
-					setOutputDimensions(group1);
-				
-					Group group2 = new Group( rightLop, Group.OperationTypes.Sort, getDataType(), getValueType());
-					setLineNumbers(group2);
-					setOutputDimensions(group2);
-				
-					Binary binary = new Binary(group1, group2, HopsOpOp2LopsB.get(op), getDataType(), getValueType(), et);
-					setLineNumbers(binary);
-					setOutputDimensions(binary);
-					
-					setLops(binary);
-				}
-			}
-		}
-	}
-
-	@Override
-	public String getOpString() {
-		String s = new String("");
-		s += "b(" + HopsOpOp2String.get(op) + ")";
-		return s;
-	}
-
-	public void printMe() throws HopsException {
-		if (LOG.isDebugEnabled()){
-			if (getVisited() != VisitStatus.DONE) {
-				super.printMe();
-				LOG.debug("  Operation: " + op );
-				for (Hop h : getInput()) {
-					h.printMe();
-				}
-				;
-			}
-			setVisited(VisitStatus.DONE);
-		}
-	}
-
-	@Override
-	protected double computeOutputMemEstimate( long dim1, long dim2, long nnz )
-	{		
-		double ret = 0;
-		
-		//preprocessing step (recognize unknowns)
-		if( dimsKnown() && _nnz<0 ) //never after inference
-			nnz = -1; 
-		
-		if((op==OpOp2.CBIND || op==OpOp2.RBIND) && !OptimizerUtils.ALLOW_DYN_RECOMPILATION && !(getDataType()==DataType.SCALAR) ) {	
-			ret = OptimizerUtils.DEFAULT_SIZE;
-		}
-		else
-		{
-			double sparsity = 1.0;
-			if( nnz < 0 ){ //check for exactly known nnz
-				Hop input1 = getInput().get(0);
-				Hop input2 = getInput().get(1);
-				if( input1.dimsKnown() && input2.dimsKnown() )
-				{
-					if( OptimizerUtils.isBinaryOpConditionalSparseSafe(op) && input2 instanceof LiteralOp ) {
-						double sp1 = (input1.getNnz()>0 && input1.getDataType()==DataType.MATRIX) ? OptimizerUtils.getSparsity(input1.getDim1(), input1.getDim2(), input1.getNnz()) : 1.0;
-						LiteralOp lit = (LiteralOp)input2;
-						sparsity = OptimizerUtils.getBinaryOpSparsityConditionalSparseSafe(sp1, op, lit);
-					}
-					else {
-						double sp1 = (input1.getNnz()>0 && input1.getDataType()==DataType.MATRIX) ? OptimizerUtils.getSparsity(input1.getDim1(), input1.getDim2(), input1.getNnz()) : 1.0;
-						double sp2 = (input2.getNnz()>0 && input2.getDataType()==DataType.MATRIX) ? OptimizerUtils.getSparsity(input2.getDim1(), input2.getDim2(), input2.getNnz()) : 1.0;
-						//sparsity estimates are conservative in terms of the worstcase behavior, however,
-						//for outer vector operations the average case is equivalent to the worst case.
-						sparsity = OptimizerUtils.getBinaryOpSparsity(sp1, sp2, op, !outer);
-					}
-				}
-			}
-			else //e.g., for append,pow or after inference
-				sparsity = OptimizerUtils.getSparsity(dim1, dim2, nnz);
-			
-			ret = OptimizerUtils.estimateSizeExactSparsity(dim1, dim2, sparsity);	
-		}
-		
-		
-		return ret;
-	}
-	
-	@Override
-	protected double computeIntermediateMemEstimate( long dim1, long dim2, long nnz )
-	{
-		double ret = 0;
-		if ( op == OpOp2.QUANTILE || op == OpOp2.IQM  || op == OpOp2.MEDIAN ) {
-			// buffer (=2*input_size) and output (=input_size) for SORT operation 
-			// getMemEstimate works for both cases of known dims and worst-case
-			ret = getInput().get(0).getMemEstimate() * 3; 
-		}
-		else if ( op == OpOp2.SOLVE ) {
-			// x=solve(A,b) relies on QR decomposition of A, which is done using Apache commons-math
-			// matrix of size same as the first input
-			double interOutput = OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); 
-			return interOutput;
-
-		}
-
-		return ret;
-	}
-	
-	@Override
-	protected long[] inferOutputCharacteristics( MemoTable memo )
-	{
-		long[] ret = null;
-		
-		MatrixCharacteristics[] mc = memo.getAllInputStats(getInput());
-		Hop input1 = getInput().get(0);
-		Hop input2 = getInput().get(1);		
-		DataType dt1 = input1.getDataType();
-		DataType dt2 = input2.getDataType();
-		
-		if( op== OpOp2.CBIND ) {
-			long ldim1 = -1, ldim2 = -1, lnnz = -1;
-			
-			if( mc[0].rowsKnown() || mc[1].rowsKnown() )
-				ldim1 = mc[0].rowsKnown() ? mc[0].getRows() : mc[1].getRows();
-			if( mc[0].colsKnown() && mc[1].colsKnown() )
-				ldim2 = mc[0].getCols()+mc[1].getCols();
-			if( mc[0].nnzKnown() && mc[1].nnzKnown() )
-				lnnz = mc[0].getNonZeros() + mc[1].getNonZeros();
-			
-			if( ldim1 > 0 || ldim2 > 0 || lnnz >= 0 )
-				return new long[]{ldim1, ldim2, lnnz};
-		}
-		else if( op== OpOp2.CBIND ) {
-			long ldim1 = -1, ldim2 = -1, lnnz = -1;
-			
-			if( mc[0].colsKnown() || mc[1].colsKnown() )
-				ldim2 = mc[0].colsKnown() ? mc[0].getCols() : mc[1].getCols();
-			if( mc[0].rowsKnown() && mc[1].rowsKnown() )
-				ldim1 = mc[0].getRows()+mc[1].getRows();
-			if( mc[0].nnzKnown() && mc[1].nnzKnown() )
-				lnnz = mc[0].getNonZeros() + mc[1].getNonZeros();
-			
-			if( ldim1 > 0 || ldim2 > 0 || lnnz >= 0 )
-				return new long[]{ldim1, ldim2, lnnz};
-		}
-		else if ( op == OpOp2.SOLVE ) {
-			// Output is a (likely to be dense) vector of size number of columns in the first input
-			if ( mc[0].getCols() > 0 ) {
-				ret = new long[]{ mc[0].getCols(), 1, mc[0].getCols()};
-			}
-		}
-		else //general case
-		{
-			long ldim1, ldim2;
-			double sp1 = 1.0, sp2 = 1.0;
-			
-			if( dt1 == DataType.MATRIX && dt2 == DataType.SCALAR && mc[0].dimsKnown() )
-			{
-				ldim1 = mc[0].getRows();
-				ldim2 = mc[0].getCols();
-				sp1 = (mc[0].getNonZeros()>0)?OptimizerUtils.getSparsity(ldim1, ldim2, mc[0].getNonZeros()):1.0;	
-			}
-			else if( dt1 == DataType.SCALAR && dt2 == DataType.MATRIX  ) 
-			{
-				ldim1 = mc[1].getRows();
-				ldim2 = mc[1].getCols();
-				sp2 = (mc[1].getNonZeros()>0)?OptimizerUtils.getSparsity(ldim1, ldim2, mc[1].getNonZeros()):1.0;
-			}
-			else //MATRIX - MATRIX 
-			{
-				//propagate if either input is known, rows need always be identical,
-				//for cols we need to be careful with regard to matrix-vector operations
-				if( outer ) //OUTER VECTOR OPERATION
-				{
-					ldim1 = mc[0].getRows();
-					ldim2 = mc[1].getCols();
-				}
-				else //GENERAL CASE
-				{
-					ldim1 = (mc[0].getRows()>0) ? mc[0].getRows() : 
-					        (mc[1].getRows()>1) ? mc[1].getRows() : -1;
-					ldim2 = (mc[0].getCols()>0) ? mc[0].getCols() : 
-						    (mc[1].getCols()>1) ? mc[1].getCols() : -1;
-				}
-				sp1 = (mc[0].getNonZeros()>0)?OptimizerUtils.getSparsity(ldim1, ldim2, mc[0].getNonZeros()):1.0;
-				sp2 = (mc[1].getNonZeros()>0)?OptimizerUtils.getSparsity(ldim1, ldim2, mc[1].getNonZeros()):1.0;
-			}
-			
-			if( ldim1>0 && ldim2>0 )
-			{
-				if( OptimizerUtils.isBinaryOpConditionalSparseSafe(op) && input2 instanceof LiteralOp ) {
-					long lnnz = (long) (ldim1*ldim2*OptimizerUtils.getBinaryOpSparsityConditionalSparseSafe(sp1, op,(LiteralOp)input2));
-					ret = new long[]{ldim1, ldim2, lnnz};	
-				}
-				else
-				{
-					//sparsity estimates are conservative in terms of the worstcase behavior, however,
-					//for outer vector operations the average case is equivalent to the worst case.
-					long lnnz = (long) (ldim1*ldim2*OptimizerUtils.getBinaryOpSparsity(sp1, sp2, op, !outer));
-					ret = new long[]{ldim1, ldim2, lnnz};
-				}
-			}
-		}
-
-		return ret;
-	}
-
-	@Override
-	public boolean allowsAllExecTypes()
-	{
-		return true;
-	}
-	
-	@Override
-	protected ExecType optFindExecType() throws HopsException {
-		
-		checkAndSetForcedPlatform();
-		
-		ExecType REMOTE = OptimizerUtils.isSparkExecutionMode() ? ExecType.SPARK : ExecType.MR;
-		DataType dt1 = getInput().get(0).getDataType();
-		DataType dt2 = getInput().get(1).getDataType();
-		
-		if( _etypeForced != null ) {		
-			_etype = _etypeForced;
-		}
-		else 
-		{
-			if ( OptimizerUtils.isMemoryBasedOptLevel() ) 
-			{
-				_etype = findExecTypeByMemEstimate();
-			}
-			else
-			{
-				_etype = null;
-				if ( dt1 == DataType.MATRIX && dt2 == DataType.MATRIX ) {
-					// choose CP if the dimensions of both inputs are below Hops.CPThreshold 
-					// OR if both are vectors
-					if ( (getInput().get(0).areDimsBelowThreshold() && getInput().get(1).areDimsBelowThreshold())
-							|| (getInput().get(0).isVector() && getInput().get(1).isVector()))
-					{
-						_etype = ExecType.CP;
-					}
-				}
-				else if ( dt1 == DataType.MATRIX && dt2 == DataType.SCALAR ) {
-					if ( getInput().get(0).areDimsBelowThreshold() || getInput().get(0).isVector() )
-					{
-						_etype = ExecType.CP;
-					}
-				}
-				else if ( dt1 == DataType.SCALAR && dt2 == DataType.MATRIX ) {
-					if ( getInput().get(1).areDimsBelowThreshold() || getInput().get(1).isVector() )
-					{
-						_etype = ExecType.CP;
-					}
-				}
-				else
-				{
-					_etype = ExecType.CP;
-				}
-				
-				//if no CP condition applied
-				if( _etype == null )
-					_etype = REMOTE;
-			}
-		
-			//check for valid CP dimensions and matrix size
-			checkAndSetInvalidCPDimsAndSize();
-		}
-			
-		//spark-specific decision refinement (execute unary scalar w/ spark input and 
-		//single parent also in spark because it's likely cheap and reduces intermediates)
-		if( _etype == ExecType.CP && _etypeForced != ExecType.CP
-			&& getDataType().isMatrix() && (dt1.isScalar() || dt2.isScalar()) 
-			&& supportsMatrixScalarOperations()                          //scalar operations
-			&& !(getInput().get(dt1.isScalar()?1:0) instanceof DataOp)   //input is not checkpoint
-			&& getInput().get(dt1.isScalar()?1:0).getParent().size()==1  //unary scalar is only parent
-			&& !HopRewriteUtils.isSingleBlock(getInput().get(dt1.isScalar()?1:0)) //single block triggered exec
-			&& getInput().get(dt1.isScalar()?1:0).optFindExecType() == ExecType.SPARK )					
-		{
-			//pull unary scalar operation into spark 
-			_etype = ExecType.SPARK;
-		}
-
-		//mark for recompile (forever)
-		if( OptimizerUtils.ALLOW_DYN_RECOMPILATION && !dimsKnown(true) && _etype==REMOTE ) {
-			setRequiresRecompile();
-		}
-		
-		//ensure cp exec type for single-node operations
-		if ( op == OpOp2.SOLVE ) {
-			_etype = ExecType.CP;
-		}
-		
-		return _etype;
-	}
-	
-	/**
-	 * General case binary append.
-	 * 
-	 * @param left
-	 * @param right
-	 * @param cbind 
-	 * @return
-	 * @throws HopsException 
-	 * @throws LopsException 
-	 */
-	public static Lop constructMRAppendLop( Hop left, Hop right, DataType dt, ValueType vt, boolean cbind, Hop current ) 
-		throws HopsException, LopsException
-	{
-		Lop ret = null;
-		
-		long m1_dim1 = left.getDim1();
-		long m1_dim2 = left.getDim2();		
-		long m2_dim1 = right.getDim1();
-		long m2_dim2 = right.getDim2();
-		long m3_dim1 = cbind ? m1_dim1 : ((m1_dim1>0 && m2_dim1>0) ? (m1_dim1 + m2_dim1) : -1); //output rows
-		long m3_dim2 = cbind ? ((m1_dim2>0 && m2_dim2>0) ? (m1_dim2 + m2_dim2) : -1): m1_dim2; //output cols
-		long m3_nnz = (left.getNnz()>0 && right.getNnz()>0) ? (left.getNnz() + right.getNnz()) : -1; //output nnz
-		long brlen = left.getRowsInBlock();
-		long bclen = left.getColsInBlock();
-		
-		Lop offset = createOffsetLop( left, cbind ); //offset 1st input
-		AppendMethod am = optFindAppendMethod(m1_dim1, m1_dim2, m2_dim1, m2_dim2, brlen, bclen, cbind);
-	
-		switch( am )
-		{
-			case MR_MAPPEND: //special case map-only append
-			{
-				boolean needPart = requiresPartitioning(right);
-				//pre partitioning 
-				Lop dcInput = right.constructLops();
-				if( needPart ) {
-					//right side in distributed cache
-					ExecType etPart = (OptimizerUtils.estimateSizeExactSparsity(right.getDim1(), right.getDim2(), OptimizerUtils.getSparsity(right.getDim1(), right.getDim2(), right.getNnz())) 
-					          < OptimizerUtils.getLocalMemBudget()) ? ExecType.CP : ExecType.MR; //operator selection
-					dcInput = new DataPartition(dcInput, DataType.MATRIX, ValueType.DOUBLE, etPart, PDataPartitionFormat.ROW_BLOCK_WISE_N);
-					dcInput.getOutputParameters().setDimensions(right.getDim1(), right.getDim2(), right.getRowsInBlock(), right.getColsInBlock(), right.getNnz());
-					dcInput.setAllPositions(right.getBeginLine(), right.getBeginColumn(), right.getEndLine(), right.getEndColumn());
-				}					
-				
-				AppendM appM = new AppendM(left.constructLops(), dcInput, offset, dt, vt, cbind, needPart, ExecType.MR);
-				appM.setAllPositions(current.getBeginLine(), current.getBeginColumn(), current.getEndLine(), current.getEndColumn());
-				appM.getOutputParameters().setDimensions(m3_dim1, m3_dim2, brlen, bclen, m3_nnz);
-				ret = appM;
-				break;
-			}
-			case MR_RAPPEND: //special case reduce append w/ one column block
-			{
-				//group
-				Group group1 = new Group(left.constructLops(), Group.OperationTypes.Sort, DataType.MATRIX, vt);
-				group1.getOutputParameters().setDimensions(m1_dim1, m1_dim2, brlen, bclen, left.getNnz());
-				group1.setAllPositions(left.getBeginLine(), left.getBeginColumn(), left.getEndLine(), left.getEndColumn());
-				
-				Group group2 = new Group(right.constructLops(), Group.OperationTypes.Sort, DataType.MATRIX, vt);
-				group1.getOutputParameters().setDimensions(m2_dim1, m2_dim2, brlen, bclen, right.getNnz());
-				group1.setAllPositions(right.getBeginLine(), right.getBeginColumn(), right.getEndLine(), right.getEndColumn());
-				
-				AppendR appR = new AppendR(group1, group2, dt, vt, cbind, ExecType.MR);
-				appR.getOutputParameters().setDimensions(m3_dim1, m3_dim2, brlen, bclen, m3_nnz);
-				appR.setAllPositions(current.getBeginLine(), current.getBeginColumn(), current.getEndLine(), current.getEndColumn());
-				
-				ret = appR;
-				break;
-			}	
-			case MR_GAPPEND:
-			{
-				//general case: map expand append, reduce aggregate
-				Lop offset2 = createOffsetLop( right, cbind ); //offset second input
-				
-				AppendG appG = new AppendG(left.constructLops(), right.constructLops(),	offset, offset2, dt, vt, cbind, ExecType.MR);
-				appG.getOutputParameters().setDimensions(m3_dim1, m3_dim2, brlen, bclen, m3_nnz);
-				appG.setAllPositions(current.getBeginLine(), current.getBeginColumn(), current.getEndLine(), current.getEndColumn());
-				
-				//group
-				Group group1 = new Group(appG, Group.OperationTypes.Sort, DataType.MATRIX, vt);
-				group1.getOutputParameters().setDimensions(m3_dim1, m3_dim2, brlen, bclen, m3_nnz);
-				group1.setAllPositions(current.getBeginLine(), current.getBeginColumn(), current.getEndLine(), current.getEndColumn());
-				
-				//aggregate
-				Aggregate agg1 = new Aggregate(group1, Aggregate.OperationTypes.Sum, DataType.MATRIX, vt, ExecType.MR);
-				agg1.getOutputParameters().setDimensions(m3_dim1, m3_dim2, brlen, bclen, m3_nnz);
-				agg1.setAllPositions(current.getBeginLine(), current.getBeginColumn(), current.getEndLine(), current.getEndColumn());
-				ret = agg1;
-				break;
-			}	
-			default:
-				throw new HopsException("Invalid MR append method: "+am);
-		}
-		
-		return ret;
-	}
-	
-	/**
-	 * 
-	 * @param left
-	 * @param right
-	 * @param dt
-	 * @param vt
-	 * @param current
-	 * @return
-	 * @throws HopsException
-	 * @throws LopsException
-	 */
-	public static Lop constructSPAppendLop( Hop left, Hop right, DataType dt, ValueType vt, boolean cbind, Hop current ) 
-		throws HopsException, LopsException
-	{
-		Lop ret = null;
-		
-		Lop offset = createOffsetLop( left, cbind ); //offset 1st input
-		AppendMethod am = optFindAppendSPMethod(left.getDim1(), left.getDim2(), right.getDim1(), right.getDim2(), 
-				right.getRowsInBlock(), right.getColsInBlock(), right.getNnz(), cbind);
-	
-		switch( am )
-		{
-			case MR_MAPPEND: //special case map-only append
-			{
-				ret = new AppendM(left.constructLops(), right.constructLops(), offset, 
-						current.getDataType(), current.getValueType(), cbind, false, ExecType.SPARK);
-				break;
-			}
-			case MR_RAPPEND: //special case reduce append w/ one column block
-			{
-				ret = new AppendR(left.constructLops(), right.constructLops(), 
-						current.getDataType(), current.getValueType(), cbind, ExecType.SPARK);
-				break;
-			}	
-			case MR_GAPPEND:
-			{
-				Lop offset2 = createOffsetLop( right, cbind ); //offset second input
-				ret = new AppendG(left.constructLops(), right.constructLops(), offset, offset2, 
-						current.getDataType(), current.getValueType(), cbind, ExecType.SPARK);
-				break;
-			}
-			case SP_GAlignedAppend:
-			{
-				ret = new AppendGAlignedSP(left.constructLops(), right.constructLops(), offset, 
-						current.getDataType(), current.getValueType(), cbind);
-				break;
-			}
-			default:
-				throw new HopsException("Invalid SP append method: "+am);
-		}
-		
-		ret.setAllPositions(current.getBeginLine(), current.getBeginColumn(), current.getEndLine(), current.getEndColumn());
-		
-		
-		return ret;
-	}
-	
-	/**
-	 * Special case tertiary append. Here, we also compile a MR_RAPPEND or MR_GAPPEND
-	 * 
-	 * @param left
-	 * @param right
-	 * @param dt
-	 * @param vt
-	 * @param current
-	 * @return
-	 * @throws HopsException
-	 * @throws LopsException
-	 */
-	public static Lop constructAppendLopChain( Hop left, Hop right1, Hop right2, DataType dt, ValueType vt, boolean cbind, Hop current ) 
-		throws HopsException, LopsException
-	{
-		long m1_dim1 = left.getDim1();
-		long m1_dim2 = left.getDim2();		
-		long m2_dim1 = right1.getDim1();
-		long m2_dim2 = right1.getDim2();
-		long m3_dim1 = right2.getDim1();
-		long m3_dim2 = right2.getDim2();		
-		long m41_dim2 = (m1_dim2>0 && m2_dim2>0) ? (m1_dim2 + m2_dim2) : -1; //output cols
-		long m41_nnz = (left.getNnz()>0 && right1.getNnz()>0) ? 
-				      (left.getNnz() + right1.getNnz()) : -1; //output nnz
-		long m42_dim2 = (m1_dim2>0 && m2_dim2>0 && m3_dim2>0) ? (m1_dim2 + m2_dim2 + m3_dim2) : -1; //output cols
-		long m42_nnz = (left.getNnz()>0 && right1.getNnz()>0 && right2.getNnz()>0) ? 
-				      (left.getNnz() + right1.getNnz()+ right2.getNnz()) : -1; //output nnz
-		long brlen = left.getRowsInBlock();
-		long bclen = left.getColsInBlock();
-		
-		//warn if assumption of blocksize>=3 does not hold
-		if( bclen < 3 )
-			throw new HopsException("MR_RAPPEND requires a blocksize of >= 3.");
-		
-		//case MR_RAPPEND:
-		//special case reduce append w/ one column block
-		
-		Group group1 = new Group(left.constructLops(), Group.OperationTypes.Sort, DataType.MATRIX, vt);
-		group1.getOutputParameters().setDimensions(m1_dim1, m1_dim2, brlen, bclen, left.getNnz());
-		group1.setAllPositions(left.getBeginLine(), left.getBeginColumn(), left.getEndLine(), left.getEndColumn());
-		
-		Group group2 = new Group(right1.constructLops(), Group.OperationTypes.Sort, DataType.MATRIX, vt);
-		group1.getOutputParameters().setDimensions(m2_dim1, m2_dim2, brlen, bclen, right1.getNnz());
-		group1.setAllPositions(right1.getBeginLine(), right1.getBeginColumn(), right1.getEndLine(), right1.getEndColumn());
-		
-		Group group3 = new Group(right2.constructLops(), Group.OperationTypes.Sort, DataType.MATRIX, vt);
-		group1.getOutputParameters().setDimensions(m3_dim1, m3_dim2, brlen, bclen, right2.getNnz());
-		group1.setAllPositions(right2.getBeginLine(), right2.getBeginColumn(), right2.getEndLine(), right2.getEndColumn());
-		
-		AppendR appR1 = new AppendR(group1, group2, dt, vt, cbind, ExecType.MR);
-		appR1.getOutputParameters().setDimensions(m1_dim1, m41_dim2, brlen, bclen, m41_nnz);
-		appR1.setAllPositions(current.getBeginLine(), current.getBeginColumn(), current.getEndLine(), current.getEndColumn());
-		
-		AppendR appR2 = new AppendR(appR1, group3, dt, vt, cbind, ExecType.MR);
-		appR1.getOutputParameters().setDimensions(m1_dim1, m42_dim2, brlen, bclen, m42_nnz);
-		appR1.setAllPositions(current.getBeginLine(), current.getBeginColumn(), current.getEndLine(), current.getEndColumn());
-	
-		return appR2;
-	}
-	
-	/**
-	 * Estimates the memory footprint of MapMult operation depending on which input is put into distributed cache.
-	 * This function is called by <code>optFindAppendMethod()</code> to decide the execution strategy, as well as by 
-	 * piggybacking to decide the number of Map-side instructions to put into a single GMR job. 
-	 */
-	public static double footprintInMapper( long m1_dim1, long m1_dim2, long m2_dim1, long m2_dim2, long m1_rpb, long m1_cpb ) {
-		double footprint = 0;
-		
-		// size of left input (matrix block)
-		footprint += OptimizerUtils.estimateSize(Math.min(m1_dim1, m1_rpb), Math.min(m1_dim2, m1_cpb));
-		
-		// size of right input (vector)
-		footprint += OptimizerUtils.estimateSize(m2_dim1, m2_dim2);
-		
-		// size of the output (only boundary block is merged)
-		footprint += OptimizerUtils.estimateSize(Math.min(m1_dim1, m1_rpb), Math.min(m1_dim2+m2_dim2, m1_cpb));
-		
-		return footprint;
-	}
-	
-	/**
-	 * 
-	 * @param m1_dim1
-	 * @param m1_dim2
-	 * @param m2_dim1
-	 * @param m2_dim2
-	 * @return
-	 */
-	private static AppendMethod optFindAppendMethod( long m1_dim1, long m1_dim2, long m2_dim1, long m2_dim2, long m1_rpb, long m1_cpb, boolean cbind )
-	{
-		if(FORCED_APPEND_METHOD != null) {
-			return FORCED_APPEND_METHOD;
-		}
-		
-		//check for best case (map-only)		
-		if(    m2_dim1 >= 1 && m2_dim2 >= 1   //rhs dims known 				
-			&& (cbind && m2_dim2 <= m1_cpb    //rhs is smaller than column block 
-			|| !cbind && m2_dim1 <= m1_rpb) ) //rhs is smaller than row block
-		{
-			double footprint = BinaryOp.footprintInMapper(m1_dim1, m1_dim2, m2_dim1, m2_dim2, m1_rpb, m1_cpb);
-			if ( footprint < APPEND_MEM_MULTIPLIER * OptimizerUtils.getRemoteMemBudgetMap(true) )
-				return AppendMethod.MR_MAPPEND;
-		}
-		
-		//check for in-block append (reduce-only)
-		if( cbind && m1_dim2 >= 1 && m2_dim2 >= 0  //column dims known
-			&& m1_dim2+m2_dim2 <= m1_cpb   //output has one column block
-		  ||!cbind && m1_dim1 >= 1 && m2_dim1 >= 0 //row dims known
-			&& m1_dim1+m2_dim1 <= m1_rpb ) //output has one column block
-		{
-			return AppendMethod.MR_RAPPEND;
-		}
-		
-		//general case (map and reduce)
-		return AppendMethod.MR_GAPPEND; 	
-	}
-	
-	private static AppendMethod optFindAppendSPMethod( long m1_dim1, long m1_dim2, long m2_dim1, long m2_dim2, long m1_rpb, long m1_cpb, long m2_nnz, boolean cbind )
-	{
-		if(FORCED_APPEND_METHOD != null) {
-			return FORCED_APPEND_METHOD;
-		}
-		
-		//check for best case (map-only w/o shuffle)		
-		if(    m2_dim1 >= 1 && m2_dim2 >= 1   //rhs dims known 				
-			&& (cbind && m2_dim2 <= m1_cpb    //rhs is smaller than column block 
-			|| !cbind && m2_dim1 <= m1_rpb) ) //rhs is smaller than row block
-		{
-			if( OptimizerUtils.checkSparkBroadcastMemoryBudget(m2_dim1, m2_dim2, m1_rpb, m1_cpb, m2_nnz) ) {
-				return AppendMethod.MR_MAPPEND;
-			}
-		}
-		
-		//check for in-block append (reduce-only)
-		if( cbind && m1_dim2 >= 1 && m2_dim2 >= 0  //column dims known
-			&& m1_dim2+m2_dim2 <= m1_cpb   //output has one column block
-		  ||!cbind && m1_dim1 >= 1 && m2_dim1 >= 0 //row dims known
-			&& m1_dim1+m2_dim1 <= m1_rpb ) //output has one column block
-		{
-			return AppendMethod.MR_RAPPEND;
-		}
-		
-		// if(mc1.getCols() % mc1.getColsPerBlock() == 0) {
-		if( cbind && m1_dim2 % m1_cpb == 0 
-		   || !cbind && m1_dim1 % m1_rpb == 0 ) 
-		{
-			return AppendMethod.SP_GAlignedAppend;
-		}
-		
-		//general case (map and reduce)
-		return AppendMethod.MR_GAPPEND; 	
-	}
-
-	/**
-	 * 
-	 * @param rightInput
-	 * @return
-	 */
-	private static boolean requiresPartitioning( Hop rightInput )
-	{
-		return (   rightInput.dimsKnown() //known input size 
-                && rightInput.getDim1()*rightInput.getDim2() > DistributedCacheInput.PARTITION_SIZE);
-	}
-	
-	/**
-	 * 
-	 * @param left
-	 * @param right
-	 * @return
-	 */
-	private static boolean requiresReplication( Hop left, Hop right )
-	{
-		return (!(left.getDim2()>=1 && right.getDim2()>=1) //cols of any input unknown 
-				||(left.getDim2() > 1 && right.getDim2()==1 && left.getDim2()>=left.getColsInBlock() ) //col MV and more than 1 block
-				||(left.getDim1() > 1 && right.getDim1()==1 && left.getDim1()>=left.getRowsInBlock() )); //row MV and more than 1 block
-	}
-
-	private MMBinaryMethod optFindMMBinaryMethodSpark(Hop left, Hop right) {
-		long m1_dim1 = left.getDim1();
-		long m1_dim2 = left.getDim2();
-		long m2_dim1 =  right.getDim1();
-		long m2_dim2 = right.getDim2();
-		long m1_rpb = left.getRowsInBlock();
-		long m1_cpb = left.getColsInBlock();
-		
-		//MR_BINARY_UAGG_CHAIN only applied if result is column/row vector of MV binary operation.
-		if( right instanceof AggUnaryOp && right.getInput().get(0) == left  //e.g., P / rowSums(P)
-			&& ((((AggUnaryOp) right).getDirection() == Direction.Row && m1_dim2 > 1 && m1_dim2 <= m1_cpb ) //single column block
-		    ||  (((AggUnaryOp) right).getDirection() == Direction.Col && m1_dim1 > 1 && m1_dim1 <= m1_rpb ))) //single row block
-		{
-			return MMBinaryMethod.MR_BINARY_UAGG_CHAIN;
-		}
-		
-		//MR_BINARY_M currently only applied for MV because potential partitioning job may cause additional latency for VV.
-		if( m2_dim1 >= 1 && m2_dim2 >= 1 // rhs dims known 
-			&& ((m1_dim2 >= 1 && m2_dim2 == 1)  //rhs column vector	
-			  ||(m1_dim1 >= 1 && m2_dim1 == 1 )) ) //rhs row vector
-		{
-			double size = OptimizerUtils.estimateSize(m2_dim1, m2_dim2);
-			if( OptimizerUtils.checkSparkBroadcastMemoryBudget(size) ) {
-				return MMBinaryMethod.MR_BINARY_M;
-			}
-		}
-		
-		//MR_BINARY_R as robust fallback strategy
-		return MMBinaryMethod.MR_BINARY_R;
-	}
-	
-	/**
-	 * 
-	 * @param left
-	 * @param right
-	 * @return
-	 */
-	private MMBinaryMethod optFindMMBinaryMethod(Hop left, Hop right)
-	{
-		long m1_dim1 = left.getDim1();
-		long m1_dim2 = left.getDim2();
-		long m2_dim1 =  right.getDim1();
-		long m2_dim2 = right.getDim2();
-		long m1_rpb = left.getRowsInBlock();
-		long m1_cpb = left.getColsInBlock();
-		
-		//MR_BINARY_OUTER only applied if outer vector operation 
-		if( outer ) {
-			return MMBinaryMethod.MR_BINARY_OUTER_R;
-		}
-		
-		//MR_BINARY_UAGG_CHAIN only applied if result is column/row vector of MV binary operation.
-		if( right instanceof AggUnaryOp && right.getInput().get(0) == left  //e.g., P / rowSums(P)
-			&& ((((AggUnaryOp) right).getDirection() == Direction.Row && m1_dim2 > 1 && m1_dim2 <= m1_cpb ) //single column block
-		    ||  (((AggUnaryOp) right).getDirection() == Direction.Col && m1_dim1 > 1 && m1_dim1 <= m1_rpb ))) //single row block
-		{
-			return MMBinaryMethod.MR_BINARY_UAGG_CHAIN;
-		}
-		
-		//MR_BINARY_M currently only applied for MV because potential partitioning job may cause additional latency for VV.
-		if( m2_dim1 >= 1 && m2_dim2 >= 1 // rhs dims known 
-			&& ((m1_dim2 >1 && m2_dim2 == 1)  //rhs column vector	
-			  ||(m1_dim1 >1 && m2_dim1 == 1 )) ) //rhs row vector
-		{
-			double footprint = BinaryOp.footprintInMapper(m1_dim1, m1_dim2, m2_dim1, m2_dim2, m1_rpb, m1_cpb);
-			if ( footprint < OptimizerUtils.getRemoteMemBudgetMap(true) )
-				return MMBinaryMethod.MR_BINARY_M;		
-		}
-		
-		//MR_BINARY_R as robust fallback strategy
-		return MMBinaryMethod.MR_BINARY_R;
-	}
-	
-	
-	
-	@Override
-	public void refreshSizeInformation()
-	{
-		Hop input1 = getInput().get(0);
-		Hop input2 = getInput().get(1);		
-		DataType dt1 = input1.getDataType();
-		DataType dt2 = input2.getDataType();
-		
-		if ( getDataType() == DataType.SCALAR ) 
-		{
-			//do nothing always known
-			setDim1(0);
-			setDim2(0);
-		}
-		else //MATRIX OUTPUT
-		{
-			//TODO quantile
-			if( op == OpOp2.CBIND )
-			{
-				setDim1( (input1.getDim1()>0) ? input1.getDim1() : input2.getDim1() );
-					
-				//ensure both columns are known, otherwise dangerous underestimation due to +(-1)
-				if( input1.getDim2()>0 && input2.getDim2()>0 )
-					setDim2( input1.getDim2() + input2.getDim2() );
-				//ensure both nnz are known, otherwise dangerous underestimation due to +(-1)
-				if( input1.getNnz()>0 && input2.getNnz()>0 )
-					setNnz( input1.getNnz() + input2.getNnz() );
-			}
-			else if( op == OpOp2.RBIND )
-			{
-				setDim2( (input1.getDim2()>0) ? input1.getDim2() : input2.getDim2() );
-					
-				//ensure both rows are known, otherwise dangerous underestimation due to +(-1)
-				if( input1.getDim1()>0 && input2.getDim1()>0 )
-					setDim1( input1.getDim1() + input2.getDim1() );
-				//ensure both nnz are known, otherwise dangerous underestimation due to +(-1)
-				if( input1.getNnz()>0 && input2.getNnz()>0 )
-					setNnz( input1.getNnz() + input2.getNnz() );
-			}
-			else if ( op == OpOp2.SOLVE )
-			{
-				//normally the second input would be of equal size as the output 
-				//however, since we use qr internally, it also supports squared first inputs
-				setDim1( input1.getDim2() );
-				setDim2( input2.getDim2() );
-			}
-			else //general case
-			{
-				long ldim1, ldim2, lnnz1 = -1;
-				
-				if( dt1 == DataType.MATRIX && dt2 == DataType.SCALAR )
-				{
-					ldim1 = input1.getDim1();
-					ldim2 = input1.getDim2();
-					lnnz1 = input1.getNnz();
-				}
-				else if( dt1 == DataType.SCALAR && dt2 == DataType.MATRIX  ) 
-				{
-					ldim1 = input2.getDim1();
-					ldim2 = input2.getDim2();
-				}
-				else //MATRIX - MATRIX 
-				{
-					//propagate if either input is known, rows need always be identical,
-					//for cols we need to be careful with regard to matrix-vector operations
-					if( outer ) //OUTER VECTOR OPERATION
-					{
-						ldim1 = input1.getDim1();
-						ldim2 = input2.getDim2();
-					}
-					else //GENERAL CASE
-					{
-						ldim1 = (input1.getDim1()>0) ? input1.getDim1()
-								: ((input2.getDim1()>1)?input2.getDim1():-1);
-						ldim2 = (input1.getDim2()>0) ? input1.getDim2() 
-								: ((input2.getDim2()>1)?input2.getDim2():-1);
-						lnnz1 = input1.getNnz();
-					}
-				}
-				
-				setDim1( ldim1 );
-				setDim2( ldim2 );
-				
-				//update nnz only if we can ensure exact results, 
-				//otherwise propagated via worst-case estimates
-				if(    op == OpOp2.POW 
-					|| (input2 instanceof LiteralOp && OptimizerUtils.isBinaryOpConditionalSparseSafeExact(op, (LiteralOp)input2)) ) 
-				{
-					setNnz( lnnz1 );
-				}
-			}
-		}	
-	}
-	
-	@Override
-	public Object clone() throws CloneNotSupportedException 
-	{
-		BinaryOp ret = new BinaryOp();	
-		
-		//copy generic attributes
-		ret.clone(this, false);
-		
-		//copy specific attributes
-		ret.op = op;
-		ret.outer = outer;
-		
-		return ret;
-	}
-	
-	@Override
-	public boolean compare( Hop that )
-	{
-		if( !(that instanceof BinaryOp) )
-			return false;
-		
-		BinaryOp that2 = (BinaryOp)that;
-		return (   op == that2.op
-				&& outer == that2.outer
-				&& getInput().get(0) == that2.getInput().get(0)
-				&& getInput().get(1) == that2.getInput().get(1));
-	}
-	
-	/**
-	 * 
-	 * @param op
-	 * @return
-	 */
-	public boolean supportsMatrixScalarOperations()
-	{
-		return (   op==OpOp2.PLUS    ||op==OpOp2.MINUS 
-		         ||op==OpOp2.MULT    ||op==OpOp2.DIV
-		         ||op==OpOp2.MODULUS ||op==OpOp2.INTDIV
-		         ||op==OpOp2.LESS    ||op==OpOp2.LESSEQUAL
-		         ||op==OpOp2.GREATER ||op==OpOp2.GREATEREQUAL
-		         ||op==OpOp2.EQUAL   ||op==OpOp2.NOTEQUAL
-		         ||op==OpOp2.MIN     ||op==OpOp2.MAX
-		         ||op==OpOp2.AND     ||op==OpOp2.OR
-		         ||op==OpOp2.LOG     ||op==OpOp2.POW );
-	}
-	
-	/**
-	 * 
-	 * @return
-	 */
-	public boolean isPPredOperation()
-	{
-		return (   op==OpOp2.LESS    ||op==OpOp2.LESSEQUAL
-		         ||op==OpOp2.GREATER ||op==OpOp2.GREATEREQUAL
-		         ||op==OpOp2.EQUAL   ||op==OpOp2.NOTEQUAL);
-	}
-}