From 14a27b5582412ed74ea59e4570336d28bb66bf2a Mon Sep 17 00:00:00 2001 From: nofa Date: Mon, 5 Jan 2026 18:12:28 +0500 Subject: [PATCH 1/2] regression matrix changes --- .../algorithms/BuildRegressionMatrix.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/com/jdoe/algorithms/BuildRegressionMatrix.java diff --git a/src/main/java/com/jdoe/algorithms/BuildRegressionMatrix.java b/src/main/java/com/jdoe/algorithms/BuildRegressionMatrix.java new file mode 100644 index 0000000..03eae1c --- /dev/null +++ b/src/main/java/com/jdoe/algorithms/BuildRegressionMatrix.java @@ -0,0 +1,20 @@ +package com.jdoe.algorithms; + +import java.util.ArrayList; +import java.util.List; + +//Build a regression matrix using a DOE matrix and a list of monomials. +public class BuildRegressionMatrix { + + public static List< Integer > grep ( String pattern, String data ) { + String[] dataArray = data.split( "" ); + List< Integer > occouranceIndexes = new ArrayList<>(); + for ( int i = 0; dataArray.length > i; i++ ) { + if ( data.substring( i, i + pattern.length() ).equalsIgnoreCase( pattern ) ) { + occouranceIndexes.add( i ); + } + } + return occouranceIndexes; + } + +} From 774aa8a83b858c0158fdf572ab585b0e796d5bdc Mon Sep 17 00:00:00 2001 From: nofa Date: Thu, 8 Jan 2026 16:09:36 +0500 Subject: [PATCH 2/2] regression matrix implementation --- .../algorithms/BuildRegressionMatrix.java | 210 +++++++++++++++++- .../util/BuildRegressionMatrixUtility.java | 197 ++++++++++++++++ 2 files changed, 398 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/jdoe/util/BuildRegressionMatrixUtility.java diff --git a/src/main/java/com/jdoe/algorithms/BuildRegressionMatrix.java b/src/main/java/com/jdoe/algorithms/BuildRegressionMatrix.java index 03eae1c..393b7d7 100644 --- a/src/main/java/com/jdoe/algorithms/BuildRegressionMatrix.java +++ b/src/main/java/com/jdoe/algorithms/BuildRegressionMatrix.java @@ -1,20 +1,212 @@ package com.jdoe.algorithms; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -//Build a regression matrix using a DOE matrix and a list of monomials. +import com.jdoe.util.BuildRegressionMatrixUtility; + public class BuildRegressionMatrix { - public static List< Integer > grep ( String pattern, String data ) { - String[] dataArray = data.split( "" ); - List< Integer > occouranceIndexes = new ArrayList<>(); - for ( int i = 0; dataArray.length > i; i++ ) { - if ( data.substring( i, i + pattern.length() ).equalsIgnoreCase( pattern ) ) { - occouranceIndexes.add( i ); + /** + * Builds a regression matrix from an experimental design matrix and a mathematical model string. + * + *

+ * This method constructs a regression matrix by evaluating mathematical expressions defined in + * the model string using values from the experimental design matrix. Each token in the model + * string represents a mathematical expression involving variables (x00, x01, etc.) that correspond + * to columns in the experimental design matrix. + *

+ * + *

+ * The method supports two modes of operation: + *

+ *

+ * + *

+ * The model string should contain space-separated mathematical expressions. Variables are + * represented as x followed by zero-padded indices (e.g., x00, x01, x02...). The method + * supports standard mathematical operations: addition (+), subtraction (-), multiplication (*), + * division (/), and exponentiation (^), as well as parentheses for grouping. + *

+ * + *

+ * Example usage: + *

+     * // Matrix mode example
+     * double[][] design = {{1.0, 2.0}, {3.0, 4.0}};
+     * String model = "x00 x01 x00^2 x01^2 x00*x01";
+     * double[][] regressionMatrix = BuildRegressionMatrix.buildRegressionMatrix(design, model, null);
+     * // Result: 2x5 matrix with evaluated expressions
+     *
+     * // Vector mode example
+     * double[][] design = {{1.0}, {2.0}, {3.0}};
+     * String model = "x00 x00^2 x00^3";
+     * double[][] regressionMatrix = BuildRegressionMatrix.buildRegressionMatrix(design, model, null);
+     * // Result: 3x1 matrix with evaluated expressions
+     * 
+ *

+ * + *

+ * If the experimental design matrix has a single row but multiple columns, it will be + * automatically transposed to column format before processing. The buildFlags parameter + * allows selective evaluation of specific terms in the model string. + *

+ * + *

+ * Supported mathematical operations: + *

+ *

+ * + * @param experimentalDesignMatrix A 2D array containing experimental design data. + * In matrix mode, each row represents an experimental run and each column represents + * a factor. In vector mode, each row represents a variable value. + * @param modelString A space-separated string containing mathematical expressions to evaluate. + * Each token represents an expression involving variables (x00, x01, etc.) and mathematical + * operations. + * @param buildFlags A boolean array indicating which terms in the model string to evaluate. + * If null, all terms will be evaluated. If provided, only terms with true values will + * be processed, though the resulting matrix will still have columns for all terms + * (with potentially unused columns). + * @return A 2D array representing the regression matrix where each element is the result + * of evaluating the corresponding mathematical expression using values from the + * experimental design matrix. The dimensions depend on the mode: + * - Matrix mode: [n_rows × n_terms] where n_rows is rows in design matrix + * - Vector mode: [n_terms × 1] where n_terms is number of valid terms in model + * @throws IllegalArgumentException if the mathematical expressions are invalid or contain + * syntax errors, or if division by zero occurs during evaluation + * @throws ArithmeticException if division by zero occurs during evaluation + * @see BuildRegressionMatrixUtility#grep(String[], String) + * @see FactorialDOE#fullFactorial(Integer[]) + * @see BoxBehnkenDOE#boxBehnkenDesign(int) + */ + public static double[][] buildRegressionMatrix(double[][] experimentalDesignMatrix, + String modelString, + boolean[] buildFlags) { + + // Spliting the model string into individual tokens + String[] listOfTokens = modelString.split(" "); + + // Determine the size index based on the matrix dimensions + int sizeIndex; + if (experimentalDesignMatrix[0].length == 1) { + // For vector mode (single column) + sizeIndex = String.valueOf(experimentalDesignMatrix.length - 1).length(); + } else { + // For matrix mode + sizeIndex = String.valueOf(experimentalDesignMatrix[0].length - 1).length(); + } + + // If buildFlags is null, create a default array with all true values + if (buildFlags == null) { + buildFlags = new boolean[listOfTokens.length]; + Arrays.fill(buildFlags, true); + } + + // Test if the matrix has the wrong orientation (single row instead of columns) + if (experimentalDesignMatrix.length == 1 && experimentalDesignMatrix[0].length > 1) { + // Transpose the matrix (single row to single column) + double[][] transposedMatrix = new double[experimentalDesignMatrix[0].length][1]; + for (int i = 0; i < experimentalDesignMatrix[0].length; i++) { + transposedMatrix[i][0] = experimentalDesignMatrix[0][i]; } + experimentalDesignMatrix = transposedMatrix; } - return occouranceIndexes; + + // Filter tokens based on buildFlags + List filteredTokens = new ArrayList<>(); + for (int i = 0; i < listOfTokens.length; i++) { + if (buildFlags[i]) { + filteredTokens.add(listOfTokens[i]); + } + } + + // Determine mode and number of variables + boolean isVectorMode = (experimentalDesignMatrix[0].length == 1); + int numberOfVariables; + + if (isVectorMode) { + numberOfVariables = experimentalDesignMatrix.length; + } else { + numberOfVariables = experimentalDesignMatrix[0].length; + } + + // Create variable replacement patterns + String[][] variableReplacements = new String[numberOfVariables][2]; + for (int i = 0; i < numberOfVariables; i++) { + String paddedIndex = String.format("%0" + sizeIndex + "d", i); + variableReplacements[i][0] = "x" + paddedIndex; + if (isVectorMode) { + variableReplacements[i][1] = "H[" + i + "]"; + } else { + variableReplacements[i][1] = "H[row][" + i + "]"; + } + } + + // Applying variable replacements to all filtered tokens + String[] processedTokens = new String[filteredTokens.size()]; + for (int tokenIndex = 0; tokenIndex < filteredTokens.size(); tokenIndex++) { + String token = filteredTokens.get(tokenIndex); + for (int varIndex = 0; varIndex < numberOfVariables; varIndex++) { + token = token.replace(variableReplacements[varIndex][0], + variableReplacements[varIndex][1]); + } + processedTokens[tokenIndex] = token; + } + + // Building the regression matrix + double[][] regressionMatrix; + + if (isVectorMode) { + // Vector mode: single column output + regressionMatrix = new double[filteredTokens.size()][1]; + + for (int tokenIndex = 0; tokenIndex < filteredTokens.size(); tokenIndex++) { + String expression = processedTokens[tokenIndex]; + // Replace H[i] with actual values + for (int i = 0; i < numberOfVariables; i++) { + String placeholder = "H[" + i + "]"; + String value = String.valueOf(experimentalDesignMatrix[i][0]); + expression = expression.replace(placeholder, value); + } + regressionMatrix[tokenIndex][0] = BuildRegressionMatrixUtility.evaluateMathExpression(expression); + } + } else { + // Matrix mode: one row per design point + int numRows = experimentalDesignMatrix.length; + int numCols = filteredTokens.size(); + regressionMatrix = new double[numRows][numCols]; + + for (int row = 0; row < numRows; row++) { + for (int tokenIndex = 0; tokenIndex < filteredTokens.size(); tokenIndex++) { + String expression = processedTokens[tokenIndex]; + // Replacing H[row][i] with actual values + for (int i = 0; i < numberOfVariables; i++) { + String placeholder = "H[row][" + i + "]"; + String value = String.valueOf(experimentalDesignMatrix[row][i]); + expression = expression.replace(placeholder, value); + } + // Replacing 'row' placeholder + expression = expression.replace("row", String.valueOf(row)); + regressionMatrix[row][tokenIndex] = BuildRegressionMatrixUtility.evaluateMathExpression(expression); + } + } + } + + return regressionMatrix; } -} +} \ No newline at end of file diff --git a/src/main/java/com/jdoe/util/BuildRegressionMatrixUtility.java b/src/main/java/com/jdoe/util/BuildRegressionMatrixUtility.java new file mode 100644 index 0000000..45a7360 --- /dev/null +++ b/src/main/java/com/jdoe/util/BuildRegressionMatrixUtility.java @@ -0,0 +1,197 @@ +package com.jdoe.util; + +import java.util.ArrayList; +import java.util.List; + +public class BuildRegressionMatrixUtility { + + /** + * Grep function to find all occurrences of a pattern in an array of strings + */ + public static List grep( String[] dataArray, String pattern ) { + List occurrenceIndexes = new ArrayList<>(); + for (int i = 0; i < dataArray.length; i++) { + if (dataArray[i].contains(pattern)) { + occurrenceIndexes.add(i); + } + } + return occurrenceIndexes; + } + + /** + * Evaluate a mathematical expression string + */ + public static double evaluateMathExpression( String expression ) { + try { + // Remove whitespace + expression = expression.replaceAll("\\s+", ""); + + // Handle parentheses first + while (expression.contains("(")) { + int openParen = expression.lastIndexOf("("); + int closeParen = expression.indexOf(")", openParen); + + if (closeParen == -1) { + throw new IllegalArgumentException("Mismatched parentheses"); + } + + String innerExpr = expression.substring(openParen + 1, closeParen); + double innerResult = evaluateSimpleExpression(innerExpr); + + expression = expression.substring(0, openParen) + innerResult + + expression.substring(closeParen + 1); + } + + // Evaluate the final expression + return evaluateSimpleExpression(expression); + } catch (Exception exception) { + System.err.println("Error in math evaluation: " + expression); + throw new IllegalArgumentException("Invalid mathematical expression: " + expression, exception); + } + } + + /** + * Evaluate simple expression without parentheses + */ + public static double evaluateSimpleExpression( String expression ) { + // Handle exponentiation first (using ^) + String[] exponentParts = splitByOperator(expression, '^'); + if (exponentParts.length > 1) { + double result = evaluateSimpleExpression(exponentParts[0]); + for (int i = 1; i < exponentParts.length; i++) { + result = Math.pow(result, evaluateSimpleExpression(exponentParts[i])); + } + return result; + } + + // Handle multiplication and division + String[] mulDivParts = splitByOperators(expression, new char[]{'*', '/'}); + if (mulDivParts.length > 1) { + double result = evaluateSimpleExpression(mulDivParts[0]); + int operatorIndex = mulDivParts[0].length(); + + for (int i = 1; i < mulDivParts.length; i++) { + char operator = expression.charAt(operatorIndex); + double nextValue = evaluateSimpleExpression(mulDivParts[i]); + + if (operator == '*') { + result *= nextValue; + } else if (operator == '/') { + if (nextValue == 0) { + throw new ArithmeticException("Division by zero"); + } + result /= nextValue; + } + + operatorIndex += mulDivParts[i].length() + 1; + } + return result; + } + + // Handle addition and subtraction + String[] addSubParts = splitByOperators(expression, new char[]{'+', '-'}); + if (addSubParts.length > 1) { + double result = evaluateSimpleExpression(addSubParts[0]); + int operatorIndex = addSubParts[0].length(); + + // Check if first part might be negative + if (expression.charAt(0) == '-') { + result = -result; + operatorIndex = 1; + } + + for (int i = 1; i < addSubParts.length; i++) { + char operator = expression.charAt(operatorIndex); + double nextValue = evaluateSimpleExpression(addSubParts[i]); + + if (operator == '+') { + result += nextValue; + } else if (operator == '-') { + result -= nextValue; + } + + operatorIndex += addSubParts[i].length() + 1; + } + return result; + } + + // If no operators, parse as a number + try { + return Double.parseDouble(expression); + } catch (NumberFormatException exception) { + // Check if it's a numeric constant like "1" or "2" + if (expression.matches("-?\\d+(\\.\\d+)?")) { + return Double.parseDouble(expression); + } + throw new IllegalArgumentException("Invalid number: " + expression); + } + } + + /** + * Split expression by a single operator + */ + public static String[] splitByOperator( String expression, char operator ) { + List parts = new ArrayList<>(); + StringBuilder currentPart = new StringBuilder(); + int parenthesisDepth = 0; + + for (int i = 0; i < expression.length(); i++) { + char currentChar = expression.charAt(i); + + if (currentChar == '(') { + parenthesisDepth++; + } else if (currentChar == ')') { + parenthesisDepth--; + } + + if (currentChar == operator && parenthesisDepth == 0) { + parts.add(currentPart.toString()); + currentPart = new StringBuilder(); + } else { + currentPart.append(currentChar); + } + } + + parts.add(currentPart.toString()); + return parts.toArray(new String[0]); + } + + /** + * Split expression by multiple operators + */ + public static String[] splitByOperators( String expression, char[] operators ) { + List parts = new ArrayList<>(); + StringBuilder currentPart = new StringBuilder(); + int parenthesisDepth = 0; + + for (int i = 0; i < expression.length(); i++) { + char currentChar = expression.charAt(i); + + if (currentChar == '(') { + parenthesisDepth++; + } else if (currentChar == ')') { + parenthesisDepth--; + } + + boolean isOperator = false; + for (char operator : operators) { + if (currentChar == operator && parenthesisDepth == 0) { + isOperator = true; + break; + } + } + + if (isOperator) { + parts.add(currentPart.toString()); + currentPart = new StringBuilder(); + // Don't append the operator + } else { + currentPart.append(currentChar); + } + } + + parts.add(currentPart.toString()); + return parts.toArray(new String[0]); + } + +}