1 package net.bmahe.genetics4j.gpu.spec;
2
3 import java.util.Arrays;
4 import java.util.Collection;
5 import java.util.Objects;
6 import java.util.Set;
7 import java.util.function.Predicate;
8
9 import org.apache.commons.collections4.CollectionUtils;
10 import org.apache.commons.lang3.Validate;
11
12 import net.bmahe.genetics4j.gpu.opencl.model.Platform;
13 import net.bmahe.genetics4j.gpu.opencl.model.PlatformProfile;
14
15 /**
16 * Utility class providing predicate-based filters for selecting OpenCL platforms in GPU-accelerated evolutionary
17 * algorithms.
18 *
19 * <p>PlatformFilters offers a fluent API for creating platform selection criteria based on platform characteristics
20 * such as profile type, supported extensions, and vendor capabilities. These filters are used to automatically select
21 * appropriate OpenCL platforms before device enumeration and selection.
22 *
23 * <p>Key functionality includes:
24 * <ul>
25 * <li><strong>Profile-based filtering</strong>: Select platforms by profile (FULL_PROFILE or EMBEDDED_PROFILE)</li>
26 * <li><strong>Extension filtering</strong>: Filter platforms based on supported OpenCL extensions</li>
27 * <li><strong>Logical combinations</strong>: Combine filters using AND and OR operations</li>
28 * <li><strong>Predicate composition</strong>: Build complex selection criteria from simple predicates</li>
29 * </ul>
30 *
31 * <p>Common usage patterns:
32 *
33 * <pre>{@code
34 * // Select platforms with full OpenCL profile
35 * Predicate<Platform> fullProfileFilter = PlatformFilters.ofProfile(PlatformProfile.FULL_PROFILE);
36 *
37 * // Select platforms supporting double precision arithmetic
38 * Predicate<Platform> fp64Filter = PlatformFilters.ofExtension("cl_khr_fp64");
39 *
40 * // Select platforms with multiple required extensions
41 * Set<String> requiredExtensions = Set.of("cl_khr_fp64", "cl_khr_global_int32_base_atomics");
42 * Predicate<Platform> extensionFilter = PlatformFilters.ofExtensions(requiredExtensions);
43 *
44 * // Combine multiple criteria
45 * Predicate<Platform> advancedFilter = PlatformFilters
46 * .and(PlatformFilters.ofProfile(PlatformProfile.FULL_PROFILE), PlatformFilters.ofExtension("cl_khr_fp64"));
47 *
48 * // Apply filter to platform selection
49 * GPUEAExecutionContext context = GPUEAExecutionContext.builder().platformFilters(fullProfileFilter).build();
50 * }</pre>
51 *
52 * <p>Platform selection workflow:
53 * <ol>
54 * <li><strong>Platform enumeration</strong>: Discover all available OpenCL platforms</li>
55 * <li><strong>Filter application</strong>: Apply platform filters to candidate platforms</li>
56 * <li><strong>Platform validation</strong>: Validate filtered platforms meet requirements</li>
57 * <li><strong>Device discovery</strong>: Enumerate devices on selected platforms</li>
58 * </ol>
59 *
60 * <p>Filter criteria considerations:
61 * <ul>
62 * <li><strong>Profile compatibility</strong>: FULL_PROFILE platforms typically offer more features</li>
63 * <li><strong>Extension requirements</strong>: Filter for extensions required by algorithms</li>
64 * <li><strong>Vendor optimization</strong>: Consider vendor-specific optimizations and extensions</li>
65 * <li><strong>Version requirements</strong>: Ensure platforms support required OpenCL versions</li>
66 * </ul>
67 *
68 * <p>Performance considerations:
69 * <ul>
70 * <li><strong>Platform enumeration overhead</strong>: Filters are applied during platform discovery</li>
71 * <li><strong>Extension validation</strong>: Extension checks may have runtime overhead</li>
72 * <li><strong>Fallback strategies</strong>: Implement fallback filters for limited platform availability</li>
73 * <li><strong>Caching</strong>: Platform characteristics are typically static during execution</li>
74 * </ul>
75 *
76 * @see Platform
77 * @see PlatformProfile
78 * @see GPUEAExecutionContext
79 */
80 public class PlatformFilters {
81
82 private PlatformFilters() {
83
84 }
85
86 /**
87 * Creates a predicate that filters platforms by the specified profile type.
88 *
89 * @param platformProfile the OpenCL profile type to filter for (FULL_PROFILE or EMBEDDED_PROFILE)
90 * @return predicate that returns true for platforms with the specified profile
91 * @throws IllegalArgumentException if platformProfile is null
92 */
93 public static Predicate<Platform> ofProfile(final PlatformProfile platformProfile) {
94 Objects.requireNonNull(platformProfile);
95
96 return platform -> platformProfile.equals(platform.profile());
97 }
98
99 /**
100 * Creates a predicate that filters platforms supporting the specified OpenCL extension.
101 *
102 * @param extension the OpenCL extension name to filter for (e.g., "cl_khr_fp64")
103 * @return predicate that returns true for platforms supporting the extension
104 * @throws IllegalArgumentException if extension is null or blank
105 */
106 public static Predicate<Platform> ofExtension(final String extension) {
107 Validate.notBlank(extension);
108
109 return platform -> platform.extensions().contains(extension);
110 }
111
112 /**
113 * Creates a predicate that filters platforms supporting all specified OpenCL extensions.
114 *
115 * @param extensions set of OpenCL extension names that must all be supported
116 * @return predicate that returns true for platforms supporting all specified extensions
117 * @throws IllegalArgumentException if extensions is null
118 */
119 public static Predicate<Platform> ofExtensions(final Set<String> extensions) {
120 Objects.requireNonNull(extensions);
121
122 return platform -> CollectionUtils.containsAll(platform.extensions(), extensions);
123 }
124
125 /**
126 * Creates a predicate that returns true if any of the provided predicates return true (logical OR).
127 *
128 * @param predicates array of platform predicates to combine with OR logic
129 * @return predicate that returns true if any input predicate returns true
130 * @throws IllegalArgumentException if predicates is null or empty
131 */
132 public static Predicate<Platform> or(@SuppressWarnings("unchecked") final Predicate<Platform>... predicates) {
133 Objects.requireNonNull(predicates);
134 Validate.isTrue(predicates.length > 0);
135
136 return platform -> Arrays.stream(predicates).anyMatch(predicate -> predicate.test(platform));
137 }
138
139 /**
140 * Creates a predicate that returns true if any of the provided predicates return true (logical OR).
141 *
142 * @param predicates collection of platform predicates to combine with OR logic
143 * @return predicate that returns true if any input predicate returns true
144 * @throws IllegalArgumentException if predicates is null or empty
145 */
146 public static Predicate<Platform> or(final Collection<Predicate<Platform>> predicates) {
147 Objects.requireNonNull(predicates);
148 Validate.isTrue(predicates.isEmpty() == false);
149
150 return platform -> predicates.stream().anyMatch(predicate -> predicate.test(platform));
151 }
152
153 /**
154 * Creates a predicate that returns true only if all provided predicates return true (logical AND).
155 *
156 * @param predicates array of platform predicates to combine with AND logic
157 * @return predicate that returns true only if all input predicates return true
158 * @throws IllegalArgumentException if predicates is null or empty
159 */
160 public static Predicate<Platform> and(@SuppressWarnings("unchecked") final Predicate<Platform>... predicates) {
161 Objects.requireNonNull(predicates);
162 Validate.isTrue(predicates.length > 0);
163
164 return platform -> Arrays.stream(predicates).allMatch(predicate -> predicate.test(platform));
165 }
166
167 /**
168 * Creates a predicate that returns true only if all provided predicates return true (logical AND).
169 *
170 * @param predicates collection of platform predicates to combine with AND logic
171 * @return predicate that returns true only if all input predicates return true
172 * @throws IllegalArgumentException if predicates is null or empty
173 */
174 public static Predicate<Platform> and(final Collection<Predicate<Platform>> predicates) {
175 Objects.requireNonNull(predicates);
176 Validate.isTrue(predicates.isEmpty() == false);
177
178 return platform -> predicates.stream().allMatch(predicate -> predicate.test(platform));
179 }
180
181 }