<#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> <#@ output extension=".cs" #> <#@ include file="TensorTemplate.ttinclude" #>// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // This file is copied and adapted from the following git repository - // https://github.com/dotnet/corefx // Commit ID: bdd0814360d4c3a58860919f292a306242f27da1 // Path: /src/System.Numerics.Tensors/tests/TensorOperations.cs // Original license statement below - // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; namespace Microsoft.ML.OnnxRuntime.Tensors { public static partial class TensorOperations { internal static void ValidateBinaryArgs(Tensor left, Tensor right) { if (left.Rank != right.Rank || left.Length != right.Length) { throw new ArgumentException("Operands must have matching dimensions", nameof(right)); } if (left.Rank == 0) { throw new ArgumentException($"Cannot operate on Tensor with {nameof(Tensor.Rank)} of 0.", nameof(left)); } for (int i = 0; i < left.Rank; i++) { if (left.dimensions[i] != right.dimensions[i]) { throw new ArgumentException("Operands must have matching dimensions", nameof(right)); } } } internal static void ValidateBinaryArgs(Tensor left, Tensor right, Tensor result) { if (left.Rank != right.Rank || left.Length != right.Length) { throw new ArgumentException("Operands must have matching dimensions", nameof(right)); } if (left.Rank != result.Rank || left.Length != result.Length) { throw new ArgumentException("Operands must have matching dimensions", nameof(result)); } if (left.Rank == 0) { throw new ArgumentException($"Cannot operate on Tensor with {nameof(Tensor.Rank)} of 0.", nameof(left)); } for (int i = 0; i < result.Rank; i++) { if (left.dimensions[i] != right.dimensions[i]) { throw new ArgumentException("Operands must have matching dimensions", nameof(right)); } if (left.dimensions[i] != result.dimensions[i]) { throw new ArgumentException("Operands and result must have matching dimensions", nameof(result)); } } } internal static void ValidateBinaryArgs(Tensor left, Tensor right, Tensor result) { if (left.Rank != right.Rank || left.Length != right.Length) { throw new ArgumentException("Operands must have matching dimensions", nameof(right)); } if (left.Rank != result.Rank || left.Length != result.Length) { throw new ArgumentException("Operands must have matching dimensions", nameof(result)); } if (left.Rank == 0) { throw new ArgumentException($"Cannot operate on Tensor with {nameof(Tensor.Rank)} of 0.", nameof(left)); } for (int i = 0; i < result.Rank; i++) { if (left.dimensions[i] != right.dimensions[i]) { throw new ArgumentException("Operands must have matching dimensions", nameof(right)); } if (left.dimensions[i] != result.dimensions[i]) { throw new ArgumentException("Operands and result must have matching dimensions", nameof(result)); } } } internal static void ValidateArgs(Tensor tensor) { if (tensor.Rank == 0) { throw new ArgumentException($"Cannot operate on Tensor with {nameof(Tensor.Rank)} of 0.", nameof(tensor)); } } internal static void ValidateArgs(Tensor tensor, Tensor result) { if (tensor.Rank != result.Rank || tensor.Length != result.Length) { throw new ArgumentException("Operands and result must have matching dimensions", nameof(result)); } if (tensor.Rank == 0) { throw new ArgumentException($"Cannot operate on Tensor with {nameof(Tensor.Rank)} of 0.", nameof(tensor)); } for (int i = 0; i < result.Rank; i++) { if (tensor.dimensions[i] != result.dimensions[i]) { throw new ArgumentException("Operands and result must have matching dimensions", nameof(result)); } } } internal static int[] ValidateContractArgs(Tensor left, Tensor right, int[] leftAxes, int[] rightAxes) { if (leftAxes == null) { throw new ArgumentNullException(nameof(left)); } if (rightAxes == null) { throw new ArgumentNullException(nameof(left)); } if (leftAxes.Length != rightAxes.Length) { throw new ArgumentException($"{nameof(leftAxes)} and {nameof(rightAxes)} must have the same length, but were {leftAxes.Length} and {rightAxes.Length}, respectively."); } for (int i = 0; i < leftAxes.Length; i++) { var leftAxis = leftAxes[i]; if (leftAxis >= left.Rank) { throw new ArgumentOutOfRangeException($"{nameof(leftAxes)}[{i}] was set to axis index {leftAxis} which exceeds the Rank of {left}."); } var leftDimension = left.dimensions[leftAxis]; var rightAxis = rightAxes[i]; if (rightAxis >= right.Rank) { throw new ArgumentOutOfRangeException($"{nameof(rightAxes)}[{i}] was set to axis index {rightAxis} which exceeds the Rank of {right}."); } var rightDimension = right.dimensions[rightAxis]; if (leftDimension != rightDimension) { throw new ArgumentOutOfRangeException($"Tensors may only be contracted on axes of the same length, but {nameof(leftAxes)} index {i} was length {leftDimension} and {nameof(rightAxes)} index {i} was length {rightDimension}."); } } var leftNonSummingDimensions = left.Rank - leftAxes.Length; var rightNonSummingDimensions = right.Rank - rightAxes.Length; var resultDimensions = new int[leftNonSummingDimensions + rightNonSummingDimensions]; int dimensionsIndex = 0; Action, int[]> fillDimensions = (tensor, axes) => { for (int i = 0; i < tensor.Rank; i++) { var skip = false; foreach (var contractionIndex in axes) { if (contractionIndex == i) { skip = true; break; } } if (!skip) { resultDimensions[dimensionsIndex++] = tensor.dimensions[i]; } } }; fillDimensions(left, leftAxes); fillDimensions(right, rightAxes); return resultDimensions; } internal static int[] ValidateContractArgs(Tensor left, Tensor right, int[] leftAxes, int[] rightAxes, Tensor result) { var expectedDimensions = ValidateContractArgs(left, right, leftAxes, rightAxes); if (result.Rank != expectedDimensions.Length) { throw new ArgumentException($"{nameof(result)} should have {expectedDimensions.Length} dimensions but had {result.Rank}."); } for (int i = 0; i < expectedDimensions.Length; i++) { if (result.dimensions[i] != expectedDimensions[i]) { throw new ArgumentException($"{nameof(result)} dimension {i} should be {expectedDimensions[i]} but was {result.dimensions[i]}."); } } return expectedDimensions; } <# foreach (MethodConfiguration method in methodConfiguration) { #> internal static <#= method.GetGenericResultMethodSignature("Tensor", "T")#> { <#= method.GetValidationMethod(true) #> TensorArithmetic.Instance.<#=method.MethodName#>(<#=method.GetCallArguments()#>, <#= method.ResultName #>); } internal static <#= method.GetGenericMethodSignature("Tensor", "T")#> { <#= method.GetValidationMethod(false) #> var <#= method.ResultName #> = <#=method.InitializeResult("T")#>; TensorArithmetic.Instance.<#=method.MethodName#>(<#=method.GetCallArguments()#>, <#= method.ResultName #>); return <#= method.ResultName #>; } <# } #> } }