fix(clippy): resolve misc warnings across ML crates

- Implement Display trait instead of inherent to_string() (versioning.rs)
- Replace iter().nth() with .get(), .get(0) with .first()
- Collapse else-if chains, derive Default for enums
- Fix deref warnings, redundant let bindings, push_str('\n')
- Convert match-to-if-let, use saturating_sub, RangeInclusive::contains
- Replace vec![push;push] with vec![...] literal (feature_extraction.rs)
- Remove redundant redefinitions, unnecessary parentheses, casts
- Fix &Vec<_> to &[_] in function parameters

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-03-10 12:12:14 +01:00
parent fca2495a73
commit ccf9e22a55
22 changed files with 115 additions and 133 deletions

View File

@@ -81,23 +81,6 @@ impl SemanticVersion {
})
}
/// Convert to string representation
pub fn to_string(&self) -> String {
let mut version = format!("{}.{}.{}", self.major, self.minor, self.patch);
if let Some(ref pre_release) = self.pre_release {
version.push('-');
version.push_str(pre_release);
}
if let Some(ref build_metadata) = self.build_metadata {
version.push('+');
version.push_str(build_metadata);
}
version
}
/// Check if this version is compatible with another version
pub const fn is_compatible_with(&self, other: &SemanticVersion) -> bool {
// Major version must match for compatibility
@@ -129,6 +112,19 @@ impl SemanticVersion {
}
}
impl std::fmt::Display for SemanticVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)?;
if let Some(ref pre_release) = self.pre_release {
write!(f, "-{pre_release}")?;
}
if let Some(ref build_metadata) = self.build_metadata {
write!(f, "+{build_metadata}")?;
}
Ok(())
}
}
impl PartialOrd for SemanticVersion {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))

View File

@@ -982,7 +982,7 @@ impl DQNAgent {
// Epsilon-greedy selection
let mut rng = rand::thread_rng();
let action_idx = if rng.gen::<f32>() < epsilon {
valid_actions.iter().nth(rng.gen_range(0..valid_actions.len())).copied().unwrap_or(0)
valid_actions.get(rng.gen_range(0..valid_actions.len())).copied().unwrap_or(0)
} else {
valid_actions.iter()
.max_by(|&&a, &&b| q_vec[a].partial_cmp(&q_vec[b]).unwrap_or(std::cmp::Ordering::Equal))

View File

@@ -700,7 +700,7 @@ impl BranchingDuelingQNetwork {
// Q(s, a) = V(s) + (1/D) x sum centered advantages
let inv_d = 1.0_f32 / d as f32;
let scale = Tensor::new(inv_d, &output.value.device())
let scale = Tensor::new(inv_d, output.value.device())
.map_err(|e| MLError::ModelError(format!("Scale tensor: {}", e)))?;
let scaled = centered_sum
.broadcast_mul(&scale)
@@ -742,7 +742,7 @@ impl BranchingDuelingQNetwork {
}
let inv_d = 1.0_f32 / d as f32;
let scale = Tensor::new(inv_d, &output.value.device())
let scale = Tensor::new(inv_d, output.value.device())
.map_err(|e| MLError::ModelError(format!("Scale tensor: {}", e)))?;
let scaled = centered_sum
.broadcast_mul(&scale)

View File

@@ -168,7 +168,7 @@ impl CategoricalDistribution {
// atom_idx = (clipped_val - v_min) / delta_z
// Shape: [batch, num_atoms]
let v_min_broadcast = Tensor::full(self.config.v_min as f32, clipped_target.shape(), device)?;
let delta_z_tensor = Tensor::full(self.delta_z as f32, clipped_target.shape(), device)?;
let delta_z_tensor = Tensor::full(self.delta_z, clipped_target.shape(), device)?;
let atom_indices = ((clipped_target - v_min_broadcast)? / delta_z_tensor)?;
// Step 3: Compute lower and upper atom indices

View File

@@ -856,6 +856,11 @@ impl ExperienceReplayBuffer {
self.buffer.len()
}
/// Check if buffer is empty
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
/// Check if buffer can sample
pub fn can_sample(&self, min_size: usize) -> bool {
self.buffer.len() >= min_size
@@ -947,7 +952,7 @@ impl Sequential {
noisy_layers.push(noisy_output);
} else {
// Standard Linear layers with Xavier initialization
for (i, &hidden_dim) in hidden_dims.into_iter().enumerate() {
for (i, &hidden_dim) in hidden_dims.iter().enumerate() {
let layer_name = format!("hidden_{}", i);
let layer_vb = var_builder.pp(&layer_name);
let layer = linear_xavier(current_dim, hidden_dim, layer_vb).map_err(|e| {
@@ -1483,8 +1488,7 @@ impl DQN {
.broadcast_as((batch_size, num_actions, num_atoms))?;
// Compute expected Q-values: Q(s,a) = Σ(z_i * p_i)
let q_values_dist = (z_probs * atoms_tensor)?.sum(2)?; // Sum over atoms
q_values_dist
(z_probs * atoms_tensor)?.sum(2)? // Sum over atoms
} else if let Some(ref dueling_net) = self.dueling_q_network {
// Wave 11.4: Priority 2 - Dueling only
dueling_net.forward(&state)?
@@ -1630,7 +1634,7 @@ impl DQN {
let branch_actions = super::branching::BranchingDuelingQNetwork::greedy_branch_actions(&output)?;
// branch_actions: [exposure_idx, order_idx, urgency_idx]
let exposure = ExposureLevel::from_index(branch_actions.get(0).copied().unwrap_or(2) as usize)?;
let exposure = ExposureLevel::from_index(branch_actions.first().copied().unwrap_or(2) as usize)?;
let order = OrderType::from_index(branch_actions.get(1).copied().unwrap_or(0) as usize)?;
let urgency = Urgency::from_index(branch_actions.get(2).copied().unwrap_or(1) as usize)?;
FactoredAction { exposure, order, urgency }
@@ -2810,9 +2814,9 @@ impl DQN {
let next_state_embed = self.get_state_embedding(&next_states_tensor)?;
// Sample random quantiles for training (IQN: τ ~ Uniform(0,1))
let taus = iqn_net.sample_random_quantiles(batch_size, &device)
let taus = iqn_net.sample_random_quantiles(batch_size, device)
.map_err(|e| MLError::TrainingError(format!("Failed to sample taus: {}", e)))?;
let target_taus = iqn_target.sample_random_quantiles(batch_size, &device)
let target_taus = iqn_target.sample_random_quantiles(batch_size, device)
.map_err(|e| MLError::TrainingError(format!("Failed to sample target taus: {}", e)))?;
// Forward through IQN: [batch, num_actions, num_quantiles]
@@ -2857,11 +2861,11 @@ impl DQN {
let rewards_broadcast = rewards_tensor
.unsqueeze(1)?
.broadcast_as((batch_size, num_quantiles))?;
let not_done_broadcast = (Tensor::ones(&[batch_size], dtype, &device)? - &dones_tensor)?
let not_done_broadcast = (Tensor::ones(&[batch_size], dtype, device)? - &dones_tensor)?
.unsqueeze(1)?
.broadcast_as((batch_size, num_quantiles))?;
let gamma_t = Tensor::full(gamma, &[batch_size, num_quantiles], &device)
let gamma_t = Tensor::full(gamma, &[batch_size, num_quantiles], device)
.map_err(|e| MLError::TrainingError(format!("Failed to create gamma tensor: {}", e)))?
.to_dtype(dtype)
.map_err(|e| MLError::TrainingError(format!("Failed to cast gamma tensor: {}", e)))?;
@@ -2887,8 +2891,7 @@ impl DQN {
.map_err(|e| MLError::TrainingError(format!("Failed to cast weights tensor: {}", e)))?
.detach()
};
let weighted_loss = (per_sample_loss * weights_tensor)?.mean_all()?;
weighted_loss
(per_sample_loss * weights_tensor)?.mean_all()?
} else if self.dist_dueling_q_network.is_some() {
// C51 CATEGORICAL LOSS PATH (Hybrid: Distributional + Dueling)
@@ -3042,9 +3045,7 @@ impl DQN {
.map_err(|e| MLError::TrainingError(format!("Failed to cast weights tensor: {}", e)))?
.detach()
};
let weighted_loss = (per_sample_loss * weights_tensor)?.mean_all()?;
weighted_loss
(per_sample_loss * weights_tensor)?.mean_all()?
} else {
// STANDARD DQN SCALAR LOSS PATH (MSE/Huber)
@@ -3311,7 +3312,7 @@ impl DQN {
let loss_clamped = result.loss_f32_tensor.clamp(0.0_f64, 1e6_f64)?;
// Squeeze grad_norm from [1] → scalar for consistent accumulation
let grad_norm_scalar = if grad_norm_tensor.dims().len() > 0 {
let grad_norm_scalar = if !grad_norm_tensor.dims().is_empty() {
grad_norm_tensor.squeeze(0)?
} else {
grad_norm_tensor
@@ -3351,7 +3352,7 @@ impl DQN {
// Squeeze grad_norm from [1] → rank-0 scalar tensor (GPU path only)
#[cfg(feature = "cuda")]
let grad_norm_scalar_tensor = if _grad_norm_tensor.dims().len() > 0 {
let grad_norm_scalar_tensor = if !_grad_norm_tensor.dims().is_empty() {
_grad_norm_tensor.squeeze(0)?
} else {
_grad_norm_tensor
@@ -3893,7 +3894,7 @@ impl DQN {
/// to eliminate triple-stacking (noisy + epsilon + count bonus).
pub const fn get_effective_epsilon(&self) -> f32 {
if self.config.use_noisy_nets {
self.config.noisy_epsilon_floor.max(0.02) as f32 // Match actual action selection logic
self.config.noisy_epsilon_floor.max(0.02) // Match actual action selection logic
} else {
self.epsilon
}

View File

@@ -202,9 +202,7 @@ fn calculate_sharpe_ratio(returns: &[f64]) -> f64 {
}
// Annualize: sqrt(252 trades/year) assuming daily trades
let sharpe = (mean_return / std_dev) * (252.0_f64).sqrt();
sharpe
(mean_return / std_dev) * (252.0_f64).sqrt()
}
/// Calculate Sortino ratio, focusing on downside deviation

View File

@@ -28,7 +28,7 @@ impl BacktestReport {
if let Some(ref _baseline) = self.baseline_results {
_ = writeln!(report, "**Baseline**: {}\n", self.baseline_name);
} else {
report.push_str("\n");
report.push('\n');
}
// Results table
@@ -104,7 +104,7 @@ impl BacktestReport {
"",
);
report.push_str("\n");
report.push('\n');
// Summary
report.push_str("## Summary\n\n");

View File

@@ -171,8 +171,8 @@ impl HindsightReplayBuffer {
// Shuffle while preserving correspondence
let mut combined: Vec<_> = all_experiences.into_iter()
.zip(all_weights.into_iter())
.zip(all_indices.into_iter())
.zip(all_weights)
.zip(all_indices)
.collect();
combined.shuffle(&mut thread_rng());

View File

@@ -68,7 +68,7 @@ impl NStepBuffer {
///
/// Panics if n < 1 or n > 10 (sanity check)
pub fn new(n: usize, gamma: f64) -> Self {
assert!(n >= 1 && n <= 10, "n must be in range 1-10 (got {})", n);
assert!((1..=10).contains(&n), "n must be in range 1-10 (got {})", n);
assert!(gamma > 0.0 && gamma <= 1.0, "gamma must be in (0, 1] (got {})", gamma);
Self {

View File

@@ -566,7 +566,7 @@ impl PrioritizedReplayBuffer {
let mut max_priority = f32::from_bits(self.max_priority.load(Ordering::Acquire) as u32);
let mut update_count = 0;
for (&idx, &priority) in indices.into_iter().zip(priorities.into_iter()) {
for (&idx, &priority) in indices.iter().zip(priorities.iter()) {
if idx >= self.config.capacity {
continue;
}
@@ -701,7 +701,7 @@ impl PrioritizedReplayBuffer {
metrics.priority_percentiles[4] =
sampled_priorities.get(safe_idx(9)).copied().unwrap_or(0.0); // 90th percentile
metrics.min_priority = sampled_priorities.get(0).copied().unwrap_or(0.0);
metrics.min_priority = sampled_priorities.first().copied().unwrap_or(0.0);
metrics.max_priority = sampled_priorities.get(len - 1).copied().unwrap_or(0.0);
}
}

View File

@@ -36,8 +36,8 @@ pub struct RainbowMetrics {
pub exploration_rate: f64,
}
impl RainbowMetrics {
pub const fn new() -> Self {
impl Default for RainbowMetrics {
fn default() -> Self {
Self {
total_steps: 0,
total_episodes: 0,
@@ -47,6 +47,12 @@ impl RainbowMetrics {
}
}
impl RainbowMetrics {
pub fn new() -> Self {
Self::default()
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -14,8 +14,9 @@ use super::noisy_layers::NoisyLinear;
use ml_core::MLError;
/// Activation function types
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
pub enum ActivationType {
#[default]
ReLU,
LeakyReLU,
Swish,
@@ -24,12 +25,6 @@ pub enum ActivationType {
Mish,
}
impl Default for ActivationType {
fn default() -> Self {
ActivationType::ReLU
}
}
/// Rainbow network configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RainbowNetworkConfig {
@@ -215,29 +210,27 @@ impl RainbowNetwork {
})?,
)
}
} else if config.use_noisy_layers {
Box::new(NoisyLinear::new(
final_feature_size,
config.num_actions * num_atoms,
vs.pp("action_dist"),
config.noisy_sigma_init,
)?)
} else {
if config.use_noisy_layers {
Box::new(NoisyLinear::new(
Box::new(
candle_nn::linear(
final_feature_size,
config.num_actions * num_atoms,
vs.pp("action_dist"),
config.noisy_sigma_init,
)?)
} else {
Box::new(
candle_nn::linear(
final_feature_size,
config.num_actions * num_atoms,
vs.pp("action_dist"),
)
.map_err(|e| {
MLError::ModelError(format!(
"Failed to create action distribution layer: {}",
e
))
})?,
)
}
.map_err(|e| {
MLError::ModelError(format!(
"Failed to create action distribution layer: {}",
e
))
})?,
)
};
let dropout =

View File

@@ -778,7 +778,7 @@ impl RegimeConditionalDQN {
for var in vars {
if let Some(src_grad) = source.get(var.as_tensor()) {
if let Some(existing) = t.get(var.as_tensor()) {
let summed = existing.add(&src_grad).map_err(|e| {
let summed = existing.add(src_grad).map_err(|e| {
MLError::TrainingError(format!("Gradient merge failed: {}", e))
})?;
t.insert(var.as_tensor(), summed);
@@ -865,7 +865,7 @@ impl RegimeConditionalDQN {
for var in vars {
if let Some(src_grad) = source.get(var.as_tensor()) {
if let Some(existing) = t.get(var.as_tensor()) {
let summed = existing.add(&src_grad).map_err(|e| {
let summed = existing.add(src_grad).map_err(|e| {
MLError::TrainingError(format!("Gradient merge failed: {}", e))
})?;
t.insert(var.as_tensor(), summed);

View File

@@ -182,11 +182,7 @@ impl ReplayBuffer {
// Transfer experiences (keep most recent if shrinking)
let to_keep = current_size.min(new_capacity);
let start = if current_size > new_capacity {
current_size - new_capacity
} else {
0
};
let start = current_size.saturating_sub(new_capacity);
for i in 0..to_keep {
new_buffer[i] = buffer[(start + i) % self.config.capacity].clone();

View File

@@ -686,9 +686,9 @@ impl RewardFunction {
}
} else {
// Legacy path: Sharpe blend + optional EMA normalization (unchanged)
let pnl_normalized_f64: f64 = ((next_state.portfolio_features.get(0).unwrap_or(&100000.0)
- current_state.portfolio_features.get(0).unwrap_or(&100000.0))
/ current_state.portfolio_features.get(0).unwrap_or(&100000.0)) as f64;
let pnl_normalized_f64: f64 = ((next_state.portfolio_features.first().unwrap_or(&100000.0)
- current_state.portfolio_features.first().unwrap_or(&100000.0))
/ current_state.portfolio_features.first().unwrap_or(&100000.0)) as f64;
// Update returns buffer for Sharpe calculation
self.returns_buffer.push_back(pnl_normalized_f64);
@@ -812,21 +812,21 @@ impl RewardFunction {
target_exposure: f64,
) -> Result<Decimal, MLError> {
// Validate portfolio_features length (defensive check)
if current_state.portfolio_features.len() < 1 {
if current_state.portfolio_features.is_empty() {
tracing::debug!(
"Current state portfolio_features is empty, using 100000.0 for portfolio value"
);
}
if next_state.portfolio_features.len() < 1 {
if next_state.portfolio_features.is_empty() {
tracing::debug!("Next state portfolio_features is empty, using 100000.0 for portfolio value");
}
let default_val = Decimal::try_from(100000.0).unwrap_or(Decimal::ONE_HUNDRED);
let current_value =
Decimal::try_from(*current_state.portfolio_features.get(0).unwrap_or(&100000.0) as f64)
Decimal::try_from(*current_state.portfolio_features.first().unwrap_or(&100000.0) as f64)
.unwrap_or(default_val);
let next_value =
Decimal::try_from(*next_state.portfolio_features.get(0).unwrap_or(&100000.0) as f64)
Decimal::try_from(*next_state.portfolio_features.first().unwrap_or(&100000.0) as f64)
.unwrap_or(default_val);
// Bug #17 Fix: Use percentage-based P&L or absolute dollar change
@@ -988,7 +988,7 @@ impl RewardFunction {
// This assumes position is normalized (0-1 range)
#[allow(unused_variables)]
let _portfolio_value =
Decimal::try_from(*current_state.portfolio_features.get(0).unwrap_or(&100000.0) as f64)
Decimal::try_from(*current_state.portfolio_features.first().unwrap_or(&100000.0) as f64)
.unwrap_or(Decimal::try_from(100000.0).unwrap_or(Decimal::ZERO));
// Apply urgency multiplier: Patient=0.5x, Normal=1.0x, Aggressive=1.5x

View File

@@ -87,34 +87,29 @@ impl FeatureExtractor {
for i in 0..bars.len() {
let bar = &bars[i];
let mut feature_vec = Vec::with_capacity(15);
// Features 1-5: OHLCV (normalized)
feature_vec.push(bar.open as f32);
feature_vec.push(bar.high as f32);
feature_vec.push(bar.low as f32);
feature_vec.push(bar.close as f32);
feature_vec.push(bar.volume as f32);
// Feature 6: RSI
feature_vec.push(rsi_values.get(i).copied().unwrap_or(50.0) as f32);
// Features 7-8: EMA
feature_vec.push(ema_fast.get(i).copied().unwrap_or(bar.close) as f32);
feature_vec.push(ema_slow.get(i).copied().unwrap_or(bar.close) as f32);
// Features 9-11: MACD
feature_vec.push(macd_line.get(i).copied().unwrap_or(0.0) as f32);
feature_vec.push(macd_signal.get(i).copied().unwrap_or(0.0) as f32);
feature_vec.push(macd_hist.get(i).copied().unwrap_or(0.0) as f32);
// Features 12-14: Bollinger Bands
feature_vec.push(bb_upper.get(i).copied().unwrap_or(bar.high) as f32);
feature_vec.push(bb_middle.get(i).copied().unwrap_or(bar.close) as f32);
feature_vec.push(bb_lower.get(i).copied().unwrap_or(bar.low) as f32);
// Feature 15: ATR
feature_vec.push(atr.get(i).copied().unwrap_or(0.0) as f32);
let feature_vec = vec![
// Features 1-5: OHLCV (normalized)
bar.open as f32,
bar.high as f32,
bar.low as f32,
bar.close as f32,
bar.volume as f32,
// Feature 6: RSI
rsi_values.get(i).copied().unwrap_or(50.0) as f32,
// Features 7-8: EMA
ema_fast.get(i).copied().unwrap_or(bar.close) as f32,
ema_slow.get(i).copied().unwrap_or(bar.close) as f32,
// Features 9-11: MACD
macd_line.get(i).copied().unwrap_or(0.0) as f32,
macd_signal.get(i).copied().unwrap_or(0.0) as f32,
macd_hist.get(i).copied().unwrap_or(0.0) as f32,
// Features 12-14: Bollinger Bands
bb_upper.get(i).copied().unwrap_or(bar.high) as f32,
bb_middle.get(i).copied().unwrap_or(bar.close) as f32,
bb_lower.get(i).copied().unwrap_or(bar.low) as f32,
// Feature 15: ATR
atr.get(i).copied().unwrap_or(0.0) as f32,
];
features.push(feature_vec);
}

View File

@@ -95,10 +95,9 @@ pub fn compute_ofi_from_file(file_path: &Path) -> Result<Vec<[f64; 8]>, MLError>
let snapshot_count = parser
.parse_mbp10_streaming(file_path, 100, |snapshot| {
match calculator.calculate(snapshot) {
Ok(f) => features.push(f.to_array()),
Err(_) => {} // Skip failed calculations (e.g. first snapshot with no prev)
}
if let Ok(f) = calculator.calculate(snapshot) {
features.push(f.to_array());
} // Skip failed calculations (e.g. first snapshot with no prev)
})
.map_err(|e| MLError::InsufficientData(format!("MBP-10 streaming parse failed: {}", e)))?;
@@ -168,10 +167,9 @@ pub fn compute_ofi_with_trades(
trade_cursor += 1;
}
match calculator.calculate(snapshot) {
Ok(f) => features.push(f.to_array()),
Err(_) => {} // Skip failed calculations (e.g. first snapshot)
}
if let Ok(f) = calculator.calculate(snapshot) {
features.push(f.to_array());
} // Skip failed calculations (e.g. first snapshot)
})
.map_err(|e| MLError::InsufficientData(format!("MBP-10 streaming parse failed: {}", e)))?;

View File

@@ -173,7 +173,7 @@ impl RegimeTransitionMatrix {
(1.0 - alpha) * self.transition_matrix[from_idx][j] + alpha;
} else {
// Other transitions: decrease probability
self.transition_matrix[from_idx][j] *= (1.0 - alpha);
self.transition_matrix[from_idx][j] *= 1.0 - alpha;
}
}

View File

@@ -384,10 +384,10 @@ impl StressTestOrchestrator {
config: self.config.clone(),
total_duration,
phase_results,
total_predictions: total_predictions,
total_errors: total_errors,
total_predictions,
total_errors,
error_rate,
latency_stats: latency_stats,
latency_stats,
requirements_met,
throughput_achieved: total_predictions as f64 / total_duration.as_secs_f64(),
recommendations,

View File

@@ -38,7 +38,7 @@ pub fn sigmoid(x: FixedPoint) -> Result<FixedPoint> {
let fraction = (clamped_x / denominator)?;
((half * fraction)? + half)
(half * fraction)? + half
}
/// Fast tanh approximation using fixed-point arithmetic
@@ -112,7 +112,7 @@ pub fn gelu(x: FixedPoint) -> Result<FixedPoint> {
// 0.5 * x * (1 + tanh(...))
((half * x)? * one_plus_tanh)
(half * x)? * one_plus_tanh
}
/// Linear activation (identity function)

View File

@@ -1598,7 +1598,6 @@ impl Mamba2SSM {
/// Forward pass with gradient computation enabled
pub fn forward_with_gradients(&mut self, input: &Tensor) -> Result<Tensor, MLError> {
// Gradient flow enabled - do not detach
let input = input;
// Input projection with gradients
let mut hidden = self.input_projection.forward(input)?;

View File

@@ -708,7 +708,7 @@ impl TGGN {
/// Restore message passing weights from checkpoint state
pub const fn restore_message_passing_weights(
&mut self,
_weights: &Vec<Vec<f32>>,
_weights: &[Vec<f32>],
) -> Result<(), MLError> {
// Production implementation for now - message passing weights would be restored here
Ok(())