Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
01b3ed0
Added operator for performing `CrossProduct`
ncguilbeault Nov 13, 2025
93adfcb
Added operator `LeastSquaresSolve` for solving systems of linear equa…
ncguilbeault Nov 13, 2025
a37c8c4
Added operator `TensorSolve` to compute a tensor solution to the prob…
ncguilbeault Nov 13, 2025
ace5c1f
Updated `SingularValueDecomposition` to return a struct output instea…
ncguilbeault Nov 13, 2025
244ea28
Added operator to support chaining matrix multiplication
ncguilbeault Nov 13, 2025
c82c661
Improved XML documentation
ncguilbeault Dec 12, 2025
6ceedd1
Refactored `MatrixMultiply` overload with a tuple of 2 tensors to use…
ncguilbeault Dec 19, 2025
6aa9dbd
Added operator to compute the rank of a matrix
ncguilbeault Dec 19, 2025
06d81b6
Added operator to compute the QR decomposition of a matrix
ncguilbeault Dec 19, 2025
99f73aa
Added an operator to solve a triangular system of equations
ncguilbeault Dec 19, 2025
e29465e
Rename LeastSquaresSolve -> LeastSquares
ncguilbeault Jul 15, 2026
f95495d
Rename TriangularSolve -> SolveTriangular
ncguilbeault Jul 15, 2026
25b6f6d
Move result structs outside of nested classes
ncguilbeault Jul 15, 2026
f0dd68c
Fix formatting issues
ncguilbeault Aug 20, 2026
a7fcd4d
Align xml docs and description attributes across package
ncguilbeault Aug 20, 2026
7c4e69c
Rename EigenDecompositionResult -> EigenvalueDecompositionResult
ncguilbeault Aug 20, 2026
b6c26b3
Refactor long, single line doc strings into multiple lines
ncguilbeault Aug 20, 2026
b060952
Align member doc strings across package
ncguilbeault Aug 20, 2026
80f53df
Remove redundant using statements
ncguilbeault Aug 20, 2026
e8fb927
Remove empty <param> and <returns> tags
ncguilbeault Aug 20, 2026
aabfdec
Move MatrixMultiply operator to root namespace consistent with torch
ncguilbeault Aug 20, 2026
357fb3e
Add separate MultiDot operator for optimized matrix multiplication
ncguilbeault Aug 20, 2026
aea5210
Refactor MultiDot to handle collections with less than two tensors
ncguilbeault Aug 20, 2026
6912521
Remove explicit property initializers that default to the same value
ncguilbeault Aug 20, 2026
9bc86a8
Rename to camelCase
ncguilbeault Aug 20, 2026
a68138a
Rename input variable in lambda expression to tensor for clarity
ncguilbeault Aug 20, 2026
596498f
Improve doc strings for members of SingularValueDecompositionResult
ncguilbeault Aug 20, 2026
d5c7279
Add using statement for TorchSharp.torch.linalg
ncguilbeault Aug 20, 2026
8e125cc
Add missing reference to tensor X in doc string
ncguilbeault Aug 20, 2026
c6a361f
Update docs strings and descriptions of boolean properties
ncguilbeault Aug 20, 2026
7646fc2
Rename to tensor for single tensor input
ncguilbeault Aug 20, 2026
2912207
Rename input to value for multi tensor input
ncguilbeault Aug 20, 2026
5591b3d
Refactor to use simplified return statement
ncguilbeault Aug 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 14 additions & 16 deletions src/Bonsai.ML.Torch/LinearAlgebra/CholeskyDecomposition.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
using System;
using System;
using System.ComponentModel;
using System.Reactive.Linq;
using static TorchSharp.torch;

namespace Bonsai.ML.Torch.LinearAlgebra
namespace Bonsai.ML.Torch.LinearAlgebra;

/// <summary>
/// Represents an operator that computes the Cholesky decomposition of a complex Hermitian or real symmetric
/// positive-definite matrix.
/// </summary>
[Combinator]
[Description("Computes the Cholesky decomposition of a complex Hermitian or real symmetric positive-definite matrix.")]
[WorkflowElementCategory(ElementCategory.Transform)]
public class CholeskyDecomposition
{
/// <summary>
/// Computes the Cholesky decomposition of a complex Hermitian or real symmetric positive-definite matrix.
/// </summary>
[Combinator]
[Description("Computes the Cholesky decomposition of a complex Hermitian or real symmetric positive-definite matrix.")]
[WorkflowElementCategory(ElementCategory.Transform)]
public class CholeskyDecomposition
public IObservable<Tensor> Process(IObservable<Tensor> source)
{
/// <summary>
/// Computes the Cholesky decomposition of a complex Hermitian or real symmetric positive-definite matrix.
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public IObservable<Tensor> Process(IObservable<Tensor> source)
{
return source.Select(linalg.cholesky);
}
return source.Select(linalg.cholesky);
}
}
}
33 changes: 33 additions & 0 deletions src/Bonsai.ML.Torch/LinearAlgebra/CrossProduct.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.ComponentModel;
using System.Reactive.Linq;
using static TorchSharp.torch;
using static TorchSharp.torch.linalg;

namespace Bonsai.ML.Torch.LinearAlgebra;

/// <summary>
/// Represents an operator that computes the cross product of 2 tensors.
/// </summary>
[Combinator]
[Description("Computes the cross product of 2 tensors.")]
[WorkflowElementCategory(ElementCategory.Transform)]
public class CrossProduct
{
/// <summary>
/// Gets or sets the dimension along which to perform the operation.
/// </summary>
Comment on lines +17 to +19

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// <summary>
/// The dimension to perform the operation.
/// </summary>
/// <summary>
/// Gets or sets the dimension along which to compute the cross product.
/// </summary>
[Description("The dimension along which to compute the cross product.")]

This is the only new property without a [Description], so it will not get a tooltip in the property grid, and the PR adds one to Norm and SingularValueDecomposition. The wording follows Concat.Dimension and Stack.Dimension, which both read "The dimension along which to concatenate the tensors."

The <summary> voice is also mixed across the PR. MatrixRank, QRDecomposition and SolveTriangular lead with "Gets or sets", while this file, Norm, SingularValueDecomposition and TensorSolve lead with "The" or "Whether". "Gets or sets" is the form I would settle on for the summaries. TensorSolve.cs:18 also reads "The dimensions to perform the operation", which is missing a preposition.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated it for this PR but it's worth another PR to update this across the entire codebase.

[Description("The dimension along which to perform the operation.")]
public long Dimension { get; set; } = -1;

/// <summary>
/// Computes the cross product of 2 tensors.
/// </summary>
public IObservable<Tensor> Process(IObservable<Tuple<Tensor, Tensor>> source)
{
return source.Select(value =>
{
return cross(value.Item1, value.Item2, Dimension);
});
}
}
29 changes: 13 additions & 16 deletions src/Bonsai.ML.Torch/LinearAlgebra/Determinant.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
using System;
using System;
using System.ComponentModel;
using System.Reactive.Linq;
using static TorchSharp.torch;

namespace Bonsai.ML.Torch.LinearAlgebra
namespace Bonsai.ML.Torch.LinearAlgebra;

/// <summary>
/// Represents an operator that computes the determinant of a square matrix.
/// </summary>
[Combinator]
[Description("Computes the determinant of a square matrix.")]
[WorkflowElementCategory(ElementCategory.Transform)]
public class Determinant
{
/// <summary>
/// Computes the determinant of a square matrix.
/// </summary>
[Combinator]
[Description("Computes the determinant of a square matrix.")]
[WorkflowElementCategory(ElementCategory.Transform)]
public class Determinant
public IObservable<Tensor> Process(IObservable<Tensor> source)
{
/// <summary>
/// Computes the determinant of a square matrix.
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public IObservable<Tensor> Process(IObservable<Tensor> source)
{
return source.Select(linalg.det);
}
return source.Select(linalg.det);
}
}
}
30 changes: 14 additions & 16 deletions src/Bonsai.ML.Torch/LinearAlgebra/EigenvalueDecomposition.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
using System;
using System;
using System.ComponentModel;
using System.Reactive.Linq;
using static TorchSharp.torch;
using static TorchSharp.torch.linalg;

namespace Bonsai.ML.Torch.LinearAlgebra
namespace Bonsai.ML.Torch.LinearAlgebra;

/// <summary>
/// Represents an operator that computes the eigenvalue decomposition of a square matrix if it exists.
/// </summary>
[Combinator]
[Description("Computes the eigenvalue decomposition of a square matrix if it exists.")]
[WorkflowElementCategory(ElementCategory.Transform)]
public class EigenvalueDecomposition
{
/// <summary>
/// Computes the eigenvalue decomposition of a square matrix if it exists.
/// </summary>
[Combinator]
[Description("Computes the eigenvalue decomposition of a square matrix if it exists.")]
[WorkflowElementCategory(ElementCategory.Transform)]
public class EigenvalueDecomposition
public IObservable<EigenvalueDecompositionResult> Process(IObservable<Tensor> source)
{
/// <summary>
/// Computes the eigenvalue decomposition of a square matrix if it exists.
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public IObservable<Tuple<Tensor, Tensor>> Process(IObservable<Tensor> source)
{
return source.Select(tensor => linalg.eig(tensor).ToTuple());
}
return source.Select(tensor => new EigenvalueDecompositionResult(eig(tensor)));
}
}
}
20 changes: 20 additions & 0 deletions src/Bonsai.ML.Torch/LinearAlgebra/EigenvalueDecompositionResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using static TorchSharp.torch;

namespace Bonsai.ML.Torch.LinearAlgebra;

/// <summary>
/// Represents the result of an eigenvalue decomposition.
/// </summary>
/// <param name="result">The tuple containing the eigenvalues and eigenvectors.</param>
public readonly struct EigenvalueDecompositionResult((Tensor eigenvalues, Tensor eigenvectors) result)
{
/// <summary>
/// The eigenvalues of the decomposition.
/// </summary>
public Tensor Eigenvalues => result.eigenvalues;

/// <summary>
/// The eigenvectors of the decomposition.
/// </summary>
public Tensor Eigenvectors => result.eigenvectors;
}
29 changes: 13 additions & 16 deletions src/Bonsai.ML.Torch/LinearAlgebra/Inverse.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
using System;
using System;
using System.ComponentModel;
using System.Reactive.Linq;
using static TorchSharp.torch;
using static TorchSharp.torch.linalg;

namespace Bonsai.ML.Torch.LinearAlgebra
namespace Bonsai.ML.Torch.LinearAlgebra;

/// <summary>
/// Represents an operator that computes the inverse of the input matrix.
/// </summary>
[Combinator]
[Description("Computes the inverse of the input matrix.")]
[WorkflowElementCategory(ElementCategory.Transform)]
public class Inverse
{
/// <summary>
/// Computes the inverse of the input matrix.
/// </summary>
[Combinator]
[Description("Computes the inverse of the input matrix.")]
[WorkflowElementCategory(ElementCategory.Transform)]
public class Inverse
public IObservable<Tensor> Process(IObservable<Tensor> source)
{
/// <summary>
/// Computes the inverse of the input matrix.
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public IObservable<Tensor> Process(IObservable<Tensor> source)
{
return source.Select(inv);
}
return source.Select(inv);
}
}
}
25 changes: 25 additions & 0 deletions src/Bonsai.ML.Torch/LinearAlgebra/LeastSquares.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.ComponentModel;
using System.Reactive.Linq;
using static TorchSharp.torch;

namespace Bonsai.ML.Torch.LinearAlgebra;

/// <summary>
/// Represents an operator that computes the solution to the least squares and least norm problems for a full rank
/// matrix A of size m*n and a matrix B of size m*k.
/// </summary>
[Combinator]
[Description("Computes the solution to the least squares and least norm problems for a full rank matrix A of size m*n and a matrix B of size m*k.")]
[WorkflowElementCategory(ElementCategory.Transform)]
public class LeastSquares
{
/// <summary>
/// Computes the solution to the least squares and least norm problems for a full rank matrix A of size m*n and a
/// matrix B of size m*k.
/// </summary>
public IObservable<LeastSquaresResult> Process(IObservable<Tuple<Tensor, Tensor>> source)
{
return source.Select(value => new LeastSquaresResult(linalg.lstsq(value.Item1, value.Item2)));
}
}
34 changes: 34 additions & 0 deletions src/Bonsai.ML.Torch/LinearAlgebra/LeastSquaresResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using static TorchSharp.torch;

namespace Bonsai.ML.Torch.LinearAlgebra;

/// <summary>
/// Represents the result of solving of linear equations using the least squares method.
/// </summary>
public readonly struct LeastSquaresResult((
Tensor solution,
Tensor residuals,
Tensor rank,
Tensor singularValues
) result)
{
/// <summary>
/// The solution to the system of equations.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The member docs lead with "The" here and in SingularValueDecompositionResult, but with "Gets the" in EigenDecompositionResult, QRDecompositionResult and SignLogDeterminantResult. These are all get-only properties, so "Gets the" is the form I would standardize on across all five.

/// </summary>
public Tensor Solution => result.solution;

/// <summary>
/// The residual error.
/// </summary>
public Tensor Residuals => result.residuals;

/// <summary>
/// The effective rank of the solution.
/// </summary>
public Tensor Rank => result.rank;

/// <summary>
/// The singular values of the solution.
/// </summary>
public Tensor SingularValues => result.singularValues;
}
46 changes: 46 additions & 0 deletions src/Bonsai.ML.Torch/LinearAlgebra/MatrixRank.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;
using System.ComponentModel;
using System.Reactive.Linq;
using static TorchSharp.torch;
using static TorchSharp.torch.linalg;

namespace Bonsai.ML.Torch.LinearAlgebra;

/// <summary>
/// Represents an operator that computes the numerical rank of a matrix.
/// </summary>
[Combinator]
[Description("Computes the numerical rank of a matrix.")]
[WorkflowElementCategory(ElementCategory.Transform)]
public class MatrixRank
{
/// <summary>
/// Gets or sets the absolute tolerance for singular values to be considered non-zero.
/// </summary>
[Description("The absolute tolerance for singular values to be considered non-zero.")]
public double? AbsoluteTolerance { get; set; }

/// <summary>
/// Gets or sets the relative tolerance for singular values to be considered non-zero.
/// </summary>
[Description("The relative tolerance for singular values to be considered non-zero.")]
public double? RelativeTolerance { get; set; }

/// <summary>
/// Gets or sets whether to treat the input matrix as Hermitian if input is complex or symmetric if real.
/// </summary>
/// <remarks>
/// True indicates that the input matrix is Hermitian if complex or symmetric if real; otherwise, the input matrix
/// is treated as a generic matrix.
/// </remarks>
[Description("True indicates that the input matrix is Hermitian if complex or symmetric if real; otherwise, the input matrix is treated as a generic matrix.")]
public bool Hermitian { get; set; }

/// <summary>
/// Computes the numerical rank of a matrix.
/// </summary>
public IObservable<Tensor> Process(IObservable<Tensor> source)
{
return source.Select(tensor => matrix_rank(tensor, atol: AbsoluteTolerance, rtol: RelativeTolerance, hermitian: Hermitian));
}
}
Loading