formas de tensor, armazenamento e diferenças no NumPy

O pacote System.Numerics.Tensors fornece Tensor<T>, TensorSpan<T>, e ReadOnlyTensorSpan<T> para dados multidimensionais. As formas dos tensores descrevem as dimensões lógicas; os strides descrevem a distância, em elementos, entre posições sucessivas ao longo de cada dimensão.

O comportamento descrito aqui é fornecido no pacote para as frameworks-alvo suportadas. Atualizar o pacote pode afetar aplicações que visam versões anteriores de .NET.

Estes tipos partilham muitas convenções com o NumPy, mas não são intercambiáveis. Tenha em conta as seguintes diferenças ao portar um algoritmo.

Formas vazias e resultados escalares

Quando se cria um tensor a partir de dimensões vazias ([]) sem dados subjacentes, o tensor tem dimensões [0] e nenhum elemento. No NumPy, a forma () tem ordem zero e contém um elemento escalar. Em particular, np.empty(()) aloca um escalar com um valor não inicializado; "vazio" não significa que não tenha elementos.

Em contraste, quando se omitem comprimentos de um construtor tensor-span sobre dados existentes, o construtor infere uma forma unidimensional a partir do array ou span fornecido.

Quando se comprime um tensor com forma [1, 1], o resultado tem forma [1], e não a forma ()de rank-zero do NumPy . Ambos os resultados contêm um elemento e podem ser difundidos como um escalar, mas o código que examina o rank, seleciona eixos ou indexa o resultado deve ter em conta a dimensão retida.

Quando as strides são omitidas, qualquer forma que contenha uma dimensão de comprimento zero tem strides zero em todas as dimensões. O número de elementos e o armazenamento necessário são zero, independentemente dos outros comprimentos dimensionais. Os comprimentos negativos continuam inválidos. Os strides explícitos devem ainda assim satisfazer a validação do layout.

Strides e dados invertidos

Os passos devem ser não negativos. O NumPy pode representar uma visão invertida com um passo negativo, por exemplo array[::-1].

Para inverter o primeiro eixo, chame Tensor.ReverseDimension com dimensão 0:

using System.Numerics.Tensors;

Tensor<int> source = Tensor.Create(new[] { 1, 2, 3, 4 }, new nint[] { 2, 2 });
Tensor<int> reversed = Tensor.ReverseDimension<int>(source.AsReadOnlyTensorSpan(), 0);

Os elementos lógicos de reversed são [3, 4, 1, 2]. A sobrecarga de alocação produz um novo tensor; não é uma vista com stride negativo sobre o storage original.

Redimensione um tensor

Tensor.Resize e Tensor.ResizeTo preenchem elementos lógicos adicionados com default(T). Por exemplo, se aumentar [1, 2] para cinco elementos int, o resultado contém [1, 2, 0, 0, 0]. O NumPy, em vez disso, np.resize repete a entrada e produz [1, 2, 1, 2, 1].

Se o destino for menor, a operação descarta elementos após a contagem lógica de elementos do destino. Se for maior, a operação inicializa os elementos lógicos de destino restantes mesmo quando o destino tem intervalos entre os elementos. O armazenamento fora do destino lógico não faz parte da operação de redimensionamento.

Tensor.ResizeTo e a concatenação num destino existente rejeitam uma dimensão de passo zero que contenha mais do que um elemento lógico. Tal dimensão faz com que múltiplas posições de saída se refiram ao mesmo armazenamento e este não possa conter valores de saída distintos. Passos zero em dimensões singleton e destinos vazios não criam este conflito.

Fontes e destinos sobrepostos

Objetos tensoriais separados e intervalos podem referir-se a regiões sobrepostas do mesmo array. Os casos suportados dependem tanto da operação como do layout:

Operação Sobreposição suportada
CopyTo e TryCopyTo Fonte e destino compactos com números iguais de elementos, ou vistas exatamente idênticas. Cópias densas utilizam cópia de bloco segura para sobreposição.
FlattenTo e TryFlattenTo Uma origem densa e o span de destino.
Operações elemento a elemento com destino Uma entrada e uma saída com a mesma referência inicial, comprimentos e passadas, desde que a visualização não tenha posições lógicas de armazenamento repetidas. Cada entrada é verificada de forma independente.
Tensor.ResizeTo Origem e destino compactos. A sobreposição com stride não é suportada e é rejeitada.
Reversão na mesma vista Uma vista densa pode ser invertida in situ. Também são suportadas reversões no-op.

Outros layouts sobrepostos não suportados geram ArgumentException antes da operação escrever a saída. Um tensor denso sozinho não garante suporte para todas as operações: por exemplo, um destino denso deslocado é suportado para uma cópia, mas não para uma operação elemento a elemento.

Os métodos TryCopyTo e TryFlattenTo retornam false sem copiar quando o destino é demasiado curto. TryCopyTo também retorna false para formas de origem e destino incompatíveis. Para formas compatíveis com capacidade de destino suficiente, estes métodos ainda podem lançar exceção em caso de sobreposição não suportada; Try não suprime exceções de layout inválido.

Por exemplo, a seguinte origem strided e o destino denso sobrepõem-se:

int[] storage = { 1, 2, 3, 4, 5, 6 };
ReadOnlyTensorSpan<int> source = new(storage, new nint[] { 2, 2 }, new nint[] { 3, 1 });
TensorSpan<int> destination = new(storage, new nint[] { 2, 2 });

Para copiar deste layout sobreposto não suportado, primeiro faça um snapshot para armazenamento independente:

int[] snapshot = new int[checked((int)source.FlattenedLength)];
source.FlattenTo(snapshot);
ReadOnlyTensorSpan<int> independentSource = new(snapshot, source.Lengths);
independentSource.CopyTo(destination);

O snapshot contém [1, 2, 4, 5]. A operação de cópia escreve esses elementos no destino sem alterar os valores originais antes da sua leitura.

Para uma operação com destino elemento a elemento, aloque um tensor de resultado separado ou tire um snapshot de cada entrada sobreposta antes de escrever a saída. Para um destino de broadcast com localizações de armazenamento repetidas, use armazenamento de saída independente; um snapshot de entrada não torna esse destino capaz de armazenar valores distintos.

A biblioteca não atribui implicitamente um instantâneo de tensor completo para suportar layouts arbitrários sobrepostos. Cópias strided não sobrepostas não requerem esse armazenamento temporário.

Ver também