| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.sysml.hops; |
| |
| import java.util.ArrayList; |
| |
| import org.apache.sysml.lops.FunctionCallCP; |
| import org.apache.sysml.lops.Lop; |
| import org.apache.sysml.lops.LopsException; |
| import org.apache.sysml.lops.LopProperties.ExecType; |
| import org.apache.sysml.parser.Expression.DataType; |
| import org.apache.sysml.parser.Expression.ValueType; |
| import org.apache.sysml.runtime.controlprogram.Program; |
| import org.apache.sysml.runtime.controlprogram.parfor.opt.CostEstimatorHops; |
| |
| /** |
| * This FunctionOp represents the call to a DML-bodied or external function. |
| * |
| * Note: Currently, we support expressions in function arguments but no function calls |
| * in expressions. |
| */ |
| public class FunctionOp extends Hop |
| { |
| |
| public static String OPSTRING = "extfunct"; |
| |
| public enum FunctionType{ |
| DML, |
| EXTERNAL_MEM, |
| EXTERNAL_FILE, |
| MULTIRETURN_BUILTIN, |
| UNKNOWN |
| } |
| |
| private FunctionType _type = null; |
| private String _fnamespace = null; |
| private String _fname = null; |
| private String[] _outputs = null; |
| private ArrayList<Hop> _outputHops = null; |
| |
| private FunctionOp() { |
| //default constructor for clone |
| } |
| |
| public FunctionOp(FunctionType type, String fnamespace, String fname, ArrayList<Hop> finputs, String[] outputs, ArrayList<Hop> outputHops) { |
| this(type, fnamespace, fname, finputs, outputs); |
| _outputHops = outputHops; |
| } |
| |
| public FunctionOp(FunctionType type, String fnamespace, String fname, ArrayList<Hop> finputs, String[] outputs) |
| { |
| super(fnamespace + Program.KEY_DELIM + fname, DataType.UNKNOWN, ValueType.UNKNOWN ); |
| |
| _type = type; |
| _fnamespace = fnamespace; |
| _fname = fname; |
| _outputs = outputs; |
| |
| for( Hop in : finputs ) |
| { |
| getInput().add(in); |
| in.getParent().add(this); |
| } |
| } |
| |
| public String getFunctionNamespace() |
| { |
| return _fnamespace; |
| } |
| |
| public String getFunctionName() |
| { |
| return _fname; |
| } |
| |
| public void setFunctionName( String fname ) |
| { |
| _fname = fname; |
| } |
| |
| public ArrayList<Hop> getOutputs() { |
| return _outputHops; |
| } |
| |
| public String[] getOutputVariableNames() |
| { |
| return _outputs; |
| } |
| |
| public FunctionType getFunctionType() |
| { |
| return _type; |
| } |
| |
| @Override |
| public boolean allowsAllExecTypes() { |
| return false; |
| } |
| |
| @Override |
| public void computeMemEstimate( MemoTable memo ) |
| { |
| //overwrites default hops behavior |
| |
| if( _type == FunctionType.DML ) |
| _memEstimate = 1; //minimal mem estimate |
| else if( _type == FunctionType.EXTERNAL_MEM ) |
| _memEstimate = 2* getInputSize(); //in/out |
| else if( _type == FunctionType.EXTERNAL_FILE || _type == FunctionType.UNKNOWN ) |
| _memEstimate = CostEstimatorHops.DEFAULT_MEM_MR; |
| else if ( _type == FunctionType.MULTIRETURN_BUILTIN ) { |
| boolean outputDimsKnown = true; |
| for(Hop out : getOutputs()){ |
| outputDimsKnown &= out.dimsKnown(); |
| } |
| if( outputDimsKnown ) { |
| long lnnz = ((_nnz>=0)?_nnz:_dim1*_dim2); |
| _outputMemEstimate = computeOutputMemEstimate( _dim1, _dim2, lnnz ); |
| _processingMemEstimate = computeIntermediateMemEstimate(_dim1, _dim2, lnnz); |
| } |
| _memEstimate = getInputOutputSize(); |
| } |
| } |
| |
| @Override |
| protected double computeOutputMemEstimate( long dim1, long dim2, long nnz ) |
| { |
| if ( getFunctionType() != FunctionType.MULTIRETURN_BUILTIN ) |
| throw new RuntimeException("Invalid call of computeOutputMemEstimate in FunctionOp."); |
| else { |
| if ( getFunctionName().equalsIgnoreCase("qr") ) { |
| // upper-triangular and lower-triangular matrices |
| long outputH = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 0.5); |
| long outputR = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 0.5); |
| return outputH+outputR; |
| } |
| else if ( getFunctionName().equalsIgnoreCase("lu") ) { |
| // upper-triangular and lower-triangular matrices |
| long outputP = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0/getOutputs().get(1).getDim2()); |
| long outputL = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 0.5); |
| long outputU = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 0.5); |
| return outputL+outputU+outputP; |
| } |
| else if ( getFunctionName().equalsIgnoreCase("eigen") ) { |
| long outputVectors = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); |
| long outputValues = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), 1, 1.0); |
| return outputVectors+outputValues; |
| } |
| else |
| throw new RuntimeException("Invalid call of computeOutputMemEstimate in FunctionOp."); |
| } |
| } |
| |
| @Override |
| protected double computeIntermediateMemEstimate( long dim1, long dim2, long nnz ) |
| { |
| if ( getFunctionType() != FunctionType.MULTIRETURN_BUILTIN ) |
| throw new RuntimeException("Invalid call of computeIntermediateMemEstimate in FunctionOp."); |
| else { |
| if ( getFunctionName().equalsIgnoreCase("qr") ) { |
| // matrix of size same as the input |
| double interOutput = OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); |
| //System.out.println("QRInter " + interOutput/1024/1024); |
| return interOutput; |
| } |
| else if ( getFunctionName().equalsIgnoreCase("lu")) { |
| // 1D vector |
| double interOutput = OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); |
| //System.out.println("LUInter " + interOutput/1024/1024); |
| return interOutput; |
| } |
| else if ( getFunctionName().equalsIgnoreCase("eigen")) { |
| // One matrix of size original input and three 1D vectors (used to represent tridiagonal matrix) |
| double interOutput = OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0) |
| + 3*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); |
| //System.out.println("EigenInter " + interOutput/1024/1024); |
| return interOutput; |
| } |
| else |
| throw new RuntimeException("Invalid call of computeIntermediateMemEstimate in FunctionOp."); |
| } |
| } |
| |
| @Override |
| protected long[] inferOutputCharacteristics( MemoTable memo ) |
| { |
| throw new RuntimeException("Invalid call of inferOutputCharacteristics in FunctionOp."); |
| } |
| |
| @Override |
| public Lop constructLops() |
| throws HopsException, LopsException |
| { |
| //return already created lops |
| if( getLops() != null ) |
| return getLops(); |
| |
| ExecType et = optFindExecType(); |
| |
| //construct input lops (recursive) |
| ArrayList<Lop> tmp = new ArrayList<Lop>(); |
| for( Hop in : getInput() ) |
| tmp.add( in.constructLops() ); |
| |
| //construct function call |
| FunctionCallCP fcall = new FunctionCallCP( tmp, _fnamespace, _fname, _outputs, _outputHops, et ); |
| setLineNumbers( fcall ); |
| setLops( fcall ); |
| |
| //note: no reblock lop because outputs directly bound |
| |
| return getLops(); |
| } |
| |
| @Override |
| public String getOpString() |
| { |
| return OPSTRING; |
| } |
| |
| @Override |
| protected ExecType optFindExecType() |
| throws HopsException |
| { |
| checkAndSetForcedPlatform(); |
| |
| if ( getFunctionType() == FunctionType.MULTIRETURN_BUILTIN ) { |
| |
| // check if there is sufficient memory to execute this function |
| if( getFunctionName().equalsIgnoreCase("transformencode") ) { |
| _etype = ((_etypeForced==ExecType.SPARK |
| || (getMemEstimate() >= OptimizerUtils.getLocalMemBudget() |
| && OptimizerUtils.isSparkExecutionMode())) ? ExecType.SPARK : ExecType.CP); |
| } |
| else { |
| // Since the memory estimate is only conservative, do not throw |
| // exception if the estimated memory is larger than the budget |
| // Nevertheless, memory estimates these functions are useful for |
| // other purposes, such as compiling parfor |
| _etype = ExecType.CP; |
| } |
| } |
| else { |
| // the actual function call is always CP |
| _etype = ExecType.CP; |
| } |
| |
| return _etype; |
| } |
| |
| @Override |
| public void refreshSizeInformation() |
| { |
| //do nothing |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public Object clone() throws CloneNotSupportedException |
| { |
| FunctionOp ret = new FunctionOp(); |
| |
| //copy generic attributes |
| ret.clone(this, false); |
| |
| //copy specific attributes |
| ret._type = _type; |
| ret._fnamespace = _fnamespace; |
| ret._fname = _fname; |
| ret._outputs = _outputs.clone(); |
| if( _outputHops != null ) |
| ret._outputHops = (ArrayList<Hop>) _outputHops.clone(); |
| |
| return ret; |
| } |
| |
| @Override |
| public boolean compare( Hop that ) |
| { |
| return false; |
| } |
| } |