OptimizerRegistry.java
package com.morphiqlabs.wavelet.api.spi;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import com.morphiqlabs.wavelet.util.Logging;
/**
* Registry for discovering and managing wavelet transform optimizers via SPI.
* This class provides fallback to scalar implementations when optimizations are not available.
*
* @since 1.0.0
*/
public class OptimizerRegistry {
private static final System.Logger LOG = Logging.getLogger(OptimizerRegistry.class);
private static final OptimizerRegistry INSTANCE = new OptimizerRegistry();
private final Map<Class<?>, Object> optimizerCache = new ConcurrentHashMap<>();
private volatile boolean initialized = false;
private OptimizerRegistry() {
initialize();
}
/**
* Returns the singleton registry instance.
* @return global {@code OptimizerRegistry}
*/
public static OptimizerRegistry getInstance() {
return INSTANCE;
}
private synchronized void initialize() {
if (initialized) {
return;
}
LOG.log(System.Logger.Level.INFO, "Initializing optimizer registry...");
// Discover MODWT optimizers
discoverOptimizers(MODWTOptimizer.class);
// Discover FFT optimizers
discoverOptimizers(FFTOptimizer.class);
// Discover Convolution optimizers
discoverOptimizers(ConvolutionOptimizer.class);
initialized = true;
LOG.log(System.Logger.Level.INFO, () -> "Optimizer registry initialized with " + optimizerCache.size() + " optimizers");
}
private <T> void discoverOptimizers(Class<T> optimizerClass) {
ServiceLoader<T> loader = ServiceLoader.load(optimizerClass);
List<T> candidates = new ArrayList<>();
for (T optimizer : loader) {
try {
if (isSupported(optimizer)) {
candidates.add(optimizer);
LOG.log(System.Logger.Level.INFO, () -> "Found " + optimizerClass.getSimpleName() + ": " +
getName(optimizer) + " (priority: " + getPriority(optimizer) + ")");
}
} catch (Exception e) {
LOG.log(System.Logger.Level.WARNING, "Error checking optimizer support: " + optimizer, e);
}
}
if (!candidates.isEmpty()) {
// Sort by priority (highest first)
candidates.sort((a, b) -> Integer.compare(getPriority(b), getPriority(a)));
T best = candidates.get(0);
optimizerCache.put(optimizerClass, best);
LOG.log(System.Logger.Level.INFO, () -> "Selected " + optimizerClass.getSimpleName() + ": " + getName(best));
} else {
LOG.log(System.Logger.Level.INFO, () -> "No " + optimizerClass.getSimpleName() + " optimizers found, will use scalar fallback");
}
}
private boolean isSupported(Object optimizer) {
try {
return (boolean) optimizer.getClass().getMethod("isSupported").invoke(optimizer);
} catch (Exception e) {
return false;
}
}
private int getPriority(Object optimizer) {
try {
return (int) optimizer.getClass().getMethod("getPriority").invoke(optimizer);
} catch (Exception e) {
return 0;
}
}
private String getName(Object optimizer) {
try {
return (String) optimizer.getClass().getMethod("getName").invoke(optimizer);
} catch (Exception e) {
return optimizer.getClass().getSimpleName();
}
}
/**
* Get the best available MODWT optimizer, or null if none available.
* @return MODWT optimizer or null
*/
public Optional<MODWTOptimizer> getMODWTOptimizer() {
return Optional.ofNullable((MODWTOptimizer) optimizerCache.get(MODWTOptimizer.class));
}
/**
* Get the best available FFT optimizer, or null if none available.
* @return FFT optimizer or null
*/
public Optional<FFTOptimizer> getFFTOptimizer() {
return Optional.ofNullable((FFTOptimizer) optimizerCache.get(FFTOptimizer.class));
}
/**
* Get the best available convolution optimizer, or null if none available.
* @return convolution optimizer or null
*/
public Optional<ConvolutionOptimizer> getConvolutionOptimizer() {
return Optional.ofNullable((ConvolutionOptimizer) optimizerCache.get(ConvolutionOptimizer.class));
}
/**
* Check if any optimizations are available.
* @return true if at least one optimizer is available
*/
public boolean hasOptimizations() {
return !optimizerCache.isEmpty();
}
/**
* Get information about available optimizers.
* @return map of optimizer types to their names
*/
public Map<String, String> getAvailableOptimizers() {
Map<String, String> result = new HashMap<>();
optimizerCache.forEach((clazz, optimizer) -> {
result.put(clazz.getSimpleName(), getName(optimizer));
});
return result;
}
}