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)
, tosize(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)
.
- (C12)
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
ifalse
. - 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
lub64
). TypysiN
z podpisem reprezentują wartości całkowite od-2^(N-1)
do2^(N-1)-1
Typy obejmujące i bez znakuuiN
reprezentują wartości całkowite od0
do2^N-1
włącznie. - Oto kilka typów liczb zmiennoprzecinkowych:
f8E4M3FN
if8E5M2
odpowiadające odpowiednio KodowanieE4M3
iE5M2
formatu FP8 opisanego w Formaty FP8 do deep learningu.- Typy
f8E4M3FNUZ
if8E5M2FNUZ
odpowiadające parametromE4M3
iE5M2
kodowania formatów FP8 opisanych w 8-bitowe formaty numeryczne dla głębokich sieci neuronowych. - Typ
f8E4M3B11FNUZ
odpowiadający kodowaniuE4M3
formatów FP8 opisane w HFP8 (HFP8) – trenowanie i wnioskowanie w przypadku głębokich sieci neuronowych. - Typ
bf16
odpowiadający formatowibfloat16
opisanemu w BFloat16: tajemnica wysokiej wydajności w Cloud TPU. - Typy
f16
,f32
if64
odpowiadające odpowiedniobinary16
(„połowa precyzji”),binary32
(„pojedyncza precyzja”) i Formatybinary64
(„podwójna precyzja”) opisane w standard IEEE 754. - Typ
tf32
odpowiada formatowi TensorFloat32 i ma ograniczoną obsługę protokołu StableHLO.
- 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ą typuf32
) orazcomplex<f64>
(obie części są typuf64
).
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śliis_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]
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)
.
- (C1)
- 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)
, toquantization_dimension(lhs) = quantization_dimension(result)
. - (C7) Jeśli
is_per_axis_quantized(rhs)
, toquantization_dimension(rhs) = quantization_dimension(result)
.
- (C2)
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]]
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
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ślichannel_id <= 0 and use_global_device_ids = false
.cross_replica_and_partition(replica_groups)
jeślichannel_id > 0 and use_global_device_ids = false
.flattened_ids(replica_groups)
jeślichannel_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 wszystkoreceiver
w:process_group
.results...@process = concatenate(operands...@process, all_gather_dim)
za wszystkoprocess
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
, tochannel_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]]
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ślichannel_id <= 0 and use_global_device_ids = false
.cross_replica_and_partition(replica_groups)
jeślichannel_id > 0 and use_global_device_ids = false
.flattened_ids(replica_groups)
jeślichannel_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 binarnegoschedule
, 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 wynosito_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
, tochannel_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]
all_to_all
Semantyka
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ślichannel_id <= 0
.cross_partition(replica_groups)
, jeślichannel_id > 0
.
Następnie w każdym elemencie process_group
:
split_parts...@sender = split(operands...@sender, split_count, split_dimension)
za wszystkiesender
wprocess_group
.scattered_parts...@receiver = [split_parts...@sender[receiver_index] for sender in process_group]
, gdziereceiver_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óresplit_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]]
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]]
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]
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
igrad_offset
mają takie same wartościbaseline_element_type
. - (C3)
operand
,grad_output
igrad_operand
mają taki sam kształt. - (C4)
scale
,mean
,variance
,grad_scale
igrad_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
iresult
mają ten sambaseline_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
ioutput
– ten sambaseline_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)
iR = 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 wszystkie0 <= 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 wszystkie0 <= i < R
.dim(operand, R - 1) * num_bits(E) = num_bits(E')
.
- Jeśli
- (C2) Jeśli
is_complex(operand) or is_complex(result)
, tois_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
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ślidim(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óczquantization_dimension(operand)
,scales(operand)
izero_points(operand)
mogą różnić się odquantization_dimension(result)
,scales(result)
izero_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
waxes(operand)
:dim(operand, d) = 1
lubdim(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
, toscales(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]
// ]
// ]
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śli0 <= 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]
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]
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]
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]
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ślichannel_id <= 0
.cross_partition(replica_groups)
, jeślichannel_id > 0
.
Później wartość result@process
jest określana przez:
operand@process_groups[i, 0]
, jeśli istniejei
, dzięki któremu proces jest w aplikacjiprocess_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
, gdzieN
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ślichannel_id <= 0
.cross_partition(source_target_pairs)
, jeślichannel_id > 0
.
Później wartość result@process
jest określana przez:
operand@process_groups[i, 0]
, jeśli występujei
taki jakprocess_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
, gdzieN
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]]
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śliis_signed_integer(element_type(lhs))
.UNSIGNED
, jeśliis_unsigned_integer(element_type(lhs)) or is_boolean(element_type(lhs))
.FLOAT
lubTOTALORDER
, jeśliis_float(element_type(lhs))
.FLOAT
, jeśliis_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]
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 typcomplex<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)]
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>
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:
id = d0 + ... + dk-1 + kd
.- Parametr
d
jest równydimension
, ad0
tod
rozmiary wymiarów zinputs
.
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óczdim(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]]
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]]
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)]
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.
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]
, gdziej[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śliresult_dim = output_batch_dimension
.dim(rhs, kernel_output_feature_dimension)
, jeśliresult_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)
.
- (C27)
- 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ępniequantization_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)
.
- (C28)
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]]
// ]]
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]]
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]]
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]
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
gdziesize(result_batching_index) = size(lhs_batching_dimensions)
,size(result_lhs_index) = size(lhs_result_dimensions)
isize(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
irhs_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
inum_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)
.
- (C13)
- 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 grupierhs_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)
.
- (C14)
- 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
.
- (C21)
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]]
// ]
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óczquantization_dimension(operand)
,scales(operand)
izero_points(operand)
mogą różnić się odquantization_dimension(result)
,scales(result)
izero_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
waxes(operand)
:dim(operand, d) = 1
lubdim(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
, toscales(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]
// ]
// ]
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śliresult_dim = output_batch_dimension
.dim(rhs, kernel_output_feature_dimension)
, jeśliresult_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)
.
- (C27)
- 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ępniequantization_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)
.
- (C28)
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]]
// ]]
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ścistart_indices
odpowiadającej wartościindex_vector_dim
.offset_dim_sizes = shape(slice_sizes)
oprócz rozmiarów wymiarów w kolumnieslice_sizes
odpowiadającej wartościcollapsed_slice_dims
nie są uwzględniane.- Funkcja
combine
umieszczabatch_dim_sizes
na osiach odpowiadającychbatch_dims
ioffset_dim_sizes
przy osiach odpowiadających wartościoffset_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]]
// ]
// ]
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]
// ]
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]
// ]
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óczquantization_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]]
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]
// ]
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śli0 <= 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]
// ]
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]]
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]
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
, gdziecomplex_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
iresult
jest zróżnicowana:- Jeśli
fft_type = FFT
,element_type(operand)
ielement_type(result)
mają taki sam typ złożony. - Jeśli
fft_type = IFFT
,element_type(operand)
ielement_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 ielement_type(result)
to typ zmiennoprzecinkowy z tą samą liczbą zmiennoprzecinkową semantyka.
- Jeśli
- (C3)
1 <= size(fft_length) <= 3
. - (C4) Jeśli wśród funkcji
operand
iresult
, występuje tensorreal
argumentu typu zmiennoprzecinkowego, a następnieshape(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
- Jeśli
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]
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.
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]
, gdziebi
to pojedyncze elementy wbatch_index
i:
są wstawione w indeksieindex_vector_dim
, jeśliindex_vector_dim
<rank(start_indices)
.- W przeciwnym razie:
[start_indices[batch_index]]
.
- W przypadku usługi
d_operand
waxes(operand)
,full_start_index[d_operand] = clamp(start_index[d_start], 0, dim(operand, d_operand) - slice_sizes[d_operand])
jeślid_operand = start_index_map[d_start]
.- W przeciwnym razie:
full_start_index[d_operand] = 0
.
- W przypadku usługi
d_operand
waxes(operand)
,full_batching_index[d_operand] = batch_index[d_start - (d_start < index_vector_dim ? 0 : 1)]
jeślid_operand = operand_batching_dims[i_batching]
id_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]
, gdzieoi
to osoby indywidualne elementów w argumencieoffset_index
, a0
jest wstawiony w indeksach zcollapsed_slice_dims
ioperand_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ścistart_indices
odpowiadającej wartościindex_vector_dim
.offset_dim_sizes = slice_sizes
oprócz rozmiarów wymiarów wslice_sizes
odpowiadające wartościomcollapsed_slice_dims
ioperand_batching_dims
nie są uwzględnione.- Funkcja
combine
umieszczabatch_dim_sizes
na osiach odpowiadającychbatch_dims
ioffset_dim_sizes
przy osiach odpowiadających wartościoffset_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]]
// ]
// ]
// ]
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
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]
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
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śliis_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]
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])
lubis_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]]
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]
// ]
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]
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]]
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]
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]]
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'>
gdzieEi = element_type(inputs[i])
iE' = 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]]
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]]
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]]
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]]
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]
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]
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
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]]
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
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śliresult_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]
// ]
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>
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]
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]
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śliis_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]
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śliis_host_transfer = true
,- W przeciwnym razie:
DEVICE_TO_DEVICE
.
- (C2)
0 < size(results)
. - (C3)
is_empty(result[:-1])
lubis_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)
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::
odimensions
.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 binarnegoschedule
, 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 wszystkichindex
windex_space(input_slices_converted)
w rosnącym porządku leksykograficznym zindex
.- 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>)
, gdzieis_promotable(element_type(inputs[i]), Ei)
- (C7)
shape(results...) = shape(inputs...)
z tym wyjątkiem, że wymiar rozmiaryinputs...
odpowiadające wartościdimensions
nie są uwzględniane. - (C8)
element_type(results[i]) = Ei
dla wszystkichi
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]
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 funkcjiroundToIntegralTiesToEven
– semantyka. - Następnie, jeśli
mantissa_bits
jest mniejsza niż liczba bitów modliszki pierwotnej wartości, bity mantysy są obcinane domantissa_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]
reduce_scatter
Semantyka
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ślichannel_id <= 0 and use_global_device_ids = false
.cross_replica_and_partition(replica_groups)
jeślichannel_id > 0 and use_global_device_ids = false
.flattened_ids(replica_groups)
jeślichannel_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 wszystkichsender
wprocess_group
, gdziereceiver_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
, tochannel_id > 0
. - (C7)
computation
ma typ(tensor<E>, tensor<E>) -> (tensor<E>)
, gdzieis_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]]
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...
.
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>)
, gdzieis_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 wszystkichi
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]]
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ąglaniaroundTowardZero
- 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]
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>
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óczquantization_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]]
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ślid
wdimensions
.- 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]]
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
, tois_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ślirng_algorithm = THREE_FRY
.2
lub3
, jeślirng_algorithm = PHILOX
.
- zdefiniowaną przez implementację, jeśli
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]
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]
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]]
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...
.
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]
, gdziesi
to osoby indywidualne elementy w tabelachupdate_scatter_index
i:
są wstawione w miejscu Indeksindex_vector_dim
, jeśliindex_vector_dim
<rank(scatter_indices)
.- W przeciwnym razie:
[scatter_indices[update_scatter_index]]
.
- W przypadku usługi
d_input
waxes(inputs[0])
,full_start_index[d_input] = start_index[d_start]
, jeślid_input = scatter_dims_to_operand_dims[d_start]
- W przeciwnym razie:
full_start_index[d_input] = 0
.
- W przypadku usługi
d_input
waxes(inputs[0])
,full_batching_index[d_input] = update_scatter_index[d_start - (d_start < index_vector_dim ? 0 : 1)]
jeślid_input = input_batching_dims[i_batching]
id_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]
, gdziewi
to osoby indywidualne elementów w argumencieupdate_window_index
, a0
jest wstawiony w indeksach zinserted_window_dims
iinput_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 funkcjiindex_space(updates[0])
exec([update_index, ...], results) = exec([...], updated_results)
, gdzie:- Jeśli
result_index
należy do zakresushape(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 notatnikaresults
zresults...[result_index]
ustawiono naupdated_values...
.- W innym przypadku
updated_results = results
.
- Jeśli
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ść wymiaruscatter_indices
odpowiadającego Nie dodanoindex_vector_dim
.update_window_dim_sizes <= shape(inputs[0])
z tym wyjątkiem rozmiary wymiarów w poluinputs[0]
odpowiadające wartościinserted_window_dims
iinput_batching_dims
nie są uwzględnione.- Funkcja
combine
umieszcza elementupdate_scatter_dim_sizes
na osiach odpowiadającychupdate_scatter_dims
iupdate_window_dim_sizes
przy odpowiednich osiach doupdate_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>)
, gdzieis_promotable(element_type(inputs[i]), Ei)
. - (C24)
shape(inputs...) = shape(results...)
. - (C25)
element_type(results[i]) = Ei
dla wszystkichi
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]]
// ]
// ]
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]]
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.
Bardziej oficjalnie:
selected_values = reduce_window_without_init(...)
z tymi danymi wejściowymi:inputs = [operand].
window_dimensions
,window_strides
ipadding
, 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)
ireduce_window_without_init
dokładnie tak samo jakreduce_window
z tą różnicą, żeschedule
bazowego źródłareduce
(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śliselected_values[source_index]
zawiera elementoperand
odoperand_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]]
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śliis_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
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]
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]
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]
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]
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]]
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]
// ]
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]
, gdzieriN
to osoby indywidualne elementów w elemencieresult_index
, a:
jest wstawiony w miejscuadjusted_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 funkcjacomparator_together
zwracatrue
, 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
, gdzieR = rank(inputs[0])
. - (C5)
comparator
ma typ(tensor<E1>, tensor<E1>, ..., tensor<EN-1>, tensor<EN-1>) -> tensor<i1>
, gdzieEi = 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]]
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]]
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]]
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]
// ]
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]
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óczquantization_dimension(operand)
i Wartośćquantization_dimension(result)
może się różnić. W przeciwnym razie.
- (C2)
permutation
to permutacja słowarange(rank(operand))
. - (C3)
shape(result) = dim(operand, permutation...)
. - (C4) Jeśli
is_per_axis_quantized(result)
, toquantization_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]]
// ]
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 zakresua
.ADJOINT
: wykonaj operację na transpozycji sprzężoneja
.
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)
ishape(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 typtuple<E0, ..., EN-1>
, gdzieEi = 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))
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>
, gdzieTi = type(operand[i])
- (C2)
body
ma typ(T0, ..., TN-1) -> (T0, ..., TN-1)
, gdzieTi = 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
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]]
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ównaniaget_tuple_element
,rng
,complex
w #560, i splotwindow_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
→ %2
→ return
i
%1
→ %0
→ %2
→ return
.
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 odpowiednioTensorElementType
lubQuantizedTensorElementType
część odpowiedniego elementuTensorType
lubQuantizedTensorType
.
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 zais_quantized(x) and quantization_dimension(x) is not None
.is_per_tensor_quantized(x: Value | Placeholder | Type) -> Value
to skrót do usługiis_quantized(x) and quantization_dimension(x) is None
.is_promotable(x: Type, y: Type) -> bool
sprawdza, czy typx
może być awansowany aby wpisaćy
. Jeśli polax
iy
mają wartośćQuantizedTensorElementType
, promocja jest stosowany tylko dostorage_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 funkcjais_float(x)
zwraca wartośćtrue
, jeślix
toFloatType
. Jeślix
jest wartością lub zmienną, ta funkcja jest skrótem dlais_type_name(type(x))
max_value(x: Type) -> Value
zwraca maksymalną wartośćTensorElementType
Jeślix
nie jest typuTensorElementType
, zwracaNone
.min_value(x: Type) -> Value
zwraca minimalną możliwą wartość argumentuTensorElementType
Jeślix
nie jest typuTensorElementType
, zwracaNone
.member_name(x: Value | Placeholder | Type) -> Any
Dostępne dla wszystkich subskrybentów definicjemember_name
wszystkich typów. Na przykład:tensor_element_type(x)
zwraca częśćTensorElementType
odpowiedniego elementuTensorType
. Jeślix
jest wartością lub zmienną, ta funkcja jest skrótem dlamember_name(type(x))
Jeślix
nie jest typem, który ma odpowiedniego członka lub wartość lub obiekt zastępczy takiego typu, zwracaNone
.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ładadd(lhs, rhs)
przyjmuje 2 wartości tensoralhs
irhs
oraz zwraca dane wyjściowe oceny operacjiadd
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 podstawietype(x)
orazdestination_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 zwracatrue
, jeśli wszystkie elementyx
mają wartośćNaN
lubfalse
w innym przypadku. Jeślix
nie jest tensorem, zwracaNone
.is_sorted(x: Value) -> Value
jest określony na tensorach i zwracatrue
, jeśli elementy funkcjix
są posortowane w kolejności rosnącej w porządku leksykograficznym ich indeksów lubfalse
w inny sposób. Jeślix
nie jest tensor, zwracaNone
.is_unique(x: Value) -> Value
jest określony dla tensorów i zwracatrue
, jeślix
nie ma zduplikowanych elementów lubfalse
w inny sposób. Jeślix
nie jest tensorem, zwracaNone
.member_name(x: Value) -> Any
jest zdefiniowany dla wszystkich definicji elementówmember_name
wszystkich wartości. Na przykładreal_part(x)
zwracaRealPart
danego elementuComplexConstant
. Jeślix
nie jest wartością, która ma parametr odpowiedniego członka, zwraca wartośćNone
.same(x: Value) -> Value
jest określony na tensorach i zwracatrue
, jeśli elementyx
są sobie równe. W przeciwnym raziefalse
. Jeśli tensor nie ma elementów, które są liczone jako „wszystkie są sobie równe”, tj. zwracatrue
. Jeślix
nie jest tensorem, zwracaNone
.Pole
split(x: Value, num_results: Value, axis: Value) -> Value
zostało zdefiniowane na tensorów i zwraca wycinkinum_results
długościx
wzdłuż osiaxis
. Jeślix
nie jest tensorem anidim(x, axis) % num_results != 0
, zwracaNone
.Pole
is_defined_in_parent_scope(x: Value) -> Value
jest zdefiniowane na ciągach znaków i zwracatrue
, jeślix
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 zwrotachtrue
, jeślix
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 indeksysize(x)
dla odpowiadającej mu wartościTensorType
posortowanej w rosnący porządek leksykograficzny, tj.[0, ..., 0]
,[0, ..., 1]
, ...,shape(x) - 1
. Jeślix
nie jest typem tensora, kwantowym typem tensora lub wartością lub obiekt zastępczy jednego z tych typów, zwracaNone
.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”. przezmember_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, czylishape
,storage_type
,expressed_type
,storage_min
,storage_max
iquantization_dimension
(w przypadku kwantyzowanego typu na oś) musi być zgodna, ale Usługiscales
izero 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 "cross_replica_and_partition" 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.