DeviceFilters.java

package net.bmahe.genetics4j.gpu.spec;

import java.util.Arrays;
import java.util.Collection;
import java.util.function.Predicate;

import org.apache.commons.lang3.Validate;

import net.bmahe.genetics4j.gpu.opencl.model.Device;
import net.bmahe.genetics4j.gpu.opencl.model.DeviceType;

/**
 * Utility class providing predicate-based filters for selecting OpenCL devices in GPU-accelerated evolutionary algorithms.
 * 
 * <p>DeviceFilters offers a fluent API for creating device selection criteria based on device characteristics
 * such as type, capabilities, and performance metrics. These filters are used to automatically select
 * appropriate OpenCL devices for GPU-accelerated evolutionary algorithm execution.
 * 
 * <p>Key functionality includes:
 * <ul>
 * <li><strong>Type-based filtering</strong>: Select devices by type (GPU, CPU, accelerator)</li>
 * <li><strong>Logical combinations</strong>: Combine filters using AND and OR operations</li>
 * <li><strong>Performance filtering</strong>: Filter devices based on computational capabilities</li>
 * <li><strong>Predicate composition</strong>: Build complex selection criteria from simple predicates</li>
 * </ul>
 * 
 * <p>Common usage patterns:
 * <pre>{@code
 * // Select GPU devices only
 * Predicate<Device> gpuFilter = DeviceFilters.ofGPU();
 * 
 * // Select CPU devices only
 * Predicate<Device> cpuFilter = DeviceFilters.ofCPU();
 * 
 * // Select GPU or accelerator devices
 * Predicate<Device> computeFilter = DeviceFilters.or(
 *     DeviceFilters.ofGPU(),
 *     DeviceFilters.ofType(DeviceType.ACCELERATOR)
 * );
 * 
 * // Select high-performance GPU devices
 * Predicate<Device> highPerformanceGPU = DeviceFilters.and(
 *     DeviceFilters.ofGPU(),
 *     device -> device.maxComputeUnits() >= 8,
 *     device -> device.maxWorkGroupSize() >= 256
 * );
 * 
 * // Apply filter to device selection
 * GPUEAExecutionContext context = GPUEAExecutionContext.builder()
 *     .deviceFilters(gpuFilter)
 *     .build();
 * }</pre>
 * 
 * <p>Device selection workflow:
 * <ol>
 * <li><strong>Platform discovery</strong>: Enumerate available OpenCL platforms</li>
 * <li><strong>Device enumeration</strong>: Discover devices on selected platforms</li>
 * <li><strong>Filter application</strong>: Apply device filters to candidate devices</li>
 * <li><strong>Device selection</strong>: Select filtered devices for EA execution</li>
 * </ol>
 * 
 * <p>Filter composition patterns:
 * <ul>
 * <li><strong>Type filtering</strong>: Select devices by computational type</li>
 * <li><strong>Capability filtering</strong>: Filter by device computational capabilities</li>
 * <li><strong>Performance filtering</strong>: Select devices meeting performance criteria</li>
 * <li><strong>Logical combinations</strong>: Combine multiple criteria using boolean logic</li>
 * </ul>
 * 
 * <p>Performance considerations:
 * <ul>
 * <li><strong>Device enumeration overhead</strong>: Filters are applied during device discovery</li>
 * <li><strong>Capability validation</strong>: Ensure selected devices meet algorithm requirements</li>
 * <li><strong>Load balancing</strong>: Consider device performance when selecting multiple devices</li>
 * <li><strong>Fallback strategies</strong>: Implement fallback filters for systems with limited devices</li>
 * </ul>
 * 
 * @see Device
 * @see DeviceType
 * @see GPUEAExecutionContext
 */
public class DeviceFilters {

	private DeviceFilters() {

	}

	/**
	 * Creates a predicate that filters devices by the specified device type.
	 * 
	 * @param deviceType the OpenCL device type to filter for
	 * @return predicate that returns true for devices of the specified type
	 * @throws IllegalArgumentException if deviceType is null
	 */
	public static Predicate<Device> ofType(final DeviceType deviceType) {
		Validate.notNull(deviceType);

		return (device) -> device.deviceType()
				.contains(deviceType);
	}

	/**
	 * Creates a predicate that filters for GPU devices only.
	 * 
	 * <p>This is a convenience method equivalent to {@code ofType(DeviceType.GPU)}.
	 * 
	 * @return predicate that returns true for GPU devices
	 */
	public static Predicate<Device> ofGPU() {
		return ofType(DeviceType.GPU);
	}

	/**
	 * Creates a predicate that filters for CPU devices only.
	 * 
	 * <p>This is a convenience method equivalent to {@code ofType(DeviceType.CPU)}.
	 * 
	 * @return predicate that returns true for CPU devices
	 */
	public static Predicate<Device> ofCPU() {
		return ofType(DeviceType.CPU);
	}

	/**
	 * Creates a predicate that returns true if any of the provided predicates return true (logical OR).
	 * 
	 * @param predicates array of device predicates to combine with OR logic
	 * @return predicate that returns true if any input predicate returns true
	 * @throws IllegalArgumentException if predicates is null or empty
	 */
	public static Predicate<Device> or(@SuppressWarnings("unchecked") final Predicate<Device>... predicates) {
		Validate.notNull(predicates);
		Validate.isTrue(predicates.length > 0);

		return device -> Arrays.stream(predicates)
				.anyMatch(predicate -> predicate.test(device));
	}

	/**
	 * Creates a predicate that returns true if any of the provided predicates return true (logical OR).
	 * 
	 * @param predicates collection of device predicates to combine with OR logic
	 * @return predicate that returns true if any input predicate returns true
	 * @throws IllegalArgumentException if predicates is null or empty
	 */
	public static Predicate<Device> or(final Collection<Predicate<Device>> predicates) {
		Validate.notNull(predicates);
		Validate.isTrue(predicates.size() > 0);

		return device -> predicates.stream()
				.anyMatch(predicate -> predicate.test(device));
	}

	/**
	 * Creates a predicate that returns true only if all provided predicates return true (logical AND).
	 * 
	 * @param predicates array of device predicates to combine with AND logic
	 * @return predicate that returns true only if all input predicates return true
	 * @throws IllegalArgumentException if predicates is null or empty
	 */
	@SafeVarargs
	public static Predicate<Device> and(final Predicate<Device>... predicates) {
		Validate.notNull(predicates);
		Validate.isTrue(predicates.length > 0);

		return device -> Arrays.stream(predicates)
				.allMatch(predicate -> predicate.test(device));
	}

	/**
	 * Creates a predicate that returns true only if all provided predicates return true (logical AND).
	 * 
	 * @param predicates collection of device predicates to combine with AND logic
	 * @return predicate that returns true only if all input predicates return true
	 * @throws IllegalArgumentException if predicates is null or empty
	 */
	public static Predicate<Device> and(final Collection<Predicate<Device>> predicates) {
		Validate.notNull(predicates);
		Validate.isTrue(predicates.size() > 0);

		return device -> predicates.stream()
				.allMatch(predicate -> predicate.test(device));
	}

}