Specyfikacja StableHLO

StableHLO to operacja ustawiona dla operacji wysokiego poziomu (HLO) w maszynie (ML). StableHLO działa jako warstwa przenośna między Platformy ML i kompilatory ML: platformy ML służące do tworzenia programów StableHLO są zgodne z kompilatorami ML, które wykorzystują programy StableHLO.

Naszym celem jest uproszczenie i przyspieszenie rozwoju ML przez opracowanie współdziałania różnych platform ML (takich jak TensorFlow, JAX PyTorch) i kompilatorów ML (np. XLA i IREE). W tym celu dokument zawiera specyfikację języka programowania StableHLO.

Specyfikacja składa się z 3 głównych sekcji. Po pierwsze, Sekcja Programy opisuje strukturę programów StableHLO. składające się z funkcji StableHLO, które składają się z operacji StableHLO. W tej strukturze sekcja Operacje określa semantykę poszczególnych operacji. Sekcja Wykonanie zawiera semantykę dla wszystkich i wykonują te operacje w programie. Pamiętaj też, że W sekcji Notation (Zapis) omawiamy sposób zapisu używany w specyfikacji.

Aby wyświetlić specyfikację z poprzedniej wersji StableHLO, otwórz repozytorium w otagowane udostępnienie. Na przykład specyfikacja produktu StableHLO w wersji 0.19.0. Aby wyświetlić zmiany, które wystąpiły przy każdym dodatkowym poziomie wersji StableHLO, zapoznaj się z artykułem log wersji w VhloDialect.td.

Programy

Program ::= {Func}

Programy StableHLO składają się z dowolnej liczby funkcji StableHLO. Poniżej znajdziesz przykładowy program z funkcją @main, która ma 3 dane wejściowe (%image, %weights i %bias) oraz 1 wynik. Treść funkcji ma 6 operacji.

func.func @main(
  %image: tensor<28x28xf32>,
  %weights: tensor<784x10xf32>,
  %bias: tensor<1x10xf32>
) -> tensor<1x10xf32> {
  %0 = "stablehlo.reshape"(%image) : (tensor<28x28xf32>) -> tensor<1x784xf32>
  %1 = "stablehlo.dot"(%0, %weights) : (tensor<1x784xf32>, tensor<784x10xf32>) -> tensor<1x10xf32>
  %2 = "stablehlo.add"(%1, %bias) : (tensor<1x10xf32>, tensor<1x10xf32>) -> tensor<1x10xf32>
  %3 = "stablehlo.constant"() {value = dense<0.0> : tensor<1x10xf32>} : () -> tensor<1x10xf32>
  %4 = "stablehlo.maximum"(%2, %3) : (tensor<1x10xf32>, tensor<1x10xf32>) -> tensor<1x10xf32>
  "func.return"(%4): (tensor<1x10xf32>) -> ()
}

Funkcje

Func        ::= 'func' '.' 'func' FuncId FuncInputs FuncOutputs '{' FuncBody '}'
FuncInputs  ::= '(' [FuncInput {',' FuncInput}] `)`
FuncInput   ::= ValueId ':' ValueType
FuncOutputs ::= ['->' FuncOutput, {',' FuncOutput}]
FuncOutput  ::= ValueType
FuncBody    ::= {Op}

Funkcje stabilneHLO (nazywane też funkcjami nazwanymi) mają identyfikatora, danych wejściowych/wyjściowych i treści. W przyszłości planujemy wprowadzenie dodatkowych metadanych funkcji, aby uzyskać lepszą zgodność z HLO (#425, #626, #740, 744).

Identyfikatory

FuncId  ::= '@' letter {letter | digit}
ValueId ::= '%' digit {digit}
          | '%' letter {letter | digit}
letter  ::= 'a' | ... | 'z' | 'A' | ... | 'Z' | '_'
digit   ::= '0' | ... | '9'

Identyfikatory StableHLO są podobne do identyfikatorów w wielu programach mają dwie cechy: 1) wszystkie identyfikatory mają sigile, rozróżniają różne typy identyfikatorów, 2) identyfikatory wartości mogą być całkowicie numeryczne, aby uprościć generowanie programów StableHLO.

Typy

Type         ::= ValueType | NonValueType
ValueType    ::= TensorType | QuantizedTensorType | TokenType | TupleType
NonValueType ::= TensorElementType | QuantizedTensorElementType | FunctionType | StringType

Typy StableHLO są podzielone na typy wartości (nazywane też typy pierwszej klasy), które reprezentują wartości StableHLO i typy niewartościowe; które opisują inne elementy programu. Typy StableHLO są podobne do typów w w wielu językach programowania, z których charakterystyczną jest specyficzny dla domeny, co skutkuje nietypowymi wynikami (np.typów skalarnych). nie są typami wartości).

TensorType ::= 'tensor' '<' Shape TensorElementType '>'
Shape ::= {DimensionSize 'x'}
DimensionSize ::= digit {digit} | '?'

Typy Tensor reprezentują tensory, czyli tablice wielowymiarowe. Mają kształt oraz typ elementu, gdzie kształt jest liczbą nieujemną lub nieznanych rozmiarów w kolejności rosnącej do odpowiednich wymiary (nazywane również osiami) numerowane od 0 do R-1. liczba wymiarów R jest nazywana rankingiem. Przykład: tensor<2x3xf32> to typ tensora o kształcie 2x3 i typie elementu f32. Ma 2 wymiary (lub, innymi słowy, dwie osie) – wymiar 0 i pierwszy wymiar, których rozmiary to 2 i 3. Jego pozycja to 2.

Kształty mogą być częściowo lub całkowicie nieznane (dynamiczne), np. tensor<?x2xf64> jest częściowo nieznana, a tensor<?x?xf64> jest całkowicie nieznana. Dynamiczna, rozmiary są podawane za pomocą ?. Kształtów nie można uszeregować w kolejności.

Planujemy w przyszłości rozszerzyć zakres typów tensorów rozmiarów i typów elementów, np. aby uwzględnić układy (#629) i rzadkie (#1078).

QuantizedTensorType ::= 'tensor' '<' Shape QuantizedTensorElementType '>'
QuantizedTensorElementType ::= '!quant.uniform' '<'
                  QuantizationStorageType
                  ['<' QuantizationStorageMin ':' QuantizationStorageMax '>']
                  ':' QuantizationExpressedType
                  [':' QuantizationDimension]
                  ',' QuantizationParameters '>'
QuantizationStorageType ::= IntegerType
QuantizationStorageMin ::= IntegerConstant
QuantizationStorageMax ::= IntegerConstant
QuantizationExpressedType ::= FloatType
QuantizationDimension ::= IntegerConstant
QuantizationParameters ::= QuantizationParameter
                         | '{' QuantizationParameter {',' QuantizationParameter} '}'
QuantizationParameter ::= QuantizationScale ':' QuantizationZeroPoint
QuantizationScale ::= FloatConstant
QuantizationZeroPoint ::= IntegerConstant
Nazwa Typ Ograniczenia
storage_type typ liczby całkowitej (C1–C3), (C8)
storage_min stała całkowita (C1), (C3), (C7)
storage_max stała całkowita (C2), (C3), (C7)
expressed_type typ zmiennoprzecinkowy (K4)
quantization_dimension opcjonalna stała liczba całkowita (C10–C12)
scales liczba zmiennoprzecinkowa stałych zmiennoprzecinkowych (C4–C6), (C9), (C10), (C13)
zero_points liczba zmiennoprzecinkowa stałych liczb całkowitych (C7–C9)

Typy elementów kwantowych reprezentują wartości całkowite typu pamięci masowej w zakres od storage_min do storage_max (włącznie), który odpowiada wartości zmiennoprzecinkowych wyrażenia typu. Dla danej liczby całkowitej: i, odpowiednia wartość zmiennoprzecinkowa f może zostać obliczona jako f = (i - zero_point) * scale, gdzie nazwy scale i zero_point są nazywane parametry kwantyzacji. storage_min i storage_max są opcjonalne w gramatyce, ale mają wartości domyślne min_value(storage_type) oraz max_value(storage_type). Typy elementów poddanych kwantyzacji mają te ograniczenia:

  • (C1) type(storage_min) = storage_type.
  • (C2) type(storage_max) = storage_type.
  • (C3) min_value(storage_type) <= storage_min < storage_max <= max_value(storage_type).
  • (C4) type(scales...) = expressed_type.
  • (C5) 0 < scales.
  • (C6) is_finite(scales...).
  • (C7) storage_min <= zero_points <= storage_max.
  • (C8) type(zero_points...) = storage_type.
  • (C9) size(scales) = size(zero_points).
  • (C10) Jeśli is_empty(quantization_dimension), to size(scales) = 1.
  • (C11) 0 <= quantization_dimension.

Obecnie QuantizationScale jest stałą zmiennoprzecinkową, ale jest duże zainteresowanie skalami opartymi na liczbach całkowitych, reprezentowanych za pomocą mnożników i zmiany. W najbliższej przyszłości planujemy to omówić. (nr 1404).

Trwa dyskusja na temat semantyki elementu QuantizationZeroPoint. typu, wartości i tego, czy może być tylko jeden potencjalnie wiele punktów zerowych w kwantowym typie tensora. Na podstawie tej dyskusji, specyfikacja dotycząca zerowej liczby punktów może ulec zmianie w przyszłości (#1405).

Kolejna trwająca dyskusja dotyczy semantyki kategorii QuantizationStorageMin i QuantizationStorageMax, aby określić, czy jakieś ograniczenia powinny być nałożone na te wartości i wartości tensorów kwantowych (#1406).

Na koniec planujemy zbadać reprezentowanie nieznanych skal i zera podobnie jak chcemy badać reprezentowanie nieznanych rozmiarów (#1407).

Kwantowe typy tensorów reprezentują tensory z elementami kwantyzowanymi. Te tensory są dokładnie takie same jak tensory regularne, z tym że ich elementy mają kwantyzowane typy elementów, a nie zwykłe.

W tensorach kwantyzowanych kwantyzacja może być pertensora, co oznacza, że jeden scale i zero_point dla całego tensora lub mogą być na oś, oznacza, że mamy wiele scales i zero_points, po jednej parze na wycinek danego wymiaru: quantization_dimension. Bardziej oficjalnie, w tensorze t z kwantyzacją na oś, powstaje dim(t, quantization_dimension) wycinków z quantization_dimension: t[:, ..., 0, ..., :], t[:, ..., 1, ..., :], itd. Wszystkie elementy w i. wycinku używają scales[i] i zero_points[i] jako ich parametry kwantyzacji. Kwantyzowane typy tensorów mają następujące właściwości ograniczenia:

  • W przypadku kwantyzacji na centrów:
    • Brak dodatkowych ograniczeń.
  • Do kwantyzacji na oś:
    • (C12) quantization_dimension < rank(self).
    • (C13) dim(self, quantization_dimension) = size(scales).
TokenType ::= 'token'

Typy tokenów reprezentują tokeny, tj.tworzone i wykorzystywane wartości nieprzejrzyste. niektóre operacje. Tokeny służą do narzucania kolejności wykonywania operacji zgodnie z opisem w sekcji Wykonanie.

TupleType ::= 'tuple' '<' TupleElementTypes '>'
TupleElementTypes ::= [ValueType {',' ValueType}]

Typy krotek reprezentują krotki, czyli listy heterogeniczne. Kropki to starsza wersja funkcję, która istnieje tylko na potrzeby zgodności z HLO. W HLO krotki to: używane do reprezentowania zmiennych danych wejściowych i wyjściowych. W StableHLO zmienne wejściowe i dane wyjściowe są obsługiwane natywnie, a jedynym wykorzystaniem krotek w StableHLO jest kompleksowo odzwierciedla wartość HLO ABI, gdzie np. T, tuple<T> i Wartość tuple<tuple<T>> może się znacznie różnić w zależności od implementacji. Planujemy w przyszłości wprowadzić zmiany w interfejsie HLO ABI. co może umożliwić nam usunięcie typów krotek ze StableHLO (#598).

TensorElementType ::= BooleanType | IntegerType | FloatType | ComplexType
BooleanType ::= 'i1'
IntegerType ::= SignedIntegerType | UnsignedIntegerType
SignedIntegerType ::= 'si2' | 'si4' | 'si8' | 'si16' | 'si32' | 'si64'
UnsignedIntegerType ::= 'ui2' | 'ui4' | 'ui8' | 'ui16' | 'ui32' | 'ui64'
FloatType ::= 'f8E4M3FN' | 'f8E5M2' | 'f8E4M3FNUZ' | 'f8E5M2FNUZ'
            | 'f8E4M3B11FNUZ' | 'bf16' | 'f16' | 'f32' | 'f64'
TensorFloat32 ::= 'tf32'
ComplexType ::= 'complex' '<' ComplexElementType '>'
ComplexElementType ::= 'f32' | 'f64'

Typy elementów reprezentują elementy typów tensorów. Inaczej niż w przypadku wielu programów W standardzie StableHLO te typy nie są najwyższej klasy. Oznacza to, że Programy StableHLO nie mogą bezpośrednio reprezentować wartości tego typu (w efekcie jest idiomatyczne reprezentuje wartości skalarne typu T za pomocą tensora 0-wymiarowego wartości typu tensor<T>).

  • Typ wartości logicznej reprezentuje wartości logiczne true i false.
  • Liczba całkowita może być typu ze znakiem (si) lub bez znaku (ui) i zawierać jedną z obsługiwanych szerokości bitów (2, 4, 8, 16, 32 lub 64). Typy siN z podpisem reprezentują wartości całkowite od -2^(N-1) do 2^(N-1)-1 Typy obejmujące i bez znaku uiN reprezentują wartości całkowite od 0 do 2^N-1 włącznie.
  • Oto kilka typów liczb zmiennoprzecinkowych:
  • Typy złożone reprezentują złożone wartości, które mają część rzeczywistą. i część urojona tego samego typu elementu. Obsługiwany kompleks typy to complex<f32> (obie części są typu f32) oraz complex<f64> (obie części są typu f64).
FunctionType ::= '(' InputTypes ')' '->' '(' OutputTypes ')'
InputTypes ::= [ValueType {',' ValueType}]
OutputTypes ::= [ValueType {',' ValueType}]

Typy funkcji reprezentują zarówno funkcje nazwane, jak i anonimowe. Mają typy danych wejściowych (lista typów po lewej stronie panelu ->) i typy danych wyjściowych (lista typów po prawej stronie ->). W wielu programach w językach, typy funkcji są pierwszej klasy, ale nie w StableHLO.

StringType ::= 'string'

Typ ciągu reprezentuje sekwencje bajtów. Inaczej niż w przypadku wielu programów typ ciągu znaków nie jest pierwszą klasą w StableHLO i jest używany określać statyczne metadane elementów programu.

Operacje

Operacje stabilne HLO (nazywane też operacjami) reprezentują zbiór zamknięty operacji wysokiego poziomu w modelach systemów uczących się. Jak omówiliśmy powyżej, Składnia StableHLO jest mocno inspirowana modelem MLIR, który nie musi jest ergonomiczną alternatywą, ale zapewne najlepiej pasuje do celu StableHLO zapewniając większą interoperacyjność między platformami ML i kompilatorami ML.

Op            ::= [OpOutputs] OpName OpInputs ':' OpSignature
OpName        ::= '"' 'stablehlo' '.' OpMnemonic '"'
OpMnemonic    ::= 'abs' | 'add' | ...

Operacje stabilne HLO (nazywane też operacjami) mają nazwę, danych wejściowych i wyjściowych oraz podpis. Nazwa składa się z prefiksu stablehlo. i mnemotechnika, która jednoznacznie identyfikuje jedno z obsługiwanych działań. Zobacz poniżej pełną listę obsługiwanych operacji.

OpInputs        ::= OpInputValues OpInputFuncs OpInputAttrs
OpInputValues   ::= '(' [OpInputValue {',' OpInputValue}] ')'
OpInputValue    ::= ValueId
OpInputFuncs    ::= ['(' OpInputFunc {',' OpInputFunc} ')']
OpInputAttrs    ::= ['{' OpInputAttr {',' OpInputAttr} '}']
OpOutputs       ::= [OpOutput {',' OpOutput} '=']
OpOutput        ::= ValueId

Operacje wykorzystują dane wejściowe i generują dane wyjściowe. Dane wejściowe są klasyfikowane jako wartości wejściowe (obliczane podczas wykonywania), funkcje wejściowe (podawane statycznie, ponieważ w StableHLO funkcje nie są wartościami pierwszej klasy) oraz atrybutów wejściowych (również statycznych). rodzaj danych wejściowych i wyjściowych, zależy od mnemotechniki, ale też od jej mnemotechniki. Na przykład add funkcja wykorzystuje 2 wartości wejściowe i generuje 1 wartość wyjściową. Dla porównania Operacje select_and_scatter używają 3 wartości wejściowych, 2 funkcji wejściowych i 3 atrybuty wejściowe.

OpInputFunc ::= '{' Unused FuncInputs ':' FuncBody '}'
Unused      ::= '^' digit {digit}
              | '^' letter {letter | digit}

Funkcje wejściowe (nazywane też funkcjami anonimowymi) są bardzo są podobne do funkcji nazwanych, z tą różnicą, że: 1) nie mają identyfikatora (więc nazwa „anonimowy”), 2) nie deklarują typów danych wyjściowych (typy danych wyjściowych to wywnioskowane z operacji return w funkcji).

Składnia funkcji wejściowych zawiera obecnie nieużywaną część (zobacz Unused powyżej), który zapewnia zgodność z MLIR. W kontekście MLIR istnieje bardziej ogólne pojęcie „regionów”. które mogą zawierać wiele „bloków” operacji połączonych za pomocą skoków. Te bloki mają identyfikatory, które odpowiadają do środowiska produkcyjnego Unused, aby można je było odróżnić. W StableHLO nie są wykonywane operacje przeskakiwania, więc odpowiadająca mu część składni MLIR to nieużywane (ale nadal jest dostępne).

OpInputAttr      ::= OpInputAttrName '=' OpInputAttrValue
OpInputAttrName  ::= letter {letter | digit}
OpInputAttrValue ::= Constant

Atrybuty wejściowe mają nazwę i wartość, która jest jedną z obsługiwanych stałe. Stanowią podstawowy sposób określania statycznych metadanych programu . Na przykład operacja concatenate używa atrybutu dimension do pozwala określić wymiar, z którym są połączone jego wartości wejściowe. Podobnie operacja slice wykorzystuje wiele atrybutów, takich jak start_indices i limit_indices aby określić granice używane do wycinania wartości wejściowej.

Obecnie programy StableHLO działające na wolności czasami zawierają atrybuty które nie zostały opisane w tym dokumencie. W przyszłości planujemy wchłania te atrybuty do Opsetu StableHLO lub zabrania ich które pojawiają się w programach StableHLO. W międzyczasie możesz zapoznać się z listą tych kategorii atrybuty:

  • layout (#629).
  • mhlo.frontend_attributes (#628).
  • mhlo.sharding (#619).
  • output_operand_aliases (#740).
  • Metadane lokalizacji (#594).
OpSignature ::= '(' [ValueType {',' ValueType}] ')' '->' '(' [ValueType {',' ValueType}] ')'

Podpis operacji składa się z typów wszystkich wartości wejściowych (listy typów w po lewej stronie elementu ->) oraz typy wszystkich wartości wyjściowych (lista po prawej stronie ->). Ogólnie typy danych wejściowych są nadmiarowe, a typy danych wyjściowych niemal zawsze są nadmiarowe (ponieważ w przypadku w przypadku większości operacji StableHLO typy danych wyjściowych można wywnioskować na podstawie danych wejściowych). Mimo to jest celowo częścią składni StableHLO, która zapewnia zgodność z MLIR.

Poniżej znajdziesz przykład operacji, której mnemotechnika to select_and_scatter. zużywa 3 elementy, wartości wejściowe (%operand, %source i %init_value), 2 funkcje wejściowe i 3 atrybuty wejściowe (window_dimensions, window_strides i padding). Zwróć uwagę, że podpis operacji obejmuje tylko typy wartości wejściowych (ale nie o typach funkcji i atrybutów wejściowych dostępnych w tekście).

%result = "stablehlo.select_and_scatter"(%operand, %source, %init_value) ({
  ^bb0(%arg0: tensor<i32>, %arg1: tensor<i32>):
    %0 = "stablehlo.compare"(%arg0, %arg1) {
      comparison_direction = #stablehlo<comparison_direction GE>
    } : (tensor<i32>, tensor<i32>) -> tensor<i1>
    "stablehlo.return"(%0) : (tensor<i1>) -> ()
}, {
  ^bb0(%arg0: tensor<i32>, %arg1: tensor<i32>):
    %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i32>, tensor<i32>) -> tensor<i32>
    "stablehlo.return"(%0) : (tensor<i32>) -> ()
}) {
  window_dimensions = dense<[3, 1]> : tensor<2xi64>,
  window_strides = dense<[2, 1]> : tensor<2xi64>,
  padding = dense<[[0, 1], [0, 0]]> : tensor<2x2xi64>
} : (tensor<4x2xi32>, tensor<2x2xi32>, tensor<i32>) -> tensor<4x2xi32>

Stałe

Constant ::= BooleanConstant
           | IntegerConstant
           | FloatConstant
           | ComplexConstant
           | TensorConstant
           | QuantizedTensorConstant
           | StringConstant
           | EnumConstant

stałe StabilHLO mają literał i typ, które razem reprezentują wartość StableHLO. Zasadniczo typ jest częścią składni stałej. Wyjątkiem gdy jest jednoznaczne (np. stała logiczna jednoznacznie ma typ i1, a stała całkowita może mieć kilka typów).

BooleanConstant ::= BooleanLiteral
BooleanLiteral  ::= 'true' | 'false'

Stałe wartości logiczne reprezentują wartości logiczne true i false. Wartość logiczna stałe mają typ i1.

IntegerConstant   ::= IntegerLiteral ':' IntegerType
IntegerLiteral    ::= ['-' | '+'] DecimalDigits
                    | ['-' | '+'] '0x' HexadecimalDigits
DecimalDigits     ::= decimalDigit {decimalDigit}
HexadecimalDigits ::= hexadecimalDigit {hexadecimalDigit}
decimalDigit      ::= '0' | ... | '9'
hexadecimalDigit  ::= decimalDigit | 'a' | ... | 'f' | 'A' | ... | 'F'

Stałe liczby całkowite reprezentują wartości całkowite za pomocą ciągów znaków z ułamkami dziesiętnymi lub w notacji szesnastkowej. Inne podstawy, np. binarnych lub ósemkowych, nie są obsługiwane. Stałe liczby całkowite mają następujące ograniczenia:

  • (C1) is_wellformed(integer_literal, integer_type).
FloatConstant  ::= FloatLiteral ':' FloatType
FloatLiteral   ::= SignPart IntegerPart FractionalPart ScientificPart
                 | '0x' [HexadecimalDigits]
SignPart       ::= ['-' | '+']
IntegerPart    ::= DecimalDigits
FractionalPart ::= ['.' [DecimalDigits]]
ScientificPart ::= [('e' | 'E') ['-' | '+'] DecimalDigits]

Stałe zmiennoprzecinkowe odzwierciedlają wartości zmiennoprzecinkowe za pomocą ciągów, które użyj zapisu dziesiętnego lub naukowego. Dodatkowo przy użyciu notacji szesnastkowej można służy do bezpośredniego określania bazowych bitów w formacie zmiennoprzecinkowym odpowiadający jej typ. Stałe zmiennoprzecinkowe mają te ograniczenia:

  • (C1) Jeśli używany jest zapis w notacji nieszesnastkowej, is_wellformed(float_literal, float_type)
  • (C2) Jeśli używany jest zapis szesnastkowy, size(hexadecimal_digits) = num_bits(float_type) / 4
ComplexConstant ::= ComplexLiteral ':' ComplexType
ComplexLiteral  ::= '(' RealPart ',' ImaginaryPart ')'
RealPart        ::= FloatLiteral
ImaginaryPart   ::= FloatLiteral

Stałe złożone przedstawiają wartości złożone za pomocą list części rzeczywistych (pierwsza jest część) i część urojona (druga). Przykład: (1.0, 0.0) : complex<f32> oznacza 1.0 + 0.0i, a (0.0, 1.0) : complex<f32> oznacza: 0.0 + 1.0i. Kolejność, są zapisywane w pamięci. Stałe złożone mają te ograniczenia:

  • (C1) is_wellformed(real_part, complex_element_type(complex_type)).
  • (C2) is_wellformed(imaginary_part, complex_element_type(complex_type)).
TensorConstant ::= TensorLiteral ':' TensorType
TensorLiteral  ::= 'dense' '<' (DenseLiteral | ElementLiteral) '>'
DenseLiteral   ::= DenseDimension | DenseElements
DenseDimension ::= '[' [DenseLiteral {',' DenseLiteral}] ']'
DenseElements  ::= [ElementLiteral {',' ElementLiteral}]
ElementLiteral ::= BooleanLiteral | IntegerLiteral | FloatLiteral | ComplexLiteral

Stałe Tensor przedstawiają wartości tensorów za pomocą zagnieżdżonych list określonych za pomocą funkcji Zapis NumPy. Na przykład: dense<[[1, 2, 3], [4, 5, 6]]> : tensor<2x3xi32> reprezentuje wartość tensora z następującym mapowaniem indeksów na elementy: {0, 0} => 1, {0, 1} => 2, {0, 2} => 3, {1, 0} => 4, {1, 1} => 5, {1, 2} => 6 Kolejność, w jakiej te elementy są zapisywane w pamięci, to jest bardzo skomplikowana. Stałe tensora mają następujące ograniczenia:

  • (C1) has_syntax(tensor_literal, element_type(tensor_type)), gdzie:
    • has_syntax(element_literal: Syntax, element_type: Type) = is_wellformed(element_literal, type).
    • has_syntax(tensor_literal: List, element_type: Type) = has_syntax(tensor_literal..., element_type).
  • (C2) has_shape(tensor_literal, shape(tensor_type)), gdzie:
    • has_shape(element_literal: Syntax, []) = true.
    • has_shape(tensor_literal: List, shape: List) = size(tensor_literal) = shape[0] and has_shape(tensor_literal..., shape[1:]).
    • w przeciwnym razie: false.
QuantizedTensorConstant ::= QuantizedTensorLiteral ':' QuantizedTensorType
QuantizedTensorLiteral  ::= 'dense' '<' (DenseLiteral | ElementLiteral) '>'

Kwantowe stałe tensora reprezentują skwantyzowane wartości tensora z wykorzystaniem tego samego w postaci stałych tensorów, z elementami określonymi jako stałe ich wartości typu pamięci masowej. Poddane kwantyzacji stałe tensora mają następujące ograniczenia:

  • (C1) has_syntax(quantized_tensor_literal, storage_type(quantized_tensor_type)).
  • (C2) has_shape(quantized_tensor_literal, shape(quantized_tensor_type)).
StringConstant  ::= StringLiteral
StringLiteral   ::= '"' {stringCharacter | escapeSequence} '"'
stringCharacter ::= all ASCII characters except '\00', '\01', ... '\1f' and '"'
escapeSequence  ::= '\' ('"' | '\' | 'n' | 't' | (hexadecimalDigit hexadecimalDigit))

Literały łańcuchowe składają się z bajtów określonych za pomocą znaków ASCII oraz sekwencje ucieczki. Nie wymagają one kodowania, więc interpretacja tych wartości Bajty są definiowane przez implementację. Literały łańcuchowe mają typ string.

Operacje

brzuch

Semantyka

Wykonuje operację absolutnego punktu widzenia na podstawie elementu na tensorze operand i tworzy result tensora. W zależności od typu elementu wykonuje te działania:

  • W przypadku liczb całkowitych ze znakiem: moduł liczby całkowitej.
  • W przypadku jednostek zmiennoprzecinkowych: abs w standardzie IEEE-754.
  • W przypadku liczb zespolonych: moduł zespolony.
  • W przypadku typów kwantowych: dequantize_op_quantize(abs, operand, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu liczby całkowitej, zmiennoprzecinkowego, typu złożonego lub kwantyzowanego tensora według tensora (K1–C2)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu liczby całkowitej ze znakiem, typu zmiennoprzecinkowego lub kwantyzowanego tensora według tensora (K1–C2)

Ograniczenia

  • (C1) shape(result) = shape(operand).
  • (C2) Parametr baseline_element_type(result) jest zdefiniowany jako:
    • complex_element_type(element_type(operand)), jeśli is_complex(operand).
    • W przeciwnym razie: baseline_element_type(operand).

Przykłady

// %operand: [-2, 0, 2]
%result = "stablehlo.abs"(%operand) : (tensor<3xi32>) -> tensor<3xi32>
// %result: [2, 0, 2]

Więcej przykładów

dodaj

Semantyka

Wykonuje dodawanie elementów z uwzględnieniem 2 tensorów lhs i rhs, tworząc Tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku wartości logicznych: logiczny LUB.
  • W przypadku liczb całkowitych: dodawanie liczby całkowitej.
  • W przypadku jednostek zmiennoprzecinkowych: addition w standardzie IEEE-754.
  • W przypadku liczb zespolonych: dodawanie zespolone.
  • W przypadku typów kwantowych: dequantize_op_quantize(add, lhs, rhs, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs tensor (tensor kwantowy) (C1–C6)
(I2) rhs tensor (tensor kwantowy) (C1–C5), (C7)

Wyniki

Nazwa Typ Ograniczenia
result tensor (tensor kwantowy) (C1–C7)

Ograniczenia

  • Jeśli operacja używa tensorów niekwantowych:
    • (C1) type(lhs) = type(rhs) = type(result).
  • Jeśli operacja używa tensorów kwantyzowanych:
    • (C2) is_quantized(lhs) and is_quantized(rhs) and is_quantized(result).
    • (C3) storage_type(lhs) = storage_type(rhs) = storage_type(result).
    • (C4) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result).
    • (C5) (is_per_axis_quantized(lhs) or is_per_axis_quantized(rhs)) = is_per_axis_quantized(result).
    • (C6) Jeśli is_per_axis_quantized(lhs), to quantization_dimension(lhs) = quantization_dimension(result).
    • (C7) Jeśli is_per_axis_quantized(rhs), to quantization_dimension(rhs) = quantization_dimension(result).

Przykłady

// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.add"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[6, 8], [10, 12]]

Więcej przykładów

after_all

Semantyka

Zapewnia, że operacje tworzące interfejs inputs są wykonywane przed operacji zależnych od result. Wykonanie tej operacji nie przyniesie żadnego efektu. istnieje tylko po to, aby ustalić zależności danych od result do inputs.

Dane wejściowe

Etykieta Nazwa Typ
(I1) inputs liczba zmiennoprzecinkowa dla argumentu token

Wyniki

Nazwa Typ
result token

Przykłady

// %input0: !stablehlo.token
// %input1: !stablehlo.token
%result = "stablehlo.after_all"(%input0, %input1) : (!stablehlo.token, !stablehlo.token) -> !stablehlo.token

Więcej przykładów

all_gather

Semantyka

W każdej grupie procesów w siatce procesów StableHLO łączy wartości tensorów operands z każdego procesu w all_gather_dim i daje wynik Tensory: results.

Operacja dzieli siatkę procesów StableHLO na siatkę procesów process_groups, która jest: zdefiniowane w następujący sposób:

  • cross_replica(replica_groups) jeśli channel_id <= 0 and use_global_device_ids = false.
  • cross_replica_and_partition(replica_groups) jeśli channel_id > 0 and use_global_device_ids = false.
  • flattened_ids(replica_groups) jeśli channel_id > 0 and use_global_device_ids = true.

Następnie w każdym elemencie process_group:

  • operands...@receiver = [operand@sender for sender in process_group] za wszystko receiver w: process_group.
  • results...@process = concatenate(operands...@process, all_gather_dim) za wszystko process w: process_group.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operands liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów na tensor (C1), (C6)
(I2) all_gather_dim stała typu si64 (C1), (C6)
(I3) replica_groups 2-wymiarowa stała tensora typu si64 (C2–C4)
(I4) channel_id stała typu si64 (K5)
(I5) use_global_device_ids stała typu i1 (K5)

Wyniki

Nazwa Typ Ograniczenia
results liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów na tensor (C6)

Ograniczenia

  • (C1) 0 <= all_gather_dim < rank(operands...).
  • (C2) is_unique(replica_groups).
  • (C3) Parametr size(replica_groups) jest zdefiniowany jako:
    • num_replicas, jeśli używana jest właściwość cross_replica.
    • num_replicas, jeśli używana jest właściwość cross_replica_and_partition.
    • num_processes, jeśli używana jest właściwość flattened_ids.
  • (C4) 0 <= replica_groups < size(replica_groups).
  • (C5) Jeśli use_global_device_ids = true, to channel_id > 0.
  • (C6) type(results...) = type(operands...) z wyjątkiem:
    • dim(results..., all_gather_dim) = dim(operands..., all_gather_dim) * dim(process_groups, 1).

Przykłady

// num_replicas: 2
// num_partitions: 1
// %operand0@(0, 0): [[1, 2], [3, 4]]
// %operand0@(1, 0): [[5, 6], [7, 8]]
// %operand1@(0, 0): [[11, 12], [13, 14]]
// %operand1@(1, 0): [[15, 16], [17, 18]]
%result:2 = "stablehlo.all_gather"(%operand0, %operand1) {
  all_gather_dim = 1 : i64,
  replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>,
  // channel_id = 0
  channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
  // use_global_device_ids = false
} : (tensor<2x2xi64>, tensor<2x2xi64>) -> (tensor<2x4xi64>, tensor<2x4xi64>)
// %result0@(0, 0): [[1, 2, 5, 6], [3, 4, 7, 8]]
// %result0@(1, 0): [[1, 2, 5, 6], [3, 4, 7, 8]]
// %result1@(0, 0): [[11, 12, 15, 16], [13, 14, 17, 18]]
// %result1@(1, 0): [[11, 12, 15, 16], [13, 14, 17, 18]]

Więcej przykładów

all_reduce

Semantyka

W każdej grupie procesów w siatce procesów StableHLO stosuje redukcję funkcji computation na wartości tensorów operands z każdego procesu i generuje tensory results.

Operacja dzieli siatkę procesów StableHLO na siatkę procesów process_groups, która jest: zdefiniowane w następujący sposób:

  • cross_replica(replica_groups) jeśli channel_id <= 0 and use_global_device_ids = false.
  • cross_replica_and_partition(replica_groups) jeśli channel_id > 0 and use_global_device_ids = false.
  • flattened_ids(replica_groups) jeśli channel_id > 0 and use_global_device_ids = true.

Następnie w każdym elemencie process_group:

  • results...@process[result_index] = exec(schedule) dla pewnego drzewa binarnego schedule, gdzie:
    • exec(node) = computation(exec(node.left), exec(node.right)).
    • exec(leaf) = leaf.value.
  • schedule to zdefiniowane w implementacji drzewo binarne, w którym przemierzanie wynosi to_destination_type(operands...@process_group...[result_index], type(func_inputs(computation)[0])).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operands liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów na tensor (C5), (C6)
(I2) replica_groups liczba zmienna jednowymiarowych stałych tensora typu si64 (K1–C3)
(I3) channel_id stała typu si64 (K4)
(I4) use_global_device_ids stała typu i1 (K4)
(I5) computation funkcja (K5)

Wyniki

Nazwa Typ Ograniczenia
results liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów na tensor (C6–C7)

Ograniczenia

  • (C1) is_unique(replica_groups).
  • (C2) Parametr size(replica_groups) jest zdefiniowany jako:
    • num_replicas, jeśli używana jest właściwość cross_replica.
    • num_replicas, jeśli używana jest właściwość cross_replica_and_partition.
    • num_processes, jeśli używana jest właściwość flattened_ids.
  • (C3) 0 <= replica_groups < size(replica_groups).
  • (C4) Jeśli use_global_device_ids = true, to channel_id > 0.
  • (C5) computation ma typ (tensor<E>, tensor<E>) -> (tensor<E>), gdzie: is_promotable(element_type(operand), E)
  • (C6) shape(results...) = shape(operands...).
  • (C7) element_type(results...) = E.

Przykłady

// num_replicas: 2
// num_partitions: 1
// %operand0@(0, 0): [1, 2, 3, 4]
// %operand0@(1, 0): [5, 6, 7, 8]
// %operand1@(0, 0): [9, 10, 11, 12]
// %operand1@(1, 0): [13, 14, 15, 16]
%result:2 = "stablehlo.all_reduce"(%operand0, %operand0) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
    "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>,
  // channel_id = 0
  channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
  // use_global_device_ids = false
} : (tensor<4xi64>, tensor<4xi64>) -> (tensor<4xi64>, tensor<4xi64>)
// %result0@(0, 0): [6, 8, 10, 12]
// %result0@(1, 0): [6, 8, 10, 12]
// %result1@(0, 0): [22, 24, 26, 28]
// %result1@(1, 0): [22, 24, 26, 28]

Więcej przykładów

all_to_all

Semantyka

all_to_all

W każdej grupie procesów w siatce procesów StableHLO dzieli wartości tensory operands wzdłuż split_dimension na części, rozkłada podział łączą różne elementy, łączą w sobie rozproszone części concat_dimension i tworzy tensory results. Operacja dzieli siatkę procesów StableHLO na siatkę procesów process_groups, która jest: zdefiniowane w następujący sposób:

  • cross_replica(replica_groups), jeśli channel_id <= 0.
  • cross_partition(replica_groups), jeśli channel_id > 0.

Następnie w każdym elemencie process_group:

  • split_parts...@sender = split(operands...@sender, split_count, split_dimension) za wszystkie sender w process_group.
  • scattered_parts...@receiver = [split_parts...@sender[receiver_index] for sender in process_group], gdzie receiver_index = process_group.index(receiver)
  • results...@process = concatenate(scattered_parts...@process, concat_dimension).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operands liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów na tensor (C1–C3), (C9)
(I2) split_dimension stała typu si64 (C1), (C2), (C9)
(I3) concat_dimension stała typu si64 (C3), (C9)
(I4) split_count stała typu si64 (C2), (C4), (C8), (C9)
(I5) replica_groups 2-wymiarowa stała tensora typu si64 (C5–C8)
(I6) channel_id stała typu si64

Wyniki

Nazwa Typ Ograniczenia
results liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów na tensor (C9)

Ograniczenia

  • (C1) 0 <= split_dimension < rank(operands...).
  • (C2) dim(operands..., split_dimension) % split_count = 0.
  • (C3) 0 <= concat_dimension < rank(operands...).
  • (C4) 0 < split_count.
  • (C5) is_unique(replica_groups).
  • (C6) Parametr size(replica_groups) jest zdefiniowany jako:
    • num_replicas, jeśli używana jest właściwość cross_replica.
    • num_partitions, jeśli używana jest właściwość cross_partition.
  • (C7) 0 <= replica_groups < size(replica_groups).
  • (C8) dim(replica_groups, 1) = split_count.
  • (C9) type(results...) = type(operands...) oprócz tych, które split_dimension != concat_dimension:
    • dim(results..., split_dimension) = dim(operands..., split_dimension) / split_count.
    • dim(results..., concat_dimension) = dim(operands..., concat_dimension) * split_count.

Przykłady

// num_replicas: 2
// num_partitions: 1
// %operand1@(0, 0): [[1, 2, 3, 4],
//                    [5, 6, 7, 8]]
// %operand1@(1, 0): [[9, 10, 11, 12],
//                    [13, 14, 15, 16]]
// %operand2@(0, 0): [[17, 18, 19, 20],
//                    [21, 22, 23, 24]]
// %operand2@(1, 0): [[25, 26, 27, 28],
//                    [29, 30, 31, 32]]
%result:2 = "stablehlo.all_to_all"(%operand1, %operand2) {
  split_dimension = 1 : i64,
  concat_dimension = 0 : i64,
  split_count = 2 : i64,
  replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>
  // channel_id = 0
} : (tensor<2x4xi64>, tensor<2x4xi64>) -> (tensor<4x2xi64>, tensor<4x2xi64>)
// %result#0@(0, 0): [[1, 2], [5, 6], [9, 10], [13, 14]]
// %result#0@(1, 0): [[3, 4], [7, 8], [11, 12], [15, 16]]
// %result#1@(0, 0): [[17, 18], [21, 22], [25, 26], [29, 30]]
// %result#1@(1, 0): [[19, 20], [23, 24], [27, 28], [31, 32]]

Więcej przykładów

i

Semantyka

Wykonuje z punktu widzenia elementu ORAZ dwa tensory lhs i rhs, generuje result tensora. W zależności od typu elementu wykonuje te działania:

  • W przypadku wartości logicznych: logiczny ORAZ.
  • W przypadku liczb całkowitych: bitowe ORAZ.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs tensor typu wartości logicznej lub liczby całkowitej (C1)
(I2) rhs tensor typu wartości logicznej lub liczby całkowitej (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu wartości logicznej lub liczby całkowitej (C1)

Ograniczenia

  • (C1) type(lhs) = type(rhs) = type(result).

Przykłady

// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.and"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[1, 2], [3, 0]]

Więcej przykładów

atan2

Semantyka

Wykonuje operacje atan2 dotyczące elementów na tensorze lhs i rhs, tworząc Tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku jednostek zmiennoprzecinkowych: atan2 w standardzie IEEE-754.
  • W przypadku liczb zespolonych: atan2 zespolona.
  • W przypadku typów kwantowych: dequantize_op_quantize(atan2, lhs, rhs, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)
(I2) rhs tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(lhs) = baseline_type(rhs) = baseline_type(result).

Przykłady

// %lhs: [0.0, 1.0, -1.0]
// %rhs: [0.0, 0.0, 0.0]
%result = "stablehlo.atan2"(%lhs, %rhs) : (tensor<3xf64>, tensor<3xf64>) -> tensor<3xf64>
// %result: [0.0, 1.57079637, -1.57079637] // [0.0, pi/2, -pi/2]

Więcej przykładów

batch_norm_grad

Semantyka

Oblicza gradienty kilku danych wejściowych z propagacją wsteczną batch_norm_training od grad_output i tworzy grad_operand, grad_scale i grad_offset tensorów. Bardziej oficjalnie operacja ta można wyrazić jako istniejących operacji StableHLO przy użyciu składni Pythona w następujący sposób:

def compute_sum(operand, feature_index):
  (sum,) = reduce(
      inputs=[operand],
      init_values=[constant(0, element_type(operand))],
      dimensions=[i for i in range(rank(operand)) if i != feature_index],
      body=lambda x, y: add(x, y))
  return sum

def compute_mean(operand, feature_index):
  sum = compute_sum(operand, feature_index)
  divisor = constant(size(operand) / dim(operand, feature_index),
                     element_type(operand))
  divisor_bcast = broadcast_in_dim(divisor, [], type(sum))
  return divide(sum, divisor_bcast)

def batch_norm_grad(operand, scale, mean, variance, grad_output, epsilon, feature_index):
  # Broadcast inputs to type(operand)
  scale_bcast = broadcast_in_dim(scale, [feature_index], type(operand))
  mean_bcast = broadcast_in_dim(mean, [feature_index], type(operand))
  variance_bcast = broadcast_in_dim(variance, [feature_index], type(operand))
  epsilon_bcast = broadcast_in_dim(constant(epsilon, element_type(operand)), [],
                                   type(operand))

  # Perform normalization using the provided `mean` and `variance`
  # Intermediate values will be useful for computing gradients
  centered_operand = subtract(operand, mean_bcast)
  stddev = sqrt(add(variance_bcast, epsilon_bcast))
  normalized_operand = divide(centered_operand, stddev)

  # Use the implementation from batchnorm_expander.cc in XLA
  # Temporary variables have exactly the same names as in the C++ code
  elements_per_feature = broadcast_in_dim(
      constant(divide(size(operand), dim(operand, feature_index)),
               element_type(grad_output)),
      [], type(operand))
  i1 = multiply(grad_output, elements_per_feature)
  i2 = broadcast_in_dim(
      compute_sum(grad_output, feature_index), [feature_index], type(operand))
  i3 = broadcast_in_dim(
      compute_sum(multiply(grad_output, centered_operand), feature_index),
      [feature_index], type(operand))
  i4 = multiply(i3, centered_operand)
  i5 = divide(i4, add(variance_bcast, epsilon_bcast))
  i6 = subtract(subtract(i1, i2), i5)

  grad_operand =
      multiply(divide(divide(scale_bcast, stddev), elements_per_feature), i6)
  grad_scale =
      compute_sum(multiply(grad_output, normalized_operand), feature_index)
  grad_offset = compute_sum(grad_output, feature_index)

  return grad_operand, grad_scale, grad_offset

W przypadku typów kwantowych dequantize_batch_norm_grad_or_training_quantize(lambda operand, scale, mean, variance, grad_output: batch_norm_grad(operand, scale, mean, variance, grad_output, epsilon, feature_index), operand, scale, mean, variance, grad_output, type(grad_operand), type(grad_scale), type(feature_index))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C1–C3), (C5)
(I2) scale Jednowymiarowy tensor typu kwantyzowanego typu zmiennoprzecinkowego lub na tensor (C2), (C4), (C5)
(I3) mean Jednowymiarowy tensor typu kwantyzowanego typu zmiennoprzecinkowego lub na tensor (C2), (C4)
(I4) variance Jednowymiarowy tensor typu kwantyzowanego typu zmiennoprzecinkowego lub na tensor (C2), (C4)
(I5) grad_output tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C2), (C3)
(I6) epsilon stała typu f32
(I7) feature_index stała typu si64 (C1), (C5)

Wyniki

Nazwa Typ Ograniczenia
grad_operand tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C2), (C3)
grad_scale Jednowymiarowy tensor typu kwantyzowanego typu zmiennoprzecinkowego lub na tensor (C2), (C4)
grad_offset Jednowymiarowy tensor typu kwantyzowanego typu zmiennoprzecinkowego lub na tensor (C2), (C4)

Ograniczenia

  • (C1) 0 <= feature_index < rank(operand).
  • (C2) operand, scale, mean, variance, grad_output, grad_operand, grad_scale i grad_offset mają takie same wartości baseline_element_type.
  • (C3) operand, grad_output i grad_operand mają taki sam kształt.
  • (C4) scale, mean, variance, grad_scale i grad_offset mają taki sam kształt.
  • (C5) size(scale) = dim(operand, feature_index).

Przykłady

// %operand: [
//            [[1.0, 2.0], [3.0, 4.0]],
//            [[3.0, 4.0], [1.0, 2.0]]
//           ]
// %scale: [1.0, 1.0]
// %mean: [2.0, 3.0]
// %variance: [1.0, 1.0]
// %grad_output: [
//                [[0.1, 0.1], [0.1, 0.1]],
//                [[0.1, 0.1], [0.1, 0.1]]
//               ]
%grad_operand, %grad_scale, %grad_offset =
"stablehlo.batch_norm_grad"(%operand, %scale, %mean, %variance, %grad_output) {
  epsilon = 0.0 : f32,
  feature_index = 2 : i64
} : (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>, tensor<2xf64>,
     tensor<2x2x2xf64>) -> (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>)
// %grad_operand: [
//                 [[0.0, 0.0], [0.0, 0.0]],
//                 [[0.0, 0.0], [0.0, 0.0]]
//                ]
// %grad_scale:  [0.0, 0.0]
// %grad_offset: [0.4, 0.4]

batch_norm_inference

Semantyka

Normalizuje tensor operand we wszystkich wymiarach oprócz feature_index i generuje tensor result. Bardziej formalnie można wyrazić jako dekompozycję na istniejące operacje StableHLO używając składni Pythona w ten sposób:

def batch_norm_inference(operand, scale, offset, mean, variance, epsilon, feature_index):
  # Broadcast inputs to shape(operand)
  scale_bcast = broadcast_in_dim(scale, [feature_index], type(operand))
  offset_bcast = broadcast_in_dim(offset, [feature_index], type(operand))
  mean_bcast = broadcast_in_dim(mean, [feature_index], type(operand))
  variance_bcast = broadcast_in_dim(variance, [feature_index], type(operand))
  epsilon_bcast = broadcast_in_dim(constant(epsilon, element_type(operand)), [],
                                   type(operand))

  # Perform normalization using the provided `mean` and `variance` instead of
  # computing them like `batch_norm_training` does.
  centered_operand = subtract(operand, mean_bcast)
  stddev = sqrt(add(variance_bcast, epsilon_bcast))
  normalized_operand = divide(centered_operand, stddev)
  return add(multiply(scale_bcast, normalized_operand), offset_bcast)

W przypadku typów kwantowych dequantize_op_quantize(lambda operand, scale, offset, mean, variance: batch_norm_inference(operand, scale, offset, mean, variance, epsilon, feature_index), operand, scale, offset, mean, variance, type(result))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C1–C7)
(I2) scale Jednowymiarowy tensor typu kwantyzowanego typu zmiennoprzecinkowego lub na tensor (C2), (C3)
(I3) offset Jednowymiarowy tensor typu kwantyzowanego typu zmiennoprzecinkowego lub na tensor (C2), (C4)
(I4) mean Jednowymiarowy tensor typu kwantyzowanego typu zmiennoprzecinkowego lub na tensor (K5)
(I5) variance Jednowymiarowy tensor typu kwantyzowanego typu zmiennoprzecinkowego lub na tensor (C2), (C6)
(I6) epsilon stała typu f32
(I7) feature_index stała typu si64 (C1), (C3–C6)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C2), (C7)

Ograniczenia

  • (C1) 0 <= feature_index < rank(operand).
  • (C2) operand, scale, offset, mean, variance i result mają ten sam baseline_element_type.
  • (C3) size(scale) = dim(operand, feature_index).
  • (C4) size(offset) = dim(operand, feature_index).
  • (C5) size(mean) = dim(operand, feature_index).
  • (C6) size(variance) = dim(operand, feature_index).
  • (C7) baseline_type(operand) = baseline_type(result).

Przykłady

// %operand: [
//            [[1.0, 2.0], [3.0, 4.0]],
//            [[3.0, 4.0], [1.0, 2.0]]
//           ]
// %scale: [1.0, 1.0]
// %offset: [1.0, 1.0]
// %mean: [2.0, 3.0]
// %variance: [1.0, 1.0]
%result = "stablehlo.batch_norm_inference"(%operand, %scale, %offset, %mean, %variance) {
  epsilon = 0.0 : f32,
  feature_index = 2 : i64
} : (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>, tensor<2xf64>, tensor<2xf64>) -> tensor<2x2x2xf64>
// %result: [
//           [[0.0, 0.0], [2.0, 2.0]],
//           [[2.0, 2.0], [0.0, 0.0]]
//          ]

batch_norm_training

Semantyka

Oblicza średnią i wariancję dla wszystkich wymiarów oprócz feature_index i normalizuje tensor operand tworzący output, batch_mean i batch_var tensorów. Bardziej oficjalnie operacja ta może zostać przedstawiona jako do istniejących operacji StableHLO przy użyciu składni Pythona jako następujące:

def compute_mean(operand, feature_index):
  (sum,) = reduce(
      inputs=[operand],
      init_values=[constant(0, element_type(operand))],
      dimensions=[i for i in range(rank(operand)) if i != feature_index],
      body=lambda x, y: add(x, y))
  divisor = constant(size(operand) / dim(operand, feature_index),
                     element_type(operand))
  divisor_bcast = broadcast_in_dim(divisor, [], type(sum))
  return divide(sum, divisor_bcast)

def compute_variance(operand, feature_index):
  mean = compute_mean(operand, feature_index)
  mean_bcast = broadcast_in_dim(mean, [feature_index], type(operand))
  centered_operand = subtract(operand, mean_bcast)
  return compute_mean(mul(centered_operand, centered_operand), feature_index)

def batch_norm_training(operand, scale, offset, epsilon, feature_index):
  mean = compute_mean(operand, feature_index)
  variance = compute_variance(operand, feature_index)
  return batch_norm_inference(operand, scale, offset, mean, variance, epsilon,
                              feature_index),
         mean, variance

W przypadku typów kwantowych dequantize_batch_norm_grad_or_training_quantize(lambda operand, scale, offset: batch_norm_training(operand, scale, offset, epsilon, feature_index), operand, scale, offset, type(output), type(batch_mean), type(batch_var))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C1)
(I2) scale Jednowymiarowy tensor kwantyzowany (zmiennoprzecinkowy lub na tensor) (C2), (C3)
(I3) offset Jednowymiarowy tensor kwantyzowany (zmiennoprzecinkowy lub na tensor) (C2), (C4)
(I4) epsilon stała typu f32 (C1), (C3–C6)
(I5) feature_index stała typu si64 (C1), (C3–C6)

Wyniki

Nazwa Typ Ograniczenia
output tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C7)
batch_mean Jednowymiarowy tensor kwantyzowany (zmiennoprzecinkowy lub na tensor) (C2), (C5)
batch_var Jednowymiarowy tensor kwantyzowany (zmiennoprzecinkowy lub na tensor) (C2), (C6)

Ograniczenia

  • (C1) 0 <= feature_index < rank(operand).
  • (C2) operand, scale, offset, batch_mean, batch_var i output – ten sam baseline_element_type.
  • (C3) size(scale) = dim(operand, feature_index).
  • (C4) size(offset) = dim(operand, feature_index).
  • (C5) size(batch_mean) = dim(operand, feature_index).
  • (C6) size(batch_var) = dim(operand, feature_index).
  • (C7) baseline_type(output) = baseline_type(operand).

Przykłady

// %operand: [
//            [[1.0, 2.0], [3.0, 4.0]],
//            [[3.0, 4.0], [1.0, 2.0]]
//           ]
// %scale: [1.0, 1.0]
// %offset: [1.0, 1.0]
%output, %batch_mean, %batch_var = "stablehlo.batch_norm_training"(%operand, %scale, %offset) {
  epsilon = 0.0 : f32,
  feature_index = 2 : i64
} : (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>) ->
    (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>)
// %output: [
//           [[0.0, 0.0], [2.0, 2.0]],
//           [[2.0, 2.0], [0.0, 0.0]]
//          ]
// %batch_mean: [2.0, 3.0]
// %batch_var: [1.0, 1.0]

bitcast_convert

Semantyka

Wykonuje operację bitcastową na tensorze operand i tworzy tensor result gdzie bity całego tensora operand są ponownie interpretowane przy użyciu funkcji typ tensora result.

Bardziej oficjalnie, biorąc pod uwagę E = element_type(operand), E' = element_type(result), i R = rank(operand):

  • Jeśli num_bits(E') < num_bits(E), bits(result[i0, ..., iR-1, :]) = bits(operand[i0, ..., iR-1])
  • Jeśli num_bits(E') > num_bits(E), bits(result[i0, ..., iR-2]) = bits(operand[i0, ..., iR-2, :])
  • Jeśli num_bits(E') = num_bits(E), bits(result[i0, ..., iR-1]) = bits(operand[i0, ..., iR-1])

Funkcja bits zwraca określoną wartość w pamięci i jej działanie jest zdefiniowana implementacją, ponieważ dokładne reprezentowanie tensorów jest są zdefiniowane, a dokładna reprezentacja typów elementów jest a także kilka sposobów wdrożenia.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor (tensor kwantowy) (K1–C2)

Wyniki

Nazwa Typ Ograniczenia
result tensor (tensor kwantowy) (K1–C2)

Ograniczenia

  • (C1) Dane E = is_quantized(operand) ? storage_type(operand) : element_type(operand), E' = is_quantized(result) ? storage_type(result) : element_type(result) i R = rank(operand):
      .
    • Jeśli num_bits(E') = num_bits(E), shape(result) = shape(operand).
    • Jeśli num_bits(E') < num_bits(E):
    • rank(result) = R + 1.
    • dim(result, i) = dim(operand, i) za wszystkie 0 <= i < R.
    • dim(result, R) * num_bits(E') = num_bits(E).
    • Jeśli num_bits(E') > num_bits(E):
    • rank(result) = R - 1.
    • dim(result, i) = dim(operand, i) za wszystkie 0 <= i < R.
    • dim(operand, R - 1) * num_bits(E) = num_bits(E').
  • (C2) Jeśli is_complex(operand) or is_complex(result), to is_complex(operand) and is_complex(result)

Przykłady

// %operand: 0x0123456789ABCDEF
%result = "stablehlo.bitcast_convert"(%operand) : (tensor<f64>) -> tensor<4xf16>
// %result: [0xCDEF, 0x89AB, 0x4567, 0x0123] // little-endian representation

Więcej przykładów

broadcast_in_dim

Semantyka

Rozszerza wymiary lub pozycję tensora wejściowego przez zduplikowanie danych w tensorze operand i tworzy tensor result. Bardziej oficjalnie, result[result_index] = operand[operand_index], gdzie dla wszystkich d w axes(operand):

  • operand_index[d] = 0, jeśli dim(operand, d) = 1.
  • W przeciwnym razie: operand_index[d] = result_index[broadcast_dimensions[d]].

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor (tensor kwantowy) (C1–C2), (C5–C6)
(I2) broadcast_dimensions Jednowymiarowa stała tensora typu si64 (C2–C6)

Wyniki

Nazwa Typ Ograniczenia
result tensor (tensor kwantowy) (C1), (C3), (C5–C6)

Ograniczenia

  • (C1) element_type(result) otrzymuje:
    • element_type(operand), jeśli !is_per_axis_quantized(operand).
    • element_type(operand) oprócz quantization_dimension(operand), scales(operand) i zero_points(operand) mogą różnić się od quantization_dimension(result), scales(result) i zero_points(result) w odniesieniu do danych osobowych.
  • (C2) size(broadcast_dimensions) = rank(operand).
  • (C3) 0 <= broadcast_dimensions < rank(result).
  • (C4) is_unique(broadcast_dimensions).
  • (C5) Dla wszystkich d w axes(operand):
    • dim(operand, d) = 1 lub
    • dim(operand, d) = dim(result, broadcast_dimensions[d]).
  • (C6) Jeśli is_per_axis_quantized(result):
    • quantization_dimension(result) = broadcast_dimensions[quantization_dimension(operand)].
    • Jeśli dim(operand, quantization_dimension(operand)) = 1, to scales(result)[i] = scales(operand)[0] and zero_points(result)[i] = zero_points(operand)[0] for i in range(dim(result, quantization_dimension(result)))

Przykłady

// %operand: [
//            [1, 2, 3]
//           ]
%result = "stablehlo.broadcast_in_dim"(%operand) {
  broadcast_dimensions = array<i64: 2, 1>
} : (tensor<1x3xi32>) -> tensor<2x3x2xi32>
// %result: [
//            [
//             [1, 1],
//             [2, 2],
//             [3, 3]
//            ],
//            [
//             [1, 1],
//             [2, 2],
//             [3, 3]
//            ]
//          ]

Więcej przykładów

etui

Semantyka

Generuje dane wyjściowe w wyniku wykonania dokładnie 1 funkcji z funkcji branches w zależności od wartości index. Więcej formalnie: result = selected_branch() gdzie:

  • selected_branch = branches[index], jeśli 0 <= index < size(branches).
  • W przeciwnym razie: selected_branch = branches[-1].

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) index Tensor 0-wymiarowy typu si32
(I2) branches liczba zmiennoprzecinkowa funkcji (K1–C4)

Wyniki

Nazwa Typ Ograniczenia
results zmiennoprzecinkowa liczba tensorów, skwantyzowanych tensorów lub tokenów (K4)

Ograniczenia

  • (C1) 0 < size(branches).
  • (C2) input_types(branches...) = [].
  • (C3) same(output_types(branches...)).
  • (C4) type(results...) = output_types(branches[0]).

Przykłady

// %index: -1
// %result_branch0: [0, 0]
// %result_branch1: [1, 1]
%result0, %result1 = "stablehlo.case"(%index) ({
  "stablehlo.return"(%result_branch0, %result_branch0) : (tensor<2xi64>, tensor<2xi64>) -> ()
}, {
  "stablehlo.return"(%result_branch1, %result_branch1) : (tensor<2xi64>, tensor<2xi64>) -> ()
}) : (tensor<i32>) -> (tensor<2xi64>, tensor<2xi64>)
// %result0: [1, 1]
// %result1: [1, 1]

Więcej przykładów

Cbrt

Semantyka

Wykonuje operację pierwiastka sześciennego z uwzględnieniem elementów na tensorze operand i tworzy Tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku jednostek zmiennoprzecinkowych: rootn(x, 3) w standardzie IEEE-754.
  • W przypadku liczb zespolonych: pierwiastek sześcienny zespolony.
  • W przypadku typów kwantowych: dequantize_op_quantize(cbrt, operand, type(result))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %operand: [0.0, 1.0, 8.0, 27.0]
%result = "stablehlo.cbrt"(%operand) : (tensor<4xf64>) -> tensor<4xf64>
// %result: [0.0, 1.0, 2.0, 3.0]

Więcej przykładów

Ceil

Semantyka

Wykonuje ruch tasorowy tensora operand z uwzględnieniem elementów i tworzy tensor result. Implementuje operację roundToIntegralTowardPositive z standardu IEEE-754 specyfikacji. W przypadku typów kwantowych dequantize_op_quantize(ceil, operand, type(result))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %operand: [-0.8166, -0.2530, 0.2530, 0.8166, 2.0]
%result = "stablehlo.ceil"(%operand) : (tensor<5xf32>) -> tensor<5xf32>
// %result: [-0.0, -0.0, 1.0, 1.0, 2.0]

Więcej przykładów

Cholesky

Semantyka

Oblicza rozkład Cholesky'ego grupy macierzy.

Mówiąc bardziej oficjalnie, z okazji wszystkich i w index_space(result). result[i0, ..., iR-3, :, :] to rozkład Cholesky'ego a[i0, ..., iR-3, :, :], w postaci dowolnego z dolnych trójkątów (jeśli lower to true) lub macierz trójkątną górną (jeśli lower to false). wartości wyjściowe w trójkącie przeciwległym, tj. ścisłego górnego trójkąta lub odpowiednio dolnego trójkąta są definiowane na potrzeby implementacji.

Jeśli istnieje i, gdzie macierz wejściowa nie jest dodatnią precyzją hermitańską to zachowanie jest niezdefiniowane.

W przypadku typów kwantowych dequantize_op_quantize(lambda operand: cholesky(operand, lower), a, type(result))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) a tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (K1–C3)
(I2) lower Stała tensora 0-wymiarowego typu i1

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(a) = baseline_type(result).
  • (C2) 2 <= rank(a).
  • (C3) dim(a, -2) = dim(a, -1).

Przykłady

// %a: [
//      [1.0, 2.0, 3.0],
//      [2.0, 20.0, 26.0],
//      [3.0, 26.0, 70.0]
//     ]
%result = "stablehlo.cholesky"(%a) {
  lower = true
} : (tensor<3x3xf32>) -> tensor<3x3xf64>
// %result: [
//           [1.0, 0.0, 0.0],
//           [2.0, 4.0, 0.0],
//           [3.0, 5.0, 6.0]
//          ]

ograniczać (zakres)

Semantyka

Łączy każdy element tensora operand między wartością minimalną a maksymalną i tworzy tensor result. result[result_index] = minimum(maximum(operand[result_index], min_element), max_element). gdzie min_element = rank(min) = 0 ? min[] : min[result_index], max_element = rank(max) = 0 ? max[] : max[result_index] W przypadku typów kwantowych wykonuje dequantize_op_quantize(clamp, min, operand, max, type(result)).

Nakładanie kolejności na liczby zespolone wymaga zaskakującej semantyki, więc w przyszłości planujemy wycofać obsługę liczb zespolonych dla tej operacji (#560).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) min kwantowy tensor lub tensor kwantowy (C1), (C3)
(I2) operand kwantowy tensor lub tensor kwantowy (K1–C4)
(I3) max kwantowy tensor lub tensor kwantowy (C2), (C3)

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (K4)

Ograniczenia

  • (C1) rank(min) = 0 or shape(min) = shape(operand).
  • (C2) rank(max) = 0 or shape(max) = shape(operand).
  • (C3) baseline_element_type(min) = baseline_element_type(operand) = baseline_element_type(max).
  • (C4) baseline_type(operand) = baseline_type(result).

Przykłady

// %min: [5, 10, 15]
// %operand: [3, 13, 23]
// %max: [10, 15, 20]
%result = "stablehlo.clamp"(%min, %operand, %max) : (tensor<3xi32>, tensor<3xi32>, tensor<3xi32>) -> tensor<3xi32>
// %result: [5, 13, 20]

Więcej przykładów

collective_broadcast

Semantyka

W ramach każdej grupy procesów w siatce procesów StableHLO wyślij wartość klucza operand tensor z procesu źródłowego do procesów docelowych i utwórz Tensor result.

Operacja dzieli siatkę procesów StableHLO na siatkę procesów process_groups, która jest: zdefiniowane w następujący sposób:

  • cross_replica(replica_groups), jeśli channel_id <= 0.
  • cross_partition(replica_groups), jeśli channel_id > 0.

Później wartość result@process jest określana przez:

  • operand@process_groups[i, 0], jeśli istnieje i, dzięki któremu proces jest w aplikacji process_groups[i].
  • broadcast_in_dim(constant(is_quantized(result) ? quantize(0, element_type(result)) : 0, element_type(result)), [], type(result)) w przeciwnym razie.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand kwantowy tensor lub tensor kwantowy (K3)
(I2) replica_groups liczba zmienna jednowymiarowych stałych tensora typu si64 (C1), (C2)
(I3) channel_id stała typu si64

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (K3)

Ograniczenia

  • (C1) is_unique(replica_groups).
  • (C2) 0 <= replica_groups < N, gdzie N jest zdefiniowany jako:
    • num_replicas, jeśli używana jest właściwość cross_replica.
    • num_partitions, jeśli używana jest właściwość cross_partition.
  • (C3) type(result) = type(operand).

Przykłady

// num_replicas: 4
// num_partitions: 1
// %operand@(0, 0): [[1, 2]]
// %operand@(1, 0): [[3, 4]]
// %operand@(2, 0): [[5, 6]]
// %operand@(3, 0): [[7, 8]]
%result = "stablehlo.collective_broadcast"(%operand) {
  replica_groups = dense<[[2, 1]]> : tensor<1x2xi64>,
  channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
} : (tensor1x2xi64>) -> tensor<1x2xi64>
// %result@(0, 0): [[0, 0]]
// %result@(1, 0): [[5, 6]]
// %result@(2, 0): [[5, 6]]
// %result@(3, 0): [[0, 0]]

collective_permute

Semantyka

W każdej grupie procesów w siatce procesów StableHLO wysyła wartość klucza Tensor operand z procesu źródłowego do procesu docelowego tworzy Tensor result.

Operacja dzieli siatkę procesów StableHLO na siatkę procesów process_groups, która jest: zdefiniowane w następujący sposób:

  • cross_replica(source_target_pairs), jeśli channel_id <= 0.
  • cross_partition(source_target_pairs), jeśli channel_id > 0.

Później wartość result@process jest określana przez:

  • operand@process_groups[i, 0], jeśli występuje i taki jak process_groups[i, 1] = process
  • broadcast_in_dim(constant(is_quantized(result) ? quantize(0, element_type(result)) : 0, element_type(result)), [], type(result)) w przeciwnym razie.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand kwantowy tensor lub tensor kwantowy (K5)
(I2) source_target_pairs 2-wymiarowa stała tensora typu si64 (K1–C4)
(I3) channel_id stała typu si64

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (C1)

Ograniczenia

  • (C1) dim(source_target_pairs, 1) = 2.
  • (C2) is_unique(source_target_pairs[:, 0]).
  • (C3) is_unique(source_target_pairs[:, 1]).
  • (C4) 0 <= source_target_pairs < N, gdzie N jest zdefiniowany jako:
    • num_replicas, jeśli używana jest właściwość cross_replica.
    • num_partitions, jeśli używana jest właściwość cross_partition.
  • (C5) type(result) = type(operand).

Przykłady

// num_replicas: 3
// num_partitions: 1
// %operand@(0, 0): [[1, 2], [3, 4]]
// %operand@(1, 0): [[5, 6], [7, 8]]
// %operand@(2, 0): [[9, 10], [11, 12]]
%result = "stablehlo.collective_permute"(%operand) {
  source_target_pairs = dense<[[0, 1], [1, 2]]> : tensor<2x2xi64>,
  channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
} : (tensor<2x2xi64>) -> tensor<2x2xi64>
//
// %result@(0, 0): [[0, 0], [0, 0]]
// %result@(1, 0): [[1, 2], [3, 4]]
// %result@(2, 0): [[5, 6], [7, 8]]

Więcej przykładów

porównaj

Semantyka

Wykonuje porównanie tensorów lhs i rhs z uwzględnieniem elementów zgodnie z comparison_direction i compare_type. Generuje tensor result.

Wartości comparison_direction i compare_type mają następujące właściwości semantyka:

W przypadku elementów z wartościami logicznymi i liczbami całkowitymi:

  • EQ: lhs = rhs.
  • NE: lhs != rhs.
  • GE: lhs >= rhs.
  • GT: lhs > rhs.
  • LE: lhs <= rhs.
  • LT: lhs < rhs.

W przypadku typów elementów zmiennoprzecinkowych z parametrem compare_type = FLOAT operator działa następujące operacje IEEE-754:

  • EQ: compareQuietEqual.
  • NE: compareQuietNotEqual.
  • GE: compareQuietGreaterEqual.
  • GT: compareQuietGreater.
  • LE: compareQuietLessEqual.
  • LT: compareQuietLess.

W przypadku elementów zmiennoprzecinkowych typu compare_type = TOTALORDER parametr op korzysta z kombinacji operacji totalOrder i compareQuietEqual z IEEE-754.

W przypadku złożonych typów elementów leksykograficzne porównanie par (real, imag) jest następujące została wykonana za pomocą podanych wartości comparison_direction i compare_type. Nakładanie kolejności na liczby zespolone wymaga zaskakującej semantyki, więc w przyszłości planujemy wycofać obsługę liczb zespolonych gdy comparison_direction to GE, GT, LE lub LT (#560).

Dotyczy typów kwantowych. wykonuje dequantize_compare(lhs, rhs, comparison_direction).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs kwantowy tensor lub tensor kwantowy (K1–C3)
(I2) rhs kwantowy tensor lub tensor kwantowy (K1–C2)
(I3) comparison_direction wyliczenie EQ, NE, GE, GT, LE i LT
(I4) compare_type wyliczenie FLOAT, TOTALORDER, SIGNED i UNSIGNED (K3)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu wartości logicznej (K2)

Ograniczenia

  • (C1) baseline_element_type(lhs) = baseline_element_type(rhs).
  • (C2) shape(lhs) = shape(rhs) = shape(result).
  • (C3) Parametr compare_type jest zdefiniowany jako:
    • SIGNED, jeśli is_signed_integer(element_type(lhs)).
    • UNSIGNED, jeśli is_unsigned_integer(element_type(lhs)) or is_boolean(element_type(lhs)).
    • FLOAT lub TOTALORDER, jeśli is_float(element_type(lhs)).
    • FLOAT, jeśli is_complex(element_type(lhs)).

Przykłady

// %lhs: [1.0, 3.0]
// %rhs: [1.1, 2.9]
%result = "stablehlo.compare"(%lhs, %rhs) {
  comparison_direction = #stablehlo<comparison_direction LT>,
  compare_type = #stablehlo<comparison_type FLOAT>
} : (tensor<2xf32>, tensor<2xf32>) -> tensor<2xi1>
// %result: [true, false]

Więcej przykładów

Złożone

Semantyka

Konwertuje elementy na podstawie wartości złożonej na podstawie pary wartości rzeczywistych i wartości urojonych lhs i rhs, tworząc tensor result.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs tensor typu f32 lub f64 (K1–C3)
(I2) rhs tensor typu f32 lub f64 (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zespolonego (C2), (C3)

Ograniczenia

  • (C1) type(lhs) = type(rhs).
  • (C2) shape(result) = shape(lhs).
  • (C3) element_type(result) ma typ complex<E>, gdzie: E = element_type(lhs)

Przykłady

// %lhs: [1.0, 3.0]
// %rhs: [2.0, 4.0]
%result = "stablehlo.complex"(%lhs, %rhs) : (tensor<2xf64>, tensor<2xf64>) -> tensor<2xcomplex<f64>>
// %result: [(1.0, 2.0), (3.0, 4.0)]

Więcej przykładów

wieloskładnikowa

Semantyka

Obejmuje operację składającą się z innych operacji StableHLO, biorąc inputs i composite_attributes oraz produkując results. semantyka operacji jest implementowana przez atrybut decomposition. composite – przeciwnicy można zastąpić własnym programem bez zmiany programu semantyka. W przypadkach, gdy wbudowanie dekompozycji nie zapewnia takiego samego semantyka operacji, preferuj użycie parametru custom_call.

Pole version (wartość domyślna to 0) jest używane do określania, kiedy trzeba zmienić semantykę.

Dane wejściowe

Etykieta Nazwa Typ
(I1) inputs liczba zmiennoprzecinkowa
(I2) name stała typu string
(I3) composite_attributes słownik atrybutów
(I4) decomposition stała typu string
(I5) version stała typu si32

Wyniki

Nazwa Typ
results liczba zmiennoprzecinkowa

Ograniczenia

  • (C1) is_namespaced_op_name(name)
  • (C2) is_defined_in_parent_scope(decomposition)
  • (C3) types(inputs...) == input_types(decomposition)
  • (C4) types(results...) == output_types(decomposition)

Przykłady

%results = "stablehlo.composite"(%input0, %input1) {
  name = "my_namespace.my_op",
  composite_attributes = {
    my_attribute = "my_value"
  },
  decomposition = @my_op,
  version = 1 : i32
} : (tensor<f32>, tensor<f32>) -> tensor<f32>

Więcej przykładów

połączyć

Semantyka

Łączy element inputs wzdłuż wymiaru dimension w tej samej kolejności jak podana argumentów i generuje tensor result. Bardziej oficjalnie, result[i0, ..., id, ..., iR-1] = inputs[k][i0, ..., kd, ..., iR-1], gdzie:

  1. id = d0 + ... + dk-1 + kd.
  2. Parametr d jest równy dimension, a d0 to d rozmiary wymiarów z inputs.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) inputs liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów na tensor (C1–C6)
(I2) dimension stała typu si64 (C2), (C4), (C6)

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (C5–C6)

Ograniczenia

  • (C1) same(element_type(inputs...)).
  • (C2) same(shape(inputs...)) oprócz dim(inputs..., dimension).
  • (C3) 0 < size(inputs).
  • (C4) 0 <= dimension < rank(inputs[0]).
  • (C5) element_type(result) = element_type(inputs[0]).
  • (C6) shape(result) = shape(inputs[0]) oprócz:
    • dim(result, dimension) = dim(inputs[0], dimension) + ....

Przykłady

// %input0: [[1, 2], [3, 4], [5, 6]]
// %input1: [[7, 8]]
%result = "stablehlo.concatenate"(%input0, %input1) {
  dimension = 0 : i64
} : (tensor<3x2xi64>, tensor<1x2xi64>) -> tensor<4x2xi64>
// %result: [[1, 2], [3, 4], [5, 6], [7, 8]]

Więcej przykładów

stała

Semantyka

Generuje tensor output ze stałej wartości value.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) value stała (C1)

Wyniki

Nazwa Typ Ograniczenia
output tensor (tensor kwantowy) (C1)

Ograniczenia

  • (C1) type(value) = type(output).

Przykłady

%output = "stablehlo.constant"() {
  value = dense<[[0.0, 1.0], [2.0, 3.0]]> : tensor<2x2xf32>
} : () -> tensor<2x2xf32>
// %output: [[0.0, 1.0], [2.0, 3.0]]

Więcej przykładów

dokonają konwersji

Semantyka

Przeprowadza konwersję z jednego typu elementu na inny Tensor operand tworzy tensor result.

W przypadku konwersji typu boolean-to-any-supported-type wartość false to jest przeliczana na zero, a wartość true jest konwertowana na 1. Dla: any-supported-type-to-boolean, wartość 0 jest konwertowana na false i wartości różne od zera są konwertowane na true. Zobacz poniżej, jak to działa działa w przypadku typów złożonych.

w przypadku konwersji, w których występują całkowite liczby zmiennoprzecinkowe i liczba zmiennoprzecinkowa na liczbę całkowitą; lub floating-point-to-floating-point, jeśli wartością źródłową może być dokładnie reprezentowana w typie miejsca docelowego, wynikową wartością będzie dokładnie to, reprezentacja. W przeciwnym razie działanie jest podawane do ustalenia. (#180).

W przypadku konwersji obejmujących liczby floating-point-to-integer część ułamkowa to obcięte. Jeśli obciętej wartości nie można zapisać w typie miejsca docelowego, zachowanie należy do ustalenia (#180).

Konwersja, w której występuje konwersja kompleksowe i złożone, jest taki sam jak w przypadku konwersji konwersji floating-point-to-floating-point na rzeczywiste i części urojonych.

W przypadku konwersji typu complex-to-any-other-type i complex-to-any-other-type wartość urojona źródła jest ignorowana lub wartość urojona miejsca docelowego jest wyzerować odpowiednio. Konwersja części rzeczywistej następuje po konwersji zmiennoprzecinkowych.

Zasadniczo operacja ta może wyrażać dekwantyzację (konwersję z kwantyzowane tensory do tensorów regularnych), kwantyzacja (konwersja ze zwykłych tensory do tensorów kwantyzowanych) i rekwantyzacji (konwersja między kwantyzowanymi danymi tensory), ale obecnie prowadzone są specjalne działania, uniform_dequantize dla pierwszego przypadku użycia i uniform_quantize dla w drugim i trzecim przypadku użycia. W przyszłości te 2 operacje mogą zostać scalone na convert (nr 1576).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand Tensor (C1)

Wyniki

Nazwa Typ Ograniczenia
result Tensor (C1)

Ograniczenia

  • (C1) shape(operand) = shape(result).

Przykłady

// %operand: [-1, 0, 1]
%result = "stablehlo.convert"(%operand) : (tensor<3xi64>) -> tensor<3xcomplex<f64>>
// %result: [(-1.0, 0.0), (0.0, 0.0), (1.0, 0.0)]

Więcej przykładów

splot

Semantyka

Oblicza iloczyn skalarny między oknami o długości lhs i wycinkach rhs, uzyskując wynik result Ten diagram pokazuje, jak elementy w result są obliczane na podstawie lhs i rhs za pomocą konkretnego przykładu.

splot

Rozważ bardziej formalne dostosowanie danych wejściowych w kontekście: lhs aby móc tworzyć okna dla trybu lhs:

  • lhs_window_dimensions = lhs_shape(dim(lhs, input_batch_dimension), dim(rhs, kernel_spatial_dimensions), dim(lhs, input_feature_dimension)).
  • lhs_window_strides = lhs_shape(1, window_strides, 1).
  • lhs_padding = lhs_shape([0, 0], padding, [0, 0]).
  • lhs_base_dilations = lhs_shape(1, lhs_dilation, 1).
  • lhs_window_dilations = lhs_shape(1, rhs_dilation, 1).

Ta zmiana kadrowania wykorzystuje te funkcje pomocnicze:

  • lhs_shape(n, hw, c) = permute([n] + hw + [c], [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]).
  • result_shape(n1, hw, c1) = permute([n1] + hw + [c1], [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]).
  • permute([j0, j1, ..., jR-1], permutation) = [i0, i1, ..., iR-1], gdzie j[d] = i[permutation[d]].

Jeśli feature_group_count = 1 i batch_group_count = 1, to dla wszystkich output_spatial_index w: index_space(dim(result, output_spatial_dimensions...)), result[result_shape(:, output_spatial_index, :)] = dot_product, gdzie:

  • padding_value = constant(0, element_type(lhs)).
  • padded_lhs = pad(lhs, padding_value, lhs_padding[:, 0], lhs_padding[:, 1], lhs_base_dilations - 1).
  • lhs_window_start = lhs_shape(0, output_spatial_index, 0) * lhs_window_strides.
  • lhs_window = slice(padded_lhs, lhs_window_start, lhs_window_start + lhs_window_dimensions, lhs_window_dilations).
  • reversed_lhs_window = reverse(lhs_window, [input_spatial_dimensions[dim] for dim in range(size(window_reversal)) if window_reversal[dim] = true]) Funkcja ta jest prawdopodobnie nieużywana, dlatego planujemy usunąć w przyszłości go (#1181).
  • dot_product = dot_general(reversed_lhs_window, rhs, lhs_batching_dimensions=[], lhs_contracting_dimensions=input_spatial_dimensions + [input_feature_dimension], rhs_batching_dimensions=[], rhs_contracting_dimensions=kernel_spatial_dimensions + [kernel_input_feature_dimension]).

Jeśli feature_group_count > 1:

  • lhses = split(lhs, feature_group_count, input_feature_dimension).
  • rhses = split(rhs, feature_group_count, kernel_output_feature_dimension).
  • results... = convolution(lhses..., rhses..., ..., feature_group_count=1, ...).
  • result = concatenate(results, output_feature_dimension).

Jeśli batch_group_count > 1:

  • lhses = split(lhs, batch_group_count, input_batch_dimension).
  • rhses = split(rhs, batch_group_count, kernel_output_feature_dimension).
  • results... = convolution(lhses..., rhses..., ..., batch_group_count=1, ...).
  • result = concatenate(results, output_feature_dimension)

W przypadku typów kwantowych funkcja dequantize_op_quantize( lambda lhs, rhs: convolution(lhs, rhs, window_strides, padding, lhs_dilation, rhs_dilation, window_reversal, input_batch_dimension, input_feature_dimension, input_spatial_dimensions, kernel_input_feature_dimension, kernel_output_feature_dimension, kernel_spatial_dimensions, output_batch_dimension, output_feature_dimension, output_spatial_dimensions, feature_group_count, batch_group_count, precision_config), lhs, rhs, type(result)).

W przypadku typów hybrydowych kwantyzowana wartość wylicza hybrid_dequantize_then_op( lambda lhs, rhs: convolution(lhs, rhs, window_strides, padding, lhs_dilation, rhs_dilation, window_reversal, input_batch_dimension, input_feature_dimension, input_spatial_dimensions, kernel_input_feature_dimension, kernel_output_feature_dimension, kernel_spatial_dimensions, output_batch_dimension, output_feature_dimension, output_spatial_dimensions, feature_group_count, batch_group_count, precision_config), lhs, rhs).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs kwantowy tensor lub tensor kwantowy (C1), (C10–C11), (C14) (C25), (C27–C28), (C31–C32), (C34)
(I2) rhs tensor (tensor kwantowy) (C1), (C14–C16), (C25), (C27–C29), (C31–C34)
(I3) window_strides Jednowymiarowa stała tensora typu si64 (C2–C3), (C25)
(I4) padding 2-wymiarowa stała tensora typu si64 (C4), (C25)
(I5) lhs_dilation Jednowymiarowa stała tensora typu si64 (C5-C6), (C25)
(I6) rhs_dilation Jednowymiarowa stała tensora typu si64 (C7-C8), (C25)
(I7) window_reversal Jednowymiarowa stała tensora typu i1 (C9)
(I8) input_batch_dimension stała typu si64 (C10), (C13), (C25)
(I9) input_feature_dimension stała typu si64 (C11), (C13–C14)
(I10) input_spatial_dimensions Jednowymiarowa stała tensora typu si64 (C12), (C13), (C25)
(I11) kernel_input_feature_dimension stała typu si64 (C14), (C18)
(I12) kernel_output_feature_dimension stała typu si64 (C15-C16), (C18), (C25), (C29)
(I13) kernel_spatial_dimensions Jednowymiarowa stała tensora typu si64 (C17-C18), (C25)
(I14) output_batch_dimension stała typu si64 (C20), (C25)
(I15) output_feature_dimension stała typu si64 (C20), (C25), (C30)
(I16) output_spatial_dimensions Jednowymiarowa stała tensora typu si64 (C19-C20), (C25)
(I17) feature_group_count stała typu si64 (C11), (C14), (C16), (C21), (C23)
(I18) batch_group_count stała typu si64 (C10), (C15), (C22), (C23), (C25)
(I19) precision_config liczba zmiennoprzecinkowa dla DEFAULT, HIGH i HIGHEST (C24)

Wyniki

Nazwa Typ Ograniczenia
result tensor (tensor kwantowy) (C25-C28), (C30), (C32-34)

Ograniczenia

  • (C1) N = rank(lhs) = rank(rhs).
  • (C2) size(window_strides) = N - 2.
  • (C3) 0 < window_strides.
  • (C4) shape(padding) = [N - 2, 2].
  • (C5) size(lhs_dilation) = N - 2.
  • (C6) 0 < lhs_dilation.
  • (C7) size(rhs_dilation) = N - 2.
  • (C8) 0 < rhs_dilation.
  • (C9) size(window_reversal) = N - 2.
  • (C10) dim(lhs, input_batch_dimension) % batch_group_count = 0.
  • (C11) dim(lhs, input_feature_dimension) % feature_group_count = 0.
  • (C12) size(input_spatial_dimensions) = N - 2.
  • (C13) Podany input_dimensions = [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]:
    • is_unique(input_dimensions).
    • 0 <= input_dimensions < N.
  • (C14) dim(rhs, kernel_input_feature_dimension) = dim(lhs, input_feature_dimension) / feature_group_count.
  • (C15) dim(rhs, kernel_output_feature_dimension) % batch_group_count = 0.
  • (C16) dim(rhs, kernel_output_feature_dimension) % feature_group_count = 0.
  • (C17) size(kernel_spatial_dimensions) = N - 2.
  • (C18) Podany kernel_dimensions = kernel_spatial_dimensions + [kernel_input_feature_dimension] + [kernel_output_feature_dimension]:
    • is_unique(kernel_dimensions).
    • 0 <= kernel_dimensions < N.
  • (C19) size(output_spatial_dimensions) = N - 2.
  • (C20) Podany output_dimensions = [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]:
    • is_unique(output_dimensions).
    • 0 <= output_dimensions < N.
  • (C21) 0 < feature_group_count.
  • (C22) 0 < batch_group_count.
  • (C23) feature_group_count = 1 or batch_group_count = 1.
  • (C24) size(precision_config) = 2.
  • (C25) Parametr dim(result, result_dim) jest zdefiniowany jako:
    • dim(lhs, input_batch_dimension) / batch_group_count, jeśli result_dim = output_batch_dimension.
    • dim(rhs, kernel_output_feature_dimension), jeśli result_dim = output_feature_dimension.
    • num_windows w przeciwnym razie, gdzie:
    • output_spatial_dimensions[spatial_dim] = result_dim.
    • lhs_dim = input_spatial_dimensions[spatial_dim].
    • rhs_dim = kernel_spatial_dimensions[spatial_dim].
    • dilated_input_shape[lhs_dim] = dim(lhs, lhs_dim) = 0 ? 0 : (dim(lhs, lhs_dim) - 1) * lhs_dilation[spatial_dim] + 1.
    • padded_input_shape[lhs_dim] = padding[spatial_dim, 0] + dilated_input_shape[lhs_dim] + padding[spatial_dim, 1].
    • dilated_window_shape[lhs_dim] = dim(rhs, rhs_dim) = 0 ? 0 : (dim(rhs, rhs_dim) - 1) * rhs_dilation[spatial_dim] + 1.
    • is_empty_window[lhs_dim] = padded_input_shape[lhs_dim] = 0 || dilated_window_shape[lhs_dim] > padded_input_shape[lhs_dim].
    • num_windows = is_empty_window[lhs_dim] ? 0 : floor((padded_input_shape[lhs_dim] - dilated_window_shape[lhs_dim]) / window_strides[spatial_dim]) + 1.
  • (C26) rank(result) = N.
  • Jeśli operacja używa tensorów niekwantowych:
    • (C27) element_type(lhs) = element_type(rhs) = element_type(result).
  • Jeśli operacja używa tensorów kwantyzowanych:
    • (C28) is_quantized(lhs) = is_quantized(result) and is_quantized(rhs).
    • (C29) Jeśli is_per_axis_quantized(rhs), a następnie quantization_dimension(rhs) = kernel_output_feature_dimension.
    • (C30) Jeśli is_per_axis_quantized(result), quantization_dimension(result) = output_feature_dimension
    • Jeśli is_quantized(lhs):
    • (C31) storage_type(lhs) = storage_type(rhs).
    • (C32) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result).
    • (C33) Jeśli is_per_tensor_quantized(rhs), is_per_tensor_quantized(result)
    • Jeśli !is_quantized(lhs):
    • (C34) element_type(lhs) = expressed_type(rhs) = element_type(result).

Przykłady

// %lhs: [[
//        [
//          [1], [2], [5], [6]
//        ],
//        [
//          [3], [4], [7], [8]
//        ],
//        [
//          [10], [11], [14], [15]
//        ],
//        [
//          [12], [13], [16], [17]
//        ]
//      ]]
//
// %rhs: [
//        [[[1]], [[1]], [[1]]],
//        [[[1]], [[1]], [[1]]],
//        [[[1]], [[1]], [[1]]]
//       ]
%result = "stablehlo.convolution"(%lhs, %rhs) {
  window_strides = array<i64: 4, 4>,
  padding = dense<0> : tensor<2x2xi64>,
  lhs_dilation = array<i64: 2, 2>,
  rhs_dilation = array<i64: 1, 1>,
  window_reversal = array<i1: false, false>,
  // In the StableHLO dialect, dimension numbers are encoded via:
  // `[<input dimensions>]x[<kernel dimensions>]->[output dimensions]`.
  // "b" is batch dimension, "f" is feature dimension,
  // "i" is input feature dimension, "o" is output feature dimension,
  // "0/1/etc" are spatial dimensions.
  dimension_numbers = #stablehlo.conv<[b, 0, 1, f]x[0, 1, i, o]->[b, 0, 1, f]>,
  batch_group_count = 1 : i64,
  feature_group_count = 1 : i64,
  precision_config = [#stablehlo<precision DEFAULT>, #stablehlo<precision DEFAULT>]
} : (tensor<1x4x4x1xi64>, tensor<3x3x1x1xi64>) -> tensor<1x2x2x1xi64>
// %result: [[
//            [[10], [26]],
//            [[46], [62]]
//          ]]

Więcej przykładów

cosinus

Semantyka

Wykonuje operacje cosinusowe związane z elementami na tensorze operand i tworzy Tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku jednostek zmiennoprzecinkowych: cos w standardzie IEEE-754.
  • W przypadku liczb zespolonych: cosinus zespolony.
  • W przypadku typów kwantowych: dequantize_op_quantize(cosine, operand, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %operand: [
//            [0.0, 1.57079632],       // [0, pi/2]
//            [3.14159265, 4.71238898] // [pi, 3pi/2]
//           ]
%result = "stablehlo.cosine"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[1.0, 0.0], [-1.0, 0.0]]

Więcej przykładów

count_leading_zeros

Semantyka

Wykonuje liczbę bitów na początku wiersza operand według elementu tensora i tworzy tensor result.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu liczby całkowitej (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu liczby całkowitej (C1)

Ograniczenia

  • (C1) type(operand) = type(result).

Przykłady

// %operand: [[0, 1], [128, -1]]
%result = "stablehlo.count_leading_zeros"(%operand) : (tensor<2x2xi64>) -> tensor<2x2xi64>
// %result: [[64, 63], [56, 0]]

Więcej przykładów

custom_call

Semantyka

Obejmuje zdefiniowaną przez implementację operację call_target_name, która wykonuje inputs i called_computations oraz tworzy results. has_side_effect, backend_config i api_version mogą posłużyć do podania dodatkowych informacji metadanych zdefiniowanych w implementacji.

W tej chwili ta operacja obejmuje dość nieuporządkowany zbiór które odzwierciedlają naturalną ewolucję ich odpowiednika kompilator XLA. W przyszłości planujemy ujednolicenie tych metadanych. (#741).

Dane wejściowe

Etykieta Nazwa Typ
(I1) inputs liczba zmiennoprzecinkowa
(I2) call_target_name stała typu string
(I3) has_side_effect stała typu i1
(I4) backend_config stała typu string lub słownik atrybutów
(I5) api_version stała typu si32
(I6) called_computations liczba zmiennoprzecinkowa typu string

Wyniki

Nazwa Typ
results liczba zmiennoprzecinkowa

Przykłady

%results = "stablehlo.custom_call"(%input0) {
  call_target_name = "foo",
  has_side_effect = false,
  backend_config = {bar = 42 : i32},
  api_version = 4 : i32,
  called_computations = [@foo]
} : (tensor<f64>) -> tensor<f64>

dzielenie

Semantyka

Dzieli dzielną lhs i dzielnik tensorów rhs z uwzględnieniem elementów i generuje tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku liczb całkowitych: dzielenie całkowity, które daje iloraz algebraiczny z dowolnym odrzucono część ułamkową.
  • W przypadku jednostek zmiennoprzecinkowych: division w standardzie IEEE-754.
  • W przypadku liczb zespolonych: dzielenie zespolone.
  • W przypadku typów kwantowych:
    • dequantize_op_quantize(divide, lhs, rhs, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs tensor typu całkowitej, zmiennoprzecinkowego, zespolonego lub kwantyzowany tensora na tensor (C1)
(I2) rhs tensor typu całkowitej, zmiennoprzecinkowego, zespolonego lub kwantyzowany tensora na tensor (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu całkowitej, zmiennoprzecinkowego, zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(lhs) = baseline_type(rhs) = baseline_type(result).

Przykłady

// %lhs: [17.1, -17.1, 17.1, -17.1]
// %rhs: [3.0, 3.0, -3.0, -3.0]
%result = "stablehlo.divide"(%lhs, %rhs) : (tensor<4xf32>, tensor<4xf32>) -> tensor<4xf32>
// %result: [5.66666651, -5.66666651, -5.66666651, 5.66666651]

Więcej przykładów

dot_general

Semantyka

Oblicza iloczyn skalarny między wycinkami lhs i rhs, uzyskując wynik Tensor result.

Więcej formalnie, result[result_index] = dot_product, gdzie:

  • lhs_result_dimensions = [d for d in axes(lhs) and d not in lhs_batching_dimensions and d not in lhs_contracting_dimensions].
  • rhs_result_dimensions = [d for d in axes(rhs) and d not in rhs_batching_dimensions and d not in rhs_contracting_dimensions].
  • result_batching_index + result_lhs_index + result_rhs_index = result_index gdzie size(result_batching_index) = size(lhs_batching_dimensions), size(result_lhs_index) = size(lhs_result_dimensions) i size(result_rhs_index) = size(rhs_result_dimensions)
  • transposed_lhs = transpose(lhs, lhs_batching_dimensions + lhs_result_dimensions + lhs_contracting_dimensions).
  • transposed_lhs_slice = slice(transposed_lhs, result_batching_index + result_lhs_index + [:, ..., :]).
  • reshaped_lhs_slice = reshape(transposed_lhs_slice, dims(lhs, lhs_contracting_dimensions)).
  • transposed_rhs = transpose(rhs, rhs_batching_dimensions + rhs_result_dimensions + rhs_contracting_dimensions).
  • transposed_rhs_slice = slice(transposed_rhs, result_batching_index + result_rhs_index + [:, ..., :]).
  • reshaped_rhs_slice = reshape(transposed_rhs_slice, dims(rhs, rhs_contracting_dimensions)).
  • dot_product = reduce( inputs=[multiply(reshaped_lhs_slice, reshaped_rhs_slice)], init_values=[constant(0, element_type(result))], dimensions=range(size(lhs_contracting_dimensions)), body=lambda x, y: add(x, y))

W przypadku typów kwantowych funkcja dequantize_op_quantize( lambda lhs, rhs: dot_general(lhs, rhs, lhs_batching_dimensions, rhs_batching_dimensions, lhs_contracting_dimensions, rhs_contracting_dimensions, precision_config), lhs, rhs, type(result)).

W przypadku typów hybrydowych kwantyzowana wartość wylicza hybrid_dequantize_then_op( lambda lhs, rhs: dot_general(lhs, rhs, lhs_batching_dimensions, rhs_batching_dimensions, lhs_contracting_dimensions, rhs_contracting_dimensions, precision_config), lhs, rhs).

precision_config kontroluje kompromis między szybkością a dokładnością w backendach akceleratora. Może to być jedna z tych wartości (na stronie , semantyka tych wartości wyliczeniowych jest niedostateczna, ale planujemy zająć się tym 755):

  • DEFAULT: najszybsze obliczenie, ale najmniej dokładne przybliżenie oryginalny numer.
  • HIGH: wolniejsze obliczenia, ale dokładniejsze przybliżenie oryginalny numer.
  • HIGHEST: najwolniejsze obliczanie, ale najdokładniejsze przybliżenie oryginalny numer.

DotAlgorithm określa główne właściwości algorytmu używanego do implementacji operacji kropkowej, która określa też dokładność. Jeśli atrybut algorytmu pola, precision_config musi mieć wartość DEFAULT. DotAlgorithms nie mają wartości domyślnej, ponieważ parametry domyślne to implementacja zdefiniowano jego definicję. Dlatego wszystkie pola algorytmu punktowego mogą mieć wartość None, aby określić algorytm z pustymi punktami, który zamiast tego używa wartości precision_config.

Pola DotAlgorithm obejmują:

  • lhs_precision_type i rhs_precision_type, wartości precyzji, które po lewej stronie i Wartość RHS operacji jest zaokrąglana do wartości. Typy dokładności są niezależne od typu pamięci masowej danych wejściowych i wyjściowych.
  • accumulation_type precyzja używana do agregacji.
  • lhs_component_count, rhs_component_count i num_primitive_operations mają zastosowanie przy algorytmie rozkładającym LHS i/lub RHS na ma wiele komponentów i wykonuje wiele elementów podstawowych operacje kropkowe na tych wartości – zwykle w celu emulacji większej dokładności (np. Wykorzystanie typu danych bfloat16 wykorzystujące sztuczną inteligencję do bardziej precyzyjnych obliczeń: bf16_6x tf32_3x itp.). W przypadku algorytmów bez rozkładu wartości te powinna mieć wartość 1.
  • allow_imprecise_accumulation, aby określić mniejszą precyzję agregacji jest dozwolony w przypadku niektórych kroków (np. CUBLASLT_MATMUL_DESC_FAST_ACCUM).

Przykładowe atrybuty DotAlgorithm:

// Inputs are casted to tf32, and then accumulated in f32:
{lhs_precision_type = tf32,
 rhs_precision_type = tf32,
 accumulation_type = f32,
 lhs_component_count = 1,
 rhs_component_count = 1,
 num_primitive_operations = 1,
 allow_imprecise_accumulation = false}


// bf16_6x: each input is decomposed to 3 bf16 components, then 6 dot operations are done on those components, and the result is accumulated in f32.
{lhs_precision_type = bf16,
 rhs_precision_type = bf16,
 accumulation_type = f32,
 lhs_component_count = 3,
 rhs_component_count = 3,
 num_primitive_operations = 6,
 allow_imprecise_accumulation = false}


// Inputs are (casted to) f8e5m2, and we accumulate in f32, but for some steps we may accumulate in lower precision.
{lhs_precision_type = f8e5m2,
 rhs_precision_type = f8e5m2,
 accumulation_type = f32,
 lhs_component_count = 1,
 rhs_component_count = 1,
 num_primitive_operations = 1,
 allow_imprecise_accumulation = true}

To od implementacji zależy, które kombinacje są obsługiwane. W nie gwarantujemy, że każdy algorytm będzie obsługiwany typu akceleratora przez konsumenta StableHLO. Jeśli dany algorytm nie jest można zgłosić błąd, a nie . Weryfikacja StableHLO zapewnia najlepszą weryfikację, co zapobiega algorytmom, o których nie wiemy, że są obsługiwane na jakimkolwiek sprzęcie.

Zobacz xla_data.proto > Algorithm dla niektórych obsługiwanych wartości algorytmu. Zgłoszenie nr 2483 przedstawia plan utworzenia scentralizowany dokument na temat obsługiwanych algorytmów w backendzie.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs kwantowy tensor lub tensor kwantowy (C5–C6), (C9–C10), (C12–C14), (C17–C18), (C20)
(I2) rhs tensor (tensor kwantowy) (C7-C10), (C12-C20)
(I3) lhs_batching_dimensions Jednowymiarowa stała tensora typu si64 (C1), (C3), (C5), (C9), (C12)
(I4) rhs_batching_dimensions Jednowymiarowa stała tensora typu si64 (C1), (C4), (C7), (C9)
(I5) lhs_contracting_dimensions Jednowymiarowa stała tensora typu si64 (C2), (C3), (C6), (C10)
(I6) rhs_contracting_dimensions Jednowymiarowa stała tensora typu si64 (C2), (C4), (C8), (C10), (C16)
(I7) precision_config liczba zmiennoprzecinkowa dla DEFAULT, HIGH i HIGHEST (C11), (C21)
(I8) lhs_precision_type FloatType lub TensorFloat32 (C21)
(I9) rhs_precision_type FloatType lub TensorFloat32 (C21)
(I10) accumulation_type FloatType lub TensorFloat32 (C21)
(I11) lhs_component_count stała typu si32 (C21), (C22)
(I12) rhs_component_count stała typu si32 (C21), (C23)
(I13) num_primitive_operations stała typu si32 (C21), (C24)
(I14) allow_imprecise_accumulation stała typu bool (C21)

Wyniki

Nazwa Typ Ograniczenia
result tensor (tensor kwantowy) (C12), (C14), (C18–C20)

Ograniczenia

  • (C1) size(lhs_batching_dimensions) = size(rhs_batching_dimensions).
  • (C2) size(lhs_contracting_dimensions) = size(rhs_contracting_dimensions).
  • (C3) is_unique(lhs_batching_dimensions + lhs_contracting_dimensions).
  • (C4) is_unique(rhs_batching_dimensions + rhs_contracting_dimensions).
  • (C5) 0 <= lhs_batching_dimensions < rank(lhs).
  • (C6) 0 <= lhs_contracting_dimensions < rank(lhs).
  • (C7) 0 <= rhs_batching_dimensions < rank(rhs).
  • (C8) 0 <= rhs_contracting_dimensions < rank(rhs).
  • (C9) dim(lhs, lhs_batching_dimensions...) = dim(rhs, rhs_batching_dimensions...).
  • (C10) dim(lhs, lhs_contracting_dimensions...) = dim(rhs, rhs_contracting_dimensions...).
  • (C11) size(precision_config) = 2.
  • (C12) shape(result) = dim(lhs, lhs_batching_dimensions) + dim(lhs, lhs_result_dimensions) + dim(rhs, rhs_result_dimensions).
  • Jeśli operacja używa tensorów niekwantowych:
    • (C13) element_type(lhs) = element_type(rhs).
  • Jeśli operacja używa tensorów kwantyzowanych:
    • (C14) is_quantized(lhs) = is_quantized(result) and is_quantized(rhs).
    • (C15) zero_points(rhs) = 0.
    • (C16) Jeśli is_per_axis_quantized(rhs), quantization_dimension(rhs) nie znajduje się w grupie rhs_contracting_dimensions.
    • Jeśli is_quantized(lhs):
    • (C17) storage_type(lhs) = storage_type(rhs).
    • (C18) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result).
    • (C19) Jeśli is_per_tensor_quantized(rhs), is_per_tensor_quantized(result)
    • Jeśli !is_quantized(lhs):
    • (C20) element_type(lhs) = expressed_type(rhs) = element_type(result).
  • Jeśli !is_empty_algorithm(lhs_precision_type, rhs_precision_type, accumulation_type, lhs_component_count, rhs_component_count, num_primitive_operations allow_imprecise_accumulation):
    • (C21) precision_config... = DEFAULT.
    • (C22) 0 < lhs_component_count.
    • (C23) 0 < rhs_component_count.
    • (C24) 0 < num_primitive_operations.

Przykłady

// %lhs: [
//        [[1, 2],
//         [3, 4]],
//        [[5, 6],
//         [7, 8]]
//       ]
// %rhs: [
//        [[1, 0],
//         [0, 1]],
//        [[1, 0],
//         [0, 1]]
//       ]
%result = "stablehlo.dot_general"(%lhs, %rhs) {
  dot_dimension_numbers = #stablehlo.dot<
    lhs_batching_dimensions = [0],
    rhs_batching_dimensions = [0],
    lhs_contracting_dimensions = [2],
    rhs_contracting_dimensions = [1]
  >,
  precision_config = [#stablehlo<precision DEFAULT>, #stablehlo<precision DEFAULT>],
  algorithm = #stablehlo.dot_algorithm<
    lhs_precision_type = tf32,
    rhs_precision_type = tf32,
    accumulation_type = f32,
    lhs_component_count = 1,
    rhs_component_count = 1,
    num_primitive_operations = 1,
    allow_imprecise_accumulation = false
  >
} : (tensor<2x2x2xi64>, tensor<2x2x2xi64>) -> tensor<2x2x2xi64>
// %result: [
//           [[1, 2],
//            [3, 4]],
//           [[5, 6],
//            [7, 8]]
//          ]

Więcej przykładów

dynamic_broadcast_in_dim

Semantyka

Operacja ta jest funkcjonalna identyczna jak broadcast_in_dim op, ale kształt wyniku jest określany dynamicznie przez output_dimensions.

Operacja akceptuje też opcjonalne atrybuty known_expanding_dimensions, known_non_expanding_dimensions w celu wyrażenia statycznej wiedzy o rozwijaniu wymiarów. Jeśli jej nie określisz, przyjmuje się, że wszystkie wymiary mogą się rozwijać.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor (tensor kwantowy) (C1–C2), (C5–C6), (C9)
(I2) output_dimensions Jednowymiarowy tensor typu liczby całkowitej (C7)
(I3) broadcast_dimensions Jednowymiarowy tensor stały typu liczby całkowitej (C2–C6)
(I4) known_expanding_dimensions Jednowymiarowy tensor stały typu liczby całkowitej (C8–C9)
(I5) known_non_expanding_dimensions Jednowymiarowy tensor stały typu liczby całkowitej (C8–C9)

Wyniki

Nazwa Typ Ograniczenia
result tensor (tensor kwantowy) (C1), (C3), (C5–C7)

Ograniczenia

  • (C1) element_type(result) otrzymuje:
    • element_type(operand), jeśli !is_per_axis_quantized(operand).
    • element_type(operand) oprócz quantization_dimension(operand), scales(operand) i zero_points(operand) mogą różnić się od quantization_dimension(result), scales(result) i zero_points(result) w odniesieniu do danych osobowych.
  • (C2) size(broadcast_dimensions) = rank(operand).
  • (C3) 0 <= broadcast_dimensions < rank(result).
  • (C4) is_unique(broadcast_dimensions).
  • (C5) Dla wszystkich d w axes(operand):
    • dim(operand, d) = 1 lub
    • dim(operand, d) = dim(result, broadcast_dimensions[d]).
  • (C6) Jeśli is_per_axis_quantized(result):
    • quantization_dimension(result) = broadcast_dimensions[quantization_dimension(operand)].
    • Jeśli dim(operand, quantization_dimension(operand)) = 1, to scales(result)[i] = scales(operand)[0] and zero_points(result)[i] = zero_points(operand)[0] for i in range(dim(result, quantization_dimension(result)))
  • (C7) size(output_dimensions) = rank(result).
  • (C8) is_unique(known_expanding_dimensions + known_non_expanding_dimensions).
  • (C9) 0 <= known_expanding_dimensions < rank(operand).
  • (C10) 0 <= known_non_expanding_dimensions < rank(operand).

Przykłady

// %operand: [
//            [1, 2, 3]
//           ]
%operand = stablehlo.constant dense<[[1, 2, 3]]> : tensor<1x3xi64>
%output_dimensions = stablehlo.constant dense<[2, 3, 2]> : tensor<3xi64>
%result = "stablehlo.dynamic_broadcast_in_dim"(%operand, %output_dimensions) {
  broadcast_dimensions = array<i64: 2, 1>,
  known_expanding_dimensions = array<i64: 0>,
  known_non_expanding_dimensions = array<i64: 1>
} : (tensor<1x3xi64>, tensor<3xi64>) -> tensor<2x3x2xi64>
// %result: [
//            [
//             [1, 1],
//             [2, 2],
//             [3, 3]
//            ],
//            [
//             [1, 1],
//             [2, 2],
//             [3, 3]
//            ]
//          ]

Więcej przykładów

dynamic_conv

Semantyka

Operacja ta jest funkcjonalna identyczna jak convolution , ale dopełnienie jest określane dynamicznie w parametrze padding.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs kwantowy tensor lub tensor kwantowy (C1), (C10–C11), (C14) (C25), (C26–C27), (C30–C31), (C33)
(I2) rhs tensor (tensor kwantowy) (C1), (C14–C16), (C26–C28), (C30–C33)
(I3) padding 2-wymiarowy tensor typu liczby całkowitej (K4)
(I4) window_strides Jednowymiarowa stała tensora typu si64 (C2–C3)
(I5) lhs_dilation Jednowymiarowa stała tensora typu si64 (C5–C6)
(I6) rhs_dilation Jednowymiarowa stała tensora typu si64 (C7–C8)
(I7) window_reversal Jednowymiarowa stała tensora typu i1 (C9)
(I8) input_batch_dimension stała typu si64 (C10), (C13)
(I9) input_feature_dimension stała typu si64 (C11), (C13–C14)
(I10) input_spatial_dimensions Jednowymiarowa stała tensora typu si64 (C12), (C13)
(I11) kernel_input_feature_dimension stała typu si64 (C14), (C18)
(I12) kernel_output_feature_dimension stała typu si64 (C15-C16), (C18), (C28)
(I13) kernel_spatial_dimensions Jednowymiarowa stała tensora typu si64 (C17–C18)
(I14) output_batch_dimension stała typu si64 (C20)
(I15) output_feature_dimension stała typu si64 (C20), (C29)
(I16) output_spatial_dimensions Jednowymiarowa stała tensora typu si64 (C19–C20)
(I17) feature_group_count stała typu si64 (C11), (C14), (C16), (C21), (C23)
(I18) batch_group_count stała typu si64 (C10), (C15), (C22), (C23)
(I19) precision_config liczba zmiennoprzecinkowa dla DEFAULT, HIGH i HIGHEST (C24)

Wyniki

Nazwa Typ Ograniczenia
result tensor (tensor kwantowy) (C25-C27), (C29), (C31-C33)

Ograniczenia

  • (C1) N = rank(lhs) = rank(rhs).
  • (C2) size(window_strides) = N - 2.
  • (C3) 0 < window_strides.
  • (C4) shape(padding) = [N - 2, 2].
  • (C5) size(lhs_dilation) = N - 2.
  • (C6) 0 < lhs_dilation.
  • (C7) size(rhs_dilation) = N - 2.
  • (C8) 0 < rhs_dilation.
  • (C9) size(window_reversal) = N - 2.
  • (C10) dim(lhs, input_batch_dimension) % batch_group_count = 0.
  • (C11) dim(lhs, input_feature_dimension) % feature_group_count = 0.
  • (C12) size(input_spatial_dimensions) = N - 2.
  • (C13) Podany input_dimensions = [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]:
    • is_unique(input_dimensions).
    • 0 <= input_dimensions < N.
  • (C14) dim(rhs, kernel_input_feature_dimension) = dim(lhs, input_feature_dimension) / feature_group_count.
  • (C15) dim(rhs, kernel_output_feature_dimension) % batch_group_count = 0.
  • (C16) dim(rhs, kernel_output_feature_dimension) % feature_group_count = 0.
  • (C17) size(kernel_spatial_dimensions) = N - 2.
  • (C18) Podany kernel_dimensions = kernel_spatial_dimensions + [kernel_input_feature_dimension] + [kernel_output_feature_dimension]:
    • is_unique(kernel_dimensions).
    • 0 <= kernel_dimensions < N.
  • (C19) size(output_spatial_dimensions) = N - 2.
  • (C20) Podany output_dimensions = [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]:
    • is_unique(output_dimensions).
    • 0 <= output_dimensions < N.
  • (C21) 0 < feature_group_count.
  • (C22) 0 < batch_group_count.
  • (C23) feature_group_count = 1 or batch_group_count = 1.
  • (C24) size(precision_config) = 2.
  • (C25) Parametr dim(result, result_dim) jest zdefiniowany jako:
    • dim(lhs, input_batch_dimension) / batch_group_count, jeśli result_dim = output_batch_dimension.
    • dim(rhs, kernel_output_feature_dimension), jeśli result_dim = output_feature_dimension.
    • num_windows w przeciwnym razie, gdzie:
    • output_spatial_dimensions[spatial_dim] = result_dim.
    • lhs_dim = input_spatial_dimensions[spatial_dim].
    • rhs_dim = kernel_spatial_dimensions[spatial_dim].
    • dilated_input_shape[lhs_dim] = dim(lhs, lhs_dim) = 0 ? 0 : (dim(lhs, lhs_dim) - 1) * lhs_dilation[spatial_dim] + 1.
    • padded_input_shape[lhs_dim] = padding[spatial_dim, 0] + dilated_input_shape[lhs_dim] + padding[spatial_dim, 1].
    • dilated_window_shape[lhs_dim] = dim(rhs, rhs_dim) = 0 ? 0 : (dim(rhs, rhs_dim) - 1) * rhs_dilation[spatial_dim] + 1.
    • is_empty_window[lhs_dim] = padded_input_shape[lhs_dim] = 0 || dilated_window_shape[lhs_dim] > padded_input_shape[lhs_dim].
    • num_windows = is_empty_window[lhs_dim] ? 0 : floor((padded_input_shape[lhs_dim] - dilated_window_shape[lhs_dim]) / window_strides[spatial_dim]) + 1.
  • (C26) rank(result) = N.
  • Jeśli operacja używa tensorów niekwantowych:
    • (C27) element_type(lhs) = element_type(rhs) = element_type(result).
  • Jeśli operacja używa tensorów kwantyzowanych:
    • (C28) is_quantized(lhs) = is_quantized(result) and is_quantized(rhs).
    • (C29) Jeśli is_per_axis_quantized(rhs), a następnie quantization_dimension(rhs) = kernel_output_feature_dimension.
    • (C30) Jeśli is_per_axis_quantized(result), quantization_dimension(result) = output_feature_dimension
    • Jeśli is_quantized(lhs):
    • (C31) storage_type(lhs) = storage_type(rhs).
    • (C32) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result).
    • (C33) Jeśli is_per_tensor_quantized(rhs), is_per_tensor_quantized(result)
    • Jeśli !is_quantized(lhs):
    • (C34) element_type(lhs) = expressed_type(rhs) = element_type(result).

Przykłady

// %lhs: [[
//        [[1], [2], [5], [6]],
//        [[3], [4], [7], [8]],
//        [[10], [11], [14], [15]],
//        [[12], [13], [16], [17]]
//      ]]
//
// %rhs: [
//         [[[1]], [[1]], [[1]]],
//         [[[1]], [[1]], [[1]]],
//         [[[1]], [[1]], [[1]]]
//        ]
// %padding: [[1, 1],
//            [1, 1]]
%result = "stablehlo.dynamic_conv"(%lhs, %rhs, %padding) {
  window_strides = array<i64: 4, 4>,
  lhs_dilation = array<i64: 2, 2>,
  rhs_dilation = array<i64: 1, 1>,
  window_reversal = array<i1: false, false>,
  dimension_numbers = #stablehlo.conv<raw
    input_batch_dimension = 0,
    input_feature_dimension = 3,
    input_spatial_dimensions = [0, 1],
    kernel_input_feature_dimension = 2,
    kernel_output_feature_dimension = 3,
    kernel_spatial_dimensions = [0, 1],
    output_batch_dimension = 0,
    output_feature_dimension = 3,
    output_spatial_dimensions = [1, 2]
  >,
  feature_group_count = 1 : i64,
  batch_group_count = 1 : i64,
  precision_config = [#stablehlo<precision DEFAULT>, #stablehlo<precision DEFAULT>]
} : (tensor<1x4x4x1xi64>, tensor<3x3x1x1xi64>, tensor<2x2xi64>) -> tensor<1x2x2x1xi64>
// %result: [[
//            [[1], [5]],
//            [[10], [14]]
//          ]]

Więcej przykładów

dynamic_gather

Semantyka

Operacja ta jest funkcjonalna identyczna jak zebranie op, gdzie wartością jest slice_sizes.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand kwantowy tensor lub tensor kwantowy (C1), (C7), (C10–C12), (C14)
(I2) start_indices tensor typu liczby całkowitej (C2), (C3), (C13)
(I3) slice_sizes Jednowymiarowy tensor typu liczby całkowitej (C8), (C11–C13)
(I4) offset_dims Jednowymiarowa stała tensora typu si64 (C1), (C4–C5), (C13)
(I5) collapsed_slice_dims Jednowymiarowa stała tensora typu si64 (C1), (C6–C8), (C13)
(I6) start_index_map Jednowymiarowa stała tensora typu si64 (C3), (C9), (C10)
(I7) index_vector_dim stała typu si64 (C2), (C3), (C13)
(I8) indices_are_sorted stała typu i1

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (C5), (C13–C14)

Ograniczenia

  • (C1) rank(operand) = size(offset_dims) + size(collapsed_slice_dims).
  • (C2) 0 <= index_vector_dim <= rank(start_indices).
  • (C3) size(start_index_map) = index_vector_dim < rank(start_indices) ? dim(start_indices, index_vector_dim) : 1.
  • (C4) is_unique(offset_dims) and is_sorted(offset_dims).
  • (C5) 0 <= offset_dims < rank(result).
  • (C6) is_unique(collapsed_slice_dims) and is_sorted(collapsed_slice_dims).
  • (C7) 0 <= collapsed_slice_dims < rank(operand).
  • (C8) slice_sizes[collapsed_slice_dims...] <= 1.
  • (C9) is_unique(start_index_map).
  • (C10) 0 <= start_index_map < rank(operand).
  • (C11) size(slice_sizes) = rank(operand).
  • (C12) 0 <= slice_sizes <= shape(operand).
  • (C13) shape(result) = combine(batch_dim_sizes, offset_dim_sizes), gdzie:
    • batch_dim_sizes = shape(start_indices) oprócz rozmiaru wymiaru nie uwzględniono wartości start_indices odpowiadającej wartości index_vector_dim.
    • offset_dim_sizes = shape(slice_sizes) oprócz rozmiarów wymiarów w kolumnie slice_sizes odpowiadającej wartości collapsed_slice_dims nie są uwzględniane.
    • Funkcja combine umieszcza batch_dim_sizes na osiach odpowiadających batch_dims i offset_dim_sizes przy osiach odpowiadających wartości offset_dims.
  • (C14) element_type(operand) = element_type(result).

Przykłady

// %operand: [
//            [[1, 2], [3, 4], [5, 6], [7, 8]],
//            [[9, 10],[11, 12], [13, 14], [15, 16]],
//            [[17, 18], [19, 20], [21, 22], [23, 24]]
//           ]
// %start_indices: [
//                  [[0, 0], [1, 0], [2, 1]],
//                  [[0, 1], [1, 1], [0, 2]]
//                 ]
// %slize_sizes: [1, 2, 2]
%result = "stablehlo.dynamic_gather"(%operand, %start_indices, %slize_sizes) {
  dimension_numbers = #stablehlo.gather<
    offset_dims = [2, 3],
    collapsed_slice_dims = [0],
    start_index_map = [1, 0],
    index_vector_dim = 2>,
  indices_are_sorted = false
} : (tensor<3x4x2xi64>, tensor<2x3x2xi64>, tensor<3xi64>) -> tensor<2x3x2x2xi64>
// %result: [
//            [
//              [[1, 2], [3, 4]],
//              [[3, 4], [5, 6]],
//              [[13, 14], [15, 16]]
//            ],
//            [
//              [[9, 10], [11, 12]],
//              [[11, 12], [13, 14]],
//              [[17, 18], [19, 20]]
//            ]
//          ]

Więcej przykładów

dynamic_iota

Semantyka

Operacja ta jest funkcjonalna identyczna jak Iota op, ale kształt wyniku jest określany dynamicznie przez output_shape.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) output_shape Jednowymiarowy tensor typu liczby całkowitej (C1), (C2)
(I2) iota_dimension si64 (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu całkowitej, zmiennoprzecinkowego, zespolonego lub kwantyzowany tensora według tensora (K2)

Ograniczenia

  • (C1) 0 <= iota_dimension < size(output_shape).
  • (C2) rank(result) = size(output_shape).

Przykłady

%output_shape = stablehlo.constant dense<[4, 5]> : tensor<2xi64>
%result = "stablehlo.dynamic_iota"(%output_shape) {
  iota_dimension = 0 : i64
} : (tensor<2xi64>) -> tensor<4x5xi64>
// %result: [
//           [0, 0, 0, 0, 0],
//           [1, 1, 1, 1, 1],
//           [2, 2, 2, 2, 2],
//           [3, 3, 3, 3, 3]
//          ]

Więcej przykładów

dynamic_pad

Semantyka

Operacja ta jest funkcjonalna identyczna jak Pad op, ale z edge_padding_low, edge_padding_high i interior_padding określane dynamicznie jako wartości.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand kwantowy tensor lub tensor kwantowy (C1), (C2), (C4)
(I2) padding_value Tensor 0-wymiarowy lub kwantowy tensor na tensor (C1)
(I3) edge_padding_low Jednowymiarowy tensor typu liczby całkowitej (C1), (C4)
(I4) edge_padding_high Jednowymiarowy tensor typu liczby całkowitej (C1), (C4)
(I5) interior_padding Jednowymiarowy tensor typu liczby całkowitej (C2–C4)

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (C3–C6)

Ograniczenia

  • (C1) element_type(operand) = element_type(padding_value) = element_type(result).
  • (C2) size(edge_padding_low) = size(edge_padding_high) = size(interior_padding) = rank(operand).
  • (C3) 0 <= interior_padding.
  • (C4) shape(result) = shape(operand) + edge_padding_low + max(shape(operand) - 1, 0) * interior_padding + edge_padding_high.

Przykłady

// %operand: [
//            [1, 2, 3],
//            [4, 5, 6]
//           ]
// %padding_value: 0
// %edge_padding_low: [0, 1]
// %edge_padding_high: [2, 1]
// %interior_padding: [1, 2]
%result = "stablehlo.dynamic_pad"(%operand, %padding_value,
  %edge_padding_low, %edge_padding_high, %interior_padding
) : (tensor<2x3xi64>, tensor<i64>, tensor<2xi64>, tensor<2xi64>, tensor<2xi64>) -> tensor<5x9xi64>
// %result: [
//           [0, 1, 0, 0, 2, 0, 0, 3, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0],
//           [0, 4, 0, 0, 5, 0, 0, 6, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0]
//          ]

Więcej przykładów

dynamic_reshape

Semantyka

Operacja ta jest funkcjonalna identyczna jak kształtowanie op, ale kształt wyniku jest określany dynamicznie przez output_shape.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor (tensor kwantowy) (K1–C3)
(I2) output_shape Jednowymiarowy tensor typu liczby całkowitej (K4)

Wyniki

Nazwa Typ Ograniczenia
result tensor (tensor kwantowy) (K1–C4)

Ograniczenia

  • (C1) element_type(result) otrzymuje:
    • element_type(operand), jeśli !is_per_axis_quantized(operand).
    • element_type(operand) oprócz quantization_dimension(operand) i Wartość quantization_dimension(result) może się różnić. W przeciwnym razie.
  • (C2) size(operand) = size(result).
  • (C3) Jeśli is_per_axis_quantized(operand):
    • reduce(dims(operand, [0, 1, ..., quantization_dimension(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [0, 1, ..., quantization_dimension(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y).
    • dim(operand, quantization_dimension(operand)) = dim(result, quantization_dimension(result)).
    • reduce(dims(operand, [quantization_dimension(operand) + 1, ..., rank(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [quantization_dimension(result) + 1, ..., rank(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y).
  • (C4) size(output_shape) = rank(result).

Przykłady

// %operand: [[1, 2, 3], [4, 5, 6]]
// %output_shape: [3, 2]
%result = "stablehlo.dynamic_reshape"(%operand, %output_shape) : (tensor<2x3xi64>, tensor<2xi64>) -> tensor<3x2xi64>
// %result: [[1, 2], [3, 4], [5, 6]]

Więcej przykładów

dynamic_slice

Semantyka

Wyodrębnia wycinek z operand przy użyciu dynamicznie obliczanych indeksów początkowych i tworzy tensor result. start_indices zawierają początkowe indeksy wycinek dla każdego wymiaru podlegającego potencjalnej korektie oraz slice_sizes będą zawierały rozmiary wycinka dla każdego wymiaru. Bardziej oficjalnie, result[result_index] = operand[operand_index], gdzie:

  • adjusted_start_indices = clamp(0, start_indices, shape(operand) - slice_sizes).
  • operand_index = adjusted_start_indices + result_index.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand kwantowy tensor lub tensor kwantowy (C1), (C2), (C4)
(I2) start_indices zmienna liczba tensorów 0-wymiarowych typu liczby całkowitej (C2), (C3)
(I3) slice_sizes Jednowymiarowa stała tensora typu si64 (C2), (C4), (C5)

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (C1), (C5)

Ograniczenia

  • (C1) element_type(operand) = element_type(result).
  • (C2) size(start_indices) = size(slice_sizes) = rank(operand).
  • (C3) same(type(start_indices...)).
  • (C4) 0 <= slice_sizes <= shape(operand).
  • (C5) shape(result) = slice_sizes.

Przykłady

// %operand: [
//            [0, 0, 1, 1],
//            [0, 0, 1, 1],
//            [0, 0, 0, 0],
//            [0, 0, 0, 0]
//           ]
// %start_indices0: -1
// %start_indices1: 3
%result = "stablehlo.dynamic_slice"(%operand, %start_indices0, %start_indices1) {
  slice_sizes = array<i64: 2, 2>
} : (tensor<4x4xi32>, tensor<i64>, tensor<i64>) -> tensor<2x2xi32>
// %result: [
//           [1, 1],
//           [1, 1]
//          ]

Więcej przykładów

dynamic_update_slice

Semantyka

Generuje tensor result, który jest równy tensorowi operand z tą różnicą, że: wycinek zaczynający się od start_indices zostanie zaktualizowany o wartości w update. Bardziej oficjalnie termin result[result_index] jest zdefiniowany jako:

  • update[update_index], jeśli 0 <= update_index < shape(update), gdzie:
    • adjusted_start_indices = clamp(0, start_indices, shape(operand) - shape(update)).
    • update_index = result_index - adjusted_start_indices.
  • W przeciwnym razie: operand[result_index].

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand kwantowy tensor lub tensor kwantowy (C1–C4), (C6)
(I2) update kwantowy tensor lub tensor kwantowy (C2), (C3), (C6)
(I3) start_indices zmienna liczba tensorów 0-wymiarowych typu liczby całkowitej (C4), (C5)

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (C1)

Ograniczenia

  • (C1) type(operand) = type(result).
  • (C2) element_type(update) = element_type(operand).
  • (C3) rank(update) = rank(operand).
  • (C4) size(start_indices) = rank(operand).
  • (C5) same(type(start_indices...)).
  • (C6) 0 <= shape(update) <= shape(operand).

Przykłady

// %operand: [
//            [1, 1, 0, 0],
//            [1, 1, 0, 0],
//            [1, 1, 1, 1],
//            [1, 1, 1, 1]
//           ]
// %update: [
//           [1, 1],
//           [1, 1]
//          ]
// %start_indices0: -1
// %start_indices1: 3
%result = "stablehlo.dynamic_update_slice"(%operand, %update, %start_indices0, %start_indices1)
  : (tensor<4x4xi32>, tensor<2x2xi32>, tensor<i64>, tensor<i64>) -> tensor<4x4xi32>
// %result: [
//           [1, 1, 1, 1],
//           [1, 1, 1, 1],
//           [1, 1, 1, 1],
//           [1, 1, 1, 1]
//          ]

Więcej przykładów

wykładniczo

Semantyka

Wykonuje operację wykładniczą według elementu na tensorze operand i tworzy Tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku jednostek zmiennoprzecinkowych: exp w standardzie IEEE-754.
  • W przypadku liczb zespolonych: zespolone wykładnicze.
  • W przypadku typów kwantowych: dequantize_op_quantize(exponential, operand, type(result))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %operand: [[0.0, 1.0], [2.0, 3.0]]
%result = "stablehlo.exponential"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [[1.0, 2.7182818284590451], [7.3890560989306504, 20.085536923187668]]

Więcej przykładów

exponential_minus_one

Semantyka

Wykonuje wykładniczą operację wykładniczą z minusem 1 operacji na tensorze operand i generuje tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku jednostek zmiennoprzecinkowych: expm1 w standardzie IEEE-754.
  • W przypadku liczb zespolonych: zespolona wykładnicza wartość minus 1.
  • W przypadku typów kwantowych: dequantize_op_quantize(exponential_minus_one, operand, type(result))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %operand: [0.0, 1.0]
%result = "stablehlo.exponential_minus_one"(%operand) : (tensor<2xf64>) -> tensor<2xf64>
// %result: [0.0, 1.71828187]

Więcej przykładów

FFT

Semantyka

Wykonuje transformacje kierunkowe i odwrotne Fouriera dla rzeczywistych i złożonych wejścia/wyjścia.

fft_type jest jedną z tych:

  • FFT: przekierowywanie szybkiego przejścia do przodu.
  • IFFT: odwrotna FFT.
  • RFFT: przesuwanie widoku w kierunku rzeczywistym do złożonego.
  • IRFFT: odwrotny FFT w czasie rzeczywistym (tzn. przyjmuje złożone wartości, zwraca wartości rzeczywiste).

Bardziej oficjalnie, biorąc pod uwagę funkcję fft, która przyjmuje jednowymiarowe tensory jako dane wejściowe powoduje generowanie tensorów jednowymiarowych tego samego typu co i oblicza dyskretną transformatę Fouriera:

W przypadku funkcji fft_type = FFT result jest zdefiniowany jako końcowy wynik szeregu L w których przypadku L = size(fft_length). Przykład dla L = 3:

  • result1[i0, ..., :] = fft(operand[i0, ..., :]).
  • result2[i0, ..., :, iR-1] = fft(result1[i0, ..., :, iR-1]).
  • result[i0, ..., :, iR-2, iR-1] = fft(result2[i0, ..., :, iR-2, iR-1]).

Ponadto funkcja ifft, która ma ten sam typ podpisu i oblicza odwrotność fft:

W przypadku funkcji fft_type = IFFT wartość result jest zdefiniowana jako odwrotność obliczeń za fft_type = FFT. Przykład dla L = 3:

  • result1[i0, ..., :, iR-2, iR-1] = ifft(operand[i0, ..., :, iR-2, iR-1]).
  • result2[i0, ..., :, iR-1] = ifft(result1[i0, ..., :, iR-1]).
  • result[i0, ..., :] = ifft(result2[i0, ..., :]).

Ponadto biorąc pod uwagę funkcję rfft, która przyjmuje jednowymiarowe tensory typu zmiennoprzecinkowego, tworzy jednowymiarowe tensory złożonych typów ta sama semantyka zmiennoprzecinkowa i działa w ten sposób:

  • rfft(real_operand) = truncated_result, gdzie
  • complex_operand... = (real_operand..., 0.0).
  • complex_result = fft(complex_operand).
  • truncated_result = complex_result[:(rank(complex_result) / 2 + 1)].

(Przy obliczaniu dyskretnej transformacji Fouriera dla prawdziwych operandów pierwszy Elementy N/2 + 1 wyniku jednoznacznie definiują resztę wyniku, więc wynik funkcji rfft jest skracany, aby uniknąć przetwarzania zbędnych elementów.

W przypadku funkcji fft_type = RFFT result jest zdefiniowany jako końcowy wynik szeregu L w których przypadku L = size(fft_length). Przykład dla L = 3:

  • result1[i0, ..., :] = rfft(operand[i0, ..., :]).
  • result2[i0, ..., :, iR-1] = fft(result1[i0, ..., :, iR-1]).
  • result[i0, ..., :, iR-2, iR-1] = fft(result2[i0, ..., :, iR-2, iR-1]).

Natomiast funkcja irfft, która ma ten sam typ podpisu i oblicza odwrotność rfft:

W przypadku funkcji fft_type = IRFFT wartość result jest zdefiniowana jako odwrotność obliczeń za fft_type = RFFT. Przykład dla L = 3:

  • result1[i0, ..., :, iR-2, iR-1] = ifft(operand[i0, ..., :, iR-2, iR-1]).
  • result2[i0, ..., :, iR-1] = ifft(result1[i0, ..., :, iR-1]).
  • result[i0, ..., :] = irfft(result2[i0, ..., :]).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub zespolonego (C1), (C2), (C4), (C5)
(I2) fft_type wyliczenie FFT, IFFT, RFFT i IRFFT (C2), (C5)
(I3) fft_length Jednowymiarowa stała tensora typu si64 (C1), (C3), (C4)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub zespolonego (C2), (C4), (C5)

Ograniczenia

  • (C1) size(fft_length) <= rank(operand).
  • (C2) Relacja między typami elementów operand i result jest zróżnicowana:
    • Jeśli fft_type = FFT, element_type(operand) i element_type(result) mają taki sam typ złożony.
    • Jeśli fft_type = IFFT, element_type(operand) i element_type(result) mają taki sam typ złożony.
    • Jeśli fft_type = RFFT, element_type(operand) jest typu zmiennoprzecinkowego, element_type(result) to typ złożony z tej samej liczby zmiennoprzecinkowej semantyka.
    • Jeśli fft_type = IRFFT, element_type(operand) jest typem złożonym i element_type(result) to typ zmiennoprzecinkowy z tą samą liczbą zmiennoprzecinkową semantyka.
  • (C3) 1 <= size(fft_length) <= 3.
  • (C4) Jeśli wśród funkcji operand i result, występuje tensor real argumentu typu zmiennoprzecinkowego, a następnie shape(real)[-size(fft_length):] = fft_length.
  • (C5) shape(result) = shape(operand) oprócz:
    • Jeśli fft_type = RFFT, dim(result, -1) = dim(operand, -1) = 0 ? 0 : dim(operand, -1) / 2 + 1
    • Jeśli fft_type = IRFFT, dim(operand, -1) = dim(result, -1) = 0 ? 0 : dim(result, -1) / 2 + 1

Przykłady

// %operand: [(1.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0)]
%result = "stablehlo.fft"(%operand) {
  fft_type = #stablehlo<fft_type FFT>,
  fft_length = array<i64: 4>
} : (tensor<4xcomplex<f32>>) -> tensor<4xcomplex<f32>>
// %result: [(1.0, 0.0), (1.0, 0.0), (1.0, 0.0), (1.0, 0.0)]

piętro

Semantyka

Wykonuje ruch minimalny dla danego elementu tensora operand i generuje tensor result. Implementuje operację roundToIntegralTowardNegative z standardu IEEE-754 specyfikacji. W przypadku typów kwantowych dequantize_op_quantize(floor, operand, type(result))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %operand: [-0.8166, -0.2530, 0.2530, 0.8166, 2.0]
%result = "stablehlo.floor"(%operand) : (tensor<5xf32>) -> tensor<5xf32>
// %result: [-1.0, -1.0, 0.0, 0.0, 2.0]

Więcej przykładów

zbierać

Semantyka

Zbiera wycinki z tensora operand z przesunięć określonych w polu start_indices i tworzy tensor result.

Ten diagram pokazuje, jak elementy w result są mapowane na elementy w operand. Diagram wybiera kilka przykładowych elementów result i dokładnie wyjaśnia, którym indeksom operand odpowiadają indeksy.

zbierać

Więcej formalnie result[result_index] = operand[operand_index], gdzie:

  • batch_dims = [d for d in axes(result) and d not in offset_dims].
  • batch_index = result_index[batch_dims...].
  • start_index jest zdefiniowany jako:
    • start_indices[bi0, ..., :, ..., biN], gdzie bi to pojedyncze elementy w batch_index i : są wstawione w indeksie index_vector_dim, jeśli index_vector_dim < rank(start_indices).
    • W przeciwnym razie: [start_indices[batch_index]].
  • W przypadku usługi d_operand w axes(operand),
    • full_start_index[d_operand] = clamp(start_index[d_start], 0, dim(operand, d_operand) - slice_sizes[d_operand]) jeśli d_operand = start_index_map[d_start].
    • W przeciwnym razie: full_start_index[d_operand] = 0.
  • W przypadku usługi d_operand w axes(operand),
    • full_batching_index[d_operand] = batch_index[d_start - (d_start < index_vector_dim ? 0 : 1)] jeśli d_operand = operand_batching_dims[i_batching] i d_start = start_indices_batching_dims[i_batching]
    • W przeciwnym razie: full_batching_index[d_operand] = 0.
  • offset_index = result_index[offset_dims...].
  • full_offset_index = [oi0, ..., 0, ..., oiN], gdzie oi to osoby indywidualne elementów w argumencie offset_index, a 0 jest wstawiony w indeksach z collapsed_slice_dims i operand_batching_dims.
  • operand_index = full_start_index + full_batching_index + full_offset_index

Jeśli indices_are_sorted to true, implementacja może przyjąć, że start_indices są posortowane w odniesieniu do start_index_map, w przeciwnym razie zachowanie jest niezdefiniowane. Mówiąc bardziej oficjalnie, pozdrawiamy wszystkich i1 < i2 z indices(result). full_start_index(i1) <= full_start_index(i2)

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand kwantowy tensor lub tensor kwantowy (C1), (C8), (C11), (C17), (C19–C21), (C23)
(I2) start_indices tensor typu liczby całkowitej (C2–C3), (C14), (C17), (C22)
(I3) offset_dims Jednowymiarowa stała tensora typu si64 (C1), (C4–C5), (C22)
(I4) collapsed_slice_dims Jednowymiarowa stała tensora typu si64 (C1), (C6–C9), (C22)
(I5) operand_batching_dims Jednowymiarowa stała tensora typu si64 (C1), (C6), (C10–C12), (C16–C18), (C22)
(I6) start_indices_batching_dims Jednowymiarowa stała tensora typu si64 (C13–C17)
(I7) start_index_map Jednowymiarowa stała tensora typu si64 (C3), (C18–C19)
(I8) index_vector_dim stała typu si64 (C2–C3), (C15), (C22)
(I9) slice_sizes Jednowymiarowa stała tensora typu si64 (C9), (C12), (C20–C22)
(I10) indices_are_sorted stała typu i1

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (C5), (C22-C23)

Ograniczenia

  • (C1) rank(operand) = size(offset_dims) + size(collapsed_slice_dims) + size(operand_batching_dims).
  • (C2) 0 <= index_vector_dim <= rank(start_indices).
  • (C3) size(start_index_map) = index_vector_dim < rank(start_indices) ? dim(start_indices, index_vector_dim) : 1.
  • (C4) is_unique(offset_dims) and is_sorted(offset_dims).
  • (C5) 0 <= offset_dims < rank(result).
  • (C6) is_unique(concatenate(collapsed_slice_dims, operand_batching_dims))
  • (C7) is_sorted(collapsed_slice_dims).
  • (C8) 0 <= collapsed_slice_dims < rank(operand).
  • (C9) slice_sizes[collapsed_slice_dims...] <= 1.
  • (C10) is_sorted(operand_batching_dims).
  • (C11) 0 <= operand_batching_dims < rank(operand).
  • (C12) slice_sizes[operand_batching_dims...] <= 1.
  • (C13) is_unique(start_indices_batching_dims).
  • (C14) 0 <= start_indices_batching_dims < rank(start_indices).
  • (C15) index_vector_dim not in start_indices_batching_dims.
  • (C16) size(operand_batching_dims) == size(start_indices_batching_dims).
  • (C17) dim(operand, operand_batching_dims...) = dim(start_indices, start_indices_batching_dims...).
  • (C18) is_unique(concatenate(start_index_map, operand_batching_dims)).
  • (C19) 0 <= start_index_map < rank(operand).
  • (C20) size(slice_sizes) = rank(operand).
  • (C21) 0 <= slice_sizes <= shape(operand).
  • (C22) shape(result) = combine(batch_dim_sizes, offset_dim_sizes), gdzie:
    • batch_dim_sizes = shape(start_indices) oprócz rozmiaru wymiaru nie uwzględniono wartości start_indices odpowiadającej wartości index_vector_dim.
    • offset_dim_sizes = slice_sizes oprócz rozmiarów wymiarów w slice_sizes odpowiadające wartościom collapsed_slice_dims i operand_batching_dims nie są uwzględnione.
    • Funkcja combine umieszcza batch_dim_sizes na osiach odpowiadających batch_dims i offset_dim_sizes przy osiach odpowiadających wartości offset_dims.
  • (C23) element_type(operand) = element_type(result).

Przykłady

// %operand: [
//            [
//             [[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, 28], [29, 30], [31, 32]],
//             [[33, 34], [35, 36], [37, 38], [39, 40]],
//             [[41, 42], [43, 44], [45, 46], [47, 48]]
//            ]
//           ]
// %start_indices: [
//                  [
//                   [[0, 0], [1, 0], [2, 1]],
//                   [[0, 1], [1, 1], [0, 9]]
//                  ],
//                  [
//                   [[0, 0], [2, 1], [2, 2]],
//                   [[1, 2], [0, 1], [1, 0]]
//                  ]
//                 ]
%result = "stablehlo.gather"(%operand, %start_indices) {
  dimension_numbers = #stablehlo.gather<
    offset_dims = [3, 4],
    collapsed_slice_dims = [1],
    operand_batching_dims = [0],
    start_indices_batching_dims = [1],
    start_index_map = [2, 1],
    index_vector_dim = 3>,
  slice_sizes = array<i64: 1, 1, 2, 2>,
  indices_are_sorted = false
} : (tensor<2x3x4x2xi32>, tensor<2x2x3x2xi64>) -> tensor<2x2x3x2x2xi32>
// %result: [
//           [
//            [
//             [[1, 2], [3, 4]],
//             [[3, 4], [5, 6]],
//             [[13, 14], [15, 16]]
//            ],
//            [
//             [[33, 34], [35, 36]],
//             [[35, 36], [37, 38]],
//             [[41, 42], [43, 44]]
//            ]
//           ],
//           [
//            [
//             [[1, 2], [3, 4]],
//             [[13, 14], [15, 16]],
//             [[21, 22], [23, 24]]
//            ],
//            [
//             [[43, 44], [45, 46]],
//             [[33, 34], [35, 36]],
//             [[27, 28], [29, 30]]
//            ]
//           ]
//          ]

Więcej przykładów

get_dimension_size

Semantyka

Zwraca rozmiar podanej wartości dimension w: operand. Bardziej oficjalnie, result = dim(operand, dimension) Semantyka dotyczy tylko kształtu danego typu. Typ elementu może mieć dowolną wartość.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor (tensor kwantowy) (C1)
(I2) dimension stała typu si64 (C1)

Wyniki

Nazwa Typ
result Tensor 0-wymiarowy typu si32

Ograniczenia

  • (C1) 0 <= dimension < rank(operand).

Przykłady

// %operand: [[1, 2, 3], [4, 5, 6]]
%result = "stablehlo.get_dimension_size"(%operand) {
  dimension = 1 : i64
} : (tensor<2x3xi64>) -> tensor<i32>
// %result: 3

Więcej przykładów

get_tuple_element

Semantyka

Wyodrębnia element w pozycji index krotki operand i tworzy result Więcej formalnie: result = operand[index].

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand krotka (C1), (C2)
(I2) index stała typu si32 (C1), (C2)

Wyniki

Nazwa Typ Ograniczenia
result dowolny obsługiwany typ (K2)

Ograniczenia

  • (C1) 0 <= index < size(operand).
  • (C2) type(result) = tuple_element_types(operand)[index].

Przykłady

// %operand: ([1.0, 2.0], (3))
  index = 0 : i32
} : (tuple<tensor<2xf32>, tuple<tensor<i32>>>) -> tensor<2xf32>
// %result: [1.0, 2.0]

Więcej przykładów

jeśli

Semantyka

Podaje dane wyjściowe w wyniku wykonania dokładnie 1 funkcji z zakresu true_branch lub false_branch w zależności od wartości pred. Więcej formalnie: result = pred ? true_branch() : false_branch().

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) pred Tensor 0-wymiarowy typu i1
(I2) true_branch funkcja (K1–C3)
(I3) false_branch funkcja (C1), (C2)

Wyniki

Nazwa Typ Ograniczenia
results zmiennoprzecinkowa liczba tensorów, skwantyzowanych tensorów lub tokenów (K3)

Ograniczenia

  • (C1) input_types(true_branch) = input_types(false_branch) = [].
  • (C2) output_types(true_branch) = output_types(false_branch).
  • (C3) type(results...) = output_types(true_branch).

Przykłady

// %result_true_branch: 10
// %result_false_branch: 11
// %pred: true
%result = "stablehlo.if"(%pred) ({
  "stablehlo.return"(%result_true_branch) : (tensor<i32>) -> ()
}, {
  "stablehlo.return"(%result_false_branch) : (tensor<i32>) -> ()
}) : (tensor<i1>) -> tensor<i32>
// %result: 10

Więcej przykładów

Obraz

Semantyka

Wyodrębnia część urojoną (oparte na elementach) z operand i tworzy Tensor result. Bardziej oficjalnie w przypadku każdego elementu x: imag(x) = is_complex(x) ? imaginary_part(x) : constant(0, element_type(result))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub zespolonego (C1), (C2)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego (C1), (C2)

Ograniczenia

  • (C1) shape(result) = shape(operand).
  • (C2) Parametr element_type(result) jest zdefiniowany jako:
    • complex_element_type(element_type(operand)), jeśli is_complex(operand).
    • W przeciwnym razie: element_type(operand).

Przykłady

// %operand: [(1.0, 2.0), (3.0, 4.0)]
%result = "stablehlo.imag"(%operand) : (tensor<2xcomplex<f32>>) -> tensor<2xf32>
// %result: [2.0, 4.0]

Więcej przykładów

In-Feed

Semantyka

Odczytuje dane z In-Feed i tworzy results.

Semantyka etykiety infeed_config jest zdefiniowana przez implementację.

results składa się z wartości ładunku, które pojawiają się jako pierwsze, i tokena, który pojawia się na końcu. W przyszłości planujemy podzielić ładunek i token na dwa osobne dane wyjściowe, aby zwiększyć ich przejrzystość; (#670).

Dane wejściowe

Etykieta Nazwa Typ
(I1) token token
(I2) infeed_config stała typu string

Wyniki

Nazwa Typ Ograniczenia
results zmiennoprzecinkowa liczba tensorów, skwantyzowanych tensorów lub tokenów (K1–C3)

Ograniczenia

  • (C1) 0 < size(results).
  • (C2) is_empty(result[:-1]) lub is_tensor(type(results[:-1])).
  • (C3) is_token(type(results[-1])).

Przykłady

// %token: !stablehlo.token
// infeed_queue[0]: [[1, 2], [3, 4]]
// infeed_queue[1]: [[5, 6], [7, 8]]
%results0:2 = "stablehlo.infeed"(%token) {
  infeed_config = ""
} : (!stablehlo.token) -> (tensor<2x2xi64>, !stablehlo.token)
// results0#0: [[1, 2], [3, 4]]
%results1:2 = "stablehlo.infeed"(%token) {
  infeed_config = ""
} : (!stablehlo.token) -> (tensor<2x2xi64>, !stablehlo.token)
// results1#0: [[5, 6], [7, 8]]

Więcej przykładów

Jota

Semantyka

Wypełnia tensor output wartościami w kolejności rosnącej, zaczynając od zera w wymiarze iota_dimension. Bardziej oficjalnie,

output[output_index] = constant(is_quantized(output) ? quantize(output_index[iota_dimension], element_type(output)) : output_index[iota_dimension], element_type(output)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) iota_dimension si64 (C1)

Wyniki

Nazwa Typ Ograniczenia
output tensor typu całkowitej, zmiennoprzecinkowego, zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) 0 <= iota_dimension < rank(output).

Przykłady

%output = "stablehlo.iota"() {
  iota_dimension = 0 : i64
} : () -> tensor<4x5xi32>
// %output: [
//           [0, 0, 0, 0, 0],
//           [1, 1, 1, 1, 1],
//           [2, 2, 2, 2, 2],
//           [3, 3, 3, 3, 3]
//          ]

%output = "stablehlo.iota"() {
  iota_dimension = 1 : i64
} : () -> tensor<4x5xi32>
// %output: [
//           [0, 1, 2, 3, 4],
//           [0, 1, 2, 3, 4],
//           [0, 1, 2, 3, 4],
//           [0, 1, 2, 3, 4]
//          ]

Więcej przykładów

is_finite

Semantyka

Sprawdza, czy wartość w polu x jest skończona (tzn. czy nie jest skończona) +Inf, -Inf lub NaN) i tworzy tensor y. Implementuje isFinite zgodnie ze specyfikacją IEEE-754. W przypadku typów kwantowych wynik jest zawsze true.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) x tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C1)

Wyniki

Nazwa Typ Ograniczenia
y tensor typu wartości logicznej (C1)

Ograniczenia

  • (C1) shape(x) = shape(y).

Przykłady

// Logical values: -Inf, +Inf, NaN, ...
// %x: [0xFFF0000000000000, 0x7FF0000000000000, 0x7FF8000000000000, -10.0, -0.0, 0.0, 10.0]
%y = "stablehlo.is_finite"(%x) : (tensor<7xf64) -> tensor<7xi1>
// %y: [false, false, false, true, true, true, true]

Więcej przykładów

log

Semantyka

Wykonuje operację logarytmiczną z uwzględnieniem elementów na tensorze operand i tworzy Tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku jednostek zmiennoprzecinkowych: log w standardzie IEEE-754.
  • W przypadku liczb zespolonych: logarytm zespolony.
  • W przypadku typów kwantowych: dequantize_op_quantize(log, operand, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %operand: [[1.0, 2.0], [3.0, 4.0]]
%result = "stablehlo.log"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [[0.0, 0.69314718055994529], [1.0986122886681098, 1.3862943611198906]]

Więcej przykładów

log_plus_one

Semantyka

Wykonuje logarytm z elementami oraz 1 operację na tensorze operand i generuje tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku jednostek zmiennoprzecinkowych: logp1 w standardzie IEEE-754.
  • W przypadku liczb zespolonych: logarytm zespolony plus 1.
  • W przypadku typów kwantowych: dequantize_op_quantize(log_plus_one, operand, type(result))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %operand: [0.0, -0.999, 7.0, 6.38905621, 15.0]
%result = "stablehlo.log_plus_one"(%operand) : (tensor<5xf64>) -> tensor<5xf64>
// %result: [0.0, -6.90776825, 2.07944155, 2.0, 2.77258873]

Więcej przykładów

logistyka

Semantyka

Wykonuje operacje logistyczne związane z elementami na tensorze operand i tworzy Tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku jednostek zmiennoprzecinkowych: division(1, addition(1, exp(-x))) w standardzie IEEE-754.
  • W przypadku liczb zespolonych: logistyka złożona.
  • W przypadku typów kwantowych: dequantize_op_quantize(logistic, operand, type(result))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %operand: [[0.0, 1.0], [2.0, 3.0]]
%result = "stablehlo.logistic"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [[0.5, 0.73105858], [0.88079708, 0.95257413]]

Więcej przykładów

mapa

Semantyka

Stosuje funkcję mapy computation do inputs wzdłuż linii dimensions oraz generuje tensor result.

Więcej formalnie: result[result_index] = computation(inputs...[result_index]).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) inputs liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów na tensor (K1–C4)
(I2) dimensions Jednowymiarowa stała tensora typu si64 (K3)
(I3) computation funkcja (K4)

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (C1), (C4)

Ograniczenia

  • (C1) shape(inputs...) = shape(result).
  • (C2) 0 < size(inputs) = N.
  • (C3) dimensions = range(rank(inputs[0])).
  • (C4) computation ma typ (tensor<E0>, ..., tensor<EN-1>) -> tensor<E'> gdzie Ei = element_type(inputs[i]) i E' = element_type(result).

Przykłady

// %input0: [[0, 1], [2, 3]]
// %input1: [[4, 5], [6, 7]]
%result = "stablehlo.map"(%input0, %input1) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = stablehlo.multiply %arg0, %arg1 : tensor<i64>
    stablehlo.return %0 : tensor<i64>
}) {
  dimensions = array<i64: 0, 1>
} : (tensor<2x2xi64>, tensor<2x2xi64>) -> tensor<2x2xi64>
// %result: [[0, 5], [12, 21]]

Więcej przykładów

maksimum

Semantyka

Wykonuje operację maksymalizacji według elementu na tensorach lhs i rhs oraz generuje Tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku wartości logicznych: logiczny LUB.
  • W przypadku liczb całkowitych: maksymalna liczba całkowita.
  • W przypadku jednostek zmiennoprzecinkowych: maximum w standardzie IEEE-754.
  • W przypadku liczb zespolonych: maksimum leksykograficzne dla pary (real, imaginary). Nakładanie kolejności na liczby zespolone wymaga zaskakującej semantyki, więc w przyszłości planujemy wycofać obsługę liczb zespolonych dla tej operacji (#560).
  • W przypadku typów kwantowych:
    • dequantize_op_quantize(maximum, lhs, rhs, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs kwantowy tensor lub tensor kwantowy (C1)
(I2) rhs kwantowy tensor lub tensor kwantowy (C1)

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (C1)

Ograniczenia

  • (C1) baseline_type(lhs) = baseline_type(rhs) = baseline_type(result).

Przykłady

// %lhs: [[1, 2], [7, 8]]
// %rhs: [[5, 6], [3, 4]]
%result = "stablehlo.maximum"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 6], [7, 8]]

Więcej przykładów

minimum

Semantyka

Wykonuje działania min. według elementu na tensorach lhs i rhs oraz generuje Tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku wartości logicznych: logiczny ORAZ.
  • W przypadku liczb całkowitych: minimalna liczba całkowita.
  • W przypadku jednostek zmiennoprzecinkowych: minimum w standardzie IEEE-754.
  • W przypadku liczb zespolonych: leksykograficzne minimum dla pary (real, imaginary). Nakładanie kolejności na liczby zespolone wymaga zaskakującej semantyki, więc w przyszłości planujemy wycofać obsługę liczb zespolonych dla tej operacji (#560).
  • W przypadku typów kwantowych:
    • dequantize_op_quantize(minimum, lhs, rhs, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs kwantowy tensor lub tensor kwantowy (C1)
(I2) rhs kwantowy tensor lub tensor kwantowy (C1)

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (C1)

Ograniczenia

  • (C1) baseline_type(lhs) = baseline_type(rhs) = baseline_type(result).

Przykłady

// %lhs: [[1, 2], [7, 8]]
// %rhs: [[5, 6], [3, 4]]
%result = "stablehlo.minimum"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[1, 2], [3, 4]]

Więcej przykładów

pomnóż

Semantyka

Wykonuje iloczyn elementu 2 tensorów lhs i rhs oraz generuje Tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku wartości logicznych: logiczny ORAZ.
  • W przypadku liczb całkowitych: mnożenie.
  • W przypadku jednostek zmiennoprzecinkowych: multiplication w standardzie IEEE-754.
  • W przypadku liczb zespolonych: mnożenie zespolone.
  • W przypadku typów kwantowych:
    • dequantize_op_quantize(multiply, lhs, rhs, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs kwantowy tensor lub tensor kwantowy (C1)
(I2) rhs kwantowy tensor lub tensor kwantowy (C1)

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.multiply"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 12], [21, 32]]

Więcej przykładów

wyklucz

Semantyka

Wykonuje negację tensora operand z uwzględnieniem elementów i tworzy result tensora. W zależności od typu elementu wykonuje te działania:

  • W przypadku liczb całkowitych ze znakiem: negacja liczby całkowitej.
  • W przypadku nieoznaczonych liczb całkowitych: przesyłanie bitowe na liczbę całkowitą ze znakiem, negacja liczby całkowitej, przesyłanie bitowe z powrotem do liczby całkowitej bez znaku.
  • W przypadku jednostek zmiennoprzecinkowych: negate w standardzie IEEE-754.
  • W przypadku liczb zespolonych: negacja zespolona.
  • W przypadku typów kwantowych: dequantize_op_quantize(negate, operand, type(result))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu całkowitej, zmiennoprzecinkowego, zespolonego lub kwantyzowany tensora według tensora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu całkowitej, zmiennoprzecinkowego, zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// Negation operation with integer Tensors
// %operand: [0, -2]
%result = "stablehlo.negate"(%operand) : (tensor<2xi32>) -> tensor<2xi32>
// %result: [0, 2]

// Negation operation with with complex tensors
// %operand: (2.5, 0.0)
%result = "stablehlo.negate"(%operand) : (tensor<1xcomplex<f32>>) -> tensor<1xcomplex<f32>>
// %result: [-2.5, -0.0]

Więcej przykładów

nie

Semantyka

Wykonuje ruch względem elementu NIE od tensora operand i generuje tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku wartości logicznych: logiczne NIE.
  • W przypadku liczb całkowitych: bitowa NIE.

Argumenty

Nazwa Typ Ograniczenia
operand tensor typu wartości logicznej lub liczby całkowitej (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu wartości logicznej lub liczby całkowitej (C1)

Ograniczenia

  • (C1) type(operand) = type(result).

Przykłady

// Bitwise operation with with integer tensors
// %operand: [[1, 2], [3, 4]]
%result = "stablehlo.not"(%operand) : (tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[-2, -3], [-4, -5]]

// Bitwise operation with with boolean tensors
// %operand: [true, false]
%result = "stablehlo.not"(%operand) : (tensor<2xi1>) -> tensor<2xi1>
// %result: [false, true]

Więcej przykładów

optimization_barrier

Semantyka

Zapewnia, że operacje generujące operand są wykonywane przed operacje zależne od result i uniemożliwiające przekształcenia kompilatora przed przenoszeniem działalności przez barierę. Poza tym operacja jest tożsamość, czyli result = operand.

Argumenty

Nazwa Typ Ograniczenia
operand liczba zmiennoprzecinkowa tensorów, skwantyzowane tensory lub tokeny na tensor (C1)

Wyniki

Nazwa Typ Ograniczenia
result liczba zmiennoprzecinkowa tensorów, skwantyzowane tensory lub tokeny na tensor (C1)

Ograniczenia

  • (C1) type(operand...) = type(result...).

Przykłady

// %operand0: 0.0
// %operand1: 1.0
%result0, %result1 = "stablehlo.optimization_barrier"(%operand0, %operand1) : (tensor<f32>, tensor<f32>) -> (tensor<f32>, tensor<f32>)
// %result0: 0.0
// %result1: 1.0

Więcej przykładów

lub

Semantyka

Wykonuje działanie z uwzględnieniem elementu LUB dwa tensory lhs i rhs, generując result tensora. W zależności od typu elementu wykonuje te działania:

  • W przypadku wartości logicznych: logiczny LUB.
  • W przypadku liczb całkowitych: bitowa LUB.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs tensor typu liczby całkowitej lub wartości logicznej (C1)
(I2) rhs tensor typu liczby całkowitej lub wartości logicznej (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu liczby całkowitej lub wartości logicznej (C1)

Ograniczenia

  • (C1) type(lhs) = type(rhs) = type(result).

Przykłady

// Bitwise operation with with integer tensors
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.or"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 6], [7, 12]]

// Logical operation with with boolean tensors
// %lhs: [[false, false], [true, true]]
// %rhs: [[false, true], [false, true]]
%result = "stablehlo.or"(%lhs, %rhs) : (tensor<2x2xi1>, tensor<2x2xi1>) -> tensor<2x2xi1>
// %result: [[false, true], [true, true]]

Więcej przykładów

OutFeed

Semantyka

Zapisuje inputs w wyniku i tworzy token result.

Semantyka etykiety outfeed_config jest zdefiniowana przez implementację.

Dane wejściowe

Etykieta Nazwa Typ
(I1) inputs liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów
(I2) token token
(I3) outfeed_config stała typu string

Wyniki

Nazwa Typ
result token

Przykłady

%result = "stablehlo.outfeed"(%input0, %token) {
  outfeed_config = ""
} : (tensor<2x2x2xi64>, !stablehlo.token) -> !stablehlo.token

Więcej przykładów

pad

Semantyka

Rozwija operand przez dopełnienie wokół tensora i między elementami tensora z podanym padding_value.

edge_padding_low i edge_padding_high określają ilość dodanego dopełnienia na niskim (obok indeksu 0) i wysokiej (obok najwyższego indeksu) poszczególnych wymiarów. Ilość dopełnienia może być ujemna, gdzie bezwzględna wartość dopełnienia ujemnego wskazuje liczbę elementów do usunięcia z określonego wymiaru.

interior_padding określa ilość dopełnienia dodanego między dowolnymi dwoma wartościami elementy w każdym wymiarze, które mogą nie być ujemne. Występuje dopełnienie wewnętrzne przed dopełnieniem krawędzi, w taki sposób, że dopełnienie negatywnego spowoduje usunięcie elementów wewnątrz operandu z dopełnieniem.

Bardziej oficjalnie termin result[result_index] jest zdefiniowany jako:

  • operand[operand_index], jeśli result_index = edge_padding_low + operand_index * (interior_padding + 1)
  • W przeciwnym razie: padding_value.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand kwantowy tensor lub tensor kwantowy (C1), (C2), (C4)
(I2) padding_value Tensor 0-wymiarowy lub kwantowy tensor na tensor (C1)
(I3) edge_padding_low Jednowymiarowa stała tensora typu si64 (C1), (C4)
(I4) edge_padding_high Jednowymiarowa stała tensora typu si64 (C1), (C4)
(I5) interior_padding Jednowymiarowa stała tensora typu si64 (C2–C4)

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (C3–C6)

Ograniczenia

  • (C1) element_type(operand) = element_type(padding_value) = element_type(result).
  • (C2) size(edge_padding_low) = size(edge_padding_high) = size(interior_padding) = rank(operand).
  • (C3) 0 <= interior_padding.
  • (C4) shape(result) = shape(operand) + edge_padding_low + max(shape(operand) - 1, 0) * interior_padding + edge_padding_high.

Przykłady

// %operand: [
//            [1, 2, 3],
//            [4, 5, 6]
//           ]
// %padding_value: 0
%result = "stablehlo.pad"(%operand, %padding_value) {
  edge_padding_low = array<i64: 0, 1>,
  edge_padding_high = array<i64: 2, 1>,
  interior_padding = array<i64: 1, 2>
} : (tensor<2x3xi32>, tensor<i32>) -> tensor<5x9xi32>
// %result: [
//           [0, 1, 0, 0, 2, 0, 0, 3, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0],
//           [0, 4, 0, 0, 5, 0, 0, 6, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0]
//          ]

Więcej przykładów

partition_id

Semantyka

Generuje partition_id bieżącego procesu.

Wyniki

Nazwa Typ
result Tensor 0-wymiarowy typu ui32

Przykłady

%result = "stablehlo.partition_id"() : () -> tensor<ui32>

Więcej przykładów

Popcnt

Semantyka

Wykonuje liczbę bitów ustawionych w tensorze operand według elementu i tworzy tensor result.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu liczby całkowitej (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu liczby całkowitej (C1)

Ograniczenia

  • (C1) type(operand) = type(result).

Przykłady

// %operand: [0, 1, 2, 127]
%result = "stablehlo.popcnt"(%operand) : (tensor<4xi64>) -> tensor<4xi64>
// %result: [0, 1, 1, 7]

Więcej przykładów

moc

Semantyka

Wykonuje wykładnicze wykładnicze tensora lhs przez tensor rhs i generuje tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku liczb całkowitych: potęga.
  • W przypadku jednostek zmiennoprzecinkowych: pow w standardzie IEEE-754.
  • W przypadku liczb zespolonych: wykładnicze.
  • W przypadku typów kwantowych: dequantize_op_quantize(power, lhs, rhs, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs tensor typu całkowitej, zmiennoprzecinkowego, zespolonego lub kwantyzowany tensora według tensora (C1)
(I2) rhs tensor typu całkowitej, zmiennoprzecinkowego, zespolonego lub kwantyzowany tensora według tensora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu całkowitej, zmiennoprzecinkowego, zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %lhs: [-2.0, -0.0, -36.0, 5.0, 3.0, 10000.0]
// %rhs: [2.0, 2.0, 1.1, 2.0, -1.0, 10.0]
%result = "stablehlo.power"(%lhs, %rhs) : (tensor<6xf64>, tensor<6xf64>) -> tensor<6xf64>
// %result: [4.0, 0.0, -nan, 25.0, 0.333333343, inf]

Więcej przykładów

prawdziwe

Semantyka

Wyodrębnia prawdziwą część (z uwzględnieniem elementów) z operand i tworzy result tensora. Bardziej oficjalnie w przypadku każdego elementu x: real(x) = is_complex(x) ? real_part(x) : x

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub zespolonego (C1), (C2)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego (C1), (C2)

Ograniczenia

  • (C1) shape(result) = shape(operand).
  • (C2) Parametr element_type(result) jest zdefiniowany jako:
    • complex_element_type(element_type(operand)), jeśli is_complex(operand).
    • W przeciwnym razie: element_type(operand).

Przykłady

// %operand: [(1.0, 2.0), (3.0, 4.0)]
%result = "stablehlo.real"(%operand) : (tensor<2xcomplex<f32>>) -> tensor<2xf32>
// %result: [1.0, 3.0]

Więcej przykładów

odebranie

Semantyka

Pobiera dane z kanału z parametrem channel_id i tworzy results.

Jeśli is_host_transfer to true, operacja przesyła dane z hosta. W przeciwnym razie dane są przenoszone z innego urządzenia. Co to oznacza, jest bardzo skomplikowana. Ta flaga powiela informacje podane w channel_type, dlatego w przyszłości planujemy zachować tylko jedną z nich (#666).

results składa się z wartości ładunku, które pojawiają się jako pierwsze, i tokena, który pojawia się na końcu. W przyszłości planujemy podzielić ładunek i token na dwa osobne dane wyjściowe, aby zwiększyć ich przejrzystość; (#670).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) token token (K4)
(I2) channel_id stała typu si64
(I3) channel_type wyliczenie DEVICE_TO_DEVICE i HOST_TO_DEVICE (C1)
(I4) is_host_transfer stała typu i1 (C1)

Wyniki

Nazwa Typ Ograniczenia
results zmiennoprzecinkowa liczba tensorów, skwantyzowanych tensorów lub tokenów (C2–C4)

Ograniczenia

  • (C1) Parametr channel_type jest zdefiniowany jako:
    • HOST_TO_DEVICE jeśli is_host_transfer = true,
    • W przeciwnym razie: DEVICE_TO_DEVICE.
  • (C2) 0 < size(results).
  • (C3) is_empty(result[:-1]) lub is_tensor(type(results[:-1])).
  • (C4) is_token(type(results[-1])).

Przykłady

%results0, %results1 = "stablehlo.recv"(%token) {
  channel_handle = #stablehlo.channel_handle<handle = 1, type = 3>,
  is_host_transfer = true
} : (!stablehlo.token) -> (tensor<2x2xi64>, !stablehlo.token)

Więcej przykładów

zmniejsz

Semantyka

Stosuje funkcję redukcji body do inputs i init_values wzdłuż dimensions i generuje tensory results.

Kolejność redukcji jest zdefiniowana przez implementację, co oznacza, że body oraz init_values musi utworzyć monoid, aby zagwarantować, że operacja generuje takie same wyniki dla wszystkich danych wejściowych we wszystkich implementacjach. Jednak ten warunek nie sprawdza się w przypadku wielu popularnych redukcji. Na przykład: dodawanie zmiennoprzecinkowe dla argumentu body i 0 dla parametru init_values w rzeczywistości nie tworzą monoidu, ponieważ dodawanie zmiennoprzecinkowe nie jest asocjacyjne.

Więcej formalnie results...[j0, ..., jR-1] = reduce(input_slices_converted), gdzie:

  • input_slices = inputs...[j0, ..., :, ..., jR-1], gdzie wstawiono: : o dimensions.
  • input_slices_converted = to_destination_type(input_slices..., type(func_inputs(body)[:len(func_inputs(body))//2])...).
  • init_values_converted = to_destination_type(init_values..., type(func_inputs(body)[len(func_inputs(body))//2:])...).
  • reduce(input_slices_converted) = exec(schedule) dla pewnego drzewa binarnego schedule, gdzie:
    • exec(node) = body(exec(node.left), exec(node.right)).
    • exec(leaf) = leaf.value.
  • schedule to zdefiniowane przez implementację pełne drzewo binarne, w którym przemierzanie obejmuje:
    • input_slices_converted...[index] wartości, dla wszystkich index w index_space(input_slices_converted) w rosnącym porządku leksykograficznym z index.
    • zdefiniowaną przez implementację liczbą init_values_converted w pozycji określonych w implementacji.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) inputs liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów na tensor (C1–C4), (C6), (C7)
(I2) init_values liczba zmiennoprzecinkowa tensorów 0-wymiarowych lub kwantyzowanych tensorów na tensor (C2), (C3)
(I3) dimensions Jednowymiarowa stała tensora typu si64 (C4), (C5), (C7)
(I4) body funkcja (C6)

Wyniki

Nazwa Typ Ograniczenia
results liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów na tensor (C3), (C7), (C8)

Ograniczenia

  • (C1) same(shape(inputs...)).
  • (C2) element_type(inputs...) = element_type(init_values...).
  • (C3) 0 < size(inputs) = size(init_values) = size(results) = N.
  • (C4) 0 <= dimensions < rank(inputs[0]).
  • (C5) is_unique(dimensions).
  • (C6) body ma typ (tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ..., tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>), gdzie is_promotable(element_type(inputs[i]), Ei)
  • (C7) shape(results...) = shape(inputs...) z tym wyjątkiem, że wymiar rozmiary inputs... odpowiadające wartości dimensions nie są uwzględniane.
  • (C8) element_type(results[i]) = Ei dla wszystkich i w [0,N).

Przykłady

// %input = [[0, 1, 2, 3, 4, 5]]
// %init_value = 0
%result = "stablehlo.reduce"(%input, %init_value) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
    "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  dimensions = array<i64: 1>
} : (tensor<1x6xi64>, tensor<i64>) -> tensor<1xi64>
// %result = [15]

Więcej przykładów

reduce_precision

Semantyka

Wykonuje konwersję typu operand z uwzględnieniem elementu na inny typ zmiennoprzecinkowy z wykorzystaniem exponent_bits i mantissa_bits z powrotem do oryginału typu zmiennoprzecinkowego i tworzy tensor output.

Bardziej oficjalnie:

  • Elementy mantysy pierwotnej wartości są zaokrąglane w celu zaokrąglenia wartości oryginalnej. do najbliższej wartości możliwej do reprezentowania za pomocą funkcji mantissa_bits przy użyciu funkcji roundToIntegralTiesToEven – semantyka.
  • Następnie, jeśli mantissa_bits jest mniejsza niż liczba bitów modliszki pierwotnej wartości, bity mantysy są obcinane do mantissa_bits.
  • Następnie, jeśli bity wykładnika wyniku pośredniego nie mieszczą się w zakres dostarczony przez funkcję exponent_bits, wynik pośredni rozszerza się do nieskończoność przy użyciu pierwotnego znaku lub niedopełnienie do zera za pomocą funkcji pierwotny znak.
  • W przypadku typów kwantowych funkcja dequantize_op_quantize( lambda operand: reduce_precision(operand, exponent_bits, mantissa_bits), operand, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C1)
(I2) exponent_bits stała typu si32 (K2)
(I3) mantissa_bits stała typu si32 (K3)

Wyniki

Nazwa Typ Ograniczenia
output tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(output).
  • (C2) 1 <= exponent_bits.
  • (C3) 0 <= mantissa_bits.

Przykłady

// Logical values: +Inf, NaN, +Denormal, 0.0, 65519.0, 65520.0
// %operand: [0x7FF0000000000000, 0x7FFFFFFFFFFFFFFF, 0x0000000000000001, 0.0, 65519.0, 65520.0]
%output = "stablehlo.reduce_precision"(%operand) {
  exponent_bits = 5 : i32,
  mantissa_bits = 10 : i32
} : (tensor<6xf64>) -> tensor<6xf64>
// Logical values: +Inf, NaN, 0.0, 0.0, 65504.0, +Inf
// %output: [0x7FF0000000000000, 0x7FFFFFFFFFFFFFFF, 0.0, 0.0, 65504.0, 0x7FF0000000000000]

Więcej przykładów

reduce_scatter

Semantyka

reduce_scatter

W każdej grupie procesów w siatce procesów StableHLO przeprowadza redukcję, przy użyciu computations przez wartości tensora operand z każdego procesu, dzieli wynik redukcji wzdłuż scatter_dimension na części i rozrzuca dzieli się części między procesami w celu wygenerowania result.

Operacja dzieli siatkę procesów StableHLO na siatkę procesów process_groups, która jest: zdefiniowane w następujący sposób:

  • cross_replica(replica_groups) jeśli channel_id <= 0 and use_global_device_ids = false.
  • cross_replica_and_partition(replica_groups) jeśli channel_id > 0 and use_global_device_ids = false.
  • flattened_ids(replica_groups) jeśli channel_id > 0 and use_global_device_ids = true.

Następnie w każdym elemencie process_group:

  • reduced_value = all_reduce(operand, replica_groups, channel_id, use_global_device_ids, computation).
  • parts@sender = split(reduced_value@sender, dim(process_groups, 1), scatter_dimension).
  • result@receiver = parts@sender[receiver_index] dla wszystkich sender w process_group, gdzie receiver_index = process_group.index(receiver).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand kwantowy tensor lub tensor kwantowy (C1), (C2), (C7), (C8)
(I2) scatter_dimension stała typu si64 (C1), (C2), (C8)
(I3) replica_groups 2-wymiarowa stała tensora typu si64 (C3–C5)
(I4) channel_id stała typu si64 (C6)
(I5) use_global_device_ids stała typu i1 (C6)
(I6) computation funkcja (C7)

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (C8–C9)

Ograniczenia

  • (C1) dim(operand, scatter_dimension) % dim(process_groups, 1) = 0.
  • (C2) 0 <= scatter_dimension < rank(operand).
  • (C3) is_unique(replica_groups).
  • (C4) Parametr size(replica_groups) jest zdefiniowany jako:
    • num_replicas, jeśli używana jest właściwość cross_replica.
    • num_replicas, jeśli używana jest właściwość cross_replica_and_partition.
    • num_processes, jeśli używana jest właściwość flattened_ids.
  • (C5) 0 <= replica_groups < size(replica_groups).
  • (C6) Jeśli use_global_device_ids = true, to channel_id > 0.
  • (C7) computation ma typ (tensor<E>, tensor<E>) -> (tensor<E>), gdzie is_promotable(element_type(operand), E)
  • (C8) shape(result) = shape(operand) z wyjątkiem:
    • dim(result, scatter_dimension) = dim(operand, scatter_dimension) / dim(process_groups, 1).
  • (C9) element_type(result) = E.

Przykłady

// num_replicas: 2
// num_partitions: 1
// %operand@(0, 0): [[1, 2, 3, 4],
//                   [5, 6, 7, 8]]
// %operand@(1, 0): [[9, 10, 11, 12],
//                   [13, 14, 15, 16]]
%result = "stablehlo.reduce_scatter"(%operand) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
  %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
  "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  scatter_dimension = 1 : i64,
  replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>,
  channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
} : (tensor<2x4xi64>) -> tensor<2x2xi64>
//
// %result@(0, 0): [[10, 12],
//                  [18, 20]]
// %result@(1, 0): [[14, 16],
//                  [22, 24]]

Więcej przykładów

reduce_window

Semantyka

Stosuje funkcję redukcji body do okien inputs i init_values i generuje results.

Ten diagram pokazuje, jak elementy w results... są obliczane na podstawie inputs....

reduce_window

Bardziej oficjalnie, results...[result_index] = reduce(windows, init_values, axes(inputs...), body) (patrz redukcja), gdzie:

  • padded_inputs = pad(inputs..., init_values..., padding[:, 0], padding[:, 1], base_dilations - 1).
  • window_start = result_index * window_strides.
  • window_end = window_start + (window_dimensions - 1) * window_dilations + 1.
  • windows = slice(padded_inputs..., window_start, window_end, window_dilations).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) inputs liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów na tensor (C1–C4), (C6), (C8), (C10), (C12), (C13), (C15)
(I2) init_values liczba zmiennoprzecinkowa tensorów 0-wymiarowych lub kwantyzowanych tensorów na tensor (C1), (C13)
(I3) window_dimensions Jednowymiarowa stała tensora typu si64 (C4), (C5), (C15)
(I4) window_strides Jednowymiarowa stała tensora typu si64 (C6), (C7), (C15)
(I5) base_dilations Jednowymiarowa stała tensora typu si64 (C8), (C9), (C15)
(I6) window_dilations Jednowymiarowa stała tensora typu si64 (C10), (C11), (C15)
(I7) padding 2-wymiarowa stała tensora typu si64 (C12), (C15)
(I8) body funkcja (C13)

Wyniki

Nazwa Typ Ograniczenia
results liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów na tensor (C1), (C14–C16)

Ograniczenia

  • (C1) 0 < size(inputs) = size(init_values) = size(results) = N.
  • (C2) same(shape(inputs...)).
  • (C3) element_type(inputs...) = element_type(init_values...).
  • (C4) size(window_dimensions) = rank(inputs[0]).
  • (C5) 0 < window_dimensions.
  • (C6) size(window_strides) = rank(inputs[0]).
  • (C7) 0 < window_strides.
  • (C8) size(base_dilations) = rank(inputs[0]).
  • (C9) 0 < base_dilations.
  • (C10) size(window_dilations) = rank(inputs[0]).
  • (C11) 0 < window_dilations.
  • (C12) shape(padding) = [rank(inputs[0]), 2].
  • (C13) body ma typ (tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ..., tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>), gdzie is_promotable(element_type(inputs[i]), Ei)
  • (C14) same(shape(results...)).
  • (C15) shape(results[0]) = num_windows, gdzie:
    • dilated_input_shape = shape(inputs[0]) = 0 ? 0 : (shape(inputs[0]) - 1) * base_dilations + 1.
    • padded_input_shape = padding[:, 0] + dilated_input_shape + padding[:, 1].
    • dilated_window_shape = (window_dimensions - 1) * window_dilations + 1.
    • is_empty_window = padded_input_shape = 0 || dilated_window_shape > padded_input_shape.
    • num_windows = is_empty_window ? 0 : floor((padded_input_shape - dilated_window_shape) / window_strides) + 1.
  • (C16) element_type(results[i]) = Ei dla wszystkich i w [0,N).

Przykłady

// %input = [[1, 2], [3, 4], [5, 6]]
// %init_value = 0
%result = "stablehlo.reduce_window"(%input, %init_value) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
    "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  window_dimensions = array<i64: 2, 1>,
  window_strides = array<i64: 4, 1>,
  base_dilations = array<i64: 2, 1>,
  window_dilations = array<i64: 3, 1>,
  padding = dense<[[2, 1], [0, 0]]> : tensor<2x2xi64>
} : (tensor<3x2xi64>, tensor<i64>) -> tensor<2x2xi64>
// %result = [[0, 0], [3, 4]]

Więcej przykładów

reszta

Semantyka

Wykonuje resztę tensorów dzielnika lhs i dzielnika rhs według elementu generuje tensor result.

Bardziej oficjalnie znak wyniku jest wyliczany z dywidendy, a wzrost wartość bezwzględna wyniku jest zawsze mniejsza od wartości bezwzględnej dzielnika. Reszta jest obliczana według wzoru: lhs - d * rhs, gdzie wartość d jest określona przez:

  • W przypadku liczb całkowitych: stablehlo.divide(lhs, rhs).
  • W przypadku jednostek zmiennoprzecinkowych: division(lhs, rhs) z standardu IEEE-754 z atrybutem zaokrąglania roundTowardZero
  • W przypadku liczb zespolonych: do ustalenia (#997).
  • W przypadku typów kwantowych:
    • dequantize_op_quantize(remainder, lhs, rhs, type(result)).

W przypadku elementów zmiennoprzecinkowych ta operacja jest kontrasta Operacja remainder ze specyfikacji IEEE-754, gdzie d jest wartością całkowitą jest najbliższa dokładnie wartości lhs/rhs z równaniami parzystymi.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs tensor typu całkowitej, zmiennoprzecinkowego, zespolonego lub kwantyzowany tensora na tensor (C1)
(I2) rhs tensor typu całkowitej, zmiennoprzecinkowego, zespolonego lub kwantyzowany tensora na tensor (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu całkowitej, zmiennoprzecinkowego, zespolonego lub kwantyzowany tensora na tensor (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %lhs: [17, -17, 17, -17]
// %rhs: [3, 3, -3, -3]
%result = "stablehlo.remainder"(%lhs, %rhs) : (tensor<4xi64>, tensor<4xi64>) -> tensor<4xi64>
// %result: [2, -2, 2, -2]

Więcej przykładów

replica_id

Semantyka

Generuje replica_id bieżącego procesu.

Wyniki

Nazwa Typ
result Tensor 0-wymiarowy typu ui32

Przykłady

%result = "stablehlo.replica_id"() : () -> tensor<ui32>

Więcej przykładów

zmienić kształt

Semantyka

Zmienia kształt tensora operand na tensor result. Zasadniczo oznacza zachowanie tego samego reprezentacji kanonicznej, ale potencjalne zmiany kształt, np. od tensor<2x3xf32> do tensor<3x2xf32> lub tensor<6xf32>.

Bardziej oficjalnie: result[result_index] = operand[operand_index], gdzie: result_index i operand_index mają to samo miejsce w leksykografii kolejność index_space(result) i index_space(operand).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor (tensor kwantowy) (K1–C3)

Wyniki

Nazwa Typ Ograniczenia
result tensor (tensor kwantowy) (K1–C3)

Ograniczenia

  • (C1) element_type(result) otrzymuje:
    • element_type(operand), jeśli !is_per_axis_quantized(operand).
    • element_type(operand) oprócz quantization_dimension(operand) i Wartość quantization_dimension(result) może się różnić. W przeciwnym razie.
  • (C2) size(operand) = size(result).
  • (C3) Jeśli is_per_axis_quantized(operand):
    • reduce(dims(operand, [0, 1, ..., quantization_dimension(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [0, 1, ..., quantization_dimension(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y).
    • dim(operand, quantization_dimension(operand)) = dim(result, quantization_dimension(result)).
    • reduce(dims(operand, [quantization_dimension(operand) + 1, ..., rank(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [quantization_dimension(result) + 1, ..., rank(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y).

Przykłady

// %operand: [[1, 2, 3], [4, 5, 6]]
%result = "stablehlo.reshape"(%operand) : (tensor<2x3xi32>) -> tensor<3x2xi32>
// %result: [[1, 2], [3, 4], [5, 6]]

Więcej przykładów

odwróć

Semantyka

Odwraca kolejność elementów w elemencie operand wzdłuż określonej wartości dimensions i tworzy tensor result. Bardziej oficjalnie, result[result_index] = operand[operand_index], gdzie:

  • operand_index[d] = dim(result, d) - result_index[d] - 1 jeśli d w dimensions.
  • W przeciwnym razie: operand_index[d] = result_index[d].

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand kwantowy tensor lub tensor kwantowy (C1), (C3)
(I2) dimensions Jednowymiarowa stała tensora typu si64 (C2), (C3)

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (C1), (C3)

Ograniczenia

  • (C1) type(operand) = type(result).
  • (C2) is_unique(dimensions).
  • (C3) 0 <= dimensions < rank(result).

Przykłady

// %operand = [[1, 2], [3, 4], [5, 6]]
%result = "stablehlo.reverse"(%operand) {
  dimensions = array<i64: 1>
} : (tensor<3x2xi32>) -> tensor<3x2xi32>
// %result: [[2, 1], [4, 3], [6, 5]]

Więcej przykładów

NG

Semantyka

Generuje liczby losowe za pomocą algorytmu rng_distribution i zwraca Tensor result określonego kształtu shape.

Jeśli rng_distribution = UNIFORM, generowane są liczby losowe według jednolitego rozkładu w przedziale [a, b). Jeśli a >= b, zachowanie jest niezdefiniowane.

Jeśli rng_distribution = NORMAL, generowane są liczby losowe uwzględniający rozkład normalny ze średnią = a i odchyleniem standardowym = b. Jeśli zasada b < 0, działanie jest niezdefiniowane.

Dokładny sposób generowania liczb losowych jest zdefiniowany w implementacji. Dla: na przykład mogą być deterministyczne i mogą, ale nie muszą, ukryty.

W rozmowach z wieloma zainteresowanymi osobami ta praca okazała się równie skuteczna jest przestarzała, więc planujemy usunąć je w przyszłości. (#597).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) a Tensor 0-wymiarowy liczby całkowitej, wartości logicznej lub typu zmiennoprzecinkowego (C1), (C2)
(I2) b Tensor 0-wymiarowy liczby całkowitej, wartości logicznej lub typu zmiennoprzecinkowego (C1), (C2)
(I3) shape Jednowymiarowa stała tensora typu si64 (K3)
(I4) rng_distribution wyliczenie UNIFORM i NORMAL (K2)

Wyniki

Nazwa Typ Ograniczenia
result tensor liczby całkowitej, wartości logicznej lub typu zmiennoprzecinkowego (K1–C3)

Ograniczenia

  • (C1) element_type(a) = element_type(b) = element_type(result).
  • (C2) Jeśli rng_distribution = NORMAL, to is_float(a).
  • (C3) shape(result) = shape.

Przykłady

// %a = 0
// %b = 2
// %shape = [3, 3]
%result = "stablehlo.rng"(%a, %b, %shape) {
  rng_distribution = #stablehlo<rng_distribution UNIFORM>
} : (tensor<i32>, tensor<i32>, tensor<2xi64>) -> tensor<3x3xi32>
// %result: [
//           [1, 0, 1],
//           [1, 1, 1],
//           [0, 0, 0]
//          ]

rng_bit_generator

Semantyka

Zwraca pole output wypełnione jednolitymi losowymi bitami i zaktualizowanym stanem wyjściowym output_state korzysta z algorytmu generatora liczb pseudorandom rng_algorithm dla początkowego stanu initial_state. Dane wyjściowe będą na pewno deterministycznej funkcji initial_state, ale nie jest to gwarantowane deterministyczny między implementacjami.

rng_algorithm jest jedną z tych:

  • DEFAULT: algorytm zdefiniowany przez implementację.
  • THREE_FRY: zdefiniowany przez implementację wariant algorytmu Threefry*.
  • PHILOX: definiowany przez implementację wariant algorytmu Philox*.

* Patrz: Salmon i in. SC 2011. Równoległe liczby losowe: oczywiście jak 1, 2, 3. .

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) rng_algorithm wyliczenie DEFAULT, THREE_FRY i PHILOX (K2)
(I2) initial_state Jednowymiarowy tensor typu ui64 (C1), (C2)

Wyniki

Nazwa Typ Ograniczenia
output_state Jednowymiarowy tensor typu ui64 (C1)
output tensor typu liczby całkowitej lub zmiennoprzecinkowego

Ograniczenia

  • (C1) type(initial_state) = type(output_state).
  • (C2) Parametr size(initial_state) jest zdefiniowany jako:
    • zdefiniowaną przez implementację, jeśli rng_algorithm = DEFAULT.
    • 2, jeśli rng_algorithm = THREE_FRY.
    • 2 lub 3, jeśli rng_algorithm = PHILOX.

Przykłady

// %initial_state: [1, 2]
%output_state, %output = "stablehlo.rng_bit_generator"(%initial_state) {
  rng_algorithm = #stablehlo<rng_algorithm THREE_FRY>
} : (tensor<2xui64>) -> (tensor<2xui64>, tensor<2x2xui64>)
// %output_state: [1, 6]
// %output: [
//           [9236835810183407956, 16087790271692313299],
//           [18212823393184779219, 2658481902456610144]
//          ]

round_nearest_afz

Semantyka

Wykonuje zaokrąglanie z poziomu elementu do najbliższej liczby całkowitej, co powoduje łamanie zasad od zera na tensoru operand. Generuje tensor result. Implementuje operacji roundToIntegralTiesToAway ze specyfikacji IEEE-754. Dla: kwantyzowane typy, skuteczność dequantize_op_quantize(round_nearest_afz, operand, type(result))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %operand = [-2.5, 0.4, 0.5, 0.6, 2.5]
%result = "stablehlo.round_nearest_afz"(%operand) : (tensor<5xf64>) -> tensor<5xf64>
// %result: [-3.0, 0.0, 1.0, 1.0, 3.0]

Więcej przykładów

round_nearest_even

Semantyka

Wykonuje zaokrąglanie z podziałem na elementy do najbliższej liczby całkowitej, naruszając zasady w kierunku parzystej liczby całkowitej, na tensorze operand i daje result tensora. Implementuje operację roundToIntegralTiesToEven z standardu IEEE-754 specyfikacji. W przypadku typów kwantowych dequantize_op_quantize(round_nearest_even, operand, type(result))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub kwantyzowany tensor na poziomie procesora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %operand = [-2.5, 0.4, 0.5, 0.6, 2.5]
%result = "stablehlo.round_nearest_even"(%operand) : (tensor<5xf64>) -> tensor<5xf64>
// %result: [-2.0, 0.0, 0.0, 1.0, 2.0]

Więcej przykładów

rsqrt

Semantyka

Wykonuje odwrotną operację odwrotności pierwiastka kwadratowego dla tensora operand i generuje tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku jednostek zmiennoprzecinkowych: rSqrt w standardzie IEEE-754.
  • W przypadku liczb zespolonych: zespolony odwrotny pierwiastek kwadratowy.
  • W przypadku typów kwantowych: dequantize_op_quantize(rsqrt, operand, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %operand: [[1.0, 4.0], [9.0, 25.0]]
%result = "stablehlo.rsqrt"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[1.0, 0.5], [0.33333343, 0.2]]

Więcej przykładów

punkt

Semantyka

Generuje tensory results, które są równe tensorom inputs, z tym wyjątkiem: kilka wycinków określonych przez scatter_indices zostało zaktualizowanych o wartości updates za pomocą: update_computation.

Ten diagram pokazuje, jak elementy w updates... są mapowane na elementy w results.... Diagram pokazuje kilka przykładów updates... określa indeksy i szczegółowo wyjaśnia, które indeksy results... .

punkt

Więcej formalnie dla wszystkich update_index w index_space(updates[0]):

  • update_scatter_dims = [d for d in axes(updates[0]) and d not in update_window_dims].
  • update_scatter_index = update_index[update_scatter_dims...].
  • start_index jest zdefiniowany jako:
    • scatter_indices[si0, ..., :, ..., siN], gdzie si to osoby indywidualne elementy w tabelach update_scatter_index i : są wstawione w miejscu Indeks index_vector_dim, jeśli index_vector_dim < rank(scatter_indices).
    • W przeciwnym razie: [scatter_indices[update_scatter_index]].
  • W przypadku usługi d_input w axes(inputs[0]),
    • full_start_index[d_input] = start_index[d_start], jeśli d_input = scatter_dims_to_operand_dims[d_start]
    • W przeciwnym razie: full_start_index[d_input] = 0.
  • W przypadku usługi d_input w axes(inputs[0]),
    • full_batching_index[d_input] = update_scatter_index[d_start - (d_start < index_vector_dim ? 0 : 1)] jeśli d_input = input_batching_dims[i_batching] i d_start = scatter_indices_batching_dims[i_batching]
    • W przeciwnym razie: full_batching_index[d_input] = 0.
  • update_window_index = update_index[update_window_dims...].
  • full_window_index = [wi0, ..., 0, ..., wiN], gdzie wi to osoby indywidualne elementów w argumencie update_window_index, a 0 jest wstawiony w indeksach z inserted_window_dims i input_batching_dims.
  • result_index = full_start_index + full_batching_index + full_window_index.

Mając to na uwadze, results = exec(schedule, inputs), gdzie:

  • schedule to zdefiniowana przez implementację permutacja funkcji index_space(updates[0])
  • exec([update_index, ...], results) = exec([...], updated_results), gdzie:
    • Jeśli result_index należy do zakresu shape(results...)
    • updates_converted = to_destination_type( updates...[update_index], type(func_inputs(update_computation) [len(func_inputs(update_computation))//2:])... )
    • updated_values = update_computation(results...[result_index], updates_converted)
    • updated_results to kopia notatnika results z results...[result_index] ustawiono na updated_values....
    • W innym przypadku
    • updated_results = results.
  • exec([], results) = results.

Jeśli indices_are_sorted to true, implementacja może przyjąć, że Listy scatter_indices są posortowane w odniesieniu do scatter_dims_to_operand_dims, W przeciwnym razie zachowanie jest niezdefiniowane. Więcej formalnie dla wszystkich i1 < i2 od indices(result), full_start_index(i1) <= full_start_index(i2).

Jeśli unique_indices to true, implementacja może zakładać, że wszystkie Indeksy result_index rozłożone w stosunku do wartości są unikalne. Jeśli unique_indices to true, ale indeksy rozproszone nie są unikalne, więc zachowanie jest nie zdefiniowano.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) inputs liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów na tensor (C1), (C2), (C4–C6), (C11), (C13), (C18), (C21), (C23–C24)
(I2) scatter_indices tensor typu liczby całkowitej (C4), (C15), (C19), (C22)
(I3) updates liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów na tensor (C3–C6), (C8)
(I4) update_window_dims Jednowymiarowa stała tensora typu si64 (C2), (C4), (C7–C8)
(I5) inserted_window_dims Jednowymiarowa stała tensora typu si64 (C2), (C4), (C9–C11)
(I6) input_batching_dims Jednowymiarowa stała tensora typu si64 (C2), (C4), (C9), (K12–13), (K17–18), (C20)
(I7) scatter_indices_batching_dims Jednowymiarowa stała tensora typu si64 (C14–C18)
(I8) scatter_dims_to_operand_dims Jednowymiarowa stała tensora typu si64 (C19–C21)
(I9) index_vector_dim stała typu si64 (C4), (C16), (C19), (C22)
(I10) indices_are_sorted stała typu i1
(I11) unique_indices stała typu i1
(I12) update_computation funkcja (C23)

Wyniki

Nazwa Typ Ograniczenia
results liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów na tensor (C24-C25)

Ograniczenia

  • (C1) same(shape(inputs...)).
  • (C2) `ranking(inputs[0]) = size(update_window_dims) + size(inserted_window_dims)
    • size(input_batching_dims)`.
  • (C3) same(shape(updates...)).
  • (C4) shape(updates[0]) = combine(update_scatter_dim_sizes, update_window_dim_sizes), gdzie:
    • update_scatter_dim_sizes = shape(scatter_indices) z tym wyjątkiem wielkość wymiaru scatter_indices odpowiadającego Nie dodano index_vector_dim.
    • update_window_dim_sizes <= shape(inputs[0]) z tym wyjątkiem rozmiary wymiarów w polu inputs[0] odpowiadające wartości inserted_window_dims i input_batching_dims nie są uwzględnione.
    • Funkcja combine umieszcza element update_scatter_dim_sizes na osiach odpowiadających update_scatter_dims i update_window_dim_sizes przy odpowiednich osiach do update_window_dims.
  • (C5) 0 < size(inputs) = size(updates) = N.
  • (C6) element_type(updates...) = element_type(inputs...).
  • (C7) is_unique(update_window_dims) and is_sorted(update_window_dims).
  • (C8) 0 <= update_window_dims < rank(updates[0]).
  • (C9) is_unique(concatenate(inserted_window_dims, input_batching_dims))
  • (C10) is_sorted(inserted_window_dims).
  • (C11) 0 <= inserted_window_dims < rank(inputs[0]).
  • (C12) is_sorted(input_batching_dims).
  • (C13) 0 <= input_batching_dims < rank(inputs[0])).
  • (C14) is_unique(scatter_indices_batching_dims).
  • (C15) 0 <= scatter_indices_batching_dims < rank(scatter_indices).
  • (C16) index_vector_dim not in scatter_indices_batching_dims.
  • (C17) size(input_batching_dims) == size(scatter_indices_batching_dims).
  • (C18) dim(inputs[0], input_batching_dims...) = dim(scatter_indices, scatter_indices_batching_dims...).
  • (C19) size(scatter_dims_to_operand_dims) = index_vector_dim < rank(scatter_indices) ? dim(scatter_indices, index_vector_dim) : 1.
  • (C20) is_unique(concatenate(scatter_dims_to_operand_dims, input_batching_dims)).
  • (C21) 0 <= scatter_dims_to_operand_dims < rank(inputs[0]).
  • (C22) 0 <= index_vector_dim <= rank(scatter_indices).
  • (C23) update_computation ma typ (tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ..., tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>), gdzie is_promotable(element_type(inputs[i]), Ei).
  • (C24) shape(inputs...) = shape(results...).
  • (C25) element_type(results[i]) = Ei dla wszystkich i w [0,N).

Przykłady

// %input: [
//          [
//           [[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, 28], [29, 30], [31, 32]],
//           [[33, 34], [35, 36], [37, 38], [39, 40]],
//           [[41, 42], [43, 44], [45, 46], [47, 48]]
//          ]
//         ]
// %scatter_indices: [
//                    [
//                     [[0, 0], [1, 0], [2, 1]],
//                     [[0, 1], [1, 1], [0, 9]]
//                    ],
//                    [
//                     [[0, 0], [2, 1], [2, 2]],
//                     [[1, 2], [0, 1], [1, 0]]
//                    ]
//                   ]
// %update: [
//           [
//            [[1, 1], [1, 1], [1, 1]],
//            [[1, 1], [1, 1], [1, 1]]
//           ],
//           [
//            [[1, 1], [1, 1], [1, 1]],
//            [[1, 1], [1, 1], [1, 1]]
//           ]
//          ]
%result = "stablehlo.scatter"(%input, %scatter_indices, %update) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
    "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  scatter_dimension_numbers = #stablehlo.scatter<
    update_window_dims = [3, 4],
    inserted_window_dims = [1],
    input_batching_dims = [0],
    scatter_indices_batching_dims = [1],
    scatter_dims_to_operand_dims = [2, 1],
    index_vector_dim = 3>,
  indices_are_sorted = false,
  unique_indices = false
} : (tensor<2x3x4x2xi64>, tensor<2x2x3x2xi64>, tensor<2x2x3x2x2xi64>) -> tensor<2x3x4x2xi64>
// %result: [
//           [
//            [[3, 4], [6, 7], [6, 7], [7, 8]],
//            [[9, 10],[11, 12], [15, 16], [17, 18]],
//            [[17, 18], [19, 20], [22, 23], [24, 25]]
//           ],
//           [
//            [[25, 26], [28, 29], [30, 31], [31, 32]],
//            [[35, 36], [38, 39], [38, 39], [39, 40]],
//            [[41, 42], [44, 45], [46, 47], [47, 48]]
//           ]
//          ]

Więcej przykładów

wybierz

Semantyka

Tworzy tensor result, w którym każdy element jest wybierany z wartości on_true lub on_false tensor na podstawie wartości odpowiedniego elementu pred. Więcej formalnie: result[result_index] = pred_element ? on_true[result_index] : on_false[result_index], gdzie pred_element = rank(pred) = 0 ? pred[] : pred[result_index]. W przypadku typów kwantowych dequantize_select_quantize(pred, on_true, on_false, type(result))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) pred tensor typu i1 (C1)
(I2) on_true kwantowy tensor lub tensor kwantowy (K1–C2)
(I3) on_false kwantowy tensor lub tensor kwantowy (K2)

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (K2)

Ograniczenia

  • (C1) rank(pred) = 0 or shape(pred) = shape(on_true).
  • (C2) baseline_type(on_true) = baseline_type(on_false) = baseline_type(result).

Przykłady

// %pred: [[false, true], [true, false]]
// %on_true: [[1, 2], [3, 4]]
// %on_false: [[5, 6], [7, 8]]
%result = "stablehlo.select"(%pred, %on_true, %on_false) : (tensor<2x2xi1>, tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 2], [3, 8]]

Więcej przykładów

select_and_scatter

Semantyka

Rozmieszcza wartości z tensora source przy użyciu scatter na podstawie wynik reduce_window tensora input przy użyciu select i daje wynik tensora result.

Ten diagram pokazuje, jak elementy w result są obliczane na podstawie operand i source za pomocą konkretnego przykładu.

select_and_scatter

Bardziej oficjalnie:

  • selected_values = reduce_window_without_init(...) z tymi danymi wejściowymi:

    • inputs = [operand].
    • window_dimensions, window_strides i padding, które są używane bez zmian.
    • base_dilations = windows_dilations = 1.
    • body jest zdefiniowany jako:
    def body(arg0: tensor<E>, arg1: tensor<E>) -> tensor<E>:
      return select(arg0, arg1) ? arg0 : arg1;
    

    gdzie działają E = element_type(operand) i reduce_window_without_init dokładnie tak samo jak reduce_window z tą różnicą, że schedule bazowego źródła reduce (patrz reduce) nie zawiera wartości init. Obecnie jest nieokreślone, co się dzieje, jeśli odpowiednie okno nie ma wartości (#731).

  • result[result_index] = reduce([source_values], [init_value], [0], scatter) gdzie:

    • source_values = [source[source_index] for source_index in source_indices].
    • selected_index(source_index) = operand_index, jeśli selected_values[source_index] zawiera element operand od operand_index.
    • source_indices = [source_index for source_index in indices(source) if selected_index(source_index) = result_index].

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand kwantowy tensor lub tensor kwantowy (C1–C4), (C6), (C8–C11)
(I2) source kwantowy tensor lub tensor kwantowy (C1), (C2)
(I3) init_value Tensor 0-wymiarowy lub kwantowy tensor na tensor (K3)
(I4) window_dimensions Jednowymiarowa stała tensora typu si64 (C2), (C4), (C5)
(I5) window_strides Jednowymiarowa stała tensora typu si64 (C2), (C6), (C7)
(I6) padding 2-wymiarowa stała tensora typu si64 (C2), (C8)
(I7) select funkcja (C9)
(I8) scatter funkcja (C10)

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (C11–C12)

Ograniczenia

  • (C1) element_type(operand) = element_type(source).
  • (C2) shape(source) = num_windows, gdzie:
    • padded_operand_shape = padding[:, 0] + shape(operand) + padding[:, 1].
    • is_empty_window = padded_operand_shape = 0 || window_dimensions > padded_operand_shape.
    • num_windows = is_empty_window ? 0 : floor((padded_operand_shape - window_dimensions) / window_strides) + 1.
  • (C3) element_type(init_value) = element_type(operand).
  • (C4) size(window_dimensions) = rank(operand).
  • (C5) 0 < window_dimensions.
  • (C6) size(window_strides) = rank(operand).
  • (C7) 0 < window_strides.
  • (C8) shape(padding) = [rank(operand), 2].
  • (C9) select ma typ (tensor<E>, tensor<E>) -> tensor<i1>, gdzie: E = element_type(operand)
  • (C10) scatter ma typ (tensor<E>, tensor<E>) -> tensor<E>, gdzie: is_promotable(element_type(operand), E)
  • (C11) shape(operand) = shape(result).
  • (C12) element_type(result) = E.

Przykłady

// %operand: [[1, 5], [2, 5], [3, 6], [4, 4]]
// %source: [[5, 6], [7, 8]]
// %init_value: 0
%result = "stablehlo.select_and_scatter"(%operand, %source, %init_value) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.compare"(%arg0, %arg1) {
      comparison_direction = #stablehlo<comparison_direction GE>
    } : (tensor<i64>, tensor<i64>) -> tensor<i1>
    "stablehlo.return"(%0) : (tensor<i1>) -> ()
}, {
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
    "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  window_dimensions = array<i64: 3, 1>,
  window_strides = array<i64: 2, 1>,
  padding = dense<[[0, 1], [0, 0]]> : tensor<2x2xi64>
} : (tensor<4x2xi64>, tensor<2x2xi64>, tensor<i64>) -> tensor<4x2xi64>
// %result: [[0, 0], [0, 0], [5, 14], [7, 0]]

Więcej przykładów

wyślij

Semantyka

Wysyła kod inputs do kanału channel_id i tworzy token result.

Jeśli is_host_transfer to true, operacja przesyła dane do hosta. W przeciwnym razie dane są przenoszone na inne urządzenie. Co to oznacza, jest bardzo skomplikowana. Ta flaga powiela informacje podane w channel_type, dlatego w przyszłości planujemy zachować tylko jedną z nich (#666).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) inputs liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów
(I2) token token
(I3) channel_id stała typu si64
(I4) channel_type wyliczenie DEVICE_TO_DEVICE i DEVICE_TO_HOST (C1)
(I5) is_host_transfer stała typu i1 (C1)

Wyniki

Nazwa Typ
result token

Ograniczenia

  • (C1) Parametr channel_type jest zdefiniowany jako:
    • DEVICE_TO_HOST jeśli is_host_transfer = true,
    • W przeciwnym razie: DEVICE_TO_DEVICE.

Przykłady

%result = "stablehlo.send"(%operand, %token) {
  channel_handle = #stablehlo.channel_handle<handle = 1, type = 2>,
  is_host_transfer = true
} : (tensor<2x2xi64>, !stablehlo.token) -> !stablehlo.token

Więcej przykładów

shift_left

Semantyka

Wykonuje operację przesunięcia w lewo względem elementu na tensorze lhs o liczbie rhs i generuje tensor result.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs tensor typu liczby całkowitej (C1)
(I2) rhs tensor typu liczby całkowitej (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu liczby całkowitej (C1)

Ograniczenia

  • (C1) type(lhs) = type(rhs) = type(result).

Przykłady

// %lhs: [-1, 0, 1]
// %rhs: [1, 2, 3]
%result = "stablehlo.shift_left"(%lhs, %rhs): (tensor<3xi64>, tensor<3xi64>) -> tensor<3xi64>
// %result: [-2, 0, 8]

Więcej przykładów

shift_right_arithmetic

Semantyka

Wykonuje operację arytmetycznego przesunięcia w prawo na tensorze lhs o Liczba bitów: rhs. Generuje tensor result.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs tensor typu liczby całkowitej (C1)
(I2) rhs tensor typu liczby całkowitej (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu liczby całkowitej (C1)

Ograniczenia

  • (C1) type(lhs) = type(rhs) = type(result).

Przykłady

// %lhs: [-1, 0, 8]
// %rhs: [1, 2, 3]
%result = "stablehlo.shift_right_arithmetic"(%lhs, %rhs): (tensor<3xi64>, tensor<3xi64>) -> tensor<3xi64>
// %result: [-1, 0, 1]

Więcej przykładów

shift_right_logical

Semantyka

Wykonuje operację logicznego przesunięcia w prawo według elementu na tensorze lhs o rhs określoną liczbę bitów i tworzy tensor result.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs tensor typu liczby całkowitej (C1)
(I2) rhs tensor typu liczby całkowitej (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu liczby całkowitej (C1)

Ograniczenia

  • (C1) type(lhs) = type(rhs) = type(result).

Przykłady

// %lhs: [-1, 0, 8]
// %rhs: [1, 2, 3]
%result = "stablehlo.shift_right_logical"(%lhs, %rhs): (tensor<3xi64>, tensor<3xi64>) -> tensor<3xi64>
// %result: [9223372036854775807, 0, 1]

Więcej przykładów

podpisywanie

Semantyka

Zwraca znak elementu operand i tworzy tensor result. Bardziej formalnie semantyka każdego elementu x można wyrazić za pomocą Jak wygląda składnia języka Python:

def sign(x):
  if is_integer(x):
    if compare(x, 0, LT, SIGNED): return -1
    if compare(x, 0, EQ, SIGNED): return 0
    return 1
  elif is_float(x):
    if is_nan(x): return NaN
    if compare(x, -0.0, EQ, FLOAT): return -0.0
    if compare(x, +0.0, EQ, FLOAT): return +0.0
    if compare(x, 0.0, LT, FLOAT): return -1.0
    return 1.0
  elif is_complex(x):
    if is_nan(real(x)) or is_nan(imag(x)): return (NaN, NaN)
    if compare(x, (0.0, 0.0), EQ, FLOAT): return (0.0, 0.0)
    return divide(x, convert(abs(x), type(x)))

W przypadku typów kwantowych dequantize_op_quantize(sign, operand, type(result))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu liczby całkowitej, zmiennoprzecinkowego, typu złożonego lub kwantyzowanego tensora według tensora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu liczby całkowitej, zmiennoprzecinkowego, typu złożonego lub kwantyzowanego tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// Logical values: +NaN, -1.0, -0.0, +0.0, 1.0
// operand: [0x7FFFFFFFFFFFFFFF, -1.0, -0.0, 0.0, 1.0]
%result = "stablehlo.sign"(%operand) : (tensor<5xf64>) -> tensor<5xf64>
// Logical values: +NaN, -1.0, -0.0, +0.0, 1.0
// %result: [0x7FFFFFFFFFFFFFFF, -1.0, -0.0, 0.0, 1.0]

Więcej przykładów

sinus

Semantyka

Wykonuje operację sinusową uwzględniającą element na tensorze operand i tworzy result tensora. W zależności od typu elementu wykonuje te działania:

  • W przypadku jednostek zmiennoprzecinkowych: sin w standardzie IEEE-754.
  • W przypadku liczb zespolonych: sinus zespolony.
  • W przypadku typów kwantowych: dequantize_op_quantize(sine, operand, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %operand: [
//            [0.0, 1.57079632],       // [0, pi/2]
//            [3.14159265, 4.71238898] // [pi, 3pi/2]
//           ]
%result = "stablehlo.sine"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[0.0, 1.0], [0.0, -1.0]]

Więcej przykładów

wycinek

Semantyka

Wyodrębnia wycinek z operand przy użyciu statycznie obliczonych indeksów początkowych i tworzy tensor result. start_indices zawierają początkowe indeksy wycinek każdego wymiaru, limit_indices zawiera indeksy końcowe (wyłącznie) dla wycinka dla każdego wymiaru, a strides zawiera kroki dla każdego wymiaru.

Bardziej oficjalnie: result[result_index] = operand[operand_index], gdzie: operand_index = start_indices + result_index * strides

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand kwantowy tensor lub tensor kwantowy (C1–C3), (C5)
(I2) start_indices Jednowymiarowa stała tensora typu si64 (C2), (C3), (C5)
(I3) limit_indices Jednowymiarowa stała tensora typu si64 (C2), (C3), (C5)
(I4) strides Jednowymiarowa stała tensora typu si64 (C2), (C4)

Wyniki

Nazwa Typ Ograniczenia
result kwantowy tensor lub tensor kwantowy (C1), (C5)

Ograniczenia

  • (C1) element_type(operand) = element_type(result).
  • (C2) size(start_indices) = size(limit_indices) = size(strides) = rank(operand).
  • (C3) 0 <= start_indices <= limit_indices <= shape(operand).
  • (C4) 0 < strides.
  • (C5) shape(result) = ceil((limit_indices - start_indices) / strides).

Przykłady

// %operand: [
//            [0, 0, 0, 0],
//            [0, 0, 1, 1],
//            [0, 0, 1, 1]
//           ]
%result = "stablehlo.slice"(%operand) {
  start_indices = array<i64: 1, 2>,
  limit_indices = array<i64: 3, 4>,
  strides = array<i64: 1, 1>
} : (tensor<3x4xi64>) -> tensor<2x2xi64>
// % result: [
//            [1, 1],
//            [1, 1]
//           ]

Więcej przykładów

sortuj

Semantyka

Sortuje razem jednowymiarowe wycinki kolumny inputs wzdłuż wymiaru dimension, według comparator i daje results.

W przeciwieństwie do podobnych danych wejściowych w innych operacjach dimension zezwala na wartości ujemne, z użyciem semantyki opisanej poniżej. W przyszłości może to być niedozwolone w celu zapewnienia spójności (#1377).

Jeśli is_stable ma wartość prawda, sortowanie jest stabilne, czyli kolejność względna elementy uznawane za równe przez funkcję są zachowywane. Etui w przypadku gdy występuje jedna wartość wejściowa, dwa elementy e1 i e2 są uważane za jest równa przez komparator tylko wtedy, gdy i tylko wtedy, comparator(e1, e2) = comparator(e2, e1) = false Poniżej znajdziesz opis formalności. jak to uogólnia do wielu danych wejściowych.

Więcej formalnie dla wszystkich result_index w index_space(results[0]):

  • adjusted_dimension = dimension >= 0 ? dimension : rank(inputs[0]) + dimension.
  • result_slice = [ri0, ..., :, ..., riR-1], gdzie riN to osoby indywidualne elementów w elemencie result_index, a : jest wstawiony w miejscu adjusted_dimension.
  • inputs_together = (inputs[0]..., ..., inputs[N-1]...).
  • results_together[result_slice] = sort(inputs_together[result_slice], comparator_together).
  • gdzie sort sortuje jednowymiarowy wycinek w kolejności niemalejącej, oczekując że funkcja comparator_together zwraca true, jeśli argument po lewej stronie to jest mniejszy niż argument drugiej strony po prawej stronie.
  • def comparator_together(lhs_together, rhs_together):
      args = []
      for (lhs_el, rhs_el) in zip(lhs_together, rhs_together):
        args.append(lhs_el)
        args.append(rhs_el)
      return comparator(*args)
    
  • (results[0]..., ..., results[N-1]...) = results_together.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) inputs liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów na tensor (K1–C5)
(I2) dimension stała typu si64 (K4)
(I3) is_stable stała typu i1
(I4) comparator funkcja (K5)

Wyniki

Nazwa Typ Ograniczenia
results liczba zmiennoprzecinkowa tensorów lub kwantyzowanych tensorów na tensor (C2), (C3)

Ograniczenia

  • (C1) 0 < size(inputs).
  • (C2) type(inputs...) = type(results...).
  • (C3) same(shape(inputs...) + shape(results...)).
  • (C4) -R <= dimension < R, gdzie R = rank(inputs[0]).
  • (C5) comparator ma typ (tensor<E1>, tensor<E1>, ..., tensor<EN-1>, tensor<EN-1>) -> tensor<i1>, gdzie Ei = element_type(inputs[i]).

Przykłady

// %input0 = [[1, 2, 3], [3, 2, 1]]
// %input1 = [[3, 2, 1], [1, 2, 3]]
%result0, %result1 = "stablehlo.sort"(%input0, %input1) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>, %arg2: tensor<i64>, %arg3: tensor<i64>):
    %predicate = "stablehlo.compare"(%arg0, %arg1) {
      comparison_direction = #stablehlo<comparison_direction GT>
    } : (tensor<i64>, tensor<i64>) -> tensor<i1>
    "stablehlo.return"(%predicate) : (tensor<i1>) -> ()
}) {
  dimension = 0 : i64,
  is_stable = true
} : (tensor<2x3xi64>, tensor<2x3xi64>) -> (tensor<2x3xi64>, tensor<2x3xi64>)
// %result0 = [[3, 2, 3], [1, 2, 1]]
// %result1 = [[1, 2, 1], [3, 2, 3]]

Więcej przykładów

sqrt

Semantyka

Wykonuje operację pierwiastka kwadratowego związaną z elementami na tensorze operand i tworzy Tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku jednostek zmiennoprzecinkowych: squareRoot w standardzie IEEE-754.
  • W przypadku liczb zespolonych: pierwiastek kwadratowy zespolony.
  • W przypadku typów kwantowych: dequantize_op_quantize(sqrt, operand, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %operand: [[0.0, 1.0], [4.0, 9.0]]
%result = "stablehlo.sqrt"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[0.0, 1.0], [2.0, 3.0]]

Więcej przykładów

odejmij

Semantyka

Wykonuje odejmowanie elementów z zakresu dwóch tensorów lhs i rhs, generujący wartość Tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku liczb całkowitych: odejmowanie liczby całkowitej.
  • W przypadku jednostek zmiennoprzecinkowych: subtraction w standardzie IEEE-754.
  • W przypadku liczb zespolonych: odejmowanie zespolone.
  • W przypadku typów kwantowych:
    • dequantize_op_quantize(subtract, lhs, rhs, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs tensor typu całkowitej, zmiennoprzecinkowego, zespolonego lub kwantyzowany tensora według tensora (C1)
(I2) rhs tensor typu całkowitej, zmiennoprzecinkowego, zespolonego lub kwantyzowany tensora według tensora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu całkowitej, zmiennoprzecinkowego, zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(lhs) = baseline_type(rhs) = baseline_type(result).

Przykłady

// %lhs: [[6, 8], [10, 12]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.subtract"(%lhs, %rhs) : (tensor<2x2xf32>, tensor<2x2xf32>) -> (tensor<2x2xf32>)
// %result: [[1, 2], [3, 4]]

Więcej przykładów

tan

Semantyka

Wykonuje operację styczną względem elementu na tensorze operand i tworzy Tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku jednostek zmiennoprzecinkowych: tan w standardzie IEEE-754.
  • W przypadku liczb zespolonych: tangens zespolony.
  • W przypadku typów kwantowych: dequantize_op_quantize(tan, operand, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %operand: [
//            [0.0, 1.57079632],       // [0, pi/2]
//            [3.14159265, 4.71238898] // [pi, 3pi/2]
//           ]
%result = "stablehlo.tan"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [
//           [0.0, 1.63312e+16],
//           [0.0, 5.44375e+15]
//          ]

Więcej przykładów

Tanh

Semantyka

Wykonuje operację tangens hiperbolicznego z uwzględnieniem elementu na tensorze operand i generuje tensor result. W zależności od typu elementu wykonuje te działania:

  • W przypadku jednostek zmiennoprzecinkowych: tanh w standardzie IEEE-754.
  • W przypadku liczb zespolonych: tangens hiperboliczny zespolony.
  • W przypadku typów kwantowych:
    • dequantize_op_quantize(tanh, operand, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_type(operand) = baseline_type(result).

Przykłady

// %operand: [-1.0, 0.0, 1.0]
%result = "stablehlo.tanh"(%operand) : (tensor<3xf32>) -> tensor<3xf32>
// %result: [-0.76159416, 0.0, 0.76159416]

Więcej przykładów

transponować

Semantyka

Permutekuje wymiary tensora operand za pomocą permutation i tworzy Tensor result. Więcej formalnie: result[result_index] = operand[operand_index] gdzie result_index[d] = operand_index[permutation[d]].

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor (tensor kwantowy) (K1–C4)
(I2) permutation Jednowymiarowa stała tensora typu si64 (C2–C4)

Wyniki

Nazwa Typ Ograniczenia
result tensor (tensor kwantowy) (C1), (C3–C4)

Ograniczenia

  • (C1) element_type(result) otrzymuje:
    • element_type(operand), jeśli !is_per_axis_quantized(operand).
    • element_type(operand) oprócz quantization_dimension(operand) i Wartość quantization_dimension(result) może się różnić. W przeciwnym razie.
  • (C2) permutation to permutacja słowa range(rank(operand)).
  • (C3) shape(result) = dim(operand, permutation...).
  • (C4) Jeśli is_per_axis_quantized(result), to quantization_dimension(operand) = permutation(quantization_dimension(result))

Przykłady

// %operand: [
//            [[1,2], [3,4], [5,6]],
//            [[7,8], [9,10], [11,12]]
//           ]
%result = "stablehlo.transpose"(%operand) {
  permutation = array<i64: 2, 1, 0>
} : (tensor<2x3x2xi32>) -> tensor<2x3x2xi32>
// %result: [
//           [[1,7], [3,9], [5,11]],
//           [[2,8], [4,10], [6,12]]
//          ]

Więcej przykładów

triangular_solve

Semantyka

Rozwiąż grupy układów równań liniowych z dolną lub górną krzywą trójkąta macierze współczynników.

Bardziej oficjalnie, biorąc pod uwagę wartości a i b, rozwiązaniem jest result[i0, ..., iR-3, :, :]. do op(a[i0, ..., iR-3, :, :]) * x = b[i0, ..., iR-3, :, :], gdy left_side jest true lub x * op(a[i0, ..., iR-3, :, :]) = b[i0, ..., iR-3, :, :], gdy left_side wynosi false, rozwiązanie zmiennej x, gdzie op(a) jest określone do transpose_a, która może być jedną z tych wartości:

  • NO_TRANSPOSE: wykonaj operację a w niezmienionej postaci.
  • TRANSPOSE: wykonaj operację transponowania zakresu a.
  • ADJOINT: wykonaj operację na transpozycji sprzężonej a.

Dane wejściowe są odczytywane tylko z dolnego trójkąta a, jeśli lower ma wartość true lub górny trójkąt dla a, w przeciwnym razie. Dane wyjściowe są zwracane w tym samym trójkącie; wartości w drugim trójkącie są definiowane przez implementację.

Jeśli unit_diagonal ma wartość prawda, implementacja może zakładać, że po przekątnej elementy a mają wartość 1, w przeciwnym razie działanie jest niezdefiniowane.

W przypadku typów kwantowych dequantize_op_quantize(lambda x, y: triangular_solve(x, y, left_side, lower, unit_diagonal, transpose_a), a, b, type(result))

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) a tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (K1–C3)
(I2) b tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (K1–C4)
(I3) left_side stała typu i1 (K3)
(I4) lower stała typu i1
(I5) unit_diagonal stała typu i1
(I6) transpose_a wyliczenie NO_TRANSPOSE, TRANSPOSE i ADJOINT

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego lub zespolonego lub kwantyzowany tensora według tensora (C1)

Ograniczenia

  • (C1) baseline_element_type(a) = baseline_element_type(b).
  • (C2) 2 <= rank(a) = rank(b) = R.
  • (C3) Relacja między stronami shape(a) i shape(b) jest zdefiniowana w ten sposób:
    • shape(a)[:-3] = shape(b)[:-3].
    • dim(a, -2) = dim(a, -1) = dim(b, left_side ? -2 : -1).
  • (C4) baseline_type(b) = baseline_type(result).

Przykłady

// %a = [
//       [1.0, 0.0, 0.0],
//       [2.0, 4.0, 0.0],
//       [3.0, 5.0, 6.0]
//      ]
// %b = [
//       [2.0, 0.0, 0.0],
//       [4.0, 8.0, 0.0],
//       [6.0, 10.0, 12.0]
//      ]
%result = "stablehlo.triangular_solve"(%a, %b) {
  left_side = true,
  lower = true,
  unit_diagonal = false,
  transpose_a = #stablehlo<transpose NO_TRANSPOSE>
} : (tensor<3x3xf32>, tensor<3x3xf32>) -> tensor<3x3xf32>
// %result: [
//           [2.0, 0.0, 0.0],
//           [0.0, 2.0, 0.0],
//           [0.0, 0.0, 2.0]
//          ]

krotka

Semantyka

Generuje krotkę result z wartości val.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) val liczba zmiennoprzecinkowa (C1)

Wyniki

Nazwa Typ Ograniczenia
result krotka (C1)

Ograniczenia

  • (C1) result ma typ tuple<E0, ..., EN-1>, gdzie Ei = type(val[i]).

Przykłady

// %val0: [1.0, 2.0]
// %val1: (3)
%result = "stablehlo.tuple"(%val0, %val1) : (tensor<2xf32>, tuple<tensor<i32>>) -> tuple<tensor<2xf32>, tuple<tensor<i32>>>
// %result: ([1.0, 2.0], (3))

Więcej przykładów

uniform_dequantize

Semantyka

Przeprowadza konwersję tensora kwantyzowanego operand z punktu widzenia elementów na tensor zmiennoprzecinkowy result zgodnie z określonymi parametrami kwantyzacji według typu operand.

Więcej formalnie: result = dequantize(operand).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor kwantowy (C1), (C2)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu zmiennoprzecinkowego (C1), (C2)

Ograniczenia

  • (C1) shape(operand) = shape(result).
  • (C2) element_type(result) = expressed_type(operand).

Przykłady

// %operand: [10, 10]
%result = "stablehlo.uniform_dequantize"(%operand) : (tensor<2x!quant.uniform<i8:f32:0, {0.1:-30,0.5:-20}>>) -> tensor<2xf32>
// %result: [4.0, 15.0]

uniform_quantize

Semantyka

Wykonuje konwersję tensora zmiennoprzecinkowego lub kwantowego tensora z wykorzystaniem elementu operand do kwantyzowanego tensora result zgodnie z kwantyzacją parametry zdefiniowane przez typ result.

Bardziej oficjalnie,

  • Jeśli is_float(operand):
    • result = quantize(operand, type(result)).
  • Jeśli is_quantized(operand):
    • float_result = dequantize(operand).
    • result = quantize(float_result, type(result)).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand tensor typu zmiennoprzecinkowego lub kwantowego (C1), (C2)

Wyniki

Nazwa Typ Ograniczenia
result tensor kwantowy (C1), (C2)

Ograniczenia

  • (C1) shape(operand) = shape(result).
  • (C2) expressed_type(result) = is_float(operand) ? element_type(operand) : expressed_type(operand).

Przykłady

// %operand: [4.0, 15.0]
%result = "stablehlo.uniform_quantize"(%operand) : (tensor<2xf32>) -> tensor<2x!quant.uniform<i8:f32:0, {0.1:-30,0.5:-20}>>
// %result: [10, 10]

// %operand: [10, 10]
%result = "stablehlo.uniform_quantize"(%operand) : (tensor<2x!quant.uniform<i8:f32:0, {0.1:-30,0.5:-20}>>) -> tensor<2x!quant.uniform<i8:f32:0, {0.1:-20,0.2:-30}>>
// %result: [20, 45]

podczas gdy

Semantyka

Podaje dane wyjściowe z wykonania funkcji body co najmniej 0 razy, podczas gdy Funkcja cond zwraca true. Bardziej oficjalnie semantyka można wyrazić używając składni Pythona w ten sposób:

internal_state = operand
while cond(*internal_state):
  internal_state = body(*internal_state)
results = internal_state

Działanie pętli nieskończonej do ustalenia (383).

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) operand zmiennoprzecinkowa liczba tensorów, skwantyzowanych tensorów lub tokenów (K1–C3)
(I2) cond funkcja (C1)
(I3) body funkcja (K2)

Wyniki

Nazwa Typ Ograniczenia
results zmiennoprzecinkowa liczba tensorów, skwantyzowanych tensorów lub tokenów (K3)

Ograniczenia

  • (C1) cond ma typ (T0, ..., TN-1) -> tensor<i1>, gdzie Ti = type(operand[i])
  • (C2) body ma typ (T0, ..., TN-1) -> (T0, ..., TN-1), gdzie Ti = type(operand[i])
  • (C3) type(results...) = type(operand...).

Przykłady

// %init_i: 1
// %init_sum: 0
// %one: 1
// %ten: 10
%results0, %results1 = "stablehlo.while"(%init_i, %init_sum) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %cond = "stablehlo.compare"(%arg0, %ten) {
      comparison_direction = #stablehlo<comparison_direction LT>
    } : (tensor<i64>, tensor<i64>) -> tensor<i1>
    stablehlo.return %cond : tensor<i1>
  }, {
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %new_sum = stablehlo.add %arg1, %one : tensor<i64>
    %new_i = stablehlo.add %arg0, %one : tensor<i64>
    stablehlo.return %new_i, %new_sum : tensor<i64>, tensor<i64>
}) : (tensor<i64>, tensor<i64>) -> (tensor<i64>, tensor<i64>)
// %results0: 10
// %results1: 10

Więcej przykładów

Xor

Semantyka

Wykonuje funkcję XOR dotyczącą elementu odnoszącą się do dwóch tensorów lhs i rhs, generując result tensora. W zależności od typu elementu wykonuje te działania:

  • W przypadku wartości logicznych: logiczna XOR.
  • W przypadku liczb całkowitych: bitowa wartość XOR.

Dane wejściowe

Etykieta Nazwa Typ Ograniczenia
(I1) lhs tensor typu wartości logicznej lub liczby całkowitej (C1)
(I2) rhs tensor typu wartości logicznej lub liczby całkowitej (C1)

Wyniki

Nazwa Typ Ograniczenia
result tensor typu wartości logicznej lub liczby całkowitej (C1)

Ograniczenia

  • (C1) type(lhs) = type(rhs) = type(result).

Przykłady

// Bitwise operation with with integer tensors
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.xor"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[4, 4], [4, 12]]

// Logical operation with with boolean tensors
// %lhs: [[false, false], [true, true]]
// %rhs: [[false, true], [false, true]]
%result = "stablehlo.xor"(%lhs, %rhs) : (tensor<2x2xi1>, tensor<2x2xi1>) -> tensor<2x2xi1>
// %result: [[false, true], [true, false]]

Więcej przykładów

Dialect Interop,

Obecnie programy StableHLO działające na wolności czasami zawierają operacje, które nie są definiowane przez StableHLO.

Moduł, funkcja, Połączenie i zwrot

StableHLO używa nadrzędnych operacji MLIR dla ModuleOp, FuncOp, CallOp i ReturnOp. W ten sposób ulepszono współpracę z dotychczasowymi maszynami MLIR, przydatnych kart jest napisanych dla FuncOp i ModuleOp, a wiele kompilacji potoki wymagają dostępności tych operacji. Gwarancje pełnej zgodności: dla tych operacji. Jeśli cokolwiek w tych działaniach się zmieni (tj. w wyniku usunięcia) zostaną dodane równoważne odpowiedniki StableHLO w celu zachowania zgodność.

CHLO

Opset CHLO zawiera operacje wyższego poziomu, które rozkładają się na StableHLO. Obecnie nie ma gwarancji zgodności dla CHLO. Zgodność przepustka chlo-legalize-to-stablehlo należy użyć przed serializacją.

Operacje na kształtach

W społeczności powszechnym przypadkiem użycia jest wykorzystywanie określonych operacji podstawowych Dialekty MLIR w dynamicznych programach StableHLO służących do obliczania kształtów. Najczęściej są to shape dialekt operacje takie jak shape_of lub num_elements, tensor dialekt operacje takie jak dim lub from_elements, oraz wbudowany typ index.

Dokument RFC Dynamizmu > O2 oznacza, że te typy nie są objęte, jednak niektóre obsługiwane typy index są uwzględnianych na potrzeby interoperacyjności. Nie ma gwarancji zgodności. operacji ani typów. Element shape-legalize-to-stablehlo mogą być używane do konwertowania tych operacji na w pełni obsługiwane operacje StableHLO.

Wycofane operacje

Istnieje kilka operacji StableHLO odziedziczonych z MHLO które są wycofywane i wkrótce znikną ze standardu StableHLO. Pełne informacje na temat tych Informacje na ten temat znajdziesz w artykule StableHLO v1.0 Cleanup #2283. W przypadku tych wycofanych rozwiązań występuje problem z lokalizatorem to #2340.

Operacje te można podzielić na kilka kategorii:

  • „Brak w HLO” kategorii operacji StableHLO. Początkowo były one częścią wersji StableHLO, ale później uznano, że do niej nie pasuje: broadcast, create_token, cross-replica-sum, dot, einsum torch_index_select, unary_einsum (#3).
  • Nieużywane operacje – te operacje mogą być kiedyś przydatne, ale operacje były niedopracowane albo potoki korzystające z tych operacji zostały zostały zmodyfikowane i nie są już potrzebne. Obejmuje to map, tuple (#598), Porównania get_tuple_element, rng, complex w #560, i splot window_reversal (#1181).

Niektóre z tych operacji można łatwo usunąć, ponieważ można ich używać za pomocą istniejące operacje (broadcast, create_token, cross-replica-sum, dot, unary_einsum) i zostaną usunięte po obecnym oknie zgodności karnety (6 miesięcy). Inne są nadal analizowane do usunięcia (einsum, get_tuple_element, map, rng, torch_index_select, tuple, complex porównania, window_reversal). Czekam na opinię społeczności, te operacje zostaną usunięte lub dodane do specyfikacji z pełną obsługą. Do te przyszłe wersje operacji są znane, zgodność jest gwarantowana tylko przez 6 miesięcy.

Wykonanie

Wykonywanie sekwencyjne

Program StableHLO jest wykonywany przez podanie wartości wejściowych do funkcji main i obliczania wartości wyjściowych. Wartości wyjściowe funkcji są obliczane według wzoru i wykonanie grafu operacji powiązanych z odpowiednimi operacjami return.

Zlecenie wykonania jest zdefiniowane, o ile jest zgodne z dataflow, czyli jeśli operacje zostaną wykonane przed ich użyciem. W StableHLO wszystkie operacje zewnętrzne wykorzystują 1 token i generują 1 token (wiele tokenów być multipleksowane do jednego tokena przez after_all), więc kolejność wykonania dla strony a także efekty jej dostosowania do Dataflow. Na przykład w programie poniżej możliwe są 2 zamówienia wykonania: %0%1%2return i %1%0%2return.

func.func @main() -> tensor<f64> {
  %0 = stablehlo.constant dense<1.0> : tensor<f64>
  %1 = stablehlo.constant dense<2.0> : tensor<f64>
  %2 = stablehlo.add %0, %1 : tensor<f64>
  return %2 : tensor<f64>
}

Bardziej oficjalnie proces StableHLO składa się z połączenia tych elementów: 1) program StableHLO, 2) stany operacji (jeszcze niewykonane, już wykonane) oraz 3) wartości pośrednich, nad którymi pracuje proces. Proces zaczyna się od wartości wejściowych do funkcji main, a potem przechodzi przez wykres operacji aktualizujących stany operacji i wartości pośrednie oraz kończy się wartościami wyjściowymi. Dalsza formalizacja do ustalenia (#484).

Wykonywanie równoległe

Programy StableHLO mogą być wykonywane równolegle, zorganizowane w siatkę procesów 2D z num_replicas przez num_partitions, które mają typ ui32.

W siatce procesów StableHLO znajduje się num_replicas * num_partitions HLO. które są wykonywane w tym samym czasie. Każdy proces ma swój unikalny process_id = (replica_id, partition_id), gdzie replica_id w: replica_ids = range(num_replicas) i partition_id w grupie partition_ids = range(num_partitions), które mają wpisz ui32.

Rozmiar siatki procesów jest znany statycznie dla każdego programu (w planujemy dodać go do programów StableHLO. #650) i położenie w siatce procesów jest statycznie znana dla każdego procesu. Każdy proces ma dostęp do swojego położenia w siatce procesów za pomocą funkcji replica_id i Operacje: partition_id

W siatce procesów wszystkie programy mogą być takie same (w sekcji „Pojedyncze Program, wiele danych style), mogą być różne (w sekcji „Wiele programów Wiele danych stylu) lub czegoś pomiędzy. Planujemy w przyszłości aby wprowadzić obsługę innych idiomów definiujących równoległe programy StableHLO, w tym GSPMD (#619).

W siatce procesów procesy są w większości od siebie niezależne – mają oddzielne stany operacji, oddzielne wartości wejściowe/pośrednie/wyjściowe a większość działań jest wykonywana niezależnie między procesami, przy czym z wyjątkiem niewielkiej liczby działań zbiorowych opisanych poniżej.

Biorąc pod uwagę, że wykonanie większości operacji korzysta tylko z wartości z tego samego używanie tych wartości za pomocą nazw jest zazwyczaj jednoznaczne. Jednak z punktu widzenia semantyki działań zbiorowych jest to niewystarczające. rozwija się zapis name@process_id odnoszący się do wartości name w konkretnym procesie. (Z tego punktu widzenia osoby, które nie spełniają kryteriów name, mogą być jako skrót od name@(replica_id(), partition_id())).

Kolejność wykonywania w różnych procesach jest zdefiniowana w implementacji, z wyjątkiem synchronizacja wprowadzana przez komunikację punkt-punkt i działania zbiorowe jak opisano poniżej.

Komunikacja punkt-punkt

Procesy StableHLO mogą komunikować się ze sobą za pomocą Kanały StableHLO. Kanał jest reprezentowany przez dodatni identyfikator typu. si64 Za pomocą różnych operacji można wysyłać wartości do kanałów i otrzymywać je na kanałach.

Dalsza formalizacja, np. skąd pochodzą te identyfikatory kanałów, jak programy uświadamiają sobie ich zjawisko i rozumieją, jaki jest rodzaj synchronizacji, został przez nich wprowadzony, do ustalenia. (#484).

Komunikacja strumieniowa

Każdy proces StableHLO ma dostęp do 2 interfejsów strumieniowania:

  • In-Feed, z których można odczytać treści.
  • Out-Feed, w którym można zapisywać treści.

W przeciwieństwie do kanałów, które służą do komunikacji między procesami i dlatego mają procesy po obu stronach, a kanały In-Feed i out-Feed – zdefiniowaną końcową implementację.

Dalsza formalizacja, np. jak transmisja strumieniowa wpływa na realizację kolejność i sposób synchronizacji, do ustalenia (#484).

Operacje zbiorowe

W StableHLO jest sześć operacji zbiorowych: all_gather, all_reduce, all_to_all, collective_broadcast, collective_permute i reduce_scatter. Wszystkie te operacje dzielą procesy w procesie StableHLO do grup procesów StabilHLO i wykonywać w nich wspólne obliczenia każdej grupy procesów niezależnie od innych grup procesów.

W każdej grupie procesów zespoły działań operacyjnych mogą wprowadzić synchronizację Bariera. Dalsza formalizacja, np. precyzowanie, kiedy dokładnie to jak przebiega synchronizacja, jak przebiegają procesy, a co się stanie, jeśli nie, (#484).

Jeśli grupa procesów obejmuje komunikację między partycjami, tzn. procesy w grupie procesów, których identyfikatory partycji są różne, a następnie wykonanie zespół potrzebuje kanału, który zbiorowy musi zapewnić dodatni channel_id typu si64. Komunikacja między replikami nie jest wymagana kanałów.

Obliczenia wykonywane przez operacje zbiorcze zależą od poszczególnych działań. i zostały opisane w poszczególnych sekcjach powyżej. Strategie stosowane przez w którym siatka procesów jest podzielona na grupy procesów, które są współużytkowane przez te operacje i zostały opisane w tej sekcji. Oficjalnie zespół StableHLO obsługuje 4 strategii.

cross_replica

W ramach każdej grupy procesów odbywa się tylko komunikacja między replikami. Ten strategia przyjmuje replica_groups – listę list identyfikatorów replik – i obliczeń iloczyn kartezjański replica_groups, źródło: partition_ids. replica_groups musi zawierać unikalne elementy i obejmować cały replica_ids. Bardziej oficjalnie, korzystając z Składnia Pythona:

def cross_replica(replica_groups: List[List[ReplicaId]]) -> List[List[ProcessId]]:
  for replica_group in replica_groups:
    for partition_id in partition_ids:
      process_group = []
      for replica_id in replica_group:
        process_group.append((replica_id, partition_id))
      yield process_group

Na przykład w przypadku kolumn replica_groups = [[0, 1], [2, 3]] i num_partitions = 2, cross_replica wygeneruje [[(0, 0), (1, 0)], [(0, 1), (1, 1)], [(2, 0), (3, 0)], [(2, 1), (3, 1)]]

cross_partition

W ramach każdej grupy procesów odbywa się tylko komunikacja międzypartyjna. Ten strategia pobiera partition_groups – listę list identyfikatorów partycji – oraz oblicza iloczyn kartezjański partition_groups przez replica_ids. partition_groups musi zawierać unikalne elementy i pokrywać wszystkie elementy (partition_ids). Bardziej oficjalnie, używając składni Pythona:

def cross_partition(partition_groups: List[List[PartitionId]]) -> List[List[ProcessId]]:
  for partition_group in partition_groups:
    for replica_id in replica_ids:
      process_group = []
      for partition_id in partition_group:
        process_group.append((replica_id, partition_id))
      yield process_group

Na przykład w przypadku kolumn partition_groups = [[0, 1]] i num_replicas = 4, cross_partition wygeneruje [[(0, 0), (0, 1)], [(1, 0), (1, 1)], [(2, 0), (2, 1)], [(3, 0), (3, 1)]]

cross_replica_and_partition

W ramach każdego w ramach grupy procesów. Ta strategia zajmuje replica_groups – listę list identyfikatory replik – i oblicza iloczyn kartezjański każdego parametru replica_group przez partition_ids replica_groups musi zawierać unikalne elementy i obejmować wszystkie replica_ids Bardziej oficjalnie, używając składni Pythona:

def cross_replica_and_partition(replica_groups: List[List[ReplicaId]]) -> List[List[ProcessId]]:
  for replica_group in replica_groups:
    process_group = []
    for partition_id in partition_ids:
      for replica_id in replica_group:
        process_group.append((replica_id, partition_id))
    yield process_group

Na przykład w przypadku kolumn replica_groups = [[0, 1], [2, 3]] i num_partitions = 2, cross_replica_and_partition wygeneruje [[(0, 0), (1, 0), (0, 1), (1, 1)], [(2, 0), (3, 0), (2, 1), (3, 1)]]

flattened_ids

Ta strategia przyjmuje flattened_id_groups – listę list „spłaszczonych” identyfikatory procesów w postaci replica_id * num_partitions + partition_id - oraz przekształca je w identyfikatory procesów. flattened_id_groups musi zawierać unikalne elementy i obejmują wszystkie te typy danych: process_ids. Bardziej oficjalnie, używając składni Pythona:

def flattened_ids(flattened_id_groups: List[List[ui32]]) -> List[List[ProcessId]]:
  for flattened_id_group in flattened_id_groups:
    process_group = []
    for flattened_id in flattened_id_group:
      replica_id = flattened_id // num_partitions
      partition_id = flattened_id % num_partitions
      process_group.append((replica_id, partition_id))
    yield process_group

Na przykład w polu flattened_id_groups = [[0, 1, 2, 3], [4, 5, 6, 7]], num_replicas = 4 i num_partitions = 2, flattened_ids wygeneruje [[(0, 0), (0, 1), (1, 0), (1, 1)], [(2, 0), (2, 1), (3, 0), (3, 1)]].

Dokładność

Obecnie StableHLO nie gwarantuje dokładności liczbowej, ale w przyszłości może się to zmienić (#1156).

Semantyka wykonania operacji kwantowej

Interpretacja skwantyzowanych operacji StableHLO może się różnić w zależności od wymagania i możliwości sprzętowe. Na przykład w przypadku niektórych urządzeń interpretuj operacje kwantowe za pomocą funkcji dekwantyzacji, wykonywania operacji zmiennoprzecinkowych a na koniec kwantyzować” strategii ustalania stawek. Inne mogą wykonać całą przy użyciu arytmetyki całkowitej. W związku z tym interpretacja kwantyzowane operacje StableHLO są określane wyłącznie na podstawie konkretnych implementacji. Interpretacja kwantyzacji hybrydowej (#1575) powinna być oparta na jej semantykę zgodnie ze specyfikacją (za pomocą 1792).

Błędy

Programy StableHLO są weryfikowane z użyciem zestawu ograniczeń przez poszczególne operacje, co wyklucza wiele klas błędów przed czasem uruchomienia. Nadal jednak mogą występować błędy, np. przez przepełnienia liczb całkowitych, dostępy poza granicami itp. Wszystkie te błędy (o ile nie zostaną wyraźnie wywołane) może wywołać zachowanie zależne od implementacji, ale może to się zmienić w przyszłości (#1157).

Wyjątki dotyczące liczby zmiennoprzecinkowej

Wyjątkiem od tej reguły są wyjątki zmiennoprzecinkowe w programach StableHLO są jasno zdefiniowane. Operacje, które powodują wyjątki zdefiniowane przez Standard IEEE-754 (nieprawidłowe działanie, dzielenie przez zero, przepełnienie, niedomiar lub nieprecyzyjne wyjątki) dają wyniki domyślne (zgodnie z definicją w standardzie) oraz kontynuuj wykonywanie kodu bez podnoszenia odpowiedniej flagi stanu; podobne do raiseNoFlag – obsługa wyjątku od standardu. Wyjątki dla wersji niestandardowych operacje (np. złożone funkcje arytmetyczne i niektóre funkcje transcendentalne), jest bardzo skomplikowana.

Niezgodność kształtu

StableHLO obsługuje tensory o dynamicznym kształcie. Kształty muszą się jednak zgodzić w środowisku wykonawczym. W przeciwnym razie działanie będzie niezdefiniowane. StableHLO nie wyraźnie zapewniają operację, która może potwierdzić, że tensor ma określony kształt w czasie działania. Za generowanie prawidłowego kodu odpowiada producent.

Konkretnym przykładem jest poniższy program. Jednak w czasie działania dokładne kształty %arg0 i %arg1 muszą być takie same, w przeciwnym razie zachowanie programu jest nieokreślone:

func.func @foo(%arg0: tensor<?xi32>, %arg1: tensor<?xi32>) -> tensor<?xi32> {
    %0 = stablehlo.add %arg0, %arg1 : tensor<?xi32>
    return %0 : tensor<?xi32>
}

Zapis

Do opisania składni w tym dokumencie użyto zmodyfikowanego rodzaju ISO EBNF (ISO/IEC 14977:1996, Wikipedia), z 2 modyfikacjami: 1) reguły są definiowane za pomocą parametru ::=, a nie =,

2) konkatenacja jest wyrażona za pomocą zestawienia, a nie ,.

Do opisywania semantyki (tj. w sekcjach „Typy”, „Stałe” i „Operacje”) korzystamy z formuł opartych na składni Pythona, która jest rozszerzona na potrzeby zwięzłego wyrażania operacji tablicowych, jak opisano poniżej. To się sprawdza w przypadku małych fragmentów kodu. W rzadkich przypadkach, gdy większe są fragmenty kodu, użyjemy składni języka vanilla w Pythonie, która jest zawsze wprowadzana w sposób jawny.

Wzory

Na przykładzie z dot_general omówimy, jak działają formuły specyfikacji. Jedno z ograniczeń tej operacji wygląda tak: dim(lhs, lhs_batching_dimensions...) = dim(rhs, rhs_batching_dimensions...)

Nazwy użyte w tej formule pochodzą z dwóch źródeł: 1) funkcji globalnych, tj. dim, 2) definicje członków odpowiedniego elementu programu, tj. Dane wejściowe lhs, lhs_batching_dimensions, rhs i rhs_batching_dimensions zdefiniowane w polu „Dane wejściowe” dot_general.

Jak już wspomnieliśmy, składnia tej formuły opiera się na języku Python i niektóre zwięzłości tekstu. Aby uzyskać sens tego wzoru, skonwertujmy w składnię Pythona waniliowego.

A) W tych formułach używamy wyrażenia = do przedstawienia równości, więc w pierwszym kroku w kierunku uzyskania składni Pythona, zastąp = ciągiem == w następujący sposób: dim(lhs, lhs_batching_dimensions...) == dim(rhs, rhs_batching_dimensions...)

B) Te formuły obsługują też wielokropki (...), które zwracają wyrażenia skalarne do wyrażeń tensorowych. W skrócie f(xs...) oznacza „dla każdej wartości wartość skalarna x w tensorze xs, oblicz wartość skalarną f(x), a następnie zwróć wszystkie wyniki te są łączone w tensor”. W składni Pythona waniliowej nasza przykładowa formuła zamienia się w: [dim(lhs, dim1) for dim1 in lhs_batching_dimensions] == [dim(rhs, dim2) for dim2 in rhs_batching_dimensions]

Dzięki wielokropkom często można uniknąć pracy na poziomie dla poszczególnych skalarów. Jednak w niektórych trudnych przypadkach udzielanie niższego poziomu lub półinformacyjnego składni można używać jak w formule start_indices[bi0, ..., :, ..., biN] ze specyfikacji gather. Aby zadbać o zwięzłość, i dokładnie określić, czy taka składnia zostanie przetłumaczona na język Python waniliowy, mam nadzieję, że wciąż będzie intuicyjnie zrozumiałe dla każdego przypadku. Daj nam znać, jeśli niektóre formuły wyglądają na przezroczyste, a my postaramy się je poprawić.

Zauważysz też, że formuły używają wielokropków do rozwijania wszelkich rodzajów list, w tym tensory, listy tensorów (które np. mogą wynikać z wariancji zmiennej) liczby tensorów) itp. To kolejny obszar, w którym nie podajemy dokładnej formalizm (np. listy nie są nawet częścią systemu typu StableHLO) i liczą się natomiast intuicyjna i zrozumiała.

C) Ostatnim istotnym mechanizmem informacji, jaki stosujemy, jest domniemany transmisji. Mimo że opcja StableHLO nie obsługuje niejawnego przesyłania, jak i wzory, dbając też o zwięzłość. Krótko mówiąc, jeśli wartość skalarna jest używany w kontekście, w którym spodziewany jest tensor, wartość skalarna jest przekazywana do uzyskać oczekiwany kształt.

Aby kontynuować przykładowy zapis typu dot_general, oto kolejne ograniczenie: 0 <= lhs_batching_dimensions < rank(lhs) Zgodnie z definicją w dokumencie dot_general specyfikacji, lhs_batching_dimensions jest tensorem, jednak zarówno 0, jak i rank(lhs) to skalary. Po zastosowaniu rozpowszechniania niejawnego formuła zmieni się na [0, ..., 0] <= lhs_batching_dimensions < [rank(lhs), ..., rank(lhs)].

Po zastosowaniu do konkretnej operacji dot_general ta formuła spowoduje do tensora wartości logicznych. Gdy formuły są używane jako ograniczenia, funkcja ograniczeń, jeśli formuła zwraca wartość true lub tensor, który ma tylko elementy true.

Nazwy

W formułach zakres leksykalny obejmuje: 1) funkcje globalne, 2) definicje członków,

3) lokalne definicje. Poniżej znajdziesz listę funkcji globalnych. Lista definicji elementów zależy od elementu programu, jakim jest notacja zastosowano do:

  • W przypadku operacji definicje elementów obejmują nazwy wprowadzone w polu „Dane wejściowe” oraz „Dane wyjściowe” sekcji.
  • W pozostałych przypadkach definicje elementów obejmują części strukturalne parametru element programu, którego nazwa pochodzi od odpowiednich nieterminali EBNF. Większość W tym czasie nazwy tych części konstrukcyjnych uzyskuje się przez przekształcenie nazwy nieterminali typu snake (np. IntegerLiteral =>) integer_literal), ale czasami nazwy są skracane (np. QuantizationStorageType => storage_type). W takim przypadku nazwy są wprowadzone jawnie podobnie jak „Dane wejściowe”. / „Dane wyjściowe” aktywne sekcje specyfikacji.
  • Ponadto definicje członków zawsze obejmują self, aby odnosić się do odpowiadającego mu elementu programu.

Wartości

Podczas obliczania formuł działają one z tymi typami wartości: 1) Value (wartości rzeczywiste, np. dense<[[1, 2], [3, 4]]> : tensor<2x2xi32>; i zawsze wiedzą, jakie są ich typy). 2) Placeholder (przyszłe wartości, np. lhs, rhs lub result; ich rzeczywiste wartości wartości nie są jeszcze znane, znane są tylko ich typy), 3) Type (typy zdefiniowane w sekcji „Typy”) 4) Function (funkcje globalne zdefiniowane w sekcji „Funkcje”).

W zależności od kontekstu nazwy mogą odnosić się do różnych wartości. Więcej a konkretnie „semantyka” dla operacji (i ich odpowiedników w innym programie) elementów) definiuje logikę środowiska wykonawczego, więc wszystkie dane wejściowe są dostępne jako Value. Natomiast w sekcji „Ograniczenia” operacji (i ich odpowiedników) określa „kompilacja” logiki, tj. czegoś, co jest zwykle wykonywane przed uruchomieniem, więc dostępne są tylko stałe dane wejściowe jako Value, a inne dostępne tylko jako Placeholder.

Nazwy W sekcji „Semantyka” W sekcji „Ograniczenia”
Funkcje globalne Function Function
Stałe dane wejściowe Value Value
Dane wejściowe o innych wartościach Value Placeholder
Wyniki Value Placeholder
Lokalne definicje Zależy od definicji Zależy od definicji

Przeanalizujmy przykładową operację transpose:

%result = "stablehlo.transpose"(%operand) {
  permutation = dense<[2, 1, 0]> : tensor<3xi64>
} : (tensor<2x3x2xi32>) -> tensor<2x3x2xi32>

W tej operacji wartość permutation jest stałą, więc jest dostępna jako Value zarówno pod względem semantyki, jak i ograniczeń. operand i result są natomiast dostępna jako Value w semantyce, ale tylko jako Placeholder w ograniczeniach.

Funkcje

Konstrukcja typów

Nie ma żadnych funkcji, których można używać do tworzenia typów. Zamiast tego użyj składni typu, ponieważ jest ona zwykle bardziej zwięzła. Na przykład: (tensor<E>, tensor<E>) -> (tensor<E>), a nie function_type( [tensor_type([], E), tensor_type([], E)], [tensor_type([], E)]).

Funkcje w typach

  • element_type jest określony dla typów tensorów i kwantyzowanych typów tensorów oraz zwraca odpowiednio TensorElementType lub QuantizedTensorElementType część odpowiedniego elementu TensorType lub QuantizedTensorType.
def element_type(x: Value | Placeholder | Type):
 if type(x) == TensorType:
    return tensor_element_type(x)
  if type(x) == QuantizedTensorType:
    return quantized_tensor_element_type(x)
  if type(x) is not Type:
    return element_type(type(x))
  • is_per_axis_quantized(x: Value | Placeholder | Type) -> Value to skrót za is_quantized(x) and quantization_dimension(x) is not None.

  • is_per_tensor_quantized(x: Value | Placeholder | Type) -> Value to skrót do usługi is_quantized(x) and quantization_dimension(x) is None.

  • is_promotable(x: Type, y: Type) -> bool sprawdza, czy typ x może być awansowany aby wpisać y. Jeśli pola x i y mają wartość QuantizedTensorElementType, promocja jest stosowany tylko do storage_type. Ta konkretna wersja promocji jest obecnie używane w kontekście obliczeń redukcyjnych (patrz RFC).

def is_promotable(x: Type, y: Type) -> Value:
  is_same_type = (is_bool(x) and is_bool(y)) or
    (is_integer(x) and is_integer(y)) or (is_float(x) and is_float(y)) or
    (is_complex(x) and is_complex(y)) or
    (is_quantized(x) and is_quantized(y) and expressed_type(x) = expressed_type(y))

  if is_same_type == False:
    return False

  if is_integer(x) or is_float(x):
    return bitwidth(x) <= bitwidth(y)

  if is_complex(x):
    return bitwidth(element_type(x)) <= bitwidth(element_type(y))

  if is_quantized(x):
    return bitwidth(storage_type(x)) <= bitwidth(storage_type(y))

  return false
  • is_quantized(x: Value | Placeholder | Type) -> Value to skrót dla: is_quantized_tensor_element_type(x)

  • is_type_name(x: Value | Placeholder | Type) -> Value Dostępne dla wszystkich . Na przykład funkcja is_float(x) zwraca wartość true, jeśli x to FloatType. Jeśli x jest wartością lub zmienną, ta funkcja jest skrótem dla is_type_name(type(x))

  • max_value(x: Type) -> Value zwraca maksymalną wartość TensorElementType Jeśli x nie jest typu TensorElementType, zwraca None.

  • min_value(x: Type) -> Value zwraca minimalną możliwą wartość argumentu TensorElementType Jeśli x nie jest typu TensorElementType, zwraca None.

  • member_name(x: Value | Placeholder | Type) -> Any Dostępne dla wszystkich subskrybentów definicje member_name wszystkich typów. Na przykład: tensor_element_type(x) zwraca część TensorElementType odpowiedniego elementu TensorType. Jeśli x jest wartością lub zmienną, ta funkcja jest skrótem dla member_name(type(x)) Jeśli x nie jest typem, który ma odpowiedniego członka lub wartość lub obiekt zastępczy takiego typu, zwraca None.

  • is_empty_algorithm(*args: Type) sprawdza, czy wszystkie pola algorytmu punktowego są ustawione do: None. Jest to konieczne, ponieważ algorytmy punktowe mają zdefiniowaną implementację działa domyślnie, więc określenie wartości domyślnej byłoby nieprawidłowe.

Tworzenie wartości

  • operation_name(*xs: Value | Type) -> Value Dostępne dla wszystkich operacji. Na przykład add(lhs, rhs) przyjmuje 2 wartości tensora lhs i rhs oraz zwraca dane wyjściowe oceny operacji add z tymi danymi wejściowymi. W przypadku niektórych operacji, np. broadcast_in_dim. Typy ich wyników są: „dźwiganie obciążenia”, czyli potrzebne do oceny operacji. W tym przypadku funkcja przyjmuje te typy jako argumenty.

Funkcje na wartościach

  • Dostępne są wszystkie operatory i funkcje Pythona. Na przykład: oba subskrypcja i dzielenie zapisy z języka Python mogą służyć do indeksowania tensorów, tensorów kwantowych i krotki.

  • Pole to_destination_type(x: Value, destination_type: Type) -> Value zostało zdefiniowane na tensorów i zwraca przekonwertowaną wartość x na podstawie type(x) oraz destination_type w następujący sposób:

def to_destination_type(x: Value, destination_type: Type) -> Value:
  if type(x) == destination_type:
    return x

  if is_quantized(destination_type):
    if is_quantized(type(x)):
      return quantize(x, destination_type)
    assert is_float(type(x))
    return quantize(x, destination_type)

  if is_quantized(type(x)):
    assert destination_type = expressed_type(type(x))
    return dequantize(type(x))

  return convert(x, destination_type)

Toczy się wcześniej dyskusja na temat scalania obszarów convert, uniform_quantize i Operacje: uniform_dequantize (#1576). Po scaleniu nie potrzebujemy powyższej funkcji i możemy użyć nazwy operacji dla convert.

  • is_nan(x: Value) -> Value jest określony na tensorach i zwraca true, jeśli wszystkie elementy x mają wartość NaN lub false w innym przypadku. Jeśli x nie jest tensorem, zwraca None.

  • is_sorted(x: Value) -> Value jest określony na tensorach i zwraca true, jeśli elementy funkcji x są posortowane w kolejności rosnącej w porządku leksykograficznym ich indeksów lub false w inny sposób. Jeśli x nie jest tensor, zwraca None.

  • is_unique(x: Value) -> Value jest określony dla tensorów i zwraca true, jeśli x nie ma zduplikowanych elementów lub false w inny sposób. Jeśli x nie jest tensorem, zwraca None.

  • member_name(x: Value) -> Any jest zdefiniowany dla wszystkich definicji elementów member_name wszystkich wartości. Na przykład real_part(x) zwraca RealPart danego elementu ComplexConstant. Jeśli x nie jest wartością, która ma parametr odpowiedniego członka, zwraca wartość None.

  • same(x: Value) -> Value jest określony na tensorach i zwraca true, jeśli elementy x są sobie równe. W przeciwnym razie false. Jeśli tensor nie ma elementów, które są liczone jako „wszystkie są sobie równe”, tj. zwraca true. Jeśli x nie jest tensorem, zwraca None.

  • Pole split(x: Value, num_results: Value, axis: Value) -> Value zostało zdefiniowane na tensorów i zwraca wycinki num_results długości x wzdłuż osi axis. Jeśli x nie jest tensorem ani dim(x, axis) % num_results != 0, zwraca None.

  • Pole is_defined_in_parent_scope(x: Value) -> Value jest zdefiniowane na ciągach znaków i zwraca true, jeśli x jest nazwą funkcji zdefiniowanej w tym samym zakresie jako funkcji nadrzędnej odpowiedniego op.

  • Pole is_namespaced_op_name(x: Value) -> Value jest zdefiniowane na ciągach znaków i zwrotach true, jeśli x jest prawidłową nazwą operacji, tzn. respektuje następujące zwykłe wyrażenie: [a-zA-Z][a-zA-Z0-9_]*([.][a-zA-Z0-9_$]+)+

Obliczenia kształtów

  • axes(x: Value | Placeholder | Type) -> Value to skrót dla: range(rank(x))

  • dim(x: Value | Placeholder | Type, axis: Value) -> Value to skrót dla: shape(x)[axis]

  • dims(x: Value | Placeholder | Type, axes: List) -> List to skrót dla: list(map(lambda axis: dim(x, axis), axes))

  • Pole index_space(x: Value | Placeholder | Type) -> Value jest zdefiniowane na tensorach i zwraca indeksy size(x) dla odpowiadającej mu wartości TensorType posortowanej w rosnący porządek leksykograficzny, tj. [0, ..., 0], [0, ..., 1], ..., shape(x) - 1. Jeśli x nie jest typem tensora, kwantowym typem tensora lub wartością lub obiekt zastępczy jednego z tych typów, zwraca None.

  • rank(x: Value | Placeholder | Type) -> Value to skrót dla: size(shape(x))

  • Pole shape(x: Value | Placeholder | Type) -> Value jest zdefiniowane w sekcji „Funkcje” w typach”. przez member_name.

  • size(x: Value | Placeholder | Type) -> Value to skrót dla: reduce(lambda x, y: x * y, shape(x))

Obliczenia kwantyzacyjne

  • def baseline_element_type(x: Value | Placeholder | Type) -> Type to skrót do: element_type(baseline_type(x)).

  • baseline_type jest określony dla typów tensorów i kwantyzowanych typów tensorów oraz przekształca je na „odniesienie”, tj. typ o tym samym kształcie, ale z parametry kwantyzacji typu elementu są resetowane do wartości domyślnych. To jest jako przydatna sztuczka przy porównywaniu zarówno tensorów, jak i typów tensorów kwantowych równomiernie, co jest konieczne dość często. W przypadku typów kwantyzowanych to umożliwia porównując typy ignorujące parametry kwantyzacji, czyli shape, storage_type, expressed_type, storage_min, storage_max i quantization_dimension (w przypadku kwantyzowanego typu na oś) musi być zgodna, ale Usługi scales i zero points mogą się różnić.

def baseline_type(x: Value | Placeholder | Type) -> Type:
  if type(x) == TensorType:
    return x
  if type(x) == QuantizedTensorType:
    element_type = quantized_tensor_element_type(x)
    baseline_element_type = QuantizedTensorElementType(
      storage_type = storage_type(element_type),
      storage_min = storage_min(element_type),
      storage_max = storage_max(element_type),
      expressed_type = expressed_type(element_type),
      quantization_dimension = quantization_dimension(element_type),
      scales = [constant(1.0, expressed_type(element_type))] * dim(x, quantization_dimension(element_type)),
      zero_points = [constant(0, storage_type(element_type))] * dim(x, quantization_dimension(element_type)))
    return QuantizedTensorType(shape(x), baseline_element_type)
  if type(x) is not Type:
    return baseline_element_type(type(x))
  • dequantize jest zdefiniowany w skwantyzowanych typach tensorów i przekształca je w: tensorów zmiennoprzecinkowych. Dzieje się to przez konwersję pierwiastków poddanych kwantyzacji które reprezentują wartości całkowite typu pamięci masowej na odpowiednie wartości wartości zmiennoprzecinkowe typu wyrażonego za pomocą punktu zerowego i skali z typem kwantyzowanego elementu.
def compute_zero_points(quantized_type, result_type):
  if is_per_tensor_quantized(quantized_type):
    return broadcast_in_dim(constant(zero_point(quantized_type), storage_type(quantized_type)), [], result_type)
  if is_per_axis_quantized(quantized_type):
    for i in index_space(result_type):
      d = quantization_dimension(quantized_type)
      zero_points[i] = zero_points(quantized_type)[i[d]]
    return zero_points

def compute_scales(quantized_type, result_type):
  if is_per_tensor_quantized(quantized_type):
    return broadcast_in_dim(constant(scale(quantized_type), expressed_type(quantized_type)), [],
            type(result_type))
  if is_per_axis_quantized(quantized_type):
    for i in index_space(result_type):
      d = quantization_dimension(quantized_type)
      scales[i] = scales(quantized_type)[i[d]]
    return scales

def dequantize(x: Value) -> Value:
  assert is_quantized(x)
  x_storage = bitcast_convert(x, storage_type(x))
  x_storage_sub = x_storage - compute_zero_points(type(x), type(x_storage))
  x_expressed_sub = convert(x_storage_sub, expressed_type(x))
  return x_expressed_sub * compute_scales(type(x), type(x_expressed_sub))
  • Pole quantize jest zdefiniowane w typach tensorów zmiennoprzecinkowych i przekształca je w kwantyzowanych typów tensorów. Dzieje się tak przez konwertowanie wartości zmiennoprzecinkowych wyrażonego typu na odpowiednie wartości całkowite typu pamięci masowej wykorzystując punkt zerowy i skalę powiązane z typem poddanego kwantyzacji.
def quantize(x: Value, result_type: Type) -> Value:
  assert is_float(x) and is_quantized(result_type)
  zero_points = compute_zero_points(result_type, TensorType(shape(x), storage_type(result_type)))
  converted_zero_points = convert(zero_points, expressed_type(result_type))
  converted_min = convert(storage_min(result_type), expressed_type(result_type))
  converted_max = convert(storage_max(result_type), expressed_type(result_type))

  x_scaled = x / compute_scales(result_type, type(x))
  x_scaled_add_zp = x_scaled + converted_zero_points
  x_clamped = clamp(converted_min, x_scaled_add_zp, converted_max)
  x_rounded = round_nearest_even(x_clamped)
  return convert(x_rounded, result_type)
  • dequantize_op_quantize jest używany do określania obliczeń elementu tensory kwantowe. Dekwantyzuje, tj. przekształca kwantowe pierwiastki w określonych typów, następnie wykonuje operację i konwertuje je kwantowo, tj. wyniki z powrotem do typu pamięci masowej. Obecnie ta funkcja jest dostępna tylko umożliwia kwantyzację na poziomie intensywności eksploatacji. Trwa kwantyzacja na oś (#1574).
def dequantize_op_quantize(op, *inputs_and_output_type):
  inputs = inputs_and_output_type[:-1]
  output_type = inputs_and_output_type[-1]

  float_inputs = map(dequantize, inputs)
  float_result = op(*float_inputs)
  return quantize(float_result, output_type)

def dequantize_batch_norm_grad_or_training_quantize(op, *inputs_and_output_types):
  inputs = inputs_and_output_type[:-3]
  float_inputs = map(dequantize, inputs)
  float_results = op(*float_inputs)
  return map(quantize, float_results, inputs_and_output_type[-3:])

def dequantize_compare(lhs, rhs, comparison_direction):
  float_lhs = dequantize(lhs)
  float_rhs = dequantize(rhs)
  return compare(float_lhs, float_rhs, comparison_direction, FLOAT)

def dequantize_select_quantize(pred, on_true, on_false, output_type):
  float_on_true = dequantize(on_true)
  float_on_false = dequantize(on_false)
  float_result = select(pred, float_on_true, float_on_false)
  return quantize(float_result, output_type)
  • Pole hybrid_dequantize_then_op jest używane do określania kwantyzacji samej wagi dla operacja hybrydowa, która akceptuje lh w postaci zmiennoprzecinkowej i rh w typach kwantyzowanych. it dekwantyzuje skwantyzowane dane wejściowe na ich wyrażone typy i przeprowadza obliczenia w postaci zmiennoprzecinkowej. Typ elementu tensora lhs zmiennoprzecinkowego i wyrażony typ kwantyzowanych wartości rh tensor powinien być taki sam.
def hybrid_dequantize_then_op(op, lhs, rhs):
  assert(is_float(lhs) and is_quantized(rhs) and element_type(lhs) == expressed_type(rhs))
  return op(lhs, dequantize(rhs))

Obliczenia siatki

  • cross_partition(replica_groups: Value) -> Value Zobacz parametr „cross_replica” powyżej.

  • cross_replica(replica_groups: Value) -> Value Zobacz parametr „cross_replica” powyżej.

  • cross_replica_and_partition(replica_groups: Value) -> Value Zobacz &quot;cross_replica_and_partition&quot; powyżej.

  • flattened_ids(replica_groups: Value) -> Value Zobacz pole „flattened_ids” powyżej.

Dynamizm

Wartości StableHLO mogą mieć dynamiczne rozmiary, np. tensor<?xi64> Wartości StableHLO nie mogą jednak mieć dynamicznej liczby wymiarów (bez rankingu dynamizm, np. tensor<*xi64>). Operatory i wyniki mogą zawierać dynamiczne nawet wtedy, gdy obowiązują ograniczenia dotyczące rozmiarów. Ograniczenia będą w miarę możliwości statycznie zweryfikowane. W przeciwnym razie są przekazywane do czasu działania będą powodować nieoczekiwane zachowanie. Przykłady znajdziesz poniżej.

Niezgodności kształtów w przypadku jednoargumentowych operacji elementów

Rozważ następujący program zabawek:

func.func @foo(%arg0: tensor<?xf64>) {
  %0 = stablehlo.abs %arg0 : (tensor<?xf64>) -> tensor<2xf64>
  return
}

Taki program jest wyjątkowy, ponieważ nie wiadomo, jaki kształt ale nie kształt danych wejściowych. Mimo to jest to prawidłowy parametr StableHLO programu. Nie można statycznie zweryfikować operacji abs w tej funkcji ponieważ dokładny kształt operandu jest nieznany. Kształty są na pewno zgodne i można to sprawdzić statycznie: ? może się okazać, wynosi 2 w czasie działania i nie będzie problemu. Jednak ? może staje się też inną liczbą całkowitą, w którym to zachowanie jest niezdefiniowane.

Pamiętaj, że jeśli rozmiar wymiaru w wyniku jest dynamiczny, nie może nieoczekiwane zachowanie. Nie ma tu czegoś „oczekiwanego” więc nie będzie można niezgodność danych.

Niezgodność kształtu w operacjach binarnych elementwise

Rozważ następujący program zabawek:

func.func @foo(%arg0: tensor<?xf64>, %arg1: tensor<?xf64>) {
  %0 = stablehlo.add %arg0, %arg0 : (tensor<?xf64>, tensor<?xf64>) -> tensor<?xf64>
  return
}

W przypadku operacji binarnych elementy wejściowe kształty danych wejściowych i wynik musi być zgodny w czasie działania. Podczas kompilowania wymiary statyczne muszą być równe w przeciwnym razie wystarczy ich zgodność. Jeśli dowolny wymiar jest dynamiczny w danych wejściowych, może istnieć niezdefiniowany w czasie działania, bo rozmiar dynamiczny może nie odpowiadać rozmiaru w drugim operandie (czy to statyczny lub dynamiczny). Jeśli wszystkie dane wejściowe to statyczny, wówczas to, czy wynik jest dynamiczny, czy nie, nie ma znaczenia: statycznie znane wymiary będą sprawdzane statycznie, a wymiary dynamiczne nie nakładać jakiekolwiek ograniczenia.

Niezgodności kształtów w operacjach, które przyjmują kształt wyjściowy jako operand

Rozważ następujący program zabawek:

func.func @foo(%arg0: tensor<2xi32>) {
  %0 = stablehlo.dynamic_iota %arg0, dim = 0 : (tensor<2xi32>) -> tensor<3x4xi64>
  return
}

Wartości w operandach kształtu w czasie działania muszą pasować do kształtu wyniku W przeciwnym razie zachowanie jest niezdefiniowane. Oznacza to, że w czasie działania %arg0 musi mieć dense<[3, 4]> : tensor<2xi32>. Jeśli operand kształtu jest stały, to można zweryfikować statycznie. Jeżeli kształt wyniku jest w pełni dynamiczny, nie mogą być niezgodne.