-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
GSoC 2026 ‐ Nakul Krishnakumar
Hey, I’m Nakul Krishnakumar, a senior undergraduate student at the Indian Institute of Information Technology, Kottayam, India, pursuing a B.Tech. in Computer Science and Engineering.
I’ve always been fascinated by low-level programming, machine learning, and the mathematics behind it. My open-source journey began with stdlib, which gave me an opportunity to explore these interests while learning how machine learning algorithms can be designed and implemented at a lower level. Contributing to stdlib has allowed me to deepen my understanding of machine learning, improve my software development skills, and experience what it means to contribute to a large, community-driven codebase.
The primary goal of this project was to lay the foundation for machine learning algorithms within the stdlib library. As a starting point, I selected four widely used machine learning algorithms and focused on implementing their double-precision variants:
- K-Means Clustering
- SGD Classification
- SGD Regression
- Perceptron Classification
To guide the design and implementation, I referred to several established machine learning libraries and existing implementations, including:
- scikit-learn
- MLJ.jl
- mlpack
@stdlib/ml/incr- Several other relevant implementations within the
stdlibecosystem
The implementation of each algorithm was structured into three distinct layers:
Utility packages reside under ml/base (for example, ml/base/sgd/learning-rates). These packages provide helper functions, structs, enums, and other building blocks required by the higher-level implementations.
For example, ml/base/sgd/learning-rates defines the learning rate schedulers supported by the SGD implementation. The JavaScript implementation exposes the learning rates as enumeration constants:
function enumerated() {
return {
'basic': 0,
'constant': 1,
'invscaling': 2,
'pegasos': 3
};
}The corresponding C implementation defines the same values using an enum:
enum STDLIB_ML_SGD_LEARNING_RATE {
STDLIB_ML_SGD_BASIC = 0,
STDLIB_ML_SGD_CONSTANT,
STDLIB_ML_SGD_INVSCALING,
STDLIB_ML_SGD_PEGASOS
};Keeping these enumerations synchronized between JavaScript and C is important because the values are passed between the two implementations.
The strided kernels contain the core computational logic of the algorithms and form the main implementation layer of the project. They are responsible for performing the underlying mathematical operations and are designed to work efficiently with strided data. Most kernels also support operations on two-dimensional matrices.
For example, ml/strided/dsgd-trainer (which is planned to be migrated to ml/strided/float64/sgd) exposes a regular implementation with the following interface:
function dsgdTrainer( order, M, N, X, LDX, y, strideY, w, strideW, workspace, strideWS, params )The corresponding strided implementation provides explicit strides and offsets for the input and output arrays:
function dsgdTrainer( M, N, X, strideX1, strideX2, offsetX, y, strideY, offsetY, w, strideW, offsetW, workspace, strideWS, offsetWS, params )The kernel also has a corresponding C implementation:
double* API_SUFFIX(stdlib_strided_dsgd_trainer)( const CBLAS_LAYOUT layout, const CBLAS_INT M, const CBLAS_INT N, const double *X, const CBLAS_INT LDX, const double *y, const CBLAS_INT strideY, double *w, const CBLAS_INT strideW, double *ws, const CBLAS_INT strideWS, const struct stdlib_ml_sgd_float64_params *params )This layered approach allows the computational kernels to be implemented once and then exposed through both JavaScript and native C interfaces.
User-facing constructors will provide the high-level APIs through which users interact with the algorithms. These APIs will delegate the computational work to the underlying strided kernels, keeping the user-facing interface simple while allowing the kernels to handle the performance-critical operations.
The work on these constructors is still pending.
Each algorithm will expose two primary methods:
-
ctor.fit()— trains the model using the provided data by invoking the corresponding strided kernels. -
ctor.predict()— uses the trained model to generate predictions for new input data.
This separation keeps the high-level API focused on usability while the lower-level kernels handle the actual computation.
Looking back at the project, several key aspects stand out:
-
Supporting kernels: I opened pull requests for nearly all the supporting kernels required by the four algorithms. These form the core computational foundation on which the high-level APIs will be built.
-
Int64Arraysupport: While implementing K-Means, we identifiedInt64Arrayfunctionality as a prerequisite for parts of the implementation. Since the required functionality was not available, I implemented the necessaryint64utilities as part of the project. -
Establishing conventions: One of the biggest challenges was that machine learning was still a relatively unexplored area within
stdlib. As a result, many of the existing conventions could not be directly applied. A significant part of the work involved designing new conventions and establishing patterns that could be consistently followed across the machine learning packages. This included determining how an algorithm should be decomposed into multiple utility packages, kernels, and higher-level APIs. -
Building the supporting infrastructure: The project required a surprisingly large number of utility packages. Initially, I questioned whether all of these utilities were necessary, but as the implementation progressed, I came to understand their importance. For a production-ready library such as
stdlib, these utilities help ensure that implementations are robust, deterministic, consistent, and resistant to edge cases. What initially seemed like additional overhead ultimately became an important part of building a reliable foundation for machine learning within the library. -
Remaining work: The primary remaining task is to implement the user-facing APIs for all four algorithms. Once these are in place, the algorithms will be accessible through high-level interfaces.
The work required to develop all four algorithms involved several layers of implementation and supporting changes. These can broadly be categorized into:
More specifically:
Packages used to implement the parameters object required by the SGD trainer kernel:
Packages used to implement the penalty enum for the SGD package:
-
feat: add
ml/base/sgd-classification/penalty-resolve-enum(later migrated toml/base/sgd/penalty-resolve-enum) -
feat: add
ml/base/sgd-classification/penalty-resolve-str(later migrated toml/base/sgd/penalty-resolve-str) - feat: add utilities for resolving penalty enumeration constants
-
feat: add
ml/base/sgd-classification/penalties(later migrated to `ml/base/sgd/penalties)
Packages used to implement the learning rate enum for the SGD package:
-
feat: add
ml/base/sgd-classification/learning-rate-resolve-str(later migrated toml/base/sgd/learning-rate-resolve-str) -
feat: add
ml/base/sgd-classification/learning-rate-resolve-enum(later migrated toml/base/sgd/learning-rate-resolve-enum) - feat: add utilities for resolving learning rate enumeration constants
-
feat: add
ml/base/sgd-classification/learning-rates(later migrated toml/base/sgd/learning-rates)
Packages used to implement the loss function enum for the SGD package:
-
feat: add
ml/base/sgd-classification/loss-function-resolve-enum(later migrated toml/base/sgd/loss-function-resolve-enum) -
feat: add
ml/base/sgd-classification/loss-function-resolve-str(later migrated toml/base/sgd/loss-function-resolve-str) - feat: add utilities for resolving loss function enumeration constants
-
feat: add
ml/base/sgd-classification/loss-functions(later migrated toml/base/sgd/loss-functions)
Package used to store K-Means results:
Package used to calculate the distance between two strided arrays based on a specified metric:
Packages used to implement the metric enum for the K-Means package:
Packages used to implement the algorithm enum for the K-Means package:
- feat: add
ml/base/kmeans/algorithm-resolve-enum - feat: add
ml/base/kmeans/algorithm-resolve-str - feat: add
ml/base/kmeans/algorithm-str2enum and ml/base/kmeans/algorithm-enum2str
- feat: add
ml/base/loss/float64/huber-gradient - feat: add
ml/base/loss/float64/squared-epsilon-insensitive-gradient - feat: add
ml/base/loss/float64/squared-error-gradient - feat: add
ml/base/loss/float64/epsilon-insensitive-gradient - feat: add
ml/base/loss/float64/squared-hinge-gradient - feat: add
ml/base/loss/float64/modified-huber-gradient - feat: add
ml/base/loss/float64/log-gradient - feat: add
ml/base/loss/float64/hinge-gradient
- feat: add
number/int64/base/assert/is-equal - feat: add
number/int64/base/to-words - feat: add
number/int64/base/identity - feat: add
number/int64/base/get-high-word - feat: add
number/int64/base/get-low-word - bench: refactor C benchmarks to use pre-computed values
- feat: add
stats/strided/distances/dcorrelation - feat: add
stats/strided/dpcorr
- feat!: migrate
ml/base/sgd-classificationtoml/base/sgd - feat: add support for none to indicate no regularization
- fix: rename enum to match conventions
- fix: add suffix wrappers and replace inline NaN literal
- bench: fix benchmark input in
stats/strided/dpcorrwd
- Almost all low-level kernels and utility packages required for the four algorithms have been implemented or are currently awaiting review.
- Pull requests have been raised for nearly all the required strided kernels and are ready for review.
- Implementing the high-level, user-facing APIs for all four algorithms would bring the project to completion.
Pull requests currently under review:
- feat: add
array/int64 - feat: add
ml/base/sgd/assert/is-regression-loss-function - feat: add
ml/base/sgd/assert/is-classification-loss-function - refactor: rename sqeuclidean to squared-euclidean
- feat: add
ml/strided/dsgd-trainer - feat: add
ml/strided/dkmeans-compute-inertia - feat: add
ml/strided/dkmeans-closest-centroids - feat: add
ml/base/kmeans/stats/struct-factory - feat: add
ml/strided/dkmeans-init-random-partition - feat: add
ml/base/kmeans/results/* - feat: add
ml/strided/dkmeans-init-plus-plus - feat: add
ml/base/loss/float64/hinge
The high-level, user-facing APIs for all four algorithms are still remaining, along with the loss function implementations and a few utility packages required by the strided kernels.
This tracking issue provides a complete overview of the packages, including those that have already been implemented and those that are still pending.
Machine learning is a vast and continuously evolving field, with no shortage of algorithms to implement. I hope to have the opportunity to continue contributing to stdlib and expand its machine learning capabilities with more algorithms in the future.
This Google Summer of Code cohort with stdlib has been a valuable learning experience. From working through unexpected blockers to learning new conventions and approaches, each challenge provided an opportunity to improve my understanding of both the codebase and the development process. Some of the key challenges I encountered and the lessons I learned are outlined below.
-
State management of PRNGs with N-API bindings: This was one of the biggest blockers I encountered and initially made me question whether implementing K-Means as a C addon would be feasible. After discussing the problem with my mentor, Athan, during our weekly 1:1 meetings, we were able to work towards a solution. This experience taught me the importance of discussing difficult problems early and exploring the constraints of the underlying system rather than abandoning an approach prematurely. I hope to continue working on and refining this solution beyond GSoC'26.
-
Handling optional parameters: The strided SGD kernel accepts several optional parameters. While JavaScript naturally supports optional parameters, C does not provide an equivalent mechanism, and we also needed to keep the JavaScript and C APIs consistent. This made parameter handling particularly challenging. Athan introduced me to a clean approach using parameterized arrays. The arrays can vary in size depending on the options selected by the user, while the kernel accesses the parameters using a predefined ordering. This was a useful lesson in designing interfaces that bridge languages with different programming models.
-
Using debug logs effectively: Unlike many mathematical and statistical routines, machine learning algorithms typically involve iterative computations that can run for a significant amount of time. When something goes wrong, it can therefore be difficult to determine where the issue occurred. I learned the importance of providing useful debug information at different stages of execution so that users and developers can understand the progress and behavior of an algorithm. For this, we used the debug library to provide structured debug output without cluttering the normal output of the package.
Overall, I had a great summer working with stdlib and I am incredibly grateful for the opportunity to be part of this community. This experience would not have been possible without the constant guidance and support of my project mentors, Athan and Philipp Burckhardt. Their feedback, discussions, and willingness to help me work through difficult problems played a major role in my growth throughout the cohort.
Over the course of GSoC, I have grown not only as a developer but also as a contributor to a large, actively maintained open-source project. This was my first experience contributing extensively to a codebase that is used by developers and projects around the world. Knowing that the code I wrote could eventually become part of someone else's project is both exciting and deeply rewarding.
I would also like to thank Mara, Gunj, and Karan for their support throughout the cohort, as well as my fellow GSoC contributors Prajjwal, Pratik, Kaustubh, and Sachin for the discussions, collaboration, and shared experiences along the way. The community around stdlib made the entire experience much more enjoyable.
Although the GSoC cohort is coming to an end, my work with stdlib does not end here. I have many ideas for expanding its machine learning capabilities, and I hope to continue contributing new algorithms, utilities, and improvements to the project. There is still a lot to explore, and I am excited to keep learning and contributing.
This may be the end of my GSoC journey, but it is only the beginning of my journey with stdlib. I am looking forward to what comes next!