diff --git a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs index ebe7a37a7de8ed..4847b33842bb4b 100644 --- a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs +++ b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs @@ -206,6 +206,7 @@ public void FlattenTo(scoped System.Span destination) { } public System.Numerics.Tensors.ReadOnlyTensorSpan Slice(params scoped System.ReadOnlySpan ranges) { throw null; } public System.Numerics.Tensors.ReadOnlyTensorSpan Slice(params scoped System.ReadOnlySpan startIndexes) { throw null; } public override string ToString() { throw null; } + public string ToString(params scoped System.ReadOnlySpan maximumLengths) { throw null; } public bool TryCopyTo(scoped in System.Numerics.Tensors.TensorSpan destination) { throw null; } public bool TryFlattenTo(scoped System.Span destination) { throw null; } public bool TryGetSpan(scoped System.ReadOnlySpan startIndexes, int length, out System.ReadOnlySpan span) { throw null; } @@ -570,9 +571,6 @@ public static void ResizeTo(scoped in System.Numerics.Tensors.Tensor tenso public static ref readonly System.Numerics.Tensors.TensorSpan TanPi(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.ITrigonometricFunctions { throw null; } public static System.Numerics.Tensors.Tensor Tan(in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.ITrigonometricFunctions { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan Tan(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.ITrigonometricFunctions { throw null; } - public static string ToString(this in System.Numerics.Tensors.ReadOnlyTensorSpan tensor, System.ReadOnlySpan maximumLengths) { throw null; } - public static string ToString(this in System.Numerics.Tensors.TensorSpan tensor, System.ReadOnlySpan maximumLengths) { throw null; } - public static string ToString(this System.Numerics.Tensors.Tensor tensor, System.ReadOnlySpan maximumLengths) { throw null; } public static System.Numerics.Tensors.Tensor TrailingZeroCount(in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.IBinaryInteger { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan TrailingZeroCount(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.IBinaryInteger { throw null; } public static System.Numerics.Tensors.Tensor Transpose(System.Numerics.Tensors.Tensor tensor) { throw null; } @@ -1218,6 +1216,7 @@ public void FlattenTo(scoped System.Span destination) { } public System.Numerics.Tensors.TensorSpan Slice(params scoped System.ReadOnlySpan ranges) { throw null; } public System.Numerics.Tensors.TensorSpan Slice(params scoped System.ReadOnlySpan startIndexes) { throw null; } public override string ToString() { throw null; } + public string ToString(params scoped System.ReadOnlySpan maximumLengths) { throw null; } public bool TryCopyTo(scoped in System.Numerics.Tensors.TensorSpan destination) { throw null; } public bool TryFlattenTo(scoped System.Span destination) { throw null; } public bool TryGetSpan(scoped System.ReadOnlySpan startIndexes, int length, out System.Span span) { throw null; } @@ -1297,6 +1296,7 @@ void System.Numerics.Tensors.ITensor.Fill(object value) { } static System.Numerics.Tensors.Tensor System.Numerics.Tensors.ITensor, T>.CreateFromShapeUninitialized(scoped System.ReadOnlySpan lengths, bool pinned) { throw null; } static System.Numerics.Tensors.Tensor System.Numerics.Tensors.ITensor, T>.CreateFromShapeUninitialized(scoped System.ReadOnlySpan lengths, scoped System.ReadOnlySpan strides, bool pinned) { throw null; } public System.Numerics.Tensors.Tensor ToDenseTensor() { throw null; } + public override string ToString() { throw null; } public string ToString(params scoped System.ReadOnlySpan maximumLengths) { throw null; } public bool TryCopyTo(scoped in System.Numerics.Tensors.TensorSpan destination) { throw null; } public bool TryFlattenTo(scoped System.Span destination) { throw null; } diff --git a/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml b/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml index b9631acb2d7041..cbc50b64ff0de3 100644 --- a/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml @@ -176,6 +176,27 @@ lib/net8.0/System.Numerics.Tensors.dll true + + CP0002 + M:System.Numerics.Tensors.Tensor.ToString``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@,System.ReadOnlySpan{System.IntPtr}) + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.ToString``1(System.Numerics.Tensors.Tensor{``0},System.ReadOnlySpan{System.IntPtr}) + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.ToString``1(System.Numerics.Tensors.TensorSpan{``0}@,System.ReadOnlySpan{System.IntPtr}) + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + CP0002 M:System.Numerics.Tensors.Tensor`1.get_Item(System.Numerics.Tensors.Tensor{System.Boolean}) @@ -379,6 +400,27 @@ lib/net9.0/System.Numerics.Tensors.dll true + + CP0002 + M:System.Numerics.Tensors.Tensor.ToString``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@,System.ReadOnlySpan{System.IntPtr}) + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.ToString``1(System.Numerics.Tensors.Tensor{``0},System.ReadOnlySpan{System.IntPtr}) + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + + + CP0002 + M:System.Numerics.Tensors.Tensor.ToString``1(System.Numerics.Tensors.TensorSpan{``0}@,System.ReadOnlySpan{System.IntPtr}) + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + CP0002 M:System.Numerics.Tensors.Tensor`1.get_Item(System.Numerics.Tensors.Tensor{System.Boolean}) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs index 931428fd3090d6..dbc60893e25672 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan_1.cs @@ -432,9 +432,20 @@ ref Unsafe.Add(ref _reference, linearOffset), ); } - /// Returns the string representation of the tensor span. - /// The string representation of the tensor span. - public override string ToString() => $"System.Numerics.Tensors.ReadOnlyTensorSpan<{typeof(T).Name}>[{_shape}]"; + /// Returns the string representation of the tensor. + /// The string representation of the tensor. + /// This API only lists the shape of the tensor, it does not include the contents. + public override string ToString() => ToString([]); + + /// Creates a representation of the tensor. + /// The maximum number of elements to print for each dimension of the tensor. + /// A representation of the tensor. + /// is not empty and does not contain elements. + /// + /// No contents will be printed if is empty. + /// If a given dimension contains more elements then the corresponding limit specified by , remaining elements will be represented by ... + /// + public string ToString(params scoped ReadOnlySpan maximumLengths) => Tensor.ToString(this, maximumLengths, "System.Numerics.Tensors.ReadOnlyTensorSpan"); /// public bool TryCopyTo(scoped in TensorSpan destination) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs index 8bc145f8e0e042..c6e4e116744fbd 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs @@ -2064,98 +2064,147 @@ public static ref readonly TensorSpan StackAlongDimension(scoped ReadOnlyS #endregion #region ToString - /// - /// Creates a representation of the ."/> - /// - /// The you want to represent as a string. - /// Maximum Length of each dimension - /// A representation of the - public static string ToString(this in TensorSpan tensor, ReadOnlySpan maximumLengths) - => tensor.AsReadOnlyTensorSpan().ToString(maximumLengths); - - /// - /// Creates a representation of the ."/> - /// - /// - /// The you want to represent as a string. - /// Maximum Length of each dimension - public static string ToString(this in ReadOnlyTensorSpan tensor, ReadOnlySpan maximumLengths) + internal static string ToString(in ReadOnlyTensorSpan tensor, ReadOnlySpan maximumLengths, string typeName) { - if (maximumLengths.Length != tensor.Rank) + if (!maximumLengths.IsEmpty) { - ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(tensor)); + ArgumentOutOfRangeException.ThrowIfNotEqual(maximumLengths.Length, tensor.Rank); } - StringBuilder sb = new(); - ToString(in tensor, maximumLengths, sb); - return sb.ToString(); - } + var sb = new StringBuilder(typeName); - internal static void ToString(in ReadOnlyTensorSpan tensor, ReadOnlySpan maximumLengths, StringBuilder sb, int indentLevel = 0) - { - Debug.Assert(maximumLengths.Length != tensor.Rank); + sb.Append('<'); + sb.Append(typeof(T).Name); + sb.Append('>'); - sb.Append(' ', indentLevel * 2); sb.Append('['); + sb.AppendJoin(", ", tensor.Lengths); + sb.Append(']'); - if (tensor.Rank != 0) + if (!maximumLengths.IsEmpty) { - nint length = nint.Max(tensor.Lengths[0], maximumLengths[0]); + sb.AppendLine(" {"); - if (tensor.Rank != 1) + if (tensor.Rank == 1) { - string separator = string.Empty; - - for (nint i = 0; i < length; i++) - { - sb.AppendLine(separator); + nint length = nint.Min(tensor.Lengths[0], maximumLengths[0]); + ToString(tensor, length, sb, indentLevel: 1); + sb.AppendLine(); + } + else + { + ToString(tensor, maximumLengths, sb); + } - TensorShape tmpShape = TensorShape.Create(tensor.Lengths[1..], tensor.Strides[1..], tensor.IsPinned); - ReadOnlyTensorSpan tmpTensor = new ReadOnlyTensorSpan(ref Unsafe.Add(ref tensor._reference, i * tensor.Strides[0]), tmpShape); - ToString(tmpTensor, maximumLengths[1..], sb, indentLevel + 1); + sb.Append('}'); + } + return sb.ToString(); + } - separator = ","; - } + private static void ToString(in ReadOnlyTensorSpan tensor, ReadOnlySpan maximumLengths, StringBuilder sb, int indentLevel = 0) + { + nint length = nint.Min(tensor.Lengths[0], maximumLengths[0]); - if (length != tensor.Lengths[0]) - { - sb.AppendLine(separator); - sb.Append(' ', indentLevel * 2); - sb.AppendLine("..."); - } + if (indentLevel != 0) + { + if (tensor.Rank != 1) + { + sb.Append(' ', indentLevel * 2); + sb.AppendLine("["); } else { - string separator = " "; + ToString(tensor, length, sb, indentLevel); + return; + } + } - for (nint i = 0; i < length; i++) - { - sb.Append(separator); - sb.Append(Unsafe.Add(ref tensor._reference, i)); - separator = ", "; - } + if (length != 0) + { + TensorShape tmpShape = TensorShape.Create(tensor.Lengths[1..], tensor.Strides[1..], tensor.IsPinned); - if (length != tensor.Lengths[0]) - { - sb.Append(separator); - sb.Append("..."); - } + ReadOnlyTensorSpan tmpTensor = new ReadOnlyTensorSpan(ref tensor._reference, tmpShape); + ToString(tmpTensor, maximumLengths[1..], sb, indentLevel + 1); - sb.Append(separator); + for (nint i = 1; i < length; i++) + { + sb.AppendLine(","); + tmpTensor = new ReadOnlyTensorSpan(ref Unsafe.Add(ref tensor._reference, i * tensor.Strides[0]), tmpShape); + ToString(tmpTensor, maximumLengths[1..], sb, indentLevel + 1); } + + if (length != tensor.Lengths[0]) + { + sb.AppendLine(","); + sb.Append(' ', (indentLevel + 1) * 2); + sb.Append(".."); + } + sb.AppendLine(); } + else + { + sb.Append(' ', (indentLevel + 1) * 2); + sb.AppendLine(".."); + } + sb.Append(' ', indentLevel * 2); + + if (indentLevel != 0) + { + sb.Append(']'); + } + } + + private static void ToString(in ReadOnlyTensorSpan tensor, nint length, StringBuilder sb, int indentLevel) + { + sb.Append(' ', indentLevel * 2); + sb.Append('['); + + if (length != 0) + { + sb.Append(tensor._reference); + + for (nint i = 1; i < length; i++) + { + sb.Append(", "); + sb.Append(Unsafe.Add(ref tensor._reference, i)); + } + + if (length != tensor.Lengths[0]) + { + sb.Append(", .."); + } + } + else + { + sb.Append(".."); + } + sb.Append(']'); } - /// - /// Creates a representation of the ."/> - /// - /// The you want to represent as a string. - /// Maximum Length of each dimension - /// A representation of the - public static string ToString(this Tensor tensor, ReadOnlySpan maximumLengths) - => tensor.AsReadOnlyTensorSpan().ToString(maximumLengths); + private static StringBuilder AppendJoin(this StringBuilder sb, string separator, ReadOnlySpan values) + { + if (values.IsEmpty) + { + return sb; + } + if (values[0] is not null) + { + sb.Append(values[0]); + } + + for (int i = 1; i < values.Length; i++) + { + sb.Append(separator); + + if (values[i] is not null) + { + sb.Append(values[i]); + } + } + return sb; + } #endregion #region Transpose diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan_1.cs index fe2778077a0f2e..e13655b0ca15dc 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan_1.cs @@ -328,8 +328,11 @@ ref Unsafe.Add(ref _reference, linearOffset), ); } - /// - public override string ToString() => $"System.Numerics.Tensors.TensorSpan<{typeof(T).Name}>[{_shape}]"; + /// + public override string ToString() => ToString([]); + + /// + public string ToString(params scoped ReadOnlySpan maximumLengths) => Tensor.ToString(AsReadOnlyTensorSpan(), maximumLengths, "System.Numerics.Tensors.TensorSpan"); /// public bool TryCopyTo(scoped in TensorSpan destination) => AsReadOnlyTensorSpan().TryCopyTo(destination); diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs index 17e42e4aec08c0..b475cd2a2e66e4 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor_1.cs @@ -288,21 +288,11 @@ public Tensor ToDenseTensor() /// public bool TryGetSpan(scoped ReadOnlySpan startIndexes, int length, out ReadOnlySpan span) => AsReadOnlyTensorSpan().TryGetSpan(startIndexes, length, out span); - /// - /// Creates a representation of the ."/> - /// - /// Maximum Length of each dimension - /// A representation of the - public string ToString(params ReadOnlySpan maximumLengths) - { - var sb = new StringBuilder($"System.Numerics.Tensors.Tensor<{typeof(T).Name}>[{_shape}]"); - - sb.AppendLine("{"); - Tensor.ToString(AsReadOnlyTensorSpan(), maximumLengths, sb); - sb.AppendLine("}"); + /// + public override string ToString() => ToString([]); - return sb.ToString(); - } + /// + public string ToString(params scoped ReadOnlySpan maximumLengths) => Tensor.ToString(AsReadOnlyTensorSpan(), maximumLengths, "System.Numerics.Tensors.Tensor"); // // IEnumerable diff --git a/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs b/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs index 939c76a9084a6c..bce2ad21e05d6c 100644 --- a/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs @@ -1304,5 +1304,138 @@ public static void TryGetSpanFailsForInvalidLengthsTest() Assert.False(ReadOnlyTensorSpan.TryGetSpan([3, 3], 2, out span)); Assert.Equal(0, span.Length); } + + [Fact] + public static void ToStringTest() + { + ReadOnlyTensorSpan tensor = Tensor.Create([1, 2, 3, 4, 5], lengths: [5]); + string expected = "System.Numerics.Tensors.ReadOnlyTensorSpan[5]"; + Assert.Equal(expected, tensor.ToString()); + + tensor = Tensor.Create([1, 2, 3, 4], lengths: [2, 2]); + expected = "System.Numerics.Tensors.ReadOnlyTensorSpan[2, 2]"; + Assert.Equal(expected, tensor.ToString()); + + tensor = Tensor.Create(Enumerable.Range(1, 27).ToArray(), lengths: [3, 3, 3]); + expected = "System.Numerics.Tensors.ReadOnlyTensorSpan[3, 3, 3]"; + Assert.Equal(expected, tensor.ToString()); + } + + [Fact] + public static void ToStringAllDataTest() + { + ReadOnlyTensorSpan tensor = Tensor.Create([1, 2, 3, 4, 5], lengths: [5]); + string expected = """ + System.Numerics.Tensors.ReadOnlyTensorSpan[5] { + [1, 2, 3, 4, 5] + } + """; + Assert.Equal(expected, tensor.ToString([5])); + + tensor = Tensor.Create([1, 2, 3, 4], lengths: [2, 2]); + expected = """ + System.Numerics.Tensors.ReadOnlyTensorSpan[2, 2] { + [1, 2], + [3, 4] + } + """; + Assert.Equal(expected, tensor.ToString([2, 2])); + + tensor = Tensor.Create(Enumerable.Range(1, 27).ToArray(), lengths: [3, 3, 3]); + expected = """ + System.Numerics.Tensors.ReadOnlyTensorSpan[3, 3, 3] { + [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9] + ], + [ + [10, 11, 12], + [13, 14, 15], + [16, 17, 18] + ], + [ + [19, 20, 21], + [22, 23, 24], + [25, 26, 27] + ] + } + """; + Assert.Equal(expected, tensor.ToString([3, 3, 3])); + } + + [Fact] + public static void ToStringPartialDataTest() + { + ReadOnlyTensorSpan tensor = Tensor.Create([1, 2, 3, 4, 5], lengths: [5]); + string expected = """ + System.Numerics.Tensors.ReadOnlyTensorSpan[5] { + [1, 2, 3, ..] + } + """; + Assert.Equal(expected, tensor.ToString([3])); + + tensor = Tensor.Create([1, 2, 3, 4], lengths: [2, 2]); + expected = """ + System.Numerics.Tensors.ReadOnlyTensorSpan[2, 2] { + [1, ..], + [3, ..] + } + """; + Assert.Equal(expected, tensor.ToString([2, 1])); + + tensor = Tensor.Create(Enumerable.Range(1, 27).ToArray(), lengths: [3, 3, 3]); + expected = """ + System.Numerics.Tensors.ReadOnlyTensorSpan[3, 3, 3] { + [ + [1, 2, ..], + [4, 5, ..], + .. + ], + [ + [10, 11, ..], + [13, 14, ..], + .. + ], + .. + } + """; + Assert.Equal(expected, tensor.ToString([2, 2, 2])); + } + + [Fact] + public static void ToStringZeroDataTest() + { + ReadOnlyTensorSpan tensor = Tensor.Create([1, 2, 3, 4, 5], lengths: [5]); + string expected = """ + System.Numerics.Tensors.ReadOnlyTensorSpan[5] { + [..] + } + """; + Assert.Equal(expected, tensor.ToString([0])); + + tensor = Tensor.Create([1, 2, 3, 4], lengths: [2, 2]); + expected = """ + System.Numerics.Tensors.ReadOnlyTensorSpan[2, 2] { + [..], + [..] + } + """; + Assert.Equal(expected, tensor.ToString([2, 0])); + + tensor = Tensor.Create(Enumerable.Range(1, 27).ToArray(), lengths: [3, 3, 3]); + expected = """ + System.Numerics.Tensors.ReadOnlyTensorSpan[3, 3, 3] { + [ + .. + ], + [ + .. + ], + .. + } + """; + Assert.Equal(expected, tensor.ToString([2, 0, 2])); + } } } diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs index 0d6a5fea0d03e7..2e8e69c4b336a0 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs @@ -1944,5 +1944,138 @@ public static void TryGetSpanFailsForInvalidLengthsTest() Assert.False(tensorSpan.TryGetSpan([3, 3], 2, out span)); Assert.Equal(0, span.Length); } + + [Fact] + public static void ToStringTest() + { + TensorSpan tensor = Tensor.Create([1, 2, 3, 4, 5], lengths: [5]); + string expected = "System.Numerics.Tensors.TensorSpan[5]"; + Assert.Equal(expected, tensor.ToString()); + + tensor = Tensor.Create([1, 2, 3, 4], lengths: [2, 2]); + expected = "System.Numerics.Tensors.TensorSpan[2, 2]"; + Assert.Equal(expected, tensor.ToString()); + + tensor = Tensor.Create(Enumerable.Range(1, 27).ToArray(), lengths: [3, 3, 3]); + expected = "System.Numerics.Tensors.TensorSpan[3, 3, 3]"; + Assert.Equal(expected, tensor.ToString()); + } + + [Fact] + public static void ToStringAllDataTest() + { + TensorSpan tensor = Tensor.Create([1, 2, 3, 4, 5], lengths: [5]); + string expected = """ + System.Numerics.Tensors.TensorSpan[5] { + [1, 2, 3, 4, 5] + } + """; + Assert.Equal(expected, tensor.ToString([5])); + + tensor = Tensor.Create([1, 2, 3, 4], lengths: [2, 2]); + expected = """ + System.Numerics.Tensors.TensorSpan[2, 2] { + [1, 2], + [3, 4] + } + """; + Assert.Equal(expected, tensor.ToString([2, 2])); + + tensor = Tensor.Create(Enumerable.Range(1, 27).ToArray(), lengths: [3, 3, 3]); + expected = """ + System.Numerics.Tensors.TensorSpan[3, 3, 3] { + [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9] + ], + [ + [10, 11, 12], + [13, 14, 15], + [16, 17, 18] + ], + [ + [19, 20, 21], + [22, 23, 24], + [25, 26, 27] + ] + } + """; + Assert.Equal(expected, tensor.ToString([3, 3, 3])); + } + + [Fact] + public static void ToStringPartialDataTest() + { + TensorSpan tensor = Tensor.Create([1, 2, 3, 4, 5], lengths: [5]); + string expected = """ + System.Numerics.Tensors.TensorSpan[5] { + [1, 2, 3, ..] + } + """; + Assert.Equal(expected, tensor.ToString([3])); + + tensor = Tensor.Create([1, 2, 3, 4], lengths: [2, 2]); + expected = """ + System.Numerics.Tensors.TensorSpan[2, 2] { + [1, ..], + [3, ..] + } + """; + Assert.Equal(expected, tensor.ToString([2, 1])); + + tensor = Tensor.Create(Enumerable.Range(1, 27).ToArray(), lengths: [3, 3, 3]); + expected = """ + System.Numerics.Tensors.TensorSpan[3, 3, 3] { + [ + [1, 2, ..], + [4, 5, ..], + .. + ], + [ + [10, 11, ..], + [13, 14, ..], + .. + ], + .. + } + """; + Assert.Equal(expected, tensor.ToString([2, 2, 2])); + } + + [Fact] + public static void ToStringZeroDataTest() + { + TensorSpan tensor = Tensor.Create([1, 2, 3, 4, 5], lengths: [5]); + string expected = """ + System.Numerics.Tensors.TensorSpan[5] { + [..] + } + """; + Assert.Equal(expected, tensor.ToString([0])); + + tensor = Tensor.Create([1, 2, 3, 4], lengths: [2, 2]); + expected = """ + System.Numerics.Tensors.TensorSpan[2, 2] { + [..], + [..] + } + """; + Assert.Equal(expected, tensor.ToString([2, 0])); + + tensor = Tensor.Create(Enumerable.Range(1, 27).ToArray(), lengths: [3, 3, 3]); + expected = """ + System.Numerics.Tensors.TensorSpan[3, 3, 3] { + [ + .. + ], + [ + .. + ], + .. + } + """; + Assert.Equal(expected, tensor.ToString([2, 0, 2])); + } } } diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs index a9568abafac06e..22c86fbbeb695e 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs @@ -3056,5 +3056,138 @@ public static void TryGetSpanFailsForInvalidLengthsTest() Assert.False(tensorSpan.TryGetSpan([3, 3], 2, out span)); Assert.Equal(0, span.Length); } + + [Fact] + public static void ToStringTest() + { + Tensor tensor = Tensor.Create([1, 2, 3, 4, 5], lengths: [5]); + string expected = "System.Numerics.Tensors.Tensor[5]"; + Assert.Equal(expected, tensor.ToString()); + + tensor = Tensor.Create([1, 2, 3, 4], lengths: [2, 2]); + expected = "System.Numerics.Tensors.Tensor[2, 2]"; + Assert.Equal(expected, tensor.ToString()); + + tensor = Tensor.Create(Enumerable.Range(1, 27).ToArray(), lengths: [3, 3, 3]); + expected = "System.Numerics.Tensors.Tensor[3, 3, 3]"; + Assert.Equal(expected, tensor.ToString()); + } + + [Fact] + public static void ToStringAllDataTest() + { + Tensor tensor = Tensor.Create([1, 2, 3, 4, 5], lengths: [5]); + string expected = """ + System.Numerics.Tensors.Tensor[5] { + [1, 2, 3, 4, 5] + } + """; + Assert.Equal(expected, tensor.ToString([5])); + + tensor = Tensor.Create([1, 2, 3, 4], lengths: [2, 2]); + expected = """ + System.Numerics.Tensors.Tensor[2, 2] { + [1, 2], + [3, 4] + } + """; + Assert.Equal(expected, tensor.ToString([2, 2])); + + tensor = Tensor.Create(Enumerable.Range(1, 27).ToArray(), lengths: [3, 3, 3]); + expected = """ + System.Numerics.Tensors.Tensor[3, 3, 3] { + [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9] + ], + [ + [10, 11, 12], + [13, 14, 15], + [16, 17, 18] + ], + [ + [19, 20, 21], + [22, 23, 24], + [25, 26, 27] + ] + } + """; + Assert.Equal(expected, tensor.ToString([3, 3, 3])); + } + + [Fact] + public static void ToStringPartialDataTest() + { + Tensor tensor = Tensor.Create([1, 2, 3, 4, 5], lengths: [5]); + string expected = """ + System.Numerics.Tensors.Tensor[5] { + [1, 2, 3, ..] + } + """; + Assert.Equal(expected, tensor.ToString([3])); + + tensor = Tensor.Create([1, 2, 3, 4], lengths: [2, 2]); + expected = """ + System.Numerics.Tensors.Tensor[2, 2] { + [1, ..], + [3, ..] + } + """; + Assert.Equal(expected, tensor.ToString([2, 1])); + + tensor = Tensor.Create(Enumerable.Range(1, 27).ToArray(), lengths: [3, 3, 3]); + expected = """ + System.Numerics.Tensors.Tensor[3, 3, 3] { + [ + [1, 2, ..], + [4, 5, ..], + .. + ], + [ + [10, 11, ..], + [13, 14, ..], + .. + ], + .. + } + """; + Assert.Equal(expected, tensor.ToString([2, 2, 2])); + } + + [Fact] + public static void ToStringZeroDataTest() + { + Tensor tensor = Tensor.Create([1, 2, 3, 4, 5], lengths: [5]); + string expected = """ + System.Numerics.Tensors.Tensor[5] { + [..] + } + """; + Assert.Equal(expected, tensor.ToString([0])); + + tensor = Tensor.Create([1, 2, 3, 4], lengths: [2, 2]); + expected = """ + System.Numerics.Tensors.Tensor[2, 2] { + [..], + [..] + } + """; + Assert.Equal(expected, tensor.ToString([2, 0])); + + tensor = Tensor.Create(Enumerable.Range(1, 27).ToArray(), lengths: [3, 3, 3]); + expected = """ + System.Numerics.Tensors.Tensor[3, 3, 3] { + [ + .. + ], + [ + .. + ], + .. + } + """; + Assert.Equal(expected, tensor.ToString([2, 0, 2])); + } } }