From d5f99841181fa9c5b42b6c75a204723bfddd7a5e Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Thu, 20 Aug 2026 16:49:56 +0200 Subject: [PATCH 1/6] feat(main/hops/recompile/SparsityDAGRecompiler.java): create class for sparsity-based recompilation NOTE: not implemented yet feat(main/conf/DMLConfig.java): add xml configuration option for enabling sparsity recompilation refactor(main/conf/DMLConfig.java): rename xml configuration options for sparsity rewrites feat(main/hops/recompile/Recompiler.java): add recompile block for sparsity-based recompilation feat(main/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java): add comment and log output to indicate the case which requires pre-fetching from the sparsity recompiler --- .../java/org/apache/sysds/conf/DMLConfig.java | 8 ++-- .../sysds/hops/recompile/Recompiler.java | 10 ++++- .../hops/recompile/SparsityDAGRecompiler.java | 41 +++++++++++++++++++ ...riteMatrixMultChainOptimizationSparse.java | 7 +++- 4 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java diff --git a/src/main/java/org/apache/sysds/conf/DMLConfig.java b/src/main/java/org/apache/sysds/conf/DMLConfig.java index b08c2864597..f5d237bbaaf 100644 --- a/src/main/java/org/apache/sysds/conf/DMLConfig.java +++ b/src/main/java/org/apache/sysds/conf/DMLConfig.java @@ -98,8 +98,9 @@ public class DMLConfig public static final String NATIVE_BLAS = "sysds.native.blas"; public static final String NATIVE_BLAS_DIR = "sysds.native.blas.directory"; public static final String DAG_LINEARIZATION = "sysds.compile.linearization"; - public static final String SPARSITY_REWRITES = "sysds.rewrites.sparsity.enabled"; // boolean - public static final String SPARSITY_ESTIMATOR = "sysds.rewrites.sparsity.estimator"; // see EstiamtionUtils.EstimatorType + public static final String SPARSITY_REWRITES = "sysds.sparsity.rewrites.enabled"; // boolean + public static final String SPARSITY_RECOMPILE = "sysds.sparsity.recompile.enabled"; // boolean + public static final String SPARSITY_ESTIMATOR = "sysds.sparsity.estimator"; // see EstiamtionUtils.EstimatorType public static final String CODEGEN = "sysds.codegen.enabled"; //boolean public static final String CODEGEN_API = "sysds.codegen.api"; // see SpoofCompiler.API public static final String CODEGEN_COMPILER = "sysds.codegen.compiler"; //see SpoofCompiler.CompilerType @@ -192,6 +193,7 @@ public class DMLConfig _defaultVals.put(COMPRESSED_TRANSFORMENCODE, "false"); _defaultVals.put(DAG_LINEARIZATION, DagLinearizer.DEPTH_FIRST.name()); _defaultVals.put(SPARSITY_REWRITES, "false"); + _defaultVals.put(SPARSITY_RECOMPILE, "false"); _defaultVals.put(SPARSITY_ESTIMATOR, EstimatorType.BASIC_AVG.name()); _defaultVals.put(CODEGEN, "false" ); _defaultVals.put(CODEGEN_API, GeneratorAPI.JAVA.name() ); @@ -481,7 +483,7 @@ public String getConfigInfo() { COMPRESSED_LINALG, COMPRESSED_LOSSY, COMPRESSED_VALID_COMPRESSIONS, COMPRESSED_OVERLAPPING, COMPRESSED_SAMPLING_RATIO, COMPRESSED_SOFT_REFERENCE_COUNT, COMPRESSED_COCODE, COMPRESSED_TRANSPOSE, COMPRESSED_TRANSFORMENCODE, DAG_LINEARIZATION, - SPARSITY_REWRITES, SPARSITY_ESTIMATOR, + SPARSITY_REWRITES, SPARSITY_RECOMPILE, SPARSITY_ESTIMATOR, CODEGEN, CODEGEN_API, CODEGEN_COMPILER, CODEGEN_OPTIMIZER, CODEGEN_PLANCACHE, CODEGEN_LITERALS, STATS_MAX_WRAP_LEN, LINEAGECACHESPILL, COMPILERASSISTED_RW, BUFFERPOOL_LIMIT, MEMORY_MANAGER, PRINT_GPU_MEMORY_INFO, AVAILABLE_GPUS, SYNCHRONIZE_GPU, EAGER_CUDA_FREE, GPU_RULE_BASED_PLACEMENT, diff --git a/src/main/java/org/apache/sysds/hops/recompile/Recompiler.java b/src/main/java/org/apache/sysds/hops/recompile/Recompiler.java index 0aef8a1583d..a4abfc1e726 100644 --- a/src/main/java/org/apache/sysds/hops/recompile/Recompiler.java +++ b/src/main/java/org/apache/sysds/hops/recompile/Recompiler.java @@ -46,6 +46,7 @@ import org.apache.sysds.common.Types.ValueType; import org.apache.sysds.conf.CompilerConfig.ConfigType; import org.apache.sysds.conf.ConfigurationManager; +import org.apache.sysds.conf.DMLConfig; import org.apache.sysds.hops.DataGenOp; import org.apache.sysds.hops.DataOp; import org.apache.sysds.hops.FunctionOp; @@ -387,6 +388,13 @@ else if( !codegen ) { memo.extract(hops, status); } + // sparsity-based DAG recompilation if enabled + if(ConfigurationManager.getDMLConfig().getBooleanValue(DMLConfig.SPARSITY_RECOMPILE)) { + // create deep copy of hops for in-place + Hop.resetVisitStatus(hops); + hops = SparsityDAGRecompiler.optimize(hops); + } + // codegen if enabled if( codegen ) { //create deep copy for in-place @@ -396,7 +404,7 @@ else if( !codegen ) { hops = SpoofCompiler.optimize(hops, (status==null || !status.isInitialCodegen())); } - + // set max parallelism constraint to ensure compilation // incl rewrites does not lose these hop-lop constraints Hop.resetVisitStatus(hops); diff --git a/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java b/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java new file mode 100644 index 00000000000..880c2835b08 --- /dev/null +++ b/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java @@ -0,0 +1,41 @@ +package org.apache.sysds.hops.recompile; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.sysds.hops.DataOp; +import org.apache.sysds.hops.Hop; +import org.apache.sysds.hops.rewrite.RewriteMatrixMultChainOptimizationSparse; + +public class SparsityDAGRecompiler { + private static final Log LOG = LogFactory.getLog(SparsityDAGRecompiler.class); + + private static void rObtainInputMatrixCharacteristics(Hop hop) { + if(hop.isMatrix() && !hop.isFederated()) { + System.out.println("Hop: " + hop.toString() + " requires recompile " + hop.requiresRecompile()); + if(hop instanceof DataOp && ((DataOp) hop).isRead()) { + DataOp dop = (DataOp) hop; + // TODO: pre-fetch this read operation + return; + } + List inputs = hop.getInput(); + for(Hop in : inputs) { + rObtainInputMatrixCharacteristics(in); + } + } + } + + public static ArrayList optimize(ArrayList roots) { + for(Hop r : roots) { + if(r.isMatrix() && !r.isFederated()) { + // TODO: pre-fetching of input data and deducing their metadata (nnz) + // rObtainInputMatrixCharacteristics(r); + RewriteMatrixMultChainOptimizationSparse rewriter = new RewriteMatrixMultChainOptimizationSparse(); + rewriter.rewriteHopDAGs(roots, null); + } + } + return roots; + } +} diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java index 5ab9d57e44c..db82ca00c6a 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java @@ -65,6 +65,11 @@ protected void optimizeMMChain(Hop hop, List mmChain, List mmOperators LOG.trace("Optimal Sparse MM Chain:"); mmChainRelinkHops(mmOperators.get(0), 0, size - 1, mmChain, mmOperators, new MutableInt(1), split, 1); } + else if(dimsKnown) { + LOG.debug("Input metadata is not available for sparsity rewrites. " + + "This could be resolved by pre-fetching data from disk during recompilation."); + // hop.setRequiresRecompile(); + } } /** @@ -132,7 +137,7 @@ private static boolean getInputMatrixCharacteristics(Hop hop, List chain, M Hop currentHop = chain.get(counter); inputMetaAvail &= currentHop.isMatrix(); inputMetaAvail &= !currentHop.isFederated(); - inputMetaAvail &= (currentHop.getDataCharacteristics().getNonZeros() != -1); + inputMetaAvail &= (currentHop.getNnz() != -1); if(inputMetaAvail) { sketchArray[counter] = new MMNode(currentHop.getDataCharacteristics()); } From 74cc41b961182fc105590f460d8c93fbabc4839d Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Mon, 24 Aug 2026 16:19:00 +0200 Subject: [PATCH 2/6] feat(main/hops/recompile/SparsityDAGRecompiler.java): mimic the sparsity-based rewriter copy the dynamic programming approach from the sparsity rewriter feat(main/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java): set dynamic recompilation flag if dimensions of hop are known but the sparsity is not NOTE: in these cases, the sparsity can be obtained during sparsity-based recompilation by pre-fetching data from disk or eagerly computing partial results --- .../sysds/hops/recompile/Recompiler.java | 2 - .../hops/recompile/SparsityDAGRecompiler.java | 411 +++++++++++++++++- .../RewriteMatrixMultChainOptimization.java | 12 +- ...riteMatrixMultChainOptimizationSparse.java | 7 +- .../RewriteMatrixMultChainOptSparseTest.java | 1 - 5 files changed, 402 insertions(+), 31 deletions(-) diff --git a/src/main/java/org/apache/sysds/hops/recompile/Recompiler.java b/src/main/java/org/apache/sysds/hops/recompile/Recompiler.java index a4abfc1e726..d0b8e66b920 100644 --- a/src/main/java/org/apache/sysds/hops/recompile/Recompiler.java +++ b/src/main/java/org/apache/sysds/hops/recompile/Recompiler.java @@ -390,8 +390,6 @@ else if( !codegen ) { // sparsity-based DAG recompilation if enabled if(ConfigurationManager.getDMLConfig().getBooleanValue(DMLConfig.SPARSITY_RECOMPILE)) { - // create deep copy of hops for in-place - Hop.resetVisitStatus(hops); hops = SparsityDAGRecompiler.optimize(hops); } diff --git a/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java b/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java index 880c2835b08..9e94222cb47 100644 --- a/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java +++ b/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java @@ -1,41 +1,416 @@ package org.apache.sysds.hops.recompile; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import org.apache.commons.lang3.mutable.MutableInt; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +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.rewrite.RewriteMatrixMultChainOptimizationSparse; +import org.apache.sysds.hops.HopsException; +import org.apache.sysds.hops.OptimizerUtils; +import org.apache.sysds.hops.estim.EstimationUtils.EstimatorType; +import org.apache.sysds.hops.estim.SparsityEstimator.OpCode; +import org.apache.sysds.hops.estim.MMNode; +import org.apache.sysds.hops.estim.SparsityEstimator; +import org.apache.sysds.hops.rewrite.HopRewriteUtils; +import org.apache.sysds.runtime.meta.MatrixCharacteristics; +import org.apache.sysds.runtime.util.CollectionUtils; +import org.apache.sysds.utils.Explain; public class SparsityDAGRecompiler { private static final Log LOG = LogFactory.getLog(SparsityDAGRecompiler.class); - private static void rObtainInputMatrixCharacteristics(Hop hop) { - if(hop.isMatrix() && !hop.isFederated()) { - System.out.println("Hop: " + hop.toString() + " requires recompile " + hop.requiresRecompile()); - if(hop instanceof DataOp && ((DataOp) hop).isRead()) { - DataOp dop = (DataOp) hop; - // TODO: pre-fetch this read operation - return; + protected static void clearLinksWithinChain(Hop hop, List operators) { + for(int i=0; i < operators.size(); i++) { + Hop op = operators.get(i); + if(op.getInput().size() != 2 || (i != 0 && op.getParent().size() > 1 )) { + throw new HopsException(hop.printErrorLocation() + + "Unexpected error while applying sparsity-based recompilation on matrix-mult chain. \n"); } - List inputs = hop.getInput(); - for(Hop in : inputs) { - rObtainInputMatrixCharacteristics(in); + Hop input1 = op.getInput().get(0); + Hop input2 = op.getInput().get(1); + + op.getInput().clear(); + input1.getParent().remove(op); + input2.getParent().remove(op); + } + } + + /** + * NOTE: Copied from RewriteMatrixMultChainOptimizationSparse.java + * Obtains all dimension information of the chain and constructs the dimArray. + * If all dimensions are known it returns true; othrewise the mmchain rewrite + * should be ended without modifications. + * + * @param hop high-level operator + * @param chain list of high-level operators + * @param dimsArray dimension array + * @return true if all dimensions known + */ + protected static boolean getDimsArray(Hop hop, List chain, double[] dimsArray) { + boolean dimsKnown = true; + + // Build the array containing dimensions from all matrices in the chain + // check the dimensions in the matrix chain to insure all dimensions are known + for( int i=0; i< chain.size(); i++ ) + if( chain.get(i).getDim1() <= 0 || chain.get(i).getDim2() <= 0 ) + dimsKnown = false; + + if(dimsKnown) { // populate dims array if all dims known + for( int i = 0; i < chain.size(); i++ ) { + if (i == 0) { + dimsArray[i] = chain.get(i).getDim1(); + if (dimsArray[i] <= 0) { + throw new HopsException(hop.printErrorLocation() + + "Hops::optimizeMMChain() : Invalid Matrix Dimension: "+ dimsArray[i]); + } + } + else if (chain.get(i - 1).getDim2() != chain.get(i).getDim1()) { + throw new HopsException(hop.printErrorLocation() + + "Hops::optimizeMMChain() : Matrix Dimension Mismatch: " + + chain.get(i - 1).getDim2()+" != "+chain.get(i).getDim1()); + } + + dimsArray[i + 1] = chain.get(i).getDim2(); + if( dimsArray[i + 1] <= 0 ) { + throw new HopsException(hop.printErrorLocation() + + "Hops::optimizeMMChain() : Invalid Matrix Dimension: " + dimsArray[i + 1]); + } } } + + return dimsKnown; } - public static ArrayList optimize(ArrayList roots) { - for(Hop r : roots) { - if(r.isMatrix() && !r.isFederated()) { - // TODO: pre-fetching of input data and deducing their metadata (nnz) - // rObtainInputMatrixCharacteristics(r); - RewriteMatrixMultChainOptimizationSparse rewriter = new RewriteMatrixMultChainOptimizationSparse(); - rewriter.rewriteHopDAGs(roots, null); + /** + * NOTE: Copied from RewriteMatrixMultChainOptimizationSparse.java + * mmChainRelinkHops(): This method gets invoked after finding the optimal + * order (split[][]) from dynamic programming. It relinks the Hops that are + * part of the mmChain. + * @param mmChain : basic operands in the entire matrix multiplication chain. + * @param mmOperators : Hops that store the intermediate results in the chain. + * For example: A = B %*% (C %*% D) there will be three + * Hops in mmChain (B,C,D), and two Hops in mmOperators + * (one for each * %*%). + * @param h high level operator + * @param i array index i + * @param j array index j + * @param opIndex operator index + * @param split optimal order + * @param level log level + */ + protected final void mmChainRelinkHops(Hop h, int i, int j, List mmChain, + List mmOperators, MutableInt opIndex, int[][] split, int level) { + // NOTE: the opIndex is a MutableInt in order to get the correct positions + // in ragged chains like ((((a, b), c), (D, E), f), e) that might be given + // like that by the original scripts variable assignments + // single matrix - end of recursion + if(i == j) { + logTraceHop(h, level); + return; + } + + if(LOG.isTraceEnabled()){ + String offset = Explain.getIdentation(level); + LOG.trace(offset + "("); + } + + // Set Input1 for current Hop h + if(i == split[i][j]) { + h.getInput().add(mmChain.get(i)); + mmChain.get(i).getParent().add(h); + } + else { + int ix = opIndex.getValue(); + opIndex.increment(); + h.getInput().add(mmOperators.get(ix)); + mmOperators.get(ix).getParent().add(h); + } + + // Set Input2 for current Hop h + if(split[i][j] + 1 == j) { + h.getInput().add(mmChain.get(j)); + mmChain.get(j).getParent().add(h); + } + else { + int ix = opIndex.getValue(); + opIndex.increment(); + h.getInput().add(mmOperators.get(ix)); + mmOperators.get(ix).getParent().add(h); + } + + // Find children for both the inputs + mmChainRelinkHops(h.getInput().get(0), i, split[i][j], + mmChain, mmOperators, opIndex, split, level+1); + mmChainRelinkHops(h.getInput().get(1), split[i][j] + 1, j, + mmChain, mmOperators, opIndex, split, level+1); + + // Propagate properties of input hops to current hop h + h.refreshSizeInformation(); + + if(LOG.isTraceEnabled()){ + String offset = Explain.getIdentation(level); + LOG.trace(offset + ")"); + } + } + + protected void optimizeMMChain(Hop hop, List mmChain, List mmOperators) { + // Step 2: construct dims array and input matrices + double[] dimsArray = new double[mmChain.size() + 1]; + boolean dimsKnown = getDimsArray(hop, mmChain, dimsArray); + MMNode[] sketchArray = new MMNode[mmChain.size() + 1]; + boolean inputMetaAvail = getInputMatrixCharacteristics(hop, mmChain, sketchArray); + if(dimsKnown && inputMetaAvail) { + // Step 3: clear the links among Hops within the identified chain + clearLinksWithinChain ( hop, mmOperators ); + + // Step 4: Find the optimal ordering via dynamic programming. + + // Invoke Dynamic Programming + int size = mmChain.size(); + int[][] split = mmChainDPSparse(dimsArray, sketchArray, mmChain.size()); + + // Step 5: Relink the hops using the optimal ordering (split[][]) found from DP. + LOG.trace("Optimal Sparse MM Chain:"); + mmChainRelinkHops(mmOperators.get(0), 0, size - 1, mmChain, mmOperators, + new MutableInt(1), split, 1); + } + } + + /** + * NOTE: Copied from RewriteMatrixMultChainOptimizationSparse.java + * mmChainDP(): Core method to perform dynamic programming on a given array + * of matrix dimensions. + * + * Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein + * Introduction to Algorithms, Third Edition, MIT Press, page 395. + */ + private static int[][] mmChainDPSparse(double[] dimArray, MMNode[] sketchArray, int size) { + double[][] dpMatrix = new double[size][size]; //min cost table + MMNode[][] dpMatrixS = new MMNode[size][size]; //min sketch table + int[][] split = new int[size][size]; //min cost index table + + //init minimum costs for chains of length 1 + for( int i = 0; i < size; i++ ) { + Arrays.fill(dpMatrix[i], 0); + Arrays.fill(split[i], -1); + dpMatrixS[i][i] = sketchArray[i]; + } + + //compute cost-optimal chains for increasing chain sizes + SparsityEstimator estim = EstimatorType.valueOf(ConfigurationManager.getDMLConfig() + .getTextValue(DMLConfig.SPARSITY_ESTIMATOR)).getEstimator(); + for(int l = 2; l <= size; l++) { // chain length + for(int i = 0; i < size - l + 1; i++) { + int j = i + l - 1; + // find cost of (i,j) + dpMatrix[i][j] = Double.MAX_VALUE; + for(int k = i; k <= j - 1; k++) { + // construct estimation nodes (w/ lazy propagation and memoization) + MMNode tmp = new MMNode(dpMatrixS[i][k], dpMatrixS[k+1][j], OpCode.MM); + estim.estim(tmp); + + // recursive cost computation + double cost = dpMatrix[i][k] + dpMatrix[k + 1][j] + + OptimizerUtils.getSparsity(tmp.getLeft().getDataCharacteristics()) * + OptimizerUtils.getSparsity(tmp.getRight().getDataCharacteristics()) * + tmp.getLeft().getRows() * tmp.getLeft().getCols() * tmp.getRight().getCols(); + + // prune suboptimal + if( cost < dpMatrix[i][j] ) { + dpMatrix[i][j] = cost; + dpMatrixS[i][j] = tmp; + split[i][j] = k; + } + } + + if(LOG.isTraceEnabled()) + LOG.trace("mmchainoptsparse [i=" + (i + 1) + ",j=" + (j + 1) + "]: costs = " + dpMatrix[i][j] + + ", split = " + (split[i][j] + 1)); } } + + return split; + } + + private static boolean getInputMatrixCharacteristics(Hop hop, List chain, MMNode[] sketchArray) { + boolean inputMetaAvail = true; + + for(int counter = 0; counter < chain.size(); counter++) { + Hop currentHop = chain.get(counter); + inputMetaAvail &= currentHop.isMatrix(); + inputMetaAvail &= !currentHop.isFederated(); + inputMetaAvail &= (currentHop.getNnz() != -1); + if(inputMetaAvail) { + sketchArray[counter] = new MMNode(currentHop.getDataCharacteristics()); + } + else + break; + } + + return inputMetaAvail; + } + + private static int inputCount(Hop p, Hop h) { + return CollectionUtils.cardinality(h, p.getInput()); + } + + private static void logTraceHop(Hop hop, int level) { + if(LOG.isTraceEnabled()) { + String offset = Explain.getIdentation(level); + LOG.trace(offset + "Hop " + hop.getName() + "(" + hop.getClass().getSimpleName() + + ", " + hop.getHopID() + ")" + " " + hop.getDim1() + "x" + hop.getDim2()); + } + } + + /** + * optimizeMMChain(): It optimizes the matrix multiplication chain in which + * the last Hop is "hop". Step-1) Identify the chain (mmChain). (Step-2) clear all + * links among the Hops that are involved in mmChain. (Step-3) Find the + * optimal ordering (dynamic programming) (Step-4) Relink the hops in + * mmChain. + * + * @param hop high-level operator + */ + private void prepAndOptimizeMMChain(Hop hop) { + if(LOG.isTraceEnabled()) { + LOG.trace("Sparsity-based MM Chain Recompilation for HOP: (" + + hop.getClass().getSimpleName() + ", " + hop.getHopID() + + ", " + hop.getName() + ")"); + } + + List mmChain = new ArrayList<>(); + List mmOperators = new ArrayList<>(); + List tempList; + + // Step 1: Identify the chain (mmChain) & clear all links among the Hops + // that are involved in mmChain. + + // Initialize mmChain with my inputs + mmOperators.add(hop); + for(Hop hi : hop.getInput()) + mmChain.add(hi); + + // expand each Hop in mmChain to find the entire matrix multiplication + // chain + int i = 0; + while(i < mmChain.size()) { + boolean expandable = false; + + Hop h = mmChain.get(i); + /* + * Check if mmChain[i] is expandable: + * 1) It must be MATMULT + * 2) It must not have been visited already + * (one MATMULT should get expanded only in one chain) + * 3) Its output should not be used in multiple places + * (either within chain or outside the chain) + */ + + if(HopRewriteUtils.isMatrixMultiply(h) && + !((AggBinaryOp)h).hasLeftPMInput() && !h.isVisited()) { + // check if the output of "h" is used at multiple places. If yes, it can + // not be expanded. + expandable = !(h.getParent().size() > 1 || + inputCount(h.getParent().get(0), h) > 1); + if(!expandable) { + optimizeHopDAG(h); + break; + } + } + else { + i = i + 1; + } + + if(expandable) { + h.setVisited(); + tempList = mmChain.get(i).getInput(); + if(tempList.size() != 2) { + throw new HopsException(hop.printErrorLocation() + + "Hops::rule_OptimizeMMChain(): AggBinary must have exactly two inputs."); + } + + // add current operator to mmOperators, and its input nodes to mmChain + mmOperators.add(mmChain.get(i)); + mmChain.set(i, tempList.get(0)); + mmChain.add(i + 1, tempList.get(1)); + } + else { + optimizeHopDAG(h); + } + } + + // print the MMChain + if(LOG.isTraceEnabled()) { + LOG.trace("Identified MM Chain: "); + for(Hop h : mmChain) { + logTraceHop(h, 1); + } + } + + // core mmchain optimization + if(mmChain.size() == 2) + return; // nothing to optimize + else + optimizeMMChain(hop, mmChain, mmOperators); + } + + private MatrixCharacteristics obtainMatrixCharacteristics(DataOp dop) { + // TODO: pre-fetch data from disk + System.out.println("SparsityDAGRecompiler.java:370 - Should prefetch read operation to obtain matrix characteristics w/ sparsity"); + MatrixCharacteristics ret = (MatrixCharacteristics)(dop.getDataCharacteristics()); + System.out.println("SparsityDAGRecompiler.java:372 - Known Matrix Characteristics: " + ret.toString()); + return ret; + } + + private void prepHop(Hop hop) { + hop.setVisited(); + + // optimize the inputs + for(Hop hi : hop.getInput()) + optimizeHopDAG(hi); // recursion + + // TODO: Optimize the hop that is not a MatMult. Its inputs are already optimized. + // (i.e., pre-fetching and pre-computations) + if(hop instanceof DataOp && ((DataOp)hop).isRead() && ((DataOp)hop).getNnz() < 0) { + obtainMatrixCharacteristics((DataOp)hop); + } + } + + private void optimizeHopDAG(Hop hop) { + if(!hop.isMatrix() || hop.isFederated() || hop.isVisited()) + return; + + // TODO: Also rewrite chains with additional operations (e.g,. cbind, etc.) + if(HopRewriteUtils.isMatrixMultiply(hop) && !((AggBinaryOp)hop).hasLeftPMInput()) { + // Try to find and optimize the chain in which current Hop is the + // last operator + prepAndOptimizeMMChain(hop); + } + + if(!hop.isVisited()) { + prepHop(hop); + } + return; + } + + private ArrayList optimizeHopDAGs(ArrayList roots) { + for(Hop r : roots) { + optimizeHopDAG(r); + } return roots; } + + public static ArrayList optimize(ArrayList roots) { + SparsityDAGRecompiler spRecomp = new SparsityDAGRecompiler(); + Hop.resetVisitStatus(roots); + ArrayList ret = spRecomp.optimizeHopDAGs(roots); + return ret; + } } diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimization.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimization.java index 884f9e82e8f..7899e6d65f0 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimization.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimization.java @@ -75,18 +75,16 @@ public Hop rewriteHopDAG(Hop root, ProgramRewriteStatus state) */ private void ruleOptimizeMMChains(Hop hop, ProgramRewriteStatus state) { - if( hop.isVisited() ) + if(hop.isVisited()) return; - - if( HopRewriteUtils.isMatrixMultiply(hop) - && !((AggBinaryOp)hop).hasLeftPMInput() && !hop.isVisited() ) - { + + if( HopRewriteUtils.isMatrixMultiply(hop) && !((AggBinaryOp)hop).hasLeftPMInput()) { // Try to find and optimize the chain in which current Hop is the // last operator prepAndOptimizeMMChain(hop, state); } - - for( Hop hi : hop.getInput() ) + + for(Hop hi : hop.getInput()) ruleOptimizeMMChains(hi, state); hop.setVisited(); diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java index db82ca00c6a..89b9c81b078 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java @@ -31,6 +31,7 @@ import org.apache.sysds.hops.estim.SparsityEstimator; import org.apache.sysds.hops.estim.EstimationUtils.EstimatorType; import org.apache.sysds.hops.estim.SparsityEstimator.OpCode; +import org.apache.sysds.runtime.DMLRuntimeException; /** * Rule: Determine the optimal order of execution for a chain of @@ -65,10 +66,10 @@ protected void optimizeMMChain(Hop hop, List mmChain, List mmOperators LOG.trace("Optimal Sparse MM Chain:"); mmChainRelinkHops(mmOperators.get(0), 0, size - 1, mmChain, mmOperators, new MutableInt(1), split, 1); } - else if(dimsKnown) { + else if(dimsKnown && ConfigurationManager.getDMLConfig().getBooleanValue(DMLConfig.SPARSITY_RECOMPILE)) { LOG.debug("Input metadata is not available for sparsity rewrites. " + - "This could be resolved by pre-fetching data from disk during recompilation."); - // hop.setRequiresRecompile(); + "Enabling dynamic recompilation to obtain metadata during sparsity-based recompilation."); + hop.setRequiresRecompile(); } } diff --git a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteMatrixMultChainOptSparseTest.java b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteMatrixMultChainOptSparseTest.java index bf9acd9e52a..a4ad161fc29 100644 --- a/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteMatrixMultChainOptSparseTest.java +++ b/src/test/java/org/apache/sysds/test/functions/rewrite/RewriteMatrixMultChainOptSparseTest.java @@ -147,7 +147,6 @@ private void testRewriteMatrixMultChainOpSparse(boolean rewrites) { writeInputMatrixWithMTD("X", X, X_nnz, true); writeInputMatrixWithMTD("Y", Y, Y_nnz, true); - //execute tests TestAppender appender = LoggingUtils.overwrite(); // capture log output runTest(true, false, null, -1); From 6da1ba86e92e6366f031b7d417248d4bee96d698 Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Tue, 25 Aug 2026 10:08:37 +0200 Subject: [PATCH 3/6] chore(main/hops/recompile/SparsityDAGRecompiler.java): create member variable for the execution context which will be needed once we perform partial computations chore(main/hops/recompile/Recompiler.java): pass the execution context to the sparsity-based recompiler --- .../sysds/hops/recompile/Recompiler.java | 2 +- .../hops/recompile/SparsityDAGRecompiler.java | 25 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/apache/sysds/hops/recompile/Recompiler.java b/src/main/java/org/apache/sysds/hops/recompile/Recompiler.java index d0b8e66b920..0f818eca2e1 100644 --- a/src/main/java/org/apache/sysds/hops/recompile/Recompiler.java +++ b/src/main/java/org/apache/sysds/hops/recompile/Recompiler.java @@ -390,7 +390,7 @@ else if( !codegen ) { // sparsity-based DAG recompilation if enabled if(ConfigurationManager.getDMLConfig().getBooleanValue(DMLConfig.SPARSITY_RECOMPILE)) { - hops = SparsityDAGRecompiler.optimize(hops); + hops = SparsityDAGRecompiler.optimize(hops, ec); } // codegen if enabled diff --git a/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java b/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java index 9e94222cb47..cff06ad72c3 100644 --- a/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java +++ b/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java @@ -19,13 +19,19 @@ import org.apache.sysds.hops.estim.MMNode; import org.apache.sysds.hops.estim.SparsityEstimator; import org.apache.sysds.hops.rewrite.HopRewriteUtils; -import org.apache.sysds.runtime.meta.MatrixCharacteristics; +import org.apache.sysds.runtime.controlprogram.context.ExecutionContext; import org.apache.sysds.runtime.util.CollectionUtils; import org.apache.sysds.utils.Explain; public class SparsityDAGRecompiler { private static final Log LOG = LogFactory.getLog(SparsityDAGRecompiler.class); + private final ExecutionContext _ec; + + public SparsityDAGRecompiler(ExecutionContext ec) { + this._ec = ec; + } + protected static void clearLinksWithinChain(Hop hop, List operators) { for(int i=0; i < operators.size(); i++) { Hop op = operators.get(i); @@ -361,14 +367,6 @@ private void prepAndOptimizeMMChain(Hop hop) { optimizeMMChain(hop, mmChain, mmOperators); } - private MatrixCharacteristics obtainMatrixCharacteristics(DataOp dop) { - // TODO: pre-fetch data from disk - System.out.println("SparsityDAGRecompiler.java:370 - Should prefetch read operation to obtain matrix characteristics w/ sparsity"); - MatrixCharacteristics ret = (MatrixCharacteristics)(dop.getDataCharacteristics()); - System.out.println("SparsityDAGRecompiler.java:372 - Known Matrix Characteristics: " + ret.toString()); - return ret; - } - private void prepHop(Hop hop) { hop.setVisited(); @@ -376,11 +374,12 @@ private void prepHop(Hop hop) { for(Hop hi : hop.getInput()) optimizeHopDAG(hi); // recursion - // TODO: Optimize the hop that is not a MatMult. Its inputs are already optimized. + // TODO: Optimize the hops that are not matrix multiplications. Its inputs are already optimized. // (i.e., pre-fetching and pre-computations) if(hop instanceof DataOp && ((DataOp)hop).isRead() && ((DataOp)hop).getNnz() < 0) { - obtainMatrixCharacteristics((DataOp)hop); + // TODO: pre-fetch } + // TODO: pre-compute } private void optimizeHopDAG(Hop hop) { @@ -407,8 +406,8 @@ private ArrayList optimizeHopDAGs(ArrayList roots) { return roots; } - public static ArrayList optimize(ArrayList roots) { - SparsityDAGRecompiler spRecomp = new SparsityDAGRecompiler(); + public static ArrayList optimize(ArrayList roots, ExecutionContext ec) { + SparsityDAGRecompiler spRecomp = new SparsityDAGRecompiler(ec); Hop.resetVisitStatus(roots); ArrayList ret = spRecomp.optimizeHopDAGs(roots); return ret; From 678cd3ee55ac527cb9a355c8b6e90c3a1cb008cc Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Tue, 25 Aug 2026 11:06:54 +0200 Subject: [PATCH 4/6] chore(main/hops/recompile/SparsityDAGRecompiler.java): print nnz information with the dimensions of hops inside a matrix multiplication chain create logger with the class name indicate that function is copied from a different class in the funcation header chore(main/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java): remove unused import --- .../apache/sysds/hops/recompile/SparsityDAGRecompiler.java | 6 ++++-- .../rewrite/RewriteMatrixMultChainOptimizationSparse.java | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java b/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java index cff06ad72c3..ad99a9163db 100644 --- a/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java +++ b/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java @@ -24,7 +24,7 @@ import org.apache.sysds.utils.Explain; public class SparsityDAGRecompiler { - private static final Log LOG = LogFactory.getLog(SparsityDAGRecompiler.class); + private static final Log LOG = LogFactory.getLog(SparsityDAGRecompiler.class.getName()); private final ExecutionContext _ec; @@ -271,11 +271,13 @@ private static void logTraceHop(Hop hop, int level) { if(LOG.isTraceEnabled()) { String offset = Explain.getIdentation(level); LOG.trace(offset + "Hop " + hop.getName() + "(" + hop.getClass().getSimpleName() + - ", " + hop.getHopID() + ")" + " " + hop.getDim1() + "x" + hop.getDim2()); + ", " + hop.getHopID() + ")" + " " + hop.getDim1() + "x" + hop.getDim2() + + "[nnz" + hop.getNnz() + "]"); } } /** + * NOTE: Copied from RewriteMatrixMultChainOptimizationSparse.java * optimizeMMChain(): It optimizes the matrix multiplication chain in which * the last Hop is "hop". Step-1) Identify the chain (mmChain). (Step-2) clear all * links among the Hops that are involved in mmChain. (Step-3) Find the diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java index 89b9c81b078..b7ada9f20d2 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java @@ -31,7 +31,6 @@ import org.apache.sysds.hops.estim.SparsityEstimator; import org.apache.sysds.hops.estim.EstimationUtils.EstimatorType; import org.apache.sysds.hops.estim.SparsityEstimator.OpCode; -import org.apache.sysds.runtime.DMLRuntimeException; /** * Rule: Determine the optimal order of execution for a chain of From a40a88a1ca78dba3405738d46b65d3158ec1c982 Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Tue, 25 Aug 2026 12:07:39 +0200 Subject: [PATCH 5/6] chore(main/hops/recompile/SparsityDAGRecompiler.java): add license header --- .../hops/recompile/SparsityDAGRecompiler.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java b/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java index ad99a9163db..b29318b1110 100644 --- a/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java +++ b/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java @@ -1,3 +1,22 @@ +/* + * 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.recompile; import java.util.ArrayList; From aec19ba3134a4ecb88a5a2baac32c1e7006b6ae0 Mon Sep 17 00:00:00 2001 From: ywcb00 Date: Tue, 25 Aug 2026 12:26:04 +0200 Subject: [PATCH 6/6] chore(**): apply formatting --- .../hops/recompile/SparsityDAGRecompiler.java | 127 +++++++++--------- .../RewriteMatrixMultChainOptimization.java | 2 +- ...riteMatrixMultChainOptimizationSparse.java | 4 +- 3 files changed, 63 insertions(+), 70 deletions(-) diff --git a/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java b/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java index b29318b1110..31fa1dbc8a6 100644 --- a/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java +++ b/src/main/java/org/apache/sysds/hops/recompile/SparsityDAGRecompiler.java @@ -52,11 +52,11 @@ public SparsityDAGRecompiler(ExecutionContext ec) { } protected static void clearLinksWithinChain(Hop hop, List operators) { - for(int i=0; i < operators.size(); i++) { + for(int i = 0; i < operators.size(); i++) { Hop op = operators.get(i); - if(op.getInput().size() != 2 || (i != 0 && op.getParent().size() > 1 )) { - throw new HopsException(hop.printErrorLocation() + - "Unexpected error while applying sparsity-based recompilation on matrix-mult chain. \n"); + if(op.getInput().size() != 2 || (i != 0 && op.getParent().size() > 1)) { + throw new HopsException(hop.printErrorLocation() + + "Unexpected error while applying sparsity-based recompilation on matrix-mult chain. \n"); } Hop input1 = op.getInput().get(0); Hop input2 = op.getInput().get(1); @@ -73,8 +73,8 @@ protected static void clearLinksWithinChain(Hop hop, List operators) { * If all dimensions are known it returns true; othrewise the mmchain rewrite * should be ended without modifications. * - * @param hop high-level operator - * @param chain list of high-level operators + * @param hop high-level operator + * @param chain list of high-level operators * @param dimsArray dimension array * @return true if all dimensions known */ @@ -83,29 +83,29 @@ protected static boolean getDimsArray(Hop hop, List chain, double[] dimsArr // Build the array containing dimensions from all matrices in the chain // check the dimensions in the matrix chain to insure all dimensions are known - for( int i=0; i< chain.size(); i++ ) - if( chain.get(i).getDim1() <= 0 || chain.get(i).getDim2() <= 0 ) + for(int i = 0; i < chain.size(); i++) + if(chain.get(i).getDim1() <= 0 || chain.get(i).getDim2() <= 0) dimsKnown = false; if(dimsKnown) { // populate dims array if all dims known - for( int i = 0; i < chain.size(); i++ ) { - if (i == 0) { + for(int i = 0; i < chain.size(); i++) { + if(i == 0) { dimsArray[i] = chain.get(i).getDim1(); - if (dimsArray[i] <= 0) { - throw new HopsException(hop.printErrorLocation() + - "Hops::optimizeMMChain() : Invalid Matrix Dimension: "+ dimsArray[i]); + if(dimsArray[i] <= 0) { + throw new HopsException(hop.printErrorLocation() + + "Hops::optimizeMMChain() : Invalid Matrix Dimension: " + dimsArray[i]); } } - else if (chain.get(i - 1).getDim2() != chain.get(i).getDim1()) { - throw new HopsException(hop.printErrorLocation() + - "Hops::optimizeMMChain() : Matrix Dimension Mismatch: " + - chain.get(i - 1).getDim2()+" != "+chain.get(i).getDim1()); + else if(chain.get(i - 1).getDim2() != chain.get(i).getDim1()) { + throw new HopsException( + hop.printErrorLocation() + "Hops::optimizeMMChain() : Matrix Dimension Mismatch: " + + chain.get(i - 1).getDim2() + " != " + chain.get(i).getDim1()); } dimsArray[i + 1] = chain.get(i).getDim2(); - if( dimsArray[i + 1] <= 0 ) { - throw new HopsException(hop.printErrorLocation() + - "Hops::optimizeMMChain() : Invalid Matrix Dimension: " + dimsArray[i + 1]); + if(dimsArray[i + 1] <= 0) { + throw new HopsException(hop.printErrorLocation() + + "Hops::optimizeMMChain() : Invalid Matrix Dimension: " + dimsArray[i + 1]); } } } @@ -118,20 +118,19 @@ else if (chain.get(i - 1).getDim2() != chain.get(i).getDim1()) { * mmChainRelinkHops(): This method gets invoked after finding the optimal * order (split[][]) from dynamic programming. It relinks the Hops that are * part of the mmChain. - * @param mmChain : basic operands in the entire matrix multiplication chain. - * @param mmOperators : Hops that store the intermediate results in the chain. - * For example: A = B %*% (C %*% D) there will be three - * Hops in mmChain (B,C,D), and two Hops in mmOperators - * (one for each * %*%). - * @param h high level operator - * @param i array index i - * @param j array index j - * @param opIndex operator index - * @param split optimal order - * @param level log level + * + * @param mmChain : basic operands in the entire matrix multiplication chain. + * @param mmOperators : Hops that store the intermediate results in the chain. For example: A = B %*% (C %*% D) + * there will be three Hops in mmChain (B,C,D), and two Hops in mmOperators (one for each * %*%). + * @param h high level operator + * @param i array index i + * @param j array index j + * @param opIndex operator index + * @param split optimal order + * @param level log level */ - protected final void mmChainRelinkHops(Hop h, int i, int j, List mmChain, - List mmOperators, MutableInt opIndex, int[][] split, int level) { + protected final void mmChainRelinkHops(Hop h, int i, int j, List mmChain, List mmOperators, + MutableInt opIndex, int[][] split, int level) { // NOTE: the opIndex is a MutableInt in order to get the correct positions // in ragged chains like ((((a, b), c), (D, E), f), e) that might be given // like that by the original scripts variable assignments @@ -141,7 +140,7 @@ protected final void mmChainRelinkHops(Hop h, int i, int j, List mmChain, return; } - if(LOG.isTraceEnabled()){ + if(LOG.isTraceEnabled()) { String offset = Explain.getIdentation(level); LOG.trace(offset + "("); } @@ -171,15 +170,13 @@ protected final void mmChainRelinkHops(Hop h, int i, int j, List mmChain, } // Find children for both the inputs - mmChainRelinkHops(h.getInput().get(0), i, split[i][j], - mmChain, mmOperators, opIndex, split, level+1); - mmChainRelinkHops(h.getInput().get(1), split[i][j] + 1, j, - mmChain, mmOperators, opIndex, split, level+1); + mmChainRelinkHops(h.getInput().get(0), i, split[i][j], mmChain, mmOperators, opIndex, split, level + 1); + mmChainRelinkHops(h.getInput().get(1), split[i][j] + 1, j, mmChain, mmOperators, opIndex, split, level + 1); // Propagate properties of input hops to current hop h h.refreshSizeInformation(); - if(LOG.isTraceEnabled()){ + if(LOG.isTraceEnabled()) { String offset = Explain.getIdentation(level); LOG.trace(offset + ")"); } @@ -193,7 +190,7 @@ protected void optimizeMMChain(Hop hop, List mmChain, List mmOperators boolean inputMetaAvail = getInputMatrixCharacteristics(hop, mmChain, sketchArray); if(dimsKnown && inputMetaAvail) { // Step 3: clear the links among Hops within the identified chain - clearLinksWithinChain ( hop, mmOperators ); + clearLinksWithinChain(hop, mmOperators); // Step 4: Find the optimal ordering via dynamic programming. @@ -201,10 +198,9 @@ protected void optimizeMMChain(Hop hop, List mmChain, List mmOperators int size = mmChain.size(); int[][] split = mmChainDPSparse(dimsArray, sketchArray, mmChain.size()); - // Step 5: Relink the hops using the optimal ordering (split[][]) found from DP. + // Step 5: Relink the hops using the optimal ordering (split[][]) found from DP. LOG.trace("Optimal Sparse MM Chain:"); - mmChainRelinkHops(mmOperators.get(0), 0, size - 1, mmChain, mmOperators, - new MutableInt(1), split, 1); + mmChainRelinkHops(mmOperators.get(0), 0, size - 1, mmChain, mmOperators, new MutableInt(1), split, 1); } } @@ -217,20 +213,20 @@ protected void optimizeMMChain(Hop hop, List mmChain, List mmOperators * Introduction to Algorithms, Third Edition, MIT Press, page 395. */ private static int[][] mmChainDPSparse(double[] dimArray, MMNode[] sketchArray, int size) { - double[][] dpMatrix = new double[size][size]; //min cost table - MMNode[][] dpMatrixS = new MMNode[size][size]; //min sketch table - int[][] split = new int[size][size]; //min cost index table + double[][] dpMatrix = new double[size][size]; // min cost table + MMNode[][] dpMatrixS = new MMNode[size][size]; // min sketch table + int[][] split = new int[size][size]; // min cost index table - //init minimum costs for chains of length 1 - for( int i = 0; i < size; i++ ) { + // init minimum costs for chains of length 1 + for(int i = 0; i < size; i++) { Arrays.fill(dpMatrix[i], 0); Arrays.fill(split[i], -1); dpMatrixS[i][i] = sketchArray[i]; } - //compute cost-optimal chains for increasing chain sizes - SparsityEstimator estim = EstimatorType.valueOf(ConfigurationManager.getDMLConfig() - .getTextValue(DMLConfig.SPARSITY_ESTIMATOR)).getEstimator(); + // compute cost-optimal chains for increasing chain sizes + SparsityEstimator estim = EstimatorType + .valueOf(ConfigurationManager.getDMLConfig().getTextValue(DMLConfig.SPARSITY_ESTIMATOR)).getEstimator(); for(int l = 2; l <= size; l++) { // chain length for(int i = 0; i < size - l + 1; i++) { int j = i + l - 1; @@ -238,7 +234,7 @@ private static int[][] mmChainDPSparse(double[] dimArray, MMNode[] sketchArray, dpMatrix[i][j] = Double.MAX_VALUE; for(int k = i; k <= j - 1; k++) { // construct estimation nodes (w/ lazy propagation and memoization) - MMNode tmp = new MMNode(dpMatrixS[i][k], dpMatrixS[k+1][j], OpCode.MM); + MMNode tmp = new MMNode(dpMatrixS[i][k], dpMatrixS[k + 1][j], OpCode.MM); estim.estim(tmp); // recursive cost computation @@ -248,7 +244,7 @@ private static int[][] mmChainDPSparse(double[] dimArray, MMNode[] sketchArray, tmp.getLeft().getRows() * tmp.getLeft().getCols() * tmp.getRight().getCols(); // prune suboptimal - if( cost < dpMatrix[i][j] ) { + if(cost < dpMatrix[i][j]) { dpMatrix[i][j] = cost; dpMatrixS[i][j] = tmp; split[i][j] = k; @@ -289,9 +285,9 @@ private static int inputCount(Hop p, Hop h) { private static void logTraceHop(Hop hop, int level) { if(LOG.isTraceEnabled()) { String offset = Explain.getIdentation(level); - LOG.trace(offset + "Hop " + hop.getName() + "(" + hop.getClass().getSimpleName() + - ", " + hop.getHopID() + ")" + " " + hop.getDim1() + "x" + hop.getDim2() + - "[nnz" + hop.getNnz() + "]"); + LOG.trace(offset + "Hop " + hop.getName() + "(" + hop.getClass().getSimpleName() + + ", " + hop.getHopID() + ")" + " " + hop.getDim1() + "x" + hop.getDim2() + + "[nnz" + hop.getNnz() + "]"); } } @@ -307,9 +303,8 @@ private static void logTraceHop(Hop hop, int level) { */ private void prepAndOptimizeMMChain(Hop hop) { if(LOG.isTraceEnabled()) { - LOG.trace("Sparsity-based MM Chain Recompilation for HOP: (" + - hop.getClass().getSimpleName() + ", " + hop.getHopID() + - ", " + hop.getName() + ")"); + LOG.trace("Sparsity-based MM Chain Recompilation for HOP: (" + hop.getClass().getSimpleName() + ", " + + hop.getHopID() + ", " + hop.getName() + ")"); } List mmChain = new ArrayList<>(); @@ -340,12 +335,10 @@ private void prepAndOptimizeMMChain(Hop hop) { * (either within chain or outside the chain) */ - if(HopRewriteUtils.isMatrixMultiply(h) && - !((AggBinaryOp)h).hasLeftPMInput() && !h.isVisited()) { + if(HopRewriteUtils.isMatrixMultiply(h) && !((AggBinaryOp) h).hasLeftPMInput() && !h.isVisited()) { // check if the output of "h" is used at multiple places. If yes, it can // not be expanded. - expandable = !(h.getParent().size() > 1 || - inputCount(h.getParent().get(0), h) > 1); + expandable = !(h.getParent().size() > 1 || inputCount(h.getParent().get(0), h) > 1); if(!expandable) { optimizeHopDAG(h); break; @@ -359,8 +352,8 @@ private void prepAndOptimizeMMChain(Hop hop) { h.setVisited(); tempList = mmChain.get(i).getInput(); if(tempList.size() != 2) { - throw new HopsException(hop.printErrorLocation() + - "Hops::rule_OptimizeMMChain(): AggBinary must have exactly two inputs."); + throw new HopsException(hop.printErrorLocation() + + "Hops::rule_OptimizeMMChain(): AggBinary must have exactly two inputs."); } // add current operator to mmOperators, and its input nodes to mmChain @@ -397,7 +390,7 @@ private void prepHop(Hop hop) { // TODO: Optimize the hops that are not matrix multiplications. Its inputs are already optimized. // (i.e., pre-fetching and pre-computations) - if(hop instanceof DataOp && ((DataOp)hop).isRead() && ((DataOp)hop).getNnz() < 0) { + if(hop instanceof DataOp && ((DataOp) hop).isRead() && ((DataOp) hop).getNnz() < 0) { // TODO: pre-fetch } // TODO: pre-compute @@ -408,7 +401,7 @@ private void optimizeHopDAG(Hop hop) { return; // TODO: Also rewrite chains with additional operations (e.g,. cbind, etc.) - if(HopRewriteUtils.isMatrixMultiply(hop) && !((AggBinaryOp)hop).hasLeftPMInput()) { + if(HopRewriteUtils.isMatrixMultiply(hop) && !((AggBinaryOp) hop).hasLeftPMInput()) { // Try to find and optimize the chain in which current Hop is the // last operator prepAndOptimizeMMChain(hop); diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimization.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimization.java index 7899e6d65f0..1337a0fcd7c 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimization.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimization.java @@ -78,7 +78,7 @@ private void ruleOptimizeMMChains(Hop hop, ProgramRewriteStatus state) if(hop.isVisited()) return; - if( HopRewriteUtils.isMatrixMultiply(hop) && !((AggBinaryOp)hop).hasLeftPMInput()) { + if(HopRewriteUtils.isMatrixMultiply(hop) && !((AggBinaryOp) hop).hasLeftPMInput()) { // Try to find and optimize the chain in which current Hop is the // last operator prepAndOptimizeMMChain(hop, state); diff --git a/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java b/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java index b7ada9f20d2..80e7ccbb3de 100644 --- a/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java +++ b/src/main/java/org/apache/sysds/hops/rewrite/RewriteMatrixMultChainOptimizationSparse.java @@ -66,8 +66,8 @@ protected void optimizeMMChain(Hop hop, List mmChain, List mmOperators mmChainRelinkHops(mmOperators.get(0), 0, size - 1, mmChain, mmOperators, new MutableInt(1), split, 1); } else if(dimsKnown && ConfigurationManager.getDMLConfig().getBooleanValue(DMLConfig.SPARSITY_RECOMPILE)) { - LOG.debug("Input metadata is not available for sparsity rewrites. " + - "Enabling dynamic recompilation to obtain metadata during sparsity-based recompilation."); + LOG.debug("Input metadata is not available for sparsity rewrites. " + + "Enabling dynamic recompilation to obtain metadata during sparsity-based recompilation."); hop.setRequiresRecompile(); } }