You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@systemml.apache.org by mb...@apache.org on 2017/04/06 22:59:58 UTC
[2/2] incubator-systemml git commit: [SYSTEMML-1288] New
multi-aggregate codegen template (compiler/runtime)
[SYSTEMML-1288] New multi-aggregate codegen template (compiler/runtime)
This patch introduces a new multi aggregate codegen template that allows
to fuse multiple full aggregation roots over shared inputs or common
subexpression into a single-pass operator.
Furthermore, this also includes a fix of the plan selection cost model
and enables the cost-based plan selector by default.
Project: http://git-wip-us.apache.org/repos/asf/incubator-systemml/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-systemml/commit/174bf7db
Tree: http://git-wip-us.apache.org/repos/asf/incubator-systemml/tree/174bf7db
Diff: http://git-wip-us.apache.org/repos/asf/incubator-systemml/diff/174bf7db
Branch: refs/heads/master
Commit: 174bf7db2158481ba054eb3f41b83738eb5f70d3
Parents: 5937df9
Author: Matthias Boehm <mb...@gmail.com>
Authored: Thu Apr 6 01:13:35 2017 -0700
Committer: Matthias Boehm <mb...@gmail.com>
Committed: Thu Apr 6 16:01:20 2017 -0700
----------------------------------------------------------------------
.../sysml/hops/codegen/SpoofCompiler.java | 42 ++-
.../sysml/hops/codegen/cplan/CNodeMultiAgg.java | 190 ++++++++++++++
.../sysml/hops/codegen/cplan/CNodeTpl.java | 35 ++-
.../hops/codegen/template/CPlanMemoTable.java | 6 +-
.../hops/codegen/template/PlanSelection.java | 3 +-
.../template/PlanSelectionFuseCostBased.java | 78 +++++-
.../hops/codegen/template/TemplateBase.java | 4 +-
.../hops/codegen/template/TemplateCell.java | 9 +-
.../hops/codegen/template/TemplateMultiAgg.java | 109 ++++++++
.../hops/codegen/template/TemplateUtils.java | 2 +
.../sysml/hops/rewrite/HopRewriteUtils.java | 11 +
.../runtime/codegen/SpoofMultiAggregate.java | 257 +++++++++++++++++++
.../instructions/spark/SpoofSPInstruction.java | 97 ++++++-
.../java/org/apache/sysml/utils/Explain.java | 11 +-
.../functions/codegen/AlgorithmLinregCG.java | 2 +-
.../functions/codegen/CellwiseTmplTest.java | 4 +-
.../functions/codegen/MultiAggTmplTest.java | 143 +++++++++++
.../functions/codegen/multiAggPattern1.R | 32 +++
.../functions/codegen/multiAggPattern1.dml | 28 ++
.../functions/codegen/multiAggPattern2.R | 32 +++
.../functions/codegen/multiAggPattern2.dml | 28 ++
.../functions/codegen/ZPackageSuite.java | 1 +
22 files changed, 1102 insertions(+), 22 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/main/java/org/apache/sysml/hops/codegen/SpoofCompiler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/hops/codegen/SpoofCompiler.java b/src/main/java/org/apache/sysml/hops/codegen/SpoofCompiler.java
index 179a06a..2e60732 100644
--- a/src/main/java/org/apache/sysml/hops/codegen/SpoofCompiler.java
+++ b/src/main/java/org/apache/sysml/hops/codegen/SpoofCompiler.java
@@ -36,6 +36,7 @@ import org.apache.sysml.api.DMLScript;
import org.apache.sysml.hops.codegen.cplan.CNode;
import org.apache.sysml.hops.codegen.cplan.CNodeCell;
import org.apache.sysml.hops.codegen.cplan.CNodeData;
+import org.apache.sysml.hops.codegen.cplan.CNodeMultiAgg;
import org.apache.sysml.hops.codegen.cplan.CNodeOuterProduct;
import org.apache.sysml.hops.codegen.cplan.CNodeTernary;
import org.apache.sysml.hops.codegen.cplan.CNodeTernary.TernaryType;
@@ -73,6 +74,7 @@ import org.apache.sysml.parser.LanguageException;
import org.apache.sysml.parser.StatementBlock;
import org.apache.sysml.parser.WhileStatement;
import org.apache.sysml.parser.WhileStatementBlock;
+import org.apache.sysml.parser.Expression.DataType;
import org.apache.sysml.runtime.DMLRuntimeException;
import org.apache.sysml.runtime.codegen.CodegenUtils;
import org.apache.sysml.runtime.codegen.SpoofCellwise.CellType;
@@ -91,7 +93,7 @@ public class SpoofCompiler
public static final boolean PRUNE_REDUNDANT_PLANS = true;
public static PlanCachePolicy PLAN_CACHE_POLICY = PlanCachePolicy.CSLH;
public static final int PLAN_CACHE_SIZE = 1024; //max 1K classes
- public static final PlanSelector PLAN_SEL_POLICY = PlanSelector.FUSE_ALL;
+ public static final PlanSelector PLAN_SEL_POLICY = PlanSelector.FUSE_COST_BASED;
public enum CompilerType {
JAVAC,
@@ -449,7 +451,7 @@ public class SpoofCompiler
throws DMLException
{
//top-down memoization of processed dag nodes
- if( hop.isVisited() )
+ if( hop == null || hop.isVisited() )
return;
//generate cplan for existing memo table entry
@@ -518,6 +520,17 @@ public class SpoofCompiler
hop.getRowsInBlock(), hop.getColsInBlock(), hop.getNnz());
if(tmpCNode instanceof CNodeOuterProduct && ((CNodeOuterProduct)tmpCNode).isTransposeOutput() )
hnew = HopRewriteUtils.createTranspose(hnew);
+ else if( tmpCNode instanceof CNodeMultiAgg ) {
+ ArrayList<Hop> roots = ((CNodeMultiAgg)tmpCNode).getRootNodes();
+ hnew.setDataType(DataType.MATRIX);
+ HopRewriteUtils.setOutputParameters(hnew, 1, roots.size(),
+ inHops[0].getRowsInBlock(), inHops[0].getColsInBlock(), -1);
+ //inject artificial right indexing operations for all parents of all nodes
+ for( int i=0; i<roots.size(); i++ ) {
+ Hop hnewi = HopRewriteUtils.createScalarIndexing(hnew, 1, i+1);
+ HopRewriteUtils.rewireAllParentChildReferences(roots.get(i), hnewi);
+ }
+ }
else if( tmpCNode instanceof CNodeCell && ((CNodeCell)tmpCNode).requiredCastDtm() ) {
HopRewriteUtils.setOutputParametersForScalar(hnew);
hnew = HopRewriteUtils.createUnary(hnew, OpOp1.CAST_AS_MATRIX);
@@ -550,7 +563,11 @@ public class SpoofCompiler
//collect cplan leaf node names
HashSet<Long> leafs = new HashSet<Long>();
- rCollectLeafIDs(tpl.getOutput(), leafs);
+ if( tpl instanceof CNodeMultiAgg )
+ for( CNode out : ((CNodeMultiAgg)tpl).getOutputs() )
+ rCollectLeafIDs(out, leafs);
+ else
+ rCollectLeafIDs(tpl.getOutput(), leafs);
//create clean cplan w/ minimal inputs
if( inHops.length == leafs.size() )
@@ -571,12 +588,29 @@ public class SpoofCompiler
CNodeData in1 = (CNodeData)tpl.getInput().get(0);
rFindAndRemoveLookup(tpl.getOutput(), in1);
}
+ else if( tpl instanceof CNodeMultiAgg ) {
+ CNodeData in1 = (CNodeData)tpl.getInput().get(0);
+ for( CNode output : ((CNodeMultiAgg)tpl).getOutputs() )
+ rFindAndRemoveLookup(output, in1);
+ }
//remove invalid plans with column indexing on main input
if( tpl instanceof CNodeCell ) {
CNodeData in1 = (CNodeData)tpl.getInput().get(0);
- if( rHasLookupRC1(tpl.getOutput(), in1) )
+ if( rHasLookupRC1(tpl.getOutput(), in1) ) {
cplans2.remove(e.getKey());
+ if( LOG.isTraceEnabled() )
+ LOG.trace("Removed cplan due to invalid rc1 indexing on main input.");
+ }
+ }
+ else if( tpl instanceof CNodeMultiAgg ) {
+ CNodeData in1 = (CNodeData)tpl.getInput().get(0);
+ for( CNode output : ((CNodeMultiAgg)tpl).getOutputs() )
+ if( rHasLookupRC1(output, in1) ) {
+ cplans2.remove(e.getKey());
+ if( LOG.isTraceEnabled() )
+ LOG.trace("Removed cplan due to invalid rc1 indexing on main input.");
+ }
}
//remove cplan w/ single op and w/o agg
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/main/java/org/apache/sysml/hops/codegen/cplan/CNodeMultiAgg.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/hops/codegen/cplan/CNodeMultiAgg.java b/src/main/java/org/apache/sysml/hops/codegen/cplan/CNodeMultiAgg.java
new file mode 100644
index 0000000..7ec07a6
--- /dev/null
+++ b/src/main/java/org/apache/sysml/hops/codegen/cplan/CNodeMultiAgg.java
@@ -0,0 +1,190 @@
+/*
+ * 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.codegen.cplan;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.sysml.hops.Hop;
+import org.apache.sysml.hops.Hop.AggOp;
+import org.apache.sysml.hops.codegen.SpoofFusedOp.SpoofOutputDimsType;
+
+public class CNodeMultiAgg extends CNodeTpl
+{
+ private static final String TEMPLATE =
+ "package codegen;\n"
+ + "import org.apache.sysml.runtime.codegen.LibSpoofPrimitives;\n"
+ + "import org.apache.sysml.runtime.codegen.SpoofMultiAggregate;\n"
+ + "import org.apache.sysml.runtime.codegen.SpoofCellwise;\n"
+ + "import org.apache.sysml.runtime.codegen.SpoofCellwise.AggOp;\n"
+ + "\n"
+ + "public final class %TMP% extends SpoofMultiAggregate { \n"
+ + " public %TMP%() {\n"
+ + " super(%AGG_OP%);\n"
+ + " }\n"
+ + " protected void genexec( double a, double[][] b, double[] scalars, double[] c, "
+ + "int m, int n, int rowIndex, int colIndex) { \n"
+ + "%BODY_dense%"
+ + " }\n"
+ + "}\n";
+ private static final String TEMPLATE_OUT_SUM = " c[%IX%] += %IN%;\n";
+ private static final String TEMPLATE_OUT_SUMSQ = " c[%IX%] += %IN% * %IN%;\n";
+ private static final String TEMPLATE_OUT_MIN = " c[%IX%] = Math.min(c[%IX%], %IN%);\n";
+ private static final String TEMPLATE_OUT_MAX = " c[%IX%] = Math.max(c[%IX%], %IN%);\n";
+
+ private ArrayList<CNode> _outputs = null;
+ private ArrayList<AggOp> _aggOps = null;
+ private ArrayList<Hop> _roots = null;
+
+ public CNodeMultiAgg(ArrayList<CNode> inputs, ArrayList<CNode> outputs) {
+ super(inputs, null);
+ _outputs = outputs;
+ }
+
+ public ArrayList<CNode> getOutputs() {
+ return _outputs;
+ }
+
+ @Override
+ public void resetVisitStatusOutputs() {
+ for( CNode output : _outputs )
+ output.resetVisitStatus();
+ }
+
+ public void setAggOps(ArrayList<AggOp> aggOps) {
+ _aggOps = aggOps;
+ _hash = 0;
+ }
+
+ public ArrayList<AggOp> getAggOps() {
+ return _aggOps;
+ }
+
+ public void setRootNodes(ArrayList<Hop> roots) {
+ _roots = roots;
+ }
+
+ public ArrayList<Hop> getRootNodes() {
+ return _roots;
+ }
+
+ @Override
+ public String codegen(boolean sparse) {
+ // note: ignore sparse flag, generate both
+ String tmp = TEMPLATE;
+
+ //rename inputs
+ rReplaceDataNode(_outputs, _inputs.get(0), "a"); // input matrix
+ renameInputs(_outputs, _inputs, 1);
+
+ //generate dense/sparse bodies
+ StringBuilder sb = new StringBuilder();
+ for( CNode out : _outputs )
+ sb.append(out.codegen(false));
+ for( CNode out : _outputs )
+ out.resetGenerated();
+
+ //append output assignments
+ for( int i=0; i<_outputs.size(); i++ ) {
+ CNode out = _outputs.get(i);
+ String tmpOut = getAggTemplate(i);
+ tmpOut = tmpOut.replace("%IN%", out.getVarname());
+ tmpOut = tmpOut.replace("%IX%", String.valueOf(i));
+ sb.append(tmpOut);
+ }
+
+ //replace class name and body
+ tmp = tmp.replaceAll("%TMP%", createVarname());
+ tmp = tmp.replaceAll("%BODY_dense%", sb.toString());
+
+ //replace meta data information
+ String aggList = "";
+ for( AggOp aggOp : _aggOps ) {
+ aggList += !aggList.isEmpty() ? "," : "";
+ aggList += "AggOp."+aggOp.name();
+ }
+ tmp = tmp.replaceAll("%AGG_OP%", aggList);
+
+ return tmp;
+ }
+
+ @Override
+ public void setOutputDims() {
+
+ }
+
+ @Override
+ public SpoofOutputDimsType getOutputDimType() {
+ return SpoofOutputDimsType.COLUMN_DIMS_COLS; //row vector
+ }
+
+ @Override
+ public CNodeTpl clone() {
+ CNodeMultiAgg ret = new CNodeMultiAgg(_inputs, _outputs);
+ ret.setAggOps(getAggOps());
+ return ret;
+ }
+
+ @Override
+ public int hashCode() {
+ if( _hash == 0 ) {
+ int[] tmp = new int[2*_outputs.size()+1];
+ tmp[0] = super.hashCode();
+ for( int i=0; i<_outputs.size(); i++ ) {
+ tmp[1+2*i] = _outputs.get(i).hashCode();
+ tmp[1+2*i+1] = _aggOps.get(i).hashCode();
+ }
+ _hash = Arrays.hashCode(tmp);
+ }
+ return _hash;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if(!(o instanceof CNodeMultiAgg))
+ return false;
+ CNodeMultiAgg that = (CNodeMultiAgg)o;
+ return super.equals(o)
+ && CollectionUtils.isEqualCollection(_aggOps, that._aggOps)
+ && equalInputReferences(
+ _outputs, that._outputs, _inputs, that._inputs);
+ }
+
+ @Override
+ public String getTemplateInfo() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("SPOOF MULTIAGG [aggOps=");
+ sb.append(Arrays.toString(_aggOps.toArray(new AggOp[0])));
+ sb.append("]");
+ return sb.toString();
+ }
+
+ private String getAggTemplate(int pos) {
+ switch( _aggOps.get(pos) ) {
+ case SUM: return TEMPLATE_OUT_SUM;
+ case SUM_SQ: return TEMPLATE_OUT_SUMSQ;
+ case MIN: return TEMPLATE_OUT_MIN;
+ case MAX: return TEMPLATE_OUT_MAX;
+ default:
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/main/java/org/apache/sysml/hops/codegen/cplan/CNodeTpl.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/hops/codegen/cplan/CNodeTpl.java b/src/main/java/org/apache/sysml/hops/codegen/cplan/CNodeTpl.java
index 7d0ae8d..e6da944 100644
--- a/src/main/java/org/apache/sysml/hops/codegen/cplan/CNodeTpl.java
+++ b/src/main/java/org/apache/sysml/hops/codegen/cplan/CNodeTpl.java
@@ -20,8 +20,10 @@
package org.apache.sysml.hops.codegen.cplan;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import org.apache.sysml.hops.codegen.SpoofFusedOp.SpoofOutputDimsType;
import org.apache.sysml.hops.codegen.cplan.CNodeUnary.UnaryType;
@@ -62,6 +64,10 @@ public abstract class CNodeTpl extends CNode implements Cloneable
return ret;
}
+ public void resetVisitStatusOutputs() {
+ getOutput().resetVisitStatus();
+ }
+
public String codegen() {
return codegen(false);
}
@@ -73,6 +79,10 @@ public abstract class CNodeTpl extends CNode implements Cloneable
public abstract String getTemplateInfo();
protected void renameInputs(ArrayList<CNode> inputs, int startIndex) {
+ renameInputs(Collections.singletonList(_output), inputs, startIndex);
+ }
+
+ protected void renameInputs(List<CNode> outputs, ArrayList<CNode> inputs, int startIndex) {
//create map of hopID to data nodes with new names, used for CSE
HashMap<Long, CNode> nodes = new HashMap<Long, CNode>();
for(int i=startIndex, sPos=0, mPos=0; i < inputs.size(); i++) {
@@ -87,7 +97,8 @@ public abstract class CNodeTpl extends CNode implements Cloneable
}
//single pass to replace all names
- rReplaceDataNode(_output, nodes, new HashMap<Long, CNode>());
+ for( CNode output : outputs )
+ rReplaceDataNode(output, nodes, new HashMap<Long, CNode>());
}
protected void rReplaceDataNode( CNode root, CNode input, String newName ) {
@@ -102,6 +113,19 @@ public abstract class CNodeTpl extends CNode implements Cloneable
rReplaceDataNode(root, names, new HashMap<Long,CNode>());
}
+ protected void rReplaceDataNode( ArrayList<CNode> roots, CNode input, String newName ) {
+ if( !(input instanceof CNodeData) )
+ return;
+
+ //create temporary name mapping
+ HashMap<Long, CNode> names = new HashMap<Long, CNode>();
+ CNodeData tmp = (CNodeData)input;
+ names.put(tmp.getHopID(), new CNodeData(tmp, newName));
+
+ for( CNode root : roots )
+ rReplaceDataNode(root, names, new HashMap<Long,CNode>());
+ }
+
/**
* Recursively searches for data nodes and replaces them if found.
*
@@ -216,7 +240,7 @@ public abstract class CNodeTpl extends CNode implements Cloneable
}
protected static boolean equalInputReferences(CNode current1, CNode current2, ArrayList<CNode> input1, ArrayList<CNode> input2) {
- boolean ret = true;
+ boolean ret = (input1.size() == input2.size());
//process childs recursively
for( int i=0; ret && i<current1.getInput().size(); i++ )
@@ -231,6 +255,13 @@ public abstract class CNodeTpl extends CNode implements Cloneable
return ret;
}
+ protected static boolean equalInputReferences(ArrayList<CNode> current1, ArrayList<CNode> current2, ArrayList<CNode> input1, ArrayList<CNode> input2) {
+ boolean ret = (current1.size() == current2.size());
+ for( int i=0; ret && i<current1.size(); i++ )
+ ret &= equalInputReferences(current1.get(i), current2.get(i), input1, input2);
+ return ret;
+ }
+
private static int indexOf(ArrayList<CNode> inputs, CNodeData probe) {
for( int i=0; i<inputs.size(); i++ ) {
CNodeData cd = ((CNodeData)inputs.get(i));
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/main/java/org/apache/sysml/hops/codegen/template/CPlanMemoTable.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/hops/codegen/template/CPlanMemoTable.java b/src/main/java/org/apache/sysml/hops/codegen/template/CPlanMemoTable.java
index 1a82f7d..3aa94d5 100644
--- a/src/main/java/org/apache/sysml/hops/codegen/template/CPlanMemoTable.java
+++ b/src/main/java/org/apache/sysml/hops/codegen/template/CPlanMemoTable.java
@@ -80,10 +80,14 @@ public class CPlanMemoTable
}
public void add(Hop hop, TemplateType type, long in1, long in2, long in3) {
+ add(hop, new MemoTableEntry(type, in1, in2, in3));
+ }
+
+ public void add(Hop hop, MemoTableEntry me) {
_hopRefs.put(hop.getHopID(), hop);
if( !_plans.containsKey(hop.getHopID()) )
_plans.put(hop.getHopID(), new ArrayList<MemoTableEntry>());
- _plans.get(hop.getHopID()).add(new MemoTableEntry(type, in1, in2, in3));
+ _plans.get(hop.getHopID()).add(me);
}
public void addAll(Hop hop, MemoTableEntrySet P) {
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/main/java/org/apache/sysml/hops/codegen/template/PlanSelection.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/hops/codegen/template/PlanSelection.java b/src/main/java/org/apache/sysml/hops/codegen/template/PlanSelection.java
index e7ae824..142040b 100644
--- a/src/main/java/org/apache/sysml/hops/codegen/template/PlanSelection.java
+++ b/src/main/java/org/apache/sysml/hops/codegen/template/PlanSelection.java
@@ -59,7 +59,8 @@ public abstract class PlanSelection
return (me.type == TemplateType.OuterProdTpl
&& (me.closed || HopRewriteUtils.isBinaryMatrixMatrixOperation(hop)))
|| (me.type == TemplateType.RowAggTpl && me.closed)
- || (me.type == TemplateType.CellTpl);
+ || (me.type == TemplateType.CellTpl)
+ || (me.type == TemplateType.MultiAggTpl);
}
protected void addBestPlan(long hopID, MemoTableEntry me) {
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/main/java/org/apache/sysml/hops/codegen/template/PlanSelectionFuseCostBased.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/hops/codegen/template/PlanSelectionFuseCostBased.java b/src/main/java/org/apache/sysml/hops/codegen/template/PlanSelectionFuseCostBased.java
index 47717c2..50d6ff1 100644
--- a/src/main/java/org/apache/sysml/hops/codegen/template/PlanSelectionFuseCostBased.java
+++ b/src/main/java/org/apache/sysml/hops/codegen/template/PlanSelectionFuseCostBased.java
@@ -35,6 +35,7 @@ import org.apache.sysml.hops.AggBinaryOp;
import org.apache.sysml.hops.AggUnaryOp;
import org.apache.sysml.hops.BinaryOp;
import org.apache.sysml.hops.Hop;
+import org.apache.sysml.hops.Hop.Direction;
import org.apache.sysml.hops.IndexingOp;
import org.apache.sysml.hops.ParameterizedBuiltinOp;
import org.apache.sysml.hops.ReorgOp;
@@ -81,7 +82,10 @@ public class PlanSelectionFuseCostBased extends PlanSelection
if( LOG.isTraceEnabled() )
LOG.trace("Partition materialization points: "+Arrays.toString(M.toArray(new Long[0])));
- //step 3: plan enumeration and plan selection
+ //step 3: create composite templates entries
+ createAndAddMultiAggPlans(memo, partition, R);
+
+ //step 4: plan enumeration and plan selection
selectPlans(memo, partition, R, M);
}
@@ -213,6 +217,74 @@ public class PlanSelectionFuseCostBased extends PlanSelection
&& partition.contains(hop.getHopID());
}
+ private static void createAndAddMultiAggPlans(CPlanMemoTable memo, HashSet<Long> partition, HashSet<Long> R)
+ {
+ //create index of plans that reference full aggregates to avoid circular dependencies
+ HashSet<Long> refHops = new HashSet<Long>();
+ for( Entry<Long, List<MemoTableEntry>> e : memo._plans.entrySet() )
+ if( !e.getValue().isEmpty() ) {
+ Hop hop = memo._hopRefs.get(e.getKey());
+ for( Hop c : hop.getInput() )
+ refHops.add(c.getHopID());
+ }
+
+ //find all full aggregations (the fact that they are in the same partition guarantees
+ //that they also have common subexpressions, also full aggregations are by def root nodes)
+ ArrayList<Long> fullAggs = new ArrayList<Long>();
+ for( Long hopID : R ) {
+ Hop root = memo._hopRefs.get(hopID);
+ if( !refHops.contains(hopID) && root instanceof AggUnaryOp
+ && ((AggUnaryOp)root).getDirection()==Direction.RowCol)
+ fullAggs.add(hopID);
+ }
+ if( LOG.isTraceEnabled() ) {
+ LOG.trace("Found ua(RC) aggregations: " +
+ Arrays.toString(fullAggs.toArray(new Long[0])));
+ }
+
+ //construct and add multiagg template plans (w/ max 3 aggregations)
+ for( int i=0; i<fullAggs.size(); i+=3 ) {
+ int ito = Math.min(i+3, fullAggs.size());
+ if( ito-i >= 2 ) {
+ MemoTableEntry me = new MemoTableEntry(TemplateType.MultiAggTpl,
+ fullAggs.get(i), fullAggs.get(i+1), ((ito-i)==3)?fullAggs.get(i+2):-1);
+ if( isValidMultiAggregate(memo, me) ) {
+ for( int j=i; j<ito; j++ ) {
+ memo.add(memo._hopRefs.get(fullAggs.get(j)), me);
+ if( LOG.isTraceEnabled() )
+ LOG.trace("Added multiagg plan: "+fullAggs.get(j)+" "+me);
+ }
+ }
+ else if( LOG.isTraceEnabled() ) {
+ LOG.trace("Removed invalid multiagg plan: "+me);
+ }
+ }
+ }
+ }
+
+ private static boolean isValidMultiAggregate(CPlanMemoTable memo, MemoTableEntry me) {
+ //ensure that aggregates are independent of each other, i.e.,
+ //they to not have potentially transitive parent child references
+ boolean ret = true;
+ for( int i=0; i<3; i++ )
+ if( me.isPlanRef(i) ) {
+ HashSet<Long> probe = new HashSet<Long>();
+ for( int j=0; j<3; j++ )
+ if( i != j )
+ probe.add(me.input(j));
+ ret &= rCheckMultiAggregate(memo._hopRefs.get(me.input(i)), probe);
+ }
+ return ret;
+ }
+
+ private static boolean rCheckMultiAggregate(Hop current, HashSet<Long> probe) {
+ boolean ret = true;
+ for( Hop c : current.getInput() )
+ ret &= rCheckMultiAggregate(c, probe);
+ ret &= !probe.contains(current.getHopID());
+ return ret;
+ }
+
private void selectPlans(CPlanMemoTable memo, HashSet<Long> partition, HashSet<Long> R, ArrayList<Long> M)
{
//if no materialization points, use basic fuse-all w/ partition awareness
@@ -275,7 +347,7 @@ public class PlanSelectionFuseCostBased extends PlanSelection
Iterator<MemoTableEntry> iter = memo.get(hopID).iterator();
while( iter.hasNext() ) {
MemoTableEntry me = iter.next();
- if( !hasNoRefToMaterialization(me, M, plan) ){
+ if( !hasNoRefToMaterialization(me, M, plan) && me.type!=TemplateType.OuterProdTpl ){
iter.remove();
if( LOG.isTraceEnabled() )
LOG.trace("Removed memo table entry: "+me);
@@ -418,7 +490,7 @@ public class PlanSelectionFuseCostBased extends PlanSelection
if( LOG.isTraceEnabled() )
LOG.trace("Cost vector for fused operator: "+costVect);
costs += costVect.outSize * 8 / WRITE_BANDWIDTH; //time for output write
- costs += Math.min(
+ costs += Math.max(
costVect.computeCosts*costVect.getMaxInputSize()/ COMPUTE_BANDWIDTH,
costVect.getSumInputSizes() * 8 / READ_BANDWIDTH);
}
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/main/java/org/apache/sysml/hops/codegen/template/TemplateBase.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/hops/codegen/template/TemplateBase.java b/src/main/java/org/apache/sysml/hops/codegen/template/TemplateBase.java
index 9d2466e..4fceb8a 100644
--- a/src/main/java/org/apache/sysml/hops/codegen/template/TemplateBase.java
+++ b/src/main/java/org/apache/sysml/hops/codegen/template/TemplateBase.java
@@ -26,11 +26,11 @@ import org.apache.sysml.runtime.matrix.data.Pair;
public abstract class TemplateBase
{
public enum TemplateType {
+ //ordering specifies type preferences
+ MultiAggTpl,
RowAggTpl,
OuterProdTpl,
CellTpl;
-
- //rank in preferred order
public int getRank() {
return this.ordinal();
}
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/main/java/org/apache/sysml/hops/codegen/template/TemplateCell.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/hops/codegen/template/TemplateCell.java b/src/main/java/org/apache/sysml/hops/codegen/template/TemplateCell.java
index 58df56f..e3c12d5 100644
--- a/src/main/java/org/apache/sysml/hops/codegen/template/TemplateCell.java
+++ b/src/main/java/org/apache/sysml/hops/codegen/template/TemplateCell.java
@@ -66,6 +66,11 @@ public class TemplateCell extends TemplateBase
public TemplateCell(boolean closed) {
super(TemplateType.CellTpl, closed);
}
+
+ public TemplateCell(TemplateType type, boolean closed) {
+ super(type, closed);
+ }
+
@Override
public boolean open(Hop hop) {
@@ -134,7 +139,7 @@ public class TemplateCell extends TemplateBase
return new Pair<Hop[],CNodeTpl>(sinHops.toArray(new Hop[0]), tpl);
}
- private void rConstructCplan(Hop hop, CPlanMemoTable memo, HashMap<Long, CNode> tmp, HashSet<Hop> inHops, boolean compileLiterals)
+ protected void rConstructCplan(Hop hop, CPlanMemoTable memo, HashMap<Long, CNode> tmp, HashSet<Hop> inHops, boolean compileLiterals)
{
//memoization for common subexpression elimination and to avoid redundant work
if( tmp.containsKey(hop.getHopID()) )
@@ -269,7 +274,7 @@ public class TemplateCell extends TemplateBase
tmp.put(hop.getHopID(), out);
}
- public static boolean isValidOperation(Hop hop)
+ protected static boolean isValidOperation(Hop hop)
{
//prepare indicators for binary operations
boolean isBinaryMatrixScalar = false;
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/main/java/org/apache/sysml/hops/codegen/template/TemplateMultiAgg.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/hops/codegen/template/TemplateMultiAgg.java b/src/main/java/org/apache/sysml/hops/codegen/template/TemplateMultiAgg.java
new file mode 100644
index 0000000..aaf00e7
--- /dev/null
+++ b/src/main/java/org/apache/sysml/hops/codegen/template/TemplateMultiAgg.java
@@ -0,0 +1,109 @@
+/*
+ * 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.codegen.template;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.sysml.hops.Hop;
+import org.apache.sysml.hops.Hop.AggOp;
+import org.apache.sysml.hops.codegen.cplan.CNode;
+import org.apache.sysml.hops.codegen.template.CPlanMemoTable.MemoTableEntry;
+import org.apache.sysml.hops.codegen.cplan.CNodeMultiAgg;
+import org.apache.sysml.hops.codegen.cplan.CNodeTpl;
+import org.apache.sysml.runtime.matrix.data.Pair;
+
+public class TemplateMultiAgg extends TemplateCell
+{
+ public TemplateMultiAgg() {
+ super(TemplateType.MultiAggTpl, false);
+ }
+
+ public TemplateMultiAgg(boolean closed) {
+ super(TemplateType.MultiAggTpl, closed);
+ }
+
+ @Override
+ public boolean open(Hop hop) {
+ //multiagg is a composite templates, which is not
+ //created via open-fuse-merge-close
+ return false;
+ }
+
+ @Override
+ public boolean fuse(Hop hop, Hop input) {
+ return false;
+ }
+
+ @Override
+ public boolean merge(Hop hop, Hop input) {
+ return false;
+ }
+
+ @Override
+ public CloseType close(Hop hop) {
+ return CloseType.CLOSED_INVALID;
+ }
+
+ public Pair<Hop[], CNodeTpl> constructCplan(Hop hop, CPlanMemoTable memo, boolean compileLiterals)
+ {
+ //get all root nodes for multi aggregation
+ MemoTableEntry multiAgg = memo.getBest(hop.getHopID(), TemplateType.MultiAggTpl);
+ ArrayList<Hop> roots = new ArrayList<Hop>();
+ for( int i=0; i<3; i++ )
+ if( multiAgg.isPlanRef(i) )
+ roots.add(memo._hopRefs.get(multiAgg.input(i)));
+ Hop.resetVisitStatus(roots);
+
+ //recursively process required cplan outputs
+ HashSet<Hop> inHops = new HashSet<Hop>();
+ HashMap<Long, CNode> tmp = new HashMap<Long, CNode>();
+ for( Hop root : roots ) //use celltpl cplan construction
+ super.rConstructCplan(root, memo, tmp, inHops, compileLiterals);
+ Hop.resetVisitStatus(roots);
+
+ //reorder inputs (ensure matrices/vectors come first) and prune literals
+ //note: we order by number of cells and subsequently sparsity to ensure
+ //that sparse inputs are used as the main input w/o unnecessary conversion
+ List<Hop> sinHops = inHops.stream()
+ .filter(h -> !(h.getDataType().isScalar() && tmp.get(h.getHopID()).isLiteral()))
+ .sorted(new HopInputComparator()).collect(Collectors.toList());
+
+ //construct template node
+ ArrayList<CNode> inputs = new ArrayList<CNode>();
+ for( Hop in : sinHops )
+ inputs.add(tmp.get(in.getHopID()));
+ ArrayList<CNode> outputs = new ArrayList<CNode>();
+ ArrayList<AggOp> aggOps = new ArrayList<AggOp>();
+ for( Hop root : roots ) {
+ outputs.add(tmp.get(root.getHopID()));
+ aggOps.add(TemplateUtils.getAggOp(root));
+ }
+ CNodeMultiAgg tpl = new CNodeMultiAgg(inputs, outputs);
+ tpl.setAggOps(aggOps);
+ tpl.setRootNodes(roots);
+
+ // return cplan instance
+ return new Pair<Hop[],CNodeTpl>(sinHops.toArray(new Hop[0]), tpl);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/main/java/org/apache/sysml/hops/codegen/template/TemplateUtils.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/hops/codegen/template/TemplateUtils.java b/src/main/java/org/apache/sysml/hops/codegen/template/TemplateUtils.java
index b959638..c6a259f 100644
--- a/src/main/java/org/apache/sysml/hops/codegen/template/TemplateUtils.java
+++ b/src/main/java/org/apache/sysml/hops/codegen/template/TemplateUtils.java
@@ -187,6 +187,7 @@ public class TemplateUtils
switch( type ) {
case CellTpl: tpl = new TemplateCell(closed); break;
case RowAggTpl: tpl = new TemplateRowAgg(closed); break;
+ case MultiAggTpl: tpl = new TemplateMultiAgg(closed); break;
case OuterProdTpl: tpl = new TemplateOuterProduct(closed); break;
}
return tpl;
@@ -197,6 +198,7 @@ public class TemplateUtils
switch( type ) {
case CellTpl: tpl = new TemplateBase[]{new TemplateCell(closed), new TemplateRowAgg(closed)}; break;
case RowAggTpl: tpl = new TemplateBase[]{new TemplateRowAgg(closed)}; break;
+ case MultiAggTpl: tpl = new TemplateBase[]{new TemplateMultiAgg(closed)}; break;
case OuterProdTpl: tpl = new TemplateBase[]{new TemplateOuterProduct(closed)}; break;
}
return tpl;
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/main/java/org/apache/sysml/hops/rewrite/HopRewriteUtils.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/hops/rewrite/HopRewriteUtils.java b/src/main/java/org/apache/sysml/hops/rewrite/HopRewriteUtils.java
index 3857ca2..fcfc14b 100644
--- a/src/main/java/org/apache/sysml/hops/rewrite/HopRewriteUtils.java
+++ b/src/main/java/org/apache/sysml/hops/rewrite/HopRewriteUtils.java
@@ -41,6 +41,7 @@ import org.apache.sysml.hops.Hop.OpOp3;
import org.apache.sysml.hops.Hop.ParamBuiltinOp;
import org.apache.sysml.hops.Hop.ReOrgOp;
import org.apache.sysml.hops.HopsException;
+import org.apache.sysml.hops.IndexingOp;
import org.apache.sysml.hops.LeftIndexingOp;
import org.apache.sysml.hops.LiteralOp;
import org.apache.sysml.hops.MemoTable;
@@ -547,6 +548,16 @@ public class HopRewriteUtils
return pbop;
}
+ public static Hop createScalarIndexing(Hop input, long rix, long cix) {
+ LiteralOp row = new LiteralOp(rix);
+ LiteralOp col = new LiteralOp(cix);
+ IndexingOp ix = new IndexingOp("tmp", DataType.MATRIX, ValueType.DOUBLE, input, row, row, col, col, true, true);
+ ix.setOutputBlocksizes(input.getRowsInBlock(), input.getColsInBlock());
+ copyLineNumbers(input, ix);
+ ix.refreshSizeInformation();
+ return createUnary(ix, OpOp1.CAST_AS_SCALAR);
+ }
+
public static Hop createValueHop( Hop hop, boolean row )
throws HopsException
{
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/main/java/org/apache/sysml/runtime/codegen/SpoofMultiAggregate.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/runtime/codegen/SpoofMultiAggregate.java b/src/main/java/org/apache/sysml/runtime/codegen/SpoofMultiAggregate.java
new file mode 100644
index 0000000..ea76ac5
--- /dev/null
+++ b/src/main/java/org/apache/sysml/runtime/codegen/SpoofMultiAggregate.java
@@ -0,0 +1,257 @@
+/*
+ * 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.codegen;
+
+import java.io.Serializable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.apache.sysml.runtime.DMLRuntimeException;
+import org.apache.sysml.runtime.codegen.SpoofCellwise.AggOp;
+import org.apache.sysml.runtime.functionobjects.Builtin;
+import org.apache.sysml.runtime.functionobjects.Builtin.BuiltinCode;
+import org.apache.sysml.runtime.functionobjects.KahanFunction;
+import org.apache.sysml.runtime.functionobjects.KahanPlus;
+import org.apache.sysml.runtime.functionobjects.KahanPlusSq;
+import org.apache.sysml.runtime.functionobjects.ValueFunction;
+import org.apache.sysml.runtime.instructions.cp.KahanObject;
+import org.apache.sysml.runtime.instructions.cp.ScalarObject;
+import org.apache.sysml.runtime.matrix.data.MatrixBlock;
+import org.apache.sysml.runtime.matrix.data.SparseBlock;
+import org.apache.sysml.runtime.util.UtilFunctions;
+
+public abstract class SpoofMultiAggregate extends SpoofOperator implements Serializable
+{
+ private static final long serialVersionUID = -6164871955591089349L;
+ private static final long PAR_NUMCELL_THRESHOLD = 1024*1024; //Min 1M elements
+
+ private final AggOp[] _aggOps;
+
+ public SpoofMultiAggregate(AggOp... aggOps) {
+ _aggOps = aggOps;
+ }
+
+ public AggOp[] getAggOps() {
+ return _aggOps;
+ }
+
+ @Override
+ public String getSpoofType() {
+ return "MA" + getClass().getName().split("\\.")[1];
+ }
+
+ @Override
+ public void execute(ArrayList<MatrixBlock> inputs, ArrayList<ScalarObject> scalarObjects, MatrixBlock out)
+ throws DMLRuntimeException
+ {
+ execute(inputs, scalarObjects, out, 1);
+ }
+
+ @Override
+ public void execute(ArrayList<MatrixBlock> inputs, ArrayList<ScalarObject> scalarObjects, MatrixBlock out, int k)
+ throws DMLRuntimeException
+ {
+ //sanity check
+ if( inputs==null || inputs.size() < 1 )
+ throw new RuntimeException("Invalid input arguments.");
+
+ if( inputs.get(0).getNumRows()*inputs.get(0).getNumColumns()<PAR_NUMCELL_THRESHOLD ) {
+ k = 1; //serial execution
+ }
+
+ //result allocation and preparations
+ out.reset(1, _aggOps.length, false);
+ out.allocateDenseBlock();
+ double[] c = out.getDenseBlock();
+
+ //input preparation
+ double[][] b = prepInputMatrices(inputs);
+ double[] scalars = prepInputScalars(scalarObjects);
+ final int m = inputs.get(0).getNumRows();
+ final int n = inputs.get(0).getNumColumns();
+
+ if( k <= 1 ) //SINGLE-THREADED
+ {
+ setInitialOutputValues(c);
+ if( !inputs.get(0).isInSparseFormat() )
+ executeDense(inputs.get(0).getDenseBlock(), b, scalars, c, m, n, 0, m);
+ else
+ executeSparse(inputs.get(0).getSparseBlock(), b, scalars, c, m, n, 0, m);
+ }
+ else //MULTI-THREADED
+ {
+ try {
+ ExecutorService pool = Executors.newFixedThreadPool( k );
+ ArrayList<ParAggTask> tasks = new ArrayList<ParAggTask>();
+ int nk = UtilFunctions.roundToNext(Math.min(8*k,m/32), k);
+ int blklen = (int)(Math.ceil((double)m/nk));
+ for( int i=0; i<nk & i*blklen<m; i++ )
+ tasks.add(new ParAggTask(inputs.get(0), b, scalars, m, n, i*blklen, Math.min((i+1)*blklen, m)));
+ //execute tasks
+ List<Future<double[]>> taskret = pool.invokeAll(tasks);
+ pool.shutdown();
+
+ //aggregate partial results
+ ArrayList<double[]> pret = new ArrayList<double[]>();
+ for( Future<double[]> task : taskret )
+ pret.add(task.get());
+ aggregatePartialResults(c, pret);
+ }
+ catch(Exception ex) {
+ throw new DMLRuntimeException(ex);
+ }
+ }
+
+ //post-processing
+ out.recomputeNonZeros();
+ out.examSparsity();
+ }
+
+ private void executeDense(double[] a, double[][] b, double[] scalars, double[] c, int m, int n, int rl, int ru) throws DMLRuntimeException
+ {
+ //core dense aggregation operation
+ for( int i=rl, ix=rl*n; i<ru; i++ ) {
+ for( int j=0; j<n; j++, ix++ ) {
+ double in = (a != null) ? a[ix] : 0;
+ genexec( in, b, scalars, c, m, n, i, j );
+ }
+ }
+ }
+
+ private void executeSparse(SparseBlock sblock, double[][] b, double[] scalars, double[] c, int m, int n, int rl, int ru)
+ throws DMLRuntimeException
+ {
+ //core dense aggregation operation
+ for( int i=rl; i<ru; i++ )
+ for( int j=0; j<n; j++ ) {
+ double in = (sblock != null) ? sblock.get(i, j) : 0;
+ genexec( in, b, scalars, c, m, n, i, j );
+ }
+ }
+
+
+ protected abstract void genexec( double a, double[][] b, double[] scalars, double[] c, int m, int n, int rowIndex, int colIndex);
+
+
+ private void setInitialOutputValues(double[] c) {
+ for( int k=0; k<_aggOps.length; k++ ) {
+ switch(_aggOps[k]) {
+ case SUM:
+ case SUM_SQ: c[k] = 0; break;
+ case MIN: c[k] = Double.MAX_VALUE; break;
+ case MAX: c[k] = -Double.MAX_VALUE; break;
+ }
+ }
+ }
+
+
+ private void aggregatePartialResults(double[] c, ArrayList<double[]> pret)
+ throws DMLRuntimeException
+ {
+ ValueFunction[] vfun = getAggFunctions(_aggOps);
+ for( int k=0; k<_aggOps.length; k++ ) {
+ if( vfun[k] instanceof KahanFunction ) {
+ KahanObject kbuff = new KahanObject(0, 0);
+ KahanPlus kplus = KahanPlus.getKahanPlusFnObject();
+ for(double[] tmp : pret)
+ kplus.execute2(kbuff, tmp[k]);
+ c[k] = kbuff._sum;
+ }
+ else {
+ for(double[] tmp : pret)
+ c[k] = vfun[k].execute(c[k], tmp[k]);
+ }
+ }
+ }
+
+ public static void aggregatePartialResults(AggOp[] aggOps, MatrixBlock c, MatrixBlock b)
+ throws DMLRuntimeException
+ {
+ ValueFunction[] vfun = getAggFunctions(aggOps);
+
+ for( int k=0; k< aggOps.length; k++ ) {
+ if( vfun[k] instanceof KahanFunction ) {
+ KahanObject kbuff = new KahanObject(c.quickGetValue(0, k), 0);
+ KahanPlus kplus = KahanPlus.getKahanPlusFnObject();
+ kplus.execute2(kbuff, b.quickGetValue(0, k));
+ c.quickSetValue(0, k, kbuff._sum);
+ }
+ else {
+ double cval = c.quickGetValue(0, k);
+ double bval = b.quickGetValue(0, k);
+ c.quickSetValue(0, k, vfun[k].execute(cval, bval));
+ }
+ }
+ }
+
+ public static ValueFunction[] getAggFunctions(AggOp[] aggOps) {
+ ValueFunction[] fun = new ValueFunction[aggOps.length];
+ for( int i=0; i<aggOps.length; i++ ) {
+ switch( aggOps[i] ) {
+ case SUM: fun[i] = KahanPlus.getKahanPlusFnObject(); break;
+ case SUM_SQ: fun[i] = KahanPlusSq.getKahanPlusSqFnObject(); break;
+ case MIN: fun[i] = Builtin.getBuiltinFnObject(BuiltinCode.MIN); break;
+ case MAX: fun[i] = Builtin.getBuiltinFnObject(BuiltinCode.MAX); break;
+ default:
+ throw new RuntimeException("Unsupported "
+ + "aggregation type: "+aggOps[i].name());
+ }
+ }
+ return fun;
+ }
+
+ private class ParAggTask implements Callable<double[]>
+ {
+ private final MatrixBlock _a;
+ private final double[][] _b;
+ private final double[] _scalars;
+ private final int _rlen;
+ private final int _clen;
+ private final int _rl;
+ private final int _ru;
+
+ protected ParAggTask( MatrixBlock a, double[][] b, double[] scalars,
+ int rlen, int clen, int rl, int ru ) {
+ _a = a;
+ _b = b;
+ _scalars = scalars;
+ _rlen = rlen;
+ _clen = clen;
+ _rl = rl;
+ _ru = ru;
+ }
+
+ @Override
+ public double[] call() throws DMLRuntimeException {
+ double[] c = new double[_aggOps.length];
+ setInitialOutputValues(c);
+ if( !_a.isInSparseFormat() )
+ executeDense(_a.getDenseBlock(), _b, _scalars, c, _rlen, _clen, _rl, _ru);
+ else
+ executeSparse(_a.getSparseBlock(), _b, _scalars, c, _rlen, _clen, _rl, _ru);
+ return c;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/main/java/org/apache/sysml/runtime/instructions/spark/SpoofSPInstruction.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/runtime/instructions/spark/SpoofSPInstruction.java b/src/main/java/org/apache/sysml/runtime/instructions/spark/SpoofSPInstruction.java
index f203913..3b067dd 100644
--- a/src/main/java/org/apache/sysml/runtime/instructions/spark/SpoofSPInstruction.java
+++ b/src/main/java/org/apache/sysml/runtime/instructions/spark/SpoofSPInstruction.java
@@ -24,6 +24,7 @@ import java.util.Iterator;
import java.util.List;
import org.apache.spark.api.java.JavaPairRDD;
+import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFlatMapFunction;
import org.apache.spark.api.java.function.PairFunction;
import org.apache.sysml.lops.PartialAggregate.CorrectionLocationType;
@@ -31,6 +32,7 @@ import org.apache.sysml.parser.Expression.DataType;
import org.apache.sysml.runtime.DMLRuntimeException;
import org.apache.sysml.runtime.codegen.CodegenUtils;
import org.apache.sysml.runtime.codegen.SpoofCellwise;
+import org.apache.sysml.runtime.codegen.SpoofMultiAggregate;
import org.apache.sysml.runtime.codegen.SpoofCellwise.AggOp;
import org.apache.sysml.runtime.codegen.SpoofCellwise.CellType;
import org.apache.sysml.runtime.codegen.SpoofOperator;
@@ -149,6 +151,18 @@ public class SpoofSPInstruction extends SPInstruction
sec.setVariable(_out.getName(), new DoubleObject(tmpMB.getValue(0, 0)));
}
}
+ else if(_class.getSuperclass() == SpoofMultiAggregate.class)
+ {
+ SpoofMultiAggregate op = (SpoofMultiAggregate) CodegenUtils.createInstance(_class);
+ AggOp[] aggOps = op.getAggOps();
+
+ MatrixBlock tmpMB = in
+ .mapToPair(new MultiAggregateFunction(_class.getName(), _classBytes, bcMatrices, scalars))
+ .values().fold(new MatrixBlock(), new MultiAggAggregateFunction(aggOps) );
+
+ sec.setMatrixOutput(_out.getName(), tmpMB);
+ return;
+ }
else if(_class.getSuperclass() == SpoofOuterProduct.class) // outer product operator
{
if( _out.getDataType()==DataType.MATRIX ) {
@@ -344,7 +358,88 @@ public class SpoofSPInstruction extends SPInstruction
}
return ret;
}
- }
+ }
+
+ private static class MultiAggregateFunction implements PairFunction<Tuple2<MatrixIndexes, MatrixBlock>, MatrixIndexes, MatrixBlock>
+ {
+ private static final long serialVersionUID = -5224519291577332734L;
+
+ private ArrayList<PartitionedBroadcast<MatrixBlock>> _vectors = null;
+ private ArrayList<ScalarObject> _scalars = null;
+ private byte[] _classBytes = null;
+ private String _className = null;
+ private SpoofOperator _op = null;
+
+ public MultiAggregateFunction(String className, byte[] classBytes, ArrayList<PartitionedBroadcast<MatrixBlock>> bcMatrices, ArrayList<ScalarObject> scalars)
+ throws DMLRuntimeException
+ {
+ _className = className;
+ _classBytes = classBytes;
+ _vectors = bcMatrices;
+ _scalars = scalars;
+ }
+
+ @Override
+ public Tuple2<MatrixIndexes, MatrixBlock> call(Tuple2<MatrixIndexes, MatrixBlock> arg)
+ throws Exception
+ {
+ //lazy load of shipped class
+ if( _op == null ) {
+ Class<?> loadedClass = CodegenUtils.getClass(_className, _classBytes);
+ _op = (SpoofOperator) CodegenUtils.createInstance(loadedClass);
+ }
+
+ //execute core operation
+ ArrayList<MatrixBlock> inputs = getVectorInputsFromBroadcast(arg._2(), arg._1());
+ MatrixBlock blkOut = new MatrixBlock();
+ _op.execute(inputs, _scalars, blkOut);
+
+ return new Tuple2<MatrixIndexes,MatrixBlock>(arg._1(), blkOut);
+ }
+
+ private ArrayList<MatrixBlock> getVectorInputsFromBroadcast(MatrixBlock blkIn, MatrixIndexes ixIn)
+ throws DMLRuntimeException
+ {
+ ArrayList<MatrixBlock> ret = new ArrayList<MatrixBlock>();
+ ret.add(blkIn);
+ for( PartitionedBroadcast<MatrixBlock> in : _vectors ) {
+ int rowIndex = (int)((in.getNumRowBlocks()>=ixIn.getRowIndex())?ixIn.getRowIndex():1);
+ int colIndex = (int)((in.getNumColumnBlocks()>=ixIn.getColumnIndex())?ixIn.getColumnIndex():1);
+ ret.add(in.getBlock(rowIndex, colIndex));
+ }
+ return ret;
+ }
+ }
+
+ private static class MultiAggAggregateFunction implements Function2<MatrixBlock, MatrixBlock, MatrixBlock>
+ {
+ private static final long serialVersionUID = 5978731867787952513L;
+
+ private AggOp[] _ops = null;
+
+ public MultiAggAggregateFunction( AggOp[] ops ) {
+ _ops = ops;
+ }
+
+ @Override
+ public MatrixBlock call(MatrixBlock arg0, MatrixBlock arg1)
+ throws Exception
+ {
+ //prepare combiner block
+ if( arg0.getNumRows() <= 0 || arg0.getNumColumns() <= 0) {
+ arg0.copy(arg1);
+ return arg0;
+ }
+ else if( arg1.getNumRows() <= 0 || arg1.getNumColumns() <= 0 ) {
+ return arg0;
+ }
+
+ //aggregate second input (in-place)
+ SpoofMultiAggregate.aggregatePartialResults(_ops, arg0, arg1);
+
+ return arg0;
+ }
+ }
private static class OuterProductFunction implements PairFlatMapFunction<Iterator<Tuple2<MatrixIndexes, MatrixBlock>>, MatrixIndexes, MatrixBlock>
{
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/main/java/org/apache/sysml/utils/Explain.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/utils/Explain.java b/src/main/java/org/apache/sysml/utils/Explain.java
index b78029a..b6e7b6f 100644
--- a/src/main/java/org/apache/sysml/utils/Explain.java
+++ b/src/main/java/org/apache/sysml/utils/Explain.java
@@ -32,6 +32,7 @@ import org.apache.sysml.hops.HopsException;
import org.apache.sysml.hops.LiteralOp;
import org.apache.sysml.hops.OptimizerUtils;
import org.apache.sysml.hops.codegen.cplan.CNode;
+import org.apache.sysml.hops.codegen.cplan.CNodeMultiAgg;
import org.apache.sysml.hops.codegen.cplan.CNodeTpl;
import org.apache.sysml.hops.globalopt.gdfgraph.GDFLoopNode;
import org.apache.sysml.hops.globalopt.gdfgraph.GDFNode;
@@ -380,9 +381,13 @@ public class Explain
sb.append("----------------------------------------\n");
//explain body dag
- cplan.getOutput().resetVisitStatus();
- sb.append(explainCNode(cplan.getOutput(), 1));
- cplan.getOutput().resetVisitStatus();
+ cplan.resetVisitStatusOutputs();
+ if( cplan instanceof CNodeMultiAgg )
+ for( CNode output : ((CNodeMultiAgg)cplan).getOutputs() )
+ sb.append(explainCNode(output, 1));
+ else
+ sb.append(explainCNode(cplan.getOutput(), 1));
+ cplan.resetVisitStatusOutputs();
sb.append("----------------------------------------\n");
return sb.toString();
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/test/java/org/apache/sysml/test/integration/functions/codegen/AlgorithmLinregCG.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/sysml/test/integration/functions/codegen/AlgorithmLinregCG.java b/src/test/java/org/apache/sysml/test/integration/functions/codegen/AlgorithmLinregCG.java
index c6897cf..1a2ecfe 100644
--- a/src/test/java/org/apache/sysml/test/integration/functions/codegen/AlgorithmLinregCG.java
+++ b/src/test/java/org/apache/sysml/test/integration/functions/codegen/AlgorithmLinregCG.java
@@ -43,7 +43,7 @@ public class AlgorithmLinregCG extends AutomatedTestBase
//TODO Investigate numerical stability issues: on certain platforms this test, occasionally fails,
//for 1e-5 (specifically testLinregCGSparseRewritesCP); apparently due to the -(-(X)) -> X rewrite.
- private final static double eps = 1e-2;
+ private final static double eps = 1e-1;
private final static int rows = 2468;
private final static int cols = 507;
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/test/java/org/apache/sysml/test/integration/functions/codegen/CellwiseTmplTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/sysml/test/integration/functions/codegen/CellwiseTmplTest.java b/src/test/java/org/apache/sysml/test/integration/functions/codegen/CellwiseTmplTest.java
index 521ea58..6468003 100644
--- a/src/test/java/org/apache/sysml/test/integration/functions/codegen/CellwiseTmplTest.java
+++ b/src/test/java/org/apache/sysml/test/integration/functions/codegen/CellwiseTmplTest.java
@@ -274,8 +274,8 @@ public class CellwiseTmplTest extends AutomatedTestBase
}
if( !(rewrites && testname.equals(TEST_NAME2)) ) //sigmoid
- Assert.assertTrue(heavyHittersContainsSubString("spoofCell")
- || heavyHittersContainsSubString("sp_spoofCell"));
+ Assert.assertTrue(heavyHittersContainsSubString(
+ "spoofCell", "sp_spoofCell", "spoofMA", "sp_spoofMA"));
if( testname.equals(TEST_NAME7) ) //ensure matrix mult is fused
Assert.assertTrue(!heavyHittersContainsSubString("tsmm"));
else if( testname.equals(TEST_NAME10) ) //ensure min/max is fused
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/test/java/org/apache/sysml/test/integration/functions/codegen/MultiAggTmplTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/sysml/test/integration/functions/codegen/MultiAggTmplTest.java b/src/test/java/org/apache/sysml/test/integration/functions/codegen/MultiAggTmplTest.java
new file mode 100644
index 0000000..21aabb8
--- /dev/null
+++ b/src/test/java/org/apache/sysml/test/integration/functions/codegen/MultiAggTmplTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.test.integration.functions.codegen;
+
+import java.io.File;
+import java.util.HashMap;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.apache.sysml.api.DMLScript;
+import org.apache.sysml.api.DMLScript.RUNTIME_PLATFORM;
+import org.apache.sysml.hops.OptimizerUtils;
+import org.apache.sysml.lops.LopProperties.ExecType;
+import org.apache.sysml.runtime.matrix.data.MatrixValue.CellIndex;
+import org.apache.sysml.test.integration.AutomatedTestBase;
+import org.apache.sysml.test.integration.TestConfiguration;
+import org.apache.sysml.test.utils.TestUtils;
+
+public class MultiAggTmplTest extends AutomatedTestBase
+{
+ private static final String TEST_NAME = "multiAggPattern";
+ private static final String TEST_NAME1 = TEST_NAME+"1"; //min(X>7), max(X>7)
+ private static final String TEST_NAME2 = TEST_NAME+"2"; //sum(X>7), sum((X>7)^2)
+
+ private static final String TEST_DIR = "functions/codegen/";
+ private static final String TEST_CLASS_DIR = TEST_DIR + MultiAggTmplTest.class.getSimpleName() + "/";
+ private final static String TEST_CONF = "SystemML-config-codegen.xml";
+ private final static File TEST_CONF_FILE = new File(SCRIPT_DIR + TEST_DIR, TEST_CONF);
+
+ private static final double eps = Math.pow(10, -10);
+
+ @Override
+ public void setUp() {
+ TestUtils.clearAssertionInformation();
+ for(int i=1; i<=2; i++)
+ addTestConfiguration( TEST_NAME+i, new TestConfiguration(TEST_CLASS_DIR, TEST_NAME+i, new String[] { String.valueOf(i) }) );
+ }
+
+ @Test
+ public void testCodegenMultiAggRewrite1CP() {
+ testCodegenIntegration( TEST_NAME1, true, ExecType.CP );
+ }
+
+ @Test
+ public void testCodegenRowAgg1CP() {
+ testCodegenIntegration( TEST_NAME1, false, ExecType.CP );
+ }
+
+ @Test
+ public void testCodegenRowAgg1Spark() {
+ testCodegenIntegration( TEST_NAME1, false, ExecType.SPARK );
+ }
+
+ @Test
+ public void testCodegenMultiAggRewrite2CP() {
+ testCodegenIntegration( TEST_NAME2, true, ExecType.CP );
+ }
+
+ @Test
+ public void testCodegenRowAgg2CP() {
+ testCodegenIntegration( TEST_NAME2, false, ExecType.CP );
+ }
+
+ @Test
+ public void testCodegenRowAgg2Spark() {
+ testCodegenIntegration( TEST_NAME2, false, ExecType.SPARK );
+ }
+
+ private void testCodegenIntegration( String testname, boolean rewrites, ExecType instType )
+ {
+ boolean oldFlag = OptimizerUtils.ALLOW_ALGEBRAIC_SIMPLIFICATION;
+ RUNTIME_PLATFORM platformOld = rtplatform;
+ switch( instType ) {
+ case MR: rtplatform = RUNTIME_PLATFORM.HADOOP; break;
+ case SPARK: rtplatform = RUNTIME_PLATFORM.SPARK; break;
+ default: rtplatform = RUNTIME_PLATFORM.HYBRID_SPARK; break;
+ }
+
+ boolean sparkConfigOld = DMLScript.USE_LOCAL_SPARK_CONFIG;
+ if( rtplatform == RUNTIME_PLATFORM.SPARK || rtplatform == RUNTIME_PLATFORM.HYBRID_SPARK )
+ DMLScript.USE_LOCAL_SPARK_CONFIG = true;
+
+ try
+ {
+ TestConfiguration config = getTestConfiguration(testname);
+ loadTestConfiguration(config);
+
+ String HOME = SCRIPT_DIR + TEST_DIR;
+ fullDMLScriptName = HOME + testname + ".dml";
+ programArgs = new String[]{"-explain", "-stats", "-args", output("S") };
+
+ fullRScriptName = HOME + testname + ".R";
+ rCmd = getRCmd(inputDir(), expectedDir());
+
+ OptimizerUtils.ALLOW_ALGEBRAIC_SIMPLIFICATION = rewrites;
+
+ runTest(true, false, null, -1);
+ runRScript(true);
+
+ //compare matrices
+ HashMap<CellIndex, Double> dmlfile = readDMLMatrixFromHDFS("S");
+ HashMap<CellIndex, Double> rfile = readRMatrixFromFS("S");
+ TestUtils.compareMatrices(dmlfile, rfile, eps, "Stat-DML", "Stat-R");
+ Assert.assertTrue(heavyHittersContainsSubString("spoofMA")
+ || heavyHittersContainsSubString("sp_spoofMA"));
+ }
+ finally {
+ rtplatform = platformOld;
+ DMLScript.USE_LOCAL_SPARK_CONFIG = sparkConfigOld;
+ OptimizerUtils.ALLOW_ALGEBRAIC_SIMPLIFICATION = oldFlag;
+ OptimizerUtils.ALLOW_AUTO_VECTORIZATION = true;
+ OptimizerUtils.ALLOW_OPERATOR_FUSION = true;
+ }
+ }
+
+ /**
+ * Override default configuration with custom test configuration to ensure
+ * scratch space and local temporary directory locations are also updated.
+ */
+ @Override
+ protected File getConfigTemplateFile() {
+ // Instrumentation in this test's output log to show custom configuration file used for template.
+ System.out.println("This test case overrides default configuration with " + TEST_CONF_FILE.getPath());
+ return TEST_CONF_FILE;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/test/scripts/functions/codegen/multiAggPattern1.R
----------------------------------------------------------------------
diff --git a/src/test/scripts/functions/codegen/multiAggPattern1.R b/src/test/scripts/functions/codegen/multiAggPattern1.R
new file mode 100644
index 0000000..4e0a22e
--- /dev/null
+++ b/src/test/scripts/functions/codegen/multiAggPattern1.R
@@ -0,0 +1,32 @@
+#-------------------------------------------------------------
+#
+# 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.
+#
+#-------------------------------------------------------------
+
+args<-commandArgs(TRUE)
+options(digits=22)
+library("Matrix")
+
+X = matrix(seq(1,15), 5, 3, byrow=TRUE);
+
+r1 = min(X>7);
+r2 = max(X>7);
+S = as.matrix(r1+r2);
+
+writeMM(as(S, "CsparseMatrix"), paste(args[2], "S", sep=""));
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/test/scripts/functions/codegen/multiAggPattern1.dml
----------------------------------------------------------------------
diff --git a/src/test/scripts/functions/codegen/multiAggPattern1.dml b/src/test/scripts/functions/codegen/multiAggPattern1.dml
new file mode 100644
index 0000000..1f5dd96
--- /dev/null
+++ b/src/test/scripts/functions/codegen/multiAggPattern1.dml
@@ -0,0 +1,28 @@
+#-------------------------------------------------------------
+#
+# 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.
+#
+#-------------------------------------------------------------
+
+X = matrix(seq(1,15), rows=5, cols=3);
+
+r1 = min(X>7);
+r2 = max(X>7);
+S = as.matrix(r1+r2);
+
+write(S,$1)
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/test/scripts/functions/codegen/multiAggPattern2.R
----------------------------------------------------------------------
diff --git a/src/test/scripts/functions/codegen/multiAggPattern2.R b/src/test/scripts/functions/codegen/multiAggPattern2.R
new file mode 100644
index 0000000..379084a
--- /dev/null
+++ b/src/test/scripts/functions/codegen/multiAggPattern2.R
@@ -0,0 +1,32 @@
+#-------------------------------------------------------------
+#
+# 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.
+#
+#-------------------------------------------------------------
+
+args<-commandArgs(TRUE)
+options(digits=22)
+library("Matrix")
+
+X = matrix(seq(1,15), 5, 3, byrow=TRUE);
+
+r1 = sum(X>7);
+r2 = sum((X>7)^2);
+S = as.matrix(r1+r2);
+
+writeMM(as(S, "CsparseMatrix"), paste(args[2], "S", sep=""));
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/test/scripts/functions/codegen/multiAggPattern2.dml
----------------------------------------------------------------------
diff --git a/src/test/scripts/functions/codegen/multiAggPattern2.dml b/src/test/scripts/functions/codegen/multiAggPattern2.dml
new file mode 100644
index 0000000..4d4fb6d
--- /dev/null
+++ b/src/test/scripts/functions/codegen/multiAggPattern2.dml
@@ -0,0 +1,28 @@
+#-------------------------------------------------------------
+#
+# 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.
+#
+#-------------------------------------------------------------
+
+X = matrix(seq(1,15), rows=5, cols=3);
+
+r1 = sum(X>7);
+r2 = sum((X>7)^2);
+S = as.matrix(r1+r2);
+
+write(S,$1)
http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/174bf7db/src/test_suites/java/org/apache/sysml/test/integration/functions/codegen/ZPackageSuite.java
----------------------------------------------------------------------
diff --git a/src/test_suites/java/org/apache/sysml/test/integration/functions/codegen/ZPackageSuite.java b/src/test_suites/java/org/apache/sysml/test/integration/functions/codegen/ZPackageSuite.java
index abba49e..d063728 100644
--- a/src/test_suites/java/org/apache/sysml/test/integration/functions/codegen/ZPackageSuite.java
+++ b/src/test_suites/java/org/apache/sysml/test/integration/functions/codegen/ZPackageSuite.java
@@ -34,6 +34,7 @@ import org.junit.runners.Suite;
AlgorithmPNMF.class,
CellwiseTmplTest.class,
DAGCellwiseTmplTest.class,
+ MultiAggTmplTest.class,
OuterProdTmplTest.class,
RowAggTmplTest.class,
RowVectorComparisonTest.class,