From 9b8a55dda6f949c6c1cd53b7b55618f8dc770494 Mon Sep 17 00:00:00 2001 From: nomus Date: Mon, 29 Dec 2025 02:34:54 +0500 Subject: [PATCH 1/2] boxBehnken changes --- .../com/jdoe/algorithms/BoxBehnkenDOE.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/main/java/com/jdoe/algorithms/BoxBehnkenDOE.java diff --git a/src/main/java/com/jdoe/algorithms/BoxBehnkenDOE.java b/src/main/java/com/jdoe/algorithms/BoxBehnkenDOE.java new file mode 100644 index 0000000..91b32ea --- /dev/null +++ b/src/main/java/com/jdoe/algorithms/BoxBehnkenDOE.java @@ -0,0 +1,33 @@ +package com.jdoe.algorithms; + +import org.apache.commons.math3.linear.Array2DRowRealMatrix; +import org.apache.commons.math3.linear.RealMatrix; + +public class BoxBehnkenDOE { + + public static void boxBehnkenDesign(int totalNumberOfFactors) { + + // validation + if (totalNumberOfFactors < 3) { + throw new IllegalArgumentException("design requires atleast 3 factors"); + } + + // making a 2 level design base for the bbdesign + RealMatrix ff2levelMatrix = FactorialDOE.fullFactorial2Level(2); + + // result matrix dimension calculation + int numberOfRows = (int) (0.5 * totalNumberOfFactors * (totalNumberOfFactors - 1) * ff2levelMatrix.getRowDimension()); + + Array2DRowRealMatrix realMatrixFactory = new Array2DRowRealMatrix(); + RealMatrix resultMatrix = realMatrixFactory.createMatrix(numberOfRows, totalNumberOfFactors); + + // populate result matrix with base matrix + for (int c = 0; c < ff2levelMatrix.getColumnDimension(); c++) { + for (int r = 0; r < ff2levelMatrix.getRowDimension(); r++) { + double entry = ff2levelMatrix.getEntry(r, c); + resultMatrix.setEntry(r, c, entry); + } + } + + } +} From 400194eb79263ba0e837d65174bc0868675f6222 Mon Sep 17 00:00:00 2001 From: nofa Date: Mon, 29 Dec 2025 16:32:15 +0500 Subject: [PATCH 2/2] boxBehnkenDOE changes --- src/main/java/com/jdoe/DesignFactory.java | 11 -- src/main/java/com/jdoe/Testing.java | 4 +- .../com/jdoe/algorithms/BoxBehnkenDOE.java | 117 ++++++++++++++++-- .../jdoe/algorithms/BoxBehnkenDOETest.java | 90 ++++++++++++++ 4 files changed, 199 insertions(+), 23 deletions(-) delete mode 100644 src/main/java/com/jdoe/DesignFactory.java create mode 100644 src/test/java/com/jdoe/algorithms/BoxBehnkenDOETest.java diff --git a/src/main/java/com/jdoe/DesignFactory.java b/src/main/java/com/jdoe/DesignFactory.java deleted file mode 100644 index 6782fc4..0000000 --- a/src/main/java/com/jdoe/DesignFactory.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.jdoe; - -public class DesignFactory { - - public static void fullFactorialFactory() { - /* - will return an instance of the fullfactorial algo object to make its methods accessible - methods can be static but that is for topic for later - */ - } -} diff --git a/src/main/java/com/jdoe/Testing.java b/src/main/java/com/jdoe/Testing.java index bba5a4a..75d54d7 100644 --- a/src/main/java/com/jdoe/Testing.java +++ b/src/main/java/com/jdoe/Testing.java @@ -1,5 +1,6 @@ package com.jdoe; +import com.jdoe.algorithms.BoxBehnkenDOE; import com.jdoe.algorithms.FactorialDOE; /** @@ -16,6 +17,7 @@ public static void main( String[] args ) // FactorialDOE.fullFactorial2Level( arrayOfLevels.length ); // FactorialDOE.fractionalFactorial( "a b -ab" ); // FactorialDOE.fractionalFactorial( "a b +ab" ); - FactorialDOE.fractionalFactorialByResolution( 6,4 ); +// FactorialDOE.fractionalFactorialByResolution( 6,4 ); + // BoxBehnkenDOE.boxBehnkenDesign( 4 ); } } diff --git a/src/main/java/com/jdoe/algorithms/BoxBehnkenDOE.java b/src/main/java/com/jdoe/algorithms/BoxBehnkenDOE.java index 91b32ea..1a2cf2d 100644 --- a/src/main/java/com/jdoe/algorithms/BoxBehnkenDOE.java +++ b/src/main/java/com/jdoe/algorithms/BoxBehnkenDOE.java @@ -5,29 +5,124 @@ public class BoxBehnkenDOE { - public static void boxBehnkenDesign(int totalNumberOfFactors) { + /** + * Generates a Box–Behnken design matrix for response surface methodology (RSM). + * + *

+ * A Box–Behnken design is a three-level experimental design where factor levels take values {@code -1}, {@code 0}, and {@code +1}. In + * each experimental run, exactly two factors vary at {@code ±1} while all remaining factors are held at the center level {@code 0}. + * This structure allows efficient estimation of quadratic response surfaces while avoiding extreme corner points of the experimental + * space. + *

+ * + *

+ * Design construction logic: + *

    + *
  1. Generate a full 2-level factorial design for two factors ({@code ±1}).
  2. + *
  3. Iterate over all unique pairs of factors {@code (i, j)}.
  4. + *
  5. For each factor pair, embed the 2-level factorial values into columns + * {@code i} and {@code j}, while setting all other factor columns to + * the center level {@code 0}.
  6. + *
  7. Concatenate all such pairwise designs into a single matrix.
  8. + *
  9. Append center-point runs (all factors set to {@code 0}) to improve + * model stability and allow pure-error estimation.
  10. + *
+ *

+ * + *

+ * Matrix dimensions: + *

+ *

+ * + *

+ * Example (4 factors): + *

+     * {-1, -1,  0,  0}
+     * { 1, -1,  0,  0}
+     * {-1,  1,  0,  0}
+     * { 1,  1,  0,  0}
+     * {-1,  0, -1,  0}
+     * ...
+     * { 0,  0,  0,  0}  // center points
+     * 
+ *

+ * + *

+ * Notes: + *

+ *

+ * + * @param totalNumberOfFactors + * the number of design factors (must be ≥ 3) + * + * @return a {@link RealMatrix} representing the Box–Behnken design + * + * @throws IllegalArgumentException + * if {@code totalNumberOfFactors < 3} + * @see FactorialDOE#fullFactorial2Level(int) + */ + + public static RealMatrix boxBehnkenDesign( int totalNumberOfFactors ) { // validation - if (totalNumberOfFactors < 3) { - throw new IllegalArgumentException("design requires atleast 3 factors"); + if ( totalNumberOfFactors < 3 ) { + throw new IllegalArgumentException( "design requires atleast 3 factors" ); } // making a 2 level design base for the bbdesign - RealMatrix ff2levelMatrix = FactorialDOE.fullFactorial2Level(2); + RealMatrix ff2levelMatrix = FactorialDOE.fullFactorial2Level( 2 ); // result matrix dimension calculation - int numberOfRows = (int) (0.5 * totalNumberOfFactors * (totalNumberOfFactors - 1) * ff2levelMatrix.getRowDimension()); + int numberOfRows = ( int ) ( 0.5 * totalNumberOfFactors * ( totalNumberOfFactors - 1 ) * ff2levelMatrix.getRowDimension() ); - Array2DRowRealMatrix realMatrixFactory = new Array2DRowRealMatrix(); - RealMatrix resultMatrix = realMatrixFactory.createMatrix(numberOfRows, totalNumberOfFactors); + RealMatrix resultMatrix = new Array2DRowRealMatrix( numberOfRows, totalNumberOfFactors ); // populate result matrix with base matrix - for (int c = 0; c < ff2levelMatrix.getColumnDimension(); c++) { - for (int r = 0; r < ff2levelMatrix.getRowDimension(); r++) { - double entry = ff2levelMatrix.getEntry(r, c); - resultMatrix.setEntry(r, c, entry); + int row = 0; + for ( int i = 0; i < totalNumberOfFactors - 1; i++ ) { + for ( int j = i + 1; j < totalNumberOfFactors; j++ ) { + for ( int r = 0; r < ff2levelMatrix.getRowDimension(); r++ ) { + // setting all columns to 0 first + for ( int c = 0; c < totalNumberOfFactors; c++ ) { + resultMatrix.setEntry( row, c, 0.0 ); + } + // assign ±1 to the factor pair + resultMatrix.setEntry( row, i, ff2levelMatrix.getEntry( r, 0 ) ); + resultMatrix.setEntry( row, j, ff2levelMatrix.getEntry( r, 1 ) ); + row++; + } } } + int center; + // center points + if ( totalNumberOfFactors >= 16 ) { + int[] predefinedPointsArr = new int[]{ 0, 0, 0, 3, 3, 6, 6, 6, 8, 9, 10, 12, 12, 13, 14, 15, 16 }; + center = predefinedPointsArr[ totalNumberOfFactors ]; + } else { + center = totalNumberOfFactors; + } + RealMatrix centerPointRepeatMatrix = new Array2DRowRealMatrix( center, totalNumberOfFactors ); + + // creating matrix and appending centerPointRepeatMatrix at the end + int rows = resultMatrix.getRowDimension() + center; + RealMatrix finalMatrix = new Array2DRowRealMatrix( rows, totalNumberOfFactors ); + + finalMatrix.setSubMatrix( resultMatrix.getData(), 0, 0 ); + finalMatrix.setSubMatrix( centerPointRepeatMatrix.getData(), resultMatrix.getRowDimension(), 0 ); + return finalMatrix; } + } diff --git a/src/test/java/com/jdoe/algorithms/BoxBehnkenDOETest.java b/src/test/java/com/jdoe/algorithms/BoxBehnkenDOETest.java new file mode 100644 index 0000000..c78f241 --- /dev/null +++ b/src/test/java/com/jdoe/algorithms/BoxBehnkenDOETest.java @@ -0,0 +1,90 @@ +package com.jdoe.algorithms; + +import static org.junit.Assert.*; +import org.apache.commons.math3.linear.RealMatrix; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Unit tests for {@link BoxBehnkenDOE}. + */ +public class BoxBehnkenDOETest { + + @Test(expected = IllegalArgumentException.class) + public void testBoxBehnkenDesign_LessThanThreeFactors_ThrowsException() { + BoxBehnkenDOE.boxBehnkenDesign(2); + } + + @Test + public void testBoxBehnkenDesign_FourFactors_DimensionsCorrect() { + int factors = 4; + + RealMatrix design = BoxBehnkenDOE.boxBehnkenDesign(factors); + + // For Box-Behnken: + // rows = 0.5 * k * (k - 1) * 2^2 + center + // = 0.5 * 4 * 3 * 4 + 4 = 24 + 4 = 28 + assertNotNull(design); + assertEquals(28, design.getRowDimension()); + assertEquals(4, design.getColumnDimension()); + } + + @Test + public void testBoxBehnkenDesign_FourFactors_OnlyTwoNonZeroPerRow() { + int factors = 4; + RealMatrix design = BoxBehnkenDOE.boxBehnkenDesign(factors); + + int nonCenterRows = 24; // last 4 are center points + + for (int r = 0; r < nonCenterRows; r++) { + int nonZeroCount = 0; + for (int c = 0; c < factors; c++) { + double v = design.getEntry(r, c); + if (v != 0.0) { + nonZeroCount++; + assertTrue(v == -1.0 || v == 1.0); + } + } + assertEquals(2, nonZeroCount); + } + } + + @Test + public void testBoxBehnkenDesign_FourFactors_CenterPointsAreZero() { + int factors = 4; + RealMatrix design = BoxBehnkenDOE.boxBehnkenDesign(factors); + + int rows = design.getRowDimension(); + int center = factors; + + for (int r = rows - center; r < rows; r++) { + for (int c = 0; c < factors; c++) { + assertEquals(0.0, design.getEntry(r, c), 0.0); + } + } + } + + @Test + public void testBoxBehnkenDesign_LevelsRestrictedToMinusOneZeroPlusOne() { + RealMatrix design = BoxBehnkenDOE.boxBehnkenDesign(5); + + for (int r = 0; r < design.getRowDimension(); r++) { + for (int c = 0; c < design.getColumnDimension(); c++) { + double v = design.getEntry(r, c); + assertTrue(v == -1.0 || v == 0.0 || v == 1.0); + } + } + } + + @Test + public void testBoxBehnkenDesign_RowCountFormulaHolds() { + int factors = 6; + RealMatrix design = BoxBehnkenDOE.boxBehnkenDesign(factors); + + int expectedRows = + (int) (0.5 * factors * (factors - 1) * 4) + factors; + + assertEquals(expectedRows, design.getRowDimension()); + } +}