-
-
Notifications
You must be signed in to change notification settings - Fork 7
Fix GitHub issue 404 #440
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Fix GitHub issue 404 #440
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,398 @@ | ||||||||||||||||||||
| using AiDotNet.Interfaces; | ||||||||||||||||||||
| using AiDotNet.LinearAlgebra; | ||||||||||||||||||||
| using AiDotNet.LossFunctions; | ||||||||||||||||||||
| using AiDotNet.Models; | ||||||||||||||||||||
| using AiDotNet.NeuralNetworks; | ||||||||||||||||||||
| using AiDotNet.NeuralNetworks.Helpers; | ||||||||||||||||||||
|
Check failure on line 6 in src/ProgramSynthesis/Engines/CodeBERT.cs
|
||||||||||||||||||||
| using AiDotNet.NeuralNetworks.Layers; | ||||||||||||||||||||
| using AiDotNet.Optimizers; | ||||||||||||||||||||
| using AiDotNet.ProgramSynthesis.Enums; | ||||||||||||||||||||
| using AiDotNet.ProgramSynthesis.Interfaces; | ||||||||||||||||||||
| using AiDotNet.ProgramSynthesis.Models; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| namespace AiDotNet.ProgramSynthesis.Engines; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||
| /// CodeBERT is a bimodal pre-trained model for programming and natural languages. | ||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||
| /// <typeparam name="T">The numeric type used for calculations (e.g., double, float).</typeparam> | ||||||||||||||||||||
| /// <remarks> | ||||||||||||||||||||
| /// <para> | ||||||||||||||||||||
| /// CodeBERT is designed to understand both code and natural language. It uses a | ||||||||||||||||||||
| /// transformer-based encoder architecture pre-trained on code-documentation pairs | ||||||||||||||||||||
| /// from GitHub. It excels at tasks like code search, code documentation generation, | ||||||||||||||||||||
| /// and code completion. | ||||||||||||||||||||
| /// </para> | ||||||||||||||||||||
| /// <para><b>For Beginners:</b> CodeBERT is an AI that understands programming languages. | ||||||||||||||||||||
| /// | ||||||||||||||||||||
| /// Just like BERT understands English, CodeBERT understands code. It's been trained | ||||||||||||||||||||
| /// on millions of code examples from GitHub and can: | ||||||||||||||||||||
| /// - Understand what code does | ||||||||||||||||||||
| /// - Find similar code | ||||||||||||||||||||
| /// - Complete code as you write | ||||||||||||||||||||
| /// - Generate documentation | ||||||||||||||||||||
| /// - Translate between code and descriptions | ||||||||||||||||||||
| /// | ||||||||||||||||||||
| /// Think of it as an AI that's read millions of lines of code and learned the | ||||||||||||||||||||
| /// patterns of good programming, just like you learn language by reading books. | ||||||||||||||||||||
| /// </para> | ||||||||||||||||||||
| /// </remarks> | ||||||||||||||||||||
| public class CodeBERT<T> : NeuralNetworkBase<T>, ICodeModel<T> | ||||||||||||||||||||
|
Check failure on line 40 in src/ProgramSynthesis/Engines/CodeBERT.cs
|
||||||||||||||||||||
| { | ||||||||||||||||||||
| private readonly CodeSynthesisArchitecture<T> _architecture; | ||||||||||||||||||||
| private IGradientBasedOptimizer<T, Tensor<T>, Tensor<T>> _optimizer; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||
| /// Gets the target programming language for this model. | ||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||
| public ProgramLanguage TargetLanguage => _architecture.TargetLanguage; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||
| /// Gets the maximum sequence length (in tokens) that the model can process. | ||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||
| public int MaxSequenceLength => _architecture.MaxSequenceLength; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||
| /// Gets the vocabulary size of the model. | ||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||
| public int VocabularySize => _architecture.VocabularySize; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||
| /// Initializes a new instance of the <see cref="CodeBERT{T}"/> class. | ||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||
| /// <param name="architecture">The architecture configuration for the model.</param> | ||||||||||||||||||||
| /// <param name="lossFunction">Optional loss function (defaults to cross-entropy for code tasks).</param> | ||||||||||||||||||||
| /// <param name="optimizer">Optional optimizer (defaults to Adam optimizer).</param> | ||||||||||||||||||||
| /// <remarks> | ||||||||||||||||||||
| /// <para> | ||||||||||||||||||||
| /// Creates a new CodeBERT model with the specified architecture. The model will | ||||||||||||||||||||
| /// be initialized with encoder layers suitable for code understanding tasks. | ||||||||||||||||||||
| /// </para> | ||||||||||||||||||||
| /// <para><b>For Beginners:</b> This creates a new CodeBERT model. | ||||||||||||||||||||
| /// | ||||||||||||||||||||
| /// You provide: | ||||||||||||||||||||
| /// - Architecture: The blueprint (size, layers, etc.) | ||||||||||||||||||||
| /// - Loss function: How to measure mistakes (optional) | ||||||||||||||||||||
| /// - Optimizer: How to improve from mistakes (optional) | ||||||||||||||||||||
| /// | ||||||||||||||||||||
| /// Like setting up a new student with a curriculum and teaching method. | ||||||||||||||||||||
| /// </para> | ||||||||||||||||||||
| /// </remarks> | ||||||||||||||||||||
| public CodeBERT( | ||||||||||||||||||||
| CodeSynthesisArchitecture<T> architecture, | ||||||||||||||||||||
| ILossFunction<T>? lossFunction = null, | ||||||||||||||||||||
| IGradientBasedOptimizer<T, Tensor<T>, Tensor<T>>? optimizer = null) | ||||||||||||||||||||
| : base(architecture, lossFunction ?? new CrossEntropyLoss<T>()) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| _architecture = architecture; | ||||||||||||||||||||
| _optimizer = optimizer ?? new AdamOptimizer<T, Tensor<T>, Tensor<T>>(this); | ||||||||||||||||||||
| InitializeLayers(); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||
| /// Initializes the layers of the CodeBERT model. | ||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||
| /// <remarks> | ||||||||||||||||||||
| /// <para> | ||||||||||||||||||||
| /// Sets up the encoder layers including embeddings, positional encoding, | ||||||||||||||||||||
| /// multi-head attention, and feed-forward networks based on the architecture. | ||||||||||||||||||||
| /// </para> | ||||||||||||||||||||
| /// <para><b>For Beginners:</b> This builds the internal structure of CodeBERT. | ||||||||||||||||||||
| /// | ||||||||||||||||||||
| /// Creates all the layers that process code: | ||||||||||||||||||||
| /// - Embedding layer: Converts code tokens to numbers | ||||||||||||||||||||
| /// - Attention layers: Let the model focus on important parts | ||||||||||||||||||||
| /// - Processing layers: Transform and analyze the code | ||||||||||||||||||||
| /// | ||||||||||||||||||||
| /// Like assembling the components of a machine according to the blueprint. | ||||||||||||||||||||
| /// </para> | ||||||||||||||||||||
| /// </remarks> | ||||||||||||||||||||
| protected override void InitializeLayers() | ||||||||||||||||||||
| { | ||||||||||||||||||||
| if (Architecture.Layers != null && Architecture.Layers.Count > 0) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| Layers.AddRange(Architecture.Layers); | ||||||||||||||||||||
| ValidateCustomLayers(Layers); | ||||||||||||||||||||
| } | ||||||||||||||||||||
| else | ||||||||||||||||||||
| { | ||||||||||||||||||||
| // Create default CodeBERT encoder layers | ||||||||||||||||||||
| // Embedding layer for code tokens | ||||||||||||||||||||
| Layers.Add(new EmbeddingLayer<T>( | ||||||||||||||||||||
| vocabularySize: _architecture.VocabularySize, | ||||||||||||||||||||
| embeddingDimension: _architecture.ModelDimension, | ||||||||||||||||||||
| maxSequenceLength: _architecture.MaxSequenceLength, | ||||||||||||||||||||
| usePositionalEncoding: _architecture.UsePositionalEncoding)); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Add encoder layers (multi-head attention + feed-forward) | ||||||||||||||||||||
| for (int i = 0; i < _architecture.NumEncoderLayers; i++) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| // Multi-head self-attention | ||||||||||||||||||||
| Layers.Add(new MultiHeadAttentionLayer<T>( | ||||||||||||||||||||
| modelDimension: _architecture.ModelDimension, | ||||||||||||||||||||
| numHeads: _architecture.NumHeads, | ||||||||||||||||||||
| dropout: _architecture.DropoutRate)); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Layer normalization after attention | ||||||||||||||||||||
| Layers.Add(new LayerNormalizationLayer<T>( | ||||||||||||||||||||
| normalizedShape: new[] { _architecture.ModelDimension })); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Feed-forward network | ||||||||||||||||||||
| Layers.Add(new DenseLayer<T>( | ||||||||||||||||||||
| inputSize: _architecture.ModelDimension, | ||||||||||||||||||||
| outputSize: _architecture.FeedForwardDimension, | ||||||||||||||||||||
| activationFunction: new GELUActivationFunction<T>())); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Layers.Add(new DenseLayer<T>( | ||||||||||||||||||||
| inputSize: _architecture.FeedForwardDimension, | ||||||||||||||||||||
| outputSize: _architecture.ModelDimension, | ||||||||||||||||||||
| activationFunction: null)); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Layer normalization after feed-forward | ||||||||||||||||||||
| Layers.Add(new LayerNormalizationLayer<T>( | ||||||||||||||||||||
| normalizedShape: new[] { _architecture.ModelDimension })); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Dropout for regularization | ||||||||||||||||||||
| Layers.Add(new DropoutLayer<T>(_architecture.DropoutRate)); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Final output projection layer | ||||||||||||||||||||
| Layers.Add(new DenseLayer<T>( | ||||||||||||||||||||
| inputSize: _architecture.ModelDimension, | ||||||||||||||||||||
| outputSize: _architecture.VocabularySize, | ||||||||||||||||||||
| activationFunction: null)); | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||
| /// Encodes source code into a vector representation. | ||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||
| /// <param name="code">The source code to encode.</param> | ||||||||||||||||||||
| /// <returns>A tensor representing the encoded code.</returns> | ||||||||||||||||||||
| /// <remarks> | ||||||||||||||||||||
| /// <para> | ||||||||||||||||||||
| /// Converts source code text into a numerical tensor that captures the semantic | ||||||||||||||||||||
| /// meaning of the code. This encoding can be used for downstream tasks like | ||||||||||||||||||||
| /// code search or classification. | ||||||||||||||||||||
| /// </para> | ||||||||||||||||||||
| /// <para><b>For Beginners:</b> This converts code text into numbers the AI understands. | ||||||||||||||||||||
| /// | ||||||||||||||||||||
| /// Code is just text to a computer, but the AI needs numbers to work with. | ||||||||||||||||||||
| /// This method: | ||||||||||||||||||||
| /// 1. Breaks code into tokens (like words) | ||||||||||||||||||||
| /// 2. Converts tokens to numbers | ||||||||||||||||||||
| /// 3. Processes them through the model | ||||||||||||||||||||
| /// 4. Returns a numerical representation that captures the code's meaning | ||||||||||||||||||||
| /// | ||||||||||||||||||||
| /// Like translating a recipe into a numerical rating system while keeping the essence. | ||||||||||||||||||||
| /// </para> | ||||||||||||||||||||
| /// </remarks> | ||||||||||||||||||||
| public Tensor<T> EncodeCode(string code) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| // Tokenize and convert to tensor (simplified - in production, use proper tokenizer) | ||||||||||||||||||||
| var input = TokenizeCode(code); | ||||||||||||||||||||
| return Predict(input); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||
| /// Decodes a vector representation back into source code. | ||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||
| /// <param name="encoding">The encoded representation to decode.</param> | ||||||||||||||||||||
| /// <returns>The decoded source code as a string.</returns> | ||||||||||||||||||||
| /// <remarks> | ||||||||||||||||||||
| /// <para> | ||||||||||||||||||||
| /// Converts the model's numerical representation back into human-readable code. | ||||||||||||||||||||
| /// This is the reverse of the encoding process. | ||||||||||||||||||||
| /// </para> | ||||||||||||||||||||
| /// <para><b>For Beginners:</b> This converts the AI's numbers back to readable code. | ||||||||||||||||||||
| /// | ||||||||||||||||||||
| /// After the AI processes code as numbers, we need to convert back to text. | ||||||||||||||||||||
| /// This method reverses the encoding process to produce readable code. | ||||||||||||||||||||
| /// </para> | ||||||||||||||||||||
| /// </remarks> | ||||||||||||||||||||
| public string DecodeCode(Tensor<T> encoding) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| // Simplified decoding - in production, use proper detokenizer | ||||||||||||||||||||
| return DetokenizeCode(encoding); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||
| /// Performs a code-related task on the input code. | ||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||
| /// <param name="code">The source code to process.</param> | ||||||||||||||||||||
| /// <param name="task">The type of task to perform.</param> | ||||||||||||||||||||
| /// <returns>The result of the task as a string.</returns> | ||||||||||||||||||||
| /// <remarks> | ||||||||||||||||||||
| /// <para> | ||||||||||||||||||||
| /// Executes various code-related tasks such as completion, summarization, | ||||||||||||||||||||
| /// bug detection, etc. The implementation adapts based on the task type. | ||||||||||||||||||||
| /// </para> | ||||||||||||||||||||
| /// <para><b>For Beginners:</b> This is the main method for doing things with code. | ||||||||||||||||||||
| /// | ||||||||||||||||||||
| /// Tell it what you want done (completion, bug finding, etc.), and it | ||||||||||||||||||||
| /// processes the code and returns the result. Like a Swiss Army knife | ||||||||||||||||||||
| /// for code - one tool, many functions. | ||||||||||||||||||||
| /// </para> | ||||||||||||||||||||
| /// </remarks> | ||||||||||||||||||||
| public string PerformTask(string code, CodeTask task) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| var encoding = EncodeCode(code); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Task-specific processing would go here | ||||||||||||||||||||
| // For now, return a placeholder implementation | ||||||||||||||||||||
| return task switch | ||||||||||||||||||||
| { | ||||||||||||||||||||
| CodeTask.Completion => PerformCompletion(encoding), | ||||||||||||||||||||
| CodeTask.Summarization => PerformSummarization(encoding), | ||||||||||||||||||||
| CodeTask.BugDetection => PerformBugDetection(encoding), | ||||||||||||||||||||
| _ => DecodeCode(encoding) | ||||||||||||||||||||
| }; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||
| /// Gets embeddings for code tokens. | ||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||
| /// <param name="code">The source code to get embeddings for.</param> | ||||||||||||||||||||
| /// <returns>A tensor containing token embeddings.</returns> | ||||||||||||||||||||
| /// <remarks> | ||||||||||||||||||||
| /// <para> | ||||||||||||||||||||
| /// Returns the embedding vectors for each token in the code. These embeddings | ||||||||||||||||||||
| /// capture semantic similarity - similar code constructs have similar embeddings. | ||||||||||||||||||||
| /// </para> | ||||||||||||||||||||
| /// <para><b>For Beginners:</b> This gets the numerical representation of each code piece. | ||||||||||||||||||||
| /// | ||||||||||||||||||||
| /// Each word/symbol in code gets a vector of numbers that represents its meaning. | ||||||||||||||||||||
| /// Similar code pieces get similar numbers. Useful for finding related code or | ||||||||||||||||||||
| /// understanding code structure. | ||||||||||||||||||||
| /// </para> | ||||||||||||||||||||
| /// </remarks> | ||||||||||||||||||||
| public Tensor<T> GetEmbeddings(string code) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| var input = TokenizeCode(code); | ||||||||||||||||||||
| // Return embeddings from the first layer (embedding layer) | ||||||||||||||||||||
| return Layers[0].Forward(input); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||
| /// Makes a prediction on the input tensor. | ||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||
| /// <param name="input">The input tensor.</param> | ||||||||||||||||||||
| /// <returns>The output tensor.</returns> | ||||||||||||||||||||
| public override Tensor<T> Predict(Tensor<T> input) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| SetTrainingMode(false); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| var output = input; | ||||||||||||||||||||
| foreach (var layer in Layers) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| output = layer.Forward(output); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| return output; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||
| /// Trains the model on a single example. | ||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||
| /// <param name="input">The input tensor.</param> | ||||||||||||||||||||
| /// <param name="expectedOutput">The expected output tensor.</param> | ||||||||||||||||||||
| public override void Train(Tensor<T> input, Tensor<T> expectedOutput) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| SetTrainingMode(true); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Forward pass | ||||||||||||||||||||
| var output = Predict(input); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Calculate loss | ||||||||||||||||||||
| var loss = LossFunction.ComputeLoss(output, expectedOutput); | ||||||||||||||||||||
| AddLoss(loss); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Backward pass | ||||||||||||||||||||
| var gradient = LossFunction.ComputeGradient(output, expectedOutput); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| for (int i = Layers.Count - 1; i >= 0; i--) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| gradient = Layers[i].Backward(gradient); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Update parameters using optimizer | ||||||||||||||||||||
| _optimizer.UpdateParameters(); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||
| /// Gets metadata about the model. | ||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||
| /// <returns>Model metadata.</returns> | ||||||||||||||||||||
| public override ModelMetadata<T> GetModelMetadata() | ||||||||||||||||||||
| { | ||||||||||||||||||||
| return new ModelMetadata<T> | ||||||||||||||||||||
| { | ||||||||||||||||||||
| ModelType = "CodeBERT", | ||||||||||||||||||||
| ParameterCount = ParameterCount, | ||||||||||||||||||||
| InputSize = _architecture.InputSize, | ||||||||||||||||||||
| OutputSize = _architecture.OutputSize, | ||||||||||||||||||||
| TrainingLosses = GetLosses() | ||||||||||||||||||||
| }; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| protected override void SerializeNetworkSpecificData(BinaryWriter writer) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| // Serialize CodeBERT-specific data | ||||||||||||||||||||
| writer.Write((int)_architecture.TargetLanguage); | ||||||||||||||||||||
| writer.Write(_architecture.MaxSequenceLength); | ||||||||||||||||||||
| writer.Write(_architecture.VocabularySize); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| protected override void DeserializeNetworkSpecificData(BinaryReader reader) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| // Deserialize CodeBERT-specific data | ||||||||||||||||||||
| var targetLanguage = (ProgramLanguage)reader.ReadInt32(); | ||||||||||||||||||||
| var maxSeqLength = reader.ReadInt32(); | ||||||||||||||||||||
|
||||||||||||||||||||
| var maxSeqLength = reader.ReadInt32(); | |
| _architecture.MaxSequenceLength = reader.ReadInt32(); |
Copilot
AI
Nov 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assignment to targetLanguage is useless, since its value is never read.
| var vocabSize = reader.ReadInt32(); | |
| var vocabSize = reader.ReadInt32(); | |
| // Update _architecture with deserialized values | |
| _architecture.TargetLanguage = targetLanguage; | |
| _architecture.MaxSequenceLength = maxSeqLength; | |
| _architecture.VocabularySize = vocabSize; |
Copilot
AI
Nov 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assignment to vocabSize is useless, since its value is never read.
| var vocabSize = reader.ReadInt32(); | |
| reader.ReadInt32(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Field '_optimizer' can be 'readonly'.