Source browser
examples/real_data.cpp
Read the complete file below or download its original bytes. Line numbers provide stable links to a declaration or processing step.
File fingerprint
1/** @file real_data.cpp
2 * @brief Evaluate the example algorithms with public images, signals, events and digits.
3 * Every requested GPU profile receives the same inputs and initial mutable state as CPU.
4 * Independent randomized references remain part of the individual example tests.
5 */
6#include "real_data_support.hpp" // Read documented public fixtures and compare complete outputs.
7#include "fft_support.hpp" // Build native radix-two FFT and frequency-filter pipelines.
8#include "signal_filters.hpp" // Build causal FIR and stateful biquad pipelines.
9#include "evt3_support.hpp" // Decode the original public EVT3 recording prefix.
10#include "evt3_flow_checks.hpp" // Compare event flow with an independent double-precision fit.
11#include "neural_support.hpp" // Train and infer the small ANN and CNN examples.
12#include "transformer_support.hpp" // Train and infer the small attention model.
13#include "lenet_support.hpp" // Run the documented modern LeNet digit classifier.
14#include "lenet_checks.hpp" // Validate every named-model stage with an independent double reference.
15#ifdef SHDR_PLUGIN_PATH // Enable the platform-specific dynamic-library example when it is built.
16#include <dlfcn.h> // Load the documented shader plugin through the ordinary POSIX interface.
17#endif // Compile the plugin case on platforms with the dynamic-loader API.
18#include <chrono> // Bound asynchronous validation waits by ordinary completion objects.
19#include <future> // Observe queued CPU work through its completion future.
20#include <numeric> // Summarize classification accuracy and signal energy.
21#include <thread> // Yield while an asynchronous device submission is incomplete.
22
23namespace { // Keep the real-data evaluator's case wiring private to this executable.
24using shdr_examples::runner; // Reuse the other examples' backend selection syntax.
25namespace data = shdr_examples::real_data; // Keep fixture and output-check names short.
26using glsl::uvec4; // Store exact decoded event records.
27using glsl::vec2; // Store complex samples and image derivatives in native shader layout.
28using glsl::vec4; // Store RGBA pixels, predictions and optical-flow results.
29const unsigned width = 64u, height = 64u, // Match the prepared NASA image size.
30 count = width * height; // Match the prepared NASA image size.
31
32std::vector<float> image() { // Convert the public RGB image into a scalar luminance fixture.
33 const auto rgb = data::bytes("earth_rgb.bin"); // Read the documented 64 by 64 RGB8 image.
34 if (rgb.size() != count * 3u) { // Require the preparation script's exact image shape.
35 throw shdr::error( // Report the failed format, numerical or resource precondition.
36 "NASA image fixture must contain 64 by 64 RGB8 pixels"); // Reject a mismatched fixture.
37 } // Every pixel has three complete published color components.
38 std::vector<float> result(count); // Allocate one normalized luminance value per pixel.
39 for (unsigned i = 0; i < count; ++i) { // Visit every real photograph sample.
40 result[i] = // Apply the documented RGB luminance weights.
41 (0.2126f * rgb[3u * i] + 0.7152f * rgb[3u * i + 1u] + 0.0722f * rgb[3u * i + 2u]) / // Apply the documented RGB luminance weights.
42 255.0f; // Apply the documented RGB luminance weights.
43 } // The resulting scalar input can feed all image and resource examples.
44 return result; // Return a reproducible real-data image to each case.
45} // Load the prepared RGB bytes directly for native image processing.
46void basic(runner& run, // Exercise linked resources and asynchronous execution with image samples.
47 const std::vector<float>& // Exercise linked resources and asynchronous execution with image samples.
48 source) { // Exercise linked resources and asynchronous execution with image samples.
49 auto input = data::resource(source); // Share the same original real pixels with all stages.
50 shdr::buffer<float> scaled(count), clipped(count), // Allocate intermediate and reduction results.
51 total(1); // Allocate intermediate and reduction results.
52 auto affine = shdr::make_kernel("affine_kernel"); // Create the scale-and-offset shader.
53 auto clamp = shdr::make_kernel("clamp_kernel"); // Create the range-limiting shader.
54 auto sum = shdr::make_kernel("sum_kernel"); // Create the deterministic reduction shader.
55 affine->bind("input_values", input) // Connect the input_values shader port to input storage.
56 .bind("output_values", scaled) // Connect the output_values shader port to scaled storage.
57 .set("count", count) // Supply the count processing parameter.
58 .set("scale", 1.7f) // Supply the scale processing parameter.
59 .set("offset", -0.2f); // Connect the first transform.
60 clamp->bind("input_values", scaled) // Connect the input_values shader port to scaled storage.
61 .bind("output_values", clipped) // Connect the output_values shader port to clipped storage.
62 .set("count", count) // Supply the count processing parameter.
63 .set("lower", 0.0f) // Supply the lower processing parameter.
64 .set("upper", 1.0f); // Connect range limiting.
65 sum->bind("input_values", clipped) // Connect the input_values shader port to clipped storage.
66 .bind("output_values", total) // Connect the output_values shader port to total storage.
67 .set("count", count); // Reduce the transformed pixels.
68 const auto stages = // Link three ordinary resource stages.
69 shdr::dispatch(*affine, shdr::extent((count + 63u) / 64u)) >> // Dispatch the affine stage with its configured geometry.
70 shdr::dispatch(*clamp, shdr::extent((count + 31u) / 32u)) >> // Dispatch the clamp stage with its configured geometry.
71 shdr::dispatch(*sum, shdr::extent(1)); // Link three ordinary resource stages.
72 data::checked_output<float> scaled_check(scaled), clipped_check(clipped), // Keep the complete stage output for the backend comparison.
73 total_check(total); // Check intermediate values as well as the final sum.
74 data::evaluate( // Compare the complete real-data pipeline outputs on CPU and the requested GPU backend.
75 run, // Select the same execution backend for this complete case.
76 "NASA linked affine/clamp/reduction", // Use the exact diagnostic or comparison label for this case.
77 stages, // Run the complete processing sequence in dependency order.
78 {&scaled_check, &clipped_check, &total_check}); // Compare complete CPU and GPU outputs.
79 double expected = 0.0; // Accumulate an independent host reduction.
80 for (unsigned i = 0; i < count; // Check each transform against ordinary scalar arithmetic.
81 ++i) { // Check each transform against ordinary scalar arithmetic.
82 const float value = // Apply the documented transformation independently.
83 std::max(0.0f, // Apply the documented transformation independently.
84 std::min(1.0f, // Apply the documented transformation independently.
85 source[i] * 1.7f - // Apply the documented transformation independently.
86 0.2f)); // Apply the documented transformation independently.
87 shdr_examples::require_close(static_cast<const shdr::buffer<float>&>(clipped).data()[i], // Validate the actual transformed pixel.
88 value, // Validate the actual transformed pixel.
89 "real affine/clamp"); // Validate the actual transformed pixel.
90 expected += value; // Sum with a wider independent accumulator.
91 } // Complete transformed data has an independent reference.
92 shdr_examples::require_close(static_cast<const shdr::buffer<float>&>(total).data()[0], // Allow long float summation error.
93 float(expected), // Allow long float summation error.
94 "real reduction", // Use the exact diagnostic or comparison label for this case.
95 0.02f); // Allow long float summation error.
96 const auto hybrid = // Force one stage onto CPU in a mixed pipeline.
97 shdr::dispatch(*affine, shdr::extent((count + 63u) / 64u)) >> // Dispatch the affine stage with its configured geometry.
98 shdr::dispatch(*clamp, shdr::extent((count + 31u) / 32u), shdr::backend::cpu) >> // Dispatch the clamp stage with its configured geometry.
99 shdr::dispatch(*sum, shdr::extent(1)); // Force one stage onto CPU in a mixed pipeline.
100 data::evaluate( // Compare the complete real-data pipeline outputs on CPU and the requested GPU backend.
101 run, // Select the same execution backend for this complete case.
102 "NASA mixed CPU/GPU resources", // Use the exact diagnostic or comparison label for this case.
103 hybrid, // Validate real-data ownership changes between backends.
104 {&clipped_check, &total_check}); // Validate real-data ownership changes between backends.
105 if (run.gpu()) { // Exercise the library's headless asynchronous GPU path.
106 stages.prepare(*run.device()); // Compile programs and allocate resources before polling.
107 auto completion = // Queue the GPU pipeline and retain its completion handle.
108 stages.submit(*run.device()); // Queue the GPU pipeline and retain its completion handle.
109 while (!completion.ready()) { // Poll the completion fence from the context thread.
110 std::this_thread:: // Allow other application work while processing remains in flight.
111 yield(); // Allow other application work while processing remains in flight.
112 } // All dependent dispatches have now completed.
113 } else { // Exercise the bounded asynchronous CPU path with the same real input.
114 shdr::executor executor(2, 2); // Create a small worker pool and bounded queue.
115 executor.submit(stages).get(); // Observe the submitted linked pipeline's completion.
116 } // Either backend uses the same captured pipeline resources.
117 (void)total_check // Compare the asynchronous result with the earlier completed reference.
118 .verify(); // Compare the asynchronous result with the earlier completed reference.
119 std::cout // Report the completed real-data processing check.
120 << "NASA asynchronous resource submission: verified\n"; // Report the real-data completion check.
121#ifdef SHDR_PLUGIN_PATH // Exercise the built dynamic shader with the same real image values.
122 void* library = // Load only the example plugin supplied by this build.
123 dlopen(SHDR_PLUGIN_PATH, // Load only the example plugin supplied by this build.
124 RTLD_NOW | RTLD_LOCAL); // Load only the example plugin supplied by this build.
125 if (!library) { // Report a genuine plugin-loading failure.
126 throw shdr::error(dlerror()); // Preserve the loader's useful diagnostic.
127 } // The library stays open until its resources and pipeline are destroyed.
128 typedef shdr::kernel* (*create_function)(); // Describe the documented exported kernel factory.
129 typedef void (*destroy_function)( // Describe the matching plugin-owned destructor.
130 shdr::kernel*); // Describe the matching plugin-owned destructor.
131 const auto create = reinterpret_cast<create_function>( // Resolve the example's factory entry point.
132 dlsym(library, "shdr_create_kernel")); // Resolve the example's factory entry point.
133 const auto destroy = reinterpret_cast<destroy_function>( // Resolve the matching destruction entry point.
134 dlsym(library, "shdr_destroy_kernel")); // Resolve the matching destruction entry point.
135 if (!create || !destroy) { // Require both shader creation and destruction exports from the plugin.
136 dlclose(library); // Release the unused library handle before reporting failure.
137 throw shdr::error( // Report the failed format, numerical or resource precondition.
138 "real-data shader plugin has an incomplete factory interface"); // Explain the missing contract.
139 } // Both exported functions are now available while the library is retained.
140 shdr::kernel* dynamic = // Construct the same dual-compiled affine shader inside the library.
141 create(); // Construct the same dual-compiled affine shader inside the library.
142 if (!dynamic) { // Reject an unusable factory result.
143 dlclose(library); // Release the plugin handle after an empty factory result.
144 throw shdr::error( // Report the failed format, numerical or resource precondition.
145 "real-data shader plugin returned no kernel"); // Report the failed factory.
146 } // The dynamic kernel can share the ordinary real-data input resources.
147 try { // Keep destruction and unloading correct when validation fails.
148 dynamic->bind("input_values", input) // Connect the input_values shader port to input storage.
149 .bind("output_values", scaled) // Connect the output_values shader port to scaled storage.
150 .set("count", count) // Supply the count processing parameter.
151 .set("scale", 1.7f) // Supply the scale processing parameter.
152 .set("offset", // Bind real pixels through the plugin's generic resource interface.
153 -0.2f); // Bind real pixels through the plugin's generic resource interface.
154 const auto dynamic_stages = // Link the loaded stage with built-in stages.
155 shdr::dispatch(*dynamic, shdr::extent((count + 63u) / 64u)) >> // Dispatch the dynamic stage with its configured geometry.
156 shdr::dispatch(*clamp, shdr::extent((count + 31u) / 32u)) >> // Dispatch the clamp stage with its configured geometry.
157 shdr::dispatch(*sum, shdr::extent(1)); // Link the loaded stage with built-in stages.
158 data::evaluate(run, // Compare the complete real-data pipeline outputs on CPU and the requested GPU backend.
159 "NASA dynamically loaded shader pipeline", // Use the exact diagnostic or comparison label for this case.
160 dynamic_stages, // Compare all complete real-data plugin outputs.
161 {&scaled_check, // Compare all complete real-data plugin outputs.
162 &clipped_check, // Compare all complete real-data plugin outputs.
163 &total_check}); // Compare all complete real-data plugin outputs.
164 } catch (...) { // Clean up plugin ownership before propagating an actual validation failure.
165 destroy(dynamic); // Destroy the kernel with the library's own matching function.
166 dlclose(library); // Unload only after the pipeline and kernel have been released.
167 throw; // Preserve the original useful validation diagnostic.
168 } // The temporary linked pipeline has now left its lifetime scope.
169 destroy(dynamic); // Release the plugin-owned shader object after all pipeline use.
170 dlclose(library); // Release the library only after all live plugin objects are destroyed.
171#endif // Finish the optional real-data dynamic-library evaluation.
172} // Generic pipeline, hybrid and async examples now have real-data functional coverage.
173void images(runner& run, // Move real pixels through image storage and an image filter.
174 const std::vector<float>& // Move real pixels through image storage and an image filter.
175 source) { // Move real pixels through image storage and an image filter.
176 auto input = data::resource(source); // Allocate the original real scalar pixel resource.
177 shdr::buffer<float> output(count); // Receive the filtered image as an ordinary buffer.
178 shdr::image2d<float, 1> original(width, height), // Allocate portable r32f image resources.
179 filtered(width, height); // Allocate portable r32f image resources.
180 auto upload = shdr::make_kernel("buffer_to_image_kernel"); // Create the buffer-to-image stage.
181 auto blur = shdr::make_kernel("blur_kernel"); // Create the explicit clamp-to-edge box filter.
182 auto download = // Create the image-to-buffer stage.
183 shdr::make_kernel("image_to_buffer_kernel"); // Create the image-to-buffer stage.
184 upload->bind("input_values", input) // Connect the input_values shader port to input storage.
185 .bind("output_image", original); // Connect original scalar pixels to image storage.
186 blur->bind("input_image", original) // Connect the input_image shader port to original storage.
187 .bind("output_image", filtered); // Connect the filtered image resource.
188 download->bind("input_image", filtered) // Connect the input_image shader port to filtered storage.
189 .bind("output_values", output); // Connect image results to a typed output buffer.
190 const shdr::extent groups(width / 8u, height / 8u); // Cover every prepared image pixel.
191 const auto stages = shdr::dispatch(*upload, groups) >> shdr::dispatch(*blur, groups) >> // Dispatch the upload stage with its configured geometry.
192 shdr::dispatch(*download, groups); // Link the image roundtrip and filter.
193 data::checked_output<float> check(output); // Record the complete output image.
194 data::evaluate(run, // Compare the complete real-data pipeline outputs on CPU and the requested GPU backend.
195 "NASA buffer/image/box-filter pipeline", // Use the exact diagnostic or comparison label for this case.
196 stages, // Run the complete processing sequence in dependency order.
197 {&check}); // Compare full CPU and GPU image processing.
198 for (unsigned y = 0; y < height; ++y) { // Independently validate each real image row.
199 for (unsigned x = 0; x < width; ++x) { // Independently validate each output pixel.
200 double sum = 0.0; // Use a double host accumulator for the reference neighborhood.
201 for (int dy = -1; dy <= 1; ++dy) { // Visit the three reference neighborhood rows.
202 for (int dx = -1; dx <= 1; // Visit the three reference neighborhood columns.
203 ++dx) { // Visit the three reference neighborhood columns.
204 const unsigned xx = unsigned( // Clamp the reference X coordinate.
205 std::max(0, // Clamp the reference X coordinate.
206 std::min(int(width) - 1, // Clamp the reference X coordinate.
207 int(x) + dx))); // Clamp the reference X coordinate.
208 const unsigned yy = unsigned( // Clamp the reference Y coordinate.
209 std::max(0, // Clamp the reference Y coordinate.
210 std::min(int(height) - 1, // Clamp the reference Y coordinate.
211 int(y) + dy))); // Clamp the reference Y coordinate.
212 sum += source[yy * width + xx]; // Add the original real image sample.
213 } // Every horizontal neighbor has been included.
214 } // Every neighborhood row has been included.
215 shdr_examples::require_close( // Compare the independent local average.
216 static_cast<const shdr::buffer<float>&>(output).data()[y * width + x], // Compare the independent local average.
217 float(sum / 9.0), // Compare the independent local average.
218 "real image box filter"); // Compare the independent local average.
219 } // This complete row matches the documented image boundary policy.
220 } // All real image pixels have independent filter references.
221} // Complete the typed image-resource and independent box-filter comparisons.
222void vision(runner& run, // Run the complete corner-detection chain on the public image.
223 const std::vector<float>& // Run the complete corner-detection chain on the public image.
224 source) { // Run the complete corner-detection chain on the public image.
225 auto input = // Share original real pixels with the first smoothing stage.
226 data::resource(source); // Share original real pixels with the first smoothing stage.
227 shdr::buffer<float> horizontal(count), blurred(count), // Allocate Gaussian and Harris scalar outputs.
228 response(count); // Allocate Gaussian and Harris scalar outputs.
229 shdr::buffer<vec2> gradients(count); // Allocate the two Sobel components for each pixel.
230 shdr::buffer<vec4> tensors(count); // Allocate the structure tensor components for each pixel.
231 shdr::buffer<unsigned> corners(count); // Allocate the exact nonmaximum-suppression mask.
232 auto gx = shdr::make_kernel( // Create the first separable Gaussian stage.
233 "gaussian_horizontal_kernel"); // Create the first separable Gaussian stage.
234 auto gy = shdr::make_kernel( // Create the second separable Gaussian stage.
235 "gaussian_vertical_kernel"); // Create the second separable Gaussian stage.
236 auto sobel = shdr::make_kernel("sobel_kernel"); // Create the image derivative stage.
237 auto tensor = shdr::make_kernel("harris_tensor_kernel"); // Create the per-pixel tensor stage.
238 auto harris = // Create the smoothed Harris response stage.
239 shdr::make_kernel("harris_response_kernel"); // Create the smoothed Harris response stage.
240 auto nms = shdr::make_kernel("nonmax_kernel"); // Create the deterministic peak-selection stage.
241 gx->bind("input_values", input) // Connect the input_values shader port to input storage.
242 .bind("output_values", horizontal); // Connect the horizontal Gaussian pass.
243 gy->bind("input_values", horizontal) // Connect the input_values shader port to horizontal storage.
244 .bind("output_values", blurred); // Connect the vertical Gaussian pass.
245 sobel->bind("input_values", blurred) // Connect the input_values shader port to blurred storage.
246 .bind("output_values", gradients); // Derive gradients from the blurred real image.
247 tensor->bind("input_values", gradients) // Connect the input_values shader port to gradients storage.
248 .bind("output_values", tensors) // Connect the output_values shader port to tensors storage.
249 .set("count", count); // Form tensors from both derivative components.
250 harris->bind("input_values", tensors) // Connect the input_values shader port to tensors storage.
251 .bind("output_values", response) // Connect the output_values shader port to response storage.
252 .set("harris_k", 0.04f); // Evaluate the standard Harris measure.
253 nms->bind("input_values", response) // Connect the input_values shader port to response storage.
254 .bind("output_values", corners) // Connect the output_values shader port to corners storage.
255 .set("threshold", 0.000001f); // Select positive local peaks with a fixed threshold.
256 for (shdr::kernel* shader : {gx.get(), // Set dimensions for every pixel-indexed stage.
257 gy.get(), // Set dimensions for every pixel-indexed stage.
258 sobel.get(), // Set dimensions for every pixel-indexed stage.
259 harris.get(), // Set dimensions for every pixel-indexed stage.
260 nms.get()}) { // Set dimensions for every pixel-indexed stage.
261 shader->set("width", width) // Supply the width processing parameter.
262 .set("height", height); // Use the same real image geometry throughout.
263 } // The tensor stage needs only its element count.
264 const shdr::extent groups((count + 63u) / 64u); // Cover every flattened real image pixel.
265 const auto stages = shdr::dispatch(*gx, groups) >> shdr::dispatch(*gy, groups) >> // Dispatch the gx stage with its configured geometry.
266 shdr::dispatch(*sobel, groups) >> shdr::dispatch(*tensor, groups) >> // Dispatch the sobel stage with its configured geometry.
267 shdr::dispatch(*harris, groups) >> // Dispatch the harris stage with its configured geometry.
268 shdr::dispatch(*nms, groups); // Link the full vision pipeline.
269 data::checked_output<float> blur_check(blurred), // Keep the complete stage output for the backend comparison.
270 response_check(response); // Compare Gaussian and Harris scalar outputs.
271 data::checked_output<vec2> gradient_check(gradients); // Compare both Sobel components.
272 data::checked_output<vec4> tensor_check(tensors); // Compare every structure tensor component.
273 data::checked_output<unsigned> corner_check(corners, // Keep the complete stage output for the backend comparison.
274 0.0); // Require identical selected corner masks.
275 data::evaluate(run, // Compare the complete real-data pipeline outputs on CPU and the requested GPU backend.
276 "NASA Gaussian/Sobel/Harris/nonmaximum suppression", // Use the exact diagnostic or comparison label for this case.
277 stages, // Run the complete processing sequence in dependency order.
278 {&blur_check, // Validate every meaningful stage output.
279 &gradient_check, // Validate every meaningful stage output.
280 &tensor_check, // Validate every meaningful stage output.
281 &response_check, // Validate every meaningful stage output.
282 &corner_check}); // Validate every meaningful stage output.
283 const unsigned* mask = // Read the completed peak mask.
284 static_cast<const shdr::buffer<unsigned>&>(corners).data(); // Read the completed peak mask.
285 const unsigned peaks = // Count the selected real image corners.
286 std::accumulate(mask, mask + count, 0u); // Count the selected real image corners.
287 if (!peaks || // Require meaningful real detections while preserving a valid mask count.
288 peaks > count) { // Require meaningful real detections while preserving a valid mask count.
289 throw shdr::error( // Report the failed format, numerical or resource precondition.
290 "real image corner detector produced no positive peaks"); // Report a failed useful real-data response.
291 } // A public image has measurable local structure at this threshold.
292 std::cout << "NASA selected Harris peaks: " << peaks // Report the completed real-data processing check.
293 << '\n'; // Record the actual real-data corner count.
294} // The guide distinguishes backend parity from annotated corner ground truth.
295void spectral( // Filter and transform real image samples through native spectral stages.
296 runner& run, // Filter and transform real image samples through native spectral stages.
297 const std::vector<float>& // Filter and transform real image samples through native spectral stages.
298 source) { // Filter and transform real image samples through native spectral stages.
299 const glsl::uvec2 size(16u, 16u); // Keep the direct two-dimensional reference transform small.
300 const unsigned samples = size.x * size.y; // Count the cropped real image samples.
301 std::vector<vec2> original(samples); // Store real image values as complex samples.
302 for (unsigned y = 0; y < size.y; // Read the selected image region from the prepared public RGB data.
303 ++y) { // Read the selected image region from the prepared public RGB data.
304 for (unsigned x = 0; x < size.x; ++x) { // Copy each pixel of the selected crop.
305 original[y * size.x + x] = vec2(source[(y + 24u) * width + x + 24u], // Select the same central crop on every host.
306 0.0f); // Select the same central crop on every host.
307 } // Each row preserves its original image order.
308 } // The crop supplies a real signal for convolution and Fourier processing.
309 auto input = data::resource(original); // Bind the original image crop as a complex resource.
310 shdr::buffer<vec2> convolved(samples), row_spectrum(samples), spectrum(samples), // Allocate complete spectral intermediates.
311 inverse_rows(samples), restored(samples); // Allocate complete spectral intermediates.
312 auto weights = data::resource( // Define a normalized cross-shaped convolution kernel.
313 std::vector<float>{0.0f, // Define a normalized cross-shaped convolution kernel.
314 0.125f, // Define a normalized cross-shaped convolution kernel.
315 0.0f, // Define a normalized cross-shaped convolution kernel.
316 0.125f, // Define a normalized cross-shaped convolution kernel.
317 0.5f, // Define a normalized cross-shaped convolution kernel.
318 0.125f, // Define a normalized cross-shaped convolution kernel.
319 0.0f, // Define a normalized cross-shaped convolution kernel.
320 0.125f, // Define a normalized cross-shaped convolution kernel.
321 0.0f}); // Define a normalized cross-shaped convolution kernel.
322 auto convolution = shdr::make_kernel( // Create the true two-dimensional convolution stage.
323 "convolution2d_kernel"); // Create the true two-dimensional convolution stage.
324 convolution->bind("input_values", input) // Connect the input_values shader port to input storage.
325 .bind("output_values", convolved) // Connect the output_values shader port to convolved storage.
326 .bind("coefficients", weights) // Connect the coefficients shader port to weights storage.
327 .set("dimensions", size) // Supply the dimensions processing parameter.
328 .set("kernel_size", glsl::uvec2(3u, 3u)); // Link pixels and dynamic coefficients.
329 auto rows = shdr::make_kernel("dft_rows_kernel"); // Create the forward row transform.
330 auto columns = shdr::make_kernel("dft_columns_kernel"); // Create the forward column transform.
331 auto inverse_row = // Create the normalized inverse row transform.
332 shdr::make_kernel("dft_rows_kernel"); // Create the normalized inverse row transform.
333 auto inverse_column = // Create the normalized inverse column transform.
334 shdr::make_kernel("dft_columns_kernel"); // Create the normalized inverse column transform.
335 rows->bind("input_values", convolved) // Connect the input_values shader port to convolved storage.
336 .bind("output_values", row_spectrum) // Connect the output_values shader port to row_spectrum storage.
337 .set("dimensions", size) // Supply the dimensions processing parameter.
338 .set("direction", -1.0f) // Supply the direction processing parameter.
339 .set("normalization", 1.0f); // Transform real filtered image rows.
340 columns->bind("input_values", row_spectrum) // Connect the input_values shader port to row_spectrum storage.
341 .bind("output_values", spectrum) // Connect the output_values shader port to spectrum storage.
342 .set("dimensions", size) // Supply the dimensions processing parameter.
343 .set("direction", -1.0f) // Supply the direction processing parameter.
344 .set("normalization", 1.0f); // Complete the full two-dimensional spectrum.
345 inverse_row->bind("input_values", spectrum) // Connect the input_values shader port to spectrum storage.
346 .bind("output_values", inverse_rows) // Connect the output_values shader port to inverse_rows storage.
347 .set("dimensions", size) // Supply the dimensions processing parameter.
348 .set("direction", 1.0f) // Supply the direction processing parameter.
349 .set("normalization", 1.0f / size.x); // Reconstruct row-domain values.
350 inverse_column->bind("input_values", inverse_rows) // Connect the input_values shader port to inverse_rows storage.
351 .bind("output_values", restored) // Connect the output_values shader port to restored storage.
352 .set("dimensions", size) // Supply the dimensions processing parameter.
353 .set("direction", 1.0f) // Supply the direction processing parameter.
354 .set("normalization", 1.0f / size.y); // Reconstruct the filtered real image.
355 const shdr::extent groups(2u, 2u); // Cover the sixteen by sixteen output geometry.
356 const auto stages = // Keep transform dependencies on the selected backend.
357 shdr::dispatch(*convolution, groups) >> shdr::dispatch(*rows, groups) >> // Dispatch the convolution stage with its configured geometry.
358 shdr::dispatch(*columns, groups) >> shdr::dispatch(*inverse_row, groups) >> // Dispatch the columns stage with its configured geometry.
359 shdr::dispatch(*inverse_column, // Dispatch the inverse_column stage with its configured geometry.
360 groups); // Keep transform dependencies on the selected backend.
361 data::checked_output<vec2> convolution_check(convolved), spectrum_check(spectrum, 0.002), // Keep the complete stage output for the backend comparison.
362 restored_check(restored); // Record filtered pixels, frequencies and reconstructed samples.
363 data::evaluate(run, // Compare the complete real-data pipeline outputs on CPU and the requested GPU backend.
364 "NASA 2D convolution/DFT/inverse", // Use the exact diagnostic or comparison label for this case.
365 stages, // Run the complete processing sequence in dependency order.
366 {&convolution_check, // Compare every meaningful spectral result.
367 &spectrum_check, // Compare every meaningful spectral result.
368 &restored_check}); // Compare every meaningful spectral result.
369 const vec2* expected = // Read the filtered real image before its spectral roundtrip.
370 static_cast<const shdr::buffer<vec2>&>(convolved) // Read the filtered real image before its spectral roundtrip.
371 .data(); // Read the filtered real image before its spectral roundtrip.
372 const vec2* actual = static_cast<const shdr::buffer<vec2>&>(restored) // Read the reconstructed image after both inverse stages.
373 .data(); // Read the reconstructed image after both inverse stages.
374 for (unsigned i = 0; i < samples; // Check an independent roundtrip invariant on every pixel.
375 ++i) { // Check an independent roundtrip invariant on every pixel.
376 shdr_examples::require_close(actual[i].x, // Verify the real image amplitude.
377 expected[i].x, // Verify the real image amplitude.
378 "real 2D inverse reconstruction", // Use the exact diagnostic or comparison label for this case.
379 0.0001f); // Verify the real image amplitude.
380 shdr_examples::require_close( // Verify that the reconstructed real image has a zero imaginary component.
381 actual[i].y, // Verify that the reconstructed real image has a zero imaginary component.
382 0.0f, // Verify that the reconstructed real image has a zero imaginary component.
383 "real 2D inverse imaginary component", // Use the exact diagnostic or comparison label for this case.
384 0.0001f); // Verify that the reconstructed real image has a zero imaginary component.
385 } // The forward and inverse transforms agree on the real image fixture.
386 const auto digits = // Read real digit images for a genuine image-stack volume.
387 data::bytes("mnist_test.bin"); // Read real digit images for a genuine image-stack volume.
388 const glsl::uvec3 volume_size( // Interpret eight digit images as separate depth planes.
389 28u, 28u, 8u); // Interpret eight digit images as separate depth planes.
390 std::vector<float> volume_values( // Allocate the volume formed by stacking real digit images along its depth axis.
391 volume_size.x * volume_size.y * // Allocate the volume formed by stacking real digit images along its depth axis.
392 volume_size.z); // Allocate the volume formed by stacking real digit images along its depth axis.
393 for (unsigned i = 0; i < volume_values.size(); // Normalize the original dataset byte values.
394 ++i) { // Normalize the original dataset byte values.
395 volume_values[i] = // Preserve the first eight published digit images in order.
396 digits.at(i) / 255.0f; // Preserve the first eight published digit images in order.
397 } // Use each original digit image as one depth slice of the convolution input.
398 auto volume = // Share the real volume with native convolution stages.
399 data::resource(volume_values); // Share the real volume with native convolution stages.
400 shdr::buffer<float> output(volume_values.size()); // Allocate the filtered real image stack.
401 auto coefficients = data::resource( // Define a normalized three-dimensional box kernel.
402 std::vector<float>(27u, 1.0f / 27.0f)); // Define a normalized three-dimensional box kernel.
403 auto filter = shdr::make_kernel( // Create the dynamic three-dimensional convolution stage.
404 "convolution3d_kernel"); // Create the dynamic three-dimensional convolution stage.
405 filter->bind("input_values", volume) // Connect the input_values shader port to volume storage.
406 .bind("output_values", output) // Connect the output_values shader port to output storage.
407 .bind("coefficients", coefficients) // Connect the coefficients shader port to coefficients storage.
408 .set("dimensions", volume_size) // Supply the dimensions processing parameter.
409 .set("kernel_size", // Connect real voxels and kernel coefficients.
410 glsl::uvec3(3u, 3u, 3u)); // Connect real voxels and kernel coefficients.
411 data::checked_output<float> volume_check(output); // Compare every real-volume output sample.
412 data::evaluate(run, // Compare the complete real-data pipeline outputs on CPU and the requested GPU backend.
413 "MNIST image-stack 3D convolution", // Use the exact diagnostic or comparison label for this case.
414 shdr::pipeline(shdr::dispatch(*filter, shdr::extent(7u, 7u, 2u))), // Dispatch the filter stage with its configured geometry.
415 {&volume_check}); // Validate full CPU and GPU volume processing.
416} // Randomized tests separately validate arbitrary 2D and 3D convolution coefficients.
417unsigned bayer_channel(unsigned x, // Resolve the color sampled at a Bayer coordinate.
418 unsigned y, // Resolve the color sampled at a Bayer coordinate.
419 unsigned pattern) { // Resolve the color sampled at a Bayer coordinate.
420 const unsigned phase = (y % 2u) * 2u + x % 2u; // Identify one of the four pixel phases.
421 const unsigned red = pattern == 0u ? 0u // Follow the documented RGGB/BGGR/GRBG/GBRG order.
422 : pattern == 1u ? 3u // Follow the documented RGGB/BGGR/GRBG/GBRG order.
423 : pattern == 2u ? 1u // Follow the documented RGGB/BGGR/GRBG/GBRG order.
424 : 2u; // Follow the documented RGGB/BGGR/GRBG/GBRG order.
425 return phase == red ? 0u // Return red, blue or green for this phase.
426 : phase == 3u - red ? 2u // Return red, blue or green for this phase.
427 : 1u; // Return red, blue or green for this phase.
428} // This host sampler remains separate from the demosaicing shader's interpolation.
429std::vector<unsigned> pack_bayer( // Encode real-derived Bayer samples into each supported transport representation.
430 const std::vector<unsigned>& samples, // Encode real-derived Bayer samples into each supported transport representation.
431 unsigned // Encode real-derived Bayer samples into each supported transport representation.
432 packing) { // Encode real-derived Bayer samples into each supported transport representation.
433 std::vector<unsigned> bytes; // Accumulate the exact little-endian packed transport bytes.
434 for (std::size_t i = 0; i < samples.size();) { // Include every Bayer sensor-site value.
435 if (packing == 0u) { // Store one unsigned byte per Bayer8 sample.
436 bytes.push_back(samples[i++]); // Copy the original eight-bit value.
437 } else if (packing == 1u) { // Store standard little-endian Bayer16 samples.
438 const unsigned value = samples[i++]; // Read one complete sixteen-bit sensor value.
439 bytes.push_back(value & 255u); // Emit the low transport byte.
440 bytes.push_back(value >> 8u); // Emit the high transport byte.
441 } else if (packing == 2u) { // Store standard four-sample RAW10 groups.
442 unsigned low = 0u; // Collect the two low bits of every sensor sample.
443 for (unsigned j = 0; j < 4u; ++j) { // Encode the group's four adjacent sensor values.
444 bytes.push_back(samples[i + j] >> 2u); // Emit eight high bits from this sample.
445 low |= (samples[i + j] & 3u) // Collect its two low bits in their standard slot.
446 << (2u * j); // Collect its two low bits in their standard slot.
447 } // The group now has four high bytes and one low-bit byte.
448 bytes.push_back(low); // Emit the combined low bits.
449 i += 4u; // Continue with the next four real sensor-site values.
450 } else { // Store standard two-sample RAW12 groups.
451 bytes.push_back(samples[i] >> 4u); // Emit the first sample's high eight bits.
452 bytes.push_back(samples[i + 1u] >> 4u); // Emit the second sample's high eight bits.
453 bytes.push_back((samples[i] & 15u) | // Combine both low nibbles.
454 ((samples[i + 1u] & 15u) << 4u)); // Combine both low nibbles.
455 i += 2u; // Continue with the next pair of sensor-site values.
456 } // Every selected packing preserves the quantized real-derived sensor values.
457 } // The complete byte transport is ready for word-oriented shader storage.
458 std::vector<unsigned> words((bytes.size() + 3u) / 4u, // Allocate zero-padded uint storage words.
459 0u); // Allocate zero-padded uint storage words.
460 for (unsigned i = 0; i < bytes.size(); ++i) { // Assemble all transport bytes explicitly.
461 words[i / 4u] |= // Place each byte in its little-endian word slot.
462 bytes[i] << (8u * (i % 4u)); // Place each byte in its little-endian word slot.
463 } // Place each encoded byte at its explicit little-endian word position.
464 return words; // Supply exactly the words consumed by the native unpack stage.
465} // Derive each Bayer mosaic and its transport packing from the public RGB image.
466void imaging( // Evaluate all Bayer formats and lossless compression with the public color image.
467 runner& // Evaluate all Bayer formats and lossless compression with the public color image.
468 run) { // Evaluate all Bayer formats and lossless compression with the public color image.
469 const auto rgb = data::bytes("earth_rgb.bin"); // Read original real RGB8 fixture values.
470 const unsigned depths[] = { // Match Bayer8, Bayer16, RAW10 and RAW12 packing modes.
471 8u, 16u, 10u, 12u}; // Match Bayer8, Bayer16, RAW10 and RAW12 packing modes.
472 for (unsigned packing = 0; packing < 4u; // Evaluate every supported sample transport layout.
473 ++packing) { // Evaluate every supported sample transport layout.
474 for (unsigned pattern = 0; pattern < 4u; // Evaluate every supported Bayer color phase.
475 ++pattern) { // Evaluate every supported Bayer color phase.
476 const unsigned maximum = // Calculate this transport format's full-scale value.
477 (1u << depths[packing]) - 1u; // Calculate this transport format's full-scale value.
478 std::vector<unsigned> mosaic(count); // Keep one sampled channel per real image pixel.
479 for (unsigned y = 0; y < height; // Visit every image row in the derived Bayer mosaic.
480 ++y) { // Visit every image row in the derived Bayer mosaic.
481 for (unsigned x = 0; x < width; // Select the channel measured at each sensor site.
482 ++x) { // Select the channel measured at each sensor site.
483 const unsigned i = y * width + x; // Locate the real RGB pixel.
484 mosaic[i] = // Quantize the sampled real color into the selected sensor depth.
485 (unsigned(rgb[3u * i + bayer_channel(x, y, pattern)]) * maximum + 127u) / // Quantize the sampled real color into the selected sensor depth.
486 255u; // Quantize the sampled real color into the selected sensor depth.
487 } // All sensor sites in this row have real image-derived samples.
488 } // The guide identifies this exact synthetic sampling of a real photograph.
489 auto packed = data::resource( // Bind the original packed sensor transport words.
490 pack_bayer(mosaic, packing)); // Bind the original packed sensor transport words.
491 shdr::buffer<unsigned> decoded(count); // Allocate exact unpacked sensor samples.
492 shdr::buffer<vec4> colors(count); // Allocate interpolated real image colors.
493 shdr::buffer<float> gray(count); // Allocate the final luminance image.
494 auto unpack = // Create the transport decoding stage.
495 shdr::make_kernel("bayer_unpack_kernel"); // Create the transport decoding stage.
496 auto debayer = // Create the native color interpolation stage.
497 shdr::make_kernel("debayer_kernel"); // Create the native color interpolation stage.
498 auto luma = // Create the luminance conversion stage.
499 shdr::make_kernel("luminance_kernel"); // Create the luminance conversion stage.
500 unpack->bind("packed_words", packed) // Connect the packed_words shader port to packed storage.
501 .bind("samples", decoded) // Connect the samples shader port to decoded storage.
502 .set("count", count) // Supply the count processing parameter.
503 .set("packing", packing) // Supply the packing processing parameter.
504 .set("bit_depth", depths[packing]); // Decode the selected real-derived transport.
505 debayer->bind("samples", decoded) // Connect the samples shader port to decoded storage.
506 .bind("colors", colors) // Connect the colors shader port to colors storage.
507 .set("width", width) // Supply the width processing parameter.
508 .set("height", height) // Supply the height processing parameter.
509 .set("pattern", pattern) // Supply the pattern processing parameter.
510 .set("bit_depth", depths[packing]); // Interpolate the selected Bayer phase.
511 luma->bind("colors", colors) // Connect the colors shader port to colors storage.
512 .bind("gray", gray) // Connect the gray shader port to gray storage.
513 .set("count", count); // Produce normalized luminance from interpolated colors.
514 const auto stages = // Link the complete image transport pipeline.
515 shdr::dispatch(*unpack, shdr::extent(count / 64u)) >> // Dispatch the unpack stage with its configured geometry.
516 shdr::dispatch(*debayer, shdr::extent(width / 8u, height / 8u)) >> // Dispatch the debayer stage with its configured geometry.
517 shdr::dispatch( // Dispatch this configured shader with the following work group geometry.
518 *luma,
519 shdr::extent(count / 64u)); // Link the complete image transport pipeline.
520 data::checked_output<unsigned> decoded_check( // Keep the complete stage output for the backend comparison.
521 decoded, 0.0); // Require exact unpacked sensor words.
522 data::checked_output<vec4> color_check( // Keep the complete stage output for the backend comparison.
523 colors); // Compare all interpolated RGBA components.
524 data::checked_output<float> gray_check(gray); // Compare final luminance values.
525 data::evaluate( // Compare the complete real-data pipeline outputs on CPU and the requested GPU backend.
526 run, // Select the same execution backend for this complete case.
527 "NASA derived Bayer packing " + std::to_string(packing) + " phase " + // Use the exact diagnostic or comparison label for this case.
528 std::to_string(pattern), // Validate all sixteen real-image transport combinations.
529 stages, // Run the complete processing sequence in dependency order.
530 {&decoded_check, // Validate all sixteen real-image transport combinations.
531 &color_check, // Validate all sixteen real-image transport combinations.
532 &gray_check}); // Validate all sixteen real-image transport combinations.
533 const unsigned* actual = static_cast<const shdr::buffer<unsigned>&>(decoded) // Read the exact unpacked samples.
534 .data(); // Read the exact unpacked samples.
535 if (!std::equal( // Compare with independently packed original sensor-site values.
536 mosaic.begin(), // Compare with independently packed original sensor-site values.
537 mosaic.end(), // Compare with independently packed original sensor-site values.
538 actual)) { // Compare with independently packed original sensor-site values.
539 throw shdr::error( // Report the failed format, numerical or resource precondition.
540 "real image Bayer transport was not lossless"); // Report an incorrect sample representation.
541 } // Packing and unpacking preserved every derived sensor value exactly.
542 } // All four color phases have been processed for this format.
543 } // Every Bayer depth and phase receives real-image functional coverage.
544 std::vector<unsigned> pixels( // Compress original public RGB bytes as portable uint samples.
545 rgb.begin(), rgb.end()); // Compress original public RGB bytes as portable uint samples.
546 const unsigned samples = static_cast<unsigned>(pixels.size()), block_size = 64u, // Define fixed-size independent compression tiles.
547 blocks = (samples + block_size - 1u) / // Define fixed-size independent compression tiles.
548 block_size; // Define fixed-size independent compression tiles.
549 auto input = data::resource(pixels); // Bind original real image color bytes.
550 shdr::buffer<unsigned> sizes(blocks), offsets(blocks + 1u), encoded(samples * 2u), // Allocate bounded lossless codec resources.
551 restored(samples); // Allocate bounded lossless codec resources.
552 auto counter = // Count run pairs in independent input tiles.
553 shdr::make_kernel("rle_count_kernel"); // Count run pairs in independent input tiles.
554 auto prefix = // Place each tile's compact encoded interval.
555 shdr::make_kernel("rle_offsets_kernel"); // Place each tile's compact encoded interval.
556 auto encoder = shdr::make_kernel("rle_encode_kernel"); // Emit the lossless value/count pairs.
557 auto decoder = shdr::make_kernel( // Reconstruct original image samples from those pairs.
558 "rle_decode_kernel"); // Reconstruct original image samples from those pairs.
559 counter->bind("values", input) // Connect the values shader port to input storage.
560 .bind("sizes", sizes) // Connect the sizes shader port to sizes storage.
561 .set("count", samples) // Supply the count processing parameter.
562 .set("block_size", block_size); // Count runs using real image bytes.
563 prefix->bind("sizes", sizes) // Connect the sizes shader port to sizes storage.
564 .bind("offsets", offsets) // Connect the offsets shader port to offsets storage.
565 .set("blocks", blocks); // Assign deterministic encoded offsets.
566 encoder->bind("values", input) // Connect the values shader port to input storage.
567 .bind("offsets", offsets) // Connect the offsets shader port to offsets storage.
568 .bind("encoded", encoded) // Connect the encoded shader port to encoded storage.
569 .set("count", samples) // Supply the count processing parameter.
570 .set("block_size", block_size); // Encode the original data into disjoint intervals.
571 decoder->bind("encoded", encoded) // Connect the encoded shader port to encoded storage.
572 .bind("offsets", offsets) // Connect the offsets shader port to offsets storage.
573 .bind("restored", restored) // Connect the restored shader port to restored storage.
574 .set("count", samples) // Supply the count processing parameter.
575 .set("block_size", block_size); // Decode the exact image byte sequence.
576 const shdr::extent groups((blocks + 63u) / 64u); // Cover every independent compression tile.
577 const auto stages = // Link count, prefix, encode and decode stages.
578 shdr::dispatch(*counter, groups) >> shdr::dispatch(*prefix, shdr::extent(1)) >> // Dispatch the counter stage with its configured geometry.
579 shdr::dispatch(*encoder, groups) >> // Dispatch the encoder stage with its configured geometry.
580 shdr::dispatch(*decoder, groups); // Link count, prefix, encode and decode stages.
581 data::checked_output<unsigned> sizes_check(sizes, 0.0), offsets_check(offsets, 0.0), // Keep the complete stage output for the backend comparison.
582 restored_check(restored, // Compare compact layout and every reconstructed byte exactly.
583 0.0); // Compare compact layout and every reconstructed byte exactly.
584 data::evaluate(run, // Compare the complete real-data pipeline outputs on CPU and the requested GPU backend.
585 "NASA tiled lossless RLE image roundtrip", // Use the exact diagnostic or comparison label for this case.
586 stages, // Run the complete processing sequence in dependency order.
587 {&sizes_check, // Require identical CPU and GPU lossless processing.
588 &offsets_check, // Require identical CPU and GPU lossless processing.
589 &restored_check}); // Require identical CPU and GPU lossless processing.
590 if (!std::equal( // Compare decoded values with the original real photograph bytes.
591 pixels.begin(), // Compare decoded values with the original real photograph bytes.
592 pixels.end(), // Compare decoded values with the original real photograph bytes.
593 static_cast<const shdr::buffer<unsigned>&>(restored) // Compare decoded values with the original real photograph bytes.
594 .data())) { // Compare decoded values with the original real photograph bytes.
595 throw shdr::error( // Report the failed format, numerical or resource precondition.
596 "public image lossless compression roundtrip differs"); // Reject data loss even if both backends agree.
597 } // RLE is correct whether this real image compresses well or expands.
598 std::cout << "NASA RLE encoded words: " // Report the completed real-data processing check.
599 << static_cast<const shdr::buffer<unsigned>&>(offsets).data()[blocks] << " from " // Report actual real-image compression behavior.
600 << samples << " input words\n"; // Report actual real-image compression behavior.
601} // Report the exact RLE round trip and encoded size for the original image bytes.
602void signals( // Analyze the two recorded ECG channels with transform and filter comparisons.
603 runner& // Analyze the two recorded ECG channels with transform and filter comparisons.
604 run) { // Analyze the two recorded ECG channels with transform and filter comparisons.
605 const auto samples = // Read the documented PhysioNet record 100 waveform prefix.
606 data::floats("signal_ecg.bin"); // Read the documented PhysioNet record 100 waveform prefix.
607 const unsigned length = 2048u, // Match the channel-major fixture shape and radix-two FFT length.
608 channels = 2u; // Match the channel-major fixture shape and radix-two FFT length.
609 if (samples.size() != length * channels) { // Require two complete waveform channels.
610 throw shdr::error( // Report the failed format, numerical or resource precondition.
611 "ECG fixture must contain two channels of 2048 float32 samples"); // Explain a preparation mismatch.
612 } // The waveform's physical sample rate is 360 hertz.
613 std::vector<vec2> complex_samples( // Store original real samples in portable complex FFT layout.
614 samples.size()); // Store original real samples in portable complex FFT layout.
615 for (unsigned i = 0; i < samples.size(); ++i) { // Copy every published waveform value.
616 complex_samples[i] = vec2( // Preserve real amplitude and set the imaginary component to zero.
617 samples[i], 0.0f); // Preserve real amplitude and set the imaginary component to zero.
618 } // Both channels remain independent throughout signal processing.
619 auto input = // Share the same real ECG input between spectral cases.
620 data::resource(complex_samples); // Share the same real ECG input between spectral cases.
621 shdr::buffer<vec2> spectrum(samples.size()), reconstructed(samples.size()), // Allocate reusable spectral outputs.
622 output(samples.size()), windowed(samples.size()); // Allocate reusable spectral outputs.
623 shdr_examples::fft1d forward( // Plan the unscaled batched forward FFT.
624 input, spectrum, length, channels); // Plan the unscaled batched forward FFT.
625 shdr_examples::fft1d inverse( // Plan the normalized inverse FFT.
626 spectrum, reconstructed, length, channels, true); // Plan the normalized inverse FFT.
627 shdr::pipeline transform; // Build a complete spectral roundtrip before execution.
628 forward.append_to(transform); // Produce each channel's independent spectrum.
629 inverse.append_to(transform); // Reconstruct each original real waveform from that spectrum.
630 data::checked_output<vec2> spectrum_check(spectrum, 0.0005), // Keep the complete stage output for the backend comparison.
631 reconstructed_check(reconstructed); // Compare every complex output value.
632 data::evaluate(run, // Compare the complete real-data pipeline outputs on CPU and the requested GPU backend.
633 "PhysioNet ECG 1D FFT/inverse", // Use the exact diagnostic or comparison label for this case.
634 transform, // Validate real spectral processing on both backends.
635 {&spectrum_check, // Validate real spectral processing on both backends.
636 &reconstructed_check}); // Validate real spectral processing on both backends.
637 const vec2* restored = static_cast<const shdr::buffer<vec2>&>(reconstructed) // Read the completed inverse-transform waveform.
638 .data(); // Read the completed inverse-transform waveform.
639 for (unsigned i = 0; i < samples.size(); // Check every original real waveform value independently.
640 ++i) { // Check every original real waveform value independently.
641 shdr_examples::require_close(restored[i].x, // Verify normalized amplitude recovery.
642 samples[i], // Verify normalized amplitude recovery.
643 "real ECG FFT reconstruction", // Use the exact diagnostic or comparison label for this case.
644 0.0001f); // Verify normalized amplitude recovery.
645 shdr_examples::require_close(restored[i].y, // Verify a real-valued roundtrip.
646 0.0f, // Verify a real-valued roundtrip.
647 "real ECG FFT imaginary residue", // Use the exact diagnostic or comparison label for this case.
648 0.0001f); // Verify a real-valued roundtrip.
649 } // The spectral representation preserves the original ECG input.
650 for (unsigned mode = 0; mode < 4u; // Test every block-frequency filtering response on real waveform data.
651 ++mode) { // Test every block-frequency filtering response on real waveform data.
652 shdr_examples::fft_filter filter(input, // Use explicit physical frequency parameters.
653 output, // Write results into the separate output resource.
654 length, // Use the validated per-channel block length.
655 channels, // Keep every channel independent.
656 360.0f, // Use explicit physical frequency parameters.
657 static_cast<shdr_examples::frequency_filter>(mode), // Use explicit physical frequency parameters.
658 5.0f, // Use explicit physical frequency parameters.
659 40.0f, // Use explicit physical frequency parameters.
660 2.0f); // Use explicit physical frequency parameters.
661 data::checked_output<vec2> output_check( // Keep the complete stage output for the backend comparison.
662 output); // Compare complete reconstructed filtered waveforms.
663 data::evaluate( // Compare the complete real-data pipeline outputs on CPU and the requested GPU backend.
664 run, // Select the same execution backend for this complete case.
665 "PhysioNet ECG FFT filter " + std::to_string(mode), // Use the exact diagnostic or comparison label for this case.
666 filter.processing_pipeline(), // Validate low, high, band and notch responses on both backends.
667 {&output_check}); // Validate low, high, band and notch responses on both backends.
668 } // Known-tone tests separately check the analytic frequency-response amplitudes.
669 auto window = // Create the dynamic amplitude-window stage.
670 shdr::make_kernel("signal_window_kernel"); // Create the dynamic amplitude-window stage.
671 window->bind("input_values", input) // Connect the input_values shader port to input storage.
672 .bind("output_values", windowed) // Connect the output_values shader port to windowed storage.
673 .set("block_length", length) // Supply the per-channel block length.
674 .set("channels", channels); // Link the original ECG channels to weighted samples.
675 shdr_examples::fft1d weighted_fft( // Plan spectral analysis of the weighted real waveform.
676 windowed, // Plan spectral analysis of the weighted real waveform.
677 spectrum, // Read the completed complex frequency bins.
678 length, // Use the validated per-channel block length.
679 channels); // Plan spectral analysis of the weighted real waveform.
680 shdr::pipeline weighted( // Begin with per-sample amplitude weighting.
681 shdr::dispatch(*window, // Dispatch the window stage with its configured geometry.
682 shdr::extent((length * channels + 63u) / // Begin with per-sample amplitude weighting.
683 64u))); // Begin with per-sample amplitude weighting.
684 weighted_fft.append_to(weighted); // Keep window output on the same backend as the transform.
685 for (unsigned mode = 0; mode < 4u; // Compare rectangular, Hann, Hamming and Blackman analysis windows.
686 ++mode) { // Compare rectangular, Hann, Hamming and Blackman analysis windows.
687 window->set("mode", mode); // Select the next documented window formula.
688 data::checked_output<vec2> window_check(windowed), // Keep the complete stage output for the backend comparison.
689 weighted_check(spectrum, 0.0005); // Compare both weighted input and its spectrum.
690 data::evaluate(run, // Compare the complete real-data pipeline outputs on CPU and the requested GPU backend.
691 "PhysioNet ECG spectral window " + std::to_string(mode), // Use the exact diagnostic or comparison label for this case.
692 weighted, // Validate the linked analysis stages.
693 {&window_check, &weighted_check}); // Validate the linked analysis stages.
694 } // Each supported window now receives real waveform input.
695 auto scalar_input = // Preserve original real samples for causal time-domain filtering.
696 data::resource(samples); // Preserve original real samples for causal time-domain filtering.
697 shdr::buffer<float> filtered(samples.size()); // Allocate channel-major real filter output.
698 for (unsigned mode = 0; mode < 5u; // Exercise every public FIR design, including moving average.
699 ++mode) { // Exercise every public FIR design, including moving average.
700 const auto selected = static_cast<shdr_examples::filter_mode>( // Select the documented finite-impulse-response type.
701 mode); // Select the documented finite-impulse-response type.
702 const double lower = // Select the cutoff frequencies used by this waveform-processing demonstration.
703 mode == 3u ? 48.0 // Select the cutoff frequencies used by this waveform-processing demonstration.
704 : mode == 0u // Select the cutoff frequencies used by this waveform-processing demonstration.
705 ? 40.0 // Select the cutoff frequencies used by this waveform-processing demonstration.
706 : 5.0; // Select the cutoff frequencies used by this waveform-processing demonstration.
707 const double upper = // Define the upper edge for band and notch designs.
708 mode == 3u ? 52.0 : 40.0; // Define the upper edge for band and notch designs.
709 shdr_examples::fir_pipeline filter(scalar_input, // Design and link a causal 65-tap filter.
710 filtered, // Design and link a causal 65-tap filter.
711 length, // Use the validated per-channel block length.
712 channels, // Keep every channel independent.
713 selected, // Design and link a causal 65-tap filter.
714 360.0, // Design and link a causal 65-tap filter.
715 lower, // Design and link a causal 65-tap filter.
716 upper, // Design and link a causal 65-tap filter.
717 65u); // Design and link a causal 65-tap filter.
718 data::checked_output<float> check(filtered); // Compare every filtered real sample.
719 data::evaluate(run, // Compare the complete real-data pipeline outputs on CPU and the requested GPU backend.
720 "PhysioNet ECG FIR filter " + std::to_string(mode), // Use the exact diagnostic or comparison label for this case.
721 filter.processing_pipeline(), // Evaluate all finite-impulse-response modes.
722 {&check}); // Evaluate all finite-impulse-response modes.
723 } // Independent impulse and tone references still test the coefficient mathematics.
724 for (unsigned mode = 0; mode < 4u; // Exercise each recursive low, high, band and notch design.
725 ++mode) { // Exercise each recursive low, high, band and notch design.
726 const auto selected = static_cast<shdr_examples::filter_mode>( // Select the matching public biquad mode.
727 mode); // Select the matching public biquad mode.
728 shdr_examples::biquad_pipeline filter( // Set an explicit sampling rate, frequency and quality factor.
729 scalar_input, // Set an explicit sampling rate, frequency and quality factor.
730 filtered, // Set an explicit sampling rate, frequency and quality factor.
731 length, // Use the validated per-channel block length.
732 channels, // Keep every channel independent.
733 selected, // Set an explicit sampling rate, frequency and quality factor.
734 360.0, // Set an explicit sampling rate, frequency and quality factor.
735 mode < 2u ? 40.0 : 50.0, // Set an explicit sampling rate, frequency and quality factor.
736 0.7071067811865476); // Set an explicit sampling rate, frequency and quality factor.
737 data::checked_output<float> check( // Keep the complete stage output for the backend comparison.
738 filtered); // Compare the complete stateful waveform output.
739 data::checked_output<vec2> state_check( // Keep the complete stage output for the backend comparison.
740 filter.state); // Compare both delay values for every independent channel.
741 data::evaluate(run, // Compare the complete real-data pipeline outputs on CPU and the requested GPU backend.
742 "PhysioNet ECG biquad filter " + std::to_string(mode), // Use the exact diagnostic or comparison label for this case.
743 filter.processing_pipeline(), // Restore identical zero history before each backend run.
744 {&check, &state_check}, // Restore identical zero history before each backend run.
745 [&filter]() { // Restore identical zero history before each backend run.
746 filter.reset_state(); // Restore identical zero history before each backend run.
747 }); // Restore identical zero history before each backend run.
748 } // Existing chunked tests separately validate state carried across successive waveform batches.
749} // Complete the waveform reconstruction and filter-response comparisons.
750std::vector<uvec4> decode_reference( // Interpret sensor words sequentially as an independent real-recording oracle.
751 const std::vector<std::uint16_t>& // Interpret sensor words sequentially as an independent real-recording oracle.
752 words) { // Interpret sensor words sequentially as an independent real-recording oracle.
753 unsigned y = 0u, base = 0u, polarity = 0u, high = 0u, low = 0u, // Track the official scalar EVT3 stream state.
754 wraps = 0u; // Track the official scalar EVT3 stream state.
755 bool synchronized = false; // Suppress event emission until a TIME_HIGH word establishes time.
756 std::vector<uvec4> events; // Preserve exact event order from the public sensor recording.
757 const auto append = // Form timestamps independently of parallel block summaries.
758 [&](unsigned x, // Form timestamps independently of parallel block summaries.
759 unsigned packed_y) { // Form timestamps independently of parallel block summaries.
760 const std::uint64_t time = std::uint64_t(wraps) * 16777216u + // Reconstruct extended time in wide host arithmetic.
761 std::uint64_t(high) * 4096u + // Reconstruct extended time in wide host arithmetic.
762 low; // Reconstruct extended time in wide host arithmetic.
763 events.push_back( // Append one exact coordinate and timestamp record.
764 uvec4(x, // Append one exact coordinate and timestamp record.
765 packed_y, // Append one exact coordinate and timestamp record.
766 unsigned(time), // Append one exact coordinate and timestamp record.
767 unsigned(time >> 32u))); // Append one exact coordinate and timestamp record.
768 }; // All event types share the same reconstructed time base.
769 for (std::uint16_t word : words) { // Read the original recording prefix in physical order.
770 const unsigned type = word >> 12u, // Separate the protocol tag from its twelve-bit payload.
771 value = // Separate the protocol tag from its twelve-bit payload.
772 word & 4095u; // Separate the protocol tag from its twelve-bit payload.
773 if (type == 0u) { // Update row and camera context.
774 y = value; // Preserve the complete row context for later CD events.
775 } else if (type == 3u) { // Update the vector's base X coordinate and polarity.
776 base = value & 2047u; // Keep the eleven-bit pixel coordinate.
777 polarity = value >> 11u; // Keep the vector's polarity bit.
778 } else if (type == 6u) { // Update the low timestamp field.
779 low = value; // Keep all twelve low timestamp bits.
780 } else if (type == 8u) { // Establish or advance the high timestamp field.
781 if (synchronized && high > value && // Detect a genuine high-time wrap using the protocol tolerance.
782 high - value >= // Detect a genuine high-time wrap using the protocol tolerance.
783 4085u) { // Detect a genuine high-time wrap using the protocol tolerance.
784 ++wraps; // Extend time beyond the twenty-four-bit hardware timestamp period.
785 } // Retain the current wrap count for small backward high-word changes.
786 high = value; // Store the current high timestamp field.
787 low = 0u; // A TIME_HIGH word resets low time until the next TIME_LOW update.
788 synchronized = true; // Permit event emission after a valid time base has arrived.
789 } else if (type == 2u && synchronized) { // Emit a direct CD event with its own polarity.
790 append(value & 2047u, // Preserve coordinate, camera and polarity.
791 (y & 2047u) | ((value >> 11u) << 11u) | // Preserve coordinate, camera and polarity.
792 ((y >> 11u) << 12u)); // Preserve coordinate, camera and polarity.
793 } else if (type == 4u || type == 5u) { // Expand a twelve-bit or eight-bit event vector.
794 const unsigned bits = // Respect the protocol's meaningful mask width.
795 type == 4u ? 12u : 8u; // Respect the protocol's meaningful mask width.
796 for (unsigned bit = 0; bit < bits; // Visit each potential CD event in the vector mask.
797 ++bit) { // Visit each potential CD event in the vector mask.
798 if (synchronized && // Emit only set mask bits after time synchronization.
799 (value & (1u << bit))) { // Emit only set mask bits after time synchronization.
800 append((base + bit) & 2047u, // Restore every original vector event.
801 (y & 2047u) | (polarity << 11u) | // Restore every original vector event.
802 ((y >> 11u) << 12u)); // Restore every original vector event.
803 } // Emit one event for each selected mask bit.
804 } // Vector events remain in increasing X-bit order.
805 base = (base + bits) & 2047u; // Advance the vector base by the complete encoded mask width.
806 } else if (type == 10u && // Preserve external trigger records in their original order.
807 synchronized) { // Preserve external trigger records in their original order.
808 append(value >> 8u, // Tag the trigger while retaining channel and edge.
809 0x80000000u | (value & 1u)); // Tag the trigger while retaining channel and edge.
810 } // Finish handling this word according to its event type and synchronization state.
811 } // All original sensor words have been interpreted once.
812 return events; // Return the complete independent event reference for this prefix.
813} // Interpret the EVT3 format sequentially as an independent decoder reference.
814void events(runner& run) { // Decode and process an actual public Prophesee EVT3 recording prefix.
815 const auto words = // Read the exact prepared sensor-word sequence.
816 data::words("evt3_words.bin"); // Read the exact prepared sensor-word sequence.
817 const auto expected = // Build an independent scalar reference for every emitted event.
818 decode_reference(words); // Build an independent scalar reference for every emitted event.
819 if (expected.empty()) { // Require real CD or trigger data after time synchronization.
820 throw shdr::error( // Report the failed format, numerical or resource precondition.
821 "public EVT3 fixture contains no synchronized events"); // Reject an unusable prefix.
822 } // The fixture now has a meaningful independent output reference.
823 shdr_examples::evt3_decoder decoder(static_cast<unsigned>( // Allocate bounded reusable parallel decode resources.
824 words.size())); // Allocate bounded reusable parallel decode resources.
825 decoder.load(words); // Copy the unmodified sensor words into packed shader storage.
826 shdr::buffer<vec4> flow( // Allocate only the actual meaningful decoded event count.
827 expected.size()); // Allocate only the actual meaningful decoded event count.
828 auto fit = shdr::make_kernel("evt3_flow_kernel"); // Create the normal-flow plane-fit stage.
829 fit->bind("events", decoder.events) // Fit bounded recent same-polarity neighborhoods.
830 .bind("offsets", decoder.offsets) // Fit bounded recent same-polarity neighborhoods.
831 .bind("flow", flow) // Connect the flow shader port to flow storage.
832 .set("blocks", decoder.blocks()) // Supply the blocks processing parameter.
833 .set("radius", 5.0f) // Supply the radius processing parameter.
834 .set("history", 64u) // Supply the history processing parameter.
835 .set("time_window", 10000u); // Fit bounded recent same-polarity neighborhoods.
836 shdr::pipeline stages = // Reset stream state before independently decoding this prefix.
837 decoder.reset_pipeline(); // Reset stream state before independently decoding this prefix.
838 stages += shdr::dispatch( // Dispatch this configured shader with the following work group geometry.
839 *fit,
840 shdr::extent((static_cast<unsigned>(expected.size()) + 63u) / // Feed the decoder's event buffer directly to the flow stage.
841 64u)); // Feed the decoder's event buffer directly to the flow stage.
842 data::checked_output<uvec4> event_check( // Keep the complete stage output for the backend comparison.
843 decoder.events, // Require exact ordered coordinates and timestamp words.
844 0.0, // Require exact ordered coordinates and timestamp words.
845 expected.size()); // Require exact ordered coordinates and timestamp words.
846 data::checked_output<unsigned> offsets_check( // Keep the complete stage output for the backend comparison.
847 decoder.offsets, 0.0); // Require identical compact event intervals and final count.
848 data::checked_output<vec4> flow_check( // Keep the complete stage output for the backend comparison.
849 flow, 0.02); // Allow float plane-fit conditioning while comparing complete flow outputs.
850 data::evaluate(run, // Compare the complete real-data pipeline outputs on CPU and the requested GPU backend.
851 "Prophesee real EVT3 decode/normal-flow pipeline", // Use the exact diagnostic or comparison label for this case.
852 stages, // Run the complete processing sequence in dependency order.
853 {&event_check, // Compare the full real decode and processing chain.
854 &offsets_check, // Compare the full real decode and processing chain.
855 &flow_check}); // Compare the full real decode and processing chain.
856 if (decoder.event_count() != // Check parallel compact length against the independent sequential decoder.
857 expected // Use the independently calculated expected values.
858 .size()) { // Check parallel compact length against the independent sequential decoder.
859 throw shdr::error( // Report the failed format, numerical or resource precondition.
860 "real EVT3 output count differs from scalar protocol reference"); // Reject a common output-length error.
861 } // Every actual event has a matching independent reference record.
862 const uvec4* actual = static_cast<const shdr::buffer<uvec4>&>(decoder.events) // Read completed decoded event records.
863 .data(); // Read completed decoded event records.
864 if (!std::equal(expected.begin(), // Compare each coordinate, metadata word and extended timestamp.
865 expected.end(), // Compare each coordinate, metadata word and extended timestamp.
866 actual)) { // Compare each coordinate, metadata word and extended timestamp.
867 throw shdr::error( // Report the failed format, numerical or resource precondition.
868 "real EVT3 ordered events differ from scalar protocol reference"); // Report any protocol interpretation error.
869 } // Backend agreement also matches independent protocol decoding.
870 const auto compact = shdr_examples::evt3_compact_cd_to64( // Exercise the CPU consumer of compact sixty-four-bit CD words.
871 decoder.events, // Exercise the CPU consumer of compact sixty-four-bit CD words.
872 decoder.event_count()); // Exercise the CPU consumer of compact sixty-four-bit CD words.
873 const vec4* fitted = // Read completed normal-flow records.
874 static_cast<const shdr::buffer<vec4>&>(flow).data(); // Read completed normal-flow records.
875 unsigned valid = 0u; // Count neighborhoods that support a nonsingular plane fit.
876 for (unsigned i = 0; i < expected.size(); // Inspect the full real-event processing output.
877 ++i) { // Inspect the full real-event processing output.
878 valid += fitted[i].w > 0.5f // Count only fits accepted by the native shader's validity rule.
879 ? 1u // Count only fits accepted by the native shader's validity rule.
880 : 0u; // Count only fits accepted by the native shader's validity rule.
881 if (i % 97u == 0u && // Sample accepted real fits for an independent double-precision check.
882 fitted[i].w > // Sample accepted real fits for an independent double-precision check.
883 0.5f) { // Sample accepted real fits for an independent double-precision check.
884 const vec4 oracle = shdr_examples::detail::evt3_reference_flow( // Solve the same documented neighborhood through independent elimination.
885 expected, // Use the independently calculated expected values.
886 i, // Solve the same documented neighborhood through independent elimination.
887 5.0f, // Solve the same documented neighborhood through independent elimination.
888 64u, // Use this fixed documented processing parameter.
889 10000u); // Solve the same documented neighborhood through independent elimination.
890 if (oracle.w > 0.5f && // Compare valid nonsingular fits with a conditioning-aware error limit.
891 data::difference(fitted[i], oracle) > // Compare valid nonsingular fits with a conditioning-aware error limit.
892 0.05) { // Compare valid nonsingular fits with a conditioning-aware error limit.
893 throw shdr::error( // Report the failed format, numerical or resource precondition.
894 "real event flow differs from independent plane-fit reference"); // Reject an incorrect real processing result.
895 } // Validate this sampled accepted fit against the independent timestamp-plane oracle.
896 } // Existing synthetic plane tests separately check exact known motion values.
897 } // Every returned record has already received a finite-value and backend check.
898 std::cout << "Prophesee recording: " << expected.size() << " decoded events, " << compact.size() // Report the completed real-data processing check.
899 << " compact CD words, " << valid // Record useful real-data output counts.
900 << " accepted normal-flow fits\n"; // Record useful real-data output counts.
901} // The guide explains the aperture ambiguity and absence of annotated flow ground truth.
902void neural(runner& run) { // Train the small neural examples on real MNIST zero-versus-one images.
903 const auto images = // Read the documented balanced training subset.
904 data::bytes("mnist_train.bin"); // Read the documented balanced training subset.
905 const auto labels = data::bytes( // Read the corresponding original MNIST training labels.
906 "mnist_train_labels.bin"); // Read the corresponding original MNIST training labels.
907 if (images.size() != 128u * 784u || // Require the documented prepared subset shape.
908 labels.size() != 128u) { // Require the documented prepared subset shape.
909 throw shdr::error( // Report the failed format, numerical or resource precondition.
910 "binary MNIST training fixtures must contain 128 image-label pairs"); // Reject mismatched real training data.
911 } // The guide distinguishes this small training demonstration from held-out LeNet evaluation.
912 const unsigned batch = // Keep real analytic-gradient checks small enough for all supported devices.
913 32u; // Keep real analytic-gradient checks small enough for all supported devices.
914 std::vector<unsigned> // Preserve original fixture order while selecting a balanced batch.
915 selected_indices; // Preserve original fixture order while selecting a balanced batch.
916 unsigned selected_counts[2] = {0u, // Count selected real examples for each binary digit class.
917 0u}; // Count selected real examples for each binary digit class.
918 for (unsigned index = 0; index < labels.size(); // Scan the original-order training fixture labels.
919 ++index) { // Scan the original-order training fixture labels.
920 if (labels[index] > 1u) { // Require the documented zero-versus-one subset.
921 throw shdr::error( // Report the failed format, numerical or resource precondition.
922 "binary MNIST fixture contains another digit class"); // Reject an incompatible prepared subset.
923 } // Original zero and one labels can now be balanced explicitly.
924 if (selected_counts[labels[index]] < // Include only the first sixteen examples of each class.
925 16u) { // Include only the first sixteen examples of each class.
926 selected_indices.push_back(index); // Retain the corresponding original image position.
927 ++selected_counts[labels[index]]; // Record this selected real training example.
928 } // Extra examples remain available to other training demonstrations.
929 } // Select the first sixteen images of each digit while preserving original dataset order.
930 if (selected_indices.size() != // Require both classes to have enough original training examples.
931 batch) { // Require both classes to have enough original training examples.
932 throw shdr::error( // Report the failed format, numerical or resource precondition.
933 "binary MNIST fixture lacks sixteen samples of each class"); // Explain a deficient prepared dataset.
934 } // Every neural case will use this exact balanced real mini-batch.
935 for (bool convolutional : // Evaluate both the dense ANN and the compact CNN example.
936 {false, true}) { // Evaluate both the dense ANN and the compact CNN example.
937 shdr_examples::binary_network model( // Allocate the same trainable model used by the individual examples.
938 convolutional, // Allocate the same trainable model used by the individual examples.
939 batch); // Allocate the same trainable model used by the individual examples.
940 float* input = // Replace only the synthetic constructor fixture with real MNIST features.
941 model.inputs // Replace only the synthetic constructor fixture with real MNIST features.
942 .data(); // Replace only the synthetic constructor fixture with real MNIST features.
943 float* targets = // Replace class targets with the original published digit labels.
944 model.targets.data(); // Replace class targets with the original published digit labels.
945 for (unsigned sample = 0; sample < batch; ++sample) { // Select a balanced real mini-batch.
946 const unsigned index = selected_indices // Select one of the first sixteen original examples from each class.
947 [sample]; // Select one of the first sixteen original examples from each class.
948 targets[sample] = float(labels[index]); // Copy the original zero-or-one labels.
949 if (convolutional) { // Supply an eight-by-eight real digit image to the compact CNN.
950 for (unsigned y = 0; y < 8u; // Sample each compact image row at fixed source positions.
951 ++y) { // Sample each compact image row at fixed source positions.
952 for (unsigned x = 0; x < 8u; // Sample each compact image column independently.
953 ++x) { // Sample each compact image column independently.
954 input[sample * 64u + y * 8u + x] = // Use deterministic center-nearest real image reduction.
955 images[index * 784u + ((2u * y + 1u) * 28u / 16u) * 28u + // Use deterministic center-nearest real image reduction.
956 (2u * x + 1u) * 28u / 16u] / // Use deterministic center-nearest real image reduction.
957 255.0f; // Use deterministic center-nearest real image reduction.
958 } // The compact row contains real source intensities.
959 } // The full compact CNN input preserves the documented preprocessing.
960 } else { // Reduce real image geometry into the ANN's two input features.
961 double mass = 0.0, horizontal_spread = 0.0, outer = 0.0, // Accumulate simple real-image stroke features.
962 center = 0.0; // Accumulate simple real-image stroke features.
963 for (unsigned y = 0; y < 28u; ++y) { // Visit every original digit row.
964 for (unsigned x = 0; x < 28u; // Include every original real pixel in feature extraction.
965 ++x) { // Include every original real pixel in feature extraction.
966 const double value = images[index * 784u + y * 28u + x] / // Read normalized original ink intensity.
967 255.0; // Read normalized original ink intensity.
968 mass += value; // Count total real-image stroke mass.
969 horizontal_spread += // Measure horizontal stroke spread about the image center.
970 value * (double(x) - 13.5) * // Measure horizontal stroke spread about the image center.
971 (double(x) - // Measure horizontal stroke spread about the image center.
972 13.5); // Measure horizontal stroke spread about the image center.
973 outer += x < 9u || x >= 19u // Measure ink in the two outer column bands.
974 ? value // Measure ink in the two outer column bands.
975 : 0.0; // Measure ink in the two outer column bands.
976 center += x >= 10u && x < 18u // Measure ink in the central column band.
977 ? value // Measure ink in the central column band.
978 : 0.0; // Measure ink in the central column band.
979 } // All pixels in this real digit row contribute to its features.
980 } // Describe each recorded digit with its measured image features.
981 input[2u * sample] = // Normalize horizontal spread to a small signed feature.
982 float(horizontal_spread / (mass + 1.0) / 30.0 - // Normalize horizontal spread to a small signed feature.
983 0.5); // Normalize horizontal spread to a small signed feature.
984 input[2u * sample + 1u] = // Normalize the outer-versus-center stroke contrast.
985 float((outer - center) / // Normalize the outer-versus-center stroke contrast.
986 (mass + 1.0)); // Normalize the outer-versus-center stroke contrast.
987 } // Both network variants receive deterministic documented real-data preprocessing.
988 } // The same balanced mini-batch is now ready for both backends.
989 model.evaluation_pipeline() // Measure initial real-data training loss before updating parameters.
990 .run(); // Measure initial real-data training loss before updating parameters.
991 const float initial_loss = // Preserve the starting mini-batch cross entropy.
992 static_cast<const shdr::buffer<float>&>(model.loss) // Preserve the starting mini-batch cross entropy.
993 .data()[0]; // Preserve the starting mini-batch cross entropy.
994 shdr::pipeline stages; // Link several complete analytic-gradient and SGD updates.
995 for (unsigned step = 0; step < 32u; // Demonstrate sustained learning on the same small real training batch.
996 ++step) { // Demonstrate sustained learning on the same small real training batch.
997 stages += // Update every trainable parameter with the native dual-compiled kernels.
998 model // Update every trainable parameter with the native dual-compiled kernels.
999 .training_pipeline(); // Update every trainable parameter with the native dual-compiled kernels.
1000 } // Fit the selected training mini-batch and record its cross-entropy change.
1001 stages += model.evaluation_pipeline(); // Produce final predictions and mean cross entropy.
1002 data::checked_output<float> parameters(model.parameters, 0.001), // Keep the complete stage output for the backend comparison.
1003 gradients(model.gradients, 0.002), predictions(model.predictions, 0.001), // Compare complete training and inference resources.
1004 loss(model.loss, 0.001); // Compare complete training and inference resources.
1005 data::evaluate(run, // Compare the complete real-data pipeline outputs on CPU and the requested GPU backend.
1006 convolutional ? "MNIST compact CNN training/inference" // Restore identical initial weights before each backend's updates.
1007 : "MNIST two-feature ANN training/inference", // Restore identical initial weights before each backend's updates.
1008 stages, // Run the complete processing sequence in dependency order.
1009 {¶meters, &gradients, &predictions, &loss}, // Restore identical initial weights before each backend's updates.
1010 [&model]() { // Restore identical initial weights before each backend's updates.
1011 model.reset_parameters(); // Restore identical initial weights before each backend's updates.
1012 }); // Restore identical initial weights before each backend's updates.
1013 const float final_loss = static_cast<const shdr::buffer<float>&>(model.loss) // Read the actual fitted training loss.
1014 .data()[0]; // Read the actual fitted training loss.
1015 if (!(final_loss < // Require a useful learning response on real image-label pairs.
1016 initial_loss)) { // Require a useful learning response on real image-label pairs.
1017 throw shdr::error( // Report the failed format, numerical or resource precondition.
1018 "real MNIST mini-batch training did not reduce cross entropy"); // Catch failed gradient or update behavior.
1019 } // Analytic training has a measurable real-data objective improvement.
1020 std::cout << (convolutional ? "MNIST compact CNN" : "MNIST two-feature ANN") // Report the completed real-data processing check.
1021 << " mini-batch loss: " << initial_loss << " -> " << final_loss // Report the initial and final training mini-batch losses.
1022 << '\n'; // Report the initial and final training mini-batch losses.
1023 } // Both small network variants receive real-data training coverage.
1024 shdr_examples::transformer_model model( // Allocate the existing fully trainable attention classifier.
1025 batch); // Allocate the existing fully trainable attention classifier.
1026 float* tokens = // Replace synthetic constructor sequences with real digit patch features.
1027 model.inputs // Replace synthetic constructor sequences with real digit patch features.
1028 .data(); // Replace synthetic constructor sequences with real digit patch features.
1029 unsigned* targets = model.labels.data(); // Store original zero-or-one digit labels.
1030 for (unsigned sample = 0; sample < batch; // Construct the same balanced real mini-batch as the other neural cases.
1031 ++sample) { // Construct the same balanced real mini-batch as the other neural cases.
1032 const unsigned index = // Select the same balanced original zero and one examples.
1033 selected_indices[sample]; // Select the same balanced original zero and one examples.
1034 targets[sample] = labels[index]; // Preserve original supervised class identities.
1035 for (unsigned row = 0; row < 4u; // Treat each seven-row image band as one sequence token.
1036 ++row) { // Treat each seven-row image band as one sequence token.
1037 for (unsigned column = 0; column < 4u; // Give each token four seven-column patch features.
1038 ++column) { // Give each token four seven-column patch features.
1039 double total = 0.0; // Accumulate real intensity within this patch.
1040 for (unsigned y = 0; y < 7u; ++y) { // Visit every source row inside the patch.
1041 for (unsigned x = 0; x < 7u; // Visit every source column inside the patch.
1042 ++x) { // Visit every source column inside the patch.
1043 total += images[index * 784u + (row * 7u + y) * 28u + column * 7u + // Include the original real pixel value.
1044 x]; // Include the original real pixel value.
1045 } // The patch row has contributed all of its original pixels.
1046 } // The complete seven-by-seven real patch has been averaged.
1047 tokens[sample * 16u + row * 4u + column] = // Normalize patch mean into a small signed token feature.
1048 float(total / (49.0 * 255.0) * 4.0 - // Normalize patch mean into a small signed token feature.
1049 0.5); // Normalize patch mean into a small signed token feature.
1050 } // All four features of this real-image token are ready.
1051 } // All four tokens represent the original image's coarse spatial layout.
1052 } // Attention receives real image sequences and their original labels.
1053 model.prediction_pipeline().run(); // Measure initial real-data classification loss.
1054 const double initial = model.loss(); // Preserve the starting native attention cross entropy.
1055 shdr::pipeline stages; // Build linked analytic attention updates and final inference.
1056 for (unsigned step = 0; step < 12u; // Keep the real attention training demonstration bounded on small devices.
1057 ++step) { // Keep the real attention training demonstration bounded on small devices.
1058 stages += // Differentiate all 134 parameters and apply averaged SGD.
1059 model.training_pipeline(); // Differentiate all 134 parameters and apply averaged SGD.
1060 } // Every parameter update uses only the single-header resource and shader runtime.
1061 stages += // Produce final probabilities, loss and predicted classes.
1062 model.prediction_pipeline(); // Produce final probabilities, loss and predicted classes.
1063 data::checked_output<float> parameters(model.parameters, 0.003), // Keep the complete stage output for the backend comparison.
1064 gradients(model.gradients, 0.005); // Compare complete learned values and final derivatives.
1065 data::checked_output<vec4> predictions( // Keep the complete stage output for the backend comparison.
1066 model.predictions, 0.003); // Compare probabilities, sample loss and selected class records.
1067 data::evaluate(run, // Compare the complete real-data pipeline outputs on CPU and the requested GPU backend.
1068 "MNIST patch-token transformer training/inference", // Use the exact diagnostic or comparison label for this case.
1069 stages, // Run the complete processing sequence in dependency order.
1070 {¶meters, &gradients, &predictions}, // Restore identical initial trainable weights for independent backend runs.
1071 [&model]() { // Restore identical initial trainable weights for independent backend runs.
1072 model.reset_parameters(); // Restore identical initial trainable weights for independent backend runs.
1073 }); // Restore identical initial trainable weights for independent backend runs.
1074 const double final_loss = model.loss(); // Read the fitted mini-batch cross entropy.
1075 if (!(final_loss < // Require a measurable native training response to the real supervised data.
1076 initial)) { // Require a measurable native training response to the real supervised data.
1077 throw shdr::error( // Report the failed format, numerical or resource precondition.
1078 "real MNIST transformer mini-batch training did not reduce cross entropy"); // Report failed attention training behavior.
1079 } // Measure the loss reduction from analytic transformer training on the selected batch.
1080 std::cout << "MNIST patch-token transformer mini-batch loss: " << initial << " -> " // Report the completed real-data processing check.
1081 << final_loss << '\n'; // Record the actual real-data learning result.
1082} // Separate complete held-out digit evaluation follows with the named deeper model.
1083void lenet( // Evaluate the pretrained named model against independent framework logits and published labels.
1084 runner& // Evaluate the pretrained named model against independent framework logits and published labels.
1085 run) { // Evaluate the pretrained named model against independent framework logits and published labels.
1086 const char* full_directory = std::getenv( // Allow explicit evaluation of the complete optional held-out split.
1087 "SHDR_FULL_MNIST_DIR"); // Allow explicit evaluation of the complete optional held-out split.
1088 const bool full = // Select the full split only when its cache directory was provided.
1089 full_directory && // Select the full split only when its cache directory was provided.
1090 *full_directory; // Select the full split only when its cache directory was provided.
1091 const auto images = // Load original test image bytes in dataset order.
1092 full ? data::bytes("mnist_full.bin", full_directory) // Load original test image bytes in dataset order.
1093 : data::bytes("mnist_test.bin"); // Load original test image bytes in dataset order.
1094 const auto labels = // Load the exact corresponding original labels.
1095 full ? data::bytes("mnist_full_labels.bin", full_directory) // Load the exact corresponding original labels.
1096 : data::bytes("mnist_labels.bin"); // Load the exact corresponding original labels.
1097 const auto parameters = data::floats( // Read documented plain float32 pretrained model weights.
1098 "lenet_weights.bin"); // Read documented plain float32 pretrained model weights.
1099 const auto reference = data::floats( // Read independent PyTorch logits for all ten thousand held-out images.
1100 "lenet_reference.bin"); // Read independent PyTorch logits for all ten thousand held-out images.
1101 if (images.size() != labels.size() * 784u || reference.size() != 10000u * 10u || // Validate all model evaluation shapes.
1102 labels.size() > 10000u) { // Validate all model evaluation shapes.
1103 throw shdr::error( // Report the failed format, numerical or resource precondition.
1104 "MNIST LeNet fixtures have inconsistent image, label or reference counts"); // Reject incomplete or mismatched evaluation data.
1105 } // Images and independent references share original held-out dataset order.
1106 unsigned correct = // Count actual native classifier predictions matching original digit labels.
1107 0u; // Count actual native classifier predictions matching original digit labels.
1108 double maximum = 0.0; // Track the worst difference from independent framework logits.
1109 const auto start = std::chrono::steady_clock:: // Measure completed native evaluation including functional comparison work.
1110 now(); // Measure completed native evaluation including functional comparison work.
1111 for (unsigned offset = 0; offset < labels.size(); // Bound activation storage and dispatch sizes on every target.
1112 offset += 128u) { // Bound activation storage and dispatch sizes on every target.
1113 const unsigned batch = std::min(128u, // Include the final partial held-out batch.
1114 static_cast<unsigned>(labels.size()) - // Include the final partial held-out batch.
1115 offset); // Include the final partial held-out batch.
1116 shdr_examples::lenet_classifier model( // Build the same seven-stage named network for this bounded batch.
1117 batch); // Build the same seven-stage named network for this bounded batch.
1118 model.load_parameters(parameters); // Supply the exact trained weights to both backends.
1119 float* input = // Access native input storage for this held-out image batch.
1120 model.inputs.data(); // Access native input storage for this held-out image batch.
1121 for (unsigned i = 0; i < batch * 784u; ++i) { // Normalize every original held-out pixel.
1122 input[i] = images[offset * 784u + i] / // Match the documented training and reference preprocessing.
1123 255.0f; // Match the documented training and reference preprocessing.
1124 } // First convolution supplies its own two-pixel zero border.
1125 run.run( // Execute the complete named model on the requested CPU or GPU backend.
1126 model // Execute the complete named model on the requested CPU or GPU backend.
1127 .inference_pipeline()); // Execute the complete named model on the requested CPU or GPU backend.
1128 const float* logits = static_cast<const shdr::buffer<float>&>(model.logits) // Read completed native classifier logits.
1129 .data(); // Read completed native classifier logits.
1130 for (unsigned sample = 0; sample < batch; // Evaluate each original held-out digit independently.
1131 ++sample) { // Evaluate each original held-out digit independently.
1132 unsigned prediction = 0u; // Begin argmax with class zero.
1133 for ( // Compare every classifier logit with the independent preparation framework.
1134 unsigned digit = 0; digit < 10u; // Compare every classifier logit with the independent preparation framework.
1135 ++digit) { // Compare every classifier logit with the independent preparation framework.
1136 const float actual = logits[sample * 10u + digit]; // Read this native class score.
1137 const float expected = // Select the matching original test image's reference score.
1138 reference[(offset + sample) * 10u + // Select the matching original test image's reference score.
1139 digit]; // Select the matching original test image's reference score.
1140 maximum = std::max( // Measure the greatest normalized difference from the framework logits.
1141 maximum, // Measure the greatest normalized difference from the framework logits.
1142 data::difference( // Measure the greatest normalized difference from the framework logits.
1143 actual, // Measure the greatest normalized difference from the framework logits.
1144 expected)); // Measure the greatest normalized difference from the framework logits.
1145 if (data::difference(actual, expected) > // Allow float reduction order while preserving a strict inference comparison.
1146 0.0005) { // Allow float reduction order while preserving a strict inference comparison.
1147 throw shdr::error( // Report the failed format, numerical or resource precondition.
1148 "native LeNet logit differs from independent framework reference"); // Report a model layout or numerical implementation error.
1149 } // Native layer wiring matches independent deep inference for this class score.
1150 if (actual > // Apply deterministic first-maximum class selection.
1151 logits[sample * 10u + // Apply deterministic first-maximum class selection.
1152 prediction]) { // Apply deterministic first-maximum class selection.
1153 prediction = digit; // Keep the highest-scoring digit class seen so far.
1154 } // Ties retain the earliest class index.
1155 } // All ten scores have an independent real-data inference reference.
1156 correct += prediction == labels[offset + sample] // Compare the native predicted digit with original ground truth.
1157 ? 1u // Compare the native predicted digit with original ground truth.
1158 : 0u; // Compare the native predicted digit with original ground truth.
1159 } // The full bounded batch contributes measured held-out accuracy.
1160 } // Every selected held-out image has been evaluated exactly once.
1161 const double seconds = std::chrono::duration<double>(std::chrono::steady_clock::now() - start) // Record completed native evaluation duration.
1162 .count(); // Record completed native evaluation duration.
1163 const double accuracy = // Calculate accuracy from correct labels in the official held-out test split.
1164 double(correct) / // Calculate accuracy from correct labels in the official held-out test split.
1165 labels // Calculate accuracy from correct labels in the official held-out test split.
1166 .size(); // Calculate accuracy from correct labels in the official held-out test split.
1167 if (accuracy < // Require the prepared model's useful real held-out behavior on every supported platform.
1168 0.95) { // Require the prepared model's useful real held-out behavior on every supported platform.
1169 throw shdr::error( // Report the failed format, numerical or resource precondition.
1170 "native LeNet held-out accuracy fell below 95 percent"); // Catch a model or dataset preprocessing regression.
1171 } // The check is tied to a real public classification task with original labels.
1172 std::cout << "LeNet MNIST held-out: " << correct << '/' << labels.size() // Report the completed real-data processing check.
1173 << " correct, accuracy " << accuracy * 100.0 << "%, normalized reference error " // Report actual labels, numerical parity and completed runtime.
1174 << maximum << ", completed seconds " << seconds // Report actual labels, numerical parity and completed runtime.
1175 << '\n'; // Report actual labels, numerical parity and completed runtime.
1176} // Offline training remains reproducible and optional for C++ deployment.
1177} // namespace
1178
1179int main(int argc, // Run every real-data algorithm family on the selected supported profile.
1180 char** argv) { // Run every real-data algorithm family on the selected supported profile.
1181 try { // Convert useful validation failures into a nonzero process result.
1182 runner run(argc, // Select ordinary CPU execution or one requested headless GPU profile.
1183 argv); // Select ordinary CPU execution or one requested headless GPU profile.
1184 const auto source = image(); // Read and normalize the same public image once.
1185 basic(run, source); // Check linked, hybrid and asynchronous resources with real data.
1186 images(run, source); // Check image storage and an independently verified box filter.
1187 vision(run, source); // Check the complete real-image corner pipeline.
1188 spectral( // Check two-dimensional Fourier processing and three-dimensional real image stacks.
1189 run, // Select the same execution backend for this complete case.
1190 source); // Check two-dimensional Fourier processing and three-dimensional real image stacks.
1191 imaging( // Check all Bayer formats and lossless compression on real-derived image data.
1192 run); // Check all Bayer formats and lossless compression on real-derived image data.
1193 signals(run); // Check every FFT, window, FIR and biquad mode on a real waveform.
1194 events( // Check exact real event decoding, compact CPU consumption and normal-flow processing.
1195 run); // Check exact real event decoding, compact CPU consumption and normal-flow processing.
1196 neural(run); // Check native analytic training and inference on real digit-image features.
1197 shdr_examples::check_lenet_classifier( // Validate all seven named-model stages against an independent double oracle.
1198 run); // Validate all seven named-model stages against an independent double oracle.
1199 lenet(run); // Check deeper named-model inference and held-out MNIST accuracy.
1200 std::cout // Report the completed real-data processing check.
1201 << "All public real-data functional cases passed\n"; // Report completion only after every case succeeds.
1202 return 0; // Mark the complete real-data evaluation as successful.
1203 } // Unsupported GPU contexts receive the same explicit skip policy as the other examples.
1204#ifdef SHDR_HAVE_EGL // Catch the optional library's unsupported-context result when EGL is built.
1205 catch (const shdr_examples::context_unavailable& // Preserve unsupported hardware as a distinct test outcome.
1206 error) { // Preserve unsupported hardware as a distinct test outcome.
1207 std::cerr << "SKIP: " << error.what() // Explain which requested profile was unavailable.
1208 << '\n'; // Explain which requested profile was unavailable.
1209 return 77; // Let CTest classify this unsupported GPU context as skipped.
1210 } // Algorithm or numerical failures remain real test failures below.
1211#endif // Finish the optional headless-context handling.
1212 catch (const std::exception& // Report any fixture, numerical or runtime validation failure.
1213 error) { // Report any fixture, numerical or runtime validation failure.
1214 std::cerr << error.what() // Keep the actual failure visible to the user and test runner.
1215 << '\n'; // Keep the actual failure visible to the user and test runner.
1216 return 1; // Mark failed real-data processing as an error.
1217 } // Every failure path has a useful process result.
1218} // This one executable covers the real-data functionality of all example algorithm families.