From b4743c17e527f7a978da4043038b84672d6759ed Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Tue, 6 Oct 2026 14:52:13 +0200 Subject: [PATCH 01/14] refactor(main/hops/rewrite/RewriteSplitDagUnknownNnz.java): rename java class to not being exclusive for read instructions --- .../java/org/apache/sysds/hops/rewrite/ProgramRewriter.java | 2 +- ...litDagUnknownNnzRead.java => RewriteSplitDagUnknownNnz.java} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/main/java/org/apache/sysds/hops/rewrite/{RewriteSplitDagUnknownNnzRead.java => RewriteSplitDagUnknownNnz.java} (98%) 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 ac3698bdcac..c1751845544 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/ProgramRewriter.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/ProgramRewriter.java @@ -107,7 +107,7 @@ public ProgramRewriter(boolean staticRewrites, boolean dynamicRewrites) if( OptimizerUtils.ALLOW_SPLIT_HOP_DAGS ) _sbRuleSet.add( new RewriteSplitDagUnknownCSVRead() ); //dependency: reblock, merge blocks if(ConfigurationManager.getDMLConfig().getBooleanValue(DMLConfig.SPARSITY_RECOMPILE)) - _sbRuleSet.add(new RewriteSplitDagUnknownNnzRead()); + _sbRuleSet.add(new RewriteSplitDagUnknownNnz()); if( OptimizerUtils.ALLOW_SPLIT_HOP_DAGS && ConfigurationManager.getCompilerConfigFlag(ConfigType.ALLOW_INDIVIDUAL_SB_SPECIFIC_OPS) ) _sbRuleSet.add( new RewriteSplitDagDataDependentOperators() ); //dependency: merge blocks diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnzRead.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java similarity index 98% rename from src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnzRead.java rename to src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java index fd39115a982..7c188ca23c8 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnzRead.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java @@ -36,7 +36,7 @@ * Rule: Split Hop DAG after reads with unknown nnz. This is important to create recompile * hooks if mtd has unspecified nnz. */ -public class RewriteSplitDagUnknownNnzRead extends StatementBlockRewriteRule { +public class RewriteSplitDagUnknownNnz extends StatementBlockRewriteRule { @Override public boolean createsSplitDag() { return true; From 033ef3a4fe5ced012bae2725e55c3274187de257 Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Mon, 12 Oct 2026 17:00:39 +0200 Subject: [PATCH 02/14] feat(main/hops/rewrite/RewriteSplitDagUnknownNnz.java): split matrix multiplication chain into their own statement block --- .../rewrite/RewriteSplitDagUnknownNnz.java | 163 +++++++++++------- 1 file changed, 102 insertions(+), 61 deletions(-) diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java index 7c188ca23c8..2bd27b1cd0b 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java @@ -20,17 +20,20 @@ package org.apache.sysds.hops.rewrite; import java.util.ArrayList; +import java.util.Collections; +import java.util.ListIterator; import java.util.List; import org.apache.sysds.common.Types.OpOpData; import org.apache.sysds.conf.ConfigurationManager; import org.apache.sysds.conf.DMLConfig; +import org.apache.sysds.hops.AggBinaryOp; import org.apache.sysds.hops.DataOp; import org.apache.sysds.hops.Hop; -import org.apache.sysds.hops.LiteralOp; import org.apache.sysds.parser.DataIdentifier; import org.apache.sysds.parser.StatementBlock; import org.apache.sysds.parser.VariableSet; +import org.apache.sysds.runtime.DMLRuntimeException; /** * Rule: Split Hop DAG after reads with unknown nnz. This is important to create recompile @@ -46,67 +49,59 @@ public boolean createsSplitDag() { public List rewriteStatementBlock(StatementBlock sb, ProgramRewriteStatus state) { ArrayList ret = new ArrayList<>(); - // collect all read hops w/ unknown nnz - ArrayList cand = new ArrayList<>(); - collectReadHopsUnknownNnz(sb.getHops(), cand); + // collect all candidate hops in levels based on their data dependencies + ArrayList> candidateLists = new ArrayList>(); + collectNnzComputationCandidates(sb.getHops(), candidateLists); + Collections.reverse(candidateLists); // reverse order of dependency levels // split hop dag on demand and if sparsity rewrites are enabled (splitting is pointless w/o rewrite) - if(!cand.isEmpty() && ConfigurationManager.getDMLConfig().getBooleanValue(DMLConfig.SPARSITY_REWRITES)) { - // duplicate sb incl live variable sets - StatementBlock sb1 = new StatementBlock(); - sb1.setDMLProg(sb.getDMLProg()); - sb1.setParseInfo(sb); - sb1.setLiveIn(new VariableSet()); - sb1.setLiveOut(new VariableSet()); - - // move reads incl reblock to new statement block - // (and replace original persistent read with transient read) - ArrayList sb1hops = new ArrayList<>(); - for(Hop reblock : cand) { - // replace reblock inputs to avoid dangling references across dags - // (otherwise, for instance, literal ops are shared across dags) - for(int i = 0; i < reblock.getInput().size(); i++) { - if(reblock.getInput().get(i) instanceof LiteralOp) { - HopRewriteUtils.replaceChildReference(reblock, reblock.getInput().get(i), - new LiteralOp((LiteralOp) reblock.getInput().get(i))); + if(!candidateLists.isEmpty() && ConfigurationManager.getDMLConfig().getBooleanValue(DMLConfig.SPARSITY_REWRITES)) { + for(ArrayList cand : candidateLists) { + // duplicate sb incl live variable sets + StatementBlock sbSplit = new StatementBlock(); + sbSplit.setDMLProg(sb.getDMLProg()); + sbSplit.setParseInfo(sb); + sbSplit.setLiveIn(new VariableSet()); + sbSplit.setLiveOut(new VariableSet()); + + // move candidate hops to new statement block + // (and replace original persistent read with transient read) + ArrayList sbSplitHops = new ArrayList<>(); + for(Hop cHop : cand) { + // create new transient read + DataOp tRead = HopRewriteUtils.createTransientRead(cHop.getName(), cHop); + tRead.setRequiresRecompile(); + + // replace persistent read with transient read + ArrayList parents = new ArrayList<>(cHop.getParent()); + for(int i = 0; i < parents.size(); i++) { + Hop parent = parents.get(i); + HopRewriteUtils.replaceChildReference(parent, cHop, tRead); } - } - // create new transient read - DataOp tRead = HopRewriteUtils.createTransientRead(reblock.getName(), reblock); - tRead.setRequiresRecompile(); + // add reblock sub dag to first statement block + DataOp tWrite = HopRewriteUtils.createTransientWrite(cHop.getName(), cHop); + sbSplitHops.add(tWrite); - // replace reblock with transient read - ArrayList parents = new ArrayList<>(reblock.getParent()); - for(int i = 0; i < parents.size(); i++) { - Hop parent = parents.get(i); - HopRewriteUtils.replaceChildReference(parent, reblock, tRead); + // update live in and out of new statement block (for piggybacking) + DataIdentifier diVar = sb.variablesRead().getVariable(cHop.getName()); + if(diVar != null) { // var read should always exist because persistent read + sbSplit.liveOut().addVariable(cHop.getName(), new DataIdentifier(diVar)); + sb.liveIn().addVariable(cHop.getName(), new DataIdentifier(diVar)); + } } - // add reblock sub dag to first statement block - DataOp tWrite = HopRewriteUtils.createTransientWrite(reblock.getName(), reblock); - sb1hops.add(tWrite); - - // update live in and out of new statement block (for piggybacking) - DataIdentifier diVar = sb.variablesRead().getVariable(reblock.getName()); - if(diVar != null) { // var read should always exist because persistent read - sb1.liveOut().addVariable(reblock.getName(), new DataIdentifier(diVar)); - sb.liveIn().addVariable(reblock.getName(), new DataIdentifier(diVar)); - } + sbSplit.setHops(sbSplitHops); + sbSplit.updateRecompilationFlag(); + ret.add(sbSplit); // statement block with permanent read and transient write + sbSplit.setSplitDag(true); // avoid later merge by other rewrites } - - sb1.setHops(sb1hops); - sb1.updateRecompilationFlag(); - ret.add(sb1); // statement block with permanent read and transient write - ret.add(sb); // statement block with transient read sb.setSplitDag(true); // avoid later merge by other rewrites LOG.debug("Applied splitDagUnknownNnzRead."); } - // keep original hop dag - else { - ret.add(sb); - } + + ret.add(sb); return ret; } @@ -115,15 +110,48 @@ public List rewriteStatementBlocks(List sbs, Pro return sbs; } - private void collectReadHopsUnknownNnz(ArrayList roots, ArrayList cand) { + private void rSplitMMChain(Hop parentHop, ListIterator> candListIter) { + if(parentHop.getInput() == null) + throw new DMLRuntimeException("rSplitMMChain should be called for MM chain hops only. " + + "An MM hop must have inputs."); + // split children again when operation is different from matrix multiply + for(Hop childHop : parentHop.getInput()) { + if(childHop.isVisited()) + continue; + if(HopRewriteUtils.isMatrixMultiply(childHop)) { + rSplitMMChain(childHop, candListIter); + } + else { + getNextLevel(candListIter).add(childHop); + rCollectNnzComputationCandidatesChildren(childHop, candListIter); + candListIter.previous(); + } + childHop.setVisited(); + } + } + + private void collectNnzComputationCandidates(ArrayList roots, ArrayList> candidateLists) { if(roots == null) return; Hop.resetVisitStatus(roots); - for(Hop root : roots) - collectReadHopsUnknownNnz(root, cand); + for(Hop root : roots) { + ListIterator> candListIter = candidateLists.listIterator(); + rCollectNnzComputationCandidates(root, candListIter); + } + } + + private ArrayList getNextLevel(ListIterator> liter) { + if(liter.hasNext()) { + return liter.next(); + } + else { + ArrayList newLevel = new ArrayList(); + liter.add(newLevel); + return newLevel; + } } - private void collectReadHopsUnknownNnz(Hop hop, ArrayList cand) { + private void rCollectNnzComputationCandidates(Hop hop, ListIterator> candListIter) { if(hop.isVisited()) return; @@ -131,17 +159,30 @@ private void collectReadHopsUnknownNnz(Hop hop, ArrayList cand) { if(hop instanceof DataOp) { DataOp dop = (DataOp) hop; if(dop.getOp() == OpOpData.PERSISTENTREAD && !dop.nnzKnown()) { - cand.add(dop); + getNextLevel(candListIter).add(dop); + candListIter.previous(); // reset level increment (no children to process) } - } - - // process children - if(hop.getInput() != null) { - for(Hop c : hop.getInput()) { - collectReadHopsUnknownNnz(c, cand); + else { + rCollectNnzComputationCandidatesChildren(hop, candListIter); } } + else if(HopRewriteUtils.isMatrixMultiply(hop) && !((AggBinaryOp) hop).hasLeftPMInput()) { + getNextLevel(candListIter).add(hop); + rSplitMMChain(hop, candListIter); + candListIter.previous(); // reset level increment after processing children + } + else{ + rCollectNnzComputationCandidatesChildren(hop, candListIter); + } hop.setVisited(); } + + private void rCollectNnzComputationCandidatesChildren(Hop parentHop, ListIterator> candListIter) { + // process children + if(parentHop.getInput() != null) { + for(Hop c : parentHop.getInput()) + rCollectNnzComputationCandidates(c, candListIter); + } + } } From 8048ee0e6ec4bdaa0c2f19c16647c132b7a804f6 Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Mon, 12 Oct 2026 17:23:44 +0200 Subject: [PATCH 03/14] refactor(main/hops/rewrite/RewriteSplitDagUnknownNnz.java): create separate method for processing the children of matrix multiplication hops chore(test/functions/rewrite/RewriteSplitDagUnkownNnzReadTest.java): update assert string check to the new version of the log output --- .../rewrite/RewriteSplitDagUnknownNnz.java | 48 +++++++++---------- .../RewriteSplitDagUnknownNnzReadTest.java | 4 +- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java index 2bd27b1cd0b..a4f9e41a9e3 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java @@ -97,9 +97,8 @@ public List rewriteStatementBlock(StatementBlock sb, ProgramRewr sbSplit.setSplitDag(true); // avoid later merge by other rewrites } sb.setSplitDag(true); // avoid later merge by other rewrites - LOG.debug("Applied splitDagUnknownNnzRead."); + LOG.debug("RewriteSplitDagUnknownNnz: Split DAG into " + (ret.size() + 1) + " statement blocks."); } - ret.add(sb); return ret; @@ -110,23 +109,28 @@ public List rewriteStatementBlocks(List sbs, Pro return sbs; } - private void rSplitMMChain(Hop parentHop, ListIterator> candListIter) { + private void rSplitMMChain(Hop hop, ListIterator> candListIter) { + if(hop.isVisited()) + return; + if(HopRewriteUtils.isMatrixMultiply(hop)) { + rSplitMMChainChildren(hop, candListIter); + } + else { + // split again when operation is different from matrix multiply (i.e., before mm chain) + getNextLevel(candListIter).add(hop); + rCollectNnzComputationCandidatesChildren(hop, candListIter); + candListIter.previous(); + } + hop.setVisited(); + } + + private void rSplitMMChainChildren(Hop parentHop, ListIterator> candListIter) { if(parentHop.getInput() == null) throw new DMLRuntimeException("rSplitMMChain should be called for MM chain hops only. " + "An MM hop must have inputs."); - // split children again when operation is different from matrix multiply + // process children for(Hop childHop : parentHop.getInput()) { - if(childHop.isVisited()) - continue; - if(HopRewriteUtils.isMatrixMultiply(childHop)) { - rSplitMMChain(childHop, candListIter); - } - else { - getNextLevel(candListIter).add(childHop); - rCollectNnzComputationCandidatesChildren(childHop, candListIter); - candListIter.previous(); - } - childHop.setVisited(); + rSplitMMChain(childHop, candListIter); } } @@ -134,8 +138,8 @@ private void collectNnzComputationCandidates(ArrayList roots, ArrayList> candListIter = candidateLists.listIterator(); for(Hop root : roots) { - ListIterator> candListIter = candidateLists.listIterator(); rCollectNnzComputationCandidates(root, candListIter); } } @@ -156,19 +160,13 @@ private void rCollectNnzComputationCandidates(Hop hop, ListIterator l.getMessage().toString()).toArray(String[]::new)); - Assert.assertTrue(log_original_string.contains("Applied splitDagUnknownNnzRead.")); + Assert.assertTrue(log_original_string.contains("RewriteSplitDagUnknownNnz: Split DAG into 4 statement blocks.")); Assert.assertTrue(log_original_string.contains("mmchainoptsparse")); Assert.assertTrue(log_original_string.contains("Optimal Sparse MM Chain:")); // check reference log output String log_reference_string = String.join(DELIMITER, log_reference.stream().map(l -> l.getMessage().toString()).toArray(String[]::new)); - Assert.assertFalse(log_reference_string.contains("splitDagUnknownNnzRead")); + Assert.assertFalse(log_reference_string.contains("RewriteSplitDagUnknownNnz")); Assert.assertFalse(log_reference_string.contains("mmchainoptsparse")); } finally { From 12ed7dfdad80a510819c7f1160ae500901e6719e Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Mon, 12 Oct 2026 17:29:37 +0200 Subject: [PATCH 04/14] refactor(test/rewrite/RewriteSplitDagUnknownNnzTest.java): rename test --- ...nNnzReadTest.java => RewriteSplitDagUnknownNnzTest.java} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename src/test/java/org/apache/sysds/test/functions/rewrite/{RewriteSplitDagUnknownNnzReadTest.java => RewriteSplitDagUnknownNnzTest.java} (97%) diff --git a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzReadTest.java b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java similarity index 97% rename from src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzReadTest.java rename to src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java index d33809fe60f..7676fd53323 100644 --- a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzReadTest.java +++ b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java @@ -51,12 +51,12 @@ @RunWith(value = Parameterized.class) @net.jcip.annotations.NotThreadSafe -public class RewriteSplitDagUnknownNnzReadTest extends AutomatedTestBase { +public class RewriteSplitDagUnknownNnzTest extends AutomatedTestBase { private static final String TEST_NAME = "RewriteSplitDagUnknownNnz"; private static final String TEST_DIR = "functions/rewrite/"; private static final String TEST_CLASS_DIR = - TEST_DIR + RewriteSplitDagUnknownNnzReadTest.class.getSimpleName() + "/"; + TEST_DIR + RewriteSplitDagUnknownNnzTest.class.getSimpleName() + "/"; private static final String PACKAGE_REWRITE = "org.apache.sysds.hops.rewrite.HopRewriteRule"; private static final String PACKAGE_RECOMPILE = "org.apache.sysds.hops.rewrite.StatementBlockRewriteRule"; private static Level _oldLevelRewrite; @@ -103,7 +103,7 @@ public void tearDown() { } @Test - public void testSplitDagUnknownNnzRead() { + public void testSplitDagUnknownNnzReadAndMMChain() { boolean oldFlag1 = OptimizerUtils.ALLOW_TRANSPOSE_MMCHAIN_REWRITES; boolean oldFlag2 = OptimizerUtils.ALLOW_SUM_PRODUCT_REWRITES; DMLConfig oldDMLConfig = ConfigurationManager.getDMLConfig(); From 87cc58436dbc8a532a7b448e7752b0856841b291 Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Tue, 13 Oct 2026 15:19:31 +0200 Subject: [PATCH 05/14] fix(main/hops/rewrite/RewriteSplitDagUnknownNnz.java): split mm chain into separate statement block only before the chain instead of before and after the chain --- .../rewrite/RewriteSplitDagUnknownNnz.java | 67 +++++++------------ .../RewriteSplitDagUnknownNnzTest.java | 2 +- 2 files changed, 27 insertions(+), 42 deletions(-) diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java index a4f9e41a9e3..2b63b99a6fe 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java @@ -33,7 +33,6 @@ import org.apache.sysds.parser.DataIdentifier; import org.apache.sysds.parser.StatementBlock; import org.apache.sysds.parser.VariableSet; -import org.apache.sysds.runtime.DMLRuntimeException; /** * Rule: Split Hop DAG after reads with unknown nnz. This is important to create recompile @@ -109,38 +108,13 @@ public List rewriteStatementBlocks(List sbs, Pro return sbs; } - private void rSplitMMChain(Hop hop, ListIterator> candListIter) { - if(hop.isVisited()) - return; - if(HopRewriteUtils.isMatrixMultiply(hop)) { - rSplitMMChainChildren(hop, candListIter); - } - else { - // split again when operation is different from matrix multiply (i.e., before mm chain) - getNextLevel(candListIter).add(hop); - rCollectNnzComputationCandidatesChildren(hop, candListIter); - candListIter.previous(); - } - hop.setVisited(); - } - - private void rSplitMMChainChildren(Hop parentHop, ListIterator> candListIter) { - if(parentHop.getInput() == null) - throw new DMLRuntimeException("rSplitMMChain should be called for MM chain hops only. " + - "An MM hop must have inputs."); - // process children - for(Hop childHop : parentHop.getInput()) { - rSplitMMChain(childHop, candListIter); - } - } - private void collectNnzComputationCandidates(ArrayList roots, ArrayList> candidateLists) { if(roots == null) return; Hop.resetVisitStatus(roots); ListIterator> candListIter = candidateLists.listIterator(); for(Hop root : roots) { - rCollectNnzComputationCandidates(root, candListIter); + rCollectNnzComputationCandidates(root, candListIter, null); } } @@ -155,32 +129,43 @@ private ArrayList getNextLevel(ListIterator> liter) { } } - private void rCollectNnzComputationCandidates(Hop hop, ListIterator> candListIter) { + private void rCollectNnzComputationCandidates(Hop hop, ListIterator> candListIter, Hop parentHop) { if(hop.isVisited()) return; - // collect persistent reads with unknown nnz - if(hop instanceof DataOp && ((DataOp) hop).getOp() == OpOpData.PERSISTENTREAD && !((DataOp) hop).nnzKnown()) { - getNextLevel(candListIter).add(hop); - candListIter.previous(); // reset level increment (no children to process) - } - else if(HopRewriteUtils.isMatrixMultiply(hop) && !((AggBinaryOp) hop).hasLeftPMInput()) { - getNextLevel(candListIter).add(hop); - rSplitMMChainChildren(hop, candListIter); - candListIter.previous(); // reset level increment after processing children + if(!hop.nnzKnown()) { + if(hop instanceof DataOp && ((DataOp) hop).getOp() == OpOpData.PERSISTENTREAD) { + // collect persistent reads with unknown nnz + splitAndProcessChildren(hop, candListIter); + } + else if(parentHop != null && HopRewriteUtils.isMatrixMultiply(parentHop) && + !((AggBinaryOp) parentHop).hasLeftPMInput() && !HopRewriteUtils.isMatrixMultiply(hop)) { + // split before mm chain to ensure correct sparsity information for mm chain sparsity estimation + splitAndProcessChildren(hop, candListIter); + } + else{ + rCollectCandidatesChildren(hop, candListIter); + } } - else{ - rCollectNnzComputationCandidatesChildren(hop, candListIter); + else { + rCollectCandidatesChildren(hop, candListIter); } hop.setVisited(); } - private void rCollectNnzComputationCandidatesChildren(Hop parentHop, ListIterator> candListIter) { + private void rCollectCandidatesChildren(Hop parentHop, ListIterator> candListIter) { // process children if(parentHop.getInput() != null) { for(Hop c : parentHop.getInput()) - rCollectNnzComputationCandidates(c, candListIter); + rCollectNnzComputationCandidates(c, candListIter, parentHop); } } + + // add the hop to the candidates and proceed with its inputs + private void splitAndProcessChildren(Hop hop, ListIterator> candListIter) { + getNextLevel(candListIter).add(hop); + rCollectCandidatesChildren(hop, candListIter); + candListIter.previous(); // reset level increment after processing children + } } diff --git a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java index 7676fd53323..72eaef552ea 100644 --- a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java +++ b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java @@ -163,7 +163,7 @@ public void testSplitDagUnknownNnzReadAndMMChain() { // check original log output String log_original_string = String.join(DELIMITER, log_original.stream().map(l -> l.getMessage().toString()).toArray(String[]::new)); - Assert.assertTrue(log_original_string.contains("RewriteSplitDagUnknownNnz: Split DAG into 4 statement blocks.")); + Assert.assertTrue(log_original_string.contains("RewriteSplitDagUnknownNnz: Split DAG into 3 statement blocks.")); Assert.assertTrue(log_original_string.contains("mmchainoptsparse")); Assert.assertTrue(log_original_string.contains("Optimal Sparse MM Chain:")); // check reference log output From e3f431503c7689e4c87eb80a5d503ee0c897dd5c Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Fri, 16 Oct 2026 17:01:17 +0200 Subject: [PATCH 06/14] fix(main/hops/rewrite/RewriteSplitDagUnknownNnz.java): split BEFORE and AFTER the MM chain regardless of whether input nnz are known or not NOTE: Thereby, we create a separate statement block for every mm chain, in which we can optimize and rewrite the mm chain isolated from other operations. --- .../rewrite/RewriteSplitDagUnknownNnz.java | 28 +++++++++---------- .../RewriteSplitDagUnknownNnzTest.java | 4 +-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java index 2b63b99a6fe..86ebb01296f 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java @@ -133,21 +133,21 @@ private void rCollectNnzComputationCandidates(Hop hop, ListIterator data() { return Arrays.asList(new Object[][] { - // {rows, cols, sparsities, eps}, + // {rows, cols, sparsities, eps, tsmm}, {1000, 300, new double[] {0.10d, 0.10d}, Math.pow(10, -10), false}, {5, 3, new double[] {0.1, 1}, Math.pow(10, -10), true},}); } @@ -163,7 +163,7 @@ public void testSplitDagUnknownNnzReadAndMMChain() { // check original log output String log_original_string = String.join(DELIMITER, log_original.stream().map(l -> l.getMessage().toString()).toArray(String[]::new)); - Assert.assertTrue(log_original_string.contains("RewriteSplitDagUnknownNnz: Split DAG into 3 statement blocks.")); + Assert.assertTrue(log_original_string.contains("RewriteSplitDagUnknownNnz: Split DAG into 4 statement blocks.")); Assert.assertTrue(log_original_string.contains("mmchainoptsparse")); Assert.assertTrue(log_original_string.contains("Optimal Sparse MM Chain:")); // check reference log output From 33dda0bb72ad002ebc6c6040a14becdf93959e35 Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Mon, 19 Oct 2026 17:23:31 +0200 Subject: [PATCH 07/14] chore(test/scripts/functions/rewrite/RewriteSplitDagUnknownNnz.dml): include loops in DAG --- .../test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java | 2 +- .../scripts/functions/rewrite/RewriteSplitDagUnknownNnz.dml | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java index fa9d4309147..b83cbc32e3e 100644 --- a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java +++ b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java @@ -163,7 +163,7 @@ public void testSplitDagUnknownNnzReadAndMMChain() { // check original log output String log_original_string = String.join(DELIMITER, log_original.stream().map(l -> l.getMessage().toString()).toArray(String[]::new)); - Assert.assertTrue(log_original_string.contains("RewriteSplitDagUnknownNnz: Split DAG into 4 statement blocks.")); + Assert.assertTrue(log_original_string.contains("RewriteSplitDagUnknownNnz: Split DAG into 5 statement blocks.")); Assert.assertTrue(log_original_string.contains("mmchainoptsparse")); Assert.assertTrue(log_original_string.contains("Optimal Sparse MM Chain:")); // check reference log output diff --git a/src/test/scripts/functions/rewrite/RewriteSplitDagUnknownNnz.dml b/src/test/scripts/functions/rewrite/RewriteSplitDagUnknownNnz.dml index 998334b611c..aa305406602 100644 --- a/src/test/scripts/functions/rewrite/RewriteSplitDagUnknownNnz.dml +++ b/src/test/scripts/functions/rewrite/RewriteSplitDagUnknownNnz.dml @@ -22,7 +22,6 @@ X = read($1); Y = read($2); - -R = t(X) %*% X %*% Y; +R = t(X) %*% X %*% t(t(X) %*% X) %*% Y; write(R, $3); From 8be9bd5c74fb9d722dd2f9bd8bb219b1024efe88 Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Mon, 19 Oct 2026 17:25:08 +0200 Subject: [PATCH 08/14] fix(main/hops/rewrite/RewriteSplitDagUnknownNnz.java): handle data dependencies (part 1) --- .../sysds/hops/rewrite/ProgramRewriter.java | 3 ++- .../hops/rewrite/RewriteSplitDagUnknownNnz.java | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) 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 c1751845544..83eb3ddb161 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/ProgramRewriter.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/ProgramRewriter.java @@ -106,7 +106,8 @@ public ProgramRewriter(boolean staticRewrites, boolean dynamicRewrites) _sbRuleSet.add( new RewriteCompressedReblock() ); // Compression Rewrite if( OptimizerUtils.ALLOW_SPLIT_HOP_DAGS ) _sbRuleSet.add( new RewriteSplitDagUnknownCSVRead() ); //dependency: reblock, merge blocks - if(ConfigurationManager.getDMLConfig().getBooleanValue(DMLConfig.SPARSITY_RECOMPILE)) + if( OptimizerUtils.ALLOW_SPLIT_HOP_DAGS && + ConfigurationManager.getDMLConfig().getBooleanValue(DMLConfig.SPARSITY_RECOMPILE) ) _sbRuleSet.add(new RewriteSplitDagUnknownNnz()); if( OptimizerUtils.ALLOW_SPLIT_HOP_DAGS && ConfigurationManager.getCompilerConfigFlag(ConfigType.ALLOW_INDIVIDUAL_SB_SPECIFIC_OPS) ) diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java index 86ebb01296f..2ccdb36c989 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java @@ -72,7 +72,7 @@ public List rewriteStatementBlock(StatementBlock sb, ProgramRewr tRead.setRequiresRecompile(); // replace persistent read with transient read - ArrayList parents = new ArrayList<>(cHop.getParent()); + List parents = cHop.getParent(); for(int i = 0; i < parents.size(); i++) { Hop parent = parents.get(i); HopRewriteUtils.replaceChildReference(parent, cHop, tRead); @@ -129,6 +129,14 @@ private ArrayList getNextLevel(ListIterator> liter) { } } + public static boolean anyParentIsMatrixMultiply(Hop hop) { + return hop.getParent().stream().anyMatch(parentHop -> HopRewriteUtils.isMatrixMultiply(parentHop)); + } + + public static boolean allParentsAreMatrixMultiply(Hop hop) { + return hop.getParent().stream().allMatch(parentHop -> HopRewriteUtils.isMatrixMultiply(parentHop)); + } + private void rCollectNnzComputationCandidates(Hop hop, ListIterator> candListIter, Hop parentHop) { if(hop.isVisited()) return; @@ -137,13 +145,12 @@ private void rCollectNnzComputationCandidates(Hop hop, ListIterator Date: Tue, 20 Oct 2026 11:08:05 +0200 Subject: [PATCH 09/14] feat(test/functions/rewrite/RewriteSplitDagUnknownNnz.java): create two separate test cases for read and mm chain --- .../RewriteSplitDagUnknownNnzTest.java | 27 ++++++++++++++----- ...l => RewriteSplitDagUnknownNnzMMChain.dml} | 0 .../rewrite/RewriteSplitDagUnknownNnzRead.dml | 27 +++++++++++++++++++ 3 files changed, 48 insertions(+), 6 deletions(-) rename src/test/scripts/functions/rewrite/{RewriteSplitDagUnknownNnz.dml => RewriteSplitDagUnknownNnzMMChain.dml} (100%) create mode 100644 src/test/scripts/functions/rewrite/RewriteSplitDagUnknownNnzRead.dml diff --git a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java index b83cbc32e3e..95ae50f684f 100644 --- a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java +++ b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java @@ -53,7 +53,8 @@ @net.jcip.annotations.NotThreadSafe public class RewriteSplitDagUnknownNnzTest extends AutomatedTestBase { - private static final String TEST_NAME = "RewriteSplitDagUnknownNnz"; + private static final String TEST_NAME_READ = "RewriteSplitDagUnknownNnzRead"; + private static final String TEST_NAME_MMCHAIN = "RewriteSplitDagUnknownNnzMMChain"; private static final String TEST_DIR = "functions/rewrite/"; private static final String TEST_CLASS_DIR = TEST_DIR + RewriteSplitDagUnknownNnzTest.class.getSimpleName() + "/"; @@ -88,7 +89,8 @@ public static Collection data() { @Override public void setUp() { TestUtils.clearAssertionInformation(); - addTestConfiguration(TEST_NAME, new TestConfiguration(TEST_CLASS_DIR, TEST_NAME, new String[] {"R"})); + addTestConfiguration(TEST_NAME_READ, new TestConfiguration(TEST_CLASS_DIR, TEST_NAME_READ, new String[] {"R"})); + addTestConfiguration(TEST_NAME_MMCHAIN, new TestConfiguration(TEST_CLASS_DIR, TEST_NAME_MMCHAIN, new String[] {"R"})); _oldLevelRewrite = Logger.getLogger(PACKAGE_REWRITE).getLevel(); Logger.getLogger(PACKAGE_REWRITE).setLevel(Level.TRACE); _oldLevelRecompile = Logger.getLogger(PACKAGE_RECOMPILE).getLevel(); @@ -103,22 +105,33 @@ public void tearDown() { } @Test - public void testSplitDagUnknownNnzReadAndMMChain() { + public void testSplitDagUnknownNnzRead() { + testSplitDagUnknownNnz(TEST_NAME_READ, 4); + } + + @Test + public void testSplitDagUnknownNnzMMChain() { + testSplitDagUnknownNnz(TEST_NAME_MMCHAIN, 5); + } + + private void testSplitDagUnknownNnz(String testName, int numSBs) { boolean oldFlag1 = OptimizerUtils.ALLOW_TRANSPOSE_MMCHAIN_REWRITES; boolean oldFlag2 = OptimizerUtils.ALLOW_SUM_PRODUCT_REWRITES; + boolean oldFlag3 = OptimizerUtils.ALLOW_SPLIT_HOP_DAGS; DMLConfig oldDMLConfig = ConfigurationManager.getDMLConfig(); try { - TestConfiguration config = getTestConfiguration(TEST_NAME); + TestConfiguration config = getTestConfiguration(testName); loadTestConfiguration(config); String HOME = SCRIPT_DIR + TEST_DIR; - fullDMLScriptName = HOME + TEST_NAME + ".dml"; + fullDMLScriptName = HOME + testName + ".dml"; programArgs = new String[] {"-explain", "hops", "-stats", "-args", input("X"), input("Y"), output("R")}; OptimizerUtils.ALLOW_TRANSPOSE_MMCHAIN_REWRITES = true; OptimizerUtils.ALLOW_SUM_PRODUCT_REWRITES = true; + OptimizerUtils.ALLOW_SPLIT_HOP_DAGS = true; double[][] X = getRandomMatrix(rows, cols, -1, 1, sparsities[0], 7); double[][] Y = getRandomMatrix(cols, 1, -1, 1, sparsities[1], 3); @@ -163,7 +176,8 @@ public void testSplitDagUnknownNnzReadAndMMChain() { // check original log output String log_original_string = String.join(DELIMITER, log_original.stream().map(l -> l.getMessage().toString()).toArray(String[]::new)); - Assert.assertTrue(log_original_string.contains("RewriteSplitDagUnknownNnz: Split DAG into 5 statement blocks.")); + Assert.assertTrue(log_original_string.contains("RewriteSplitDagUnknownNnz: Split DAG into " + + numSBs + " statement blocks.")); Assert.assertTrue(log_original_string.contains("mmchainoptsparse")); Assert.assertTrue(log_original_string.contains("Optimal Sparse MM Chain:")); // check reference log output @@ -175,6 +189,7 @@ public void testSplitDagUnknownNnzReadAndMMChain() { finally { OptimizerUtils.ALLOW_TRANSPOSE_MMCHAIN_REWRITES = oldFlag1; OptimizerUtils.ALLOW_SUM_PRODUCT_REWRITES = oldFlag2; + OptimizerUtils.ALLOW_SPLIT_HOP_DAGS = oldFlag3; try { overwriteCurrentConfig(oldDMLConfig); } diff --git a/src/test/scripts/functions/rewrite/RewriteSplitDagUnknownNnz.dml b/src/test/scripts/functions/rewrite/RewriteSplitDagUnknownNnzMMChain.dml similarity index 100% rename from src/test/scripts/functions/rewrite/RewriteSplitDagUnknownNnz.dml rename to src/test/scripts/functions/rewrite/RewriteSplitDagUnknownNnzMMChain.dml diff --git a/src/test/scripts/functions/rewrite/RewriteSplitDagUnknownNnzRead.dml b/src/test/scripts/functions/rewrite/RewriteSplitDagUnknownNnzRead.dml new file mode 100644 index 00000000000..498da9ec519 --- /dev/null +++ b/src/test/scripts/functions/rewrite/RewriteSplitDagUnknownNnzRead.dml @@ -0,0 +1,27 @@ +#------------------------------------------------------------- +# +# 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 = read($1); +Y = read($2); +R = t(X) %*% X %*% Y; + +write(R, $3); From f99f53a8e0f65a49a1e47d043ea26669ab933aaf Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Tue, 20 Oct 2026 15:51:25 +0200 Subject: [PATCH 10/14] feat(main/hops/rewrite/RewriteSplitDagUnknownNnz.java): change rewrite function to recursive instead of relying on layer structure through a list --- .../rewrite/RewriteSplitDagUnknownNnz.java | 160 +++++++++--------- 1 file changed, 77 insertions(+), 83 deletions(-) diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java index 2ccdb36c989..b1a18e39802 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java @@ -20,8 +20,6 @@ package org.apache.sysds.hops.rewrite; import java.util.ArrayList; -import java.util.Collections; -import java.util.ListIterator; import java.util.List; import org.apache.sysds.common.Types.OpOpData; @@ -46,59 +44,64 @@ public boolean createsSplitDag() { @Override public List rewriteStatementBlock(StatementBlock sb, ProgramRewriteStatus state) { + return rRewriteStatementBlock(sb, state); + } + + public List rRewriteStatementBlock(StatementBlock sb, ProgramRewriteStatus state) { ArrayList ret = new ArrayList<>(); // collect all candidate hops in levels based on their data dependencies - ArrayList> candidateLists = new ArrayList>(); - collectNnzComputationCandidates(sb.getHops(), candidateLists); - Collections.reverse(candidateLists); // reverse order of dependency levels + ArrayList cand = new ArrayList(); + collectNnzComputationCandidates(sb.getHops(), cand); // split hop dag on demand and if sparsity rewrites are enabled (splitting is pointless w/o rewrite) - if(!candidateLists.isEmpty() && ConfigurationManager.getDMLConfig().getBooleanValue(DMLConfig.SPARSITY_REWRITES)) { - for(ArrayList cand : candidateLists) { - // duplicate sb incl live variable sets - StatementBlock sbSplit = new StatementBlock(); - sbSplit.setDMLProg(sb.getDMLProg()); - sbSplit.setParseInfo(sb); - sbSplit.setLiveIn(new VariableSet()); - sbSplit.setLiveOut(new VariableSet()); - - // move candidate hops to new statement block - // (and replace original persistent read with transient read) - ArrayList sbSplitHops = new ArrayList<>(); - for(Hop cHop : cand) { - // create new transient read - DataOp tRead = HopRewriteUtils.createTransientRead(cHop.getName(), cHop); - tRead.setRequiresRecompile(); - - // replace persistent read with transient read - List parents = cHop.getParent(); - for(int i = 0; i < parents.size(); i++) { - Hop parent = parents.get(i); - HopRewriteUtils.replaceChildReference(parent, cHop, tRead); - } - - // add reblock sub dag to first statement block - DataOp tWrite = HopRewriteUtils.createTransientWrite(cHop.getName(), cHop); - sbSplitHops.add(tWrite); - - // update live in and out of new statement block (for piggybacking) - DataIdentifier diVar = sb.variablesRead().getVariable(cHop.getName()); - if(diVar != null) { // var read should always exist because persistent read - sbSplit.liveOut().addVariable(cHop.getName(), new DataIdentifier(diVar)); - sb.liveIn().addVariable(cHop.getName(), new DataIdentifier(diVar)); - } + if(!cand.isEmpty() && ConfigurationManager.getDMLConfig().getBooleanValue(DMLConfig.SPARSITY_REWRITES)) { + // duplicate sb incl live variable sets + StatementBlock sbSplit = new StatementBlock(); + sbSplit.setDMLProg(sb.getDMLProg()); + sbSplit.setParseInfo(sb); + sbSplit.setLiveIn(new VariableSet()); + sbSplit.setLiveOut(new VariableSet()); + + // move candidate hops to new statement block + // (and replace original persistent read with transient read) + ArrayList sbSplitHops = new ArrayList<>(); + for(Hop cHop : cand) { + // create new transient read + DataOp tRead = HopRewriteUtils.createTransientRead(cHop.getName(), cHop); + tRead.setRequiresRecompile(); + + // replace persistent read with transient read + List parents = cHop.getParent(); + for(int i = 0; i < parents.size(); i++) { + Hop parent = parents.get(i); + HopRewriteUtils.replaceChildReference(parent, cHop, tRead); } - sbSplit.setHops(sbSplitHops); - sbSplit.updateRecompilationFlag(); - ret.add(sbSplit); // statement block with permanent read and transient write - sbSplit.setSplitDag(true); // avoid later merge by other rewrites + // add reblock sub dag to first statement block + DataOp tWrite = HopRewriteUtils.createTransientWrite(cHop.getName(), cHop); + sbSplitHops.add(tWrite); + + // update live in and out of new statement block (for piggybacking) + DataIdentifier diVar = sb.variablesRead().getVariable(cHop.getName()); + if(diVar != null) { // var read should always exist because persistent read + sbSplit.liveOut().addVariable(cHop.getName(), new DataIdentifier(diVar)); + sb.liveIn().addVariable(cHop.getName(), new DataIdentifier(diVar)); + } } + + sbSplit.setHops(sbSplitHops); + sbSplit.updateRecompilationFlag(); + sbSplit.setSplitDag(true); // avoid later merge by other rewrites + + //recursive application of rewrite rule + List tmp = rRewriteStatementBlock(sbSplit, state); + + ret.addAll(tmp); // statement block with permanent read and transient write sb.setSplitDag(true); // avoid later merge by other rewrites LOG.debug("RewriteSplitDagUnknownNnz: Split DAG into " + (ret.size() + 1) + " statement blocks."); } - + ret.add(sb); return ret; } @@ -108,24 +111,12 @@ public List rewriteStatementBlocks(List sbs, Pro return sbs; } - private void collectNnzComputationCandidates(ArrayList roots, ArrayList> candidateLists) { + private void collectNnzComputationCandidates(ArrayList roots, ArrayList cand) { if(roots == null) return; Hop.resetVisitStatus(roots); - ListIterator> candListIter = candidateLists.listIterator(); for(Hop root : roots) { - rCollectNnzComputationCandidates(root, candListIter, null); - } - } - - private ArrayList getNextLevel(ListIterator> liter) { - if(liter.hasNext()) { - return liter.next(); - } - else { - ArrayList newLevel = new ArrayList(); - liter.add(newLevel); - return newLevel; + rCollectNnzComputationCandidates(root, cand, null); } } @@ -137,42 +128,45 @@ public static boolean allParentsAreMatrixMultiply(Hop hop) { return hop.getParent().stream().allMatch(parentHop -> HopRewriteUtils.isMatrixMultiply(parentHop)); } - private void rCollectNnzComputationCandidates(Hop hop, ListIterator> candListIter, Hop parentHop) { + private void rCollectNnzComputationCandidates(Hop hop, ArrayList cand, Hop parentHop) { if(hop.isVisited()) return; - - if(hop instanceof DataOp && ((DataOp) hop).getOp() == OpOpData.PERSISTENTREAD && !hop.nnzKnown()) { - // split persistent reads with unknown nnz to obtain their nnz before continuing rewrites - splitAndProcessChildren(hop, candListIter); - } - else if(parentHop != null && anyParentIsMatrixMultiply(hop) && !HopRewriteUtils.isMatrixMultiply(hop)) { - // split before mm chain to ensure correct sparsity information for mm chain sparsity estimation - splitAndProcessChildren(hop, candListIter); - } - else if((!HopRewriteUtils.isMatrixMultiply(parentHop) || !allParentsAreMatrixMultiply(hop)) - && HopRewriteUtils.isMatrixMultiply(hop) && !((AggBinaryOp) hop).hasLeftPMInput()) { - // split after mm chain to force the mm chain into a separate statment block for isolated optimization - splitAndProcessChildren(hop, candListIter); + // has already been split off + boolean noSplitRequired = HopRewriteUtils.hasOnlyWriteParents(hop, true, false); + + if(!noSplitRequired) { + if(hop instanceof DataOp && ((DataOp) hop).getOp() == OpOpData.PERSISTENTREAD && !hop.nnzKnown()) { + // split persistent reads with unknown nnz to obtain their nnz before continuing rewrites + // splitAndProcessChildren(hop, candListIter); + cand.add(hop); + } + else if(parentHop != null && anyParentIsMatrixMultiply(hop) && !HopRewriteUtils.isMatrixMultiply(hop)) { + // split before mm chain to ensure correct sparsity information for mm chain sparsity estimation + // splitAndProcessChildren(hop, candListIter); + cand.add(hop); + } + else if((!HopRewriteUtils.isMatrixMultiply(parentHop) || !allParentsAreMatrixMultiply(hop)) + && HopRewriteUtils.isMatrixMultiply(hop) && !((AggBinaryOp) hop).hasLeftPMInput()) { + // split after mm chain to force the mm chain into a separate statment block for isolated optimization + // splitAndProcessChildren(hop, candListIter); + cand.add(hop); + } + else { + rCollectCandidatesChildren(hop, cand); + } } - else{ - rCollectCandidatesChildren(hop, candListIter); + else { + rCollectCandidatesChildren(hop, cand); } hop.setVisited(); } - private void rCollectCandidatesChildren(Hop parentHop, ListIterator> candListIter) { + private void rCollectCandidatesChildren(Hop parentHop, ArrayList cand) { // process children if(parentHop.getInput() != null) { for(Hop c : parentHop.getInput()) - rCollectNnzComputationCandidates(c, candListIter, parentHop); + rCollectNnzComputationCandidates(c, cand, parentHop); } } - - // add the hop to the candidates and proceed with its inputs - private void splitAndProcessChildren(Hop hop, ListIterator> candListIter) { - getNextLevel(candListIter).add(hop); - rCollectCandidatesChildren(hop, candListIter); - candListIter.previous(); // reset level increment after processing children - } } From ee260698ea999e22e5fa9986106947aecb44a2ca Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Fri, 23 Oct 2026 11:45:53 +0200 Subject: [PATCH 11/14] fix(main/hops/rewrite/RewriteSplitDagUnknownNnz.java): fix problem of creating duplicated statement blocks --- .../rewrite/RewriteSplitDagUnknownNnz.java | 89 +++++++++++++++---- 1 file changed, 73 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java index b1a18e39802..cfaa6a84d02 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java @@ -20,6 +20,8 @@ package org.apache.sysds.hops.rewrite; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; import org.apache.sysds.common.Types.OpOpData; @@ -28,6 +30,7 @@ import org.apache.sysds.hops.AggBinaryOp; import org.apache.sysds.hops.DataOp; import org.apache.sysds.hops.Hop; +import org.apache.sysds.hops.recompile.Recompiler; import org.apache.sysds.parser.DataIdentifier; import org.apache.sysds.parser.StatementBlock; import org.apache.sysds.parser.VariableSet; @@ -48,14 +51,26 @@ public List rewriteStatementBlock(StatementBlock sb, ProgramRewr } public List rRewriteStatementBlock(StatementBlock sb, ProgramRewriteStatus state) { + // split only if sparsity rewrites are enabled (splitting is pointless w/o rewrite) + if(!ConfigurationManager.getDMLConfig().getBooleanValue(DMLConfig.SPARSITY_REWRITES)) + return Arrays.asList(sb); + ArrayList ret = new ArrayList<>(); // collect all candidate hops in levels based on their data dependencies ArrayList cand = new ArrayList(); collectNnzComputationCandidates(sb.getHops(), cand); - // split hop dag on demand and if sparsity rewrites are enabled (splitting is pointless w/o rewrite) - if(!cand.isEmpty() && ConfigurationManager.getDMLConfig().getBooleanValue(DMLConfig.SPARSITY_REWRITES)) { + // split hop dag on demand + if(!cand.isEmpty()) { + //collect child operators of candidates (to prevent rewrite anomalies) + //For example, B = rmEmpty(A), C = rmEmpty(B), op(C, nrow(B)) needs to + //preserve the link rmEmpty(B) in the split-off DAG. Therefore, the + //candidates themselves need to be part of this anomaly filter as well. + HashSet candChilds = new HashSet<>(); + collectCandidateChildOperators(cand, candChilds); + candChilds.addAll(cand); + // duplicate sb incl live variable sets StatementBlock sbSplit = new StatementBlock(); sbSplit.setDMLProg(sb.getDMLProg()); @@ -68,29 +83,35 @@ public List rRewriteStatementBlock(StatementBlock sb, ProgramRew ArrayList sbSplitHops = new ArrayList<>(); for(Hop cHop : cand) { // create new transient read - DataOp tRead = HopRewriteUtils.createTransientRead(cHop.getName(), cHop); + String varname = createCutVarName(false); + DataOp tRead = HopRewriteUtils.createTransientRead(varname, cHop); tRead.setRequiresRecompile(); // replace persistent read with transient read - List parents = cHop.getParent(); + ArrayList parents = new ArrayList<>(cHop.getParent()); for(int i = 0; i < parents.size(); i++) { + // prevent concurrent modification by index access Hop parent = parents.get(i); - HopRewriteUtils.replaceChildReference(parent, cHop, tRead); + if(!candChilds.contains(parent)) // anomaly filter + HopRewriteUtils.replaceChildReference(parent, cHop, tRead); } // add reblock sub dag to first statement block - DataOp tWrite = HopRewriteUtils.createTransientWrite(cHop.getName(), cHop); + DataOp tWrite = HopRewriteUtils.createTransientWrite(varname, cHop); sbSplitHops.add(tWrite); // update live in and out of new statement block (for piggybacking) - DataIdentifier diVar = sb.variablesRead().getVariable(cHop.getName()); - if(diVar != null) { // var read should always exist because persistent read - sbSplit.liveOut().addVariable(cHop.getName(), new DataIdentifier(diVar)); - sb.liveIn().addVariable(cHop.getName(), new DataIdentifier(diVar)); - } + DataIdentifier diVar = new DataIdentifier(varname); + diVar.setDimensions(cHop.getDim1(), cHop.getDim2()); + diVar.setBlocksize(cHop.getBlocksize()); + diVar.setDataType(cHop.getDataType()); + diVar.setValueType(cHop.getValueType()); + sbSplit.liveOut().addVariable(varname, new DataIdentifier(diVar)); + sb.liveIn().addVariable(varname, new DataIdentifier(diVar)); + sb.variablesRead().addVariable(varname, new DataIdentifier(diVar)); } - sbSplit.setHops(sbSplitHops); + sbSplit.setHops(Recompiler.deepCopyHopsDag(sbSplitHops)); sbSplit.updateRecompilationFlag(); sbSplit.setSplitDag(true); // avoid later merge by other rewrites @@ -106,6 +127,44 @@ public List rRewriteStatementBlock(StatementBlock sb, ProgramRew return ret; } + private void collectCandidateChildOperators(ArrayList cand, HashSet candChilds) { + Hop.resetVisitStatus(cand); + if(cand != null) { + for(Hop root : cand) { + rCollectCandidateChildOperators(root, cand, candChilds, false); + } + } + + // Immediately reset the visit status because candidates might be inner nodes in the DAG. + // Subsequent resets on the root nodes of the DAG would otherwise not necessarily reach + // these nodes which could lead to missing checks on subsequent passes (e.g., when checking + // for replicated operators). + Hop.resetVisitStatus(cand); + } + + private void rCollectCandidateChildOperators(Hop hop, ArrayList cand, HashSet candChilds, boolean collect) { + if(hop.isVisited()) + return; + + // collect operator if necessary + if(collect) + candChilds.add(hop); + + // activate collection if we passed a hop from the candidates + boolean passedFlag = collect; + if(cand.contains(hop)) + passedFlag = true; + + // process childs recursively + if(hop.getInput() != null) { + for(Hop childHop : hop.getInput()) { + rCollectCandidateChildOperators(childHop, cand, candChilds, passedFlag); + } + } + + hop.setVisited(); + } + @Override public List rewriteStatementBlocks(List sbs, ProgramRewriteStatus state) { return sbs; @@ -137,18 +196,16 @@ private void rCollectNnzComputationCandidates(Hop hop, ArrayList cand, Hop if(!noSplitRequired) { if(hop instanceof DataOp && ((DataOp) hop).getOp() == OpOpData.PERSISTENTREAD && !hop.nnzKnown()) { // split persistent reads with unknown nnz to obtain their nnz before continuing rewrites - // splitAndProcessChildren(hop, candListIter); cand.add(hop); } - else if(parentHop != null && anyParentIsMatrixMultiply(hop) && !HopRewriteUtils.isMatrixMultiply(hop)) { + else if(parentHop != null && anyParentIsMatrixMultiply(hop) && !HopRewriteUtils.isMatrixMultiply(hop) + && !(hop instanceof DataOp && ((DataOp) hop).getOp() == OpOpData.TRANSIENTREAD)) { // split before mm chain to ensure correct sparsity information for mm chain sparsity estimation - // splitAndProcessChildren(hop, candListIter); cand.add(hop); } else if((!HopRewriteUtils.isMatrixMultiply(parentHop) || !allParentsAreMatrixMultiply(hop)) && HopRewriteUtils.isMatrixMultiply(hop) && !((AggBinaryOp) hop).hasLeftPMInput()) { // split after mm chain to force the mm chain into a separate statment block for isolated optimization - // splitAndProcessChildren(hop, candListIter); cand.add(hop); } else { From 6c2d1ca6b8f0c8632a742e342578a7475136ecb6 Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Fri, 23 Oct 2026 14:52:31 +0200 Subject: [PATCH 12/14] fix(main/hops/rewrite/RewriteSplitDagUnknownNnz.java): split only mm chains with at least two matrix multiplications --- .../rewrite/RewriteSplitDagUnknownNnz.java | 125 +++++++++++++++++- .../RewriteSplitDagUnknownNnzTest.java | 2 +- 2 files changed, 123 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java index cfaa6a84d02..58149e77612 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -30,10 +31,12 @@ import org.apache.sysds.hops.AggBinaryOp; import org.apache.sysds.hops.DataOp; import org.apache.sysds.hops.Hop; +import org.apache.sysds.hops.LiteralOp; import org.apache.sysds.hops.recompile.Recompiler; import org.apache.sysds.parser.DataIdentifier; import org.apache.sysds.parser.StatementBlock; import org.apache.sysds.parser.VariableSet; +import org.apache.sysds.runtime.matrix.data.Pair; /** * Rule: Split Hop DAG after reads with unknown nnz. This is important to create recompile @@ -111,6 +114,9 @@ public List rRewriteStatementBlock(StatementBlock sb, ProgramRew sb.variablesRead().addVariable(varname, new DataIdentifier(diVar)); } + // ensure disjoint operators across DAGs (prevent replicated operations) + handleReplicatedOperators(sbSplitHops, sb.getHops(), sbSplit.liveOut(), sb.liveIn()); + sbSplit.setHops(Recompiler.deepCopyHopsDag(sbSplitHops)); sbSplit.updateRecompilationFlag(); sbSplit.setSplitDag(true); // avoid later merge by other rewrites @@ -127,6 +133,96 @@ public List rRewriteStatementBlock(StatementBlock sb, ProgramRew return ret; } + private void handleReplicatedOperators(ArrayList rootsSbSplit, ArrayList rootsSbOrigin, + VariableSet sbSplitOut, VariableSet sbOriginIn) { + // step 1: create probe set SB1 + HashSet probeSet = new HashSet<>(); + Hop.resetVisitStatus(rootsSbSplit); + for(Hop h : rootsSbSplit) { + rAddHopsToProbeSet(h, probeSet); + } + + // step 2: probe SB2 operators top-down (collect cut candidates) + HashSet> candSet = new HashSet<>(); + Hop.resetVisitStatus(rootsSbOrigin); + for(Hop h : rootsSbOrigin) { + rProbeAndAddHopsToCandidateSet(h, probeSet, candSet); + } + + // step 3: create additional cuts with reuse for common references + HashMap reuseTRead = new HashMap<>(); + for(Pair p : candSet) { + Hop hop = p.getKey(); + Hop c = p.getValue(); + + DataOp tRead = reuseTRead.get(c.getHopID()); + if(tRead == null) { + String varname = createCutVarName(false); + + tRead = HopRewriteUtils.createTransientRead(varname, c); + reuseTRead.put(c.getHopID(), tRead); + + DataOp twrite = HopRewriteUtils.createTransientWrite(varname, c); + + // update live in and out of new statement block (for piggybacking) + DataIdentifier diVar = new DataIdentifier(varname); + diVar.setDimensions(c.getDim1(), c.getDim2()); + diVar.setBlocksize(c.getBlocksize()); + diVar.setDataType(c.getDataType()); + diVar.setValueType(c.getValueType()); + sbSplitOut.addVariable(varname, new DataIdentifier(diVar)); + sbOriginIn.addVariable(varname, new DataIdentifier(diVar)); + + rootsSbSplit.add(twrite); + } + + // create additional cut by rewriting both hop dags + int pos = HopRewriteUtils.getChildReferencePos(hop, c); + HopRewriteUtils.removeChildReferenceByPos(hop, c, pos); + HopRewriteUtils.addChildReference(hop, tRead, pos); + } + } + + private void rAddHopsToProbeSet(Hop hop, HashSet probeSet) { + if(hop.isVisited()) + return; + + // prevent cuts for no-ops + if(!(hop instanceof DataOp && !((DataOp)hop).isPersistentReadWrite()) && !(hop instanceof LiteralOp)) + probeSet.add(hop); + + if(hop.getInput() != null) + for(Hop c : hop.getInput()) + rAddHopsToProbeSet(c, probeSet); + + hop.setVisited(); + } + + /** + * NOTE: candset is a set of parent-child pairs because a parent might have + * multiple references to replicated hops. + * + * @param hop high-level operator + * @param probeSet probe set? + * @param candSet candidate set? + */ + private void rProbeAndAddHopsToCandidateSet(Hop hop, HashSet probeSet, HashSet> candSet) { + if(hop.isVisited()) + return; + + if(hop.getInput() != null) + for(Hop c : hop.getInput()) { + // probe for replicated operator, if any child is replicated, keep parent + // for cut between parent-child; otherwise recursively descend. + if(!probeSet.contains(c)) + rProbeAndAddHopsToCandidateSet(c, probeSet, candSet); + else + candSet.add(new Pair<>(hop,c)); + } + + hop.setVisited(); + } + private void collectCandidateChildOperators(ArrayList cand, HashSet candChilds) { Hop.resetVisitStatus(cand); if(cand != null) { @@ -187,6 +283,27 @@ public static boolean allParentsAreMatrixMultiply(Hop hop) { return hop.getParent().stream().allMatch(parentHop -> HopRewriteUtils.isMatrixMultiply(parentHop)); } + public static boolean anyChildIsMatrixMultiply(Hop hop) { + return hop.getInput().stream().anyMatch(childHop -> HopRewriteUtils.isMatrixMultiply(childHop)); + } + + public static boolean allChildsAreMatrixMultiply(Hop hop) { + return hop.getInput().stream().allMatch(childHop -> HopRewriteUtils.isMatrixMultiply(childHop)); + } + + public static boolean isMMChain(Hop hop) { + // hop is part of mm chain with at least two matrix multplications + return HopRewriteUtils.isMatrixMultiply(hop) + && (anyParentIsMatrixMultiply(hop) || anyChildIsMatrixMultiply(hop)); + } + + public static boolean anyParentIsMMChain(Hop hop) { + List parentHops = hop.getParent(); + if(parentHops == null) + return false; + return parentHops.stream().anyMatch(pHop -> isMMChain(pHop)); + } + private void rCollectNnzComputationCandidates(Hop hop, ArrayList cand, Hop parentHop) { if(hop.isVisited()) return; @@ -198,13 +315,15 @@ private void rCollectNnzComputationCandidates(Hop hop, ArrayList cand, Hop // split persistent reads with unknown nnz to obtain their nnz before continuing rewrites cand.add(hop); } - else if(parentHop != null && anyParentIsMatrixMultiply(hop) && !HopRewriteUtils.isMatrixMultiply(hop) - && !(hop instanceof DataOp && ((DataOp) hop).getOp() == OpOpData.TRANSIENTREAD)) { + else if(parentHop != null && !HopRewriteUtils.isMatrixMultiply(hop) + && !(hop instanceof DataOp && ((DataOp) hop).getOp() == OpOpData.TRANSIENTREAD) + && anyParentIsMMChain(hop)) { // split only mm chains (i.e., at least two matrix multiplications) // split before mm chain to ensure correct sparsity information for mm chain sparsity estimation cand.add(hop); } else if((!HopRewriteUtils.isMatrixMultiply(parentHop) || !allParentsAreMatrixMultiply(hop)) - && HopRewriteUtils.isMatrixMultiply(hop) && !((AggBinaryOp) hop).hasLeftPMInput()) { + && isMMChain(hop) // split only mm chains (i.e., at least two matrix multiplications) + && !((AggBinaryOp) hop).hasLeftPMInput()) { // split after mm chain to force the mm chain into a separate statment block for isolated optimization cand.add(hop); } diff --git a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java index 95ae50f684f..19f7c80ca4d 100644 --- a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java +++ b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java @@ -111,7 +111,7 @@ public void testSplitDagUnknownNnzRead() { @Test public void testSplitDagUnknownNnzMMChain() { - testSplitDagUnknownNnz(TEST_NAME_MMCHAIN, 5); + testSplitDagUnknownNnz(TEST_NAME_MMCHAIN, 4); } private void testSplitDagUnknownNnz(String testName, int numSBs) { From dd165b3874c09fcf2c592e4a2e4d3ab90bb39e6e Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Fri, 23 Oct 2026 15:27:26 +0200 Subject: [PATCH 13/14] feat(main/hops/rewrite/SplitStatementBlockRewriteRule.java): create an abstract class for common functionality of rewrite rules that split statement blocks refactor(main/hops/rewrite/RewriteSplit**.java): extend the newly created abstract class for spit statement blocks functionality --- ...RewriteSplitDagDataDependentOperators.java | 143 +-------------- .../RewriteSplitDagUnknownCSVRead.java | 7 +- .../rewrite/RewriteSplitDagUnknownNnz.java | 144 +-------------- .../SplitStatementBlockRewriteRule.java | 168 ++++++++++++++++++ 4 files changed, 174 insertions(+), 288 deletions(-) create mode 100644 src/main/java/org/apache/sysds/hops/rewrite/SplitStatementBlockRewriteRule.java diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagDataDependentOperators.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagDataDependentOperators.java index 3eaed10792b..44f97a7c045 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagDataDependentOperators.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagDataDependentOperators.java @@ -21,7 +21,6 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -48,7 +47,6 @@ import org.apache.sysds.parser.DataIdentifier; import org.apache.sysds.parser.StatementBlock; import org.apache.sysds.parser.VariableSet; -import org.apache.sysds.runtime.matrix.data.Pair; /** * Rule: Split Hop DAG after specific data-dependent operators. This is @@ -67,13 +65,8 @@ * split statements blocks again (for consistency). However, currently it is * only used in places (e.g., parfor optimizer) that are not directly affected. */ -public class RewriteSplitDagDataDependentOperators extends StatementBlockRewriteRule +public class RewriteSplitDagDataDependentOperators extends SplitStatementBlockRewriteRule { - @Override - public boolean createsSplitDag() { - return true; - } - @Override public List rewriteStatementBlock(StatementBlock sb, ProgramRewriteStatus state) { @@ -342,140 +335,6 @@ private static Hop getFirstTransientWriteParent( Hop hop ) { return null; } - private void handleReplicatedOperators( ArrayList rootsSB1, ArrayList rootsSB2, VariableSet sb1out, VariableSet sb2in ) - { - //step 1: create probe set SB1 - HashSet probeSet = new HashSet<>(); - Hop.resetVisitStatus(rootsSB1); - for( Hop h : rootsSB1 ) - rAddHopsToProbeSet( h, probeSet ); - - //step 2: probe SB2 operators top-down (collect cut candidates) - HashSet> candSet = new HashSet<>(); - Hop.resetVisitStatus(rootsSB2); - for( Hop h : rootsSB2 ) - rProbeAndAddHopsToCandidateSet(h, probeSet, candSet); - - //step 3: create additional cuts with reuse for common references - HashMap reuseTRead = new HashMap<>(); - for( Pair p : candSet ) { - Hop hop = p.getKey(); - Hop c = p.getValue(); - - DataOp tread = reuseTRead.get(c.getHopID()); - if( tread == null ) { - String varname = createCutVarName(false); - - tread = HopRewriteUtils.createTransientRead(varname, c); - reuseTRead.put(c.getHopID(), tread); - - DataOp twrite = HopRewriteUtils.createTransientWrite(varname, c); - - //update live in and out of new statement block (for piggybacking) - DataIdentifier diVar = new DataIdentifier(varname); - diVar.setDimensions(c.getDim1(), c.getDim2()); - diVar.setBlocksize(c.getBlocksize()); - diVar.setDataType(c.getDataType()); - diVar.setValueType(c.getValueType()); - sb1out.addVariable(varname, new DataIdentifier(diVar)); - sb2in.addVariable(varname, new DataIdentifier(diVar)); - - rootsSB1.add(twrite); - } - - //create additional cut by rewriting both hop dags - int pos = HopRewriteUtils.getChildReferencePos(hop, c); - HopRewriteUtils.removeChildReferenceByPos(hop, c, pos); - HopRewriteUtils.addChildReference(hop, tread, pos); - } - } - - private void rAddHopsToProbeSet( Hop hop, HashSet probeSet ) - { - if( hop.isVisited() ) - return; - - //prevent cuts for no-ops - if( !( (hop instanceof DataOp && !((DataOp)hop).isPersistentReadWrite() ) - || hop instanceof LiteralOp) ) - { - probeSet.add(hop); - } - - if( hop.getInput() != null ) - for( Hop c : hop.getInput() ) - rAddHopsToProbeSet(c, probeSet); - - hop.setVisited(); - } - - /** - * NOTE: candset is a set of parent-child pairs because a parent might have - * multiple references to replicated hops. - * - * @param hop high-level operator - * @param probeSet probe set? - * @param candSet candidate set? - */ - private void rProbeAndAddHopsToCandidateSet( Hop hop, HashSet probeSet, HashSet> candSet ) - { - if( hop.isVisited() ) - return; - - if( hop.getInput() != null ) - for( Hop c : hop.getInput() ) { - //probe for replicated operator, if any child is replicated, keep parent - //for cut between parent-child; otherwise recursively descend. - if( !probeSet.contains(c) ) - rProbeAndAddHopsToCandidateSet(c, probeSet, candSet); - else - { - candSet.add(new Pair<>(hop,c)); - } - } - - hop.setVisited(); - } - - private void collectCandidateChildOperators( ArrayList cand, HashSet candChilds ) - { - Hop.resetVisitStatus(cand); - if( cand != null ) - for( Hop root : cand ) - rCollectCandidateChildOperators(root, cand, candChilds, false); - - // Immediately reset the visit status because candidates might be inner nodes in the DAG. - // Subsequent resets on the root nodes of the DAG would otherwise not necessarily reach - // these nodes which could lead to missing checks on subsequent passes (e.g., when checking - // for replicated operators). - Hop.resetVisitStatus(cand); - } - - private void rCollectCandidateChildOperators( Hop hop, ArrayList cand, HashSet candChilds, boolean collect ) - { - if( hop.isVisited() ) - return; - - //collect operator if necessary - if( collect ) { - candChilds.add(hop); - } - - //activate collection if we passed a candidate - boolean passedFlag = collect; - if( cand.contains(hop) ) { - passedFlag = true; - } - - //process childs recursively - if( hop.getInput()!=null ) { - for( Hop c : hop.getInput() ) - rCollectCandidateChildOperators(c, cand, candChilds, passedFlag); - } - - hop.setVisited(); - } - @Override public List rewriteStatementBlocks(List sbs, ProgramRewriteStatus sate) { return sbs; diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownCSVRead.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownCSVRead.java index a6cf158ad84..33b9861122d 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownCSVRead.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownCSVRead.java @@ -42,13 +42,8 @@ * mtd has unknown size (which can only happen for CSV). * */ -public class RewriteSplitDagUnknownCSVRead extends StatementBlockRewriteRule +public class RewriteSplitDagUnknownCSVRead extends SplitStatementBlockRewriteRule { - @Override - public boolean createsSplitDag() { - return true; - } - @Override public List rewriteStatementBlock(StatementBlock sb, ProgramRewriteStatus state) { diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java index 58149e77612..8f1b6d1bdc5 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java @@ -21,7 +21,6 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -31,23 +30,16 @@ import org.apache.sysds.hops.AggBinaryOp; import org.apache.sysds.hops.DataOp; import org.apache.sysds.hops.Hop; -import org.apache.sysds.hops.LiteralOp; import org.apache.sysds.hops.recompile.Recompiler; import org.apache.sysds.parser.DataIdentifier; import org.apache.sysds.parser.StatementBlock; import org.apache.sysds.parser.VariableSet; -import org.apache.sysds.runtime.matrix.data.Pair; /** * Rule: Split Hop DAG after reads with unknown nnz. This is important to create recompile * hooks if mtd has unspecified nnz. */ -public class RewriteSplitDagUnknownNnz extends StatementBlockRewriteRule { - @Override - public boolean createsSplitDag() { - return true; - } - +public class RewriteSplitDagUnknownNnz extends SplitStatementBlockRewriteRule { @Override public List rewriteStatementBlock(StatementBlock sb, ProgramRewriteStatus state) { return rRewriteStatementBlock(sb, state); @@ -133,134 +125,6 @@ public List rRewriteStatementBlock(StatementBlock sb, ProgramRew return ret; } - private void handleReplicatedOperators(ArrayList rootsSbSplit, ArrayList rootsSbOrigin, - VariableSet sbSplitOut, VariableSet sbOriginIn) { - // step 1: create probe set SB1 - HashSet probeSet = new HashSet<>(); - Hop.resetVisitStatus(rootsSbSplit); - for(Hop h : rootsSbSplit) { - rAddHopsToProbeSet(h, probeSet); - } - - // step 2: probe SB2 operators top-down (collect cut candidates) - HashSet> candSet = new HashSet<>(); - Hop.resetVisitStatus(rootsSbOrigin); - for(Hop h : rootsSbOrigin) { - rProbeAndAddHopsToCandidateSet(h, probeSet, candSet); - } - - // step 3: create additional cuts with reuse for common references - HashMap reuseTRead = new HashMap<>(); - for(Pair p : candSet) { - Hop hop = p.getKey(); - Hop c = p.getValue(); - - DataOp tRead = reuseTRead.get(c.getHopID()); - if(tRead == null) { - String varname = createCutVarName(false); - - tRead = HopRewriteUtils.createTransientRead(varname, c); - reuseTRead.put(c.getHopID(), tRead); - - DataOp twrite = HopRewriteUtils.createTransientWrite(varname, c); - - // update live in and out of new statement block (for piggybacking) - DataIdentifier diVar = new DataIdentifier(varname); - diVar.setDimensions(c.getDim1(), c.getDim2()); - diVar.setBlocksize(c.getBlocksize()); - diVar.setDataType(c.getDataType()); - diVar.setValueType(c.getValueType()); - sbSplitOut.addVariable(varname, new DataIdentifier(diVar)); - sbOriginIn.addVariable(varname, new DataIdentifier(diVar)); - - rootsSbSplit.add(twrite); - } - - // create additional cut by rewriting both hop dags - int pos = HopRewriteUtils.getChildReferencePos(hop, c); - HopRewriteUtils.removeChildReferenceByPos(hop, c, pos); - HopRewriteUtils.addChildReference(hop, tRead, pos); - } - } - - private void rAddHopsToProbeSet(Hop hop, HashSet probeSet) { - if(hop.isVisited()) - return; - - // prevent cuts for no-ops - if(!(hop instanceof DataOp && !((DataOp)hop).isPersistentReadWrite()) && !(hop instanceof LiteralOp)) - probeSet.add(hop); - - if(hop.getInput() != null) - for(Hop c : hop.getInput()) - rAddHopsToProbeSet(c, probeSet); - - hop.setVisited(); - } - - /** - * NOTE: candset is a set of parent-child pairs because a parent might have - * multiple references to replicated hops. - * - * @param hop high-level operator - * @param probeSet probe set? - * @param candSet candidate set? - */ - private void rProbeAndAddHopsToCandidateSet(Hop hop, HashSet probeSet, HashSet> candSet) { - if(hop.isVisited()) - return; - - if(hop.getInput() != null) - for(Hop c : hop.getInput()) { - // probe for replicated operator, if any child is replicated, keep parent - // for cut between parent-child; otherwise recursively descend. - if(!probeSet.contains(c)) - rProbeAndAddHopsToCandidateSet(c, probeSet, candSet); - else - candSet.add(new Pair<>(hop,c)); - } - - hop.setVisited(); - } - - private void collectCandidateChildOperators(ArrayList cand, HashSet candChilds) { - Hop.resetVisitStatus(cand); - if(cand != null) { - for(Hop root : cand) { - rCollectCandidateChildOperators(root, cand, candChilds, false); - } - } - - // Immediately reset the visit status because candidates might be inner nodes in the DAG. - // Subsequent resets on the root nodes of the DAG would otherwise not necessarily reach - // these nodes which could lead to missing checks on subsequent passes (e.g., when checking - // for replicated operators). - Hop.resetVisitStatus(cand); - } - - private void rCollectCandidateChildOperators(Hop hop, ArrayList cand, HashSet candChilds, boolean collect) { - if(hop.isVisited()) - return; - - // collect operator if necessary - if(collect) - candChilds.add(hop); - - // activate collection if we passed a hop from the candidates - boolean passedFlag = collect; - if(cand.contains(hop)) - passedFlag = true; - - // process childs recursively - if(hop.getInput() != null) { - for(Hop childHop : hop.getInput()) { - rCollectCandidateChildOperators(childHop, cand, candChilds, passedFlag); - } - } - - hop.setVisited(); - } - @Override public List rewriteStatementBlocks(List sbs, ProgramRewriteStatus state) { return sbs; @@ -328,17 +192,17 @@ && isMMChain(hop) // split only mm chains (i.e., at least two matrix multiplicat cand.add(hop); } else { - rCollectCandidatesChildren(hop, cand); + rCollectNnzComputationCandidatesChildren(hop, cand); } } else { - rCollectCandidatesChildren(hop, cand); + rCollectNnzComputationCandidatesChildren(hop, cand); } hop.setVisited(); } - private void rCollectCandidatesChildren(Hop parentHop, ArrayList cand) { + private void rCollectNnzComputationCandidatesChildren(Hop parentHop, ArrayList cand) { // process children if(parentHop.getInput() != null) { for(Hop c : parentHop.getInput()) diff --git a/src/main/java/org/apache/sysds/hops/rewrite/SplitStatementBlockRewriteRule.java b/src/main/java/org/apache/sysds/hops/rewrite/SplitStatementBlockRewriteRule.java new file mode 100644 index 00000000000..eeda6bd704a --- /dev/null +++ b/src/main/java/org/apache/sysds/hops/rewrite/SplitStatementBlockRewriteRule.java @@ -0,0 +1,168 @@ +/* + * 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.HashMap; +import java.util.HashSet; + +import org.apache.sysds.hops.DataOp; +import org.apache.sysds.hops.Hop; +import org.apache.sysds.hops.LiteralOp; +import org.apache.sysds.parser.DataIdentifier; +import org.apache.sysds.parser.VariableSet; +import org.apache.sysds.runtime.matrix.data.Pair; + +public abstract class SplitStatementBlockRewriteRule extends StatementBlockRewriteRule { + @Override + public boolean createsSplitDag() { + return true; + } + + protected void handleReplicatedOperators(ArrayList rootsSbSplit, ArrayList rootsSbOrigin, + VariableSet sbSplitOut, VariableSet sbOriginIn) { + // step 1: create probe set SB1 + HashSet probeSet = new HashSet<>(); + Hop.resetVisitStatus(rootsSbSplit); + for(Hop h : rootsSbSplit) { + rAddHopsToProbeSet(h, probeSet); + } + + // step 2: probe SB2 operators top-down (collect cut candidates) + HashSet> candSet = new HashSet<>(); + Hop.resetVisitStatus(rootsSbOrigin); + for(Hop h : rootsSbOrigin) { + rProbeAndAddHopsToCandidateSet(h, probeSet, candSet); + } + + // step 3: create additional cuts with reuse for common references + HashMap reuseTRead = new HashMap<>(); + for(Pair p : candSet) { + Hop hop = p.getKey(); + Hop c = p.getValue(); + + DataOp tRead = reuseTRead.get(c.getHopID()); + if(tRead == null) { + String varname = createCutVarName(false); + + tRead = HopRewriteUtils.createTransientRead(varname, c); + reuseTRead.put(c.getHopID(), tRead); + + DataOp twrite = HopRewriteUtils.createTransientWrite(varname, c); + + // update live in and out of new statement block (for piggybacking) + DataIdentifier diVar = new DataIdentifier(varname); + diVar.setDimensions(c.getDim1(), c.getDim2()); + diVar.setBlocksize(c.getBlocksize()); + diVar.setDataType(c.getDataType()); + diVar.setValueType(c.getValueType()); + sbSplitOut.addVariable(varname, new DataIdentifier(diVar)); + sbOriginIn.addVariable(varname, new DataIdentifier(diVar)); + + rootsSbSplit.add(twrite); + } + + // create additional cut by rewriting both hop dags + int pos = HopRewriteUtils.getChildReferencePos(hop, c); + HopRewriteUtils.removeChildReferenceByPos(hop, c, pos); + HopRewriteUtils.addChildReference(hop, tRead, pos); + } + } + + private void rAddHopsToProbeSet(Hop hop, HashSet probeSet) { + if(hop.isVisited()) + return; + + // prevent cuts for no-ops + if(!(hop instanceof DataOp && !((DataOp)hop).isPersistentReadWrite()) && !(hop instanceof LiteralOp)) + probeSet.add(hop); + + if(hop.getInput() != null) { + for(Hop c : hop.getInput()) { + rAddHopsToProbeSet(c, probeSet); + } + } + + hop.setVisited(); + } + + /** + * NOTE: candset is a set of parent-child pairs because a parent might have + * multiple references to replicated hops. + * + * @param hop high-level operator + * @param probeSet probe set? + * @param candSet candidate set? + */ + private void rProbeAndAddHopsToCandidateSet(Hop hop, HashSet probeSet, HashSet> candSet) { + if(hop.isVisited()) + return; + + if(hop.getInput() != null) + for(Hop c : hop.getInput()) { + // probe for replicated operator, if any child is replicated, keep parent + // for cut between parent-child; otherwise recursively descend. + if(!probeSet.contains(c)) + rProbeAndAddHopsToCandidateSet(c, probeSet, candSet); + else + candSet.add(new Pair<>(hop,c)); + } + + hop.setVisited(); + } + + protected void collectCandidateChildOperators(ArrayList cand, HashSet candChilds) { + Hop.resetVisitStatus(cand); + if(cand != null) { + for(Hop root : cand) { + rCollectCandidateChildOperators(root, cand, candChilds, false); + } + } + + // Immediately reset the visit status because candidates might be inner nodes in the DAG. + // Subsequent resets on the root nodes of the DAG would otherwise not necessarily reach + // these nodes which could lead to missing checks on subsequent passes (e.g., when checking + // for replicated operators). + Hop.resetVisitStatus(cand); + } + + private void rCollectCandidateChildOperators(Hop hop, ArrayList cand, HashSet candChilds, boolean collect) { + if(hop.isVisited()) + return; + + // collect operator if necessary + if(collect) + candChilds.add(hop); + + // activate collection if we passed a hop from the candidates + boolean passedFlag = collect; + if(cand.contains(hop)) + passedFlag = true; + + // process childs recursively + if(hop.getInput() != null) { + for(Hop childHop : hop.getInput()) { + rCollectCandidateChildOperators(childHop, cand, candChilds, passedFlag); + } + } + + hop.setVisited(); + } +} From 981362f43c8d8b8c67fca27b2764581b9fa3729e Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Fri, 23 Oct 2026 15:46:33 +0200 Subject: [PATCH 14/14] refactor(main/hops/rewrite/RewriteSplitDagSparsityRecompile.java): rename rewrite class refactor(main/test/functions/rewrite/RewriteSplitDagSparsityRecompileTest.java): rename test related to the renamed rewrite class --- .../sysds/hops/rewrite/ProgramRewriter.java | 2 +- ... => RewriteSplitDagSparsityRecompile.java} | 4 ++-- ...RewriteSplitDagSparsityRecompileTest.java} | 22 +++++++++---------- ...writeSplitDagSparsityRecompileMMChain.dml} | 0 ... RewriteSplitDagSparsityRecompileRead.dml} | 0 5 files changed, 14 insertions(+), 14 deletions(-) rename src/main/java/org/apache/sysds/hops/rewrite/{RewriteSplitDagUnknownNnz.java => RewriteSplitDagSparsityRecompile.java} (97%) rename src/test/java/org/apache/sysds/test/functions/rewrite/{RewriteSplitDagUnknownNnzTest.java => RewriteSplitDagSparsityRecompileTest.java} (92%) rename src/test/scripts/functions/rewrite/{RewriteSplitDagUnknownNnzMMChain.dml => RewriteSplitDagSparsityRecompileMMChain.dml} (100%) rename src/test/scripts/functions/rewrite/{RewriteSplitDagUnknownNnzRead.dml => RewriteSplitDagSparsityRecompileRead.dml} (100%) 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 83eb3ddb161..178a3ecf238 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/ProgramRewriter.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/ProgramRewriter.java @@ -108,7 +108,7 @@ public ProgramRewriter(boolean staticRewrites, boolean dynamicRewrites) _sbRuleSet.add( new RewriteSplitDagUnknownCSVRead() ); //dependency: reblock, merge blocks if( OptimizerUtils.ALLOW_SPLIT_HOP_DAGS && ConfigurationManager.getDMLConfig().getBooleanValue(DMLConfig.SPARSITY_RECOMPILE) ) - _sbRuleSet.add(new RewriteSplitDagUnknownNnz()); + _sbRuleSet.add(new RewriteSplitDagSparsityRecompile()); if( OptimizerUtils.ALLOW_SPLIT_HOP_DAGS && ConfigurationManager.getCompilerConfigFlag(ConfigType.ALLOW_INDIVIDUAL_SB_SPECIFIC_OPS) ) _sbRuleSet.add( new RewriteSplitDagDataDependentOperators() ); //dependency: merge blocks diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagSparsityRecompile.java similarity index 97% rename from src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java rename to src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagSparsityRecompile.java index 8f1b6d1bdc5..c3237677ce9 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagUnknownNnz.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteSplitDagSparsityRecompile.java @@ -39,7 +39,7 @@ * Rule: Split Hop DAG after reads with unknown nnz. This is important to create recompile * hooks if mtd has unspecified nnz. */ -public class RewriteSplitDagUnknownNnz extends SplitStatementBlockRewriteRule { +public class RewriteSplitDagSparsityRecompile extends SplitStatementBlockRewriteRule { @Override public List rewriteStatementBlock(StatementBlock sb, ProgramRewriteStatus state) { return rRewriteStatementBlock(sb, state); @@ -118,7 +118,7 @@ public List rRewriteStatementBlock(StatementBlock sb, ProgramRew ret.addAll(tmp); // statement block with permanent read and transient write sb.setSplitDag(true); // avoid later merge by other rewrites - LOG.debug("RewriteSplitDagUnknownNnz: Split DAG into " + (ret.size() + 1) + " statement blocks."); + LOG.debug("RewriteSplitDagSparsityRecompile: Split DAG into " + (ret.size() + 1) + " statement blocks."); } ret.add(sb); diff --git a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagSparsityRecompileTest.java similarity index 92% rename from src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java rename to src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagSparsityRecompileTest.java index 19f7c80ca4d..1802ef54660 100644 --- a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagUnknownNnzTest.java +++ b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteSplitDagSparsityRecompileTest.java @@ -51,13 +51,13 @@ @RunWith(value = Parameterized.class) @net.jcip.annotations.NotThreadSafe -public class RewriteSplitDagUnknownNnzTest extends AutomatedTestBase { +public class RewriteSplitDagSparsityRecompileTest extends AutomatedTestBase { - private static final String TEST_NAME_READ = "RewriteSplitDagUnknownNnzRead"; - private static final String TEST_NAME_MMCHAIN = "RewriteSplitDagUnknownNnzMMChain"; + private static final String TEST_NAME_READ = "RewriteSplitDagSparsityRecompileRead"; + private static final String TEST_NAME_MMCHAIN = "RewriteSplitDagSparsityRecompileMMChain"; private static final String TEST_DIR = "functions/rewrite/"; private static final String TEST_CLASS_DIR = - TEST_DIR + RewriteSplitDagUnknownNnzTest.class.getSimpleName() + "/"; + TEST_DIR + RewriteSplitDagSparsityRecompileTest.class.getSimpleName() + "/"; private static final String PACKAGE_REWRITE = "org.apache.sysds.hops.rewrite.HopRewriteRule"; private static final String PACKAGE_RECOMPILE = "org.apache.sysds.hops.rewrite.StatementBlockRewriteRule"; private static Level _oldLevelRewrite; @@ -105,16 +105,16 @@ public void tearDown() { } @Test - public void testSplitDagUnknownNnzRead() { - testSplitDagUnknownNnz(TEST_NAME_READ, 4); + public void testSplitDagSparsityRecompileRead() { + testSplitDagSparsityRecompile(TEST_NAME_READ, 4); } @Test - public void testSplitDagUnknownNnzMMChain() { - testSplitDagUnknownNnz(TEST_NAME_MMCHAIN, 4); + public void testSplitDagSparsityRecompileMMChain() { + testSplitDagSparsityRecompile(TEST_NAME_MMCHAIN, 4); } - private void testSplitDagUnknownNnz(String testName, int numSBs) { + private void testSplitDagSparsityRecompile(String testName, int numSBs) { boolean oldFlag1 = OptimizerUtils.ALLOW_TRANSPOSE_MMCHAIN_REWRITES; boolean oldFlag2 = OptimizerUtils.ALLOW_SUM_PRODUCT_REWRITES; boolean oldFlag3 = OptimizerUtils.ALLOW_SPLIT_HOP_DAGS; @@ -176,14 +176,14 @@ private void testSplitDagUnknownNnz(String testName, int numSBs) { // check original log output String log_original_string = String.join(DELIMITER, log_original.stream().map(l -> l.getMessage().toString()).toArray(String[]::new)); - Assert.assertTrue(log_original_string.contains("RewriteSplitDagUnknownNnz: Split DAG into " + + Assert.assertTrue(log_original_string.contains("RewriteSplitDagSparsityRecompile: Split DAG into " + numSBs + " statement blocks.")); Assert.assertTrue(log_original_string.contains("mmchainoptsparse")); Assert.assertTrue(log_original_string.contains("Optimal Sparse MM Chain:")); // check reference log output String log_reference_string = String.join(DELIMITER, log_reference.stream().map(l -> l.getMessage().toString()).toArray(String[]::new)); - Assert.assertFalse(log_reference_string.contains("RewriteSplitDagUnknownNnz")); + Assert.assertFalse(log_reference_string.contains("RewriteSplitDagSparsityRecompile")); Assert.assertFalse(log_reference_string.contains("mmchainoptsparse")); } finally { diff --git a/src/test/scripts/functions/rewrite/RewriteSplitDagUnknownNnzMMChain.dml b/src/test/scripts/functions/rewrite/RewriteSplitDagSparsityRecompileMMChain.dml similarity index 100% rename from src/test/scripts/functions/rewrite/RewriteSplitDagUnknownNnzMMChain.dml rename to src/test/scripts/functions/rewrite/RewriteSplitDagSparsityRecompileMMChain.dml diff --git a/src/test/scripts/functions/rewrite/RewriteSplitDagUnknownNnzRead.dml b/src/test/scripts/functions/rewrite/RewriteSplitDagSparsityRecompileRead.dml similarity index 100% rename from src/test/scripts/functions/rewrite/RewriteSplitDagUnknownNnzRead.dml rename to src/test/scripts/functions/rewrite/RewriteSplitDagSparsityRecompileRead.dml