mirror of
https://github.com/ruvnet/RuView
synced 2026-08-10 20:31:42 +00:00
feat: vendor midstream and sublinear-time-solver libraries
Add ruvnet/midstream (AIMDS real-time inference) and ruvnet/sublinear-time-solver (sublinear optimization algorithms) as vendored dependencies under vendor/. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
+612
@@ -0,0 +1,612 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Python Inference Example for Temporal Neural Solver
|
||||
|
||||
This script demonstrates how to use the Temporal Neural Solver models
|
||||
for ultra-low latency inference in Python applications.
|
||||
"""
|
||||
|
||||
import time
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple, Optional, Union
|
||||
import warnings
|
||||
warnings.filterwarnings('ignore')
|
||||
|
||||
try:
|
||||
import numpy as np
|
||||
import onnxruntime as ort
|
||||
import matplotlib.pyplot as plt
|
||||
from dataclasses import dataclass
|
||||
except ImportError as e:
|
||||
print(f"❌ Missing dependencies: {e}")
|
||||
print("Install with: pip install numpy onnxruntime matplotlib")
|
||||
exit(1)
|
||||
|
||||
@dataclass
|
||||
class PredictionResult:
|
||||
"""Structured prediction result"""
|
||||
prediction: np.ndarray
|
||||
latency_ms: float
|
||||
confidence: Optional[float] = None
|
||||
certificate_error: Optional[float] = None
|
||||
metadata: Optional[Dict] = None
|
||||
|
||||
class TemporalNeuralSolver:
|
||||
"""
|
||||
Python interface for Temporal Neural Solver inference
|
||||
|
||||
This class provides a high-level interface for running inference
|
||||
with the breakthrough sub-millisecond neural network.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
model_path: str,
|
||||
optimize: bool = True,
|
||||
enable_profiling: bool = False
|
||||
):
|
||||
"""
|
||||
Initialize the Temporal Neural Solver
|
||||
|
||||
Args:
|
||||
model_path: Path to ONNX model file
|
||||
optimize: Enable ONNX Runtime optimizations
|
||||
enable_profiling: Enable performance profiling
|
||||
"""
|
||||
self.model_path = Path(model_path)
|
||||
self.enable_profiling = enable_profiling
|
||||
|
||||
if not self.model_path.exists():
|
||||
raise FileNotFoundError(f"Model file not found: {model_path}")
|
||||
|
||||
# Configure ONNX Runtime for optimal performance
|
||||
self.session_options = ort.SessionOptions()
|
||||
|
||||
if optimize:
|
||||
self.session_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
|
||||
self.session_options.execution_mode = ort.ExecutionMode.ORT_SEQUENTIAL
|
||||
self.session_options.intra_op_num_threads = 1 # Single thread for latency
|
||||
|
||||
if enable_profiling:
|
||||
self.session_options.enable_profiling = True
|
||||
|
||||
# Load model with optimal providers
|
||||
providers = self._get_optimal_providers()
|
||||
|
||||
try:
|
||||
self.session = ort.InferenceSession(
|
||||
str(self.model_path),
|
||||
sess_options=self.session_options,
|
||||
providers=providers
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to load model: {e}")
|
||||
raise
|
||||
|
||||
# Get model metadata
|
||||
self.input_info = self.session.get_inputs()[0]
|
||||
self.output_info = self.session.get_outputs()[0]
|
||||
|
||||
print(f"✅ Temporal Neural Solver loaded: {self.model_path.name}")
|
||||
print(f" Input: {self.input_info.name} {self.input_info.shape}")
|
||||
print(f" Output: {self.output_info.name} {self.output_info.shape}")
|
||||
print(f" Providers: {self.session.get_providers()}")
|
||||
|
||||
# Warmup for stable performance
|
||||
self._warmup()
|
||||
|
||||
def _get_optimal_providers(self) -> List[str]:
|
||||
"""Get optimal execution providers based on availability"""
|
||||
available_providers = ort.get_available_providers()
|
||||
optimal_providers = []
|
||||
|
||||
# Prefer GPU providers if available
|
||||
if 'TensorrtExecutionProvider' in available_providers:
|
||||
optimal_providers.append('TensorrtExecutionProvider')
|
||||
if 'CUDAExecutionProvider' in available_providers:
|
||||
optimal_providers.append('CUDAExecutionProvider')
|
||||
|
||||
# Always include CPU as fallback
|
||||
optimal_providers.append('CPUExecutionProvider')
|
||||
|
||||
return optimal_providers
|
||||
|
||||
def _warmup(self, num_runs: int = 100) -> None:
|
||||
"""Warmup the model for stable benchmarking"""
|
||||
print(f"🔥 Warming up model ({num_runs} runs)...")
|
||||
|
||||
# Generate dummy input matching model expectations
|
||||
dummy_input = self._generate_dummy_input()
|
||||
input_dict = {self.input_info.name: dummy_input}
|
||||
|
||||
for _ in range(num_runs):
|
||||
_ = self.session.run(None, input_dict)
|
||||
|
||||
print("✅ Warmup complete")
|
||||
|
||||
def _generate_dummy_input(self) -> np.ndarray:
|
||||
"""Generate dummy input for warmup and testing"""
|
||||
# Parse input shape, handling dynamic dimensions
|
||||
shape = []
|
||||
for dim in self.input_info.shape:
|
||||
if isinstance(dim, str) or dim == -1:
|
||||
# Dynamic dimension - use reasonable default
|
||||
if len(shape) == 0: # Batch dimension
|
||||
shape.append(1)
|
||||
elif len(shape) == 1: # Sequence dimension
|
||||
shape.append(10)
|
||||
else: # Feature dimension
|
||||
shape.append(4)
|
||||
else:
|
||||
shape.append(dim)
|
||||
|
||||
return np.random.randn(*shape).astype(np.float32)
|
||||
|
||||
def predict(
|
||||
self,
|
||||
sequence: Union[np.ndarray, List[List[float]]],
|
||||
return_latency: bool = True,
|
||||
validate_input: bool = True
|
||||
) -> PredictionResult:
|
||||
"""
|
||||
Run prediction on input sequence
|
||||
|
||||
Args:
|
||||
sequence: Input time series data [timesteps, features] or [batch, timesteps, features]
|
||||
return_latency: Whether to measure and return latency
|
||||
validate_input: Whether to validate input format
|
||||
|
||||
Returns:
|
||||
PredictionResult with prediction and metadata
|
||||
"""
|
||||
# Convert to numpy array if needed
|
||||
if isinstance(sequence, list):
|
||||
sequence = np.array(sequence, dtype=np.float32)
|
||||
|
||||
# Validate and reshape input
|
||||
if validate_input:
|
||||
sequence = self._validate_and_reshape_input(sequence)
|
||||
|
||||
# Prepare input dictionary
|
||||
input_dict = {self.input_info.name: sequence}
|
||||
|
||||
# Run inference with optional timing
|
||||
if return_latency:
|
||||
start_time = time.perf_counter()
|
||||
outputs = self.session.run(None, input_dict)
|
||||
end_time = time.perf_counter()
|
||||
latency_ms = (end_time - start_time) * 1000
|
||||
else:
|
||||
outputs = self.session.run(None, input_dict)
|
||||
latency_ms = 0.0
|
||||
|
||||
# Extract prediction (first batch if batched)
|
||||
prediction = outputs[0]
|
||||
if prediction.ndim > 1:
|
||||
prediction = prediction[0]
|
||||
|
||||
return PredictionResult(
|
||||
prediction=prediction,
|
||||
latency_ms=latency_ms,
|
||||
metadata={
|
||||
'model': str(self.model_path.name),
|
||||
'input_shape': list(sequence.shape),
|
||||
'output_shape': list(outputs[0].shape)
|
||||
}
|
||||
)
|
||||
|
||||
def _validate_and_reshape_input(self, sequence: np.ndarray) -> np.ndarray:
|
||||
"""Validate and reshape input to match model expectations"""
|
||||
# Ensure float32 dtype
|
||||
if sequence.dtype != np.float32:
|
||||
sequence = sequence.astype(np.float32)
|
||||
|
||||
# Handle different input shapes
|
||||
if sequence.ndim == 1:
|
||||
# Single timestep: [features] -> [1, 1, features]
|
||||
sequence = sequence.reshape(1, 1, -1)
|
||||
elif sequence.ndim == 2:
|
||||
# Sequence: [timesteps, features] -> [1, timesteps, features]
|
||||
sequence = sequence.reshape(1, *sequence.shape)
|
||||
elif sequence.ndim == 3:
|
||||
# Already correct: [batch, timesteps, features]
|
||||
pass
|
||||
else:
|
||||
raise ValueError(f"Invalid input shape: {sequence.shape}")
|
||||
|
||||
return sequence
|
||||
|
||||
def predict_batch(
|
||||
self,
|
||||
sequences: List[np.ndarray],
|
||||
batch_size: int = 32
|
||||
) -> List[PredictionResult]:
|
||||
"""
|
||||
Run batch prediction on multiple sequences
|
||||
|
||||
Args:
|
||||
sequences: List of input sequences
|
||||
batch_size: Batch size for processing
|
||||
|
||||
Returns:
|
||||
List of PredictionResult objects
|
||||
"""
|
||||
results = []
|
||||
|
||||
for i in range(0, len(sequences), batch_size):
|
||||
batch = sequences[i:i + batch_size]
|
||||
|
||||
# Stack into batch tensor
|
||||
batch_tensor = np.stack([
|
||||
self._validate_and_reshape_input(seq)[0] for seq in batch
|
||||
], axis=0)
|
||||
|
||||
# Run batch prediction
|
||||
input_dict = {self.input_info.name: batch_tensor}
|
||||
|
||||
start_time = time.perf_counter()
|
||||
outputs = self.session.run(None, input_dict)
|
||||
end_time = time.perf_counter()
|
||||
|
||||
total_latency_ms = (end_time - start_time) * 1000
|
||||
per_sample_latency = total_latency_ms / len(batch)
|
||||
|
||||
# Create results for each item in batch
|
||||
for j, prediction in enumerate(outputs[0]):
|
||||
results.append(PredictionResult(
|
||||
prediction=prediction,
|
||||
latency_ms=per_sample_latency,
|
||||
metadata={
|
||||
'model': str(self.model_path.name),
|
||||
'batch_size': len(batch),
|
||||
'batch_index': j
|
||||
}
|
||||
))
|
||||
|
||||
return results
|
||||
|
||||
def benchmark(
|
||||
self,
|
||||
num_samples: int = 1000,
|
||||
warmup_samples: int = 100,
|
||||
return_raw_latencies: bool = False
|
||||
) -> Dict:
|
||||
"""
|
||||
Run comprehensive performance benchmark
|
||||
|
||||
Args:
|
||||
num_samples: Number of inference samples
|
||||
warmup_samples: Number of warmup samples
|
||||
return_raw_latencies: Whether to include raw latency data
|
||||
|
||||
Returns:
|
||||
Dictionary with benchmark statistics
|
||||
"""
|
||||
print(f"📊 Running benchmark ({num_samples} samples)...")
|
||||
|
||||
# Generate test data
|
||||
dummy_input = self._generate_dummy_input()
|
||||
input_dict = {self.input_info.name: dummy_input}
|
||||
|
||||
# Warmup
|
||||
print(f"🔥 Warmup ({warmup_samples} samples)...")
|
||||
for _ in range(warmup_samples):
|
||||
_ = self.session.run(None, input_dict)
|
||||
|
||||
# Benchmark
|
||||
print(f"⏱️ Measuring latency...")
|
||||
latencies = []
|
||||
errors = 0
|
||||
|
||||
for i in range(num_samples):
|
||||
if i % 100 == 0 and i > 0:
|
||||
print(f" Progress: {i}/{num_samples}")
|
||||
|
||||
try:
|
||||
start_time = time.perf_counter()
|
||||
outputs = self.session.run(None, input_dict)
|
||||
end_time = time.perf_counter()
|
||||
|
||||
latency_ms = (end_time - start_time) * 1000
|
||||
latencies.append(latency_ms)
|
||||
|
||||
# Basic output validation
|
||||
if not outputs or outputs[0] is None:
|
||||
errors += 1
|
||||
|
||||
except Exception as e:
|
||||
errors += 1
|
||||
print(f" Error in sample {i}: {e}")
|
||||
|
||||
latencies = np.array(latencies)
|
||||
|
||||
# Calculate comprehensive statistics
|
||||
stats = {
|
||||
'num_samples': len(latencies),
|
||||
'errors': errors,
|
||||
'success_rate': (len(latencies) / num_samples) * 100,
|
||||
'latency_ms': {
|
||||
'mean': float(np.mean(latencies)),
|
||||
'std': float(np.std(latencies)),
|
||||
'min': float(np.min(latencies)),
|
||||
'max': float(np.max(latencies)),
|
||||
'median': float(np.median(latencies)),
|
||||
'p90': float(np.percentile(latencies, 90)),
|
||||
'p95': float(np.percentile(latencies, 95)),
|
||||
'p99': float(np.percentile(latencies, 99)),
|
||||
'p99_9': float(np.percentile(latencies, 99.9)),
|
||||
'p99_99': float(np.percentile(latencies, 99.99)),
|
||||
},
|
||||
'throughput_pps': 1000 / np.mean(latencies),
|
||||
'success_criteria': {
|
||||
'sub_millisecond_p99_9': float(np.percentile(latencies, 99.9)) < 1.0,
|
||||
'target_0_9ms_p99_9': float(np.percentile(latencies, 99.9)) < 0.9,
|
||||
'high_success_rate': (len(latencies) / num_samples) * 100 > 99.0
|
||||
}
|
||||
}
|
||||
|
||||
if return_raw_latencies:
|
||||
stats['raw_latencies'] = latencies.tolist()
|
||||
|
||||
print(f"✅ Benchmark complete!")
|
||||
print(f" Mean latency: {stats['latency_ms']['mean']:.3f}ms")
|
||||
print(f" P99.9 latency: {stats['latency_ms']['p99_9']:.3f}ms")
|
||||
print(f" Throughput: {stats['throughput_pps']:.0f} pps")
|
||||
print(f" Sub-ms P99.9: {'✅' if stats['success_criteria']['sub_millisecond_p99_9'] else '❌'}")
|
||||
print(f" Target 0.9ms: {'✅' if stats['success_criteria']['target_0_9ms_p99_9'] else '❌'}")
|
||||
|
||||
return stats
|
||||
|
||||
def get_model_info(self) -> Dict:
|
||||
"""Get detailed model information"""
|
||||
return {
|
||||
'model_path': str(self.model_path),
|
||||
'model_size_mb': self.model_path.stat().st_size / (1024 * 1024),
|
||||
'inputs': [{
|
||||
'name': inp.name,
|
||||
'shape': inp.shape,
|
||||
'type': inp.type
|
||||
} for inp in self.session.get_inputs()],
|
||||
'outputs': [{
|
||||
'name': out.name,
|
||||
'shape': out.shape,
|
||||
'type': out.type
|
||||
} for out in self.session.get_outputs()],
|
||||
'providers': self.session.get_providers(),
|
||||
'onnx_version': ort.__version__
|
||||
}
|
||||
|
||||
def generate_sample_trajectory(length: int = 10, noise_level: float = 0.1) -> np.ndarray:
|
||||
"""Generate a sample trajectory for demonstration"""
|
||||
trajectory = []
|
||||
|
||||
for i in range(length):
|
||||
t = i / length
|
||||
# Position with sinusoidal motion + noise
|
||||
x = np.sin(2 * np.pi * t) + np.random.normal(0, noise_level)
|
||||
y = np.cos(2 * np.pi * t) + np.random.normal(0, noise_level)
|
||||
|
||||
# Velocity (derivatives)
|
||||
vx = 2 * np.pi * np.cos(2 * np.pi * t) + np.random.normal(0, noise_level * 0.5)
|
||||
vy = -2 * np.pi * np.sin(2 * np.pi * t) + np.random.normal(0, noise_level * 0.5)
|
||||
|
||||
trajectory.append([x, y, vx, vy])
|
||||
|
||||
return np.array(trajectory, dtype=np.float32)
|
||||
|
||||
def plot_trajectory_and_prediction(
|
||||
input_trajectory: np.ndarray,
|
||||
prediction: np.ndarray,
|
||||
title: str = "Trajectory Prediction"
|
||||
) -> None:
|
||||
"""Visualize input trajectory and prediction"""
|
||||
try:
|
||||
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
|
||||
|
||||
# Plot trajectory in 2D space
|
||||
ax1.plot(input_trajectory[:, 0], input_trajectory[:, 1], 'b-o',
|
||||
label='Input Trajectory', alpha=0.7)
|
||||
ax1.plot(prediction[0], prediction[1], 'ro',
|
||||
label='Predicted Next Point', markersize=10)
|
||||
ax1.set_xlabel('X Position')
|
||||
ax1.set_ylabel('Y Position')
|
||||
ax1.set_title('Spatial Trajectory')
|
||||
ax1.legend()
|
||||
ax1.grid(True, alpha=0.3)
|
||||
ax1.axis('equal')
|
||||
|
||||
# Plot time series
|
||||
time_steps = range(len(input_trajectory))
|
||||
for i, label in enumerate(['X', 'Y', 'VX', 'VY']):
|
||||
ax2.plot(time_steps, input_trajectory[:, i],
|
||||
label=f'{label} (input)', alpha=0.7)
|
||||
|
||||
# Show prediction as next timestep
|
||||
next_time = len(input_trajectory)
|
||||
for i, label in enumerate(['X', 'Y', 'VX', 'VY']):
|
||||
ax2.plot(next_time, prediction[i], 'o',
|
||||
label=f'{label} (pred)', markersize=8)
|
||||
|
||||
ax2.set_xlabel('Time Step')
|
||||
ax2.set_ylabel('Value')
|
||||
ax2.set_title('Time Series Features')
|
||||
ax2.legend()
|
||||
ax2.grid(True, alpha=0.3)
|
||||
|
||||
plt.suptitle(title, fontsize=14, fontweight='bold')
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ Plotting failed: {e}")
|
||||
|
||||
def demo_basic_usage(model_path: str) -> None:
|
||||
"""Demonstrate basic usage of the Temporal Neural Solver"""
|
||||
print("🎯 Basic Usage Demo")
|
||||
print("="*40)
|
||||
|
||||
# Initialize solver
|
||||
solver = TemporalNeuralSolver(model_path)
|
||||
|
||||
# Generate sample data
|
||||
trajectory = generate_sample_trajectory(length=10)
|
||||
print(f"📊 Generated trajectory with shape: {trajectory.shape}")
|
||||
|
||||
# Single prediction
|
||||
result = solver.predict(trajectory)
|
||||
|
||||
print(f"✅ Prediction Results:")
|
||||
print(f" Prediction: {result.prediction}")
|
||||
print(f" Latency: {result.latency_ms:.3f}ms")
|
||||
print(f" Sub-millisecond: {'✅' if result.latency_ms < 1.0 else '❌'}")
|
||||
|
||||
# Visualize if matplotlib available
|
||||
plot_trajectory_and_prediction(trajectory, result.prediction,
|
||||
"Basic Usage - Trajectory Prediction")
|
||||
|
||||
def demo_batch_processing(model_path: str) -> None:
|
||||
"""Demonstrate batch processing capabilities"""
|
||||
print("\n📦 Batch Processing Demo")
|
||||
print("="*40)
|
||||
|
||||
solver = TemporalNeuralSolver(model_path)
|
||||
|
||||
# Generate multiple trajectories
|
||||
trajectories = [generate_sample_trajectory(length=10) for _ in range(5)]
|
||||
print(f"📊 Generated {len(trajectories)} trajectories")
|
||||
|
||||
# Batch prediction
|
||||
start_time = time.time()
|
||||
results = solver.predict_batch(trajectories, batch_size=32)
|
||||
total_time = time.time() - start_time
|
||||
|
||||
print(f"✅ Batch Results:")
|
||||
print(f" Total trajectories: {len(results)}")
|
||||
print(f" Total time: {total_time*1000:.3f}ms")
|
||||
print(f" Average latency per sample: {np.mean([r.latency_ms for r in results]):.3f}ms")
|
||||
print(f" Throughput: {len(results)/total_time:.0f} predictions/second")
|
||||
|
||||
def demo_benchmark(model_path: str) -> None:
|
||||
"""Demonstrate comprehensive benchmarking"""
|
||||
print("\n📊 Benchmark Demo")
|
||||
print("="*40)
|
||||
|
||||
solver = TemporalNeuralSolver(model_path)
|
||||
|
||||
# Run benchmark
|
||||
stats = solver.benchmark(num_samples=1000, return_raw_latencies=True)
|
||||
|
||||
# Display results
|
||||
print(f"\n🏆 Benchmark Summary:")
|
||||
print(f" Samples: {stats['num_samples']}")
|
||||
print(f" Success rate: {stats['success_rate']:.1f}%")
|
||||
print(f" Mean latency: {stats['latency_ms']['mean']:.3f}ms")
|
||||
print(f" P99.9 latency: {stats['latency_ms']['p99_9']:.3f}ms")
|
||||
print(f" Throughput: {stats['throughput_pps']:.0f} pps")
|
||||
|
||||
print(f"\n🎯 Success Criteria:")
|
||||
for criterion, passed in stats['success_criteria'].items():
|
||||
status = "✅" if passed else "❌"
|
||||
print(f" {criterion}: {status}")
|
||||
|
||||
# Plot latency distribution
|
||||
if 'raw_latencies' in stats:
|
||||
try:
|
||||
plt.figure(figsize=(10, 6))
|
||||
|
||||
latencies = stats['raw_latencies']
|
||||
|
||||
plt.subplot(1, 2, 1)
|
||||
plt.hist(latencies, bins=50, alpha=0.7, edgecolor='black')
|
||||
plt.axvline(stats['latency_ms']['p99_9'], color='red', linestyle='--',
|
||||
label=f'P99.9: {stats["latency_ms"]["p99_9"]:.3f}ms')
|
||||
plt.axvline(0.9, color='green', linestyle='--', label='Target: 0.9ms')
|
||||
plt.xlabel('Latency (ms)')
|
||||
plt.ylabel('Frequency')
|
||||
plt.title('Latency Distribution')
|
||||
plt.legend()
|
||||
plt.grid(True, alpha=0.3)
|
||||
|
||||
plt.subplot(1, 2, 2)
|
||||
plt.plot(latencies[:100], alpha=0.7) # First 100 samples
|
||||
plt.xlabel('Sample Number')
|
||||
plt.ylabel('Latency (ms)')
|
||||
plt.title('Latency Time Series')
|
||||
plt.grid(True, alpha=0.3)
|
||||
|
||||
plt.suptitle('Benchmark Results - Latency Analysis', fontsize=14, fontweight='bold')
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ Plotting failed: {e}")
|
||||
|
||||
def main():
|
||||
"""Main demonstration function"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Python Inference Example for Temporal Neural Solver"
|
||||
)
|
||||
parser.add_argument(
|
||||
"model_path",
|
||||
help="Path to ONNX model file"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--demo",
|
||||
choices=["basic", "batch", "benchmark", "all"],
|
||||
default="all",
|
||||
help="Which demo to run"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-plots",
|
||||
action="store_true",
|
||||
help="Disable matplotlib plots"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Disable plotting if requested
|
||||
if args.no_plots:
|
||||
def dummy_plot(*args, **kwargs):
|
||||
pass
|
||||
global plot_trajectory_and_prediction
|
||||
plot_trajectory_and_prediction = dummy_plot
|
||||
|
||||
print("🚀 Temporal Neural Solver - Python Inference Example")
|
||||
print("="*60)
|
||||
print(f"Model: {args.model_path}")
|
||||
print(f"Demo: {args.demo}")
|
||||
print()
|
||||
|
||||
# Check if model exists
|
||||
if not Path(args.model_path).exists():
|
||||
print(f"❌ Model file not found: {args.model_path}")
|
||||
print("Please ensure the ONNX model file exists.")
|
||||
return
|
||||
|
||||
try:
|
||||
# Run requested demos
|
||||
if args.demo in ["basic", "all"]:
|
||||
demo_basic_usage(args.model_path)
|
||||
|
||||
if args.demo in ["batch", "all"]:
|
||||
demo_batch_processing(args.model_path)
|
||||
|
||||
if args.demo in ["benchmark", "all"]:
|
||||
demo_benchmark(args.model_path)
|
||||
|
||||
print("\n🎉 Demo complete!")
|
||||
print("\n💡 Next steps:")
|
||||
print("- Integrate the TemporalNeuralSolver class into your application")
|
||||
print("- Customize input preprocessing for your data")
|
||||
print("- Monitor latency in production environments")
|
||||
print("- Use batch processing for higher throughput")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Demo failed: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+595
@@ -0,0 +1,595 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Real-Time Inference Demo for Temporal Neural Solver
|
||||
|
||||
This script demonstrates real-time inference capabilities of the Temporal Neural Solver,
|
||||
simulating time-critical applications like HFT, robotics, and autonomous systems.
|
||||
"""
|
||||
|
||||
import time
|
||||
import threading
|
||||
import queue
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Callable
|
||||
from dataclasses import dataclass, field
|
||||
from collections import deque
|
||||
import warnings
|
||||
warnings.filterwarnings('ignore')
|
||||
|
||||
try:
|
||||
import numpy as np
|
||||
import onnxruntime as ort
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.animation import FuncAnimation
|
||||
import pandas as pd
|
||||
except ImportError as e:
|
||||
print(f"❌ Missing dependencies: {e}")
|
||||
print("Install with: pip install numpy onnxruntime matplotlib pandas")
|
||||
exit(1)
|
||||
|
||||
@dataclass
|
||||
class RealTimeEvent:
|
||||
"""Real-time event with timestamp and data"""
|
||||
timestamp: float
|
||||
data: np.ndarray
|
||||
event_id: int
|
||||
metadata: Dict = field(default_factory=dict)
|
||||
|
||||
@dataclass
|
||||
class PredictionEvent:
|
||||
"""Prediction result event"""
|
||||
timestamp: float
|
||||
prediction: np.ndarray
|
||||
latency_ms: float
|
||||
event_id: int
|
||||
success: bool
|
||||
metadata: Dict = field(default_factory=dict)
|
||||
|
||||
class RealTimeDataGenerator:
|
||||
"""Generates realistic real-time data streams"""
|
||||
|
||||
def __init__(self, frequency_hz: float = 100.0, noise_level: float = 0.1):
|
||||
self.frequency_hz = frequency_hz
|
||||
self.noise_level = noise_level
|
||||
self.start_time = time.time()
|
||||
self.event_counter = 0
|
||||
|
||||
def generate_market_data(self) -> RealTimeEvent:
|
||||
"""Generate synthetic high-frequency trading data"""
|
||||
current_time = time.time()
|
||||
elapsed = current_time - self.start_time
|
||||
|
||||
# Simulate price movements with trend and volatility
|
||||
base_price = 100.0
|
||||
trend = 0.001 * elapsed # Slow upward trend
|
||||
volatility = 0.5 * np.sin(2 * np.pi * elapsed * 0.1) # Cyclical volatility
|
||||
noise = np.random.normal(0, self.noise_level)
|
||||
|
||||
# Create OHLCV-like data over small time windows
|
||||
price = base_price + trend + volatility + noise
|
||||
volume = 1000 + 500 * np.abs(noise)
|
||||
|
||||
# Simulate order book features
|
||||
bid_ask_spread = 0.01 + 0.005 * np.abs(noise)
|
||||
market_depth = 10000 + 2000 * noise
|
||||
|
||||
data = np.array([price, volume, bid_ask_spread, market_depth], dtype=np.float32)
|
||||
|
||||
self.event_counter += 1
|
||||
return RealTimeEvent(
|
||||
timestamp=current_time,
|
||||
data=data,
|
||||
event_id=self.event_counter,
|
||||
metadata={"type": "market_data", "symbol": "DEMO/USD"}
|
||||
)
|
||||
|
||||
def generate_sensor_data(self) -> RealTimeEvent:
|
||||
"""Generate synthetic robotics/autonomous vehicle sensor data"""
|
||||
current_time = time.time()
|
||||
elapsed = current_time - self.start_time
|
||||
|
||||
# Simulate vehicle motion in a circular path
|
||||
angular_freq = 0.5 # rad/s
|
||||
radius = 5.0
|
||||
|
||||
# Position
|
||||
x = radius * np.cos(angular_freq * elapsed) + np.random.normal(0, self.noise_level * 0.1)
|
||||
y = radius * np.sin(angular_freq * elapsed) + np.random.normal(0, self.noise_level * 0.1)
|
||||
|
||||
# Velocity
|
||||
vx = -radius * angular_freq * np.sin(angular_freq * elapsed) + np.random.normal(0, self.noise_level * 0.05)
|
||||
vy = radius * angular_freq * np.cos(angular_freq * elapsed) + np.random.normal(0, self.noise_level * 0.05)
|
||||
|
||||
data = np.array([x, y, vx, vy], dtype=np.float32)
|
||||
|
||||
self.event_counter += 1
|
||||
return RealTimeEvent(
|
||||
timestamp=current_time,
|
||||
data=data,
|
||||
event_id=self.event_counter,
|
||||
metadata={"type": "sensor_data", "vehicle_id": "demo_vehicle"}
|
||||
)
|
||||
|
||||
def generate_iot_data(self) -> RealTimeEvent:
|
||||
"""Generate synthetic IoT edge device data"""
|
||||
current_time = time.time()
|
||||
elapsed = current_time - self.start_time
|
||||
|
||||
# Simulate environmental sensors with daily patterns
|
||||
time_of_day = (elapsed % 86400) / 86400 # Normalize to [0, 1] for daily cycle
|
||||
|
||||
# Temperature with daily cycle
|
||||
base_temp = 20.0
|
||||
daily_variation = 10.0 * np.sin(2 * np.pi * time_of_day - np.pi/2) # Peak at noon
|
||||
temp = base_temp + daily_variation + np.random.normal(0, self.noise_level)
|
||||
|
||||
# Humidity (inverse correlation with temperature)
|
||||
humidity = 60.0 - 0.5 * daily_variation + np.random.normal(0, self.noise_level * 5)
|
||||
|
||||
# Light level (solar pattern)
|
||||
light = max(0, 1000 * np.sin(np.pi * time_of_day)) + np.random.normal(0, self.noise_level * 10)
|
||||
|
||||
# Motion detection (binary with some activity patterns)
|
||||
motion_prob = 0.1 + 0.2 * np.sin(4 * np.pi * time_of_day) # More active during day
|
||||
motion = 1.0 if np.random.random() < motion_prob else 0.0
|
||||
|
||||
data = np.array([temp, humidity, light, motion], dtype=np.float32)
|
||||
|
||||
self.event_counter += 1
|
||||
return RealTimeEvent(
|
||||
timestamp=current_time,
|
||||
data=data,
|
||||
event_id=self.event_counter,
|
||||
metadata={"type": "iot_data", "device_id": "demo_sensor"}
|
||||
)
|
||||
|
||||
class RealTimePredictor:
|
||||
"""Real-time inference engine with sub-millisecond latency"""
|
||||
|
||||
def __init__(self, model_path: str, sequence_length: int = 10):
|
||||
self.model_path = Path(model_path)
|
||||
self.sequence_length = sequence_length
|
||||
|
||||
# Configure ONNX Runtime for minimal latency
|
||||
self.session_options = ort.SessionOptions()
|
||||
self.session_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
|
||||
self.session_options.execution_mode = ort.ExecutionMode.ORT_SEQUENTIAL
|
||||
self.session_options.intra_op_num_threads = 1
|
||||
|
||||
# Load model
|
||||
if self.model_path.exists():
|
||||
self.session = ort.InferenceSession(
|
||||
str(self.model_path),
|
||||
sess_options=self.session_options,
|
||||
providers=['CPUExecutionProvider']
|
||||
)
|
||||
self.input_name = self.session.get_inputs()[0].name
|
||||
print(f"✅ Model loaded: {self.model_path}")
|
||||
else:
|
||||
print(f"⚠️ Model not found: {self.model_path}, using synthetic predictor")
|
||||
self.session = None
|
||||
|
||||
# Sliding window buffer for sequence data
|
||||
self.data_buffer = deque(maxlen=self.sequence_length)
|
||||
|
||||
# Performance tracking
|
||||
self.prediction_count = 0
|
||||
self.total_latency = 0.0
|
||||
self.max_latency = 0.0
|
||||
self.latency_history = deque(maxlen=1000)
|
||||
|
||||
def add_data_point(self, data: np.ndarray) -> bool:
|
||||
"""Add data point to sliding window buffer"""
|
||||
self.data_buffer.append(data)
|
||||
return len(self.data_buffer) == self.sequence_length
|
||||
|
||||
def predict(self, event: RealTimeEvent) -> PredictionEvent:
|
||||
"""Run real-time prediction on event data"""
|
||||
# Add to buffer
|
||||
buffer_ready = self.add_data_point(event.data)
|
||||
|
||||
if not buffer_ready:
|
||||
# Not enough data yet - return dummy prediction
|
||||
return PredictionEvent(
|
||||
timestamp=time.time(),
|
||||
prediction=np.zeros(4, dtype=np.float32),
|
||||
latency_ms=0.0,
|
||||
event_id=event.event_id,
|
||||
success=False,
|
||||
metadata={"error": "insufficient_data"}
|
||||
)
|
||||
|
||||
# Prepare input sequence
|
||||
sequence = np.array(list(self.data_buffer), dtype=np.float32)
|
||||
sequence = sequence.reshape(1, self.sequence_length, -1)
|
||||
|
||||
# Run inference with timing
|
||||
start_time = time.perf_counter()
|
||||
|
||||
try:
|
||||
if self.session is not None:
|
||||
# Real model inference
|
||||
outputs = self.session.run(None, {self.input_name: sequence})
|
||||
prediction = outputs[0][0]
|
||||
else:
|
||||
# Synthetic prediction for demo
|
||||
time.sleep(0.0008) # Simulate 0.8ms processing time
|
||||
prediction = sequence[0, -1] + np.random.normal(0, 0.01, 4)
|
||||
|
||||
end_time = time.perf_counter()
|
||||
latency_ms = (end_time - start_time) * 1000
|
||||
|
||||
# Update statistics
|
||||
self.prediction_count += 1
|
||||
self.total_latency += latency_ms
|
||||
self.max_latency = max(self.max_latency, latency_ms)
|
||||
self.latency_history.append(latency_ms)
|
||||
|
||||
return PredictionEvent(
|
||||
timestamp=end_time,
|
||||
prediction=prediction,
|
||||
latency_ms=latency_ms,
|
||||
event_id=event.event_id,
|
||||
success=True,
|
||||
metadata={
|
||||
"avg_latency_ms": self.total_latency / self.prediction_count,
|
||||
"max_latency_ms": self.max_latency
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
end_time = time.perf_counter()
|
||||
latency_ms = (end_time - start_time) * 1000
|
||||
|
||||
return PredictionEvent(
|
||||
timestamp=end_time,
|
||||
prediction=np.zeros(4, dtype=np.float32),
|
||||
latency_ms=latency_ms,
|
||||
event_id=event.event_id,
|
||||
success=False,
|
||||
metadata={"error": str(e)}
|
||||
)
|
||||
|
||||
def get_statistics(self) -> Dict:
|
||||
"""Get current performance statistics"""
|
||||
if self.prediction_count == 0:
|
||||
return {"predictions": 0}
|
||||
|
||||
latencies = list(self.latency_history)
|
||||
return {
|
||||
"predictions": self.prediction_count,
|
||||
"avg_latency_ms": self.total_latency / self.prediction_count,
|
||||
"max_latency_ms": self.max_latency,
|
||||
"current_p99_ms": np.percentile(latencies, 99) if latencies else 0,
|
||||
"current_p99_9_ms": np.percentile(latencies, 99.9) if latencies else 0,
|
||||
"sub_millisecond_rate": (np.array(latencies) < 1.0).mean() * 100 if latencies else 0,
|
||||
}
|
||||
|
||||
class RealTimeSimulator:
|
||||
"""Real-time inference simulation orchestrator"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
model_path: str,
|
||||
scenario: str = "market",
|
||||
frequency_hz: float = 100.0,
|
||||
duration_seconds: float = 60.0
|
||||
):
|
||||
self.scenario = scenario
|
||||
self.frequency_hz = frequency_hz
|
||||
self.duration_seconds = duration_seconds
|
||||
|
||||
# Initialize components
|
||||
self.data_generator = RealTimeDataGenerator(frequency_hz)
|
||||
self.predictor = RealTimePredictor(model_path)
|
||||
|
||||
# Event queues
|
||||
self.input_queue = queue.Queue(maxsize=1000)
|
||||
self.output_queue = queue.Queue(maxsize=1000)
|
||||
|
||||
# Monitoring
|
||||
self.events_processed = 0
|
||||
self.simulation_start_time = None
|
||||
self.running = False
|
||||
|
||||
# Results storage
|
||||
self.results = {
|
||||
"events": [],
|
||||
"predictions": [],
|
||||
"statistics": {}
|
||||
}
|
||||
|
||||
def data_producer_thread(self):
|
||||
"""Producer thread generating real-time data"""
|
||||
interval = 1.0 / self.frequency_hz
|
||||
|
||||
while self.running:
|
||||
start_time = time.time()
|
||||
|
||||
# Generate data based on scenario
|
||||
if self.scenario == "market":
|
||||
event = self.data_generator.generate_market_data()
|
||||
elif self.scenario == "robotics":
|
||||
event = self.data_generator.generate_sensor_data()
|
||||
elif self.scenario == "iot":
|
||||
event = self.data_generator.generate_iot_data()
|
||||
else:
|
||||
event = self.data_generator.generate_sensor_data()
|
||||
|
||||
try:
|
||||
self.input_queue.put(event, timeout=0.001)
|
||||
except queue.Full:
|
||||
print("⚠️ Input queue full, dropping event")
|
||||
|
||||
# Maintain frequency
|
||||
elapsed = time.time() - start_time
|
||||
sleep_time = max(0, interval - elapsed)
|
||||
if sleep_time > 0:
|
||||
time.sleep(sleep_time)
|
||||
|
||||
def inference_consumer_thread(self):
|
||||
"""Consumer thread running inference"""
|
||||
while self.running:
|
||||
try:
|
||||
event = self.input_queue.get(timeout=0.1)
|
||||
prediction = self.predictor.predict(event)
|
||||
self.output_queue.put(prediction)
|
||||
self.events_processed += 1
|
||||
|
||||
# Check for latency violations
|
||||
if prediction.success and prediction.latency_ms > 1.0:
|
||||
print(f"⚠️ Latency violation: {prediction.latency_ms:.3f}ms")
|
||||
|
||||
except queue.Empty:
|
||||
continue
|
||||
|
||||
def monitoring_thread(self):
|
||||
"""Monitoring thread for real-time statistics"""
|
||||
while self.running:
|
||||
time.sleep(1.0) # Update every second
|
||||
|
||||
stats = self.predictor.get_statistics()
|
||||
elapsed = time.time() - self.simulation_start_time
|
||||
|
||||
print(f"📊 [{elapsed:6.1f}s] Events: {self.events_processed:5d} | "
|
||||
f"Avg: {stats.get('avg_latency_ms', 0):.3f}ms | "
|
||||
f"P99.9: {stats.get('current_p99_9_ms', 0):.3f}ms | "
|
||||
f"Sub-ms: {stats.get('sub_millisecond_rate', 0):.1f}%")
|
||||
|
||||
def run_simulation(self):
|
||||
"""Run the complete real-time simulation"""
|
||||
print(f"🚀 Starting real-time simulation")
|
||||
print(f" Scenario: {self.scenario}")
|
||||
print(f" Frequency: {self.frequency_hz} Hz")
|
||||
print(f" Duration: {self.duration_seconds}s")
|
||||
print(f" Expected events: {int(self.frequency_hz * self.duration_seconds)}")
|
||||
print()
|
||||
|
||||
self.simulation_start_time = time.time()
|
||||
self.running = True
|
||||
|
||||
# Start threads
|
||||
producer = threading.Thread(target=self.data_producer_thread, daemon=True)
|
||||
consumer = threading.Thread(target=self.inference_consumer_thread, daemon=True)
|
||||
monitor = threading.Thread(target=self.monitoring_thread, daemon=True)
|
||||
|
||||
producer.start()
|
||||
consumer.start()
|
||||
monitor.start()
|
||||
|
||||
# Run for specified duration
|
||||
time.sleep(self.duration_seconds)
|
||||
|
||||
# Stop simulation
|
||||
self.running = False
|
||||
print("\n🛑 Stopping simulation...")
|
||||
|
||||
# Wait for threads to finish
|
||||
producer.join(timeout=1.0)
|
||||
consumer.join(timeout=1.0)
|
||||
monitor.join(timeout=1.0)
|
||||
|
||||
# Collect final results
|
||||
final_stats = self.predictor.get_statistics()
|
||||
self.results["statistics"] = final_stats
|
||||
|
||||
print("\n✅ Simulation complete!")
|
||||
return self.results
|
||||
|
||||
def print_summary(self):
|
||||
"""Print simulation summary"""
|
||||
stats = self.results["statistics"]
|
||||
|
||||
print("\n📋 REAL-TIME SIMULATION SUMMARY")
|
||||
print("=" * 50)
|
||||
print(f"Scenario: {self.scenario}")
|
||||
print(f"Events processed: {self.events_processed}")
|
||||
print(f"Total predictions: {stats.get('predictions', 0)}")
|
||||
print(f"Processing rate: {stats.get('predictions', 0) / self.duration_seconds:.1f} pps")
|
||||
|
||||
print(f"\n⏱️ Latency Performance:")
|
||||
print(f" Average: {stats.get('avg_latency_ms', 0):.3f}ms")
|
||||
print(f" Maximum: {stats.get('max_latency_ms', 0):.3f}ms")
|
||||
print(f" P99: {stats.get('current_p99_ms', 0):.3f}ms")
|
||||
print(f" P99.9: {stats.get('current_p99_9_ms', 0):.3f}ms")
|
||||
|
||||
print(f"\n🎯 Success Criteria:")
|
||||
sub_ms_rate = stats.get('sub_millisecond_rate', 0)
|
||||
p99_9 = stats.get('current_p99_9_ms', 0)
|
||||
|
||||
print(f" Sub-millisecond rate: {sub_ms_rate:.1f}% {'✅' if sub_ms_rate > 95 else '❌'}")
|
||||
print(f" P99.9 < 1.0ms: {p99_9:.3f}ms {'✅' if p99_9 < 1.0 else '❌'}")
|
||||
print(f" P99.9 < 0.9ms: {p99_9:.3f}ms {'✅' if p99_9 < 0.9 else '❌'}")
|
||||
|
||||
# Application-specific metrics
|
||||
if self.scenario == "market":
|
||||
print(f"\n💰 HFT Application:")
|
||||
print(f" Decision latency: {p99_9:.3f}ms")
|
||||
print(f" Market opportunity: {'✅ Captured' if p99_9 < 0.5 else '⚠️ Marginal' if p99_9 < 1.0 else '❌ Missed'}")
|
||||
|
||||
elif self.scenario == "robotics":
|
||||
print(f"\n🤖 Robotics Application:")
|
||||
print(f" Control loop latency: {p99_9:.3f}ms")
|
||||
print(f" Real-time control: {'✅ Achieved' if p99_9 < 1.0 else '❌ Failed'}")
|
||||
|
||||
elif self.scenario == "iot":
|
||||
print(f"\n📱 IoT Edge Application:")
|
||||
print(f" Edge inference: {p99_9:.3f}ms")
|
||||
print(f" Battery efficient: {'✅ Yes' if stats.get('avg_latency_ms', 0) < 0.5 else '⚠️ Moderate'}")
|
||||
|
||||
def create_live_visualization(simulator: RealTimeSimulator):
|
||||
"""Create live visualization of real-time inference"""
|
||||
try:
|
||||
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
|
||||
fig.suptitle(f'Real-Time Inference Monitor - {simulator.scenario.title()}', fontsize=14)
|
||||
|
||||
# Data storage for plotting
|
||||
times = deque(maxlen=200)
|
||||
latencies = deque(maxlen=200)
|
||||
predictions = deque(maxlen=200)
|
||||
throughput = deque(maxlen=200)
|
||||
|
||||
def update_plots(frame):
|
||||
if not simulator.running:
|
||||
return
|
||||
|
||||
current_time = time.time() - simulator.simulation_start_time
|
||||
stats = simulator.predictor.get_statistics()
|
||||
|
||||
# Update data
|
||||
times.append(current_time)
|
||||
latencies.append(stats.get('current_p99_9_ms', 0))
|
||||
throughput.append(stats.get('predictions', 0) / max(current_time, 1))
|
||||
|
||||
# Get recent prediction if available
|
||||
try:
|
||||
prediction = simulator.output_queue.get_nowait()
|
||||
predictions.append(np.mean(prediction.prediction))
|
||||
except queue.Empty:
|
||||
if predictions:
|
||||
predictions.append(predictions[-1])
|
||||
else:
|
||||
predictions.append(0)
|
||||
|
||||
# Clear and redraw plots
|
||||
for ax in axes.flat:
|
||||
ax.clear()
|
||||
|
||||
# Plot 1: Latency over time
|
||||
if times and latencies:
|
||||
axes[0, 0].plot(list(times), list(latencies), 'b-', alpha=0.7)
|
||||
axes[0, 0].axhline(y=1.0, color='r', linestyle='--', label='1ms target')
|
||||
axes[0, 0].axhline(y=0.9, color='g', linestyle='--', label='0.9ms target')
|
||||
axes[0, 0].set_ylabel('P99.9 Latency (ms)')
|
||||
axes[0, 0].set_title('Latency Performance')
|
||||
axes[0, 0].legend()
|
||||
axes[0, 0].grid(True, alpha=0.3)
|
||||
|
||||
# Plot 2: Throughput
|
||||
if times and throughput:
|
||||
axes[0, 1].plot(list(times), list(throughput), 'g-', alpha=0.7)
|
||||
axes[0, 1].set_ylabel('Predictions/Second')
|
||||
axes[0, 1].set_title('Throughput')
|
||||
axes[0, 1].grid(True, alpha=0.3)
|
||||
|
||||
# Plot 3: Prediction values
|
||||
if times and predictions:
|
||||
axes[1, 0].plot(list(times), list(predictions), 'orange', alpha=0.7)
|
||||
axes[1, 0].set_ylabel('Prediction Value')
|
||||
axes[1, 0].set_title('Prediction Trend')
|
||||
axes[1, 0].grid(True, alpha=0.3)
|
||||
|
||||
# Plot 4: Statistics summary
|
||||
axes[1, 1].axis('off')
|
||||
if stats:
|
||||
stats_text = f"""Real-Time Statistics:
|
||||
|
||||
Events: {simulator.events_processed:,}
|
||||
Predictions: {stats.get('predictions', 0):,}
|
||||
Avg Latency: {stats.get('avg_latency_ms', 0):.3f}ms
|
||||
Max Latency: {stats.get('max_latency_ms', 0):.3f}ms
|
||||
P99.9 Latency: {stats.get('current_p99_9_ms', 0):.3f}ms
|
||||
Sub-ms Rate: {stats.get('sub_millisecond_rate', 0):.1f}%
|
||||
|
||||
Status: {'🟢 REAL-TIME' if stats.get('current_p99_9_ms', 0) < 1.0 else '🟡 MARGINAL' if stats.get('current_p99_9_ms', 0) < 2.0 else '🔴 DELAYED'}"""
|
||||
|
||||
axes[1, 1].text(0.1, 0.9, stats_text, fontsize=10, verticalalignment='top',
|
||||
fontfamily='monospace')
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
# Create animation
|
||||
ani = FuncAnimation(fig, update_plots, interval=100, cache_frame_data=False)
|
||||
return ani
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ Visualization failed: {e}")
|
||||
return None
|
||||
|
||||
def main():
|
||||
"""Main entry point for real-time demo"""
|
||||
parser = argparse.ArgumentParser(description="Real-Time Inference Demo")
|
||||
parser.add_argument("--model", default="system_b.onnx", help="Path to ONNX model")
|
||||
parser.add_argument("--scenario", choices=["market", "robotics", "iot"], default="market",
|
||||
help="Application scenario")
|
||||
parser.add_argument("--frequency", type=float, default=100.0, help="Data frequency (Hz)")
|
||||
parser.add_argument("--duration", type=float, default=30.0, help="Simulation duration (seconds)")
|
||||
parser.add_argument("--visualize", action="store_true", help="Show live visualization")
|
||||
parser.add_argument("--save-results", help="Save results to JSON file")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print("⚡ Temporal Neural Solver - Real-Time Inference Demo")
|
||||
print("=" * 60)
|
||||
|
||||
# Create simulator
|
||||
simulator = RealTimeSimulator(
|
||||
model_path=args.model,
|
||||
scenario=args.scenario,
|
||||
frequency_hz=args.frequency,
|
||||
duration_seconds=args.duration
|
||||
)
|
||||
|
||||
# Setup visualization if requested
|
||||
animation = None
|
||||
if args.visualize:
|
||||
try:
|
||||
animation = create_live_visualization(simulator)
|
||||
# Start visualization in background
|
||||
plt.ion()
|
||||
plt.show()
|
||||
except Exception as e:
|
||||
print(f"⚠️ Visualization setup failed: {e}")
|
||||
|
||||
# Run simulation
|
||||
try:
|
||||
results = simulator.run_simulation()
|
||||
simulator.print_summary()
|
||||
|
||||
# Save results if requested
|
||||
if args.save_results:
|
||||
with open(args.save_results, 'w') as f:
|
||||
json.dump(results, f, indent=2, default=str)
|
||||
print(f"\n📄 Results saved: {args.save_results}")
|
||||
|
||||
# Keep visualization alive
|
||||
if animation and args.visualize:
|
||||
print("\n🖥️ Close the plot window to exit...")
|
||||
plt.ioff()
|
||||
plt.show()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n⚠️ Simulation interrupted by user")
|
||||
simulator.running = False
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Simulation failed: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
print("\n🎉 Real-time demo complete!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+546
@@ -0,0 +1,546 @@
|
||||
//! Rust Integration Example for Temporal Neural Solver
|
||||
//!
|
||||
//! This example demonstrates how to integrate the Temporal Neural Solver
|
||||
//! into Rust applications for ultra-low latency inference.
|
||||
|
||||
use std::time::Instant;
|
||||
use std::path::Path;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use nalgebra::{DVector, DMatrix};
|
||||
|
||||
// Re-export from the main crate
|
||||
use temporal_neural_net::{
|
||||
models::{SystemA, SystemB, ModelTrait},
|
||||
config::{Config, ModelConfig, InferenceConfig},
|
||||
data::{TimeSeriesData, WindowedSample},
|
||||
inference::{Predictor, Prediction},
|
||||
export::ONNXExporter,
|
||||
error::{Result, TemporalNeuralError},
|
||||
};
|
||||
|
||||
/// Configuration for the Rust integration example
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ExampleConfig {
|
||||
/// Model configuration
|
||||
pub model: ModelConfig,
|
||||
/// Inference configuration
|
||||
pub inference: InferenceConfig,
|
||||
/// Example-specific settings
|
||||
pub example: ExampleSettings,
|
||||
}
|
||||
|
||||
/// Example-specific settings
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ExampleSettings {
|
||||
/// Number of benchmark iterations
|
||||
pub benchmark_iterations: usize,
|
||||
/// Whether to enable detailed logging
|
||||
pub enable_logging: bool,
|
||||
/// Output directory for results
|
||||
pub output_dir: String,
|
||||
}
|
||||
|
||||
impl Default for ExampleSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
benchmark_iterations: 10000,
|
||||
enable_logging: true,
|
||||
output_dir: "output".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Performance metrics for benchmarking
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PerformanceMetrics {
|
||||
/// Number of samples processed
|
||||
pub num_samples: usize,
|
||||
/// Mean latency in milliseconds
|
||||
pub mean_latency_ms: f64,
|
||||
/// Standard deviation of latency
|
||||
pub std_latency_ms: f64,
|
||||
/// P99.9 latency in milliseconds
|
||||
pub p99_9_latency_ms: f64,
|
||||
/// Throughput in predictions per second
|
||||
pub throughput_pps: f64,
|
||||
/// Success rate percentage
|
||||
pub success_rate: f64,
|
||||
/// Whether sub-millisecond target was achieved
|
||||
pub sub_millisecond_achieved: bool,
|
||||
}
|
||||
|
||||
/// Rust-native Temporal Neural Solver interface
|
||||
pub struct RustTemporalSolver {
|
||||
/// The underlying neural network model
|
||||
model: Box<dyn ModelTrait>,
|
||||
/// Inference engine
|
||||
predictor: Predictor,
|
||||
/// Configuration
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl RustTemporalSolver {
|
||||
/// Create a new Temporal Neural Solver instance
|
||||
pub fn new(config_path: &str) -> Result<Self> {
|
||||
// Load configuration
|
||||
let config = Config::from_file(config_path)?;
|
||||
|
||||
// Create model based on configuration
|
||||
let model: Box<dyn ModelTrait> = match config.model.system_type.as_str() {
|
||||
"A" => Box::new(SystemA::new(config.model.clone())?),
|
||||
"B" => Box::new(SystemB::new(config.model.clone())?),
|
||||
_ => return Err(TemporalNeuralError::ConfigurationError {
|
||||
field: "system_type".to_string(),
|
||||
message: "Must be 'A' or 'B'".to_string(),
|
||||
}),
|
||||
};
|
||||
|
||||
// Create predictor
|
||||
let predictor = Predictor::new(*model.clone(), config.inference.clone())?;
|
||||
|
||||
println!("✅ Rust Temporal Neural Solver initialized");
|
||||
println!(" System type: {}", config.model.system_type);
|
||||
println!(" Architecture: {}", config.model.architecture);
|
||||
|
||||
Ok(Self {
|
||||
model,
|
||||
predictor,
|
||||
config,
|
||||
})
|
||||
}
|
||||
|
||||
/// Run a single prediction with timing
|
||||
pub fn predict_timed(&self, input: &DVector<f64>) -> Result<(Prediction, f64)> {
|
||||
let start = Instant::now();
|
||||
let prediction = self.predictor.predict(input)?;
|
||||
let elapsed = start.elapsed();
|
||||
let latency_ms = elapsed.as_secs_f64() * 1000.0;
|
||||
|
||||
Ok((prediction, latency_ms))
|
||||
}
|
||||
|
||||
/// Generate synthetic test data for demonstration
|
||||
pub fn generate_test_data(&self, sequence_length: usize) -> DVector<f64> {
|
||||
let mut data = Vec::new();
|
||||
|
||||
for i in 0..sequence_length {
|
||||
let t = i as f64 / sequence_length as f64;
|
||||
|
||||
// Generate sinusoidal trajectory with noise
|
||||
let x = (2.0 * std::f64::consts::PI * t).sin() + self.random_noise(0.1);
|
||||
let y = (2.0 * std::f64::consts::PI * t).cos() + self.random_noise(0.1);
|
||||
let vx = 2.0 * std::f64::consts::PI * (2.0 * std::f64::consts::PI * t).cos() + self.random_noise(0.05);
|
||||
let vy = -2.0 * std::f64::consts::PI * (2.0 * std::f64::consts::PI * t).sin() + self.random_noise(0.05);
|
||||
|
||||
data.extend_from_slice(&[x, y, vx, vy]);
|
||||
}
|
||||
|
||||
DVector::from_vec(data)
|
||||
}
|
||||
|
||||
/// Generate random noise (simplified - in practice use proper RNG)
|
||||
fn random_noise(&self, std_dev: f64) -> f64 {
|
||||
// Simplified Box-Muller transform for demonstration
|
||||
use std::f64::consts::PI;
|
||||
static mut U1: f64 = 0.0;
|
||||
static mut U2: f64 = 0.0;
|
||||
static mut CACHED: bool = false;
|
||||
|
||||
unsafe {
|
||||
if CACHED {
|
||||
CACHED = false;
|
||||
std_dev * (-2.0 * U1.ln()).sqrt() * (2.0 * PI * U2).sin()
|
||||
} else {
|
||||
U1 = fastrand::f64();
|
||||
U2 = fastrand::f64();
|
||||
CACHED = true;
|
||||
std_dev * (-2.0 * U1.ln()).sqrt() * (2.0 * PI * U2).cos()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run comprehensive benchmark
|
||||
pub fn benchmark(&self, num_iterations: usize) -> Result<PerformanceMetrics> {
|
||||
println!("🏃♂️ Running Rust benchmark ({} iterations)...", num_iterations);
|
||||
|
||||
let mut latencies = Vec::with_capacity(num_iterations);
|
||||
let mut successes = 0;
|
||||
|
||||
// Warmup
|
||||
println!("🔥 Warming up...");
|
||||
for _ in 0..100 {
|
||||
let test_data = self.generate_test_data(10);
|
||||
let _ = self.predictor.predict(&test_data);
|
||||
}
|
||||
|
||||
// Benchmark loop
|
||||
println!("⏱️ Measuring performance...");
|
||||
let benchmark_start = Instant::now();
|
||||
|
||||
for i in 0..num_iterations {
|
||||
if i % 1000 == 0 && i > 0 {
|
||||
println!(" Progress: {}/{}", i, num_iterations);
|
||||
}
|
||||
|
||||
let test_data = self.generate_test_data(10);
|
||||
|
||||
match self.predict_timed(&test_data) {
|
||||
Ok((_, latency_ms)) => {
|
||||
latencies.push(latency_ms);
|
||||
successes += 1;
|
||||
},
|
||||
Err(_) => {
|
||||
// Count failures but continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let total_time = benchmark_start.elapsed();
|
||||
|
||||
// Calculate statistics
|
||||
if latencies.is_empty() {
|
||||
return Err(TemporalNeuralError::BenchmarkError {
|
||||
message: "No successful predictions in benchmark".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
latencies.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
||||
|
||||
let mean_latency = latencies.iter().sum::<f64>() / latencies.len() as f64;
|
||||
let variance = latencies.iter()
|
||||
.map(|x| (x - mean_latency).powi(2))
|
||||
.sum::<f64>() / latencies.len() as f64;
|
||||
let std_latency = variance.sqrt();
|
||||
|
||||
let p99_9_index = ((latencies.len() as f64 * 0.999) as usize).min(latencies.len() - 1);
|
||||
let p99_9_latency = latencies[p99_9_index];
|
||||
|
||||
let throughput = latencies.len() as f64 / total_time.as_secs_f64();
|
||||
let success_rate = (successes as f64 / num_iterations as f64) * 100.0;
|
||||
|
||||
let metrics = PerformanceMetrics {
|
||||
num_samples: latencies.len(),
|
||||
mean_latency_ms: mean_latency,
|
||||
std_latency_ms: std_latency,
|
||||
p99_9_latency_ms: p99_9_latency,
|
||||
throughput_pps: throughput,
|
||||
success_rate,
|
||||
sub_millisecond_achieved: p99_9_latency < 1.0,
|
||||
};
|
||||
|
||||
println!("✅ Benchmark complete!");
|
||||
println!(" Mean latency: {:.3}ms", metrics.mean_latency_ms);
|
||||
println!(" P99.9 latency: {:.3}ms", metrics.p99_9_latency_ms);
|
||||
println!(" Throughput: {:.0} pps", metrics.throughput_pps);
|
||||
println!(" Success rate: {:.1}%", metrics.success_rate);
|
||||
println!(" Sub-millisecond: {}", if metrics.sub_millisecond_achieved { "✅" } else { "❌" });
|
||||
|
||||
Ok(metrics)
|
||||
}
|
||||
|
||||
/// Export model to ONNX format
|
||||
pub fn export_to_onnx(&self, output_path: &str) -> Result<()> {
|
||||
println!("📤 Exporting to ONNX: {}", output_path);
|
||||
|
||||
let exporter = ONNXExporter::new();
|
||||
|
||||
match self.config.model.system_type.as_str() {
|
||||
"A" => {
|
||||
if let Ok(system_a) = self.model.as_any().downcast_ref::<SystemA>() {
|
||||
exporter.export_system_a(system_a, output_path)?;
|
||||
}
|
||||
},
|
||||
"B" => {
|
||||
if let Ok(system_b) = self.model.as_any().downcast_ref::<SystemB>() {
|
||||
exporter.export_system_b(system_b, output_path)?;
|
||||
}
|
||||
},
|
||||
_ => return Err(TemporalNeuralError::ExportError {
|
||||
message: "Unknown system type for export".to_string(),
|
||||
}),
|
||||
}
|
||||
|
||||
println!("✅ ONNX export complete: {}", output_path);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Save benchmark results to file
|
||||
pub fn save_benchmark_results(&self, metrics: &PerformanceMetrics, path: &str) -> Result<()> {
|
||||
let json = serde_json::to_string_pretty(metrics)
|
||||
.map_err(|e| TemporalNeuralError::SerializationError {
|
||||
message: format!("Failed to serialize metrics: {}", e),
|
||||
})?;
|
||||
|
||||
std::fs::write(path, json)
|
||||
.map_err(|e| TemporalNeuralError::IoError {
|
||||
operation: "write_benchmark_results".to_string(),
|
||||
path: path.into(),
|
||||
source: e,
|
||||
})?;
|
||||
|
||||
println!("📄 Benchmark results saved: {}", path);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Demonstration functions
|
||||
pub mod demo {
|
||||
use super::*;
|
||||
|
||||
/// Basic usage demonstration
|
||||
pub fn basic_usage() -> Result<()> {
|
||||
println!("🎯 Basic Usage Demo");
|
||||
println!("==================");
|
||||
|
||||
// Load configuration (you would typically load from file)
|
||||
let config = Config::default_system_b();
|
||||
|
||||
// Create solver
|
||||
let solver = RustTemporalSolver::new("configs/B_temporal_solver.yaml")
|
||||
.or_else(|_| {
|
||||
// Fallback to in-memory config if file doesn't exist
|
||||
println!("⚠️ Config file not found, using default configuration");
|
||||
Ok(RustTemporalSolver {
|
||||
model: Box::new(SystemB::new(config.model.clone())?),
|
||||
predictor: Predictor::new(
|
||||
SystemB::new(config.model.clone())?,
|
||||
config.inference.clone()
|
||||
)?,
|
||||
config,
|
||||
})
|
||||
})?;
|
||||
|
||||
// Generate test data
|
||||
let test_input = solver.generate_test_data(10);
|
||||
println!("📊 Generated test input with {} elements", test_input.len());
|
||||
|
||||
// Run single prediction
|
||||
let (prediction, latency) = solver.predict_timed(&test_input)?;
|
||||
|
||||
println!("✅ Prediction complete:");
|
||||
println!(" Result: {:?}", prediction.value.as_slice());
|
||||
println!(" Latency: {:.3}ms", latency);
|
||||
println!(" Certificate error: {:.6}", prediction.certificate.error);
|
||||
println!(" Sub-millisecond: {}", if latency < 1.0 { "✅" } else { "❌" });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Performance benchmark demonstration
|
||||
pub fn benchmark_demo() -> Result<()> {
|
||||
println!("\n📊 Benchmark Demo");
|
||||
println!("=================");
|
||||
|
||||
let config = Config::default_system_b();
|
||||
let solver = RustTemporalSolver::new("configs/B_temporal_solver.yaml")
|
||||
.or_else(|_| {
|
||||
println!("⚠️ Using default configuration");
|
||||
Ok(RustTemporalSolver {
|
||||
model: Box::new(SystemB::new(config.model.clone())?),
|
||||
predictor: Predictor::new(
|
||||
SystemB::new(config.model.clone())?,
|
||||
config.inference.clone()
|
||||
)?,
|
||||
config,
|
||||
})
|
||||
})?;
|
||||
|
||||
// Run benchmark
|
||||
let metrics = solver.benchmark(5000)?; // Reduced for demo
|
||||
|
||||
// Save results
|
||||
solver.save_benchmark_results(&metrics, "rust_benchmark_results.json")?;
|
||||
|
||||
println!("\n🏆 Benchmark Summary:");
|
||||
println!(" Samples: {}", metrics.num_samples);
|
||||
println!(" Mean latency: {:.3}ms ± {:.3}ms", metrics.mean_latency_ms, metrics.std_latency_ms);
|
||||
println!(" P99.9 latency: {:.3}ms", metrics.p99_9_latency_ms);
|
||||
println!(" Throughput: {:.0} predictions/second", metrics.throughput_pps);
|
||||
println!(" Success rate: {:.1}%", metrics.success_rate);
|
||||
|
||||
println!("\n🎯 Success Criteria:");
|
||||
println!(" Sub-millisecond P99.9: {}", if metrics.sub_millisecond_achieved { "✅" } else { "❌" });
|
||||
println!(" Target 0.9ms P99.9: {}", if metrics.p99_9_latency_ms < 0.9 { "✅" } else { "❌" });
|
||||
println!(" High success rate: {}", if metrics.success_rate > 99.0 { "✅" } else { "❌" });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// ONNX export demonstration
|
||||
pub fn onnx_export_demo() -> Result<()> {
|
||||
println!("\n📤 ONNX Export Demo");
|
||||
println!("===================");
|
||||
|
||||
let config = Config::default_system_b();
|
||||
let solver = RustTemporalSolver::new("configs/B_temporal_solver.yaml")
|
||||
.or_else(|_| {
|
||||
println!("⚠️ Using default configuration");
|
||||
Ok(RustTemporalSolver {
|
||||
model: Box::new(SystemB::new(config.model.clone())?),
|
||||
predictor: Predictor::new(
|
||||
SystemB::new(config.model.clone())?,
|
||||
config.inference.clone()
|
||||
)?,
|
||||
config,
|
||||
})
|
||||
})?;
|
||||
|
||||
// Export to ONNX
|
||||
solver.export_to_onnx("temporal_solver_rust_export.onnx")?;
|
||||
|
||||
println!("✅ ONNX export demonstration complete");
|
||||
println!(" File: temporal_solver_rust_export.onnx");
|
||||
println!(" Ready for deployment with ONNX Runtime");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Real-time simulation demonstration
|
||||
pub fn realtime_simulation() -> Result<()> {
|
||||
println!("\n⚡ Real-time Simulation Demo");
|
||||
println!("===========================");
|
||||
|
||||
let config = Config::default_system_b();
|
||||
let solver = RustTemporalSolver::new("configs/B_temporal_solver.yaml")
|
||||
.or_else(|_| {
|
||||
println!("⚠️ Using default configuration");
|
||||
Ok(RustTemporalSolver {
|
||||
model: Box::new(SystemB::new(config.model.clone())?),
|
||||
predictor: Predictor::new(
|
||||
SystemB::new(config.model.clone())?,
|
||||
config.inference.clone()
|
||||
)?,
|
||||
config,
|
||||
})
|
||||
})?;
|
||||
|
||||
println!("🎮 Simulating real-time inference loop...");
|
||||
|
||||
let mut total_latency = 0.0;
|
||||
let mut max_latency = 0.0;
|
||||
let simulation_steps = 100;
|
||||
|
||||
for step in 0..simulation_steps {
|
||||
// Generate "sensor data"
|
||||
let sensor_data = solver.generate_test_data(10);
|
||||
|
||||
// Run prediction (simulating real-time requirement)
|
||||
let (prediction, latency) = solver.predict_timed(&sensor_data)?;
|
||||
|
||||
total_latency += latency;
|
||||
max_latency = max_latency.max(latency);
|
||||
|
||||
// Simulate real-time constraints
|
||||
if latency > 1.0 {
|
||||
println!("⚠️ Step {}: Latency exceeded 1ms ({:.3}ms)", step, latency);
|
||||
}
|
||||
|
||||
if step % 20 == 0 {
|
||||
println!(" Step {}: {:.3}ms latency", step, latency);
|
||||
}
|
||||
|
||||
// Simulate 10ms control loop
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
}
|
||||
|
||||
let avg_latency = total_latency / simulation_steps as f64;
|
||||
|
||||
println!("✅ Real-time simulation complete:");
|
||||
println!(" Steps: {}", simulation_steps);
|
||||
println!(" Average latency: {:.3}ms", avg_latency);
|
||||
println!(" Maximum latency: {:.3}ms", max_latency);
|
||||
println!(" Real-time capable: {}", if max_latency < 1.0 { "✅" } else { "❌" });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Main entry point for Rust integration example
|
||||
fn main() -> Result<()> {
|
||||
println!("🚀 Temporal Neural Solver - Rust Integration Example");
|
||||
println!("=====================================================");
|
||||
|
||||
// Initialize logging
|
||||
env_logger::init();
|
||||
|
||||
// Run all demonstrations
|
||||
demo::basic_usage()?;
|
||||
demo::benchmark_demo()?;
|
||||
demo::onnx_export_demo()?;
|
||||
demo::realtime_simulation()?;
|
||||
|
||||
println!("\n🎉 Rust integration example complete!");
|
||||
println!("\n💡 Integration Tips:");
|
||||
println!(" • Use RustTemporalSolver for high-performance applications");
|
||||
println!(" • Export to ONNX for cross-platform deployment");
|
||||
println!(" • Monitor latency in production with predict_timed()");
|
||||
println!(" • Implement proper error handling for production use");
|
||||
println!(" • Consider async patterns for concurrent inference");
|
||||
|
||||
println!("\n📚 Next Steps:");
|
||||
println!(" • Integrate into your Rust application");
|
||||
println!(" • Customize for your specific data format");
|
||||
println!(" • Implement production monitoring and alerting");
|
||||
println!(" • Consider GPU acceleration for batch processing");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_solver_creation() {
|
||||
// Test with default configuration
|
||||
let config = Config::default_system_b();
|
||||
|
||||
let result = RustTemporalSolver {
|
||||
model: Box::new(SystemB::new(config.model.clone()).unwrap()),
|
||||
predictor: Predictor::new(
|
||||
SystemB::new(config.model.clone()).unwrap(),
|
||||
config.inference.clone()
|
||||
).unwrap(),
|
||||
config,
|
||||
};
|
||||
|
||||
// Basic validation
|
||||
assert!(true); // If we get here, creation succeeded
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_data_generation() {
|
||||
let config = Config::default_system_b();
|
||||
let solver = RustTemporalSolver {
|
||||
model: Box::new(SystemB::new(config.model.clone()).unwrap()),
|
||||
predictor: Predictor::new(
|
||||
SystemB::new(config.model.clone()).unwrap(),
|
||||
config.inference.clone()
|
||||
).unwrap(),
|
||||
config,
|
||||
};
|
||||
|
||||
let data = solver.generate_test_data(10);
|
||||
assert_eq!(data.len(), 40); // 10 timesteps * 4 features
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_benchmark_structure() {
|
||||
// Test with minimal iterations for fast testing
|
||||
let config = Config::default_system_b();
|
||||
let solver = RustTemporalSolver {
|
||||
model: Box::new(SystemB::new(config.model.clone()).unwrap()),
|
||||
predictor: Predictor::new(
|
||||
SystemB::new(config.model.clone()).unwrap(),
|
||||
config.inference.clone()
|
||||
).unwrap(),
|
||||
config,
|
||||
};
|
||||
|
||||
let metrics = solver.benchmark(10).unwrap();
|
||||
|
||||
assert!(metrics.num_samples > 0);
|
||||
assert!(metrics.mean_latency_ms > 0.0);
|
||||
assert!(metrics.success_rate > 0.0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user