सार्वजनिक जानकारी से बना चयन मार्गदर्शन। केवल संदर्भ हेतु।
ml_linalg एक उच्च प्रदर्शन, SIMD-अनुकूलित लाइनर बीजगणित और सांख्यिकीय संचालन के लिए डार्ट में एक पुस्तकालय है। निर्माण के लिए दक्ष संख्यात्मक डेटा संचालन के लिए डिज़ाइन किया गया है, जो मशीन लर्निंग, वैज्ञानिक गणना और डेटा विश्लेषण के लिए आवश्यक त्वरित मैट्रिक्स और वेक्टर गणना की अनुमति देता है। यह पैकेज Android, iOS, Web, macOS, Windows और Linux जैसे कई प्लेटफॉर्म का समर्थन करता है, जिससे घने गणितीय प्रक्रिया की आवश्यकता वाले क्रॉस-प्लेटफॉर्म एप्लिकेशन के लिए आदर्श बनता है। प्रदर्शन के लिए बनाया गया, यह निम्न-स्तरीय अनुकूलन का उपयोग करके गति और सटीकता प्रदान करता है।
उपयोग
मशीन लर्निंग मॉडल प्रशिक्षण
वैज्ञानिक सिमुलेशन
डेटा रूपांतरण पाइपलाइन
रियल-टाइम विश्लेषण
सांख्यिकीय मॉडलिंग
संख्यात्मक अनुकूलन
मुख्य विशेषताएँ
SIMD-त्वरित गणना
क्रॉस-प्लेटफॉर्म संगतता
मैट्रिक्स और वेक्टर संचालन
सांख्यिकीय कार्य
सभी समर्थित प्लेटफॉर्म पर उच्च प्रदर्शन
कुशल मेमोरी उपयोग
इनके लिए उपयुक्त
एमएल एप्लिकेशन बना रहे डेवलपर्स
Dart का उपयोग करने वाले डेटा वैज्ञानिक
त्वरित संख्यात्मक प्रसंस्करण की आवश्यकता वाले अनुप्रयोग
गणित-भारी एप्लिकेशन जो प्लेटफॉर्म के अनुसार काम करते हैं
प्रदर्शन-महत्वपूर्ण संख्यात्मक कोड
विचार योग्य बातें
रैखिक बीजगणित की समझ की आवश्यकता है
संख्यात्मक डेटा कार्यभार के लिए सबसे उपयुक्त
सीमित दस्तावेज़ हो सकते हैं
सामान्य उद्देश्य के लिए नहीं बनाया गया है
विशिष्ट हार्डवेयर विशेषताओं के लिए अनुकूलित
मूल README
संबंधित सोर्स रिपॉज़िटरी के मूल README का स्नैपशॉट। नवीनतम सामग्री के लिए रिपॉज़िटरी देखें।
SIMD-based linear algebra and statistics for data science with Dart
In a few words, linear algebra is a branch of mathematics that works with vectors and matrices.
Vectors and matrices are practical tools in real-life applications, such as machine learning algorithms. These significant
mathematical entities are implemented in plenty of programming languages.
As Dart offers developers good instrumentation, e.g. highly optimized virtual machine, specific data types and rich out-of-the-box library, Dart-based implementation of vectors and matrices has to be quite performant.
Among numerous standard Dart tools, there are SIMD data types, and support of SIMD computational
architecture served as inspiration for creating this library.
What is SIMD?
SIMD stands for Single instruction, multiple data - it's a computer architecture that allows
to perform uniform mathematical operations in parallel on a list-like data structure. For instance, one has two arrays:
final a = [10, 20, 30, 40];
final b = [50, 60, 70, 80];
and one needs to add these arrays element-wise. Using the regular architecture this operation could be done in the following
manner:
As you may have noticed, we need to do 4 operations one by one in a row using regular
computational approach. But with help of SIMD architecture we may do one arithmetic operation on several operands in
parallel, thus element-wise sum of two arrays can be done for just one step:
Vectors
A couple of words about the underlying architecture
The library contains two high performant vector classes based on Float32x4
and Float64x2 data types -
Float32x4Vector and Float64x2Vector (the second one is generated from the source code of the first vector's implementation)
Most of element-wise operations in the first one are performed in four "threads" and in the second one - in two "threads".
Implementation of both classes is hidden from the library's users. You can create a
Float32x4Vector or a Float64x2Vector instance via Vector factory (see examples below).
One can create Float32x4-based vectors the following way:
Float32x4-based vectors are much faster than Float64x2-based ones, but Float64x2-based vectors are more precise since
they use 64 bits to represent numbers in the memory versus 32 bits for Float32x4-based vectors.
Nevertheless, Float32x4 representation uses by default since usually 32 bits is enough for number precision, and along
with that, this representation is more performant.
The vectors are immutable: once created, the vector cannot be changed. All the vector operations
lead to creation of a new vector instance (of course, if the operation is supposed to return a Vector).
Both classes implement Iterable<double> interface, so it's possible to use them as regular
iterable collections.
It's possible to use vector instances as keys for HashMap and similar data structures
and to look up a value by the vector-key, since the hash code for equal vectors is the same:
To see the performance benefits provided by the library's vector classes, one may visit benchmark directory: one may find
there a baseline benchmark -
element-wise summation of two regular List instances and a benchmark
of a similar operation, but performed on two Float32x4Vector instances on the same amount of elements and compare
the timings:
Baseline benchmark (executed on Macbook Air mid 2017), 2 regular lists each with 10,000,000 elements:
Actual benchmark (executed on Macbook Air mid 2017), 2 vectors each with 10,000,000 elements:
It took 15 seconds to create a new regular list by summing the elements of two lists, and 0.7 second to sum two vectors -
the difference is significant.
Vector operations examples
Vector summation
import'package:ml_linalg/linalg.dart';
final vector1 = Vector.fromList([1.0, 2.0, 3.0, 4.0, 5.0]);
final vector2 = Vector.fromList([2.0, 3.0, 4.0, 5.0, 6.0]);
final result = vector1 + vector2;
print(result.toList()); // [3.0, 5.0, 7.0, 9.0, 11.0]
Vector and List summation
This operation doesn't benefit from SIMD
import'package:ml_linalg/linalg.dart';
final vector = Vector.fromList([1.0, 2.0, 3.0, 4.0, 5.0]);
final result = vector + [2.0, 3.0, 4.0, 5.0, 6.0];
print(result.toList()); // [3.0, 5.0, 7.0, 9.0, 11.0]
Summation of Vectors of different dtype
This operation doesn't benefit from SIMD
import'package:ml_linalg/linalg.dart';
final vector1 = Vector.fromList([1.0, 2.0, 3.0, 4.0, 5.0], dtype: DType.float32);
final vector2 = Vector.fromList([2.0, 3.0, 4.0, 5.0, 6.0], dtype: DType.float64);
final result = vector1 + vector2;
print(result.toList()); // [3.0, 5.0, 7.0, 9.0, 11.0]
Vector subtraction
import'package:ml_linalg/linalg.dart';
final vector1 = Vector.fromList([4.0, 5.0, 6.0, 7.0, 8.0]);
final vector2 = Vector.fromList([2.0, 3.0, 2.0, 3.0, 2.0]);
final result = vector1 - vector2;
print(result.toList()); // [2.0, 2.0, 4.0, 4.0, 6.0]
Vector and List subtraction
This operation doesn't benefit from SIMD
import'package:ml_linalg/linalg.dart';
final vector = Vector.fromList([4.0, 5.0, 6.0, 7.0, 8.0]);
final result = vector - [2.0, 3.0, 2.0, 3.0, 2.0];
print(result.toList()); // [2.0, 2.0, 4.0, 4.0, 6.0]
Subtraction of vectors of different dtype
This operation doesn't benefit from SIMD
import'package:ml_linalg/linalg.dart';
final vector1 = Vector.fromList([4.0, 5.0, 6.0, 7.0, 8.0], dtype: DType.float32);
final vector2 = Vector.fromList([2.0, 3.0, 2.0, 3.0, 2.0], dtype: DType.float64);
final result = vector1 - vector2;
print(result.toList()); // [2.0, 2.0, 4.0, 4.0, 6.0]
Element wise Vector by Vector multiplication
import'package:ml_linalg/linalg.dart';
final vector1 = Vector.fromList([1.0, 2.0, 3.0, 4.0, 5.0]);
final vector2 = Vector.fromList([2.0, 3.0, 4.0, 5.0, 6.0]);
final result = vector1 * vector2;
print(result.toList()); // [2.0, 6.0, 12.0, 20.0, 30.0]
Element wise Vector and List multiplication
This operation doesn't benefit from SIMD
import'package:ml_linalg/linalg.dart';
final vector = Vector.fromList([1.0, 2.0, 3.0, 4.0, 5.0]);
final result = vector * [2.0, 3.0, 4.0, 5.0, 6.0];
print(result.toList()); // [2.0, 6.0, 12.0, 20.0, 30.0]
Element wise multiplication of Vectors of different dtype
This operation doesn't benefit from SIMD
import'package:ml_linalg/linalg.dart';
final vector1 = Vector.fromList([1.0, 2.0, 3.0, 4.0, 5.0], dtype: DType.float32);
final vector2 = Vector.fromList([2.0, 3.0, 4.0, 5.0, 6.0], dtype: DType.float64);
final result = vector1 * vector2;
print(result.toList()); // [2.0, 6.0, 12.0, 20.0, 30.0]
Element wise Vector by Vector division
import'package:ml_linalg/linalg.dart';
final vector1 = Vector.fromList([6.0, 12.0, 24.0, 48.0, 96.0]);
final vector2 = Vector.fromList([3.0, 4.0, 6.0, 8.0, 12.0]);
final result = vector1 / vector2;
print(result.toList()); // [2.0, 3.0, 4.0, 6.0, 8.0]
Element-wise Vector and List division
This operation doesn't benefit from SIMD
import'package:ml_linalg/linalg.dart';
final vector = Vector.fromList([6.0, 12.0, 24.0, 48.0, 96.0]);
final result = vector / [3.0, 4.0, 6.0, 8.0, 12.0];
print(result.toList()); // [2.0, 3.0, 4.0, 6.0, 8.0]
Element wise division of vectors of different dtype
This operation doesn't benefit from SIMD
import'package:ml_linalg/linalg.dart';
final vector1 = Vector.fromList([6.0, 12.0, 24.0, 48.0, 96.0], dtype: DType.float32);
final vector2 = Vector.fromList([3.0, 4.0, 6.0, 8.0, 12.0], dtype: DType.float64);
final result = vector1 / vector2;
print(result.toList()); // [2.0, 3.0, 4.0, 6.0, 8.0]
Euclidean norm
import'package:ml_linalg/linalg.dart';
final vector = Vector.fromList([2.0, 3.0, 4.0, 5.0, 6.0]);
final result = vector.norm();
print(result); // sqrt(2^2 + 3^2 + 4^2 + 5^2 + 6^2) = sqrt(90) ~~ 9.48
Manhattan norm
import'package:ml_linalg/linalg.dart';
final vector = Vector.fromList([2.0, 3.0, 4.0, 5.0, 6.0]);
final result = vector.norm(Norm.manhattan);
print(result); // 2 + 3 + 4 + 5 + 6 = 20.0
Mean value
import'package:ml_linalg/linalg.dart';
final vector = Vector.fromList([2.0, 3.0, 4.0, 5.0, 6.0]);
final result = vector.mean();
print(result); // (2 + 3 + 4 + 5 + 6) / 5 = 4.0
Median value
Even length
import'package:ml_linalg/linalg.dart';
final vector = Vector.fromList([10, 12, 4, 7, 9, 12]);
final result = vector.median();
print(result); // 9.5
Odd length
import'package:ml_linalg/linalg.dart';
final vector = Vector.fromList([10, 12, 4, 7, 9, 12, 34]);
final result = vector.median();
print(result); // 10
Sum of all vector elements
import'package:ml_linalg/linalg.dart';
final vector = Vector.fromList([2.0, 3.0, 4.0, 5.0, 6.0]);
final result = vector.sum();
print(result); // 2 + 3 + 4 + 5 + 6 = 20.0
Product of all vector elements
import'package:ml_linalg/linalg.dart';
final vector = Vector.fromList([2.0, 3.0, 4.0, 5.0, 6.0]);
final result = vector.prod();
print(result); // 2 * 3 * 4 * 5 * 6 = 720
import'package:ml_linalg/linalg.dart';
final vector = Vector.fromList([2.0, 3.0, 4.0, 5.0, 6.0]);
final result = vector.exp();
print(result); // [e ^ 2, e ^ 3, e ^ 4, e ^ 5, e ^ 6]
Along with SIMD vectors, the library contains SIMD-based Matrices. One can use the matrices via
Matrix factory. The matrices are immutable as well
as vectors and also they implement Iterable interface (to be more precise, Iterable<Iterable<double>>), thus it's
possible to use them as a regular iterable collection.
Matrices are serializable, and that means that one can easily convert a Matrix instance to a json-serializable map
via toJson method, see the examples below.
Matrix operations examples
Creation of diagonal matrix
import'package:ml_linalg/matrix.dart';
final matrix = Matrix.diagonal([1, 2, 3, 4, 5]);
print(matrix);
final matrix = Matrix.fromList([
[4, 12, -16],
[12, 37, -43],
[-16, -43, 98],
], dtype: dtype);
final decomposed = matrix.decompose(Decomposition.LU);
// yields approximately the same matrix as the original one:print(decomposed.first * decomposed.last);
Matrix Cholesky decomposition
final matrix = Matrix.fromList([
[4, 12, -16],
[12, 37, -43],
[-16, -43, 98],
], dtype: dtype);
final decomposed = matrix.decompose(Decomposition.cholesky);
// yields approximately the same matrix as the original one:print(decomposed.first * decomposed.last);
Keep in mind that Cholesky decomposition is applicable only for positive definite and symmetric matrices
Matrix LU inversion
final matrix = Matrix.fromList([
[-16, -43, 98],
[33, 12.4, 37],
[12, -88.3, 4],
], dtype: dtype);
final inverted = matrix.inverse(Inverse.LU);
print(inverted * matrix);
// The output (there can be some round-off errors):// [1, 0, 0],// [0, 1, 0],// [0, 0, 1],
Matrix Cholesky inversion
final matrix = Matrix.fromList([
[4, 12, -16],
[12, 37, -43],
[-16, -43, 98],
], dtype: dtype);
final inverted = matrix.inverse(Inverse.cholesky);
print(inverted * matrix);
// The output (there can be some round-off errors):// [1, 0, 0],// [0, 1, 0],// [0, 0, 1],
Keep in mind that since this kind of inversion is based on Cholesky decomposition, the inversion is applicable only for positive definite and symmetric matrices
Lower triangular matrix inversion
final matrix = Matrix.fromList([
[ 4, 0, 0],
[ 12, 37, 0],
[-16, -43, 98],
], dtype: dtype);
final inverted = matrix.inverse(Inverse.forwardSubstitution);
print(inverted * matrix);
// The output (there can be some round-off errors):// [1, 0, 0],// [0, 1, 0],// [0, 0, 1],
Upper triangular matrix inversion
final matrix = Matrix.fromList([
[4, 12, -16],
[0, 37, -43],
[0, 0, -98],
], dtype: dtype);
final inverted = matrix.inverse(Inverse.backwardSubstitution);
print(inverted * matrix);
// The output (there can be some round-off errors):// [1, 0, 0],// [0, 1, 0],// [0, 0, 1],
To solve the system and find X, one may use the solve method:
import'package:ml_linalg/linalg.dart';
voidmain() {
final A = Matrix.fromList([
[1, 1, 1],
[0, 2, 5],
[2, 5, -1],
], dtype: dtype);
final B = Matrix.fromList([
[6],
[-4],
[27],
], dtype: dtype);
final result = A.solve(B);
print(result); // the output is close to [[5], [3], [-2]]
}
Obtaining Matrix eigenvectors and eigenvalues, Power Iteration method
The method returns a collection of pairs of an eigenvector and its corresponding eigenvalue.
By default Power iteration method is used.
final matrix = Matrix.fromList([
[1, 0],
[0, 2],
]);
final eigen = matrix.eigen();
print(eigen); // It prints the following: [Value: 1.999, Vector: (0.001, 0.999);]
The library's matrix interface offers sample method that is supposed to return a new matrix,
consisting of different segments of a source matrix. It's possible to build a new matrix from certain columns and
vectors and they should not be necessarily subsequent.
For example, one needs to create a matrix from rows 1, 3, 5 and columns 1 and 3. To do so,
it's needed to perform the following:
To restore a serialized matrix one may use Matrix.fromJson constructor:
import'package:ml_linalg/linalg.dart';
final matrix = Matrix.fromJson(serialized);
Differences between vector math and ml linalg
There are similar solutions on the internet, the most famous of which is vector_math by the Google team. At first
glance, vector_math and ml_linalg look similar - both of them are based on SIMD, but in fact, these are two completely
different libraries:
vector_math supports only four dimensions for vectors and matrices at max; ml_linalg can handle vectors and matrices
of potentially infinite length, keeping SIMD nature.