/*
* 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.runtime.controlprogram;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.StringTokenizer;
import java.util.TreeMap;
import org.apache.sysml.api.DMLScript;
import org.apache.sysml.conf.ConfigurationManager;
import org.apache.sysml.lops.Lop;
import org.apache.sysml.lops.ReBlock;
import org.apache.sysml.lops.compile.JobType;
import org.apache.sysml.parser.DataIdentifier;
import org.apache.sysml.parser.Expression.DataType;
import org.apache.sysml.parser.Expression.ValueType;
import org.apache.sysml.parser.ExternalFunctionStatement;
import org.apache.sysml.runtime.DMLRuntimeException;
import org.apache.sysml.runtime.controlprogram.caching.CacheException;
import org.apache.sysml.runtime.controlprogram.caching.MatrixObject;
import org.apache.sysml.runtime.controlprogram.context.ExecutionContext;
import org.apache.sysml.runtime.controlprogram.parfor.util.IDSequence;
import org.apache.sysml.runtime.instructions.Instruction;
import org.apache.sysml.runtime.instructions.MRJobInstruction;
import org.apache.sysml.runtime.instructions.cp.BooleanObject;
import org.apache.sysml.runtime.instructions.cp.Data;
import org.apache.sysml.runtime.instructions.cp.DoubleObject;
import org.apache.sysml.runtime.instructions.cp.IntObject;
import org.apache.sysml.runtime.instructions.cp.ScalarObject;
import org.apache.sysml.runtime.instructions.cp.StringObject;
import org.apache.sysml.runtime.instructions.cp.VariableCPInstruction;
import org.apache.sysml.runtime.matrix.MatrixCharacteristics;
import org.apache.sysml.runtime.matrix.MatrixFormatMetaData;
import org.apache.sysml.runtime.matrix.data.InputInfo;
import org.apache.sysml.runtime.matrix.data.OutputInfo;
import org.apache.sysml.udf.ExternalFunctionInvocationInstruction;
import org.apache.sysml.udf.FunctionParameter;
import org.apache.sysml.udf.Matrix;
import org.apache.sysml.udf.PackageFunction;
import org.apache.sysml.udf.Scalar;
import org.apache.sysml.udf.FunctionParameter.FunctionParameterType;
import org.apache.sysml.udf.BinaryObject;
import org.apache.sysml.udf.Scalar.ScalarValueType;
public class ExternalFunctionProgramBlock extends FunctionProgramBlock
{
protected static final IDSequence _idSeq = new IDSequence();
protected String _baseDir = null;
ArrayList<Instruction> block2CellInst;
ArrayList<Instruction> cell2BlockInst;
// holds other key value parameters specified in function declaration
protected HashMap<String, String> _otherParams;
protected HashMap<String, String> _unblockedFileNames;
protected HashMap<String, String> _blockedFileNames;
protected long _runID = -1; //ID for block of statements
/**
* Constructor that also provides otherParams that are needed for external
* functions. Remaining parameters will just be passed to constructor for
* function program block.
*
* @param prog runtime program
* @param inputParams list of input data identifiers
* @param outputParams list of output data indentifiers
* @param baseDir base directory
* @throws DMLRuntimeException if DMLRuntimeException occurs
*/
protected ExternalFunctionProgramBlock(Program prog,
ArrayList<DataIdentifier> inputParams,
ArrayList<DataIdentifier> outputParams,
String baseDir) throws DMLRuntimeException
{
super(prog, inputParams, outputParams);
_baseDir = baseDir;
}
public ExternalFunctionProgramBlock(Program prog,
ArrayList<DataIdentifier> inputParams,
ArrayList<DataIdentifier> outputParams,
HashMap<String, String> otherParams,
String baseDir) throws DMLRuntimeException {
super(prog, inputParams, outputParams);
_baseDir = baseDir;
// copy other params
_otherParams = new HashMap<String, String>();
_otherParams.putAll(otherParams);
_unblockedFileNames = new HashMap<String, String>();
_blockedFileNames = new HashMap<String, String>();
// generate instructions
createInstructions();
}
private void changeTmpInput( long id, ExecutionContext ec )
{
ArrayList<DataIdentifier> inputParams = getInputParams();
block2CellInst = getBlock2CellInstructions(inputParams, _unblockedFileNames);
//post processing FUNCTION PATCH
for( String var : _skipInReblock )
{
Data dat = ec.getVariable(var);
if( dat instanceof MatrixObject )
_unblockedFileNames.put(var, ((MatrixObject)dat).getFileName());
}
}
/**
* It is necessary to change the local temporary files as only file handles are passed out
* by the external function program block.
*
*
* @param id this field does nothing
*/
private void changeTmpOutput( long id )
{
ArrayList<DataIdentifier> outputParams = getOutputParams();
cell2BlockInst = getCell2BlockInstructions(outputParams, _blockedFileNames);
}
public String getBaseDir()
{
return _baseDir;
}
/**
* Method to be invoked to execute instructions for the external function
* invocation
*/
@Override
public void execute(ExecutionContext ec)
throws DMLRuntimeException
{
_runID = _idSeq.getNextID();
changeTmpInput( _runID, ec );
changeTmpOutput( _runID );
// export input variables to HDFS (see RunMRJobs)
ArrayList<DataIdentifier> inputParams = null;
try {
inputParams = getInputParams();
for(DataIdentifier di : inputParams ) {
Data d = ec.getVariable(di.getName());
if ( d.getDataType() == DataType.MATRIX ) {
MatrixObject inputObj = (MatrixObject) d;
inputObj.exportData();
}
}
}
catch (Exception e){
throw new DMLRuntimeException(this.printBlockErrorLocation() + "Error exporting input variables to HDFS", e);
}
// convert block to cell
if( block2CellInst != null )
{
ArrayList<Instruction> tempInst = new ArrayList<Instruction>();
tempInst.addAll(block2CellInst);
try {
this.executeInstructions(tempInst,ec);
} catch (Exception e) {
throw new DMLRuntimeException(this.printBlockErrorLocation() + "Error executing "
+ tempInst.toString(), e);
}
}
// now execute package function
for (int i = 0; i < _inst.size(); i++)
{
try {
if (_inst.get(i) instanceof ExternalFunctionInvocationInstruction)
executeInstruction(ec, (ExternalFunctionInvocationInstruction) _inst.get(i));
}
catch(Exception e) {
throw new DMLRuntimeException(this.printBlockErrorLocation() +
"Failed to execute instruction " + _inst.get(i).toString(), e);
}
}
// convert cell to block
if( cell2BlockInst != null )
{
ArrayList<Instruction> tempInst = new ArrayList<Instruction>();
try {
tempInst.clear();
tempInst.addAll(cell2BlockInst);
this.executeInstructions(tempInst, ec);
} catch (Exception e) {
throw new DMLRuntimeException(this.printBlockErrorLocation() + "Failed to execute instruction "
+ cell2BlockInst.toString(), e);
}
}
// check return values
checkOutputParameters(ec.getVariables());
}
/**
* Given a list of parameters as data identifiers, returns a string
* representation.
*
* @param params list of data identifiers
* @return parameter string
*/
protected String getParameterString(ArrayList<DataIdentifier> params) {
String parameterString = "";
for (int i = 0; i < params.size(); i++) {
if (i != 0)
parameterString += ",";
DataIdentifier param = params.get(i);
if (param.getDataType() == DataType.MATRIX) {
String s = getDataTypeString(DataType.MATRIX) + ":";
s = s + "" + param.getName() + "" + ":";
s = s + getValueTypeString(param.getValueType());
parameterString += s;
continue;
}
if (param.getDataType() == DataType.SCALAR) {
String s = getDataTypeString(DataType.SCALAR) + ":";
s = s + "" + param.getName() + "" + ":";
s = s + getValueTypeString(param.getValueType());
parameterString += s;
continue;
}
if (param.getDataType() == DataType.OBJECT) {
String s = getDataTypeString(DataType.OBJECT) + ":";
s = s + "" + param.getName() + "" + ":";
parameterString += s;
continue;
}
}
return parameterString;
}
/**
* method to create instructions
*/
protected void createInstructions() {
_inst = new ArrayList<Instruction>();
// unblock all input matrices
block2CellInst = getBlock2CellInstructions(getInputParams(),_unblockedFileNames);
// assemble information provided through keyvalue pairs
String className = _otherParams.get(ExternalFunctionStatement.CLASS_NAME);
String configFile = _otherParams.get(ExternalFunctionStatement.CONFIG_FILE);
// class name cannot be null, however, configFile and execLocation can
// be null
if (className == null)
throw new RuntimeException(this.printBlockErrorLocation() + ExternalFunctionStatement.CLASS_NAME + " not provided!");
// assemble input and output param strings
String inputParameterString = getParameterString(getInputParams());
String outputParameterString = getParameterString(getOutputParams());
// generate instruction
ExternalFunctionInvocationInstruction einst = new ExternalFunctionInvocationInstruction(
className, configFile, inputParameterString,
outputParameterString);
if (getInputParams().size() > 0)
einst.setLocation(getInputParams().get(0));
else if (getOutputParams().size() > 0)
einst.setLocation(getOutputParams().get(0));
else
einst.setLocation(this._beginLine, this._endLine, this._beginColumn, this._endColumn);
_inst.add(einst);
// block output matrices
cell2BlockInst = getCell2BlockInstructions(getOutputParams(),_blockedFileNames);
}
/**
* Method to generate a reblock job to convert the cell representation into block representation
*
* @param outputParams list out output data identifiers
* @param blockedFileNames map of blocked file names
* @return list of instructions
*/
private ArrayList<Instruction> getCell2BlockInstructions(
ArrayList<DataIdentifier> outputParams,
HashMap<String, String> blockedFileNames) {
ArrayList<Instruction> c2binst = null;
//list of matrices that need to be reblocked
ArrayList<DataIdentifier> matrices = new ArrayList<DataIdentifier>();
ArrayList<DataIdentifier> matricesNoReblock = new ArrayList<DataIdentifier>();
// identify outputs that are matrices
for (int i = 0; i < outputParams.size(); i++) {
if (outputParams.get(i).getDataType() == DataType.MATRIX) {
if( _skipOutReblock.contains(outputParams.get(i).getName()) )
matricesNoReblock.add(outputParams.get(i));
else
matrices.add(outputParams.get(i));
}
}
if( !matrices.isEmpty() )
{
c2binst = new ArrayList<Instruction>();
MRJobInstruction reblkInst = new MRJobInstruction(JobType.REBLOCK);
TreeMap<Integer, ArrayList<String>> MRJobLineNumbers = null;
if(DMLScript.ENABLE_DEBUG_MODE) {
MRJobLineNumbers = new TreeMap<Integer, ArrayList<String>>();
}
ArrayList<String> inLabels = new ArrayList<String>();
ArrayList<String> outLabels = new ArrayList<String>();
String[] outputs = new String[matrices.size()];
byte[] resultIndex = new byte[matrices.size()];
String reblock = "";
String reblockStr = ""; //Keep a copy of a single MR reblock instruction
String scratchSpaceLoc = ConfigurationManager.getScratchSpace();
try {
// create a RBLK job that transforms each output matrix from cell to block
for (int i = 0; i < matrices.size(); i++) {
inLabels.add(matrices.get(i).getName());
outLabels.add(matrices.get(i).getName() + "_extFnOutput");
outputs[i] = scratchSpaceLoc +
Lop.FILE_SEPARATOR + Lop.PROCESS_PREFIX + DMLScript.getUUID() + Lop.FILE_SEPARATOR +
_otherParams.get(ExternalFunctionStatement.CLASS_NAME) + _runID + "_" + i + "Output";
blockedFileNames.put(matrices.get(i).getName(), outputs[i]);
resultIndex[i] = (byte) i; // (matrices.size()+i);
if (i > 0)
reblock += Lop.INSTRUCTION_DELIMITOR;
reblock += "MR" + ReBlock.OPERAND_DELIMITOR + "rblk" + ReBlock.OPERAND_DELIMITOR +
i + ReBlock.DATATYPE_PREFIX + matrices.get(i).getDataType() + ReBlock.VALUETYPE_PREFIX + matrices.get(i).getValueType() + ReBlock.OPERAND_DELIMITOR +
i + ReBlock.DATATYPE_PREFIX + matrices.get(i).getDataType() + ReBlock.VALUETYPE_PREFIX + matrices.get(i).getValueType() + ReBlock.OPERAND_DELIMITOR +
ConfigurationManager.getBlocksize() + ReBlock.OPERAND_DELIMITOR + ConfigurationManager.getBlocksize() + ReBlock.OPERAND_DELIMITOR + "true";
if(DMLScript.ENABLE_DEBUG_MODE) {
//Create a copy of reblock instruction but as a single instruction (FOR DEBUGGER)
reblockStr = "MR" + ReBlock.OPERAND_DELIMITOR + "rblk" + ReBlock.OPERAND_DELIMITOR +
i + ReBlock.DATATYPE_PREFIX + matrices.get(i).getDataType() + ReBlock.VALUETYPE_PREFIX + matrices.get(i).getValueType() + ReBlock.OPERAND_DELIMITOR +
i + ReBlock.DATATYPE_PREFIX + matrices.get(i).getDataType() + ReBlock.VALUETYPE_PREFIX + matrices.get(i).getValueType() + ReBlock.OPERAND_DELIMITOR +
ConfigurationManager.getBlocksize() + ReBlock.OPERAND_DELIMITOR + ConfigurationManager.getBlocksize() + ReBlock.OPERAND_DELIMITOR + "true";
//Set MR reblock instruction line number (FOR DEBUGGER)
if (!MRJobLineNumbers.containsKey(matrices.get(i).getBeginLine())) {
MRJobLineNumbers.put(matrices.get(i).getBeginLine(), new ArrayList<String>());
}
MRJobLineNumbers.get(matrices.get(i).getBeginLine()).add(reblockStr);
}
// create metadata instructions to populate symbol table
// with variables that hold blocked matrices
Instruction createInst = VariableCPInstruction.prepareCreateMatrixVariableInstruction(outLabels.get(i), outputs[i], false, OutputInfo.outputInfoToString(OutputInfo.BinaryBlockOutputInfo));
createInst.setLocation(matrices.get(i));
c2binst.add(createInst);
}
reblkInst.setReBlockInstructions(inLabels.toArray(new String[inLabels.size()]), "", reblock, "",
outLabels.toArray(new String[inLabels.size()]), resultIndex, 1, 1);
c2binst.add(reblkInst);
// generate instructions that rename the output variables of REBLOCK job
Instruction cpInst = null, rmInst = null;
for (int i = 0; i < matrices.size(); i++) {
cpInst = VariableCPInstruction.prepareCopyInstruction(outLabels.get(i), matrices.get(i).getName());
rmInst = VariableCPInstruction.prepareRemoveInstruction(outLabels.get(i));
cpInst.setLocation(matrices.get(i));
rmInst.setLocation(matrices.get(i));
c2binst.add(cpInst);
c2binst.add(rmInst);
//c2binst.add(CPInstructionParser.parseSingleInstruction("CP" + Lops.OPERAND_DELIMITOR + "cpvar"+Lops.OPERAND_DELIMITOR+ outLabels.get(i) + Lops.OPERAND_DELIMITOR + matrices.get(i).getName()));
}
} catch (Exception e) {
throw new RuntimeException(this.printBlockErrorLocation() + "error generating instructions", e);
}
//LOGGING instructions
if (LOG.isTraceEnabled()){
LOG.trace("\n--- Cell-2-Block Instructions ---");
for(Instruction i : c2binst) {
LOG.trace(i.toString());
}
LOG.trace("----------------------------------");
}
}
return c2binst; //null if no output matrices
}
/**
* Method to generate instructions to convert input matrices from block to
* cell. We generate a GMR job here.
*
* @param inputParams list of data identifiers
* @param unBlockedFileNames map of unblocked file names
* @return list of instructions
*/
private ArrayList<Instruction> getBlock2CellInstructions(
ArrayList<DataIdentifier> inputParams,
HashMap<String, String> unBlockedFileNames) {
ArrayList<Instruction> b2cinst = null;
//list of input matrices
ArrayList<DataIdentifier> matrices = new ArrayList<DataIdentifier>();
ArrayList<DataIdentifier> matricesNoReblock = new ArrayList<DataIdentifier>();
// find all inputs that are matrices
for (int i = 0; i < inputParams.size(); i++) {
if (inputParams.get(i).getDataType() == DataType.MATRIX) {
if( _skipInReblock.contains(inputParams.get(i).getName()) )
matricesNoReblock.add(inputParams.get(i));
else
matrices.add(inputParams.get(i));
}
}
if( !matrices.isEmpty() )
{
b2cinst = new ArrayList<Instruction>();
MRJobInstruction gmrInst = new MRJobInstruction(JobType.GMR);
TreeMap<Integer, ArrayList<String>> MRJobLineNumbers = null;
if(DMLScript.ENABLE_DEBUG_MODE) {
MRJobLineNumbers = new TreeMap<Integer, ArrayList<String>>();
}
String gmrStr="";
ArrayList<String> inLabels = new ArrayList<String>();
ArrayList<String> outLabels = new ArrayList<String>();
String[] outputs = new String[matrices.size()];
byte[] resultIndex = new byte[matrices.size()];
String scratchSpaceLoc = ConfigurationManager.getScratchSpace();
try {
// create a GMR job that transforms each of these matrices from block to cell
for (int i = 0; i < matrices.size(); i++) {
inLabels.add(matrices.get(i).getName());
outLabels.add(matrices.get(i).getName()+"_extFnInput");
resultIndex[i] = (byte) i; //(matrices.size()+i);
outputs[i] = scratchSpaceLoc +
Lop.FILE_SEPARATOR + Lop.PROCESS_PREFIX + DMLScript.getUUID() + Lop.FILE_SEPARATOR +
_otherParams.get(ExternalFunctionStatement.CLASS_NAME) + _runID + "_" + i + "Input";
unBlockedFileNames.put(matrices.get(i).getName(), outputs[i]);
if(DMLScript.ENABLE_DEBUG_MODE) {
//Create a dummy gmr instruction (FOR DEBUGGER)
gmrStr = "MR" + Lop.OPERAND_DELIMITOR + "gmr" + Lop.OPERAND_DELIMITOR +
i + Lop.DATATYPE_PREFIX + matrices.get(i).getDataType() + Lop.VALUETYPE_PREFIX + matrices.get(i).getValueType() + Lop.OPERAND_DELIMITOR +
i + Lop.DATATYPE_PREFIX + matrices.get(i).getDataType() + Lop.VALUETYPE_PREFIX + matrices.get(i).getValueType() + Lop.OPERAND_DELIMITOR +
ConfigurationManager.getBlocksize() + Lop.OPERAND_DELIMITOR + ConfigurationManager.getBlocksize();
//Set MR gmr instruction line number (FOR DEBUGGER)
if (!MRJobLineNumbers.containsKey(matrices.get(i).getBeginLine())) {
MRJobLineNumbers.put(matrices.get(i).getBeginLine(), new ArrayList<String>());
}
MRJobLineNumbers.get(matrices.get(i).getBeginLine()).add(gmrStr);
}
// create metadata instructions to populate symbol table
// with variables that hold unblocked matrices
Instruction createInst = VariableCPInstruction.prepareCreateMatrixVariableInstruction(outLabels.get(i), outputs[i], false, OutputInfo.outputInfoToString(OutputInfo.TextCellOutputInfo));
createInst.setLocation(matrices.get(i));
b2cinst.add(createInst);
}
// Finally, generate GMR instruction that performs block2cell conversion
gmrInst.setGMRInstructions(inLabels.toArray(new String[inLabels.size()]), "", "", "", "",
outLabels.toArray(new String[outLabels.size()]), resultIndex, 0, 1);
b2cinst.add(gmrInst);
// generate instructions that rename the output variables of GMR job
Instruction cpInst=null, rmInst=null;
for (int i = 0; i < matrices.size(); i++) {
cpInst = VariableCPInstruction.prepareCopyInstruction(outLabels.get(i), matrices.get(i).getName());
rmInst = VariableCPInstruction.prepareRemoveInstruction(outLabels.get(i));
cpInst.setLocation(matrices.get(i));
rmInst.setLocation(matrices.get(i));
b2cinst.add(cpInst);
b2cinst.add(rmInst);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
//LOG instructions
if (LOG.isTraceEnabled()){
LOG.trace("\n--- Block-2-Cell Instructions ---");
for(Instruction i : b2cinst) {
LOG.trace(i.toString());
}
LOG.trace("----------------------------------");
}
}
//BEGIN FUNCTION PATCH
if( !matricesNoReblock.isEmpty() )
{
for( int i=0; i<matricesNoReblock.size(); i++ )
{
String scratchSpaceLoc = ConfigurationManager.getScratchSpace();
String filename = scratchSpaceLoc +
Lop.FILE_SEPARATOR + Lop.PROCESS_PREFIX + DMLScript.getUUID() + Lop.FILE_SEPARATOR +
_otherParams.get(ExternalFunctionStatement.CLASS_NAME) + _runID + "_" + i + "Input";
unBlockedFileNames.put(matricesNoReblock.get(i).getName(), filename);
}
}
//END FUNCTION PATCH
return b2cinst; //null if no input matrices
}
/**
* Method to execute an external function invocation instruction.
*
* @param ec execution context
* @param inst external function invocation instructions
* @throws DMLRuntimeException if DMLRuntimeException occurs
*/
@SuppressWarnings("unchecked")
public void executeInstruction(ExecutionContext ec, ExternalFunctionInvocationInstruction inst)
throws DMLRuntimeException
{
String className = inst.getClassName();
String configFile = inst.getConfigFile();
if (className == null)
throw new DMLRuntimeException(this.printBlockErrorLocation() + "Class name can't be null");
// create instance of package function.
Object o;
try
{
Class<Instruction> cla = (Class<Instruction>) Class.forName(className);
o = cla.newInstance();
}
catch (Exception e)
{
throw new DMLRuntimeException(this.printBlockErrorLocation() + "Error generating package function object " ,e );
}
if (!(o instanceof PackageFunction))
throw new DMLRuntimeException(this.printBlockErrorLocation() + "Class is not of type PackageFunction");
PackageFunction func = (PackageFunction) o;
// add inputs to this package function based on input parameter
// and their mappings.
setupInputs(func, inst.getInputParams(), ec.getVariables());
func.setConfiguration(configFile);
func.setBaseDir(_baseDir);
//executes function
func.execute();
// verify output of function execution matches declaration
// and add outputs to variableMapping and Metadata
verifyAndAttachOutputs(ec, func, inst.getOutputParams());
}
/**
* Method to verify that function outputs match with declared outputs
*
* @param ec execution context
* @param returnFunc package function
* @param outputParams output parameters
* @throws DMLRuntimeException if DMLRuntimeException occurs
*/
protected void verifyAndAttachOutputs(ExecutionContext ec, PackageFunction returnFunc,
String outputParams) throws DMLRuntimeException {
ArrayList<String> outputs = getParameters(outputParams);
// make sure they are of equal size first
if (outputs.size() != returnFunc.getNumFunctionOutputs()) {
throw new DMLRuntimeException(
"Number of function outputs ("+returnFunc.getNumFunctionOutputs()+") " +
"does not match with declaration ("+outputs.size()+").");
}
// iterate over each output and verify that type matches
for (int i = 0; i < outputs.size(); i++) {
StringTokenizer tk = new StringTokenizer(outputs.get(i), ":");
ArrayList<String> tokens = new ArrayList<String>();
while (tk.hasMoreTokens()) {
tokens.add(tk.nextToken());
}
if (returnFunc.getFunctionOutput(i).getType() == FunctionParameterType.Matrix) {
Matrix m = (Matrix) returnFunc.getFunctionOutput(i);
if (!(tokens.get(0).equals(getFunctionParameterDataTypeString(FunctionParameterType.Matrix)))
|| !(tokens.get(2).equals(getMatrixValueTypeString(m.getValueType())))) {
throw new DMLRuntimeException(
"Function output '"+outputs.get(i)+"' does not match with declaration.");
}
// add result to variableMapping
String varName = tokens.get(1);
MatrixObject newVar = createOutputMatrixObject( m );
newVar.setVarName(varName);
//getVariables().put(varName, newVar); //put/override in local symbol table
ec.setVariable(varName, newVar);
continue;
}
if (returnFunc.getFunctionOutput(i).getType() == FunctionParameterType.Scalar) {
Scalar s = (Scalar) returnFunc.getFunctionOutput(i);
if (!tokens.get(0).equals(getFunctionParameterDataTypeString(FunctionParameterType.Scalar))
|| !tokens.get(2).equals(
getScalarValueTypeString(s.getScalarType()))) {
throw new DMLRuntimeException(
"Function output '"+outputs.get(i)+"' does not match with declaration.");
}
// allocate and set appropriate object based on type
ScalarObject scalarObject = null;
ScalarValueType type = s.getScalarType();
switch (type) {
case Integer:
scalarObject = new IntObject(tokens.get(1),
Long.parseLong(s.getValue()));
break;
case Double:
scalarObject = new DoubleObject(tokens.get(1),
Double.parseDouble(s.getValue()));
break;
case Boolean:
scalarObject = new BooleanObject(tokens.get(1),
Boolean.parseBoolean(s.getValue()));
break;
case Text:
scalarObject = new StringObject(tokens.get(1), s.getValue());
break;
default:
throw new DMLRuntimeException(
"Unknown scalar value type '"+type+"' of output '"+outputs.get(i)+"'.");
}
//this.getVariables().put(tokens.get(1), scalarObject);
ec.setVariable(tokens.get(1), scalarObject);
continue;
}
if (returnFunc.getFunctionOutput(i).getType() == FunctionParameterType.Object) {
if (!tokens.get(0).equals(getFunctionParameterDataTypeString(FunctionParameterType.Object))) {
throw new DMLRuntimeException(
"Function output '"+outputs.get(i)+"' does not match with declaration.");
}
throw new DMLRuntimeException(
"Object types not yet supported");
// continue;
}
throw new DMLRuntimeException(
"Unknown data type '"+returnFunc.getFunctionOutput(i).getType()+"' " +
"of output '"+outputs.get(i)+"'.");
}
}
protected MatrixObject createOutputMatrixObject( Matrix m )
throws CacheException
{
MatrixCharacteristics mc = new MatrixCharacteristics(m.getNumRows(),m.getNumCols(), ConfigurationManager.getBlocksize(), ConfigurationManager.getBlocksize());
MatrixFormatMetaData mfmd = new MatrixFormatMetaData(mc, OutputInfo.TextCellOutputInfo, InputInfo.TextCellInputInfo);
return new MatrixObject(ValueType.DOUBLE, m.getFilePath(), mfmd);
}
/**
* Method to get string representation of scalar value type
*
* @param scalarType scalar value type
* @return scalar value type string
*/
protected String getScalarValueTypeString(ScalarValueType scalarType) {
if (scalarType.equals(ScalarValueType.Text))
return "String";
else
return scalarType.toString();
}
/**
* Method to parse inputs, update labels, and add to package function.
*
* @param func package function
* @param inputParams input parameters
* @param variableMapping local variable map
*/
protected void setupInputs (PackageFunction func, String inputParams, LocalVariableMap variableMapping) {
ArrayList<String> inputs = getParameters(inputParams);
ArrayList<FunctionParameter> inputObjects = getInputObjects(inputs, variableMapping);
func.setNumFunctionInputs(inputObjects.size());
for (int i = 0; i < inputObjects.size(); i++)
func.setInput(inputObjects.get(i), i);
}
/**
* Method to convert string representation of input into function input
* object.
*
* @param inputs list of inputs
* @param variableMapping local variable map
* @return list of function parameters
*/
protected ArrayList<FunctionParameter> getInputObjects(ArrayList<String> inputs,
LocalVariableMap variableMapping) {
ArrayList<FunctionParameter> inputObjects = new ArrayList<FunctionParameter>();
for (int i = 0; i < inputs.size(); i++) {
ArrayList<String> tokens = new ArrayList<String>();
StringTokenizer tk = new StringTokenizer(inputs.get(i), ":");
while (tk.hasMoreTokens()) {
tokens.add(tk.nextToken());
}
if (tokens.get(0).equals("Matrix")) {
String varName = tokens.get(1);
MatrixObject mobj = (MatrixObject) variableMapping.get(varName);
MatrixCharacteristics mc = mobj.getMatrixCharacteristics();
Matrix m = new Matrix(mobj.getFileName(),
mc.getRows(), mc.getCols(),
getMatrixValueType(tokens.get(2)));
modifyInputMatrix(m, mobj);
inputObjects.add(m);
}
if (tokens.get(0).equals("Scalar")) {
String varName = tokens.get(1);
ScalarObject so = (ScalarObject) variableMapping.get(varName);
Scalar s = new Scalar(getScalarValueType(tokens.get(2)),
so.getStringValue());
inputObjects.add(s);
}
if (tokens.get(0).equals("Object")) {
String varName = tokens.get(1);
Object o = variableMapping.get(varName);
BinaryObject obj = new BinaryObject(o);
inputObjects.add(obj);
}
}
return inputObjects;
}
protected void modifyInputMatrix(Matrix m, MatrixObject mobj)
{
//do nothing, intended for extensions
}
/**
* Converts string representation of scalar value type to enum type
*
* @param string scalar value string
* @return scalar value type
*/
protected ScalarValueType getScalarValueType(String string) {
if (string.equals("String"))
return ScalarValueType.Text;
else
return ScalarValueType.valueOf(string);
}
/**
* Get string representation of matrix value type
*
* @param t matrix value type
* @return matrix value type as string
*/
protected String getMatrixValueTypeString(Matrix.ValueType t) {
return t.toString();
}
/**
* Converts string representation of matrix value type into enum type
*
* @param string matrix value type as string
* @return matrix value type
*/
protected Matrix.ValueType getMatrixValueType(String string) {
return Matrix.ValueType.valueOf(string);
}
/**
* Method to break the comma separated input parameters into an arraylist of
* parameters
*
* @param inputParams input parameters
* @return list of string inputs
*/
protected ArrayList<String> getParameters(String inputParams) {
ArrayList<String> inputs = new ArrayList<String>();
StringTokenizer tk = new StringTokenizer(inputParams, ",");
while (tk.hasMoreTokens()) {
inputs.add(tk.nextToken());
}
return inputs;
}
/**
* Get string representation for data type
*
* @param d data type
* @return string representation of data type
*/
protected String getDataTypeString(DataType d) {
if (d.equals(DataType.MATRIX))
return "Matrix";
if (d.equals(DataType.SCALAR))
return "Scalar";
if (d.equals(DataType.OBJECT))
return "Object";
throw new RuntimeException("Should never come here");
}
/**
* Method to get string representation of function parameter type.
*
* @param t function parameter type
* @return function parameter type as string
*/
protected String getFunctionParameterDataTypeString(FunctionParameterType t) {
return t.toString();
}
/**
* Get string representation of value type
*
* @param v value type
* @return value type string
*/
protected String getValueTypeString(ValueType v) {
if (v.equals(ValueType.DOUBLE))
return "Double";
if (v.equals(ValueType.INT))
return "Integer";
if (v.equals(ValueType.BOOLEAN))
return "Boolean";
if (v.equals(ValueType.STRING))
return "String";
throw new RuntimeException("Should never come here");
}
public HashMap<String,String> getOtherParams() {
return _otherParams;
}
public String printBlockErrorLocation(){
return "ERROR: Runtime error in external function program block generated from external function statement block between lines " + _beginLine + " and " + _endLine + " -- ";
}
/////////////////////////////////////////////////
// Extension for Global Data Flow Optimization
// by Mathias Peters
///////
//FUNCTION PATCH
private Collection<String> _skipInReblock = new HashSet<String>();
private Collection<String> _skipOutReblock = new HashSet<String>();
@Override
public ArrayList<Instruction> getInstructions()
{
ArrayList<Instruction> tmp = new ArrayList<Instruction>();
if( cell2BlockInst != null )
tmp.addAll(cell2BlockInst);
if( block2CellInst != null )
tmp.addAll(block2CellInst);
return tmp;
}
}