ringkernel_procint/kernels/
conformance.rs1use crate::cuda::{
6 generate_conformance_kernel, CpuFallbackExecutor, ExecutionResult, GpuStats, GpuStatus,
7 KernelExecutor,
8};
9use crate::models::{ComplianceLevel, ConformanceResult, GpuObjectEvent, ProcessModel};
10
11pub struct ConformanceKernel {
13 model: ProcessModel,
15 executor: KernelExecutor,
17 use_gpu: bool,
19 kernel_compiled: bool,
21}
22
23impl ConformanceKernel {
24 pub fn new(model: ProcessModel) -> Self {
26 let mut kernel = Self {
27 model,
28 executor: KernelExecutor::new(),
29 use_gpu: true,
30 kernel_compiled: false,
31 };
32
33 kernel.try_compile_kernel();
35 kernel
36 }
37
38 fn try_compile_kernel(&mut self) {
40 if self.executor.is_cuda_available() && !self.kernel_compiled {
41 let source = generate_conformance_kernel();
42 match self.executor.compile(&source) {
43 Ok(_) => {
44 log::info!("Conformance CUDA kernel compiled successfully");
45 self.kernel_compiled = true;
46 }
47 Err(e) => {
48 log::warn!("Conformance CUDA kernel compilation failed: {}", e);
49 self.kernel_compiled = false;
50 }
51 }
52 }
53 }
54
55 pub fn with_cpu_only(mut self) -> Self {
57 self.use_gpu = false;
58 self
59 }
60
61 pub fn gpu_status(&self) -> GpuStatus {
63 self.executor.gpu_status()
64 }
65
66 pub fn gpu_stats(&self) -> &GpuStats {
68 &self.executor.stats
69 }
70
71 pub fn is_using_gpu(&self) -> bool {
73 self.use_gpu && self.kernel_compiled && self.executor.is_cuda_available()
74 }
75
76 pub fn model(&self) -> &ProcessModel {
78 &self.model
79 }
80
81 pub fn check(&mut self, events: &[GpuObjectEvent]) -> ConformanceCheckResult {
83 let start = std::time::Instant::now();
84
85 #[cfg(feature = "cuda")]
87 let (gpu_results, exec_result) = if self.is_using_gpu() {
88 match self.executor.execute_conformance_gpu(events, &self.model) {
89 Ok((results, result)) => {
90 log::debug!(
91 "Conformance GPU execution: {} events -> {} results in {}µs",
92 events.len(),
93 results.len(),
94 result.execution_time_us
95 );
96 (Some(results), result)
97 }
98 Err(e) => {
99 log::warn!(
100 "Conformance GPU execution failed, falling back to CPU: {}",
101 e
102 );
103 (None, ExecutionResult::default())
104 }
105 }
106 } else {
107 (None, ExecutionResult::default())
108 };
109
110 #[cfg(not(feature = "cuda"))]
111 let gpu_results: Option<Vec<ConformanceResult>> = None;
112 #[cfg(not(feature = "cuda"))]
113 let exec_result = ExecutionResult::default();
114
115 let (results, exec_result) = if let Some(gpu_results) = gpu_results {
117 (gpu_results, exec_result)
118 } else {
119 let (results, result) = CpuFallbackExecutor::execute_conformance(events, &self.model);
121 (results, result)
122 };
123
124 let total_time = start.elapsed().as_micros() as u64;
125
126 ConformanceCheckResult {
127 results,
128 execution_result: exec_result,
129 total_time_us: total_time,
130 }
131 }
132}
133
134#[derive(Debug, Clone)]
136pub struct ConformanceCheckResult {
137 pub results: Vec<ConformanceResult>,
139 pub execution_result: ExecutionResult,
141 pub total_time_us: u64,
143}
144
145impl ConformanceCheckResult {
146 pub fn conformant_count(&self) -> usize {
148 self.results.iter().filter(|r| r.is_conformant()).count()
149 }
150
151 pub fn non_conformant_count(&self) -> usize {
153 self.results.len() - self.conformant_count()
154 }
155
156 pub fn avg_fitness(&self) -> f32 {
158 if self.results.is_empty() {
159 return 1.0;
160 }
161 let total: f32 = self.results.iter().map(|r| r.fitness).sum();
162 total / self.results.len() as f32
163 }
164
165 pub fn by_compliance(&self, level: ComplianceLevel) -> Vec<&ConformanceResult> {
167 self.results
168 .iter()
169 .filter(|r| r.get_compliance_level() == level)
170 .collect()
171 }
172
173 pub fn distribution(&self) -> ConformanceDistribution {
175 let mut dist = ConformanceDistribution::default();
176 for result in &self.results {
177 match result.get_compliance_level() {
178 ComplianceLevel::FullyCompliant => dist.fully_compliant += 1,
179 ComplianceLevel::MostlyCompliant => dist.mostly_compliant += 1,
180 ComplianceLevel::PartiallyCompliant => dist.partially_compliant += 1,
181 ComplianceLevel::NonCompliant => dist.non_compliant += 1,
182 }
183 }
184 dist
185 }
186}
187
188#[derive(Debug, Clone, Default)]
190pub struct ConformanceDistribution {
191 pub fully_compliant: usize,
193 pub mostly_compliant: usize,
195 pub partially_compliant: usize,
197 pub non_compliant: usize,
199}
200
201impl ConformanceDistribution {
202 pub fn total(&self) -> usize {
204 self.fully_compliant + self.mostly_compliant + self.partially_compliant + self.non_compliant
205 }
206
207 pub fn percentages(&self) -> [f32; 4] {
209 let total = self.total() as f32;
210 if total == 0.0 {
211 return [0.0; 4];
212 }
213 [
214 self.fully_compliant as f32 / total * 100.0,
215 self.mostly_compliant as f32 / total * 100.0,
216 self.partially_compliant as f32 / total * 100.0,
217 self.non_compliant as f32 / total * 100.0,
218 ]
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225 use crate::models::{HybridTimestamp, ProcessModelType};
226
227 fn create_test_model() -> ProcessModel {
228 let mut model = ProcessModel::new(1, "Test", ProcessModelType::DFG);
229 model.start_activities = vec![1];
230 model.end_activities = vec![3];
231 model.add_transition(1, 2); model.add_transition(2, 3); model
234 }
235
236 fn create_conformant_events() -> Vec<GpuObjectEvent> {
237 vec![
239 GpuObjectEvent {
240 event_id: 1,
241 object_id: 100,
242 activity_id: 1,
243 timestamp: HybridTimestamp::new(0, 0),
244 ..Default::default()
245 },
246 GpuObjectEvent {
247 event_id: 2,
248 object_id: 100,
249 activity_id: 2,
250 timestamp: HybridTimestamp::new(100, 0),
251 ..Default::default()
252 },
253 GpuObjectEvent {
254 event_id: 3,
255 object_id: 100,
256 activity_id: 3,
257 timestamp: HybridTimestamp::new(200, 0),
258 ..Default::default()
259 },
260 ]
261 }
262
263 #[test]
264 fn test_conformant_trace() {
265 let model = create_test_model();
266 let mut kernel = ConformanceKernel::new(model).with_cpu_only();
267 let events = create_conformant_events();
268 let result = kernel.check(&events);
269
270 assert_eq!(result.results.len(), 1);
271 assert!(result.results[0].is_conformant());
272 assert_eq!(result.results[0].fitness, 1.0);
273 }
274
275 #[test]
276 fn test_non_conformant_trace() {
277 let model = create_test_model();
278 let mut kernel = ConformanceKernel::new(model).with_cpu_only();
279
280 let events = vec![
282 GpuObjectEvent {
283 event_id: 1,
284 object_id: 100,
285 activity_id: 1,
286 timestamp: HybridTimestamp::new(0, 0),
287 ..Default::default()
288 },
289 GpuObjectEvent {
290 event_id: 2,
291 object_id: 100,
292 activity_id: 3,
293 timestamp: HybridTimestamp::new(100, 0),
294 ..Default::default()
295 },
296 ];
297
298 let result = kernel.check(&events);
299 assert!(!result.results[0].is_conformant());
300 }
301
302 #[test]
303 fn test_conformance_distribution() {
304 let model = create_test_model();
305 let mut kernel = ConformanceKernel::new(model).with_cpu_only();
306 let events = create_conformant_events();
307 let result = kernel.check(&events);
308
309 let dist = result.distribution();
310 assert_eq!(dist.fully_compliant, 1);
311 }
312}