Repository navigation
feat: add learning rate scheduler integration and refactor diffusion architecture - #574
Conversation
…izers - Add SchedulerStepMode enum with StepPerBatch, StepPerEpoch, WarmupThenEpoch modes - Add LearningRateScheduler and SchedulerStepMode properties to GradientBasedOptimizerOptions - Add scheduler fields and step tracking to GradientBasedOptimizerBase - Add OnEpochEnd(), OnBatchEnd(), StepScheduler() methods for scheduler control - Add IsInWarmupPhase() for warmup detection heuristics - Update Reset() to properly reset scheduler state 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
… to inoisescheduler - Move src/Diffusion/ to src/NeuralNetworks/Diffusion/ - Rename IStepScheduler to INoiseScheduler to avoid confusion with LR schedulers - Rename StepSchedulerBase to NoiseSchedulerBase - Update namespace from AiDotNet.Diffusion to AiDotNet.NeuralNetworks.Diffusion - Update all references in IDiffusionModel, DiffusionModelBase, DDPMModel - Update scheduler implementations: DDIMScheduler, PNDMScheduler, SchedulerConfig This clarifies that noise schedulers are specific to diffusion models, while learning rate schedulers (ILearningRateScheduler) control optimization. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Warning Rate limit exceeded@ooples has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 20 minutes and 23 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (4)
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughRenames diffusion scheduler types and moves diffusion code into a NeuralNetworks namespace, integrates learning-rate scheduler support into gradient-based optimizers (new enum and lifecycle hooks), renames optimizer learning-rate properties to InitialLearningRate, and adds integration tests for optimizer + scheduler behaviors. Changes
Sequence Diagram(s)mermaid Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/Optimizers/GradientBasedOptimizerBase.cs (1)
1031-1046: Warmup heuristic may misfire after learning rate decay.The heuristic
currentLr < baseLrwill returntruefor any scheduler that has decayed below the base rate (e.g., step decay, cosine annealing, reduce-on-plateau), not just during warmup. This could causeWarmupThenEpochmode to unexpectedly switch back to per-batch stepping after the learning rate decays.Consider querying the scheduler directly for warmup state:
🔎 Proposed improvement
protected virtual bool IsInWarmupPhase() { if (_learningRateScheduler == null) { return false; } - // Check if the scheduler supports warmup detection - // A scheduler is in warmup if current LR is below base LR and still increasing - var currentLr = _learningRateScheduler.CurrentLearningRate; - var baseLr = _learningRateScheduler.BaseLearningRate; - - // If current LR is less than base LR, we're likely still in warmup - // This is a heuristic - specific schedulers may override this behavior - return currentLr < baseLr; + // Query the scheduler directly if it supports warmup detection + if (_learningRateScheduler is IWarmupAwareScheduler warmupScheduler) + { + return warmupScheduler.IsInWarmupPhase; + } + + // Fallback: check if current LR is below base LR AND we're in early steps + // This adds a step threshold to avoid false positives after decay + var currentLr = _learningRateScheduler.CurrentLearningRate; + var baseLr = _learningRateScheduler.BaseLearningRate; + return currentLr < baseLr && _currentStep < 1000; // Reasonable warmup threshold }Alternatively, introduce an
IWarmupAwareSchedulerinterface that warmup-capable schedulers can implement to provide accurate warmup state detection.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
src/Interfaces/IDiffusionModel.cssrc/Interfaces/INoiseScheduler.cssrc/LearningRateSchedulers/SchedulerStepMode.cssrc/Models/Options/GradientBasedOptimizerOptions.cssrc/NeuralNetworks/Diffusion/DDPMModel.cssrc/NeuralNetworks/Diffusion/DiffusionModelBase.cssrc/NeuralNetworks/Diffusion/Schedulers/DDIMScheduler.cssrc/NeuralNetworks/Diffusion/Schedulers/NoiseSchedulerBase.cssrc/NeuralNetworks/Diffusion/Schedulers/PNDMScheduler.cssrc/NeuralNetworks/Diffusion/Schedulers/SchedulerConfig.cssrc/Optimizers/GradientBasedOptimizerBase.cs
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-12-18T08:49:25.295Z
Learnt from: ooples
Repo: ooples/AiDotNet PR: 444
File: src/Interfaces/IPruningMask.cs:1-102
Timestamp: 2025-12-18T08:49:25.295Z
Learning: In the AiDotNet repository, the project-level global using includes AiDotNet.Tensors.LinearAlgebra via AiDotNet.csproj. Therefore, Vector<T>, Matrix<T>, and Tensor<T> are available without per-file using directives. Do not flag missing using directives for these types in any C# files within this project. Apply this guideline broadly to all C# files (not just a single file) to avoid false positives. If a file uses a type from a different namespace not covered by the global using, flag as usual.
Applied to files:
src/NeuralNetworks/Diffusion/Schedulers/DDIMScheduler.cssrc/Interfaces/IDiffusionModel.cssrc/LearningRateSchedulers/SchedulerStepMode.cssrc/Interfaces/INoiseScheduler.cssrc/NeuralNetworks/Diffusion/Schedulers/NoiseSchedulerBase.cssrc/NeuralNetworks/Diffusion/Schedulers/PNDMScheduler.cssrc/NeuralNetworks/Diffusion/DiffusionModelBase.cssrc/NeuralNetworks/Diffusion/Schedulers/SchedulerConfig.cssrc/Models/Options/GradientBasedOptimizerOptions.cssrc/NeuralNetworks/Diffusion/DDPMModel.cssrc/Optimizers/GradientBasedOptimizerBase.cs
📚 Learning: 2025-12-18T08:49:53.103Z
Learnt from: ooples
Repo: ooples/AiDotNet PR: 444
File: src/Interfaces/IPruningStrategy.cs:1-4
Timestamp: 2025-12-18T08:49:53.103Z
Learning: In this repository, global using directives are declared in AiDotNet.csproj for core namespaces (AiDotNet.Tensors.* and AiDotNet.*) and common system types. When reviewing C# files, assume these global usings are in effect; avoid adding duplicate using statements for these namespaces and for types like Vector<T>, Matrix<T>, Tensor<T>, etc. If a type is not found, verify the global usings or consider adding a file-scoped using if needed. Prefer relying on global usings to reduce boilerplate.
Applied to files:
src/NeuralNetworks/Diffusion/Schedulers/DDIMScheduler.cssrc/Interfaces/IDiffusionModel.cssrc/LearningRateSchedulers/SchedulerStepMode.cssrc/Interfaces/INoiseScheduler.cssrc/NeuralNetworks/Diffusion/Schedulers/NoiseSchedulerBase.cssrc/NeuralNetworks/Diffusion/Schedulers/PNDMScheduler.cssrc/NeuralNetworks/Diffusion/DiffusionModelBase.cssrc/NeuralNetworks/Diffusion/Schedulers/SchedulerConfig.cssrc/Models/Options/GradientBasedOptimizerOptions.cssrc/NeuralNetworks/Diffusion/DDPMModel.cssrc/Optimizers/GradientBasedOptimizerBase.cs
📚 Learning: 2025-12-19T19:05:13.598Z
Learnt from: ooples
Repo: ooples/AiDotNet PR: 445
File: src/Interfaces/IPredictionModelBuilder.cs:7-8
Timestamp: 2025-12-19T19:05:13.598Z
Learning: In ooples/AiDotNet, IAggregationStrategy<TModel> is declared in AiDotNet.Interfaces (src/Interfaces/IAggregationStrategy.cs). For src/Interfaces/IPredictionModelBuilder.cs (also in AiDotNet.Interfaces), referencing IAggregationStrategy does not require an additional using. Avoid flagging missing using directives for this case in future reviews.
Applied to files:
src/NeuralNetworks/Diffusion/DiffusionModelBase.cssrc/Optimizers/GradientBasedOptimizerBase.cs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: CodeQL analysis (csharp)
- GitHub Check: Agent
- GitHub Check: CodeQL Analysis
- GitHub Check: Build (Windows)
🔇 Additional comments (15)
src/Models/Options/GradientBasedOptimizerOptions.cs (1)
131-164: LGTM! Clean integration of scheduler options.The nullable
ILearningRateScheduler?property withnulldefaulting to constant learning rate maintains backward compatibility. TheStepPerEpochdefault aligns with the documented best practice for most schedulers.src/LearningRateSchedulers/SchedulerStepMode.cs (1)
1-45: Well-designed enum with clear semantics.The three modes cover the common scheduling patterns. The documentation clearly explains when to use each mode, with the
WarmupThenEpochmode being particularly useful for transformer-style training workflows.src/Optimizers/GradientBasedOptimizerBase.cs (5)
91-122: LGTM!The protected fields appropriately allow derived optimizer classes to access scheduler state if needed for specialized behavior.
163-172: LGTM!Correctly synchronizes the initial learning rate from the scheduler when provided, ensuring consistency between the optimizer and scheduler states from the start.
913-928: LGTM!The reset logic properly mirrors the constructor initialization, ensuring a clean slate for scheduler state when the optimizer is reset.
966-1018: LGTM!The lifecycle hooks correctly implement all three stepping modes. The
WarmupThenEpochlogic properly transitions from per-batch stepping during warmup to per-epoch stepping afterward.
1048-1070: LGTM!Clean public API for monitoring learning rate and training progress. Useful for logging, debugging, and implementing custom training loops.
src/Interfaces/INoiseScheduler.cs (1)
1-188: LGTM! Clean interface rename with excellent documentation.The interface rename from
IStepScheduler<T>toINoiseScheduler<T>effectively distinguishes diffusion noise schedulers from learning rate schedulers. The updated documentation (lines 16-19) clearly explains the rationale. Namespace migration toAiDotNet.NeuralNetworks.Diffusion.Schedulersaligns with the broader reorganization.src/NeuralNetworks/Diffusion/Schedulers/SchedulerConfig.cs (1)
3-3: LGTM! Namespace updated to match new directory structure.The namespace change aligns with the relocation of diffusion schedulers to
AiDotNet.NeuralNetworks.Diffusion.Schedulers.src/NeuralNetworks/Diffusion/Schedulers/DDIMScheduler.cs (1)
1-1: LGTM! Base class and namespace updated consistently.The namespace migration and base class rename from
StepSchedulerBase<T>toNoiseSchedulerBase<T>are consistent with the broader refactoring. All implementation logic remains unchanged.Also applies to: 34-34
src/NeuralNetworks/Diffusion/Schedulers/PNDMScheduler.cs (1)
1-1: LGTM! Consistent refactoring applied to PNDM scheduler.The namespace and base class updates match the pattern across all scheduler implementations. The complex PRK/PLMS logic remains intact.
Also applies to: 38-38
src/Interfaces/IDiffusionModel.cs (1)
36-36: LGTM! Interface updated to use renamed scheduler type.The
Schedulerproperty type updated fromIStepScheduler<T>toINoiseScheduler<T>, with corresponding documentation reference updated on line 36.Also applies to: 59-59
src/NeuralNetworks/Diffusion/DiffusionModelBase.cs (1)
3-3: LGTM! Comprehensive scheduler type updates applied consistently.All references to
IStepScheduler<T>updated toINoiseScheduler<T>throughout the base class (private field line 49, public property line 72, constructor parameter line 88). Namespace migration toAiDotNet.NeuralNetworks.Diffusionaligns with the reorganization.Also applies to: 12-12, 49-49, 72-72, 88-88
src/NeuralNetworks/Diffusion/DDPMModel.cs (1)
1-1: LGTM! Complete scheduler type migration in DDPM model.All constructor signatures (lines 104, 119) and the
DeepCopymethod (line 204) updated to useINoiseScheduler<T>. Namespace migration toAiDotNet.NeuralNetworks.Diffusionis consistent.Also applies to: 5-5, 104-104, 119-119, 204-204
src/NeuralNetworks/Diffusion/Schedulers/NoiseSchedulerBase.cs (1)
4-4: LGTM! Base class rename executed comprehensively.The class rename from
StepSchedulerBase<T>toNoiseSchedulerBase<T>is complete and well-documented. The explanation in lines 17-19 clarifies the distinction from learning rate schedulers. All implementation logic remains unchanged - this is a pure naming refactor.Also applies to: 7-31, 89-94
There was a problem hiding this comment.
Pull request overview
This PR implements learning rate scheduler integration into gradient-based optimizers and performs a major refactoring of the diffusion model architecture to improve code organization and naming clarity.
Key Changes:
- Adds comprehensive learning rate scheduler support to all 25 gradient-based optimizers via
GradientBasedOptimizerBase, including configurable step modes (per-batch, per-epoch, warmup-then-epoch) - Moves diffusion model code from
src/Diffusion/tosrc/NeuralNetworks/Diffusion/for better architectural organization - Renames diffusion-related interfaces and classes (
IStepScheduler→INoiseScheduler,StepSchedulerBase→NoiseSchedulerBase) to avoid confusion with learning rate schedulers
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
src/LearningRateSchedulers/SchedulerStepMode.cs |
New enum defining when schedulers should step (per-batch, per-epoch, or warmup-then-epoch modes) |
src/Models/Options/GradientBasedOptimizerOptions.cs |
Adds LearningRateScheduler and SchedulerStepMode properties to optimizer configuration |
src/Optimizers/GradientBasedOptimizerBase.cs |
Integrates scheduler support with step tracking, lifecycle methods (OnEpochEnd, OnBatchEnd), and warmup detection logic |
src/NeuralNetworks/Diffusion/Schedulers/SchedulerConfig.cs |
Updates namespace from AiDotNet.Diffusion.Schedulers to AiDotNet.NeuralNetworks.Diffusion.Schedulers |
src/NeuralNetworks/Diffusion/Schedulers/NoiseSchedulerBase.cs |
Renames from StepSchedulerBase and updates namespace; adds documentation explaining the rename |
src/NeuralNetworks/Diffusion/Schedulers/PNDMScheduler.cs |
Updates base class reference and namespace |
src/NeuralNetworks/Diffusion/Schedulers/DDIMScheduler.cs |
Updates base class reference and namespace |
src/NeuralNetworks/Diffusion/DiffusionModelBase.cs |
Updates to use INoiseScheduler interface and new namespace |
src/NeuralNetworks/Diffusion/DDPMModel.cs |
Updates to use INoiseScheduler interface and new namespace |
src/Interfaces/INoiseScheduler.cs |
Renames from IStepScheduler and updates namespace; adds documentation explaining the rename |
src/Interfaces/IDiffusionModel.cs |
Updates interface reference from IStepScheduler to INoiseScheduler |
Comments suppressed due to low confidence (1)
src/NeuralNetworks/Diffusion/Schedulers/SchedulerConfig.cs:3
- The namespace change from
AiDotNet.DiffusiontoAiDotNet.NeuralNetworks.Diffusionand the interface rename fromIStepSchedulertoINoiseSchedulerare breaking changes that will cause compilation errors in existing test files. Multiple test files in the repository still reference the old namespaces and interface names (e.g.,tests/AiDotNet.Tests/UnitTests/Diffusion/Models/DDPMModelTests.cs,tests/AiDotNet.Tests/IntegrationTests/Diffusion/DiffusionSchedulersIntegrationTests.cs, and others). These test files must be updated as part of this PR to use the new namespaces and interface names to ensure the build passes.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Update test file imports to use AiDotNet.NeuralNetworks.Diffusion namespace after the diffusion module was moved from AiDotNet.Diffusion. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…r check The previous heuristic (currentLr < baseLr) incorrectly identified decay phases as warmup when the learning rate dropped below the base learning rate. This caused incorrect scheduler stepping behavior with WarmupThenEpoch mode. Now checks if the scheduler is a LinearWarmupScheduler and uses its explicit WarmupSteps property for accurate detection. For other schedulers, returns false as warmup cannot be reliably detected without explicit support. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/Optimizers/GradientBasedOptimizerBase.cs (1)
1032-1050: Improved warmup detection logic.The explicit
LinearWarmupSchedulertype check withWarmupStepsproperty is a robust solution that addresses the previous review concern about the flawed heuristic. Returningfalsefor other schedulers is a safe default that prevents incorrect behavior withWarmupThenEpochmode.For future extensibility, consider defining an
IWarmupSchedulerinterface withWarmupStepsproperty if more scheduler types need warmup support.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/Optimizers/GradientBasedOptimizerBase.cstests/AiDotNet.Tests/IntegrationTests/Diffusion/DiffusionSchedulersIntegrationTests.cstests/AiDotNet.Tests/UnitTests/Diffusion/Models/DDPMModelTests.cstests/AiDotNet.Tests/UnitTests/Diffusion/Schedulers/DDIMSchedulerTests.cstests/AiDotNet.Tests/UnitTests/Diffusion/Schedulers/PNDMSchedulerTests.cstests/AiDotNet.Tests/UnitTests/Diffusion/Schedulers/SchedulerConfigTests.cs
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-12-18T08:49:25.295Z
Learnt from: ooples
Repo: ooples/AiDotNet PR: 444
File: src/Interfaces/IPruningMask.cs:1-102
Timestamp: 2025-12-18T08:49:25.295Z
Learning: In the AiDotNet repository, the project-level global using includes AiDotNet.Tensors.LinearAlgebra via AiDotNet.csproj. Therefore, Vector<T>, Matrix<T>, and Tensor<T> are available without per-file using directives. Do not flag missing using directives for these types in any C# files within this project. Apply this guideline broadly to all C# files (not just a single file) to avoid false positives. If a file uses a type from a different namespace not covered by the global using, flag as usual.
Applied to files:
tests/AiDotNet.Tests/UnitTests/Diffusion/Schedulers/PNDMSchedulerTests.cstests/AiDotNet.Tests/UnitTests/Diffusion/Schedulers/SchedulerConfigTests.cstests/AiDotNet.Tests/UnitTests/Diffusion/Models/DDPMModelTests.cstests/AiDotNet.Tests/IntegrationTests/Diffusion/DiffusionSchedulersIntegrationTests.cstests/AiDotNet.Tests/UnitTests/Diffusion/Schedulers/DDIMSchedulerTests.cssrc/Optimizers/GradientBasedOptimizerBase.cs
📚 Learning: 2025-12-18T08:49:53.103Z
Learnt from: ooples
Repo: ooples/AiDotNet PR: 444
File: src/Interfaces/IPruningStrategy.cs:1-4
Timestamp: 2025-12-18T08:49:53.103Z
Learning: In this repository, global using directives are declared in AiDotNet.csproj for core namespaces (AiDotNet.Tensors.* and AiDotNet.*) and common system types. When reviewing C# files, assume these global usings are in effect; avoid adding duplicate using statements for these namespaces and for types like Vector<T>, Matrix<T>, Tensor<T>, etc. If a type is not found, verify the global usings or consider adding a file-scoped using if needed. Prefer relying on global usings to reduce boilerplate.
Applied to files:
tests/AiDotNet.Tests/UnitTests/Diffusion/Schedulers/PNDMSchedulerTests.cstests/AiDotNet.Tests/UnitTests/Diffusion/Schedulers/SchedulerConfigTests.cstests/AiDotNet.Tests/UnitTests/Diffusion/Models/DDPMModelTests.cstests/AiDotNet.Tests/IntegrationTests/Diffusion/DiffusionSchedulersIntegrationTests.cstests/AiDotNet.Tests/UnitTests/Diffusion/Schedulers/DDIMSchedulerTests.cssrc/Optimizers/GradientBasedOptimizerBase.cs
📚 Learning: 2025-12-19T19:05:13.598Z
Learnt from: ooples
Repo: ooples/AiDotNet PR: 445
File: src/Interfaces/IPredictionModelBuilder.cs:7-8
Timestamp: 2025-12-19T19:05:13.598Z
Learning: In ooples/AiDotNet, IAggregationStrategy<TModel> is declared in AiDotNet.Interfaces (src/Interfaces/IAggregationStrategy.cs). For src/Interfaces/IPredictionModelBuilder.cs (also in AiDotNet.Interfaces), referencing IAggregationStrategy does not require an additional using. Avoid flagging missing using directives for this case in future reviews.
Applied to files:
tests/AiDotNet.Tests/UnitTests/Diffusion/Schedulers/SchedulerConfigTests.cstests/AiDotNet.Tests/UnitTests/Diffusion/Models/DDPMModelTests.cstests/AiDotNet.Tests/IntegrationTests/Diffusion/DiffusionSchedulersIntegrationTests.cstests/AiDotNet.Tests/UnitTests/Diffusion/Schedulers/DDIMSchedulerTests.cssrc/Optimizers/GradientBasedOptimizerBase.cs
🔇 Additional comments (11)
src/Optimizers/GradientBasedOptimizerBase.cs (6)
91-123: Well-structured scheduler field declarations.The new fields for scheduler integration are properly documented with clear XML comments explaining their purpose. The protected visibility allows subclasses to access scheduler state when needed.
162-169: LGTM - Clean scheduler initialization.The scheduler initialization correctly uses the null-coalescing operator to set the learning rate from the scheduler if available, otherwise falling back to the initial rate. This addresses the previous review feedback about redundant assignments.
906-919: LGTM - Reset properly clears scheduler state.The reset logic correctly handles all scheduler-related state: resets the scheduler itself, clears step/epoch counters, and restores the initial learning rate using the same pattern as initialization.
935-942: LGTM - StepScheduler correctly advances scheduler state.The implementation properly guards against null scheduler and updates the internal learning rate field after stepping.
944-1012: LGTM - Clear documentation and correct stepping logic.The documentation now clearly indicates when these methods must be called manually in the training loop. The
SchedulerStepModeswitch logic correctly handles all three modes:
StepPerEpoch: steps only on epoch endStepPerBatch: steps only on batch endWarmupThenEpoch: steps per batch during warmup, per epoch after warmup
1052-1074: LGTM - Clean accessor implementations.The
GetCurrentLearningRate()method andCurrentStep/CurrentEpochproperties provide appropriate read-only access to scheduler state for monitoring and debugging purposes.tests/AiDotNet.Tests/UnitTests/Diffusion/Models/DDPMModelTests.cs (1)
1-2: LGTM - Namespace updated to reflect diffusion module reorganization.The using directives correctly reference the new
AiDotNet.NeuralNetworks.Diffusionnamespace structure following the refactor fromAiDotNet.Diffusion.tests/AiDotNet.Tests/UnitTests/Diffusion/Schedulers/DDIMSchedulerTests.cs (1)
1-1: LGTM - Namespace updated for scheduler refactor.The using directive correctly references
AiDotNet.NeuralNetworks.Diffusion.Schedulersto align with the diffusion module reorganization.tests/AiDotNet.Tests/IntegrationTests/Diffusion/DiffusionSchedulersIntegrationTests.cs (1)
2-2: LGTM - Namespace updated for scheduler refactor.The using directive correctly references
AiDotNet.NeuralNetworks.Diffusion.Schedulersconsistent with other test files.tests/AiDotNet.Tests/UnitTests/Diffusion/Schedulers/PNDMSchedulerTests.cs (1)
1-1: LGTM - Namespace updated for scheduler refactor.The using directive correctly references
AiDotNet.NeuralNetworks.Diffusion.Schedulers.tests/AiDotNet.Tests/UnitTests/Diffusion/Schedulers/SchedulerConfigTests.cs (1)
1-1: LGTM - Namespace updated for scheduler refactor.The using directive correctly references
AiDotNet.NeuralNetworks.Diffusion.Schedulers, completing the consistent namespace updates across all diffusion-related test files.
…trolled lr across all optimizers - Rename LearningRate to InitialLearningRate in optimizer options classes: - AdamOptimizerOptions, AdamWOptimizerOptions, LionOptimizerOptions - AdaMaxOptimizerOptions, AMSGradOptimizerOptions, NadamOptimizerOptions - Add SetLearningRate helper method in GradientBasedOptimizerBase to sync both _currentLearningRate (double) and CurrentLearningRate (T) fields - Remove redundant _currentLearningRate private fields from Adam, AdamW, Lion - Update all RL agents and regression classes to use InitialLearningRate - Ensure all 25 gradient-based optimizers use scheduler-controlled learning rate This ensures learning rate schedulers properly affect optimizer behavior by keeping the base class fields synchronized. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…reset methods - Merged master branch and resolved conflicts in Options and OptimizerBase files - Added BatchSize property to Adam, AdamW, and Lion optimizer options (from master) - Kept InitialLearningRate property naming from feature branch - Fixed critical bug: Adam, AdamW, and Lion Reset() methods now call base.Reset() to properly reset learning rate scheduler and internal state - Added integration tests for optimizer + scheduler combinations (18 tests) - Updated test files to use InitialLearningRate property 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
tests/AiDotNet.Tests/UnitTests/Optimizers/AdamWOptimizerTests.cs (1)
6-7: Remove duplicate using directive.Line 7 duplicates the using directive on line 6.
🔎 Proposed fix
using AiDotNet.Tensors.LinearAlgebra; -using AiDotNet.Tensors.LinearAlgebra;src/Optimizers/AdamWOptimizer.cs (1)
29-39: Update example code in XML documentation.The example code in the XML documentation still references
LearningRateinstead ofInitialLearningRate.🔎 Proposed fix
/// <example> /// <code> /// var options = new AdamWOptimizerOptions<float, Matrix<float>, Vector<float>> /// { -/// LearningRate = 0.001, +/// InitialLearningRate = 0.001, /// WeightDecay = 0.01, /// Beta1 = 0.9, /// Beta2 = 0.999 /// }; /// var optimizer = new AdamWOptimizer<float, Matrix<float>, Vector<float>>(model, options); /// </code> /// </example>
🧹 Nitpick comments (3)
tests/AiDotNet.Tests/IntegrationTests/Optimizers/OptimizerSchedulerIntegrationTests.cs (3)
222-281: Consider adding a test forWarmupThenEpochmode.The PR objectives mention
SchedulerStepMode.WarmupThenEpochas one of the three modes. Tests coverStepPerBatchandStepPerEpoch, butWarmupThenEpoch(which should step per-batch during warmup, then per-epoch) is not exercised.Would you like me to generate a test case for
WarmupThenEpochmode?
323-348: Test doesn't verify the constant learning rate behavior.The test name
Optimizer_WithNullScheduler_UsesConstantLearningRateimplies verification that LR stays constant, but the assertions only check that parameters are updated. Consider adding an assertion thatGetCurrentLearningRate()returns the initial value after all iterations.🔎 Proposed fix to add LR verification
// Assert - Optimizer should still work and update parameters Assert.True(parameters[0] < initialParams[0]); Assert.True(parameters[1] < initialParams[1]); Assert.True(parameters[2] < initialParams[2]); + + // LR should remain constant without a scheduler + Assert.Equal(0.01, optimizer.GetCurrentLearningRate(), Tolerance);
354-448: Consider consolidating duplicate test logic with parameterized tests.These three tests (
AdamOptimizer_WorksWithStepScheduler,AdamWOptimizer_WorksWithStepScheduler,LionOptimizer_WorksWithStepScheduler) share nearly identical structure. Using xUnit's[Theory]with a factory pattern or[MemberData]could reduce duplication while maintaining test isolation.That said, the explicit tests are clear and maintainable as-is—this is a nice-to-have refinement.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (27)
src/Models/Options/AMSGradOptimizerOptions.cssrc/Models/Options/AdaMaxOptimizerOptions.cssrc/Models/Options/AdamOptimizerOptions.cssrc/Models/Options/AdamWOptimizerOptions.cssrc/Models/Options/GradientBasedOptimizerOptions.cssrc/Models/Options/LionOptimizerOptions.cssrc/Models/Options/MultilayerPerceptronRegressionOptions.cssrc/Models/Options/NadamOptimizerOptions.cssrc/Optimizers/AMSGradOptimizer.cssrc/Optimizers/AdaMaxOptimizer.cssrc/Optimizers/AdamOptimizer.cssrc/Optimizers/AdamWOptimizer.cssrc/Optimizers/GradientBasedOptimizerBase.cssrc/Optimizers/LionOptimizer.cssrc/Optimizers/NadamOptimizer.cssrc/Regression/MultilayerPerceptronRegression.cssrc/Regression/NeuralNetworkRegression.cssrc/ReinforcementLearning/Agents/A3CAgent.cssrc/ReinforcementLearning/Agents/DecisionTransformerAgent.cssrc/ReinforcementLearning/Agents/DreamerAgent.cssrc/ReinforcementLearning/Agents/MADDPGAgent.cssrc/ReinforcementLearning/Agents/QMIXAgent.cssrc/ReinforcementLearning/Agents/RainbowDQNAgent.cssrc/ReinforcementLearning/Agents/TRPOAgent.cstests/AiDotNet.Tests/IntegrationTests/Optimizers/OptimizerSchedulerIntegrationTests.cstests/AiDotNet.Tests/UnitTests/Optimizers/AdamWOptimizerTests.cstests/AiDotNet.Tests/UnitTests/Optimizers/LionOptimizerTests.cs
✅ Files skipped from review due to trivial changes (1)
- src/Optimizers/AdamOptimizer.cs
🚧 Files skipped from review as they are similar to previous changes (1)
- src/Optimizers/GradientBasedOptimizerBase.cs
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-12-18T08:49:25.295Z
Learnt from: ooples
Repo: ooples/AiDotNet PR: 444
File: src/Interfaces/IPruningMask.cs:1-102
Timestamp: 2025-12-18T08:49:25.295Z
Learning: In the AiDotNet repository, the project-level global using includes AiDotNet.Tensors.LinearAlgebra via AiDotNet.csproj. Therefore, Vector<T>, Matrix<T>, and Tensor<T> are available without per-file using directives. Do not flag missing using directives for these types in any C# files within this project. Apply this guideline broadly to all C# files (not just a single file) to avoid false positives. If a file uses a type from a different namespace not covered by the global using, flag as usual.
Applied to files:
src/ReinforcementLearning/Agents/DreamerAgent.cssrc/Optimizers/AMSGradOptimizer.cssrc/Optimizers/NadamOptimizer.cssrc/ReinforcementLearning/Agents/DecisionTransformerAgent.cssrc/Models/Options/AdamOptimizerOptions.cssrc/Optimizers/AdaMaxOptimizer.cssrc/ReinforcementLearning/Agents/TRPOAgent.cssrc/Regression/MultilayerPerceptronRegression.cssrc/Regression/NeuralNetworkRegression.cssrc/Models/Options/NadamOptimizerOptions.cstests/AiDotNet.Tests/IntegrationTests/Optimizers/OptimizerSchedulerIntegrationTests.cssrc/Models/Options/LionOptimizerOptions.cssrc/ReinforcementLearning/Agents/A3CAgent.cssrc/Optimizers/LionOptimizer.cssrc/Optimizers/AdamWOptimizer.cssrc/ReinforcementLearning/Agents/MADDPGAgent.cstests/AiDotNet.Tests/UnitTests/Optimizers/AdamWOptimizerTests.cssrc/ReinforcementLearning/Agents/QMIXAgent.cssrc/ReinforcementLearning/Agents/RainbowDQNAgent.cssrc/Models/Options/AMSGradOptimizerOptions.cssrc/Models/Options/MultilayerPerceptronRegressionOptions.cstests/AiDotNet.Tests/UnitTests/Optimizers/LionOptimizerTests.cssrc/Models/Options/AdaMaxOptimizerOptions.cssrc/Models/Options/AdamWOptimizerOptions.cssrc/Models/Options/GradientBasedOptimizerOptions.cs
📚 Learning: 2025-12-18T08:49:53.103Z
Learnt from: ooples
Repo: ooples/AiDotNet PR: 444
File: src/Interfaces/IPruningStrategy.cs:1-4
Timestamp: 2025-12-18T08:49:53.103Z
Learning: In this repository, global using directives are declared in AiDotNet.csproj for core namespaces (AiDotNet.Tensors.* and AiDotNet.*) and common system types. When reviewing C# files, assume these global usings are in effect; avoid adding duplicate using statements for these namespaces and for types like Vector<T>, Matrix<T>, Tensor<T>, etc. If a type is not found, verify the global usings or consider adding a file-scoped using if needed. Prefer relying on global usings to reduce boilerplate.
Applied to files:
src/ReinforcementLearning/Agents/DreamerAgent.cssrc/Optimizers/AMSGradOptimizer.cssrc/Optimizers/NadamOptimizer.cssrc/ReinforcementLearning/Agents/DecisionTransformerAgent.cssrc/Models/Options/AdamOptimizerOptions.cssrc/Optimizers/AdaMaxOptimizer.cssrc/ReinforcementLearning/Agents/TRPOAgent.cssrc/Regression/MultilayerPerceptronRegression.cssrc/Regression/NeuralNetworkRegression.cssrc/Models/Options/NadamOptimizerOptions.cstests/AiDotNet.Tests/IntegrationTests/Optimizers/OptimizerSchedulerIntegrationTests.cssrc/Models/Options/LionOptimizerOptions.cssrc/ReinforcementLearning/Agents/A3CAgent.cssrc/Optimizers/LionOptimizer.cssrc/Optimizers/AdamWOptimizer.cssrc/ReinforcementLearning/Agents/MADDPGAgent.cstests/AiDotNet.Tests/UnitTests/Optimizers/AdamWOptimizerTests.cssrc/ReinforcementLearning/Agents/QMIXAgent.cssrc/ReinforcementLearning/Agents/RainbowDQNAgent.cssrc/Models/Options/AMSGradOptimizerOptions.cssrc/Models/Options/MultilayerPerceptronRegressionOptions.cstests/AiDotNet.Tests/UnitTests/Optimizers/LionOptimizerTests.cssrc/Models/Options/AdaMaxOptimizerOptions.cssrc/Models/Options/AdamWOptimizerOptions.cssrc/Models/Options/GradientBasedOptimizerOptions.cs
📚 Learning: 2025-12-19T19:05:13.598Z
Learnt from: ooples
Repo: ooples/AiDotNet PR: 445
File: src/Interfaces/IPredictionModelBuilder.cs:7-8
Timestamp: 2025-12-19T19:05:13.598Z
Learning: In ooples/AiDotNet, IAggregationStrategy<TModel> is declared in AiDotNet.Interfaces (src/Interfaces/IAggregationStrategy.cs). For src/Interfaces/IPredictionModelBuilder.cs (also in AiDotNet.Interfaces), referencing IAggregationStrategy does not require an additional using. Avoid flagging missing using directives for this case in future reviews.
Applied to files:
src/Models/Options/GradientBasedOptimizerOptions.cs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: CodeQL Analysis
- GitHub Check: Build (Windows)
🔇 Additional comments (37)
src/Optimizers/NadamOptimizer.cs (1)
94-96: LGTM! Clean refactor to centralize learning rate initialization.The removal of the explicit
CurrentLearningRateassignment is correct. The base class now handles learning rate initialization fromoptions.InitialLearningRate, which aligns with the broader scheduler integration effort.src/Models/Options/AdamWOptimizerOptions.cs (1)
35-43: LGTM! Public API update aligns with scheduler integration.The rename from
LearningRatetoInitialLearningRatewith thenewmodifier correctly reflects the semantic change—this property now specifies the initial rate before any scheduler adjustments. The default value of 0.001 remains appropriate for AdamW.src/ReinforcementLearning/Agents/TRPOAgent.cs (1)
56-62: LGTM! Consistent API usage update.The property name change from
LearningRatetoInitialLearningRatecorrectly reflects the updated optimizer options API.src/Optimizers/AdaMaxOptimizer.cs (1)
149-154: LGTM! Consistent refactor to centralize learning rate handling.The removal of explicit
CurrentLearningRateassignment is correct and consistent with other optimizers in this PR. The base class now manages initial learning rate setup fromoptions.InitialLearningRate.src/ReinforcementLearning/Agents/DecisionTransformerAgent.cs (1)
55-61: LGTM! Consistent API usage update.The property rename to
InitialLearningRatecorrectly reflects the updatedAdamOptimizerOptionsAPI.src/ReinforcementLearning/Agents/RainbowDQNAgent.cs (1)
63-69: LGTM! Consistent API usage update.The property rename to
InitialLearningRatecorrectly reflects the updatedAdamOptimizerOptionsAPI. The learning rate of 0.0001 remains appropriate for Rainbow DQN.src/Regression/NeuralNetworkRegression.cs (1)
86-92: LGTM! Consistent API usage update.The property rename to
InitialLearningRatecorrectly reflects the updatedAdamOptimizerOptionsAPI. The default learning rate of 0.001 remains appropriate for neural network regression.src/ReinforcementLearning/Agents/DreamerAgent.cs (1)
66-72: Verify property reference consistency between line 68 and lines 305/334.Line 68 correctly uses
InitialLearningRatefor the Adam optimizer options. However, lines 305 and 334 still reference_options.LearningRate. Confirm whetherDreamerOptions<T>exposes a separateLearningRateproperty distinct from the optimizer'sInitialLearningRate, or if these references should be updated to match the line 68 pattern.src/ReinforcementLearning/Agents/A3CAgent.cs (1)
55-55: LGTM! Property rename aligned with PR objectives.The change from
LearningRatetoInitialLearningRateis consistent with the repository-wide refactoring to support learning rate schedulers. The default value of 0.001 is preserved.src/Regression/MultilayerPerceptronRegression.cs (1)
149-149: LGTM! Consistent property rename.The change to
InitialLearningRatemaintains consistency with the new optimizer options API while preserving the default learning rate value.src/Optimizers/AMSGradOptimizer.cs (1)
76-77: LGTM! Base class now handles learning rate initialization.The refactoring to remove direct
CurrentLearningRateassignment and delegate to the base classInitializeAdaptiveParameters()centralizes learning rate management and enables scheduler integration. The explanatory comment clearly documents this change.src/ReinforcementLearning/Agents/QMIXAgent.cs (1)
63-63: LGTM! Property rename consistent across agents.The
InitialLearningRateproperty usage aligns with the optimizer options API changes throughout the codebase.src/Models/Options/MultilayerPerceptronRegressionOptions.cs (1)
446-446: LGTM! Optimizer factory updated correctly.The property rename in the default optimizer factory is consistent with the new
InitialLearningRateAPI across all optimizer options.src/Models/Options/AdaMaxOptimizerOptions.cs (1)
38-38: LGTM! Property declaration updated with appropriate modifier.The
newmodifier correctly hides the base class member to provide AdaMax's specific default value (0.002). The property rename fromLearningRatetoInitialLearningRatealigns with the scheduler integration architecture.src/Models/Options/AMSGradOptimizerOptions.cs (1)
45-45: LGTM! Property and documentation updated correctly.The property declaration with the
newmodifier and the updated documentation referencing "initial step size" properly reflect the scheduler-based learning rate architecture.src/Models/Options/LionOptimizerOptions.cs (1)
31-41: LGTM! Documentation and property declaration updated correctly.Both the XML documentation (line 31) and property declaration (line 41) properly reference "initial learning rate". The
newmodifier preserves Lion's optimizer-specific default value of 1e-4, which is appropriate for sign-based updates.src/Models/Options/NadamOptimizerOptions.cs (1)
84-84: LGTM!The property rename from
LearningRatetoInitialLearningRatewith thenewmodifier correctly shadows the base class property with Nadam's recommended default of 0.002, aligning with the broader refactor across optimizer options.tests/AiDotNet.Tests/UnitTests/Optimizers/AdamWOptimizerTests.cs (1)
28-29: LGTM!Tests correctly updated to validate
InitialLearningRateinstead ofLearningRate, ensuring the property rename is properly tested across default options, custom options, and serialization scenarios.src/Models/Options/AdamOptimizerOptions.cs (1)
38-48: LGTM!The property rename to
InitialLearningRatewith the standard Adam default of 0.001 is consistent with the broader refactor. Documentation correctly describes the property as the "initial learning rate."tests/AiDotNet.Tests/UnitTests/Optimizers/LionOptimizerTests.cs (1)
26-29: LGTM!Tests correctly validate
InitialLearningRatewith Lion's recommended default of 1e-4 (lower than Adam/AdamW due to Lion's sign-based updates). All test scenarios properly reference the renamed property.src/ReinforcementLearning/Agents/MADDPGAgent.cs (1)
65-71: LGTM!The fallback Adam optimizer correctly uses
InitialLearningRateinstead of the oldLearningRateproperty, aligning with the renamed property inAdamOptimizerOptions.src/Optimizers/LionOptimizer.cs (3)
85-94: LGTM!The removal of the private learning rate field and delegation to the base class
CurrentLearningRateproperty correctly enables scheduler-controlled learning rates. The comment clearly documents this design decision.
465-470: Good:Reset()now properly callsbase.Reset().This ensures the base class's learning rate state (including scheduler-related counters like
_currentStepand_currentEpoch) is properly reset alongside Lion's momentum state.
608-612: LGTM!Using
_options.InitialLearningRatefor the cache key correctly identifies the optimizer configuration, as this is the configured starting point rather than the dynamicCurrentLearningRate.src/Models/Options/GradientBasedOptimizerOptions.cs (2)
2-2: LGTM!The using directive for
AiDotNet.LearningRateSchedulersis required for the newILearningRateSchedulerandSchedulerStepModetypes.
205-238: Well-documented scheduler integration properties.The new
LearningRateSchedulerandSchedulerStepModeproperties provide a clean API for scheduler integration:
LearningRateSchedulerdefaults tonull(constant learning rate), which maintains backward compatibility.SchedulerStepModedefaults toStepPerEpoch, the most common scheduler stepping behavior.- Documentation clearly explains all three stepping modes and their use cases.
src/Optimizers/AdamWOptimizer.cs (3)
120-125: LGTM!The removal of the private learning rate field and delegation to the base class
CurrentLearningRateproperty correctly enables scheduler-controlled learning rates, consistent with the LionOptimizer refactor.
557-564: Good:Reset()now properly callsbase.Reset().This ensures the base class's learning rate state (including scheduler counters) is properly reset alongside AdamW's moment vectors.
697-701: LGTM!Using
_options.InitialLearningRatefor the cache key correctly identifies the optimizer configuration, consistent with the LionOptimizer implementation.tests/AiDotNet.Tests/IntegrationTests/Optimizers/OptimizerSchedulerIntegrationTests.cs (8)
1-17: LGTM!The imports, namespace, and class setup are well-structured. The tolerance constant of 1e-6 is appropriate for double precision floating-point comparisons in these scheduler tests. Based on learnings, global usings cover
Vector<T>and related types.
21-124: LGTM!The AdamW scheduler integration tests are well-designed:
- StepLR decay test correctly verifies gamma-based decay after stepSize epochs.
- Cosine annealing test properly verifies monotonic decrease with tolerance handling.
- Linear warmup test correctly verifies LR increases during warmup and reaches base LR at completion.
130-183: LGTM!Both Adam scheduler tests correctly verify expected decay behaviors with precise mathematical assertions.
189-218: LGTM!The Lion optimizer test uses appropriately small learning rate values typical for Lion and correctly verifies cosine annealing behavior.
285-317: LGTM!The reset test properly verifies that
Reset()restores the learning rate to its initial value after decay.
454-491: LGTM!The OneCycle scheduler test properly verifies the characteristic warmup-peak-cooldown learning rate trajectory.
497-529: LGTM!The cyclic scheduler test correctly verifies that the learning rate varies significantly through the cycle, which is the key characteristic of cyclic LR scheduling.
535-602: LGTM!The
GetCurrentLearningRate,OnEpochEnd, andOnBatchEndtests effectively verify the scheduler lifecycle hooks and their interaction with the respectiveSchedulerStepModesettings.
Changed LearningRate to InitialLearningRate in testconsole example files to match the renamed property in optimizer options classes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| var denominator = (Vector<T>)Engine.Add(vHatSqrt, epsilonVec); | ||
| var updateDiv = (Vector<T>)Engine.Divide(mHat, denominator); | ||
| var update = (Vector<T>)Engine.Multiply(updateDiv, _currentLearningRate); | ||
| var update = (Vector<T>)Engine.Multiply(updateDiv, CurrentLearningRate); |
Check warning
Code scanning / CodeQL
Cast to same type Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 10 months ago
In general, to fix “cast to same type” issues, you delete the explicit cast when the compiler already knows the expression has the desired type. This avoids redundant syntax and potential confusion without changing runtime behavior.
For this specific case in src/Optimizers/AdamOptimizer.cs, you should remove the (Vector<T>) cast from the assignment on line 261 so that update is directly initialized from the result of Engine.Multiply(updateDiv, CurrentLearningRate). Since CodeQL has determined that this expression already has type Vector<T>, the variable declaration var update will still infer the same type, and no further changes are needed. No new methods, imports, or definitions are required, and no other lines in this snippet need modification.
| @@ -258,7 +258,7 @@ | ||
| var epsilonVec = Vector<T>.CreateDefault(vHatSqrt.Length, epsilon); | ||
| var denominator = (Vector<T>)Engine.Add(vHatSqrt, epsilonVec); | ||
| var updateDiv = (Vector<T>)Engine.Divide(mHat, denominator); | ||
| var update = (Vector<T>)Engine.Multiply(updateDiv, CurrentLearningRate); | ||
| var update = Engine.Multiply(updateDiv, CurrentLearningRate); | ||
|
|
||
| // Apply update: parameters = parameters - update | ||
| var updatedParams = (Vector<T>)Engine.Subtract(parameters, update); |
|
|
||
| // Apply update: parameters = parameters - learningRate * update | ||
| var scaledUpdate = (Vector<T>)Engine.Multiply(update, _currentLearningRate); | ||
| var scaledUpdate = (Vector<T>)Engine.Multiply(update, CurrentLearningRate); |
Check warning
Code scanning / CodeQL
Cast to same type Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 10 months ago
In general, the fix for a "cast to same type" issue is simply to remove the unnecessary cast, leaving the expression as-is. This removes noise without changing semantics, since the compiler already knows the expression’s type is the same as the cast target.
For this specific case in src/Optimizers/AdamOptimizer.cs, on line 342 the code reads:
var scaledUpdate = (Vector<T>)Engine.Multiply(update, CurrentLearningRate);Since Engine.Multiply(update, CurrentLearningRate) already returns Vector<T>, we should remove the explicit cast and keep the inferred var type, preserving existing behavior:
var scaledUpdate = Engine.Multiply(update, CurrentLearningRate);No additional methods, imports, or definitions are needed. Only this line inside the shown method body needs to be changed.
| @@ -339,7 +339,7 @@ | ||
| var update = (Vector<T>)Engine.Divide(mHat, denominator); | ||
|
|
||
| // Apply update: parameters = parameters - learningRate * update | ||
| var scaledUpdate = (Vector<T>)Engine.Multiply(update, CurrentLearningRate); | ||
| var scaledUpdate = Engine.Multiply(update, CurrentLearningRate); | ||
| var updatedParameters = (Vector<T>)Engine.Subtract(parameters, scaledUpdate); | ||
|
|
||
| return updatedParameters; |
| var denominator = (Vector<T>)Engine.Add(vHatSqrt, epsilonVec); | ||
| var update = (Vector<T>)Engine.Divide(mHat, denominator); | ||
| var scaledUpdate = (Vector<T>)Engine.Multiply(update, _currentLearningRate); | ||
| var scaledUpdate = (Vector<T>)Engine.Multiply(update, CurrentLearningRate); |
Check warning
Code scanning / CodeQL
Cast to same type Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 10 months ago
To fix this, remove the redundant cast so that the code uses the value directly as the Vector<T> it already is. This keeps the same functionality while simplifying the code and addressing the CodeQL warning.
Concretely, in src/Optimizers/AdamOptimizer.cs around line 419, change:
var scaledUpdate = (Vector<T>)Engine.Multiply(update, CurrentLearningRate);to:
var scaledUpdate = Engine.Multiply(update, CurrentLearningRate);No additional imports, methods, or definitions are needed, as we are only removing an unnecessary cast and not changing types or behavior.
| @@ -416,7 +416,7 @@ | ||
| var epsilonVec = new Vector<T>(Enumerable.Repeat(epsilon, vHatSqrt.Length)); | ||
| var denominator = (Vector<T>)Engine.Add(vHatSqrt, epsilonVec); | ||
| var update = (Vector<T>)Engine.Divide(mHat, denominator); | ||
| var scaledUpdate = (Vector<T>)Engine.Multiply(update, CurrentLearningRate); | ||
| var scaledUpdate = Engine.Multiply(update, CurrentLearningRate); | ||
|
|
||
| // Apply update | ||
| var updatedVec = (Vector<T>)Engine.Subtract(paramVec, scaledUpdate); |
|
|
||
| // Scale Adam update by learning rate | ||
| var scaledAdamUpdate = (Vector<T>)Engine.Multiply(adamUpdate, _currentLearningRate); | ||
| var scaledAdamUpdate = (Vector<T>)Engine.Multiply(adamUpdate, CurrentLearningRate); |
Check warning
Code scanning / CodeQL
Cast to same type Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 10 months ago
In general, to fix “cast to same type” issues, you remove explicit casts when the compiler and type system already ensure the expression has the desired type. This simplifies the code and avoids misleading readers into thinking a conversion is needed.
For this concrete case in src/Optimizers/AdamWOptimizer.cs, line 285 defines:
var scaledAdamUpdate = (Vector<T>)Engine.Multiply(adamUpdate, CurrentLearningRate);CodeQL and the compiler understand that Engine.Multiply(adamUpdate, CurrentLearningRate) already returns a Vector<T>, so the (Vector<T>) cast is redundant. The best fix, without changing any behavior, is to remove the cast and let type inference handle it:
var scaledAdamUpdate = Engine.Multiply(adamUpdate, CurrentLearningRate);No new methods, imports, or definitions are needed; we are only simplifying an existing assignment. All other lines remain unchanged.
| @@ -282,7 +282,7 @@ | ||
| var adamUpdate = (Vector<T>)Engine.Divide(mHat, denominator); | ||
|
|
||
| // Scale Adam update by learning rate | ||
| var scaledAdamUpdate = (Vector<T>)Engine.Multiply(adamUpdate, CurrentLearningRate); | ||
| var scaledAdamUpdate = Engine.Multiply(adamUpdate, CurrentLearningRate); | ||
|
|
||
| // DECOUPLED WEIGHT DECAY: Apply weight decay directly to parameters | ||
| // AdamW: parameters = parameters - lr * adam_update - lr * weight_decay * parameters |
| // AdamW: parameters = parameters - lr * adam_update - lr * weight_decay * parameters | ||
| var weightDecayTerm = (Vector<T>)Engine.Multiply(parameters, weightDecay); | ||
| var scaledWeightDecay = (Vector<T>)Engine.Multiply(weightDecayTerm, _currentLearningRate); | ||
| var scaledWeightDecay = (Vector<T>)Engine.Multiply(weightDecayTerm, CurrentLearningRate); |
Check warning
Code scanning / CodeQL
Cast to same type Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 10 months ago
To fix this, remove the unnecessary cast to Vector<T> on the result of Engine.Multiply(weightDecayTerm, CurrentLearningRate) and let the expression’s natural type be used. This preserves existing functionality because the expression is already of type Vector<T>; the cast adds no runtime behavior.
Concretely, in src/Optimizers/AdamWOptimizer.cs in the method where weight decay is applied (around line 290), change:
var scaledWeightDecay = (Vector<T>)Engine.Multiply(weightDecayTerm, CurrentLearningRate);to:
var scaledWeightDecay = Engine.Multiply(weightDecayTerm, CurrentLearningRate);No additional imports, methods, or definitions are needed, and no other lines in this file need to change for this specific issue.
| @@ -287,7 +287,7 @@ | ||
| // DECOUPLED WEIGHT DECAY: Apply weight decay directly to parameters | ||
| // AdamW: parameters = parameters - lr * adam_update - lr * weight_decay * parameters | ||
| var weightDecayTerm = (Vector<T>)Engine.Multiply(parameters, weightDecay); | ||
| var scaledWeightDecay = (Vector<T>)Engine.Multiply(weightDecayTerm, CurrentLearningRate); | ||
| var scaledWeightDecay = Engine.Multiply(weightDecayTerm, CurrentLearningRate); | ||
|
|
||
| // Combine: parameters = parameters - scaledAdamUpdate - scaledWeightDecay | ||
| var afterAdamUpdate = (Vector<T>)Engine.Subtract(parameters, scaledAdamUpdate); |
| // Decoupled weight decay | ||
| var weightDecayTerm = (Vector<T>)Engine.Multiply(parameters, weightDecay); | ||
| var scaledWeightDecay = (Vector<T>)Engine.Multiply(weightDecayTerm, _currentLearningRate); | ||
| var scaledWeightDecay = (Vector<T>)Engine.Multiply(weightDecayTerm, CurrentLearningRate); |
Check warning
Code scanning / CodeQL
Cast to same type Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 10 months ago
In general, to fix a “cast to same type” issue, you remove the explicit cast when the compiler and static analyzer agree that the expression already has the desired type. This reduces clutter and eliminates misleading or unnecessary type noise.
Here, on line 481 in src/Optimizers/AdamWOptimizer.cs, CodeQL reports that (Vector<T>)Engine.Multiply(weightDecayTerm, CurrentLearningRate) is redundant because Engine.Multiply(weightDecayTerm, CurrentLearningRate) already returns Vector<T>. The best fix is to delete the cast and assign the result directly to var scaledWeightDecay. No behavior changes, and no additional imports or methods are required.
Concretely:
- In the method containing lines 479–485 (the update computation), change line 481 from:
var scaledWeightDecay = (Vector<T>)Engine.Multiply(weightDecayTerm, CurrentLearningRate);
to:var scaledWeightDecay = Engine.Multiply(weightDecayTerm, CurrentLearningRate);
No other lines need to be adjusted, since var will still infer Vector<T> based on the method’s return type.
| @@ -478,7 +478,7 @@ | ||
|
|
||
| // Decoupled weight decay | ||
| var weightDecayTerm = (Vector<T>)Engine.Multiply(parameters, weightDecay); | ||
| var scaledWeightDecay = (Vector<T>)Engine.Multiply(weightDecayTerm, CurrentLearningRate); | ||
| var scaledWeightDecay = Engine.Multiply(weightDecayTerm, CurrentLearningRate); | ||
|
|
||
| var afterAdamUpdate = (Vector<T>)Engine.Subtract(parameters, scaledAdamUpdate); | ||
| return (Vector<T>)Engine.Subtract(afterAdamUpdate, scaledWeightDecay); |
| var parameters = currentSolution.GetParameters(); | ||
| var weightDecay = NumOps.FromDouble(_options.WeightDecay); | ||
| var effectiveLearningRate = _currentLearningRate; | ||
| var effectiveLearningRate = CurrentLearningRate; |
Check warning
Code scanning / CodeQL
Useless assignment to local variable Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 10 months ago
In general, a "useless assignment to local variable" should be fixed either by removing the assignment (and the variable) if it is truly unnecessary, or by using the variable in place of repeated expressions if the intent was to cache or clarify a value. The key is to ensure that either the assignment has an observable effect (via subsequent reads) or it is not present at all.
For this specific case in src/Optimizers/LionOptimizer.cs, inside UpdateSolution, the variable effectiveLearningRate declared on line 229 is not used anywhere. The right-hand side CurrentLearningRate is a simple property access without side effects. The best, smallest fix that does not alter existing functionality is to remove the declaration/assignment line:
var effectiveLearningRate = CurrentLearningRate;and leave the remaining code unchanged. No additional imports, methods, or definitions are needed, and no other lines in this method need to be updated because CurrentLearningRate is already used directly where required.
| @@ -226,7 +226,6 @@ | ||
|
|
||
| var parameters = currentSolution.GetParameters(); | ||
| var weightDecay = NumOps.FromDouble(_options.WeightDecay); | ||
| var effectiveLearningRate = CurrentLearningRate; | ||
|
|
||
| // Step 1: Interpolate between momentum and gradient | ||
| var oneMinusBeta1 = NumOps.Subtract(NumOps.One, _currentBeta1); |
|
|
||
| // Step 4: Update parameters | ||
| var lrTimesUpdate = (Vector<T>)Engine.Multiply(update, _currentLearningRate); | ||
| var lrTimesUpdate = (Vector<T>)Engine.Multiply(update, CurrentLearningRate); |
Check warning
Code scanning / CodeQL
Cast to same type Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 10 months ago
In general, a “cast to same type” problem is fixed by removing the unnecessary cast and letting the compiler use the expression’s inferred type. This simplifies the code and removes misleading type information.
Specifically for this case in src/Optimizers/LionOptimizer.cs, at line 249, the expression:
var lrTimesUpdate = (Vector<T>)Engine.Multiply(update, CurrentLearningRate);should be changed to:
var lrTimesUpdate = Engine.Multiply(update, CurrentLearningRate);No additional imports or helper methods are needed, since the method already returns the correct type (Vector<T> as per CodeQL’s inference). This change preserves existing functionality while cleaning up the redundant cast. All other lines remain as they are.
| @@ -246,7 +246,7 @@ | ||
| } | ||
|
|
||
| // Step 4: Update parameters | ||
| var lrTimesUpdate = (Vector<T>)Engine.Multiply(update, CurrentLearningRate); | ||
| var lrTimesUpdate = Engine.Multiply(update, CurrentLearningRate); | ||
| var newParameters = (Vector<T>)Engine.Subtract(parameters, lrTimesUpdate); | ||
|
|
||
| // Step 5: Update momentum for next iteration |
|
|
||
| // Update parameters | ||
| var lrTimesUpdate = (Vector<T>)Engine.Multiply(update, _currentLearningRate); | ||
| var lrTimesUpdate = (Vector<T>)Engine.Multiply(update, CurrentLearningRate); |
Check warning
Code scanning / CodeQL
Cast to same type Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 10 months ago
In general, a redundant cast to the same type should be removed so that the expression is left in its original form. This keeps the code clearer, avoids confusion about types, and prevents unnecessary clutter.
In this method, at line 303 inside LionOptimizer<T, TInput, TOutput>, the expression Engine.Multiply(update, CurrentLearningRate) is already of type Vector<T> (per CodeQL), so casting it to (Vector<T>) before assigning it to lrTimesUpdate is unnecessary. To fix this without changing behavior, we simply remove the cast and assign the result directly. No imports, method changes, or additional definitions are required.
Concretely, in src/Optimizers/LionOptimizer.cs, in the body of the method where _m and updatedParams are handled (the vectorized Lion update), change line 303 from:
var lrTimesUpdate = (Vector<T>)Engine.Multiply(update, CurrentLearningRate);to:
var lrTimesUpdate = Engine.Multiply(update, CurrentLearningRate);All other casts in the shown snippet should remain unchanged, since CodeQL has only flagged this specific one as redundant and we must not assume the types of other Engine operations.
| @@ -300,7 +300,7 @@ | ||
| } | ||
|
|
||
| // Update parameters | ||
| var lrTimesUpdate = (Vector<T>)Engine.Multiply(update, CurrentLearningRate); | ||
| var lrTimesUpdate = Engine.Multiply(update, CurrentLearningRate); | ||
| var updatedParams = (Vector<T>)Engine.Subtract(parameters, lrTimesUpdate); | ||
|
|
||
| // Update momentum: m_t = beta2 * m_{t-1} + (1 - beta2) * g_t |
|
|
||
| // Update parameters | ||
| var lrTimesUpdate = (Vector<T>)Engine.Multiply(update, _currentLearningRate); | ||
| var lrTimesUpdate = (Vector<T>)Engine.Multiply(update, CurrentLearningRate); |
Check warning
Code scanning / CodeQL
Cast to same type Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 10 months ago
General fix: For a “cast to same type” issue, delete the explicit cast and let the expression’s existing type stand. This removes noise without affecting functionality.
Best fix here: On line 443 in src/Optimizers/LionOptimizer.cs, Engine.Multiply(update, CurrentLearningRate) already returns Vector<T>, so we can assign it directly to lrTimesUpdate without casting. We will replace:
var lrTimesUpdate = (Vector<T>)Engine.Multiply(update, CurrentLearningRate);with:
var lrTimesUpdate = Engine.Multiply(update, CurrentLearningRate);No other changes, imports, or helper methods are required. All other casts in the shown snippet should remain untouched because the static analyzer did not flag them; they may be necessary if those particular Engine methods return a more general type.
| @@ -440,7 +440,7 @@ | ||
| } | ||
|
|
||
| // Update parameters | ||
| var lrTimesUpdate = (Vector<T>)Engine.Multiply(update, CurrentLearningRate); | ||
| var lrTimesUpdate = Engine.Multiply(update, CurrentLearningRate); | ||
| var updatedParams = (Vector<T>)Engine.Subtract(paramVector, lrTimesUpdate); | ||
|
|
||
| // Update momentum: m_t = beta2 * m_{t-1} + (1 - beta2) * g_t |
|
…e2e test Tensors 0.92.0 is published and ships MixedPrecisionCompiledPlan.ComputeGradients (#574) that this PR's all-fused-optimizer FP16 path consumes via the reflection bridge. Bumps AiDotNet.Tensors + the lockstep AiDotNet.Native.OneDNN/OpenBLAS pins to 0.92.0 (CLBlast already there). Adds an end-to-end integration test: with AIDOTNET_FP16_ACTIVATIONS=1 + RMSprop (non-Adam fused optimizer), the compiled path routes through ComputeGradients and RMSprop applies its own master update — fused path engages and loss descends with finite values. Verified against published 0.92.0 (7/7 FusedOptimizerIntegrationTests green, net10.0). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…#575) This PR's Cholesky/Gram zero-alloc copy-buffer dot depends on AiDotNet.Tensors #575 (zero the result buffer before the accumulating fallback GEMM paths) — without it, pooled buffers are read non-deterministically and the dot can accumulate into stale data. #575 shipped in published Tensors 0.92.1 (also carries #574 FP16 ComputeGradients). Bumps AiDotNet.Tensors + the lockstep AiDotNet.Native OneDNN/OpenBLAS/CLBlast pins to 0.92.1. src builds clean against 0.92.1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…1543) * feat(fp16): extend FP16-activation training to all fused optimizers The compiled FP16-activation path (AIDOTNET_FP16_ACTIVATIONS=1) covered only SGD (StepMixedPrecision) and Adam/AdamW (StepAdam) — the other fused optimizers (Lion, RMSprop, LAMB, Adagrad, AdaMax, AdaDelta, Nadam) silently fell back to FP32 activations, so the ~1/2-resident memory win never reached them. Adds a generic FP16 branch in CompiledTapeTrainingStep.TryStepWithFusedOptimizer: for float + flag-on + a Tensors build exposing ComputeGradients + any fused optimizer beyond the inline Adam/SGD fast paths, it runs the optimizer-agnostic MixedPrecisionCompiledPlan.ComputeGradients (FP16 activations, grads bridged FP16<->FP32) and hands the unscaled FP32 gradients to the optimizer INSTANCE's Step (via a TapeStepContext) so the optimizer applies its own master update + maintains its own state + gradient clipping — the proven eager optimizer.Step path, just fed FP16-computed grads. Skipped on FP16 overflow (the GradScaler backs off). The optimizer is threaded in from NeuralNetworkBase via a new optional eagerOptimizer parameter. Default off => byte-identical to the single-type plan. Wired through the existing MixedPrecisionReflection bridge (not a direct type reference), matching the merged #1513 pattern: a new ComputeGradients + IsComputeGradientsAvailable surface reflects into the Tensors API at runtime, so this compiles against the published 0.91.12 and the path lights up automatically once Tensors publishes ComputeGradients (PR #574). No release coupling. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(deps): bump Tensors 0.91.12 → 0.92.0 (ComputeGradients) + FP16 e2e test Tensors 0.92.0 is published and ships MixedPrecisionCompiledPlan.ComputeGradients (#574) that this PR's all-fused-optimizer FP16 path consumes via the reflection bridge. Bumps AiDotNet.Tensors + the lockstep AiDotNet.Native.OneDNN/OpenBLAS pins to 0.92.0 (CLBlast already there). Adds an end-to-end integration test: with AIDOTNET_FP16_ACTIVATIONS=1 + RMSprop (non-Adam fused optimizer), the compiled path routes through ComputeGradients and RMSprop applies its own master update — fused path engages and loss descends with finite values. Verified against published 0.92.0 (7/7 FusedOptimizerIntegrationTests green, net10.0). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(fp16): clear _mpPlan in Invalidate + accept double Loss in reflection (PR #1543 review) Addresses CodeRabbit review comments on #1543: - CompiledTapeTrainingStep.Invalidate() cleared _mpAdamPlan/_mpGenericPlan but not the SGD mixed-precision plan _mpPlan, so an explicit Invalidate (model-structure change) left a stale SGD MP plan capturing the old layer set's tensors. Now drops all three, matching InvalidateIfLayerSetChanged. - MixedPrecisionReflection Loss extraction (ExtractGradientResult + ExtractLoss) only handled float and silently returned 0 for a double; now narrows float OR double via a shared AsFloat helper, tolerating a future Tensors API that widens Loss to double. Build green; FusedOptimizerIntegrationTests 7/7. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>


Summary
This PR implements Issue #388 - AdamW, Learning Rate Schedulers, and Scheduler Architecture Refactor.
Changes:
SchedulerStepModeenum withStepPerBatch,StepPerEpoch, andWarmupThenEpochmodesGradientBasedOptimizerOptionswithLearningRateSchedulerandSchedulerStepModepropertiesGradientBasedOptimizerBasewith:_currentStep,_currentEpoch)OnEpochEnd(),OnBatchEnd(),StepScheduler()methodsIsInWarmupPhase()heuristic for warmup detectionReset()methodsrc/Diffusion/→src/NeuralNetworks/Diffusion/)IStepSchedulertoINoiseSchedulerto avoid confusion with learning rate schedulersStepSchedulerBasetoNoiseSchedulerBasefor consistencyAll 25 gradient-based optimizers automatically inherit scheduler support via the base class.
AdamW already has proper decoupled weight decay implementation.
Closes #388
Test plan
🤖 Generated with Claude Code