This is an automated email from the ASF dual-hosted git repository.
mboehm7 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/systemds.git
The following commit(s) were added to refs/heads/master by this push:
new 87e4d50 [SYSTEMDS-2801] New rewrite for removing (par)for loops w/o
iterations
87e4d50 is described below
commit 87e4d502205919f1e3ee9aeb7838731e677e6ef0
Author: Patrick Damme <[email protected]>
AuthorDate: Sat Jan 23 19:58:32 2021 +0100
[SYSTEMDS-2801] New rewrite for removing (par)for loops w/o iterations
This commit adds a new StatementBlockRewriteRule removing (par)for-loops
which are known to iterate over an empty sequence, and, thus, will not
perform a single iteration. This works if the loop's from, to, and
increment are constants (possibly DML script arguments, and after
constant folding). Furthermore, OptimizerUtils has a new flag
determining whether this rule shall be applied. In ProgramRewriter's
default rule sequence, this new rule is inserted directly after the
removal of unnecessary branches, since they have a lot in common.
Merging subsequent blocks is done once afterwards if any of removing
unnecessary branches or removing (par)for-loops shall be applied.
Closes #1165.
---
.../java/org/apache/sysds/hops/OptimizerUtils.java | 10 +++
.../apache/sysds/hops/rewrite/ProgramRewriter.java | 8 +-
.../rewrite/RewriteRemoveForLoopEmptySequence.java | 79 +++++++++++++++++++
.../runtime/controlprogram/ForProgramBlock.java | 36 ++++-----
.../runtime/controlprogram/ParForProgramBlock.java | 23 +++---
.../sysds/runtime/controlprogram/ProgramBlock.java | 2 +-
.../apache/sysds/runtime/util/UtilFunctions.java | 11 ++-
.../rewrite/RewriteForLoopRemovalTest.java | 91 ++++++++++++++++++++++
src/test/scripts/functions/rewrite/removal_for.dml | 28 +++++++
.../scripts/functions/rewrite/removal_parfor.dml | 28 +++++++
10 files changed, 283 insertions(+), 33 deletions(-)
diff --git a/src/main/java/org/apache/sysds/hops/OptimizerUtils.java
b/src/main/java/org/apache/sysds/hops/OptimizerUtils.java
index 2d94b59..c05eaa3 100644
--- a/src/main/java/org/apache/sysds/hops/OptimizerUtils.java
+++ b/src/main/java/org/apache/sysds/hops/OptimizerUtils.java
@@ -124,6 +124,13 @@ public class OptimizerUtils
*/
public static boolean ALLOW_BRANCH_REMOVAL = true;
+ /**
+ * Enables the removal of (par)for-loops when from, to, and increment
are constants
+ * (original literals or results of constant folding) and lead to an
empty sequence,
+ * i.e., (par)for-loops without a single iteration.
+ */
+ public static boolean ALLOW_FOR_LOOP_REMOVAL = true;
+
public static boolean ALLOW_AUTO_VECTORIZATION = true;
/**
@@ -300,6 +307,7 @@ public class OptimizerUtils
ALLOW_INTER_PROCEDURAL_ANALYSIS = false;
IPA_NUM_REPETITIONS = 1;
ALLOW_BRANCH_REMOVAL = false;
+ ALLOW_FOR_LOOP_REMOVAL = false;
ALLOW_SUM_PRODUCT_REWRITES = false;
break;
// opt level 1: memory-based (no advanced rewrites)
@@ -312,6 +320,7 @@ public class OptimizerUtils
ALLOW_INTER_PROCEDURAL_ANALYSIS = false;
IPA_NUM_REPETITIONS = 1;
ALLOW_BRANCH_REMOVAL = false;
+ ALLOW_FOR_LOOP_REMOVAL = false;
ALLOW_SUM_PRODUCT_REWRITES = false;
ALLOW_LOOP_UPDATE_IN_PLACE = false;
break;
@@ -366,6 +375,7 @@ public class OptimizerUtils
ALLOW_ALGEBRAIC_SIMPLIFICATION = true;
ALLOW_AUTO_VECTORIZATION = true;
ALLOW_BRANCH_REMOVAL = true;
+ ALLOW_FOR_LOOP_REMOVAL = true;
ALLOW_CONSTANT_FOLDING = true;
ALLOW_COMMON_SUBEXPRESSION_ELIMINATION = true;
ALLOW_INTER_PROCEDURAL_ANALYSIS = true;
diff --git a/src/main/java/org/apache/sysds/hops/rewrite/ProgramRewriter.java
b/src/main/java/org/apache/sysds/hops/rewrite/ProgramRewriter.java
index af81e86..5127384 100644
--- a/src/main/java/org/apache/sysds/hops/rewrite/ProgramRewriter.java
+++ b/src/main/java/org/apache/sysds/hops/rewrite/ProgramRewriter.java
@@ -90,10 +90,12 @@ public class ProgramRewriter
_dagRuleSet.add( new
RewriteInjectSparkPReadCheckpointing() ); //dependency: reblock
//add statement block rewrite rules
- if( OptimizerUtils.ALLOW_BRANCH_REMOVAL ) {
+ if( OptimizerUtils.ALLOW_BRANCH_REMOVAL )
_sbRuleSet.add( new
RewriteRemoveUnnecessaryBranches() ); //dependency: constant folding
- _sbRuleSet.add( new
RewriteMergeBlockSequence() ); //dependency: remove branches
- }
+ if( OptimizerUtils.ALLOW_FOR_LOOP_REMOVAL )
+ _sbRuleSet.add( new
RewriteRemoveForLoopEmptySequence() ); //dependency: constant folding
+ if( OptimizerUtils.ALLOW_BRANCH_REMOVAL ||
OptimizerUtils.ALLOW_FOR_LOOP_REMOVAL )
+ _sbRuleSet.add( new
RewriteMergeBlockSequence() ); //dependency: remove branches,
remove for-loops
_sbRuleSet.add( new RewriteCompressedReblock()
); // Compression Rewrite
if( OptimizerUtils.ALLOW_SPLIT_HOP_DAGS )
_sbRuleSet.add( new
RewriteSplitDagUnknownCSVRead() ); //dependency: reblock, merge
blocks
diff --git
a/src/main/java/org/apache/sysds/hops/rewrite/RewriteRemoveForLoopEmptySequence.java
b/src/main/java/org/apache/sysds/hops/rewrite/RewriteRemoveForLoopEmptySequence.java
new file mode 100644
index 0000000..82b5f0f
--- /dev/null
+++
b/src/main/java/org/apache/sysds/hops/rewrite/RewriteRemoveForLoopEmptySequence.java
@@ -0,0 +1,79 @@
+/*
+ * 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.sysds.hops.rewrite;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.sysds.hops.Hop;
+import org.apache.sysds.hops.LiteralOp;
+import org.apache.sysds.parser.ForStatementBlock;
+import org.apache.sysds.parser.StatementBlock;
+import org.apache.sysds.runtime.util.UtilFunctions;
+
+/**
+ * Rule: Simplify program structure by removing (par)for statements iterating
over
+ * an empty sequence, i.e., (par)for-loops without a single iteration.
+ */
+public class RewriteRemoveForLoopEmptySequence extends
StatementBlockRewriteRule {
+
+ @Override
+ public boolean createsSplitDag() {
+ return false;
+ }
+
+ @Override
+ public List<StatementBlock> rewriteStatementBlock(StatementBlock sb,
ProgramRewriteStatus state) {
+ ArrayList<StatementBlock> ret = new ArrayList<>();
+ boolean apply = false;
+
+ if( sb instanceof ForStatementBlock ) {
+ ForStatementBlock fsb = (ForStatementBlock) sb;
+
+ //consider rewrite if the increment was specified
+ Hop from = fsb.getFromHops().getInput().get(0);
+ Hop to = fsb.getToHops().getInput().get(0);
+ Hop incr = (fsb.getIncrementHops() != null) ?
+ fsb.getIncrementHops().getInput().get(0) : new
LiteralOp(1);
+
+ //consider rewrite if from, to, and incr are literal
ops (constant values)
+ if( from instanceof LiteralOp && to instanceof
LiteralOp && incr instanceof LiteralOp ) {
+ double dfrom =
HopRewriteUtils.getDoubleValue((LiteralOp) from);
+ double dto =
HopRewriteUtils.getDoubleValue((LiteralOp) to);
+ double dincr =
HopRewriteUtils.getDoubleValue((LiteralOp) incr);
+ if( dfrom > dto && dincr == 1 )
+ dincr = -1;
+ apply = UtilFunctions.getSeqLength(dfrom, dto,
dincr, false) <= 0;
+ }
+ }
+
+ if(apply) //remove for-loop (add nothing)
+ LOG.debug("Applied removeForLoopEmptySequence (lines
"+sb.getBeginLine()+"-"+sb.getEndLine()+").");
+ else //keep original sb (no for)
+ ret.add( sb );
+
+ return ret;
+ }
+
+ @Override
+ public List<StatementBlock> rewriteStatementBlocks(List<StatementBlock>
sbs, ProgramRewriteStatus state) {
+ return sbs;
+ }
+}
diff --git
a/src/main/java/org/apache/sysds/runtime/controlprogram/ForProgramBlock.java
b/src/main/java/org/apache/sysds/runtime/controlprogram/ForProgramBlock.java
index 4f6f552..4faadff 100644
--- a/src/main/java/org/apache/sysds/runtime/controlprogram/ForProgramBlock.java
+++ b/src/main/java/org/apache/sysds/runtime/controlprogram/ForProgramBlock.java
@@ -36,6 +36,7 @@ import org.apache.sysds.runtime.instructions.cp.IntObject;
import org.apache.sysds.runtime.instructions.cp.ScalarObject;
import org.apache.sysds.runtime.lineage.Lineage;
import org.apache.sysds.runtime.lineage.LineageDedupUtils;
+import org.apache.sysds.runtime.util.UtilFunctions;
public class ForProgramBlock extends ProgramBlock
{
@@ -100,15 +101,16 @@ public class ForProgramBlock extends ProgramBlock
@Override
public void execute(ExecutionContext ec) {
// evaluate from, to, incr only once (assumption: known at for
entry)
- IntObject from = executePredicateInstructions( 1,
_fromInstructions, ec );
- IntObject to = executePredicateInstructions( 2,
_toInstructions, ec );
- IntObject incr = (_incrementInstructions == null ||
_incrementInstructions.isEmpty()) ?
+ ScalarObject from = executePredicateInstructions( 1,
_fromInstructions, ec, false);
+ ScalarObject to = executePredicateInstructions( 2,
_toInstructions, ec, false);
+ ScalarObject incr = (_incrementInstructions == null ||
_incrementInstructions.isEmpty()) ?
new IntObject((from.getLongValue()<=to.getLongValue())
? 1 : -1) :
- executePredicateInstructions( 3,
_incrementInstructions, ec );
+ executePredicateInstructions( 3,
_incrementInstructions, ec, false);
- if ( incr.getLongValue() == 0 ) //would produce infinite loop
- throw new DMLRuntimeException(printBlockErrorLocation()
+ "Expression for increment "
- + "of variable '" + _iterPredVar + "' must
evaluate to a non-zero value.");
+ long numIterations = UtilFunctions.getSeqLength(
+ from.getDoubleValue(), to.getDoubleValue(),
incr.getDoubleValue(), false);
+ if( numIterations <= 0 )
+ return;
// execute for loop
try
@@ -166,10 +168,10 @@ public class ForProgramBlock extends ProgramBlock
executeExitInstructions(_exitInstruction, "for", ec);
}
- protected IntObject executePredicateInstructions( int pos,
ArrayList<Instruction> instructions, ExecutionContext ec )
+ protected ScalarObject executePredicateInstructions( int pos,
ArrayList<Instruction> instructions, ExecutionContext ec, boolean downCast )
{
- ScalarObject tmp = null;
- IntObject ret = null;
+ ScalarObject ret = null;
+ ValueType vt = downCast ? ValueType.INT64 : null;
try
{
@@ -190,10 +192,10 @@ public class ForProgramBlock extends ProgramBlock
predHops = fsb.getIncrementHops();
recompile =
fsb.requiresIncrementRecompilation();
}
- tmp = executePredicate(instructions, predHops,
recompile, ValueType.INT64, ec);
+ ret = executePredicate(instructions, predHops,
recompile, vt, ec);
}
else
- tmp = executePredicate(instructions, null,
false, ValueType.INT64, ec);
+ ret = executePredicate(instructions, null,
false, vt, ec);
}
catch(Exception ex) {
String predNameStr = null;
@@ -205,10 +207,8 @@ public class ForProgramBlock extends ProgramBlock
}
//final check of resulting int object (guaranteed to be
non-null, see executePredicate)
- if( tmp instanceof IntObject )
- ret = (IntObject)tmp;
- else //downcast to int if necessary
- ret = new IntObject(tmp.getLongValue());
+ if(downCast && !(ret instanceof IntObject)) //downcast to int
if necessary
+ ret = new IntObject(ret.getLongValue());
return ret;
}
@@ -228,7 +228,7 @@ public class ForProgramBlock extends ProgramBlock
private long _incr = -1;
private boolean _inuse = false;
- protected SequenceIterator(IntObject from, IntObject to,
IntObject incr) {
+ protected SequenceIterator(ScalarObject from, ScalarObject to,
ScalarObject incr) {
_cur = from.getLongValue();
_to = to.getLongValue();
_incr = incr.getLongValue();
@@ -259,4 +259,4 @@ public class ForProgramBlock extends ProgramBlock
throw new RuntimeException("Unsupported remove on
iterator.");
}
}
-}
\ No newline at end of file
+}
diff --git
a/src/main/java/org/apache/sysds/runtime/controlprogram/ParForProgramBlock.java
b/src/main/java/org/apache/sysds/runtime/controlprogram/ParForProgramBlock.java
index d95512f..f11d795 100644
---
a/src/main/java/org/apache/sysds/runtime/controlprogram/ParForProgramBlock.java
+++
b/src/main/java/org/apache/sysds/runtime/controlprogram/ParForProgramBlock.java
@@ -77,6 +77,7 @@ import org.apache.sysds.runtime.instructions.cp.Data;
import org.apache.sysds.runtime.instructions.cp.DoubleObject;
import org.apache.sysds.runtime.instructions.cp.IntObject;
import org.apache.sysds.runtime.instructions.cp.ListObject;
+import org.apache.sysds.runtime.instructions.cp.ScalarObject;
import org.apache.sysds.runtime.instructions.cp.StringObject;
import org.apache.sysds.runtime.instructions.cp.VariableCPInstruction;
import org.apache.sysds.runtime.lineage.Lineage;
@@ -558,22 +559,26 @@ public class ParForProgramBlock extends ForProgramBlock
ParForStatementBlock sb =
(ParForStatementBlock)getStatementBlock();
// evaluate from, to, incr only once (assumption: known at for
entry)
- IntObject from = executePredicateInstructions( 1,
_fromInstructions, ec );
- IntObject to = executePredicateInstructions( 2,
_toInstructions, ec );
- IntObject incr = (_incrementInstructions == null ||
_incrementInstructions.isEmpty()) ?
- new IntObject((from.getLongValue()<=to.getLongValue())
? 1 : -1) :
- executePredicateInstructions( 3,
_incrementInstructions, ec );
+ ScalarObject from0 = executePredicateInstructions(1,
_fromInstructions, ec, false);
+ ScalarObject to0 = executePredicateInstructions(2,
_toInstructions, ec, false);
+ ScalarObject incr0 = (_incrementInstructions == null ||
_incrementInstructions.isEmpty()) ?
+ new
IntObject((from0.getLongValue()<=to0.getLongValue()) ? 1 : -1) :
+ executePredicateInstructions( 3,
_incrementInstructions, ec, false);
- if ( incr.getLongValue() == 0 ) //would produce infinite loop
+ if ( incr0.getLongValue() == 0 ) //would produce infinite loop
throw new
DMLRuntimeException(this.printBlockErrorLocation() + "Expression for increment "
+ "of variable '" + _iterPredVar + "' must
evaluate to a non-zero value.");
- //early exit on num iterations = zero
- _numIterations = UtilFunctions.getSeqLength(
- from.getDoubleValue(), to.getDoubleValue(),
incr.getDoubleValue());
+ //early exit on num iterations (e.g., for invalid loop bounds)
+ _numIterations = UtilFunctions.getSeqLength(
+ from0.getDoubleValue(), to0.getDoubleValue(),
incr0.getDoubleValue(), false);
if( _numIterations <= 0 )
return; //avoid unnecessary optimization/initialization
+ IntObject from = new IntObject(from0.getLongValue());
+ IntObject to = new IntObject(to0.getLongValue());
+ IntObject incr = new IntObject(incr0.getLongValue());
+
///////
//OPTIMIZATION of ParFOR body (incl all child parfor PBs)
///////
diff --git
a/src/main/java/org/apache/sysds/runtime/controlprogram/ProgramBlock.java
b/src/main/java/org/apache/sysds/runtime/controlprogram/ProgramBlock.java
index a555a5d..0ee6553 100644
--- a/src/main/java/org/apache/sysds/runtime/controlprogram/ProgramBlock.java
+++ b/src/main/java/org/apache/sysds/runtime/controlprogram/ProgramBlock.java
@@ -217,7 +217,7 @@ public abstract class ProgramBlock implements ParseInfo
ScalarObject ret = ec.getScalarInput(PRED_VAR, retType, false);
//check and correct scalar ret type (incl save double to int)
- if( ret.getValueType() != retType )
+ if( retType != null && retType != ret.getValueType() )
switch( retType ) {
case BOOLEAN: ret = new
BooleanObject(ret.getBooleanValue()); break;
case INT64: ret = new
IntObject(ret.getLongValue()); break;
diff --git a/src/main/java/org/apache/sysds/runtime/util/UtilFunctions.java
b/src/main/java/org/apache/sysds/runtime/util/UtilFunctions.java
index 5c8ed95..6f578c4 100644
--- a/src/main/java/org/apache/sysds/runtime/util/UtilFunctions.java
+++ b/src/main/java/org/apache/sysds/runtime/util/UtilFunctions.java
@@ -405,9 +405,12 @@ public class UtilFunctions {
//a very small increment. Hence, we use a different formulation
//that exhibits better numerical stability by avoiding the
subtraction
//of numbers of different magnitude.
- if( check && (Double.isNaN(from) || Double.isNaN(to) ||
Double.isNaN(incr)
+ if( (isSpecial(from) || isSpecial(to) || isSpecial(incr)
|| (from > to && incr > 0) || (from < to && incr < 0))
) {
- throw new RuntimeException("Invalid seq parameters:
("+from+", "+to+", "+incr+")");
+ if( check )
+ throw new RuntimeException("Invalid seq
parameters: ("+from+", "+to+", "+incr+")");
+ else
+ return 0; // invalid loop configuration
}
return 1L + (long) Math.floor(to/incr - from/incr);
}
@@ -590,6 +593,10 @@ public class UtilFunctions {
return true;
}
+ public static boolean isSpecial(double value) {
+ return Double.isNaN(value) || Double.isInfinite(value);
+ }
+
public static int[] getSortedSampleIndexes(int range, int sampleSize) {
return getSortedSampleIndexes(range, sampleSize, -1);
}
diff --git
a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteForLoopRemovalTest.java
b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteForLoopRemovalTest.java
new file mode 100644
index 0000000..1187948
--- /dev/null
+++
b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteForLoopRemovalTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.sysds.test.functions.rewrite;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.sysds.hops.OptimizerUtils;
+import org.apache.sysds.test.AutomatedTestBase;
+import org.apache.sysds.test.TestConfiguration;
+import org.apache.sysds.utils.Statistics;
+
+public class RewriteForLoopRemovalTest extends AutomatedTestBase
+{
+ private final static String TEST_NAME1 = "removal_for";
+ private final static String TEST_NAME2 = "removal_parfor";
+ private final static String TEST_DIR = "functions/rewrite/";
+ private final static String TEST_CLASS_DIR = TEST_DIR +
RewriteForLoopRemovalTest.class.getSimpleName() + "/";
+
+ private final static int rows = 10;
+ private final static int cols = 15;
+
+ @Override
+ public void setUp() {
+ addTestConfiguration(TEST_NAME1, new
TestConfiguration(TEST_CLASS_DIR, TEST_NAME1, new String[] {"R"}) );
+ addTestConfiguration(TEST_NAME2, new
TestConfiguration(TEST_CLASS_DIR, TEST_NAME2, new String[] {"R"}) );
+ }
+
+ @Test
+ public void runForLoopRemovalTest() {
+ runLoopRemovalTest(TEST_NAME1, true);
+ }
+
+ @Test
+ public void runParForLoopRemovalTest() {
+ runLoopRemovalTest(TEST_NAME2, true);
+ }
+
+ @Test
+ public void runForLoopRemovalNoRewriteTest() {
+ runLoopRemovalTest(TEST_NAME1, false);
+ }
+
+ @Test
+ public void runParForLoopRemovalNoRewriteTest() {
+ runLoopRemovalTest(TEST_NAME2, false);
+ }
+
+ private void runLoopRemovalTest(String testname, boolean rewrites)
+ {
+ boolean rewritesOld = OptimizerUtils.ALLOW_FOR_LOOP_REMOVAL;
+ OptimizerUtils.ALLOW_FOR_LOOP_REMOVAL = rewrites;
+
+ try {
+ TestConfiguration config =
getTestConfiguration(testname);
+ loadTestConfiguration(config);
+
+ String HOME = SCRIPT_DIR + TEST_DIR;
+ fullDMLScriptName = HOME + testname + ".dml";
+ programArgs = new String[]{"-explain", "-stats",
"-args",
+ Integer.toString(rows), Integer.toString(cols)};
+
+ runTest(true, false, null, -1);
+
+ //check for applied rewrite (which enabled CSE of sum)
+ long cnt = Statistics.getCPHeavyHitterCount("uak+");
+ long expected = rewrites ? 1 : 2;
+ Assert.assertEquals(expected, cnt);
+ }
+ finally {
+ OptimizerUtils.ALLOW_FOR_LOOP_REMOVAL = rewritesOld;
+ }
+ }
+}
diff --git a/src/test/scripts/functions/rewrite/removal_for.dml
b/src/test/scripts/functions/rewrite/removal_for.dml
new file mode 100644
index 0000000..6da6588
--- /dev/null
+++ b/src/test/scripts/functions/rewrite/removal_for.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 = rand(rows=$1, cols=$2);
+print(sum(X));
+
+for(i in seq(NaN,1))
+ print("Iteration "+i)
+
+print(sum(X))
diff --git a/src/test/scripts/functions/rewrite/removal_parfor.dml
b/src/test/scripts/functions/rewrite/removal_parfor.dml
new file mode 100644
index 0000000..2c32c5f
--- /dev/null
+++ b/src/test/scripts/functions/rewrite/removal_parfor.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 = rand(rows=$1, cols=$2);
+print(sum(X));
+
+parfor(i in seq(NaN,1))
+ print("Iteration "+i)
+
+print(sum(X))