diff --git a/Cortex.sln b/Cortex.sln index 7efbb3f..de5bc21 100644 --- a/Cortex.sln +++ b/Cortex.sln @@ -1,4 +1,3 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.10.34607.79 @@ -57,6 +56,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cortex.Streams.Elasticsearc EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cortex.Types", "src\Cortex.Types\Cortex.Types.csproj", "{64E12D4C-FBB2-4004-8316-C886CBFC614B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cortex.Vectors", "src\Cortex.Vectors\Cortex.Vectors.csproj", "{268BA5C7-C6FB-4A6B-875A-492659ED4573}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cortex.Mediator.Behaviors.FluentValidation", "src\Cortex.Mediator.Behaviors.FluentValidation\Cortex.Mediator.Behaviors.FluentValidation.csproj", "{44A166BD-01E9-4A4B-9BC5-7DE01B472E73}" EndProject Global @@ -172,6 +173,10 @@ Global {64E12D4C-FBB2-4004-8316-C886CBFC614B}.Debug|Any CPU.Build.0 = Debug|Any CPU {64E12D4C-FBB2-4004-8316-C886CBFC614B}.Release|Any CPU.ActiveCfg = Release|Any CPU {64E12D4C-FBB2-4004-8316-C886CBFC614B}.Release|Any CPU.Build.0 = Release|Any CPU + {268BA5C7-C6FB-4A6B-875A-492659ED4573}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {268BA5C7-C6FB-4A6B-875A-492659ED4573}.Debug|Any CPU.Build.0 = Debug|Any CPU + {268BA5C7-C6FB-4A6B-875A-492659ED4573}.Release|Any CPU.ActiveCfg = Release|Any CPU + {268BA5C7-C6FB-4A6B-875A-492659ED4573}.Release|Any CPU.Build.0 = Release|Any CPU {44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Debug|Any CPU.Build.0 = Debug|Any CPU {44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/src/Cortex.Vectors/Abstractions/IVector.cs b/src/Cortex.Vectors/Abstractions/IVector.cs new file mode 100644 index 0000000..f0a3b44 --- /dev/null +++ b/src/Cortex.Vectors/Abstractions/IVector.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Numerics; + +namespace Cortex.Vectors +{ + /// + /// Common contract for vector collections. + /// Provides dimension metadata and core linear‑algebra operations. + /// + /// Any IEEE‑754 floating‑point numeric type (float, double, Half, decimal). + public interface IVector : IReadOnlyList where T : IFloatingPointIeee754 + { + /// Gets the number of components in the vector. + int Dimension { get; } + + /// Dot (inner) product with another vector. + /// Thrown when vector dimensions differ. + T Dot(IVector other); + + /// Euclidean norm (L2). + T Norm(); + + /// Returns a unit‑length copy of this vector. + IVector Normalize(); + } +} diff --git a/src/Cortex.Vectors/Assets/andyX.png b/src/Cortex.Vectors/Assets/andyX.png new file mode 100644 index 0000000..101a1fb Binary files /dev/null and b/src/Cortex.Vectors/Assets/andyX.png differ diff --git a/src/Cortex.Vectors/Assets/license.md b/src/Cortex.Vectors/Assets/license.md new file mode 100644 index 0000000..3c845d4 --- /dev/null +++ b/src/Cortex.Vectors/Assets/license.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2025 Buildersoft + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/Cortex.Vectors/BitVector.cs b/src/Cortex.Vectors/BitVector.cs new file mode 100644 index 0000000..69c9c99 --- /dev/null +++ b/src/Cortex.Vectors/BitVector.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Cortex.Vectors +{ + /// + /// Fixed‑length bit‑packed vector that implements for any IEEE‑754 type . + /// Each bit encodes 0 → , 1 → . + /// + public sealed class BitVector : IVector, IEquatable> where T : IFloatingPointIeee754 + { + private readonly ulong[] _blocks; + + public int Dimension { get; } + public int Count => Dimension; + + #region Construction + public BitVector(int dimension) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(dimension); + Dimension = dimension; + _blocks = new ulong[(dimension + 63) >> 6]; + } + + /// Create from indices that should be set to 1. + public BitVector(int dimension, IEnumerable oneIndices) : this(dimension) + { + foreach (var idx in oneIndices) SetBit(idx, true); + } + + /// Create from a span of bools. + public BitVector(ReadOnlySpan bits) : this(bits.Length) + { + for (int i = 0; i < bits.Length; i++) if (bits[i]) SetBit(i, true); + } + #endregion + + #region Bit helpers + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static (int blk, int off) Loc(int idx) => (idx >> 6, idx & 63); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool GetBit(int idx) + { + if ((uint)idx >= Dimension) throw new IndexOutOfRangeException(); + var (b, o) = Loc(idx); + return (_blocks[b] & (1UL << o)) != 0UL; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetBit(int idx, bool value) + { + if ((uint)idx >= Dimension) throw new IndexOutOfRangeException(); + var (b, o) = Loc(idx); + if (value) _blocks[b] |= 1UL << o; else _blocks[b] &= ~(1UL << o); + } + public int PopCount() + { + int c = 0; foreach (var v in _blocks) c += BitOperations.PopCount(v); return c; + } + #endregion + + #region IVector Implementation + public T this[int index] + { + get => GetBit(index) ? T.One : T.Zero; + } + + public T Dot(IVector other) + { + if (other is BitVector bv) + { + ValidateSameDimension(bv); + int count = 0; + for (int i = 0; i < _blocks.Length; i++) count += BitOperations.PopCount(_blocks[i] & bv._blocks[i]); + return T.CreateTruncating(count); + } + else + { + ValidateSameDimension(other); + T sum = T.Zero; + for (int i = 0; i < Dimension; i++) sum += this[i] * other[i]; + return sum; + } + } + + public T Norm() => T.Sqrt(T.CreateTruncating(PopCount())); + + public IVector Normalize() + { + var n = Norm(); + if (n == T.Zero) throw new InvalidOperationException("Cannot normalize zero bit‑vector."); + var inv = T.One / n; + var data = new T[Dimension]; + for (int i = 0; i < Dimension; i++) if (GetBit(i)) data[i] = inv; // zeros already default + return new DenseVector(data); + } + #endregion + + #region IEnumerable + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Dimension; i++) yield return this[i]; + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + #endregion + + #region Equality & HashCode + public bool Equals(BitVector? other) + { + if (other is null || other.Dimension != Dimension) return false; + for (int i = 0; i < _blocks.Length; i++) if (_blocks[i] != other._blocks[i]) return false; + return true; + } + public override bool Equals(object? obj) => obj is BitVector bv && Equals(bv); + public override int GetHashCode() => HashCode.Combine(Dimension, _blocks.Length > 0 ? _blocks[0] : 0UL); + #endregion + + private void ValidateSameDimension(IVector other) + { + if (other.Dimension != Dimension) throw new ArgumentException("Vector dimensions must match.", nameof(other)); + } + } +} diff --git a/src/Cortex.Vectors/Cortex.Vectors.csproj b/src/Cortex.Vectors/Cortex.Vectors.csproj new file mode 100644 index 0000000..8e1bda8 --- /dev/null +++ b/src/Cortex.Vectors/Cortex.Vectors.csproj @@ -0,0 +1,59 @@ + + + + net9.0;net8.0 + + 2.0.0 + 2.0.0 + Buildersoft Cortex Framework + Buildersoft + Buildersoft,EnesHoxha + Copyright © Buildersoft 2025 + + Cortex Data Framework is a robust, extensible platform designed to facilitate real-time data streaming, processing, and state management. It provides developers with a comprehensive suite of tools and libraries to build scalable, high-performance data pipelines tailored to diverse use cases. By abstracting underlying streaming technologies and state management solutions, Cortex Data Framework enables seamless integration, simplified development workflows, and enhanced maintainability for complex data-driven applications. + + + https://github.com/buildersoftio/cortex + cortex;machine‑learning;vector;ai;streaming + + 2.0.0 + license.md + andyX.png + Cortex.Vectors + True + True + True + git + Just as the Cortex in our brains handles complex processing efficiently, Cortex Data Framework brings brainpower to your data management! + https://buildersoft.io/ + README.md + + + + + + + + + + True + \ + Always + + + + + True + + + + True + + + + + + + + + diff --git a/src/Cortex.Vectors/DenseVector.cs b/src/Cortex.Vectors/DenseVector.cs new file mode 100644 index 0000000..15c71e8 --- /dev/null +++ b/src/Cortex.Vectors/DenseVector.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Cortex.Vectors +{ + /// + /// Contiguous dense representation of a mathematical vector. + /// Suitable for small‑to‑medium dimensions (< 10⁶) that are mostly non‑zero. + /// + /// Floating‑point element type. + public sealed class DenseVector : IVector, IEquatable> where T : IFloatingPointIeee754 + { + private readonly T[] _data; + + #region Construction + + public DenseVector(ReadOnlySpan span) + { + _data = span.ToArray(); + } + + public DenseVector(params T[] values) + { + _data = values.Length == 0 + ? throw new ArgumentException("Vector must have at least one component.", nameof(values)) + : (T[])values.Clone(); + } + + /// Creates a zero‑filled vector of given dimension. + public static DenseVector Zeros(int dimension) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(dimension); + return new DenseVector(new T[dimension]); + } + + /// Creates a vector where every component equals . + public static DenseVector Filled(int dimension, T value) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(dimension); + var data = new T[dimension]; + Array.Fill(data, value); + return new DenseVector(data); + } + + #endregion + + #region IVector & IReadOnlyList Implementation + + public int Dimension => _data.Length; + + public int Count => _data.Length; // IReadOnlyCollection implementation + + public T this[int index] => _data[index]; + + public T Dot(IVector other) + { + ValidateSameDimension(other); + T sum = T.Zero; + for (int i = 0; i < _data.Length; i++) + sum += _data[i] * other[i]; + return sum; + } + + public T Norm() + { + T sumSq = T.Zero; + foreach (var v in _data) + sumSq += v * v; + return T.Sqrt(sumSq); + } + + public IVector Normalize() + { + var n = Norm(); + if (n == T.Zero) + throw new InvalidOperationException("Cannot normalize the zero vector."); + var scaled = new T[_data.Length]; + for (int i = 0; i < _data.Length; i++) + scaled[i] = _data[i] / n; + return new DenseVector(scaled); + } + + #endregion + + #region Arithmetic Operators + + public static DenseVector operator +(DenseVector left, DenseVector right) + { + left.ValidateSameDimension(right); + var result = new T[left.Dimension]; + for (int i = 0; i < result.Length; i++) + result[i] = left._data[i] + right._data[i]; + return new DenseVector(result); + } + + public static DenseVector operator -(DenseVector left, DenseVector right) + { + left.ValidateSameDimension(right); + var result = new T[left.Dimension]; + for (int i = 0; i < result.Length; i++) + result[i] = left._data[i] - right._data[i]; + return new DenseVector(result); + } + + public static DenseVector operator *(DenseVector vector, T scalar) + { + var result = new T[vector.Dimension]; + for (int i = 0; i < result.Length; i++) + result[i] = vector._data[i] * scalar; + return new DenseVector(result); + } + + public static DenseVector operator *(T scalar, DenseVector vector) => vector * scalar; + + #endregion + + #region Equality & Hash + + public bool Equals(DenseVector? other) + { + if (other is null || other.Dimension != Dimension) return false; + for (int i = 0; i < Dimension; i++) + if (_data[i] != other._data[i]) return false; + return true; + } + + public override bool Equals(object? obj) => obj is DenseVector v && Equals(v); + + public override int GetHashCode() => HashCode.Combine(Dimension, _data[0], _data[^1]); + + #endregion + + #region IEnumerable Implementation + + public IEnumerator GetEnumerator() => ((IEnumerable)_data).GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _data.GetEnumerator(); + + #endregion + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ValidateSameDimension(IVector other) + { + if (other.Dimension != Dimension) + throw new ArgumentException($"Vector dimensions must match (this: {Dimension}, other: {other.Dimension}).", nameof(other)); + } + } + +} diff --git a/src/Cortex.Vectors/README.md b/src/Cortex.Vectors/README.md new file mode 100644 index 0000000..773ac55 --- /dev/null +++ b/src/Cortex.Vectors/README.md @@ -0,0 +1,123 @@ +# Cortex.Vectors 🧠 + +**Cortex.Vectors** is a High‑performance vector types—Dense, Sparse, and Bit—for AI & for .NET. + + +Built as part of the [Cortex Data Framework](https://github.com/buildersoftio/cortex), this library offers High‑performance vector types—Dense, Sparse, and Bit—for AI for: + + +- ✨ Generic‑math powered (IFloatingPointIeee754): works with float, double, decimal, … +- 🟢 DenseVector – contiguous storage, SIMD‑friendly operations +- 🔵 SparseVector – dictionary‑backed, memory‑efficient for huge, mostly‑zero spaces +- 🟡 BitVector – bit‑packed booleans with popcount & logical ops +- ⚙️ Core ops out‑of‑the‑box: dot product, L2 norm, cosine similarity, scaling, +/‑ + +--- + +[![GitHub License](https://img.shields.io/github/license/buildersoftio/cortex)](https://github.com/buildersoftio/cortex/blob/master/LICENSE) +[![NuGet Version](https://img.shields.io/nuget/v/Cortex.Vectors?label=Cortex.Vectors)](https://www.nuget.org/packages/Cortex.Vectors) +[![GitHub contributors](https://img.shields.io/github/contributors/buildersoftio/cortex)](https://github.com/buildersoftio/cortex) +[![Discord Shield](https://discord.com/api/guilds/1310034212371566612/widget.png?style=shield)](https://discord.gg/JnMJV33QHu) + + +## 🚀 Getting Started + +### Install via NuGet + +```bash +dotnet add package Cortex.Vectors +``` + +## DenseVector +```csharp +using Cortex.Vectors; + +// (1, 2, 3) +var a = new DenseVector(1f, 2f, 3f); + +// (0.5, 0.5, 0.5) +var b = DenseVector.Filled(3, 0.5f); + +float dot = a.Dot(b); // = 3.0 +var normA = a.Norm(); // ≈ 3.7417 +var unitA = a.Normalize(); // unit length +float cosine = a.CosineSimilarity(b); +``` + +## SparseVector +```csharp +using Cortex.Vectors; +using System.Collections.Generic; + +// 1‑million‑dimensional vector with two non‑zeros +var sv = new SparseVector( + dimension: 1_000_000, + nonZero: new[] + { + new KeyValuePair(42, 1.0), + new KeyValuePair(123456, 2.5) + }); + +double l2 = sv.Norm(); // √(1² + 2.5²) +var unit = sv.Normalize(); +``` + +## BitVector + +```csharp +using Cortex.Vectors; + +// length 128, bits 0, 3, and 5 set to 1 +var bv = new BitVector(128, new[] { 0, 3, 5 }); + +int ones = bv.PopCount(); // 3 +float selfDot = bv.Dot(bv); // 3.0 (generic type ⇒ float) +var l2 = bv.Norm(); // √3 +``` + +## 💬 Contributing +We welcome contributions from the community! Whether it's reporting bugs, suggesting features, or submitting pull requests, your involvement helps improve Cortex for everyone. + +### 💬 How to Contribute +1. **Fork the Repository** +2. **Create a Feature Branch** +```bash +git checkout -b feature/YourFeature +``` +3. **Commit Your Changes** +```bash +git commit -m "Add your feature" +``` +4. **Push to Your Fork** +```bash +git push origin feature/YourFeature +``` +5. **Open a Pull Request** + +Describe your changes and submit the pull request for review. + +## 📄 License +This project is licensed under the MIT License. + +## 📚 Sponsorship +Cortex is an open-source project maintained by BuilderSoft. Your support helps us continue developing and improving Cortex. Consider sponsoring us to contribute to the future of resilient streaming platforms. + +### How to Sponsor +* **Financial Contributions**: Support us through [GitHub Sponsors](https://github.com/sponsors/buildersoftio) or other preferred platforms. +* **Corporate Sponsorship**: If your organization is interested in sponsoring Cortex, please contact us directly. + +Contact Us: cortex@buildersoft.io + + +## Contact +We'd love to hear from you! Whether you have questions, feedback, or need support, feel free to reach out. + +- Email: cortex@buildersoft.io +- Website: https://buildersoft.io +- GitHub Issues: [Cortex Data Framework Issues](https://github.com/buildersoftio/cortex/issues) +- Join our Discord Community: [![Discord Shield](https://discord.com/api/guilds/1310034212371566612/widget.png?style=shield)](https://discord.gg/JnMJV33QHu) + + +Thank you for using Cortex Data Framework! We hope it empowers you to build scalable and efficient data processing pipelines effortlessly. + +Built with ❤️ by the Buildersoft team. diff --git a/src/Cortex.Vectors/SparseVector.cs b/src/Cortex.Vectors/SparseVector.cs new file mode 100644 index 0000000..bea7c51 --- /dev/null +++ b/src/Cortex.Vectors/SparseVector.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Cortex.Vectors +{ + public sealed class SparseVector : IVector, IEquatable> where T : IFloatingPointIeee754 + { + private readonly int _dimension; + private readonly Dictionary _values; + + #region Construction + public SparseVector(int dimension) : this(dimension, Enumerable.Empty>()) { } + + public SparseVector(int dimension, IEnumerable> nonZero) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(dimension); + _dimension = dimension; + _values = new Dictionary(); + foreach (var (i, val) in nonZero) + { + if (i < 0 || i >= dimension) throw new ArgumentOutOfRangeException(nameof(nonZero), "Index out of range."); + if (val != T.Zero) _values[i] = val; + } + } + + /// Creates a sparse vector where the provided indices hold the same . + public static SparseVector FromIndices(int dimension, IEnumerable indices, T value) + => new SparseVector(dimension, indices.Select(i => new KeyValuePair(i, value))); + #endregion + + #region IReadOnlyList Implementation + public int Dimension => _dimension; + public int Count => _dimension; // total logical length + public int NonZeroCount => _values.Count; + + public T this[int index] + { + get + { + if ((uint)index >= _dimension) throw new IndexOutOfRangeException(); + return _values.TryGetValue(index, out var v) ? v : T.Zero; + } + } + #endregion + + #region Core Vector Operations + public T Dot(IVector other) + { + ValidateSameDimension(other); + T sum = T.Zero; + foreach (var (i, v) in _values) sum += v * other[i]; + return sum; + } + + public T Norm() + { + T sumSq = T.Zero; + foreach (var v in _values.Values) sumSq += v * v; + return T.Sqrt(sumSq); + } + + public IVector Normalize() + { + var n = Norm(); + if (n == T.Zero) throw new InvalidOperationException("Cannot normalize zero vector."); + var scaled = _values.Select(kv => new KeyValuePair(kv.Key, kv.Value / n)); + return new SparseVector(_dimension, scaled); + } + #endregion + + #region Operators + public static SparseVector operator +(SparseVector a, SparseVector b) + { + a.ValidateSameDimension(b); + var result = new Dictionary(a._values); + foreach (var (i, v) in b._values) + { + if (result.TryGetValue(i, out var existing)) + { + var sum = existing + v; + if (sum == T.Zero) result.Remove(i); else result[i] = sum; + } + else result[i] = v; + } + return new SparseVector(a._dimension, result); + } + + public static SparseVector operator -(SparseVector a, SparseVector b) + { + a.ValidateSameDimension(b); + var result = new Dictionary(a._values); + foreach (var (i, v) in b._values) + { + if (result.TryGetValue(i, out var existing)) + { + var diff = existing - v; + if (diff == T.Zero) result.Remove(i); else result[i] = diff; + } + else if (v != T.Zero) result[i] = -v; + } + return new SparseVector(a._dimension, result); + } + + public static SparseVector operator *(SparseVector vector, T scalar) + { + if (scalar == T.Zero) return new SparseVector(vector._dimension); + var result = vector._values.ToDictionary(k => k.Key, k => k.Value * scalar); + return new SparseVector(vector._dimension, result); + } + + public static SparseVector operator *(T scalar, SparseVector vector) => vector * scalar; + #endregion + + #region Equality & Hashing + public bool Equals(SparseVector? other) + { + if (other is null || other._dimension != _dimension || other._values.Count != _values.Count) return false; + foreach (var kv in _values) + if (!other._values.TryGetValue(kv.Key, out var v) || v != kv.Value) return false; + return true; + } + + public override bool Equals(object? obj) => obj is SparseVector sv && Equals(sv); + public override int GetHashCode() => HashCode.Combine(_dimension, _values.Count); + #endregion + + #region Enumeration + public IEnumerator GetEnumerator() + { + for (int i = 0; i < _dimension; i++) yield return this[i]; + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + #endregion + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ValidateSameDimension(IVector other) + { + if (other.Dimension != _dimension) throw new ArgumentException("Vector dimensions must match.", nameof(other)); + } + } +} diff --git a/src/Cortex.Vectors/VectorExtensions.cs b/src/Cortex.Vectors/VectorExtensions.cs new file mode 100644 index 0000000..6ee463e --- /dev/null +++ b/src/Cortex.Vectors/VectorExtensions.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Linq; +using System.Numerics; + +namespace Cortex.Vectors +{ + public static class VectorExtensions + { + public static T CosineSimilarity(this IVector a, IVector b) where T : IFloatingPointIeee754 + { + var denom = a.Norm() * b.Norm(); + return denom == T.Zero ? T.Zero : a.Dot(b) / denom; + } + + public static SparseVector ToSparse(this DenseVector v) where T : IFloatingPointIeee754 + => new SparseVector(v.Dimension, + Enumerable.Range(0, v.Dimension) + .Where(i => v[i] != T.Zero) + .Select(i => new KeyValuePair(i, v[i]))); + } +}