TransformConfig.java
package com.morphiqlabs.wavelet.config;
import com.morphiqlabs.wavelet.api.BoundaryMode;
import com.morphiqlabs.wavelet.exception.InvalidArgumentException;
import com.morphiqlabs.wavelet.exception.InvalidConfigurationException;
import com.morphiqlabs.wavelet.util.NullChecks;
/**
* Immutable configuration for wavelet transform operations.
*
* <p>This class provides fine-grained control over wavelet transform behavior,
* including performance optimizations, boundary handling, and decomposition limits.
* It uses the builder pattern for flexible, readable construction.</p>
*
* <p>Key configuration options:</p>
* <ul>
* <li><b>Boundary Mode</b>: How to handle signal boundaries during convolution</li>
* <li><b>Optimization Path</b>: Force scalar or vector operations, or auto-detect</li>
* <li><b>Decomposition Levels</b>: Maximum allowed levels for multi-level transforms</li>
* </ul>
*
* <p>Example usage:</p>
* <pre>{@code
* // Auto-detect optimal path (recommended)
* TransformConfig autoConfig = TransformConfig.defaultConfig();
*
* // Force scalar operations for debugging
* TransformConfig scalarConfig = TransformConfig.builder()
* .forceScalar(true)
* .boundaryMode(BoundaryMode.ZERO_PADDING)
* .build();
*
* // Force Vector API for maximum performance
* TransformConfig vectorConfig = TransformConfig.builder()
* .forceVector(true)
* .maxDecompositionLevels(5)
* .build();
*
* // Use with transform
* WaveletTransform transform = new WaveletTransform(
* Daubechies.DB4,
* BoundaryMode.PERIODIC,
* vectorConfig
* );
* }</pre>
*
* <p>Performance notes:</p>
* <ul>
* <li>Auto-detection usually provides optimal performance</li>
* <li>Force scalar for consistent behavior across platforms</li>
* <li>Force vector when you know your data size benefits from vectorization</li>
* </ul>
*/
public final class TransformConfig {
// Boundary handling mode for the transform.
private final BoundaryMode boundaryMode;
// Forces scalar engine if true, otherwise auto-detects.
private final boolean forceScalar;
// Forces use of Vector API operations when available.
private final boolean forceVector;
// Maximum allowed decomposition levels.
private final int maxDecompositionLevels;
/**
* Private constructor used by Builder.
*
* @param builder Builder instance with configuration values.
*/
private TransformConfig(Builder builder) {
this.boundaryMode = builder.boundaryMode;
this.forceScalar = builder.forceScalar;
this.forceVector = builder.forceVector;
this.maxDecompositionLevels = builder.maxDecompositionLevels;
// Validate: can't force both scalar and vector
if (forceScalar && forceVector) {
throw InvalidConfigurationException.conflictingOptions("forceScalar", "forceVector");
}
}
/**
* Creates a new builder for TransformConfig.
*
* @return a new builder instance
*/
public static Builder builder() {
return new Builder();
}
/**
* Creates a default configuration.
*
* @return default config with periodic boundaries and auto-detected engine
*/
public static TransformConfig defaultConfig() {
return new Builder().build();
}
/**
* @return boundary mode for the transform
*/
public BoundaryMode getBoundaryMode() {
return boundaryMode;
}
/**
* @return true if scalar engine is forced
*/
public boolean isForceScalar() {
return forceScalar;
}
/**
* @return true if scalar operations are forced (same as isForceScalar)
*/
public boolean isForceScalarOperations() {
return forceScalar;
}
/**
* @return true if Vector API operations are forced when available
*/
public boolean isForceVector() {
return forceVector;
}
/**
* @return maximum decomposition levels
*/
public int getMaxDecompositionLevels() {
return maxDecompositionLevels;
}
/**
* Returns a string representation of the configuration.
*
* @return string with field values
*/
@Override
public String toString() {
return "TransformConfig{" +
"boundaryMode=" + boundaryMode +
", forceScalar=" + forceScalar +
", forceVector=" + forceVector +
", maxDecompositionLevels=" + maxDecompositionLevels +
'}';
}
/**
* Builder for TransformConfig.
* Allows stepwise construction of configuration.
*/
public static class Builder {
// Default boundary mode is PERIODIC.
private BoundaryMode boundaryMode = BoundaryMode.PERIODIC;
// Default: do not force scalar engine.
private boolean forceScalar = false;
// Default: auto-detect Vector API availability.
private boolean forceVector = false;
// Default: allow up to 20 decomposition levels (handles signals up to 2^20 = 1,048,576 samples).
private int maxDecompositionLevels = 20;
// Private constructor to enforce use via TransformConfig.
private Builder() {
}
/**
* Sets the boundary mode.
*
* @param boundaryMode boundary handling mode
* @return this builder
*/
public Builder boundaryMode(BoundaryMode boundaryMode) {
this.boundaryMode = NullChecks.requireNonNull(boundaryMode, "boundaryMode");
return this;
}
/**
* Sets whether to force scalar engine.
*
* @param forceScalar true to force scalar engine
* @return this builder
*/
public Builder forceScalar(boolean forceScalar) {
this.forceScalar = forceScalar;
return this;
}
/**
* Sets whether to force Vector API operations when available.
* If Vector API is not available, this setting is ignored.
*
* @param forceVector true to force Vector API operations
* @return this builder
*/
public Builder forceVector(boolean forceVector) {
this.forceVector = forceVector;
return this;
}
/**
* Sets the maximum decomposition levels.
*
* @param maxLevels maximum levels (must be >= 1)
* @return this builder
* @throws InvalidArgumentException if maxLevels {@literal <} 1
*/
public Builder maxDecompositionLevels(int maxLevels) {
if (maxLevels < 1) {
throw InvalidArgumentException.notPositive(maxLevels);
}
this.maxDecompositionLevels = maxLevels;
return this;
}
/**
* Builds the TransformConfig instance.
*
* @return new TransformConfig
*/
public TransformConfig build() {
return new TransformConfig(this);
}
}
}