Skip to content

Commit 7611caf

Browse files
author
nofa
committed
fractional factorial by resolution value
1 parent 9b26a7a commit 7611caf

5 files changed

Lines changed: 459 additions & 57 deletions

File tree

pom.xml

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,6 @@
1717
</properties>
1818

1919
<dependencies>
20-
<dependency>
21-
<groupId>junit</groupId>
22-
<artifactId>junit</artifactId>
23-
<version>3.8.1</version>
24-
<scope>test</scope>
25-
</dependency>
2620
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-math3 -->
2721
<dependency>
2822
<groupId>org.apache.commons</groupId>
@@ -39,6 +33,28 @@
3933
<artifactId>log4j-api</artifactId>
4034
<version>2.20.0</version>
4135
</dependency>
36+
<!-- TESTING DEPENDENCIES -->
37+
<!-- https://mvnrepository.com/artifact/junit/junit -->
38+
<dependency>
39+
<groupId>junit</groupId>
40+
<artifactId>junit</artifactId>
41+
<version>4.13.2</version>
42+
<scope>test</scope>
43+
</dependency>
44+
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
45+
<dependency>
46+
<groupId>org.mockito</groupId>
47+
<artifactId>mockito-core</artifactId>
48+
<version>5.20.0</version>
49+
<scope>test</scope>
50+
</dependency>
51+
<dependency>
52+
<groupId>org.mockito</groupId>
53+
<artifactId>mockito-inline</artifactId>
54+
<version>5.2.0</version>
55+
<scope>test</scope>
56+
</dependency>
57+
4258
</dependencies>
4359

4460
<build>

src/main/java/com/jdoe/Testing.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ public class Testing
1111
//! for testing purposes only does not require a main entry point
1212
public static void main( String[] args )
1313
{
14-
int[] arrayOfLevels = {2,3,6};
14+
// int[] arrayOfLevels = {2,3,6};
1515
// FactorialDOE.fullFactorial(arrayOfLevels);
1616
// FactorialDOE.fullFactorial2Level( arrayOfLevels.length );
17-
FactorialDOE.fractionalFactorial( "a b -ab" );
18-
FactorialDOE.fractionalFactorial( "a b +ab" );
17+
// FactorialDOE.fractionalFactorial( "a b -ab" );
18+
// FactorialDOE.fractionalFactorial( "a b +ab" );
19+
FactorialDOE.fractionalFactorialByResolution( 6,4 );
1920
}
2021
}

src/main/java/com/jdoe/algorithms/FactorialDOE.java

Lines changed: 202 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
import java.util.ArrayList;
44
import java.util.Arrays;
55
import java.util.List;
6+
import java.util.stream.Collectors;
7+
import java.util.stream.IntStream;
8+
import java.util.stream.Stream;
69

710
import org.apache.commons.math3.linear.Array2DRowRealMatrix;
811
import org.apache.commons.math3.linear.RealMatrix;
@@ -67,7 +70,7 @@ public class FactorialDOE {
6770
* @param designFactorLevelArray
6871
* an array where each element specifies the number of levels for a design factor
6972
*/
70-
public static RealMatrix fullFactorial( int[] designFactorLevelArray ) {
73+
public static RealMatrix fullFactorial( Integer[] designFactorLevelArray ) {
7174
// calculate matrix size by number of combinations
7275
int length = designFactorLevelArray.length;
7376
int combinationCount = 1;
@@ -150,10 +153,10 @@ public static RealMatrix fullFactorial2Level( int designFactorCount ) {
150153
* Generates a fractional factorial design matrix from a generator string.
151154
*
152155
* <p>
153-
* This method creates a design matrix for a fractional factorial experiment based on a user-provided generator string.
154-
* The generator string defines main factors and combination factors, optionally with a leading '+' or '-' sign for
155-
* combination factors. Each main factor is treated as a 2-level factor (-1 and +1). Combination factors are computed
156-
* as the row-wise product of the relevant main factor columns, with signs applied if specified.
156+
* This method creates a design matrix for a fractional factorial experiment based on a user-provided generator string. The generator
157+
* string defines main factors and combination factors, optionally with a leading '+' or '-' sign for combination factors. Each main
158+
* factor is treated as a 2-level factor (-1 and +1). Combination factors are computed as the row-wise product of the relevant main
159+
* factor columns, with signs applied if specified.
157160
* </p>
158161
*
159162
* <p>
@@ -197,12 +200,11 @@ public static RealMatrix fullFactorial2Level( int designFactorCount ) {
197200
* </p>
198201
*
199202
* @param generetor
200-
* A generator string defining main factors and combination factors. Main factors are single letters,
201-
* combination factors are multiple letters possibly prefixed with '+' or '-' to indicate sign.
203+
* A generator string defining main factors and combination factors. Main factors are single letters, combination factors are
204+
* multiple letters possibly prefixed with '+' or '-' to indicate sign.
202205
*
203206
* @throws IllegalArgumentException
204207
* If the generator string is invalid according to {@link FactorialUtility#validateGeneretor(String)}.
205-
*
206208
* @see FactorialUtility#validateGeneretor(String)
207209
*/
208210
public static RealMatrix fractionalFactorial( String generetor ) {
@@ -244,7 +246,7 @@ public static RealMatrix fractionalFactorial( String generetor ) {
244246
List< String > factorCombinationSplit = Arrays.asList( factorList.get( combinationDesignFactorIndexValue ).split( "" ) );
245247
if ( factorList.get( combinationDesignFactorIndexValue ).startsWith( "-" ) || factorList.get(
246248
combinationDesignFactorIndexValue ).startsWith( "+" ) ) {
247-
signOfCurrentCombinationDesignFactor = factorList.get( combinationDesignFactorIndexValue ).split( "" )[0];
249+
signOfCurrentCombinationDesignFactor = factorList.get( combinationDesignFactorIndexValue ).split( "" )[ 0 ];
248250
}
249251
List< Integer > matchingMainFactorFinalMatrixIndexList = new ArrayList<>();
250252
for ( int c = 0; factorCombinationSplit.size() > c; c++ ) {
@@ -271,9 +273,199 @@ public static RealMatrix fractionalFactorial( String generetor ) {
271273
}
272274
}
273275

274-
log.info("Fractional Factorial Result Matrix: " + finalDesignMatrix );
276+
log.info( "Fractional Factorial Result Matrix: " + finalDesignMatrix );
275277

276278
return finalDesignMatrix;
277279
}
278280

281+
// NOTE:
282+
// Combinations factors = which factors/levels are tested together
283+
// Resolution = how clearly we can separate their effects after testing
284+
// This method will generete teh optimal generetors for you based on the resolution value provided
285+
public static RealMatrix fractionalFactorialByResolution( int n, int res ) {
286+
/*
287+
STEPS:
288+
determine minimum base factors from the total number of factors provided
289+
determine total number combination factors using resolution -1
290+
generete generator string to produce 2 level fractional factorial
291+
*/
292+
// Validating resolution
293+
if ( res < 3 || res > 5 ) {
294+
throw new IllegalArgumentException( "resolution number should be in range of 3 to 5" );
295+
}
296+
297+
// 1. Find minimum base factors needed
298+
Integer minFac = null;
299+
300+
for (int k = res - 1; k < n; k++) {
301+
if (nFacAtRes(k, res) >= n) {
302+
minFac = k;
303+
break;
304+
}
305+
}
306+
307+
if (minFac == null) {
308+
throw new IllegalArgumentException("design not possible");
309+
}
310+
311+
// 2. Check if we have enough letters (theoretical, but included for completeness)
312+
if (minFac > 26) { // Only 26 letters in alphabet
313+
throw new IllegalArgumentException("design requires too many base-factors.");
314+
}
315+
316+
// 3. Get base factors as letters
317+
List<String> factors = new ArrayList<>();
318+
for (int i = 0; i < minFac; i++) {
319+
factors.add(String.valueOf((char) ('a' + i)));
320+
}
321+
322+
// 4. Generate combinations for extra factors
323+
List<String> extraFactors = new ArrayList<>();
324+
int needed = n - factors.size();
325+
326+
// Generate combinations from length (res-1) up to length of factors
327+
for (int r = res - 1; r <= factors.size() && needed > 0; r++) {
328+
// Generate all combinations of size r from factors
329+
List<List<String>> combos = generateCombinations(factors, r);
330+
331+
for (List<String> combo : combos) {
332+
if (needed <= 0) break;
333+
334+
// Join combination (e.g., ["a", "b"] -> "ab")
335+
String comboStr = String.join("", combo);
336+
extraFactors.add(comboStr);
337+
needed--;
338+
}
339+
}
340+
341+
// 5. Combine all factors
342+
List<String> allFactors = new ArrayList<>(factors);
343+
allFactors.addAll(extraFactors);
344+
345+
// 6. Create generator string
346+
String generator = String.join(" ", allFactors);
347+
return fractionalFactorial( generator );
348+
349+
}
350+
// Helper method to calculate number of factors at given resolution
351+
private static int nFacAtRes(int k, int res) {
352+
// This calculates total factors possible with k base factors at resolution res
353+
// For k base factors, total possible factors = k + C(k, res-1) + C(k, res) + ...
354+
int total = k; // Base factors
355+
356+
// Add combinations starting from length (res-1) up to k
357+
for (int r = res - 1; r <= k; r++) {
358+
total += combinations(k, r);
359+
}
360+
361+
return total;
362+
}
363+
364+
private static int combinations(int n, int k) {
365+
if (k < 0 || k > n) return 0;
366+
if (k == 0 || k == n) return 1;
367+
368+
int result = 1;
369+
for (int i = 1; i <= k; i++) {
370+
result = result * (n - k + i) / i;
371+
}
372+
return result;
373+
}
374+
375+
376+
// Helper to generate all combinations of size k from a list
377+
private static List<List<String>> generateCombinations(List<String> items, int k) {
378+
List<List<String>> result = new ArrayList<>();
379+
if (k == 0) {
380+
result.add(new ArrayList<>());
381+
return result;
382+
}
383+
if (k > items.size()) {
384+
return result;
385+
}
386+
387+
// Using backtracking to generate combinations
388+
generateCombinationsHelper(items, k, 0, new ArrayList<>(), result);
389+
return result;
390+
}
391+
392+
private static void generateCombinationsHelper(List<String> items, int k, int start,
393+
List<String> current, List<List<String>> result) {
394+
if (current.size() == k) {
395+
result.add(new ArrayList<>(current));
396+
return;
397+
}
398+
399+
for (int i = start; i < items.size(); i++) {
400+
current.add(items.get(i));
401+
generateCombinationsHelper(items, k, i + 1, current, result);
402+
current.remove(current.size() - 1);
403+
}
404+
}
405+
406+
// Alternative: Using Java Streams for a more functional approach (Java 8+)
407+
public static String createGeneratorStream(int n, int res) {
408+
// Find minimum base factors
409+
int minFac = IntStream.range(res - 1, n)
410+
.filter(k -> nFacAtRes(k, res) >= n)
411+
.findFirst()
412+
.orElseThrow(() -> new IllegalArgumentException("design not possible"));
413+
414+
if (minFac > 26) {
415+
throw new IllegalArgumentException("design requires too many base-factors.");
416+
}
417+
418+
// Get base factors
419+
List<String> factors = IntStream.range(0, minFac)
420+
.mapToObj(i -> String.valueOf((char) ('a' + i)))
421+
.collect( Collectors.toList());
422+
423+
// Generate extra factors using streams
424+
List<String> extraFactors = new ArrayList<>();
425+
int needed = n - factors.size();
426+
427+
// Create stream of combination lengths
428+
Stream<Integer> lengths = IntStream.range(res - 1, factors.size() + 1)
429+
.boxed();
430+
431+
// Flat map to get all combinations
432+
List<String> allCombinations = lengths
433+
.flatMap(r -> generateCombinationsStream(factors, r).stream())
434+
.collect(Collectors.toList());
435+
436+
// Take only as many as needed
437+
extraFactors = allCombinations.stream()
438+
.limit(needed)
439+
.collect(Collectors.toList());
440+
441+
// Combine all factors
442+
List<String> allFactors = new ArrayList<>(factors);
443+
allFactors.addAll(extraFactors);
444+
445+
return String.join(" ", allFactors);
446+
}
447+
448+
// Generate combinations using streams
449+
private static List<String> generateCombinationsStream(List<String> items, int r) {
450+
if (r == 0) {
451+
return List.of("");
452+
}
453+
if (r == 1) {
454+
return new ArrayList<>(items);
455+
}
456+
457+
List<String> result = new ArrayList<>();
458+
for (int i = 0; i <= items.size() - r; i++) {
459+
String first = items.get(i);
460+
List<String> remaining = items.subList(i + 1, items.size());
461+
List<String> smallerCombos = generateCombinationsStream(remaining, r - 1);
462+
463+
for (String combo : smallerCombos) {
464+
result.add(first + combo);
465+
}
466+
}
467+
468+
return result;
469+
}
470+
279471
}

src/test/java/com/jdoe/TestingTest.java

Lines changed: 0 additions & 38 deletions
This file was deleted.

0 commit comments

Comments
 (0)