This is an automated email from the ASF dual-hosted git repository.
mboehm7 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/systemds.git
The following commit(s) were added to refs/heads/main by this push:
new 37bf83d705 [SYSTEMDS-3358] New rewrite cumsum of constant vector to
sequence
37bf83d705 is described below
commit 37bf83d705676ddc4f15ccb2575d424577bc36a2
Author: Matthias Boehm <[email protected]>
AuthorDate: Sat Apr 23 23:47:50 2022 +0200
[SYSTEMDS-3358] New rewrite cumsum of constant vector to sequence
There are a few scripts that compute a sequence via cumsum on a constant
vector. We now automatically rewrite these patterns as follows:
weight = matrix(1/s_max, rows=s_max, cols=1)
weight = cumsum(weight)
--> (new rewrite)
weight = seq(1/s_max, s_max * 1/s_max, 1/s_max)
--> (constant folding)
weight = seq(1/s_max, 1, 1/s_max)
Furthermore, this patch includes the cleanup of a few warnings, and
the fix of a corrupted test that ran into null pointer exceptions on
probing the buffered std-output (which only exists if ran through mvn).
---
.../apache/sysds/hops/rewrite/HopRewriteUtils.java | 25 ++++---
.../RewriteAlgebraicSimplificationDynamic.java | 18 +++++
.../compress/colgroup/offset/OffsetSingle.java | 1 +
.../compress/colgroup/offset/OffsetTwo.java | 1 +
.../readers/ReadersTestCompareReaders.java | 1 +
.../pipelines/BuiltinTopkEvaluateTest.java | 1 -
...MaxTest.java => RewriteCumsumConstantTest.java} | 83 ++++++++++++----------
.../functions/rewrite/RewriteFoldMinMaxTest.java | 15 +---
.../functions/rewrite/RewriteFoldRCBindTest.java | 15 +---
.../rewrite/RewriteMMCBindZeroVector.java | 1 +
.../functions/rewrite/RewriteCumsumConstant1.dml | 28 ++++++++
.../functions/rewrite/RewriteCumsumConstant2.dml | 28 ++++++++
12 files changed, 139 insertions(+), 78 deletions(-)
diff --git a/src/main/java/org/apache/sysds/hops/rewrite/HopRewriteUtils.java
b/src/main/java/org/apache/sysds/hops/rewrite/HopRewriteUtils.java
index 25201ccc86..6dbc5e35b6 100644
--- a/src/main/java/org/apache/sysds/hops/rewrite/HopRewriteUtils.java
+++ b/src/main/java/org/apache/sysds/hops/rewrite/HopRewriteUtils.java
@@ -775,23 +775,22 @@ public class HopRewriteUtils {
public static DataGenOp createSeqDataGenOp( Hop input, boolean asc ) {
Hop to = input.rowsKnown() ? new LiteralOp(input.getDim1()) :
new UnaryOp("tmprows", DataType.SCALAR,
ValueType.INT64, OpOp1.NROW, input);
-
+ if( asc )
+ return createSeqDataGenOp(input, new LiteralOp(1), to,
new LiteralOp(1));
+ else
+ return createSeqDataGenOp(input, to, new LiteralOp(1),
new LiteralOp(-1));
+ }
+
+ public static DataGenOp createSeqDataGenOp(Hop proxy, Hop from, Hop to,
Hop incr) {
HashMap<String, Hop> params = new HashMap<>();
- if( asc ) {
- params.put(Statement.SEQ_FROM, new LiteralOp(1));
- params.put(Statement.SEQ_TO, to);
- params.put(Statement.SEQ_INCR, new LiteralOp(1));
- }
- else {
- params.put(Statement.SEQ_FROM, to);
- params.put(Statement.SEQ_TO, new LiteralOp(1));
- params.put(Statement.SEQ_INCR, new LiteralOp(-1));
- }
+ params.put(Statement.SEQ_FROM, from);
+ params.put(Statement.SEQ_TO, to);
+ params.put(Statement.SEQ_INCR, incr);
//note internal refresh size information
DataGenOp datagen = new DataGenOp(OpOpDG.SEQ, new
DataIdentifier("tmp"), params);
- datagen.setBlocksize(input.getBlocksize());
- copyLineNumbers(input, datagen);
+ datagen.setBlocksize(proxy.getBlocksize());
+ copyLineNumbers(proxy, datagen);
return datagen;
}
diff --git
a/src/main/java/org/apache/sysds/hops/rewrite/RewriteAlgebraicSimplificationDynamic.java
b/src/main/java/org/apache/sysds/hops/rewrite/RewriteAlgebraicSimplificationDynamic.java
index d0b1ac58d1..c142dab12f 100644
---
a/src/main/java/org/apache/sysds/hops/rewrite/RewriteAlgebraicSimplificationDynamic.java
+++
b/src/main/java/org/apache/sysds/hops/rewrite/RewriteAlgebraicSimplificationDynamic.java
@@ -177,6 +177,7 @@ public class RewriteAlgebraicSimplificationDynamic extends
HopRewriteRule
hi = simplifyDiagMatrixMult(hop, hi, i);
//e.g., diag(X%*%Y)->rowSums(X*t(Y)); if col vector
hi = simplifySumDiagToTrace(hi);
//e.g., sum(diag(X)) -> trace(X); if col vector
hi = simplifyLowerTriExtraction(hop, hi, i);
//e.g., X * cumsum(diag(matrix(1,nrow(X),1))) -> lower.tri
+ hi = simplifyConstantCumsum(hop, hi, i);
//e.g., cumsum(matrix(1/n,n,1)) -> seq(1/n, 1, 1/n)
hi = pushdownBinaryOperationOnDiag(hop, hi, i);
//e.g., diag(X)*7 -> diag(X*7); if col vector
hi = pushdownSumOnAdditiveBinary(hop, hi, i);
//e.g., sum(A+B) -> sum(A)+sum(B); if dims(A)==dims(B)
if(OptimizerUtils.ALLOW_OPERATOR_FUSION) {
@@ -1217,6 +1218,23 @@ public class RewriteAlgebraicSimplificationDynamic
extends HopRewriteRule
return hi;
}
+ private static Hop simplifyConstantCumsum(Hop parent, Hop hi, int pos) {
+ //pattern: cumsum(matrix(1/n,n,1)) -> seq(1/n, 1, 1/n)
+ if( HopRewriteUtils.isUnary(hi, OpOp1.CUMSUM)
+ &&
HopRewriteUtils.isDataGenOpWithConstantValue(hi.getInput(0))
+ && hi.getInput(0).getParent().size() == 1 //cumsum only
consumer
+ && hi.dimsKnown() && hi.getDim2() == 1 )
+ {
+ Hop constVal = ((DataGenOp)
hi.getInput(0)).getConstantValue();
+ Hop to = HopRewriteUtils.createBinary(new
LiteralOp(hi.getDim1()), constVal, OpOp2.MULT);
+ Hop hnew = HopRewriteUtils.createSeqDataGenOp(hi,
constVal, to, constVal);
+ HopRewriteUtils.replaceChildReference(parent, hi, hnew,
pos);
+ hi = hnew;
+ LOG.debug("Applied simplifyConstantCumsum (line
"+hi.getBeginLine()+").");
+ }
+ return hi;
+ }
+
@SuppressWarnings("unchecked")
private static Hop pushdownBinaryOperationOnDiag(Hop parent, Hop hi,
int pos)
{
diff --git
a/src/main/java/org/apache/sysds/runtime/compress/colgroup/offset/OffsetSingle.java
b/src/main/java/org/apache/sysds/runtime/compress/colgroup/offset/OffsetSingle.java
index b01bbb0e4c..38ffc8b34a 100644
---
a/src/main/java/org/apache/sysds/runtime/compress/colgroup/offset/OffsetSingle.java
+++
b/src/main/java/org/apache/sysds/runtime/compress/colgroup/offset/OffsetSingle.java
@@ -24,6 +24,7 @@ import java.io.DataOutput;
import java.io.IOException;
public class OffsetSingle extends AOffset {
+ private static final long serialVersionUID = -614636669776415032L;
private final int off;
diff --git
a/src/main/java/org/apache/sysds/runtime/compress/colgroup/offset/OffsetTwo.java
b/src/main/java/org/apache/sysds/runtime/compress/colgroup/offset/OffsetTwo.java
index ac2f6338f0..fbb447f4b7 100644
---
a/src/main/java/org/apache/sysds/runtime/compress/colgroup/offset/OffsetTwo.java
+++
b/src/main/java/org/apache/sysds/runtime/compress/colgroup/offset/OffsetTwo.java
@@ -24,6 +24,7 @@ import java.io.DataOutput;
import java.io.IOException;
public class OffsetTwo extends AOffset {
+ private static final long serialVersionUID = -3756723021239389269L;
private final int first;
private final int last;
diff --git
a/src/test/java/org/apache/sysds/test/component/compress/readers/ReadersTestCompareReaders.java
b/src/test/java/org/apache/sysds/test/component/compress/readers/ReadersTestCompareReaders.java
index 9ae2ad780e..480b147000 100644
---
a/src/test/java/org/apache/sysds/test/component/compress/readers/ReadersTestCompareReaders.java
+++
b/src/test/java/org/apache/sysds/test/component/compress/readers/ReadersTestCompareReaders.java
@@ -548,6 +548,7 @@ public class ReadersTestCompareReaders {
}
private class DenseBlockFP64Mock extends DenseBlockFP64 {
+ private static final long serialVersionUID =
-3601232958390554672L;
public DenseBlockFP64Mock(int[] dims, double[] data) {
super(dims, data);
diff --git
a/src/test/java/org/apache/sysds/test/functions/pipelines/BuiltinTopkEvaluateTest.java
b/src/test/java/org/apache/sysds/test/functions/pipelines/BuiltinTopkEvaluateTest.java
index b46dba2903..d1e7ba472d 100644
---
a/src/test/java/org/apache/sysds/test/functions/pipelines/BuiltinTopkEvaluateTest.java
+++
b/src/test/java/org/apache/sysds/test/functions/pipelines/BuiltinTopkEvaluateTest.java
@@ -24,7 +24,6 @@ import org.apache.sysds.test.AutomatedTestBase;
import org.apache.sysds.test.TestConfiguration;
import org.apache.sysds.test.TestUtils;
import org.junit.Assert;
-import org.junit.Ignore;
import org.junit.Test;
public class BuiltinTopkEvaluateTest extends AutomatedTestBase {
diff --git
a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteFoldMinMaxTest.java
b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteCumsumConstantTest.java
similarity index 55%
copy from
src/test/java/org/apache/sysds/test/functions/rewrite/RewriteFoldMinMaxTest.java
copy to
src/test/java/org/apache/sysds/test/functions/rewrite/RewriteCumsumConstantTest.java
index cf8b4be365..e5a8625325 100644
---
a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteFoldMinMaxTest.java
+++
b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteCumsumConstantTest.java
@@ -21,7 +21,6 @@ package org.apache.sysds.test.functions.rewrite;
import org.junit.Assert;
import org.junit.Test;
-import org.apache.sysds.api.DMLScript;
import org.apache.sysds.common.Types.ExecMode;
import org.apache.sysds.hops.OptimizerUtils;
import org.apache.sysds.common.Types.ExecType;
@@ -29,18 +28,16 @@ import
org.apache.sysds.runtime.matrix.data.MatrixValue.CellIndex;
import org.apache.sysds.test.AutomatedTestBase;
import org.apache.sysds.test.TestConfiguration;
import org.apache.sysds.test.TestUtils;
-import org.apache.sysds.utils.Statistics;
-public class RewriteFoldMinMaxTest extends AutomatedTestBase
+public class RewriteCumsumConstantTest extends AutomatedTestBase
{
- private static final String TEST_NAME1 = "RewriteFoldMin";
- private static final String TEST_NAME2 = "RewriteFoldMax";
+ private static final String TEST_NAME1 = "RewriteCumsumConstant1";
+ private static final String TEST_NAME2 = "RewriteCumsumConstant2";
private static final String TEST_DIR = "functions/rewrite/";
- private static final String TEST_CLASS_DIR = TEST_DIR +
RewriteFoldMinMaxTest.class.getSimpleName() + "/";
+ private static final String TEST_CLASS_DIR = TEST_DIR +
RewriteCumsumConstantTest.class.getSimpleName() + "/";
- private static final int rows = 1932;
- private static final int cols = 14;
+ private static final int rows = 4;
@Override
public void setUp() {
@@ -50,37 +47,48 @@ public class RewriteFoldMinMaxTest extends AutomatedTestBase
}
@Test
- public void testRewriteFoldMinNoRewrite() {
- testRewriteFoldMinMax( TEST_NAME1, false, ExecType.CP );
+ public void testRewriteCumsumPosNoRewrite() {
+ testRewriteCumsumConst( TEST_NAME1, false, 1, ExecType.CP );
}
@Test
- public void testRewriteFoldMinRewrite() {
- testRewriteFoldMinMax( TEST_NAME1, true, ExecType.CP );
+ public void testRewriteCumsumPosRewrite() {
+ testRewriteCumsumConst( TEST_NAME1, true, 1, ExecType.CP );
}
@Test
- public void testRewriteFoldMaxNoRewrite() {
- testRewriteFoldMinMax( TEST_NAME2, false, ExecType.CP );
+ public void testRewriteCumsumNegNoRewrite() {
+ testRewriteCumsumConst( TEST_NAME2, false, 1, ExecType.CP );
}
@Test
- public void testRewriteFoldMaxRewrite() {
- testRewriteFoldMinMax( TEST_NAME2, true, ExecType.CP );
+ public void testRewriteCumsumNegRewrite() {
+ testRewriteCumsumConst( TEST_NAME2, true, 1, ExecType.CP );
+ }
+
+ @Test
+ public void testRewriteCumsumPos2NoRewrite() {
+ testRewriteCumsumConst( TEST_NAME1, false, 2, ExecType.CP );
+ }
+
+ @Test
+ public void testRewriteCumsumPos2Rewrite() {
+ testRewriteCumsumConst( TEST_NAME1, true, 2, ExecType.CP );
+ }
+
+ @Test
+ public void testRewriteCumsumNeg2NoRewrite() {
+ testRewriteCumsumConst( TEST_NAME2, false, 2, ExecType.CP );
+ }
+
+ @Test
+ public void testRewriteCumsumNeg2Rewrite() {
+ testRewriteCumsumConst( TEST_NAME2, true, 2, ExecType.CP );
}
- private void testRewriteFoldMinMax( String testname, boolean rewrites,
ExecType et )
+ private void testRewriteCumsumConst( String testname, boolean rewrites,
int cols, ExecType et )
{
- ExecMode platformOld = rtplatform;
- switch( et ){
- case SPARK: rtplatform = ExecMode.SPARK; break;
- default: rtplatform = ExecMode.HYBRID; break;
- }
-
- boolean sparkConfigOld = DMLScript.USE_LOCAL_SPARK_CONFIG;
- if( rtplatform == ExecMode.SPARK || rtplatform ==
ExecMode.HYBRID )
- DMLScript.USE_LOCAL_SPARK_CONFIG = true;
-
+ ExecMode platformOld = setExecMode(et);
boolean oldFlag = OptimizerUtils.ALLOW_ALGEBRAIC_SIMPLIFICATION;
try {
@@ -89,29 +97,28 @@ public class RewriteFoldMinMaxTest extends AutomatedTestBase
String HOME = SCRIPT_DIR + TEST_DIR;
fullDMLScriptName = HOME + testname + ".dml";
- programArgs = new String[]{ "-stats", "-args",
String.valueOf(rows),
- String.valueOf(cols), output("R") };
+ programArgs = new String[]{ "-stats", "-args",
+ String.valueOf(rows), String.valueOf(cols),
output("R") };
OptimizerUtils.ALLOW_ALGEBRAIC_SIMPLIFICATION =
rewrites;
//run performance tests
- runTest(true, false, null, -1);
+ runTest(true, false, null, -1);
//compare matrices
Double ret = readDMLMatrixFromOutputDir("R").get(new
CellIndex(1,1));
- Assert.assertEquals("Wrong result",
Double.valueOf(5*rows*cols), ret);
+ double expected = cols * ((double)rows*(rows+1)/2)/rows
+ * (testname.equals(TEST_NAME1) ? 1 : -1);
+ Assert.assertEquals("Wrong result",
Double.valueOf(expected), ret);
//check for applied rewrites
- if( rewrites ) {
-
Assert.assertTrue(!heavyHittersContainsString("min") &&
!heavyHittersContainsString("max")
- && (!testname.equals(TEST_NAME1) ||
Statistics.getCPHeavyHitterCount("nmin") == 1)
- && (!testname.equals(TEST_NAME2) ||
Statistics.getCPHeavyHitterCount("nmax") == 1));
+ if( rewrites && cols==1 ) { //sequence only for col
vectors
+
Assert.assertTrue(!heavyHittersContainsString("rand")
+ &&
!heavyHittersContainsString("ucumk+"));
}
}
finally {
OptimizerUtils.ALLOW_ALGEBRAIC_SIMPLIFICATION = oldFlag;
- DMLScript.USE_LOCAL_SPARK_CONFIG = sparkConfigOld;
- rtplatform = platformOld;
+ resetExecMode(platformOld);
}
}
}
-
diff --git
a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteFoldMinMaxTest.java
b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteFoldMinMaxTest.java
index cf8b4be365..bcae201717 100644
---
a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteFoldMinMaxTest.java
+++
b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteFoldMinMaxTest.java
@@ -21,7 +21,6 @@ package org.apache.sysds.test.functions.rewrite;
import org.junit.Assert;
import org.junit.Test;
-import org.apache.sysds.api.DMLScript;
import org.apache.sysds.common.Types.ExecMode;
import org.apache.sysds.hops.OptimizerUtils;
import org.apache.sysds.common.Types.ExecType;
@@ -71,16 +70,7 @@ public class RewriteFoldMinMaxTest extends AutomatedTestBase
private void testRewriteFoldMinMax( String testname, boolean rewrites,
ExecType et )
{
- ExecMode platformOld = rtplatform;
- switch( et ){
- case SPARK: rtplatform = ExecMode.SPARK; break;
- default: rtplatform = ExecMode.HYBRID; break;
- }
-
- boolean sparkConfigOld = DMLScript.USE_LOCAL_SPARK_CONFIG;
- if( rtplatform == ExecMode.SPARK || rtplatform ==
ExecMode.HYBRID )
- DMLScript.USE_LOCAL_SPARK_CONFIG = true;
-
+ ExecMode platformOld = setExecMode(et);
boolean oldFlag = OptimizerUtils.ALLOW_ALGEBRAIC_SIMPLIFICATION;
try {
@@ -109,8 +99,7 @@ public class RewriteFoldMinMaxTest extends AutomatedTestBase
}
finally {
OptimizerUtils.ALLOW_ALGEBRAIC_SIMPLIFICATION = oldFlag;
- DMLScript.USE_LOCAL_SPARK_CONFIG = sparkConfigOld;
- rtplatform = platformOld;
+ resetExecMode(platformOld);
}
}
}
diff --git
a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteFoldRCBindTest.java
b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteFoldRCBindTest.java
index ca09e23326..f25ce672f3 100644
---
a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteFoldRCBindTest.java
+++
b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteFoldRCBindTest.java
@@ -21,7 +21,6 @@ package org.apache.sysds.test.functions.rewrite;
import org.junit.Assert;
import org.junit.Test;
-import org.apache.sysds.api.DMLScript;
import org.apache.sysds.common.Types.ExecMode;
import org.apache.sysds.hops.OptimizerUtils;
import org.apache.sysds.common.Types.ExecType;
@@ -71,16 +70,7 @@ public class RewriteFoldRCBindTest extends AutomatedTestBase
private void testRewriteFoldRCBind( String testname, boolean rewrites,
ExecType et )
{
- ExecMode platformOld = rtplatform;
- switch( et ){
- case SPARK: rtplatform = ExecMode.SPARK; break;
- default: rtplatform = ExecMode.HYBRID; break;
- }
-
- boolean sparkConfigOld = DMLScript.USE_LOCAL_SPARK_CONFIG;
- if( rtplatform == ExecMode.SPARK || rtplatform ==
ExecMode.HYBRID )
- DMLScript.USE_LOCAL_SPARK_CONFIG = true;
-
+ ExecMode platformOld = setExecMode(et);
boolean oldFlag = OptimizerUtils.ALLOW_ALGEBRAIC_SIMPLIFICATION;
try {
@@ -109,8 +99,7 @@ public class RewriteFoldRCBindTest extends AutomatedTestBase
}
finally {
OptimizerUtils.ALLOW_ALGEBRAIC_SIMPLIFICATION = oldFlag;
- DMLScript.USE_LOCAL_SPARK_CONFIG = sparkConfigOld;
- rtplatform = platformOld;
+ resetExecMode(platformOld);
}
}
}
diff --git
a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteMMCBindZeroVector.java
b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteMMCBindZeroVector.java
index 6a8bfd77ae..d344d7eee3 100644
---
a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteMMCBindZeroVector.java
+++
b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteMMCBindZeroVector.java
@@ -121,6 +121,7 @@ public class RewriteMMCBindZeroVector extends
AutomatedTestBase {
writeInputMatrixWithMTD("Y", Y, false);
// execute tests
+ setOutputBuffering(true);
String out = runTest(null).toString();
for(String line : out.split("\n")) {
diff --git a/src/test/scripts/functions/rewrite/RewriteCumsumConstant1.dml
b/src/test/scripts/functions/rewrite/RewriteCumsumConstant1.dml
new file mode 100644
index 0000000000..445f0e4c7a
--- /dev/null
+++ b/src/test/scripts/functions/rewrite/RewriteCumsumConstant1.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(1/$1, $1, $2)
+Y = cumsum(X);
+
+while(FALSE){}
+R = as.matrix(sum(Y))
+
+write(R, $3);
diff --git a/src/test/scripts/functions/rewrite/RewriteCumsumConstant2.dml
b/src/test/scripts/functions/rewrite/RewriteCumsumConstant2.dml
new file mode 100644
index 0000000000..fd1982eae1
--- /dev/null
+++ b/src/test/scripts/functions/rewrite/RewriteCumsumConstant2.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(-1/$1, $1, $2)
+Y = cumsum(X);
+
+while(FALSE){}
+R = as.matrix(sum(Y))
+
+write(R, $3);