StableHLO ist ein Operation-Set für High-Level-Operationen (HLO) in Maschinen, Lernmodelle (ML). StableHLO dient als Portabilitätsebene zwischen verschiedenen ML-Frameworks und ML-Compiler: ML-Frameworks, die StableHLO-Programme erstellen sind mit ML-Compilern kompatibel, die StableHLO-Programme nutzen.
Unser Ziel ist es, die ML-Entwicklung zu vereinfachen und zu beschleunigen, Interoperabilität zwischen verschiedenen ML-Frameworks (wie TensorFlow, JAX und PyTorch) und ML-Compiler (wie XLA und IREE). Gegen dieses Ende enthält eine Spezifikation für die Programmiersprache StableHLO.
Diese Spezifikation enthält drei Hauptabschnitte. Zunächst sollte der Im Abschnitt Programme wird die Struktur von StableHLO-Programmen beschrieben. die aus StableHLO-Funktionen bestehen, die wiederum aus StableHLO-Operationen bestehen. Innerhalb dieser Struktur gibt der Abschnitt Ops die Semantik der für einzelne Vorgänge. Der Abschnitt Ausführung enthält Semantik für alle die diese Vorgänge gemeinsam in einem Programm ausführen. Die Funktion Im Abschnitt Notation wird die im gesamten Dokument verwendete Notation erläutert. Spezifikation zu ändern.
Um die Spezifikation einer früheren StableHLO-Version anzuzeigen, öffnen Sie das Repository im getaggte Freigabe von Interesse. Beispiel: StableHLO v0.19.0 Spec Informationen zum Anzeigen von Änderungen, die bei jedem Nebenversions-Bump von StableHLO vorgenommen wurden, finden Sie unter das Versionsprotokoll in VhloDialect.td
Programme
Program ::= {Func}
StableHLO-Programme bestehen aus einer beliebigen Anzahl von StableHLO-Funktionen.
Unten sehen Sie ein Beispielprogramm mit der Funktion @main
mit 3 Eingaben
(%image
, %weights
und %bias
) und 1 Ausgabe. Der Hauptteil der Funktion
hat 6 Vorgänge.
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>) -> ()
}
Funktionen
Func ::= 'func' '.' 'func' FuncId FuncInputs FuncOutputs '{' FuncBody '}'
FuncInputs ::= '(' [FuncInput {',' FuncInput}] `)`
FuncInput ::= ValueId ':' ValueType
FuncOutputs ::= ['->' FuncOutput, {',' FuncOutput}]
FuncOutput ::= ValueType
FuncBody ::= {Op}
StableHLO-Funktionen, die auch als benannte Funktionen bezeichnet werden, haben eine Kennung, Ein-/Ausgaben und einen Textkörper. Für die Zukunft planen wir, Es werden zusätzliche Metadaten für Funktionen eingeführt, um eine bessere Kompatibilität zu erreichen. mit HLO (#425, #626 Nr. 740 #744).
IDs
FuncId ::= '@' letter {letter | digit}
ValueId ::= '%' digit {digit}
| '%' letter {letter | digit}
letter ::= 'a' | ... | 'z' | 'A' | ... | 'Z' | '_'
digit ::= '0' | ... | '9'
StableHLO-Kennungen ähneln Kennungen in vielen Programmiersprachen. Sprachen, mit zwei Besonderheiten: 1) Alle Kennungen haben Sieigzeichen, die verschiedene Arten von Kennungen unterscheiden, 2) Wertkennungen vollständig numerisch, um die Generierung von StableHLO-Programmen zu vereinfachen.
Typen
Type ::= ValueType | NonValueType
ValueType ::= TensorType | QuantizedTensorType | TokenType | TupleType
NonValueType ::= TensorElementType | QuantizedTensorElementType | FunctionType | StringType
StableHLO-Typen werden in Werttypen kategorisiert, die auch als First-class-Typen), die StableHLO-Werte und Nicht-Werttypen darstellen. die andere Programmelemente beschreiben. StableHLO-Typen ähneln Typen in viele Programmiersprachen, wobei die größte Besonderheit die Funktionen von StableHLO sind. domainspezifischen Charakter, der zu ungewöhnlichen Ergebnissen führt (z.B. skalare Typen) sind keine Werttypen).
TensorType ::= 'tensor' '<' Shape TensorElementType '>'
Shape ::= {DimensionSize 'x'}
DimensionSize ::= digit {digit} | '?'
Tensor-Typen stellen Tensoren dar, also mehrdimensionale Arrays. Sie haben eine
Form und einen Unterelementtyp, wobei eine Form für nicht negative oder
unbekannte Dimensionsgrößen in aufsteigender Reihenfolge der zugehörigen
Dimensionen (auch Achsen genannt) von 0
bis R-1
nummeriert. Die
Anzahl der Dimensionen R
wird als Rang bezeichnet. Beispiel: tensor<2x3xf32>
ist
einen Tensortyp mit der Form 2x3
und dem Elementtyp f32
. Es hat zwei Dimensionen.
(oder, mit anderen Worten, zwei Achsen) – die 0. Dimension und die erste Dimension, deren Größen
2 und 3 sind. Sein Rang ist 2.
Formen können teilweise oder vollständig unbekannt (dynamisch) sein, z.B. tensor<?x2xf64>
ist teilweise unbekannt und tensor<?x?xf64>
ist vollständig unbekannt. Dynamisch
Dimensionsgrößen durch ?
dargestellt. Die Rangfolge von Formen kann nicht aufgehoben werden.
Zukünftig möchten wir die Tensortypen Größen und Elementtypen beispielsweise, um Layouts (#629) und Datendichte (#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
Name | Typ | Einschränkungen |
---|---|---|
storage_type |
Ganzzahltyp | (C1–C3), (C8) |
storage_min |
Ganzzahlkonstante | (C1), (C3), (C7) |
storage_max |
Ganzzahlkonstante | (C2), (C3), (C7) |
expressed_type |
Gleitkommatyp | (C4) |
quantization_dimension |
optionale Ganzzahlkonstante | (C10–C12) |
scales |
variadische Anzahl von Gleitkommakonstanten | (C4-C6), (C9), (C10), (C13) |
zero_points |
variadische Anzahl ganzzahliger Konstanten | (C7–C9) |
Quantisierte Elementtypen stehen für ganzzahlige Werte eines Speichertyps in
den Bereich von storage_min
bis storage_max
(einschließlich), die sich auf
Gleitkommawerte eines ausgedrückten Typs. Für einen gegebenen ganzzahligen Wert i
kann der entsprechende Gleitkommawert f
als
f = (i - zero_point) * scale
, wobei scale
und zero_point
aufgerufen werden
Quantisierungsparametern. storage_min
und storage_max
sind optional
in der Grammatik, aber haben die Standardwerte min_value(storage_type)
und
max_value(storage_type)
. Quantisierte Elementtypen haben die
folgenden Einschränkungen:
- (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) Wenn
is_empty(quantization_dimension)
, dannsize(scales) = 1
. - (C11)
0 <= quantization_dimension
.
Derzeit ist QuantizationScale
eine Gleitkommakonstante, es gibt jedoch
starkes Interesse an ganzzahlbasierten Skalen, dargestellt durch Multiplikatoren und
Schichten. Wir möchten dies in naher Zukunft untersuchen
(#1404)
Die Semantik von QuantizationZeroPoint
wird derzeit diskutiert.
einschließlich Typ, Werten und ob es nur eine oder
in einem quantisierten Tensortyp. Basierend auf den
zum Ergebnis dieser Diskussion kann sich die Spezifikation um Nullpunkte
in der Zukunft erfolgen (#1405).
Eine weitere laufende Diskussion zur Semantik von QuantizationStorageMin
und QuantizationStorageMax
, um zu bestimmen, ob Einschränkungen
auf diese Werte und die Werte von quantisierten Tensoren angewendet.
(#1406)
Schließlich wollen wir erkunden, wie unbekannte Skalen und Nullen Ähnlich wie die Darstellung von unbekannten Dimensionsgrößen (#1407)
Quantisierte Tensortypen stellen Tensoren mit quantisierten Elementen dar. Diese Tensoren sind genau die gleichen wie reguläre Tensoren, mit der Ausnahme, dass ihre Elemente quantisierte Elementtypen anstelle von regulären Elementtypen haben.
In quantisierten Tensoren kann die Quantisierung per-Tensor erfolgen, was bedeutet,
Ein scale
und zero_point
für den gesamten Tensor oder kann pro Achse sein,
Dies bedeutet, dass bei mehreren scales
und zero_points
ein Paar pro Segment von
um eine bestimmte Dimension quantization_dimension
zu erhalten. Formals in einem Tensor-t
Mit der Quantisierung pro Achse gibt es dim(t, quantization_dimension)
Slices
der quantization_dimension
: t[:, ..., 0, ..., :], t[:, ..., 1, ..., :]
,
Alle Elemente im i
. Segment verwenden scales[i]
und zero_points[i]
als
ihre Quantisierungsparameter. Quantisierte Tensortypen haben Folgendes:
Einschränkungen:
- Für die Quantisierung pro Tensor:
<ph type="x-smartling-placeholder">
- </ph>
- Keine zusätzlichen Einschränkungen.
- Für die Quantisierung pro Achse:
<ph type="x-smartling-placeholder">
- </ph>
- (C12)
quantization_dimension < rank(self)
. - (C13)
dim(self, quantization_dimension) = size(scales)
.
- (C12)
TokenType ::= 'token'
Tokentypen stellen Tokens dar, d. h. intransparente Werte, die erzeugt und verbraucht werden. durch einige Operationen. Tokens werden zum Festlegen der Ausführungsreihenfolge für Vorgänge verwendet wie im Abschnitt Ausführung beschrieben.
TupleType ::= 'tuple' '<' TupleElementTypes '>'
TupleElementTypes ::= [ValueType {',' ValueType}]
Tupeltypen stehen für Tupel, also heterogene Listen. Tupel sind ein Vermächtnis
die nur aus Gründen der Kompatibilität mit HLO besteht. In HLO werden Tupel
zur Darstellung variadischer Ein- und Ausgaben. In StableHLO werden variadische Eingaben und
Ausgaben werden nativ unterstützt und die einzige Verwendung von Tupeln in StableHLO besteht darin,
umfassende Darstellung von HLO ABI, wo z.B. T
, tuple<T>
und
tuple<tuple<T>>
kann je nach
Implementierung. Wir planen, in Zukunft Änderungen am HLO ABI vorzunehmen.
mit der wir Tupeltypen aus StableHLO entfernen können,
(#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'
Elementtypen stellen Elemente von Tensortypen dar. Anders als bei vielen Programmen
Sprachen sind, sind diese Typen in StableHLO nicht an erster Stelle. Das bedeutet, dass
StableHLO-Programme können Werte dieser Typen nicht direkt darstellen (daher
Es ist idiomatisch, Skalarwerte des Typs T
mit einem 0-dimensionalen Tensor darzustellen.
Werte des Typs tensor<T>
) enthält.
- Der Boolesche Typ steht für die booleschen Werte
true
undfalse
. - Ganzzahltypen können entweder vorzeichen (
si
) oder unvorzeichent (ui
) sein und haben eine der unterstützten Bitbreiten (2
,4
,8
,16
,32
oder64
). VorzeichenbehaftetesiN
-Typen stellen ganzzahlige Werte von-2^(N-1)
bis2^(N-1)-1
dar einschließlich und vorzeichenloseuiN
-Typen stellen Ganzzahlwerte von0
bis2^N-1
einschließlich. - Es gibt folgende Gleitkommatypen:
<ph type="x-smartling-placeholder">
- </ph>
f8E4M3FN
- undf8E5M2
-Typen, die den entsprechendenE4M3
- undE5M2
-Codierungen des FP8-Formats beschrieben in FP8-Formate für Deep Learning.- Typen
f8E4M3FNUZ
undf8E5M2FNUZ
, dieE4M3
undE5M2
entsprechen Codierungen der FP8-Formate, die in Numerische 8-Bit-Formate für neuronale Deep-Learning-Netzwerke. - Typ
f8E4M3B11FNUZ
, der derE4M3
-Codierung der FP8-Formate entspricht beschrieben in Training und Inferenz für neuronale Deep-Learning-Netzwerke (Hybrid 8-Bit Floating Point, HFP8). bf16
-Typ, der dembfloat16
-Format entspricht, das beschrieben wird in BFloat16: Das Geheimnis für hohe Leistung auf Cloud TPUsf16
-,f32
- undf64
-Typen, die dem jeweiligen Typ entsprechenbinary16
("halbe Genauigkeit"),binary32
("einfache Genauigkeit") undbinary64
-Formate („Double Precision“), die unter IEEE 754-Standard.- Der Typ
tf32
entspricht dem TensorFloat32-Format. und wird in StableHLO nur eingeschränkt unterstützt.
- Komplexe Typen stehen für komplexe Werte, die einen reellen Teil haben
und einem imaginären Teil desselben Elementtyps. Unterstützt, komplex
Typen sind
complex<f32>
(beide Teile sind vom Typf32
) undcomplex<f64>
(beide Teile sind vom Typf64
).
FunctionType ::= '(' InputTypes ')' '->' '(' OutputTypes ')'
InputTypes ::= [ValueType {',' ValueType}]
OutputTypes ::= [ValueType {',' ValueType}]
Funktionstypen stellen sowohl benannte als auch anonyme Funktionen dar. Sie haben
Eingabetypen (die Liste der Typen auf der linken Seite von ->
) und Ausgabetypen
(die Liste der Typen rechts von ->
). In vielen Programmierungen
sind Funktionstypen erstklassig,
aber in StableHLO nicht.
StringType ::= 'string'
Der Stringtyp steht für Bytesequenzen. Anders als bei vielen Programmen ist der Zeichenfolgentyp in StableHLO nicht die erste Klasse und wird nur verwendet, geben statische Metadaten für Programmelemente an.
Vorgänge
StableHLO-Operationen (auch Ops genannt) stellen eine geschlossene Menge dar. übergeordneten Abläufen in ML-Modellen. Wie bereits erwähnt, StableHLO-Syntax ist stark von MLIR inspiriert, was nicht unbedingt die am stärksten eine ergonomische Alternative, ist aber wohl am besten für das Ziel von StableHLO geeignet, und mehr Interoperabilität zwischen ML-Frameworks und ML-Compilern schaffen.
Op ::= [OpOutputs] OpName OpInputs ':' OpSignature
OpName ::= '"' 'stablehlo' '.' OpMnemonic '"'
OpMnemonic ::= 'abs' | 'add' | ...
StableHLO-Operationen (auch Operations genannt) haben einen Namen,
Ein-/Ausgaben und eine Signatur. Der Name besteht aus dem Präfix stablehlo.
und
Eine mnemonische Kennzeichnung, die einen der unterstützten Vorgänge eindeutig identifiziert. Siehe unten für
eine umfassende Liste aller unterstützten Operationen.
OpInputs ::= OpInputValues OpInputFuncs OpInputAttrs
OpInputValues ::= '(' [OpInputValue {',' OpInputValue}] ')'
OpInputValue ::= ValueId
OpInputFuncs ::= ['(' OpInputFunc {',' OpInputFunc} ')']
OpInputAttrs ::= ['{' OpInputAttr {',' OpInputAttr} '}']
OpOutputs ::= [OpOutput {',' OpOutput} '=']
OpOutput ::= ValueId
Betriebsabläufe verbrauchen Eingaben und erzeugen Ausgaben. Eingaben sind kategorisiert in
Eingabewerte (berechnet während der Ausführung), Eingabefunktionen (bereitgestellt
statisch, da StableHLO-Funktionen keine First-Class-Werte sind) und
Eingabeattribute (auch statisch bereitgestellt). Die Art der Ein- und Ausgaben
von einem Opaion konsumiert und produziert wird, hängt von seiner Eselsbrücke ab. Beispiel: add
op verbraucht 2 Eingabewerte und erzeugt 1 Ausgabewert. Im Vergleich dazu
Der Vorgang select_and_scatter
verbraucht 3 Eingabewerte, 2 Eingabefunktionen und
3 Eingabeattribute.
OpInputFunc ::= '{' Unused FuncInputs ':' FuncBody '}'
Unused ::= '^' digit {digit}
| '^' letter {letter | digit}
Eingabefunktionen (auch anonyme Funktionen genannt) sind sehr
ähnlich wie benannte Funktionen, mit dem Unterschied, dass sie keine Kennung haben (daher
der Name „anonymous“), 2) es werden keine Ausgabetypen deklariert (Ausgabetypen sind
aus dem return
-Vorgang in der Funktion abgeleitet wird).
Die Syntax für Eingabefunktionen enthält einen derzeit nicht verwendeten Teil (siehe
Unused
-Produktion oben), das aus Gründen der MLIR-Kompatibilität gedacht ist. In MLIR
gibt es ein allgemeineres Konzept von „Regionen“, die mehrere „Blöcke“ haben kann,
der Operationen sind über Jump Ops miteinander verbunden. Die IDs dieser Blöcke entsprechen
an die Unused
-Produktion, damit sie voneinander unterschieden werden können.
StableHLO hat keine Jump Ops, sodass der entsprechende Teil der MLIR-Syntax
nicht verwendet werden (aber noch vorhanden ist).
OpInputAttr ::= OpInputAttrName '=' OpInputAttrValue
OpInputAttrName ::= letter {letter | digit}
OpInputAttrValue ::= Constant
Eingabeattribute haben einen Namen und einen Wert. Dies ist einer der unterstützten Werte.
Konstanten. Sie sind die primäre Möglichkeit, statische Metadaten für das Programm anzugeben.
Elemente. Beim Vorgang concatenate
wird beispielsweise das Attribut dimension
verwendet,
gibt die Dimension an, mit der die Eingabewerte verkettet werden. In ähnlicher Weise
Der Vorgang slice
verwendet mehrere Attribute wie start_indices
und limit_indices
, um die Grenzen anzugeben, die zum Aufteilen des Eingabewerts verwendet werden.
Derzeit enthalten StableHLO-Programme, die derzeit frei sind, manchmal Attribute die in diesem Dokument nicht beschrieben sind. Für die Zukunft planen wir, diese Attribute entweder in das StableHLO-Opset aufnehmen oder sie StableHLO-Programmen. In der Zwischenzeit finden Sie hier eine Liste mit diesen Attribute:
layout
(#629)mhlo.frontend_attributes
(#628)mhlo.sharding
(#619)output_operand_aliases
(#740)- Standortmetadaten (#594)
OpSignature ::= '(' [ValueType {',' ValueType}] ')' '->' '(' [ValueType {',' ValueType}] ')'
Die Vorgangssignatur besteht aus den Typen aller Eingabewerte (der Liste der Typen
der linken Seite von ->
) und die Typen aller Ausgabewerte (die Liste der
auf der rechten Seite von ->
. Genau genommen sind Eingabetypen
redundant sind und Ausgabetypen fast immer redundant sind.
Bei den meisten StableHLO-Operationen lassen sich Ausgabetypen aus Eingaben ableiten). Trotzdem
Signatur ist absichtlich Teil der StableHLO-Syntax für die Kompatibilität mit MLIR.
Unten sehen Sie ein Beispiel für eine Operation mit der mnemonischen Kennzeichnung select_and_scatter
. Es verbraucht 3
Eingabewerte (%operand
, %source
und %init_value
), 2 Eingabefunktionen
und 3 Eingabeattribute (window_dimensions
, window_strides
und padding
).
Beachten Sie, dass die Signatur des Vorgangs nur die Typen der Eingabewerte enthält.
Dies gilt jedoch nicht für die Art der Eingabefunktionen und Attribute, die inline bereitgestellt werden.
%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>
Konstanten
Constant ::= BooleanConstant
| IntegerConstant
| FloatConstant
| ComplexConstant
| TensorConstant
| QuantizedTensorConstant
| StringConstant
| EnumConstant
StableHLO-Konstanten haben ein Literal und einen Typ, die zusammen für
einen StableHLO-Wert. Im Allgemeinen ist der Typ Teil der konstanten Syntax, außer
wenn sie eindeutig ist (z.B. hat eine boolesche Konstante eindeutig den Typ i1
,
während eine ganzzahlige Konstante mehrere mögliche Typen haben kann).
BooleanConstant ::= BooleanLiteral
BooleanLiteral ::= 'true' | 'false'
Boolesche Konstanten stellen die booleschen Werte true
und false
dar. Boolescher Wert
haben Konstanten den Typ i1
.
IntegerConstant ::= IntegerLiteral ':' IntegerType
IntegerLiteral ::= ['-' | '+'] DecimalDigits
| ['-' | '+'] '0x' HexadecimalDigits
DecimalDigits ::= decimalDigit {decimalDigit}
HexadecimalDigits ::= hexadecimalDigit {hexadecimalDigit}
decimalDigit ::= '0' | ... | '9'
hexadecimalDigit ::= decimalDigit | 'a' | ... | 'f' | 'A' | ... | 'F'
Ganzzahlkonstanten stellen Ganzzahlwerte über Strings dar, die Dezimalzahlen oder Hexadezimalschreibweise. Andere Basen, z.B. binär oder oktal, werden nicht unterstützt. Für Ganzzahlkonstanten gelten die folgenden Einschränkungen:
- (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]
Gleitkommakonstanten stellen Gleitkommawerte über Strings dar, die Dezimalschreibweise oder wissenschaftliche Schreibweise verwenden. Außerdem kann die hexadezimale Notation werden die zugrunde liegenden Bits direkt im Gleitkommaformat eines mit dem entsprechenden Typ aus. Für Gleitkommakonstanten gelten die folgenden Einschränkungen:
- (C1) Wenn die Hexadezimalschreibweise nicht verwendet wird,
is_wellformed(float_literal, float_type)
- (C2) Bei Verwendung der Hexadezimalschreibweise
size(hexadecimal_digits) = num_bits(float_type) / 4
ComplexConstant ::= ComplexLiteral ':' ComplexType
ComplexLiteral ::= '(' RealPart ',' ImaginaryPart ')'
RealPart ::= FloatLiteral
ImaginaryPart ::= FloatLiteral
Komplexe Konstanten stellen komplexe Werte mithilfe von Listen eines reellen Teils dar.
(kommt an erster Stelle) und ein imaginärer Teil (an zweiter Stelle). Beispiel:
(1.0, 0.0) : complex<f32>
steht für 1.0 + 0.0i
und
(0.0, 1.0) : complex<f32>
repräsentiert 0.0 + 1.0i
. Die Reihenfolge, in der diese
werden -Teile
dann im Arbeitsspeicher gespeichert. Komplexe Konstanten
haben folgende Einschränkungen:
- (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
Tensor-Konstanten stellen Tensor-Werte mithilfe verschachtelter Listen dar, die über
NumPy-Notation. Beispiel: dense<[[1, 2, 3], [4, 5, 6]]> : tensor<2x3xi32>
stellt einen Tensorwert mit der folgenden Zuordnung von Indexen zu Elementen dar:
{0, 0} => 1
, {0, 1} => 2
, {0, 2} => 3
, {1, 0} => 4
, {1, 1} => 5
,
{1, 2} => 6
Die Reihenfolge, in der diese Elemente
dann im Arbeitsspeicher gespeichert werden,
Implementierung definiert. Für Tensorkonstanten gelten die folgenden Einschränkungen:
- (C1)
has_syntax(tensor_literal, element_type(tensor_type))
, wobei gilt: <ph type="x-smartling-placeholder">- </ph>
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))
, wobei gilt: <ph type="x-smartling-placeholder">- </ph>
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:])
.- Andernfalls
false
.
QuantizedTensorConstant ::= QuantizedTensorLiteral ':' QuantizedTensorType
QuantizedTensorLiteral ::= 'dense' '<' (DenseLiteral | ElementLiteral) '>'
Quantisierte Tensorkonstanten stellen quantisierte Tensorwerte mit denselben Werten Notation als Tensorkonstanten, wobei Elemente als Konstanten ihrer Speichertyp. Quantisierte Tensorkonstanten haben die folgenden Einschränkungen:
- (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))
Stringliterale bestehen aus Byte, die mithilfe von ASCII-Zeichen und
Escapesequenzen verwenden. Da sie unabhängig von der Codierung sind,
Bytes ist implementierungsdefiniert. Stringliterale haben den Typ string
.
Operativer Betrieb
Bauchmuskeln
Semantik
Führt eine elementweise abs-Operation für den operand
-Tensor durch und erzeugt einen result
-Wert
Tensor. Gehen Sie je nach Elementtyp so vor:
- Für vorzeichenbehaftete Ganzzahlen: Modulus ganzzahl.
- Für Gleitkommazahlen:
abs
aus IEEE-754. - Für komplexe Zahlen: komplexer Modulus.
- Für quantisierte Typen:
dequantize_op_quantize(abs, operand, type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor eines vorzeichenbehafteten Ganzzahl-, Gleitkomma- oder komplexer Typ oder eines quantisierten Tensors pro Tensor | (C1–C2) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des vorzeichenbehafteten Ganzzahl- oder Gleitkommatyps oder quantisierter Tensor pro Tensor | (C1–C2) |
Einschränkungen
- (C1)
shape(result) = shape(operand)
. - (C2)
baseline_element_type(result)
ist definiert als: <ph type="x-smartling-placeholder">- </ph>
complex_element_type(element_type(operand))
wennis_complex(operand)
.- Andernfalls
baseline_element_type(operand)
.
Beispiele
// %operand: [-2, 0, 2]
%result = "stablehlo.abs"(%operand) : (tensor<3xi32>) -> tensor<3xi32>
// %result: [2, 0, 2]
Hinzufügen
Semantik
Führt eine elementweise Addition der beiden Tensoren lhs
und rhs
durch und erzeugt einen
result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für boolesche Werte: logisches ODER.
- Für Ganzzahlen: Addition von Ganzzahlen.
- Für Gleitkommazahlen:
addition
aus IEEE-754. - Für komplexe Zahlen: komplexe Addition
- Für quantisierte Typen:
dequantize_op_quantize(add, lhs, rhs, type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor oder quantisierter Tensor | (C1–C6) |
(I2) | rhs |
Tensor oder quantisierter Tensor | (C1–C5), (C7) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor | (C1–C7) |
Einschränkungen
- Wenn die Operation nicht quantisierte Tensoren verwendet:
<ph type="x-smartling-placeholder">
- </ph>
- (C1)
type(lhs) = type(rhs) = type(result)
.
- (C1)
- Wenn die Operation quantisierte Tensoren verwendet:
<ph type="x-smartling-placeholder">
- </ph>
- (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) Wenn
is_per_axis_quantized(lhs)
, dannquantization_dimension(lhs) = quantization_dimension(result)
. - (C7) Wenn
is_per_axis_quantized(rhs)
, dannquantization_dimension(rhs) = quantization_dimension(result)
.
- (C2)
Beispiele
// %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
Semantik
Stellt sicher, dass die Vorgänge, die die inputs
erzeugen, vor jeder Ausführung ausgeführt werden
Vorgänge, die von result
abhängig sind. Bei der Ausführung dieses Vorgangs passiert nichts.
Er existiert nur zum Einrichten von Datenabhängigkeiten von result
bis inputs
.
Eingaben
Label | Name | Typ |
---|---|---|
(I1) | inputs |
variadische Zahl von token |
Ausgaben
Name | Typ |
---|---|
result |
token |
Beispiele
// %input0: !stablehlo.token
// %input1: !stablehlo.token
%result = "stablehlo.after_all"(%input0, %input1) : (!stablehlo.token, !stablehlo.token) -> !stablehlo.token
all_gather
Semantik
Verkettet innerhalb jeder Prozessgruppe im StableHLO-Prozessraster die Werte
der operands
-Tensoren aus jedem Prozess entlang von all_gather_dim
und erzeugt
results
-Tensoren.
Der Vorgang teilt das StableHLO-Prozessraster in process_groups
auf,
wie folgt definiert:
cross_replica(replica_groups)
wennchannel_id <= 0 and use_global_device_ids = false
.cross_replica_and_partition(replica_groups)
wennchannel_id > 0 and use_global_device_ids = false
.flattened_ids(replica_groups)
wennchannel_id > 0 and use_global_device_ids = true
.
Danach gilt in jeder process_group
Folgendes:
operands...@receiver = [operand@sender for sender in process_group]
für allereceiver
inprocess_group
.results...@process = concatenate(operands...@process, all_gather_dim)
für alleprocess
inprocess_group
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operands |
variadische Anzahl von Tensoren oder quantisierte Tensoren pro Tensor | (C1), (C6) |
(I2) | all_gather_dim |
Konstante vom Typ si64 |
(C1), (C6) |
(I3) | replica_groups |
2-dimensionale Tensorkonstante vom Typ si64 |
(C2–C4) |
(I4) | channel_id |
Konstante vom Typ si64 |
(C5) |
(I5) | use_global_device_ids |
Konstante vom Typ i1 |
(C5) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
results |
variadische Anzahl von Tensoren oder quantisierte Tensoren pro Tensor | (C6) |
Einschränkungen
- (C1)
0 <= all_gather_dim < rank(operands...)
. - (C2)
is_unique(replica_groups)
. - (C3)
size(replica_groups)
ist definiert als: <ph type="x-smartling-placeholder">- </ph>
num_replicas
, wenncross_replica
verwendet wird.num_replicas
, wenncross_replica_and_partition
verwendet wird.num_processes
, wennflattened_ids
verwendet wird.
- (C4)
0 <= replica_groups < size(replica_groups)
. - (C5) Wenn
use_global_device_ids = true
, dannchannel_id > 0
. - (C6)
type(results...) = type(operands...)
mit Ausnahme von: <ph type="x-smartling-placeholder">- </ph>
dim(results..., all_gather_dim) = dim(operands..., all_gather_dim) * dim(process_groups, 1)
.
Beispiele
// 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
Semantik
Wendet innerhalb jeder Prozessgruppe im StableHLO-Prozessraster eine Reduzierung an
Funktion computation
zu den Werten der operands
-Tensoren aus jedem Prozess
und erzeugt results
-Tensoren.
Der Vorgang teilt das StableHLO-Prozessraster in process_groups
auf,
wie folgt definiert:
cross_replica(replica_groups)
wennchannel_id <= 0 and use_global_device_ids = false
.cross_replica_and_partition(replica_groups)
wennchannel_id > 0 and use_global_device_ids = false
.flattened_ids(replica_groups)
wennchannel_id > 0 and use_global_device_ids = true
.
Danach gilt in jeder process_group
Folgendes:
results...@process[result_index] = exec(schedule)
für einen binären Baumschedule
, wobei gilt: <ph type="x-smartling-placeholder">- </ph>
exec(node)
=computation(exec(node.left), exec(node.right))
.exec(leaf)
=leaf.value
.
schedule
ist eine implementierungsdefinierte binäre Baumstruktur, deren Reihenfolge Durchlauf istto_destination_type(operands...@process_group...[result_index], type(func_inputs(computation)[0]))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operands |
variadische Anzahl von Tensoren oder quantisierte Tensoren pro Tensor | (C5), (C6) |
(I2) | replica_groups |
Variadische Anzahl der eindimensionalen Tensorkonstanten vom Typ si64 |
(C1–C3) |
(I3) | channel_id |
Konstante vom Typ si64 |
(C4) |
(I4) | use_global_device_ids |
Konstante vom Typ i1 |
(C4) |
(I5) | computation |
Funktion | (C5) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
results |
variadische Anzahl von Tensoren oder quantisierte Tensoren pro Tensor | (C6–C7) |
Einschränkungen
- (C1)
is_unique(replica_groups)
. - (C2)
size(replica_groups)
ist definiert als: <ph type="x-smartling-placeholder">- </ph>
num_replicas
, wenncross_replica
verwendet wird.num_replicas
, wenncross_replica_and_partition
verwendet wird.num_processes
, wennflattened_ids
verwendet wird.
- (C3)
0 <= replica_groups < size(replica_groups)
. - (C4) Wenn
use_global_device_ids = true
, dannchannel_id > 0
. - (C5)
computation
hat den Typ(tensor<E>, tensor<E>) -> (tensor<E>)
, wobeiis_promotable(element_type(operand), E)
- (C6)
shape(results...) = shape(operands...)
. - (C7)
element_type(results...) = E
.
Beispiele
// 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
Semantik
Innerhalb jeder Prozessgruppe im StableHLO-Prozessraster werden die Werte der
die operands
-Tensoren entlang von split_dimension
in Teile, verteilt die
zwischen Prozessen und verkettet die verstreuten Teile entlang
concat_dimension
und erzeugt results
-Tensoren.
Der Vorgang teilt das StableHLO-Prozessraster in process_groups
auf,
wie folgt definiert:
cross_replica(replica_groups)
wennchannel_id <= 0
.cross_partition(replica_groups)
wennchannel_id > 0
.
Danach gilt in jeder process_group
Folgendes:
split_parts...@sender = split(operands...@sender, split_count, split_dimension)
für allesender
inprocess_group
.scattered_parts...@receiver = [split_parts...@sender[receiver_index] for sender in process_group]
, wobeireceiver_index = process_group.index(receiver)
.results...@process = concatenate(scattered_parts...@process, concat_dimension)
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operands |
variadische Anzahl von Tensoren oder quantisierte Tensoren pro Tensor | (C1–C3), (C9) |
(I2) | split_dimension |
Konstante vom Typ si64 |
(C1), (C2), (C9) |
(I3) | concat_dimension |
Konstante vom Typ si64 |
(C3), (C9) |
(I4) | split_count |
Konstante vom Typ si64 |
(C2), (C4), (C8), (C9) |
(I5) | replica_groups |
2-dimensionale Tensorkonstante vom Typ si64 |
(C5–C8) |
(I6) | channel_id |
Konstante vom Typ si64 |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
results |
variadische Anzahl von Tensoren oder quantisierte Tensoren pro Tensor | (C9) |
Einschränkungen
- (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)
size(replica_groups)
ist definiert als: <ph type="x-smartling-placeholder">- </ph>
num_replicas
, wenncross_replica
verwendet wird.num_partitions
, wenncross_partition
verwendet wird.
- (C7)
0 <= replica_groups < size(replica_groups)
. - (C8)
dim(replica_groups, 1) = split_count
. - (C9)
type(results...) = type(operands...)
mit Ausnahme vonsplit_dimension != concat_dimension
: <ph type="x-smartling-placeholder">- </ph>
dim(results..., split_dimension) = dim(operands..., split_dimension) / split_count
.dim(results..., concat_dimension) = dim(operands..., concat_dimension) * split_count
.
Beispiele
// 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]]
und
Semantik
Führt das elementweise UND der zwei Tensoren lhs
und rhs
durch und erzeugt eine result
Tensor. Gehen Sie je nach Elementtyp so vor:
- Für boolesche Werte: logisches UND.
- Für Ganzzahlen: bitweises UND.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor des booleschen oder Ganzzahltyps | (C1) |
(I2) | rhs |
Tensor des booleschen oder Ganzzahltyps | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des booleschen oder Ganzzahltyps | (C1) |
Einschränkungen
- (C1)
type(lhs) = type(rhs) = type(result)
.
Beispiele
// %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
Semantik
Führt eine elementweise atan2-Operation für den Tensor lhs
und rhs
durch und erzeugt einen
result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für Gleitkommazahlen:
atan2
aus IEEE-754. - Für komplexe Zahlen: komplexe atan2.
- Für quantisierte Typen:
dequantize_op_quantize(atan2, lhs, rhs, type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
(I2) | rhs |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
.
Beispiele
// %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
Semantik
Berechnet Gradienten mehrerer Eingaben der Rückpropagierung von batch_norm_training
aus grad_output
und produziert grad_operand
, grad_scale
und grad_offset
Tensoren. Formal kann diese Operation
als eine Zerlegung in
vorhandene StableHLO-Vorgänge mit der folgenden Python-Syntax:
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
Führt für quantisierte Typen durch
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))
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C1–C3), (C5) |
(I2) | scale |
1-dimensionaler Tensor eines Gleitkomma- oder pro Tensor quantisierten Typs | (C2), (C4), (C5) |
(I3) | mean |
1-dimensionaler Tensor eines Gleitkomma- oder pro Tensor quantisierten Typs | (C2), (C4) |
(I4) | variance |
1-dimensionaler Tensor eines Gleitkomma- oder pro Tensor quantisierten Typs | (C2), (C4) |
(I5) | grad_output |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C2), (C3) |
(I6) | epsilon |
Konstante vom Typ f32 |
|
(I7) | feature_index |
Konstante vom Typ si64 |
(C1), (C5) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
grad_operand |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C2), (C3) |
grad_scale |
1-dimensionaler Tensor eines Gleitkomma- oder pro Tensor quantisierten Typs | (C2), (C4) |
grad_offset |
1-dimensionaler Tensor eines Gleitkomma- oder pro Tensor quantisierten Typs | (C2), (C4) |
Einschränkungen
- (C1)
0 <= feature_index < rank(operand)
. - (C2)
operand
,scale
,mean
,variance
,grad_output
,grad_operand
,grad_scale
undgrad_offset
haben dieselbebaseline_element_type
. - (C3)
operand
,grad_output
undgrad_operand
haben die gleiche Form. - (C4)
scale
,mean
,variance
,grad_scale
undgrad_offset
haben die dieselbe Form haben. - (C5)
size(scale) = dim(operand, feature_index)
.
Beispiele
// %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
Semantik
Normalisiert den Tensor operand
für alle Dimensionen mit Ausnahme der
feature_index
-Dimension und erzeugt einen result
-Tensor. Formell gesprochen
Operation kann als Zerlegung vorhandener StableHLO-Vorgänge ausgedrückt werden.
mit der folgenden Python-Syntax:
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)
Führt für quantisierte Typen durch
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))
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C1–C7) |
(I2) | scale |
1-dimensionaler Tensor eines Gleitkomma- oder pro Tensor quantisierten Typs | (C2), (C3) |
(I3) | offset |
1-dimensionaler Tensor eines Gleitkomma- oder pro Tensor quantisierten Typs | (C2), (C4) |
(I4) | mean |
1-dimensionaler Tensor eines Gleitkomma- oder pro Tensor quantisierten Typs | (C5) |
(I5) | variance |
1-dimensionaler Tensor eines Gleitkomma- oder pro Tensor quantisierten Typs | (C2), (C6) |
(I6) | epsilon |
Konstante vom Typ f32 |
|
(I7) | feature_index |
Konstante vom Typ si64 |
(C1), (C3–C6) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C2), (C7) |
Einschränkungen
- (C1)
0 <= feature_index < rank(operand)
. - (C2)
operand
,scale
,offset
,mean
,variance
undresult
haben die Gleichebaseline_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)
.
Beispiele
// %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
Semantik
Berechnet den Mittelwert und die Varianz für alle Dimensionen mit Ausnahme von feature_index
Dimension und normalisiert den Tensor operand
, der output
, batch_mean
erzeugt
und batch_var
-Tensoren. Formal kann dieser Vorgang als
Zerlegung in vorhandene StableHLO-Vorgänge unter Verwendung der Python-Syntax als
folgt:
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
Führt für quantisierte Typen durch
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))
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C1) |
(I2) | scale |
1-dimensionaler Tensor von Gleitkomma oder pro Tensor quantisiert | (C2), (C3) |
(I3) | offset |
1-dimensionaler Tensor von Gleitkomma oder pro Tensor quantisiert | (C2), (C4) |
(I4) | epsilon |
Konstante vom Typ f32 |
(C1), (C3–C6) |
(I5) | feature_index |
Konstante vom Typ si64 |
(C1), (C3–C6) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
output |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C7) |
batch_mean |
1-dimensionaler Tensor von Gleitkomma oder pro Tensor quantisiert | (C2), (C5) |
batch_var |
1-dimensionaler Tensor von Gleitkomma oder pro Tensor quantisiert | (C2), (C6) |
Einschränkungen
- (C1)
0 <= feature_index < rank(operand)
. - (C2) Für
operand
,scale
,offset
,batch_mean
,batch_var
undoutput
wurden denselbenbaseline_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)
.
Beispiele
// %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
Semantik
Führt einen Bitcast-Vorgang für den operand
-Tensor aus und erzeugt einen result
-Tensor
wobei die Bits des gesamten operand
-Tensors mit der Methode
Typ des result
-Tensors.
Formeller gesprochen: E = element_type(operand)
, E' = element_type(result)
,
und R = rank(operand)
:
- Wenn
num_bits(E') < num_bits(E)
,bits(result[i0, ..., iR-1, :]) = bits(operand[i0, ..., iR-1])
. - Wenn
num_bits(E') > num_bits(E)
,bits(result[i0, ..., iR-2]) = bits(operand[i0, ..., iR-2, :])
. - Wenn
num_bits(E') = num_bits(E)
,bits(result[i0, ..., iR-1]) = bits(operand[i0, ..., iR-1])
.
bits
gibt eine speicherinterne Darstellung eines bestimmten Werts und sein Verhalten zurück.
ist implementierungsdefiniert, da die genaue Darstellung von Tensoren
Implementierung definiert. Die genaue Darstellung von Elementtypen ist
Implementierung definiert werden.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor | (C1–C2) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor | (C1–C2) |
Einschränkungen
- (C1) Für
E = is_quantized(operand) ? storage_type(operand) : element_type(operand)
,E' = is_quantized(result) ? storage_type(result) : element_type(result)
undR = rank(operand)
: <ph type="x-smartling-placeholder">- </ph>
- Wenn
num_bits(E') = num_bits(E)
,shape(result) = shape(operand)
. - Wenn
num_bits(E') < num_bits(E)
: rank(result) = R + 1
.dim(result, i) = dim(operand, i)
für alle0 <= i < R
.dim(result, R) * num_bits(E') = num_bits(E)
.- Wenn
num_bits(E') > num_bits(E)
: rank(result) = R - 1
.dim(result, i) = dim(operand, i)
für alle0 <= i < R
.dim(operand, R - 1) * num_bits(E) = num_bits(E')
.
- Wenn
- (C2) Wenn
is_complex(operand) or is_complex(result)
, dannis_complex(operand) and is_complex(result)
.
Beispiele
// %operand: 0x0123456789ABCDEF
%result = "stablehlo.bitcast_convert"(%operand) : (tensor<f64>) -> tensor<4xf16>
// %result: [0xCDEF, 0x89AB, 0x4567, 0x0123] // little-endian representation
broadcast_in_dim
Semantik
Erweitert die Dimensionen und/oder den Rang eines Eingabetensors durch Duplizieren der Daten
im operand
-Tensor und erzeugt einen result
-Tensor. Formeller gesprochen
result[result_index] = operand[operand_index]
– wo für alle d
in
axes(operand)
:
operand_index[d] = 0
wenndim(operand, d) = 1
.- Andernfalls
operand_index[d] = result_index[broadcast_dimensions[d]]
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor | (C1–C2), (C5–C6) |
(I2) | broadcast_dimensions |
Eindimensionale Tensorkonstante vom Typ si64 |
(C2–C6) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor | (C1), (C3), (C5-C6) |
Einschränkungen
- (C1)
element_type(result)
ist gegeben durch: <ph type="x-smartling-placeholder">- </ph>
element_type(operand)
, wenn!is_per_axis_quantized(operand)
.element_type(operand)
mit Ausnahme vonquantization_dimension(operand)
,scales(operand)
undzero_points(operand)
können abweichen vonquantization_dimension(result)
,scales(result)
undzero_points(result)
oder „Sonstiges“.
- (C2)
size(broadcast_dimensions) = rank(operand)
. - (C3)
0 <= broadcast_dimensions < rank(result)
. - (C4)
is_unique(broadcast_dimensions)
. - (C5) Für alle
d
inaxes(operand)
: <ph type="x-smartling-placeholder">- </ph>
dim(operand, d) = 1
oderdim(operand, d) = dim(result, broadcast_dimensions[d])
.
- (C6) Wenn
is_per_axis_quantized(result)
: <ph type="x-smartling-placeholder">- </ph>
quantization_dimension(result) = broadcast_dimensions[quantization_dimension(operand)]
.- Wenn
dim(operand, quantization_dimension(operand)) = 1
, dannscales(result)[i] = scales(operand)[0] and zero_points(result)[i] = zero_points(operand)[0] for i in range(dim(result, quantization_dimension(result)))
.
Beispiele
// %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]
// ]
// ]
Supportanfrage
Semantik
Erzeugt die Ausgabe, wenn genau eine Funktion aus branches
ausgeführt wird
abhängig vom Wert von index
. Formeller result = selected_branch()
Dabei gilt:
selected_branch = branches[index]
wenn0 <= index < size(branches)
.- Andernfalls
selected_branch = branches[-1]
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | index |
0-dimensionaler Tensor vom Typ si32 |
|
(I2) | branches |
variadische Anzahl von Funktionen | (C1–C4) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
results |
variadische Anzahl von Tensoren, quantisierte Tensoren oder Tokens | (C4) |
Einschränkungen
- (C1)
0 < size(branches)
. - (C2)
input_types(branches...) = []
. - (C3)
same(output_types(branches...))
. - (C4)
type(results...) = output_types(branches[0])
.
Beispiele
// %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
Semantik
Führt eine elementweise kubische Wurzeloperation für den operand
-Tensor durch und erzeugt eine
result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für Gleitkommazahlen:
rootn(x, 3)
aus IEEE-754. - Für komplexe Zahlen: komplexe kubische Wurzel.
- Für quantisierte Typen:
dequantize_op_quantize(cbrt, operand, type(result))
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %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
Semantik
Führt die elementweise Obergrenze des operand
-Tensors durch und erzeugt einen result
-Tensor.
Implementiert den roundToIntegralTowardPositive
-Vorgang aus IEEE-754
Spezifikation zu ändern. Führt für quantisierte Typen durch
dequantize_op_quantize(ceil, operand, type(result))
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %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
Semantik
Berechnet die Cholesky-Zerlegung eines Batches von Matrizen.
Formeller gesprochen: für alle i
in index_space(result)
,
result[i0, ..., iR-3, :, :]
ist eine Cholesky-Zerlegung von
a[i0, ..., iR-3, :, :]
in Form eines unteren Dreiecks
(wenn lower
true
ist) oder eine obere dreieckige Form (wenn lower
false
ist) Matrix.
Die Ausgabewerte im entgegengesetzten Dreieck, d.h. dem genauen oberen Dreieck oder
sind implementierungsdefiniert.
Wenn es i
gibt, bei dem die Eingabematrix keine hermitische positiv-definierte ist
ist das Verhalten nicht definiert.
Führt für quantisierte Typen durch
dequantize_op_quantize(lambda operand: cholesky(operand, lower), a, type(result))
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | a |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1–C3) |
(I2) | lower |
0-dimensionale Tensorkonstante vom Typ i1 |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(a) = baseline_type(result)
. - (C2)
2 <= rank(a)
. - (C3)
dim(a, -2) = dim(a, -1)
.
Beispiele
// %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]
// ]
einschränken
Semantik
Fixiert jedes Element des Tensors operand
zwischen einem Mindest- und einem Höchstwert
und erzeugt einen result
-Tensor. Formeller, result[result_index] =
minimum(maximum(operand[result_index], min_element), max_element)
,
wobei min_element = rank(min) = 0 ? min[] : min[result_index]
,
max_element = rank(max) = 0 ? max[] : max[result_index]
Bei quantisierten Typen
führt dequantize_op_quantize(clamp, min, operand, max, type(result))
aus.
Komplexe Zahlen müssen mit einer überraschenden Semantik, Daher planen wir, die Unterstützung für komplexe Zahlen in Zukunft einzustellen. für diesen Vorgang (#560).
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | min |
Tensor oder quantisierter Tensor pro Tensor | (C1), (C3) |
(I2) | operand |
Tensor oder quantisierter Tensor pro Tensor | (C1–C4) |
(I3) | max |
Tensor oder quantisierter Tensor pro Tensor | (C2), (C3) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C4) |
Einschränkungen
- (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)
.
Beispiele
// %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
Semantik
Innerhalb jeder Prozessgruppe im StableHLO-Prozessraster senden Sie den Wert des
operand
-Tensor vom Quellprozess zu den Zielprozessen und erzeugt einen
result
-Tensor.
Der Vorgang teilt das StableHLO-Prozessraster in process_groups
auf,
wie folgt definiert:
cross_replica(replica_groups)
wennchannel_id <= 0
.cross_partition(replica_groups)
wennchannel_id > 0
.
Danach wird result@process
folgendermaßen angegeben:
operand@process_groups[i, 0]
, wenn eini
vorhanden ist, sodass der Prozess inprocess_groups[i]
.broadcast_in_dim(constant(is_quantized(result) ? quantize(0, element_type(result)) : 0, element_type(result)), [], type(result))
sonst.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor pro Tensor | (C3) |
(I2) | replica_groups |
Variadische Anzahl der eindimensionalen Tensorkonstanten vom Typ si64 |
(C1), (C2) |
(I3) | channel_id |
Konstante vom Typ si64 |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C3) |
Einschränkungen
- (C1)
is_unique(replica_groups)
. - (C2)
0 <= replica_groups < N
, wobeiN
definiert ist als: <ph type="x-smartling-placeholder">- </ph>
num_replicas
, wenncross_replica
verwendet wird.num_partitions
, wenncross_partition
verwendet wird.
- (C3)
type(result) = type(operand)
.
Beispiele
// 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
Semantik
Innerhalb jeder Prozessgruppe im StableHLO-Prozessraster wird der Wert der
operand
-Tensor vom Quellprozess zum Zielprozess und erzeugt einen
result
-Tensor.
Der Vorgang teilt das StableHLO-Prozessraster in process_groups
auf,
wie folgt definiert:
cross_replica(source_target_pairs)
wennchannel_id <= 0
.cross_partition(source_target_pairs)
wennchannel_id > 0
.
Danach wird result@process
folgendermaßen angegeben:
operand@process_groups[i, 0]
, wenn eini
vorhanden ist, sodassprocess_groups[i, 1] = process
broadcast_in_dim(constant(is_quantized(result) ? quantize(0, element_type(result)) : 0, element_type(result)), [], type(result))
sonst.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor pro Tensor | (C5) |
(I2) | source_target_pairs |
2-dimensionale Tensorkonstante vom Typ si64 |
(C1–C4) |
(I3) | channel_id |
Konstante vom Typ si64 |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (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
, wobeiN
definiert ist als: <ph type="x-smartling-placeholder">- </ph>
num_replicas
, wenncross_replica
verwendet wird.num_partitions
, wenncross_partition
verwendet wird.
- (C5)
type(result) = type(operand)
.
Beispiele
// 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]]
vergleichen
Semantik
Führt einen elementweisen Vergleich von lhs
- und rhs
-Tensoren durch
comparison_direction
und compare_type
und erzeugt einen result
-Tensor.
Für die Werte comparison_direction
und compare_type
gilt Folgendes:
Semantik:
Für boolesche und ganzzahlige Elementtypen:
EQ
:lhs = rhs
.NE
:lhs != rhs
.GE
:lhs >= rhs
.GT
:lhs > rhs
.LE
:lhs <= rhs
.LT
:lhs < rhs
.
Für Gleitkommaelementtypen mit compare_type = FLOAT
wird durch die Operation implementiert
folgende IEEE-754-Vorgänge:
EQ
:compareQuietEqual
.NE
:compareQuietNotEqual
.GE
:compareQuietGreaterEqual
.GT
:compareQuietGreater
.LE
:compareQuietLessEqual
.LT
:compareQuietLess
.
Bei Gleitkomma-Elementtypen mit compare_type = TOTALORDER
wird die Operation
verwendet die Kombination aus totalOrder
- und compareQuietEqual
-Vorgängen aus
IEEE-754.
Bei komplexen Elementtypen ist der lexikografische Vergleich von (real, imag)
-Paaren
wird mit den angegebenen comparison_direction
und compare_type
ausgeführt.
Komplexe Zahlen müssen mit einer überraschenden Semantik,
Daher planen wir, die Unterstützung für komplexe Zahlen in Zukunft einzustellen.
wenn comparison_direction
GE
, GT
, LE
oder LT
ist
(#560)
Für quantisierte Typen. führt dequantize_compare(lhs, rhs,
comparison_direction)
aus.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor oder quantisierter Tensor pro Tensor | (C1–C3) |
(I2) | rhs |
Tensor oder quantisierter Tensor pro Tensor | (C1–C2) |
(I3) | comparison_direction |
Aufzählung von EQ , NE , GE , GT , LE und LT |
|
(I4) | compare_type |
Aufzählung von FLOAT , TOTALORDER , SIGNED und UNSIGNED |
(C3) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des booleschen Typs | (C2) |
Einschränkungen
- (C1)
baseline_element_type(lhs) = baseline_element_type(rhs)
. - (C2)
shape(lhs) = shape(rhs) = shape(result)
. - (C3)
compare_type
ist definiert als: <ph type="x-smartling-placeholder">- </ph>
SIGNED
wennis_signed_integer(element_type(lhs))
.UNSIGNED
wennis_unsigned_integer(element_type(lhs)) or is_boolean(element_type(lhs))
.FLOAT
oderTOTALORDER
, wennis_float(element_type(lhs))
.FLOAT
wennis_complex(element_type(lhs))
.
Beispiele
// %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]
komplex
Semantik
Führt eine elementweise Konvertierung in einen komplexen Wert aus einem Paar aus reellen und
lhs
und rhs
und erzeugt einen result
-Tensor.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor vom Typ f32 oder f64 |
(C1–C3) |
(I2) | rhs |
Tensor vom Typ f32 oder f64 |
(C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des komplexen Typs | (C2), (C3) |
Einschränkungen
- (C1)
type(lhs) = type(rhs)
. - (C2)
shape(result) = shape(lhs)
. - (C3)
element_type(result)
hat den Typcomplex<E>
, wobeiE = element_type(lhs)
Beispiele
// %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)]
Zusammengesetzt
Semantik
Kapselt einen Vorgang ein, der aus anderen StableHLO-Vorgängen besteht,
inputs
und composite_attributes
nehmen und dabei results
produzieren. Die
Die Semantik des Vorgangs wird durch das Attribut decomposition
implementiert. Die
composite
-Op kann durch seine Zerlegung ersetzt werden, ohne das Programm zu ändern
Semantik. In Fällen, in denen die Inline-Zerlegung nicht die gleichen
Op-Semantik. Verwenden Sie am besten custom_call
.
Mit dem Feld version
(standardmäßig 0
) wird angegeben, wann die
eine Änderung der Semantik.
Eingaben
Label | Name | Typ |
---|---|---|
(I1) | inputs |
variadische Anzahl von Werten |
(I2) | name |
Konstante vom Typ string |
(I3) | composite_attributes |
Attributwörterbuch |
(I4) | decomposition |
Konstante vom Typ string |
(I5) | version |
Konstante vom Typ si32 |
Ausgaben
Name | Typ |
---|---|
results |
variadische Anzahl von Werten |
Einschränkungen
- (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)
Beispiele
%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>
concatenate
Semantik
Verkettet inputs
entlang der Dimension dimension
in derselben Reihenfolge wie bei
und erzeugt einen result
-Tensor. Formeller gesprochen
result[i0, ..., id, ..., iR-1] = inputs[k][i0, ..., kd, ..., iR-1]
. Dabei gilt:
id = d0 + ... + dk-1 + kd
.d
ist gleichdimension
undd0
sind died
. Dimensionsgrößen voninputs
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | inputs |
variadische Anzahl von Tensoren oder quantisierte Tensoren pro Tensor | (C1–C6) |
(I2) | dimension |
Konstante vom Typ si64 |
(C2), (C4), (C6) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C5 bis C6) |
Einschränkungen
- (C1)
same(element_type(inputs...))
. - (C2)
same(shape(inputs...))
mit Ausnahme vondim(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])
mit Ausnahme von: <ph type="x-smartling-placeholder">- </ph>
dim(result, dimension) = dim(inputs[0], dimension) + ...
.
Beispiele
// %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]]
Konstante
Semantik
Erzeugt einen output
-Tensor aus einer konstanten value
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | value |
Konstante | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
output |
Tensor oder quantisierter Tensor | (C1) |
Einschränkungen
- (C1)
type(value) = type(output)
.
Beispiele
%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]]
eine Conversion ausführen
Semantik
Führt eine elementweise Konvertierung von einem Elementtyp in einen anderen durch
operand
-Tensor und erzeugt einen result
-Tensor.
Für Conversions vom Typ boolean-to-any-supported-type ist der Wert false
in Null und der Wert true
in eins konvertiert. Für
any-supported-type-to-boolean-Conversions wird der Wert Null in
false
und Werte ungleich null werden in true
konvertiert. Unten sehen Sie, wie dies
auch für komplexe Typen.
Für Konvertierungen mit integer-to-integer und integer-to-floating-point oder floating-point-to-floating-point, wenn der Quellwert genau im Zieltyp dargestellt, ist der Ergebniswert genau Darstellung. Ansonsten steht das Verhalten noch nicht fest. (#180)
Bei Konvertierungen mit floating-point-to-integer ist der Bruchteil abgeschnitten. Wenn der abgeschnittene Wert nicht im Zieltyp dargestellt werden kann, Das Verhalten steht noch nicht fest (#180).
Konvertierungen, die komplex zu komplex umfassen, folgen dem gleichen Verhalten wie floating-point-to-floating-point-Conversions, mit denen reale und imaginären Teilen.
Für Conversions vom Typ complex-to-any-other-type und complex-to-any-other-type der imaginäre Quellwert wird ignoriert oder der imaginäre Zielwert ist auf 0 gesetzt. Die Umrechnung des realen Teils folgt der Gleitkomma-Conversions.
Prinzipiell könnte diese Operation eine Dequantisierung (Konvertierung von
quantisierte Tensoren in reguläre Tensoren), Quantisierung (Umwandlung von regulären Tensoren)
Tensoren in quantisierte Tensoren) und Requantisierung (Umwandlung zwischen quantisierten Tensoren
Tensoren), aber derzeit haben wir dafür dedizierte Operationen –
uniform_dequantize
für den ersten Anwendungsfall und uniform_quantize
für den
zweiten und dritten Anwendungsfälle. In Zukunft werden diese beiden Vorgänge möglicherweise zusammengeführt.
in convert
(#1576) ein.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor | (C1) |
Einschränkungen
- (C1)
shape(operand) = shape(result)
.
Beispiele
// %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)]
Faltung
Semantik
Berechnet Punktprodukte zwischen Fenstern von lhs
und Segmenten von rhs
und erzeugt
result
Das folgende Diagramm zeigt, wie Elemente in result
aus
lhs
und rhs
anhand eines konkreten Beispiels.
Formaler sollten Sie die folgende Neuformulierung der Eingaben in Bezug auf lhs
betrachten:
um Fenster von lhs
ausdrücken zu können:
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)
.
Für diese Neuausrichtung werden die folgenden Hilfsfunktionen verwendet:
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]
, wobeij[d] = i[permutation[d]]
.
Wenn feature_group_count = 1
und batch_group_count = 1
, dann für alle
output_spatial_index
in index_space(dim(result, output_spatial_dimensions...))
,
result[result_shape(:, output_spatial_index, :)] = dot_product
, wobei:
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])
Diese Funktion wird offenbar noch nicht genutzt. Daher planen wir, sie in Zukunft zu entfernen. es (#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])
.
Wenn 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)
.
Wenn 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)
Führt für quantisierte Typen 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))
aus.
Führt für hybrid quantisierte Typen 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)
aus.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor oder quantisierter Tensor pro Tensor | (C1), (C10-C11), (C14) (C25), (C27-C28), (C31-C32), (C34) |
(I2) | rhs |
Tensor oder quantisierter Tensor | (C1), (C14-C16), (C25), (C27-C29), (C31-C34) |
(I3) | window_strides |
Eindimensionale Tensorkonstante vom Typ si64 |
(C2–C3), (C25) |
(I4) | padding |
2-dimensionale Tensorkonstante vom Typ si64 |
(C4), (C25) |
(I5) | lhs_dilation |
Eindimensionale Tensorkonstante vom Typ si64 |
(C5–C6), (C25) |
(I6) | rhs_dilation |
Eindimensionale Tensorkonstante vom Typ si64 |
(C7–C8), (C25) |
(I7) | window_reversal |
Eindimensionale Tensorkonstante vom Typ i1 |
(C9) |
(I8) | input_batch_dimension |
Konstante vom Typ si64 |
(C10), (C13), (C25) |
(I9) | input_feature_dimension |
Konstante vom Typ si64 |
(C11), (C13-C14) |
(I10) | input_spatial_dimensions |
Eindimensionale Tensorkonstante vom Typ si64 |
(C12), (C13), (C25) |
(I11) | kernel_input_feature_dimension |
Konstante vom Typ si64 |
(C14), (C18) |
(I12) | kernel_output_feature_dimension |
Konstante vom Typ si64 |
(C15-C16), (C18), (C25), (C29) |
(I13) | kernel_spatial_dimensions |
Eindimensionale Tensorkonstante vom Typ si64 |
(C17-C18), (C25) |
(I14) | output_batch_dimension |
Konstante vom Typ si64 |
(C20), (C25) |
(I15) | output_feature_dimension |
Konstante vom Typ si64 |
(C20), (C25), (C30) |
(I16) | output_spatial_dimensions |
Eindimensionale Tensorkonstante vom Typ si64 |
(C19–C20), (C25) |
(I17) | feature_group_count |
Konstante vom Typ si64 |
(C11), (C14), (C16), (C21), (C23) |
(I18) | batch_group_count |
Konstante vom Typ si64 |
(C10), (C15), (C22), (C23), (C25) |
(I19) | precision_config |
Variadische Anzahl von Enums von DEFAULT , HIGH und HIGHEST |
(C24) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor | (C25-C28), (C30), (C32-34) |
Einschränkungen
- (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) Angegebener
input_dimensions = [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]
: <ph type="x-smartling-placeholder">- </ph>
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) Angegebener
kernel_dimensions = kernel_spatial_dimensions + [kernel_input_feature_dimension] + [kernel_output_feature_dimension]
: <ph type="x-smartling-placeholder">- </ph>
is_unique(kernel_dimensions)
.0 <= kernel_dimensions < N
.
- (C19)
size(output_spatial_dimensions) = N - 2
. - (C20) Angegebener
output_dimensions = [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]
: <ph type="x-smartling-placeholder">- </ph>
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)
dim(result, result_dim)
ist definiert als: <ph type="x-smartling-placeholder">- </ph>
dim(lhs, input_batch_dimension) / batch_group_count
wennresult_dim = output_batch_dimension
.dim(rhs, kernel_output_feature_dimension)
wennresult_dim = output_feature_dimension
.num_windows
, andernfalls gilt: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
. - Wenn die Operation nicht quantisierte Tensoren verwendet:
<ph type="x-smartling-placeholder">
- </ph>
- (C27)
element_type(lhs) = element_type(rhs) = element_type(result)
.
- (C27)
- Wenn die Operation quantisierte Tensoren verwendet:
<ph type="x-smartling-placeholder">
- </ph>
- (C28)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
. - (C29) Wenn
is_per_axis_quantized(rhs)
, danachquantization_dimension(rhs) = kernel_output_feature_dimension
. - (C30) Wenn
is_per_axis_quantized(result)
, dannquantization_dimension(result) = output_feature_dimension
. - Wenn
is_quantized(lhs)
: - (C31)
storage_type(lhs) = storage_type(rhs)
. - (C32)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
. - (C33) Wenn
is_per_tensor_quantized(rhs)
, dannis_per_tensor_quantized(result)
. - Wenn
!is_quantized(lhs)
: - (C34)
element_type(lhs) = expressed_type(rhs) = element_type(result)
.
- (C28)
Beispiele
// %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]]
// ]]
Kosinus
Semantik
Führt eine elementweise Kosinusoperation für den operand
-Tensor durch und erzeugt einen
result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für Gleitkommazahlen:
cos
aus IEEE-754. - Für komplexe Zahlen: komplexer Kosinus.
- Für quantisierte Typen:
dequantize_op_quantize(cosine, operand, type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %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
Semantik
Führt eine elementweise Zählung der Anzahl der führenden Null-Bits im operand
durch
und erzeugt einen result
-Tensor.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Ganzzahltyps | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Ganzzahltyps | (C1) |
Einschränkungen
- (C1)
type(operand) = type(result)
.
Beispiele
// %operand: [[0, 1], [128, -1]]
%result = "stablehlo.count_leading_zeros"(%operand) : (tensor<2x2xi64>) -> tensor<2x2xi64>
// %result: [[64, 63], [56, 0]]
custom_call
Semantik
Kapselt einen implementierungsdefinierten Vorgang call_target_name
, der
inputs
und called_computations
und produziert results
. has_side_effect
,
backend_config
und api_version
können verwendet werden, um zusätzliche
implementierungsdefinierten Metadaten.
Im Moment enthält dieser Vorgang eine ziemlich unorganisierte Sammlung von die die organische Entwicklung des entsprechenden Betriebs in den XLA-Compiler. Wir planen, diese Metadaten in Zukunft zu vereinheitlichen. (#741)
Eingaben
Label | Name | Typ |
---|---|---|
(I1) | inputs |
variadische Anzahl von Werten |
(I2) | call_target_name |
Konstante vom Typ string |
(I3) | has_side_effect |
Konstante vom Typ i1 |
(I4) | backend_config |
Konstante vom Typ string oder Attributwörterbuch |
(I5) | api_version |
Konstante vom Typ si32 |
(I6) | called_computations |
variadische Anzahl von Konstanten vom Typ string |
Ausgaben
Name | Typ |
---|---|
results |
variadische Anzahl von Werten |
Beispiele
%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>
Dividieren
Semantik
Führt die elementweise Division der Tensoren des Dividenden lhs
und des Divisors rhs
durch und
erzeugt einen result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für Ganzzahlen: Ganzzahldivision, die den algebraischen Quotienten mit einer beliebigen Bruchteil verworfen.
- Für Gleitkommazahlen:
division
aus IEEE-754. - Für komplexe Zahlen: komplexe Division.
- Für quantisierte Typen:
<ph type="x-smartling-placeholder">
- </ph>
dequantize_op_quantize(divide, lhs, rhs, type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor von Ganzzahl, Gleitkomma oder komplexer Typ oder quantisierter Tensor pro Tensor | (C1) |
(I2) | rhs |
Tensor von Ganzzahl, Gleitkomma oder komplexer Typ oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor von Ganzzahl, Gleitkomma oder komplexer Typ oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
.
Beispiele
// %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
Semantik
Berechnet die Punktprodukte zwischen Segmenten von lhs
und Segmenten von rhs
und erzeugt einen
result
-Tensor.
Formeller result[result_index] = dot_product
, wobei Folgendes gilt:
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
wobeisize(result_batching_index) = size(lhs_batching_dimensions)
,size(result_lhs_index) = size(lhs_result_dimensions)
undsize(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))
Führt für quantisierte Typen 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))
aus.
Führt für hybrid quantisierte Typen 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)
aus.
precision_config
steuert den Kompromiss zwischen Geschwindigkeit und Genauigkeit
auf Beschleuniger-Back-Ends. Dies kann einer der folgenden sein (im
ist die Semantik dieser enum-Werte zu niedrig angegeben.
wie Sie dieses Problem
#755):
DEFAULT
: Schnellste Berechnung, aber am wenigsten genaue Schätzung zum die ursprüngliche Nummer.HIGH
: langsamere Berechnung, aber genauere Annäherung an den Wert die ursprüngliche Nummer.HIGHEST
: Die langsamste Berechnung, aber die genaueste Annäherung an den Wert die ursprüngliche Nummer.
Ein DotAlgorithm
definiert die Haupteigenschaften des Algorithmus, der zur Implementierung verwendet wird.
die Punktoperation, die auch die Genauigkeit definiert. Wenn das Algorithmusattribut
festgelegt sind, muss precision_config
auf DEFAULT
gesetzt sein. DotAlgorithms
haben keinen Standardwert, da die Standardparameter
definiert. Daher können alle Punktalgorithmusfelder auf None
gesetzt werden, um ein
Algorithmus mit leerem Punkt, der stattdessen den Wert precision_config
verwendet.
DotAlgorithm
-Felder enthalten:
lhs_precision_type
undrhs_precision_type
, die Genauigkeit, die die linke und die linke Seite und die Die RHS-Werte des Vorgangs werden auf gerundet. Genauigkeitstypen sind unabhängig vom die Speichertypen der Ein- und Ausgabe.accumulation_type
ist die für die Akkumulation verwendete Genauigkeit.lhs_component_count
,rhs_component_count
undnum_primitive_operations
anwenden, wenn wir einen Algorithmus ausführen, der die linke und/oder rechte Seite in und führt mehrere "primitive" Punkt-Operationen auf diesen -Werte haben, um eine höhere Genauigkeit zu emulieren (z.B. Mit dem Datentyp bfloat16 „Artificial Intelligence“ für präzisere Berechnungen arbeiten: bf16_6x tf32_3x usw.). Bei Algorithmen ohne Zerlegung sind diese Werte sollte auf1
festgelegt sein.allow_imprecise_accumulation
, um anzugeben, ob die Akkumulation mit geringerer Genauigkeit verwendet wird ist für einige Schritte zulässig (z.B.CUBLASLT_MATMUL_DESC_FAST_ACCUM
).
Beispiele für DotAlgorithm
-Attribute:
// 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}
Es liegt an den Implementierungen, zu entscheiden, welche Kombinationen unterstützt werden. In kann nicht garantiert werden, dass jeder Algorithmus Beschleunigertyp durch den Nutzer des StableHLO. Wenn ein bestimmter Algorithmus sollte ein Fehler ausgegeben werden, anstatt auf einen als Alternative. Die StableHLO-Überprüfung ermöglicht eine bestmögliche Verifizierung, um Algorithmen zu verhindern, die bekanntermaßen auf keiner Hardware unterstützt werden.
Weitere Informationen finden Sie unter xla_data.proto > Algorithm
.
für einige unterstützte Algorithmuswerte. Ticket Nr. 2483 enthält den Plan zur Erstellung eines
zentralisiertes Dokument zu unterstützten Algorithmen vom Back-End.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor oder quantisierter Tensor pro Tensor | (C5-C6), (C9-C10), (C12-C14), (C17-C18), (C20) |
(I2) | rhs |
Tensor oder quantisierter Tensor | (C7-C10), (C12-C20) |
(I3) | lhs_batching_dimensions |
Eindimensionale Tensorkonstante vom Typ si64 |
(C1), (C3), (C5), (C9), (C12) |
(I4) | rhs_batching_dimensions |
Eindimensionale Tensorkonstante vom Typ si64 |
(C1), (C4), (C7), (C9) |
(I5) | lhs_contracting_dimensions |
Eindimensionale Tensorkonstante vom Typ si64 |
(C2), (C3), (C6), (C10) |
(I6) | rhs_contracting_dimensions |
Eindimensionale Tensorkonstante vom Typ si64 |
(C2), (C4), (C8), (C10), (C16) |
(I7) | precision_config |
Variadische Anzahl von Enums von DEFAULT , HIGH und HIGHEST |
(C11), (C21) |
(I8) | lhs_precision_type |
FloatType oder TensorFloat32 | (C21) |
(I9) | rhs_precision_type |
FloatType oder TensorFloat32 | (C21) |
(I10) | accumulation_type |
FloatType oder TensorFloat32 | (C21) |
(I11) | lhs_component_count |
Konstante vom Typ si32 |
(C21), (C22) |
(I12) | rhs_component_count |
Konstante vom Typ si32 |
(C21), (C23) |
(I13) | num_primitive_operations |
Konstante vom Typ si32 |
(C21), (C24) |
(I14) | allow_imprecise_accumulation |
Konstante vom Typ bool |
(C21) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor | (C12), (C14), (C18-C20) |
Einschränkungen
- (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)
. - Wenn die Operation nicht quantisierte Tensoren verwendet:
<ph type="x-smartling-placeholder">
- </ph>
- (C13)
element_type(lhs) = element_type(rhs)
.
- (C13)
- Wenn die Operation quantisierte Tensoren verwendet:
<ph type="x-smartling-placeholder">
- </ph>
- (C14)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
. - (C15)
zero_points(rhs) = 0
. - (C16) Wenn
is_per_axis_quantized(rhs)
, dannquantization_dimension(rhs)
nicht inrhs_contracting_dimensions
. - Wenn
is_quantized(lhs)
: - (C17)
storage_type(lhs) = storage_type(rhs)
. - (C18)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
. - (C19) Wenn
is_per_tensor_quantized(rhs)
, dannis_per_tensor_quantized(result)
. - Wenn
!is_quantized(lhs)
: - (C20)
element_type(lhs) = expressed_type(rhs) = element_type(result)
.
- (C14)
- Wenn
!is_empty_algorithm(lhs_precision_type, rhs_precision_type, accumulation_type, lhs_component_count, rhs_component_count, num_primitive_operations allow_imprecise_accumulation)
: <ph type="x-smartling-placeholder">- </ph>
- (C21)
precision_config... = DEFAULT
. - (C22)
0 < lhs_component_count
. - (C23)
0 < rhs_component_count
. - (C24)
0 < num_primitive_operations
.
- (C21)
Beispiele
// %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
Semantik
Funktional ist dieser Vorgang identisch mit
broadcast_in_dim
op, aber die Ergebnisform wird dynamisch über output_dimensions
angegeben.
Für den Vorgang werden auch die optionalen Attribute known_expanding_dimensions
und known_non_expanding_dimensions
akzeptiert.
um statisches Wissen über das Expansionsverhalten von Dimensionen auszudrücken.
Wenn nicht angegeben, wird angenommen, dass alle Abmessungen möglicherweise maximiert werden.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor | (C1-C2), (C5-C6), (C9) |
(I2) | output_dimensions |
Eindimensionaler Tensor vom Typ „Ganzzahl“ | (C7) |
(I3) | broadcast_dimensions |
1-dimensionaler Konstantentensor vom Ganzzahltyp | (C2–C6) |
(I4) | known_expanding_dimensions |
1-dimensionaler Konstantentensor vom Ganzzahltyp | (C8–C9) |
(I5) | known_non_expanding_dimensions |
1-dimensionaler Konstantentensor vom Ganzzahltyp | (C8–C9) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor | (C1), (C3), (C5-C7) |
Einschränkungen
- (C1)
element_type(result)
ist gegeben durch: <ph type="x-smartling-placeholder">- </ph>
element_type(operand)
, wenn!is_per_axis_quantized(operand)
.element_type(operand)
mit Ausnahme vonquantization_dimension(operand)
,scales(operand)
undzero_points(operand)
können abweichen vonquantization_dimension(result)
,scales(result)
undzero_points(result)
oder „Sonstiges“.
- (C2)
size(broadcast_dimensions) = rank(operand)
. - (C3)
0 <= broadcast_dimensions < rank(result)
. - (C4)
is_unique(broadcast_dimensions)
. - (C5) Für alle
d
inaxes(operand)
: <ph type="x-smartling-placeholder">- </ph>
dim(operand, d) = 1
oderdim(operand, d) = dim(result, broadcast_dimensions[d])
.
- (C6) Wenn
is_per_axis_quantized(result)
: <ph type="x-smartling-placeholder">- </ph>
quantization_dimension(result) = broadcast_dimensions[quantization_dimension(operand)]
.- Wenn
dim(operand, quantization_dimension(operand)) = 1
, dannscales(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)
.
Beispiele
// %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
Semantik
Funktional ist dieser Vorgang identisch mit
Faltung
op, aber das Padding wird dynamisch über padding
angegeben.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor oder quantisierter Tensor pro Tensor | (C1), (C10-C11), (C14) (C25), (C26-C27), (C30-C31), (C33) |
(I2) | rhs |
Tensor oder quantisierter Tensor | (C1), (C14-C16), (C26-C28), (C30-C33) |
(I3) | padding |
2-dimensionaler Tensor vom Ganzzahltyp | (C4) |
(I4) | window_strides |
Eindimensionale Tensorkonstante vom Typ si64 |
(C2–C3) |
(I5) | lhs_dilation |
Eindimensionale Tensorkonstante vom Typ si64 |
(C5 bis C6) |
(I6) | rhs_dilation |
Eindimensionale Tensorkonstante vom Typ si64 |
(C7–C8) |
(I7) | window_reversal |
Eindimensionale Tensorkonstante vom Typ i1 |
(C9) |
(I8) | input_batch_dimension |
Konstante vom Typ si64 |
(C10), (C13) |
(I9) | input_feature_dimension |
Konstante vom Typ si64 |
(C11), (C13-C14) |
(I10) | input_spatial_dimensions |
Eindimensionale Tensorkonstante vom Typ si64 |
(C12), (C13) |
(I11) | kernel_input_feature_dimension |
Konstante vom Typ si64 |
(C14), (C18) |
(I12) | kernel_output_feature_dimension |
Konstante vom Typ si64 |
(C15-C16), (C18), (C28) |
(I13) | kernel_spatial_dimensions |
Eindimensionale Tensorkonstante vom Typ si64 |
(C17–C18) |
(I14) | output_batch_dimension |
Konstante vom Typ si64 |
(C20) |
(I15) | output_feature_dimension |
Konstante vom Typ si64 |
(C20), (C29) |
(I16) | output_spatial_dimensions |
Eindimensionale Tensorkonstante vom Typ si64 |
(C19–C20) |
(I17) | feature_group_count |
Konstante vom Typ si64 |
(C11), (C14), (C16), (C21), (C23) |
(I18) | batch_group_count |
Konstante vom Typ si64 |
(C10), (C15), (C22), (C23) |
(I19) | precision_config |
Variadische Anzahl von Enums von DEFAULT , HIGH und HIGHEST |
(C24) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor | (C25-C27), (C29), (C31-C33) |
Einschränkungen
- (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) Angegebener
input_dimensions = [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]
: <ph type="x-smartling-placeholder">- </ph>
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) Angegebener
kernel_dimensions = kernel_spatial_dimensions + [kernel_input_feature_dimension] + [kernel_output_feature_dimension]
: <ph type="x-smartling-placeholder">- </ph>
is_unique(kernel_dimensions)
.0 <= kernel_dimensions < N
.
- (C19)
size(output_spatial_dimensions) = N - 2
. - (C20) Angegebener
output_dimensions = [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]
: <ph type="x-smartling-placeholder">- </ph>
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)
dim(result, result_dim)
ist definiert als: <ph type="x-smartling-placeholder">- </ph>
dim(lhs, input_batch_dimension) / batch_group_count
wennresult_dim = output_batch_dimension
.dim(rhs, kernel_output_feature_dimension)
wennresult_dim = output_feature_dimension
.num_windows
, andernfalls gilt: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
. - Wenn die Operation nicht quantisierte Tensoren verwendet:
<ph type="x-smartling-placeholder">
- </ph>
- (C27)
element_type(lhs) = element_type(rhs) = element_type(result)
.
- (C27)
- Wenn die Operation quantisierte Tensoren verwendet:
<ph type="x-smartling-placeholder">
- </ph>
- (C28)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
. - (C29) Wenn
is_per_axis_quantized(rhs)
, danachquantization_dimension(rhs) = kernel_output_feature_dimension
. - (C30) Wenn
is_per_axis_quantized(result)
, dannquantization_dimension(result) = output_feature_dimension
. - Wenn
is_quantized(lhs)
: - (C31)
storage_type(lhs) = storage_type(rhs)
. - (C32)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
. - (C33) Wenn
is_per_tensor_quantized(rhs)
, dannis_per_tensor_quantized(result)
. - Wenn
!is_quantized(lhs)
: - (C34)
element_type(lhs) = expressed_type(rhs) = element_type(result)
.
- (C28)
Beispiele
// %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
Semantik
Funktional ist dieser Vorgang identisch mit
sammeln
Vorgang ausführen, wobei slice_sizes
dynamisch als Wert angegeben wird.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor pro Tensor | (C1), (C7), (C10-C12), (C14) |
(I2) | start_indices |
Tensor des Ganzzahltyps | (C2), (C3), (C13) |
(I3) | slice_sizes |
Eindimensionaler Tensor vom Typ „Ganzzahl“ | (C8), (C11–C13) |
(I4) | offset_dims |
Eindimensionale Tensorkonstante vom Typ si64 |
(C1), (C4-C5), (C13) |
(I5) | collapsed_slice_dims |
Eindimensionale Tensorkonstante vom Typ si64 |
(C1), (C6-C8), (C13) |
(I6) | start_index_map |
Eindimensionale Tensorkonstante vom Typ si64 |
(C3), (C9), (C10) |
(I7) | index_vector_dim |
Konstante vom Typ si64 |
(C2), (C3), (C13) |
(I8) | indices_are_sorted |
Konstante vom Typ i1 |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C5), (C13-C14) |
Einschränkungen
- (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)
, wobei gilt: <ph type="x-smartling-placeholder">- </ph>
batch_dim_sizes = shape(start_indices)
mit dem Unterschied, dass die Dimensionsgröße vonstart_indices
, dieindex_vector_dim
entsprechen, sind nicht enthalten.offset_dim_sizes = shape(slice_sizes)
mit dem Unterschied, dass die Dimensionsgrößen inslice_sizes
, diecollapsed_slice_dims
entsprechen, sind nicht enthalten.combine
platziertbatch_dim_sizes
auf den Achsenbatch_dims
undoffset_dim_sizes
an den Achsen, dieoffset_dims
entsprechen.
- (C14)
element_type(operand) = element_type(result)
.
Beispiele
// %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
Semantik
Funktional ist dieser Vorgang identisch mit
IoTa
op, aber die Ergebnisform wird dynamisch über output_shape
angegeben.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | output_shape |
Eindimensionaler Tensor vom Typ „Ganzzahl“ | (C1), (C2) |
(I2) | iota_dimension |
si64 |
(C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor von Ganzzahl, Gleitkomma oder komplexer Typ oder quantisierter Tensor pro Tensor | (C2) |
Einschränkungen
- (C1)
0 <= iota_dimension < size(output_shape)
. - (C2)
rank(result) = size(output_shape)
.
Beispiele
%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
Semantik
Funktional ist dieser Vorgang identisch mit
Pad
op, aber mit edge_padding_low
, edge_padding_high
und interior_padding
dynamisch als Werte angegeben werden.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor pro Tensor | (C1), (C2), (C4) |
(I2) | padding_value |
0-dimensionaler Tensor oder quantisierter Tensor pro Tensor | (C1) |
(I3) | edge_padding_low |
Eindimensionaler Tensor vom Typ „Ganzzahl“ | (C1), (C4) |
(I4) | edge_padding_high |
Eindimensionaler Tensor vom Typ „Ganzzahl“ | (C1), (C4) |
(I5) | interior_padding |
Eindimensionaler Tensor vom Typ „Ganzzahl“ | (C2–C4) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C3 bis C6) |
Einschränkungen
- (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
.
Beispiele
// %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
Semantik
Funktional ist dieser Vorgang identisch mit
umformen
op, aber die Ergebnisform wird dynamisch über output_shape
angegeben.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor | (C1–C3) |
(I2) | output_shape |
Eindimensionaler Tensor vom Typ „Ganzzahl“ | (C4) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor | (C1–C4) |
Einschränkungen
- (C1)
element_type(result)
ist gegeben durch: <ph type="x-smartling-placeholder">- </ph>
element_type(operand)
, wenn!is_per_axis_quantized(operand)
.element_type(operand)
mit dem Unterschied, dassquantization_dimension(operand)
und Andernfalls kannquantization_dimension(result)
abweichen.
- (C2)
size(operand) = size(result)
. - (C3) Wenn
is_per_axis_quantized(operand)
: <ph type="x-smartling-placeholder">- </ph>
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)
.
Beispiele
// %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
Semantik
Extrahiert ein Segment aus dem operand
mit dynamisch berechneten Startindexen
und erzeugt einen result
-Tensor. start_indices
enthalten die Startindizes von
das Segment für jede Dimension vorbehaltlich möglicher Anpassungen und slice_sizes
die Größen des Segments für jede Dimension enthalten. Formeller gesprochen
result[result_index] = operand[operand_index]
, wobei gilt:
adjusted_start_indices = clamp(0, start_indices, shape(operand) - slice_sizes)
.operand_index = adjusted_start_indices + result_index
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor pro Tensor | (C1), (C2), (C4) |
(I2) | start_indices |
Variadische Anzahl von 0-dimensionalen Tensoren vom Typ „Ganzzahl“ | (C2), (C3) |
(I3) | slice_sizes |
Eindimensionale Tensorkonstante vom Typ si64 |
(C2), (C4), (C5) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C1), (C5) |
Einschränkungen
- (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
.
Beispiele
// %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
Semantik
Erzeugt einen result
-Tensor, der dem operand
-Tensor entspricht, mit der Ausnahme,
Das Segment, das bei start_indices
beginnt, wird mit den Werten in update
aktualisiert.
Formal ist result[result_index]
so definiert:
update[update_index]
, wenn0 <= update_index < shape(update)
, wobei gilt: <ph type="x-smartling-placeholder">- </ph>
adjusted_start_indices = clamp(0, start_indices, shape(operand) - shape(update))
.update_index = result_index - adjusted_start_indices
.
- Andernfalls
operand[result_index]
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor pro Tensor | (C1–C4), (C6) |
(I2) | update |
Tensor oder quantisierter Tensor pro Tensor | (C2), (C3), (C6) |
(I3) | start_indices |
Variadische Anzahl von 0-dimensionalen Tensoren vom Typ „Ganzzahl“ | (C4), (C5) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (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)
.
Beispiele
// %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]
// ]
Exponentialfunktionen
Semantik
Führt eine elementweise exponentielle Operation für den operand
-Tensor durch und erzeugt einen
result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für Gleitkommazahlen:
exp
aus IEEE-754. - Für komplexe Zahlen: komplexe Exponentialfunktionen.
- Für quantisierte Typen:
dequantize_op_quantize(exponential, operand, type(result))
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %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
Semantik
Führt eine elementweise exponentielle abzüglich einer Operation mit dem Tensor operand
durch und
erzeugt einen result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für Gleitkommazahlen:
expm1
aus IEEE-754. - Für komplexe Zahlen: komplexe Exponentialzahlen minus eins.
- Für quantisierte Typen:
dequantize_op_quantize(exponential_minus_one, operand, type(result))
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %operand: [0.0, 1.0]
%result = "stablehlo.exponential_minus_one"(%operand) : (tensor<2xf64>) -> tensor<2xf64>
// %result: [0.0, 1.71828187]
fft
Semantik
Führt die Vorwärts- und Inversen Fourier-Transformationen für reelle und komplexe Ein- und Ausgaben.
fft_type
ist einer der folgenden Werte:
FFT
: Forward-Komplex-zu-Komplex-FFT.IFFT
: Umgekehrte komplexe-zu-komplexe FFT.RFFT
: Echtzeit-FFT (Real-to-Complexed FFT)IRFFT
: Inverse Real-to-Complex-FFT (d.h., nimmt komplex an und gibt reell zurück).
Formaler ist die Funktion fft
, die eindimensionale Tensoren von
als Eingabe nutzt, werden 1-dimensionale Tensoren
derselben Typen wie
Ausgabe und berechnet die diskrete Fourier-Transformation:
Für fft_type = FFT
ist result
als Endergebnis einer Reihe von L
Berechnungen, bei denen L = size(fft_length)
. Zum Beispiel für 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])
.
Außerdem hat die Funktion ifft
dieselbe Typsignatur und
berechnet den Kehrwert von fft
:
Für fft_type = IFFT
ist result
als Kehrwert der Berechnungen definiert.
für fft_type = FFT
. Zum Beispiel für 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, ..., :])
.
Darüber hinaus werden bei der Funktion rfft
, die eindimensionale Tensoren
Gleitkommatypen werden, erzeugt 1-dimensionale Tensoren von komplexen Typen des
Gleitkomma-Semantik und funktioniert so:
rfft(real_operand) = truncated_result
, wobeicomplex_operand... = (real_operand..., 0.0)
.complex_result = fft(complex_operand)
.truncated_result = complex_result[:(rank(complex_result) / 2 + 1)]
.
(Bei der Berechnung der diskreten Fourier-Transformation für reelle Operanden wird die erste
N/2 + 1
-Elemente des Ergebnisses definieren den Rest des Ergebnisses eindeutig,
sodass das Ergebnis von rfft
abgeschnitten wird, um die Berechnung redundanter Elemente zu vermeiden.
Für fft_type = RFFT
ist result
als Endergebnis einer Reihe von L
Berechnungen, bei denen L = size(fft_length)
. Zum Beispiel für 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])
.
Bei der Funktion irfft
, die dieselbe Typsignatur und
berechnet den Kehrwert von rfft
:
Für fft_type = IRFFT
ist result
als Kehrwert der Berechnungen definiert.
für fft_type = RFFT
. Zum Beispiel für 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, ..., :])
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkomma- oder komplexen Typs | (C1), (C2), (C4), (C5) |
(I2) | fft_type |
Aufzählung von FFT , IFFT , RFFT und IRFFT |
(C2), (C5) |
(I3) | fft_length |
Eindimensionale Tensorkonstante vom Typ si64 |
(C1), (C3), (C4) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkomma- oder komplexen Typs | (C2), (C4), (C5) |
Einschränkungen
- (C1)
size(fft_length) <= rank(operand)
. - (C2) Die Beziehung zwischen den Elementtypen
operand
undresult
variiert: <ph type="x-smartling-placeholder">- </ph>
- Wenn
fft_type = FFT
,element_type(operand)
undelement_type(result)
denselben komplexen Typ haben. - Wenn
fft_type = IFFT
,element_type(operand)
undelement_type(result)
denselben komplexen Typ haben. - Wenn
fft_type = RFFT
, istelement_type(operand)
ein Gleitkommatyp undelement_type(result)
ist ein komplexer Typ desselben Gleitkommawerts Semantik. - Wenn
fft_type = IRFFT
, istelement_type(operand)
ein komplexer Typ undelement_type(result)
ist ein Gleitkommatyp desselben Gleitkommawerts Semantik.
- Wenn
- (C3)
1 <= size(fft_length) <= 3
. - (C4) Wenn es zwischen
operand
undresult
gibt, gibt es den Tensorreal
eines Gleitkommatyp, dannshape(real)[-size(fft_length):] = fft_length
. - (C5)
shape(result) = shape(operand)
mit Ausnahme von: <ph type="x-smartling-placeholder">- </ph>
- Wenn
fft_type = RFFT
,dim(result, -1) = dim(operand, -1) = 0 ? 0 : dim(operand, -1) / 2 + 1
. - Wenn
fft_type = IRFFT
,dim(operand, -1) = dim(result, -1) = 0 ? 0 : dim(result, -1) / 2 + 1
.
- Wenn
Beispiele
// %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)]
Boden
Semantik
Führt die elementweisen Untergrenze des operand
-Tensors durch und erzeugt einen result
-Tensor.
Implementiert den roundToIntegralTowardNegative
-Vorgang aus IEEE-754
Spezifikation zu ändern. Führt für quantisierte Typen durch
dequantize_op_quantize(floor, operand, type(result))
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %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]
sammeln
Semantik
Erfasst Segmente vom Tensor operand
aus den in start_indices
angegebenen Offsets
und erzeugt einen result
-Tensor.
Das folgende Diagramm zeigt, wie Elemente in result
den Elementen in
operand
anhand eines konkreten Beispiels. Im Diagramm finden Sie einige Beispiele result
-Indizes und erläutert detailliert, welchen operand
-Indizes sie entsprechen.
Formeller result[result_index] = operand[operand_index]
, wobei Folgendes gilt:
batch_dims = [d for d in axes(result) and d not in offset_dims]
.batch_index = result_index[batch_dims...]
.start_index
ist definiert als: <ph type="x-smartling-placeholder">- </ph>
start_indices[bi0, ..., :, ..., biN]
, wobeibi
einzelne Elemente inbatch_index
und:
werden beim Indexindex_vector_dim
eingefügt, wennindex_vector_dim
<rank(start_indices)
- Andernfalls
[start_indices[batch_index]]
.
- Für
d_operand
inaxes(operand)
, <ph type="x-smartling-placeholder">- </ph>
full_start_index[d_operand] = clamp(start_index[d_start], 0, dim(operand, d_operand) - slice_sizes[d_operand])
wennd_operand = start_index_map[d_start]
.- Andernfalls
full_start_index[d_operand] = 0
.
- Für
d_operand
inaxes(operand)
, <ph type="x-smartling-placeholder">- </ph>
full_batching_index[d_operand] = batch_index[d_start - (d_start < index_vector_dim ? 0 : 1)]
wennd_operand = operand_batching_dims[i_batching]
undd_start = start_indices_batching_dims[i_batching]
- Andernfalls
full_batching_index[d_operand] = 0
.
offset_index = result_index[offset_dims...]
.full_offset_index = [oi0, ..., 0, ..., oiN]
, bei denenoi
individuell sind Elemente inoffset_index
und0
wird an Indexen voncollapsed_slice_dims
undoperand_batching_dims
.operand_index = full_start_index + full_batching_index + full_offset_index
Wenn indices_are_sorted
den Wert true
hat, kann die Implementierung davon ausgehen, dass
start_indices
werden nach start_index_map
sortiert. Andernfalls
Verhalten ist nicht definiert. Formeller für alle i1 < i2
aus indices(result)
,
full_start_index(i1) <= full_start_index(i2)
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor pro Tensor | (C1), (C8), (C11), (C17), (C19-C21), (C23) |
(I2) | start_indices |
Tensor des Ganzzahltyps | (C2-C3), (C14), (C17), (C22) |
(I3) | offset_dims |
Eindimensionale Tensorkonstante vom Typ si64 |
(C1), (C4-C5), (C22) |
(I4) | collapsed_slice_dims |
Eindimensionale Tensorkonstante vom Typ si64 |
(C1), (C6-C9), (C22) |
(I5) | operand_batching_dims |
Eindimensionale Tensorkonstante vom Typ si64 |
(C1), (C6), (C10-C12), (C16-C18), (C22) |
(I6) | start_indices_batching_dims |
Eindimensionale Tensorkonstante vom Typ si64 |
(C13–C17) |
(I7) | start_index_map |
Eindimensionale Tensorkonstante vom Typ si64 |
(C3), (C18–C19) |
(I8) | index_vector_dim |
Konstante vom Typ si64 |
(C2-C3), (C15), (C22) |
(I9) | slice_sizes |
Eindimensionale Tensorkonstante vom Typ si64 |
(C9), (C12), (C20-C22) |
(I10) | indices_are_sorted |
Konstante vom Typ i1 |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C5), (C22-C23) |
Einschränkungen
- (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)
, wobei Folgendes gilt: <ph type="x-smartling-placeholder">- </ph>
batch_dim_sizes = shape(start_indices)
mit dem Unterschied, dass die Dimensionsgröße vonstart_indices
, dieindex_vector_dim
entsprechen, sind nicht enthalten.offset_dim_sizes = slice_sizes
, mit dem Unterschied, dass die Dimensionsgrößen inslice_sizes
entsprichtcollapsed_slice_dims
undoperand_batching_dims
sind nicht enthalten.combine
platziertbatch_dim_sizes
auf den Achsenbatch_dims
undoffset_dim_sizes
an den Achsen, dieoffset_dims
entsprechen.
- (C23)
element_type(operand) = element_type(result)
.
Beispiele
// %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
Semantik
Erzeugt die Größe des angegebenen dimension
von operand
. Formeller gesprochen
result = dim(operand, dimension)
Die Semantik betrifft nur die Form.
-Komponente des Typs. Der Elementtyp kann beliebig sein.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor | (C1) |
(I2) | dimension |
Konstante vom Typ si64 |
(C1) |
Ausgaben
Name | Typ |
---|---|
result |
0-dimensionaler Tensor vom Typ si32 |
Einschränkungen
- (C1)
0 <= dimension < rank(operand)
.
Beispiele
// %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
<ph type="x-smartling-placeholder"></ph>
Semantik
Extrahiert ein Element an der index
-Position des operand
-Tupels und erzeugt einen
result
Formeller: result = operand[index]
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tupel | (C1), (C2) |
(I2) | index |
Konstante vom Typ si32 |
(C1), (C2) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Beliebiger unterstützter Typ | (C2) |
Einschränkungen
- (C1)
0 <= index < size(operand)
. - (C2)
type(result) = tuple_element_types(operand)[index]
.
Beispiele
// %operand: ([1.0, 2.0], (3))
index = 0 : i32
} : (tuple<tensor<2xf32>, tuple<tensor<i32>>>) -> tensor<2xf32>
// %result: [1.0, 2.0]
wenn
Semantik
Erzeugt die Ausgabe, wenn genau eine Funktion aus true_branch
ausgeführt wird oder
false_branch
abhängig vom Wert von pred
. Formeller: result =
pred ? true_branch() : false_branch()
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | pred |
0-dimensionaler Tensor vom Typ i1 |
|
(I2) | true_branch |
Funktion | (C1–C3) |
(I3) | false_branch |
Funktion | (C1), (C2) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
results |
variadische Anzahl von Tensoren, quantisierte Tensoren oder Tokens | (C3) |
Einschränkungen
- (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)
.
Beispiele
// %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
Bild
Semantik
Extrahiert den imaginären Teil elementweise aus dem operand
und erzeugt ein
result
-Tensor. Formeller gesprochener Text für jedes Element x
:
imag(x) = is_complex(x) ? imaginary_part(x) :
constant(0, element_type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkomma- oder komplexen Typs | (C1), (C2) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkommatyps | (C1), (C2) |
Einschränkungen
- (C1)
shape(result) = shape(operand)
. - (C2)
element_type(result)
ist definiert als: <ph type="x-smartling-placeholder">- </ph>
complex_element_type(element_type(operand))
wennis_complex(operand)
.- Andernfalls
element_type(operand)
.
Beispiele
// %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
Semantik
Liest Daten aus dem In-Feed und erstellt results
.
Die Semantik von infeed_config
ist implementierungsdefiniert.
results
bestehen aus Nutzlastwerten, die an erster Stelle stehen, und einem Token, das
zuletzt. In Zukunft planen wir, die Nutzlast und das Token in zwei Teile aufzuteilen.
separate Ausgaben, um die Verständlichkeit zu verbessern
(#670)
Eingaben
Label | Name | Typ |
---|---|---|
(I1) | token |
token |
(I2) | infeed_config |
Konstante vom Typ string |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
results |
variadische Anzahl von Tensoren, quantisierte Tensoren oder Tokens | (C1–C3) |
Einschränkungen
- (C1)
0 < size(results)
. - (C2)
is_empty(result[:-1])
oderis_tensor(type(results[:-1]))
. - (C3)
is_token(type(results[-1]))
.
Beispiele
// %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]]
ITA
Semantik
Füllt einen output
-Tensor mit Werten in aufsteigender Reihenfolge, beginnend bei null
entlang der Dimension iota_dimension
. Formeller gesprochen
output[output_index] = constant(is_quantized(output) ?
quantize(output_index[iota_dimension], element_type(output)) :
output_index[iota_dimension], element_type(output))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | iota_dimension |
si64 |
(C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
output |
Tensor von Ganzzahl, Gleitkomma oder komplexer Typ oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
0 <= iota_dimension < rank(output)
.
Beispiele
%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
Semantik
Führt eine elementweise Prüfung durch, ob der Wert in x
endlich ist (d.h. weder
+Inf, -Inf oder NaN) und erzeugt einen y
-Tensor. Implementiert die isFinite
aus der IEEE-754-Spezifikation. Für quantisierte Typen lautet das Ergebnis:
immer true
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | x |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
y |
Tensor des booleschen Typs | (C1) |
Einschränkungen
- (C1)
shape(x) = shape(y)
.
Beispiele
// 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
Semantik
Führt eine elementweise Logarithmus-Operation für den operand
-Tensor durch und erzeugt einen
result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für Gleitkommazahlen:
log
aus IEEE-754. - Für komplexe Zahlen: komplexer Logarithmus.
- Für quantisierte Typen:
dequantize_op_quantize(log, operand, type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %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
Semantik
Führt einen elementweisen Logarithmus plus eine Operation für den operand
-Tensor durch und
erzeugt einen result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für Gleitkommazahlen:
logp1
aus IEEE-754. - Für komplexe Zahlen: komplexer Logarithmus plus eins.
- Für quantisierte Typen:
dequantize_op_quantize(log_plus_one, operand, type(result))
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %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]
Logistik
Semantik
Führt eine elementweise logistische Operation am Tensor operand
durch und erzeugt eine
result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für Gleitkommazahlen:
division(1, addition(1, exp(-x)))
aus IEEE-754. - Für komplexe Zahlen: komplexe logistische Zahlen
- Für quantisierte Typen:
dequantize_op_quantize(logistic, operand, type(result))
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %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]]
Karte
<ph type="x-smartling-placeholder"></ph>
Semantik
Wendet eine Kartenfunktion computation
auf inputs
entlang der dimensions
und
erzeugt einen result
-Tensor.
Formeller: result[result_index] = computation(inputs...[result_index])
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | inputs |
variadische Anzahl von Tensoren oder quantisierte Tensoren pro Tensor | (C1–C4) |
(I2) | dimensions |
Eindimensionale Tensorkonstante vom Typ si64 |
(C3) |
(I3) | computation |
Funktion | (C4) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C1), (C4) |
Einschränkungen
- (C1)
shape(inputs...) = shape(result)
. - (C2)
0 < size(inputs) = N
. - (C3)
dimensions = range(rank(inputs[0]))
. - (C4)
computation
hat den Typ(tensor<E0>, ..., tensor<EN-1>) -> tensor<E'>
Dabei gilt:Ei = element_type(inputs[i])
undE' = element_type(result)
.
Beispiele
// %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]]
Maximum
Semantik
Führt eine elementweise max-Operation für die Tensoren lhs
und rhs
durch und erzeugt eine
result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für boolesche Werte: logisches ODER.
- Für Ganzzahlen: Ganzzahlhöchstwert.
- Für Gleitkommazahlen:
maximum
aus IEEE-754. - Für komplexe Zahlen: lexikografisches Maximum für das
(real, imaginary)
-Paar. Komplexe Zahlen müssen mit einer überraschenden Semantik, Daher planen wir, die Unterstützung für komplexe Zahlen in Zukunft einzustellen. für diesen Vorgang (#560). - Für quantisierte Typen:
<ph type="x-smartling-placeholder">
- </ph>
dequantize_op_quantize(maximum, lhs, rhs, type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor oder quantisierter Tensor pro Tensor | (C1) |
(I2) | rhs |
Tensor oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
.
Beispiele
// %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
Semantik
Führt eine elementweise Min-Operation für die Tensoren lhs
und rhs
aus und erzeugt eine
result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für boolesche Werte: logisches UND.
- Für Ganzzahlen: minimale Ganzzahl.
- Für Gleitkommazahlen:
minimum
aus IEEE-754. - Für komplexe Zahlen: lexikografisches Minimum für das
(real, imaginary)
-Paar. Komplexe Zahlen müssen mit einer überraschenden Semantik, Daher planen wir, die Unterstützung für komplexe Zahlen in Zukunft einzustellen. für diesen Vorgang (#560). - Für quantisierte Typen:
<ph type="x-smartling-placeholder">
- </ph>
dequantize_op_quantize(minimum, lhs, rhs, type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor oder quantisierter Tensor pro Tensor | (C1) |
(I2) | rhs |
Tensor oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
.
Beispiele
// %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]]
multiplizieren
Semantik
Führt das elementweise Produkt der zwei Tensoren lhs
und rhs
durch und erzeugt einen
result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für boolesche Werte: logisches UND.
- Für Ganzzahlen: Ganzzahlmultiplikation.
- Für Gleitkommazahlen:
multiplication
aus IEEE-754. - Für komplexe Zahlen: komplexe Multiplikation
- Für quantisierte Typen:
<ph type="x-smartling-placeholder">
- </ph>
dequantize_op_quantize(multiply, lhs, rhs, type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor oder quantisierter Tensor pro Tensor | (C1) |
(I2) | rhs |
Tensor oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %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]]
negate
Semantik
Führt eine elementweise Negation des operand
-Tensors durch und erzeugt einen result
-Wert
Tensor. Gehen Sie je nach Elementtyp so vor:
- Für vorzeichenbehaftete Ganzzahlen: Ganzzahl-Negation.
- Für vorzeichenlose Ganzzahlen: Bitcast in eine vorzeichenbehaftete Ganzzahl, Ganzzahlnegation, Bitcast zurück zur vorzeichenlosen Ganzzahl.
- Für Gleitkommazahlen:
negate
aus IEEE-754. - Für komplexe Zahlen: komplexe Negation.
- Für quantisierte Typen:
dequantize_op_quantize(negate, operand, type(result))
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor von Ganzzahl, Gleitkomma oder komplexer Typ oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor von Ganzzahl, Gleitkomma oder komplexer Typ oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// 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]
nicht
Semantik
Führt das elementweise NOT des Tensors operand
durch und erzeugt einen result
-Tensor.
Gehen Sie je nach Elementtyp so vor:
- Für boolesche Werte: logisches NOT.
- Für Ganzzahlen: bitweises NOT.
Argumente
Name | Typ | Einschränkungen |
---|---|---|
operand |
Tensor des booleschen oder Ganzzahltyps | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des booleschen oder Ganzzahltyps | (C1) |
Einschränkungen
- (C1)
type(operand) = type(result)
.
Beispiele
// 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
Semantik
Sorgt dafür, dass die Vorgänge, die den operand
erzeugen, vor jedem ausgeführt werden
Vorgänge, die von result
abhängig sind und Compilertransformationen verhindern
die Hürde zu überwinden. Ansonsten ist der Vorgang
eine Identität, z.B. result = operand
.
Argumente
Name | Typ | Einschränkungen |
---|---|---|
operand |
variadische Anzahl von Tensoren, quantisierte Tensoren oder Tokens pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
variadische Anzahl von Tensoren, quantisierte Tensoren oder Tokens pro Tensor | (C1) |
Einschränkungen
- (C1)
type(operand...) = type(result...)
.
Beispiele
// %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
oder
Semantik
Führt das elementweise ODER der zwei Tensoren lhs
und rhs
durch und erzeugt eine result
Tensor. Gehen Sie je nach Elementtyp so vor:
- Für boolesche Werte: logisches ODER.
- Für Ganzzahlen: bitweises ODER.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor des Ganzzahl- oder booleschen Typs | (C1) |
(I2) | rhs |
Tensor des Ganzzahl- oder booleschen Typs | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Ganzzahl- oder booleschen Typs | (C1) |
Einschränkungen
- (C1)
type(lhs) = type(rhs) = type(result)
.
Beispiele
// 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
Semantik
Schreibt inputs
in die Ausgabe und generiert ein result
-Token.
Die Semantik von outfeed_config
ist implementierungsdefiniert.
Eingaben
Label | Name | Typ |
---|---|---|
(I1) | inputs |
variadische Anzahl von Tensoren oder quantisierten Tensoren |
(I2) | token |
token |
(I3) | outfeed_config |
Konstante vom Typ string |
Ausgaben
Name | Typ |
---|---|
result |
token |
Beispiele
%result = "stablehlo.outfeed"(%input0, %token) {
outfeed_config = ""
} : (tensor<2x2x2xi64>, !stablehlo.token) -> !stablehlo.token
Pad
Semantik
Erweitert operand
durch ein Padding um den Tensor und zwischen den Elementen
des Tensors mit dem angegebenen padding_value
.
edge_padding_low
und edge_padding_high
geben an, wie viel Abstand hinzugefügt wurde.
am unteren Rand (neben Index 0) und dem High-End-Wert (neben dem höchsten Index) von
für jede Dimension auswählen. Der Abstand kann negativ sein, wobei der
Der absolute Wert des negativen Paddings gibt die Anzahl der zu entfernenden Elemente an.
aus der angegebenen Dimension auswählen.
interior_padding
gibt den Abstand zwischen zwei beliebigen Elementen an.
-Elemente in jeder Dimension, die nicht negativ sein darf. Innenabstände treten auf
vor dem Randabstand, sodass durch den negativen Rand
Elemente entfernt werden,
den mit Innenrand versehenen Operanden.
Formal ist result[result_index]
so definiert:
operand[operand_index]
wennresult_index = edge_padding_low + operand_index * (interior_padding + 1)
.- Andernfalls
padding_value
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor pro Tensor | (C1), (C2), (C4) |
(I2) | padding_value |
0-dimensionaler Tensor oder quantisierter Tensor pro Tensor | (C1) |
(I3) | edge_padding_low |
Eindimensionale Tensorkonstante vom Typ si64 |
(C1), (C4) |
(I4) | edge_padding_high |
Eindimensionale Tensorkonstante vom Typ si64 |
(C1), (C4) |
(I5) | interior_padding |
Eindimensionale Tensorkonstante vom Typ si64 |
(C2–C4) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C3 bis C6) |
Einschränkungen
- (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
.
Beispiele
// %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
Semantik
Erzeugt partition_id
des aktuellen Prozesses.
Ausgaben
Name | Typ |
---|---|
result |
0-dimensionaler Tensor vom Typ ui32 |
Beispiele
%result = "stablehlo.partition_id"() : () -> tensor<ui32>
Popcnt
Semantik
Führt die elementweise Zählung der Anzahl von Bits durch, die im operand
-Tensor festgelegt ist.
und erzeugt einen result
-Tensor.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Ganzzahltyps | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Ganzzahltyps | (C1) |
Einschränkungen
- (C1)
type(operand) = type(result)
.
Beispiele
// %operand: [0, 1, 2, 127]
%result = "stablehlo.popcnt"(%operand) : (tensor<4xi64>) -> tensor<4xi64>
// %result: [0, 1, 1, 7]
Leistung
Semantik
Führt eine elementweise Exponentierung des lhs
-Tensors mit dem rhs
-Tensor durch und
erzeugt einen result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für Ganzzahlen: Ganzzahlexponentie
- Für Gleitkommazahlen:
pow
aus IEEE-754. - Für komplexe Zahlen: komplexe Exponentie
- Für quantisierte Typen:
dequantize_op_quantize(power, lhs, rhs, type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor von Ganzzahl, Gleitkomma oder komplexer Typ oder quantisierter Tensor pro Tensor | (C1) |
(I2) | rhs |
Tensor von Ganzzahl, Gleitkomma oder komplexer Typ oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor von Ganzzahl, Gleitkomma oder komplexer Typ oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %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]
real
Semantik
Extrahiert den realen Teil elementweise aus dem operand
und erzeugt ein result
Tensor. Formeller gesprochener Text für jedes Element x
:
real(x) = is_complex(x) ? real_part(x) : x
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkomma- oder komplexen Typs | (C1), (C2) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkommatyps | (C1), (C2) |
Einschränkungen
- (C1)
shape(result) = shape(operand)
. - (C2)
element_type(result)
ist definiert als: <ph type="x-smartling-placeholder">- </ph>
complex_element_type(element_type(operand))
wennis_complex(operand)
.- Andernfalls
element_type(operand)
.
Beispiele
// %operand: [(1.0, 2.0), (3.0, 4.0)]
%result = "stablehlo.real"(%operand) : (tensor<2xcomplex<f32>>) -> tensor<2xf32>
// %result: [1.0, 3.0]
Recv
Semantik
Empfängt Daten von einem Kanal mit channel_id
und erstellt results
.
Wenn is_host_transfer
den Wert true
hat, überträgt der Vorgang Daten aus der
Host. Andernfalls werden Daten von einem anderen Gerät übertragen. Das bedeutet,
Implementierung definiert. Dieses Flag dupliziert die in
channel_type
, daher planen wir, in Zukunft nur eine davon zu behalten
(#666)
results
bestehen aus Nutzlastwerten, die an erster Stelle stehen, und einem Token, das
zuletzt. Wir planen, Nutzlast und Token in Zukunft in zwei Teile aufzuteilen.
separate Ausgaben, um die Verständlichkeit zu verbessern
(#670)
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | token |
token |
(C4) |
(I2) | channel_id |
Konstante vom Typ si64 |
|
(I3) | channel_type |
Aufzählung von DEVICE_TO_DEVICE und HOST_TO_DEVICE |
(C1) |
(I4) | is_host_transfer |
Konstante vom Typ i1 |
(C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
results |
variadische Anzahl von Tensoren, quantisierte Tensoren oder Tokens | (C2–C4) |
Einschränkungen
- (C1)
channel_type
ist definiert als: <ph type="x-smartling-placeholder">- </ph>
HOST_TO_DEVICE
, wennis_host_transfer = true
,- Andernfalls
DEVICE_TO_DEVICE
.
- (C2)
0 < size(results)
. - (C3)
is_empty(result[:-1])
oderis_tensor(type(results[:-1]))
. - (C4)
is_token(type(results[-1]))
.
Beispiele
%results0, %results1 = "stablehlo.recv"(%token) {
channel_handle = #stablehlo.channel_handle<handle = 1, type = 3>,
is_host_transfer = true
} : (!stablehlo.token) -> (tensor<2x2xi64>, !stablehlo.token)
reduce
Semantik
Wendet eine Reduktionsfunktion body
auf inputs
und init_values
entlang der
dimensions
und erzeugt results
-Tensoren.
Die Reihenfolge der Reduzierungen ist implementierungsdefiniert, was bedeutet, dass body
und
init_values
muss eine Monoid bilden, um zu gewährleisten, dass die Operation
für alle Eingaben
in allen Implementierungen dieselben Ergebnisse. Diese Bedingung
nicht für viele beliebte Preissenkungen gilt. Beispiel: Gleitkommazahl für
body
und Null für init_values
bilden eigentlich keine Monoide,
Das Hinzufügen von Gleitkommazahlen ist nicht assoziativ.
Formeller results...[j0, ..., jR-1] = reduce(input_slices_converted)
, wobei Folgendes gilt:
input_slices = inputs...[j0, ..., :, ..., jR-1]
, wobei:
eingefügt wird umdimensions
.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)
für einen binären Baumschedule
, wobei gilt: <ph type="x-smartling-placeholder">- </ph>
exec(node) = body(exec(node.left), exec(node.right))
.exec(leaf) = leaf.value
.
schedule
ist eine implementierungsdefinierte vollständige binäre Baumstruktur, deren besteht aus: <ph type="x-smartling-placeholder">- </ph>
input_slices_converted...[index]
Werte für alleindex
inindex_space(input_slices_converted)
in aufsteigender lexikografischer Reihenfolge vonindex
.- Dazwischen ist eine von der Implementierung definierte Menge von
init_values_converted
an den von der Implementierung definierten Positionen.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | inputs |
variadische Anzahl von Tensoren oder quantisierte Tensoren pro Tensor | (C1–C4), (C6), (C7) |
(I2) | init_values |
variadische Anzahl von 0-dimensionalen Tensoren oder quantisierten Tensoren pro Tensor | (C2), (C3) |
(I3) | dimensions |
Eindimensionale Tensorkonstante vom Typ si64 |
(C4), (C5), (C7) |
(I4) | body |
Funktion | (C6) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
results |
variadische Anzahl von Tensoren oder quantisierte Tensoren pro Tensor | (C3), (C7), (C8) |
Einschränkungen
- (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
hat den Typ(tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ...,
tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>)
, wobeiis_promotable(element_type(inputs[i]), Ei)
. - (C7)
shape(results...) = shape(inputs...)
mit dem Unterschied, dass die Dimension Größen voninputs...
, diedimensions
entsprechen, sind nicht enthalten. - (C8)
element_type(results[i]) = Ei
für allei
in[0,N)
.
Beispiele
// %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
Semantik
Führt eine elementweise Konvertierung von operand
in einen anderen Gleitkommatyp durch
mit exponent_bits
und mantissa_bits
und umgekehrt
Gleitkommatyp und
erzeugt einen output
-Tensor.
Formeller:
- Die Mantissenbits des ursprünglichen Werts werden aktualisiert, um das Original zu runden.
Wert auf den nächsten mit
mantissa_bits
darstellbaren Wert unter Verwendung vonroundToIntegralTiesToEven
-Semantik. - Wenn
mantissa_bits
kleiner ist als die Anzahl der Mantissenbits von Originalwert, werden die Mantissenbits aufmantissa_bits
gekürzt. - Wenn dann die Exponentenbits des Zwischenergebnisses nicht in die
Bereich bereitgestellt von
exponent_bits
, läuft das Zwischenergebnis zu unendlich mit dem ursprünglichen Vorzeichen oder Unterflüsse auf Null mithilfe der Originalzeichen - Führt für quantisierte Typen
dequantize_op_quantize( lambda operand: reduce_precision(operand, exponent_bits, mantissa_bits), operand, type(result))
aus.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C1) |
(I2) | exponent_bits |
Konstante vom Typ si32 |
(C2) |
(I3) | mantissa_bits |
Konstante vom Typ si32 |
(C3) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
output |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(output)
. - (C2)
1 <= exponent_bits
. - (C3)
0 <= mantissa_bits
.
Beispiele
// 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
Semantik
Innerhalb jeder Prozessgruppe im StableHLO-Prozessraster wird eine Reduzierung durchgeführt,
mit computations
über die Werte des Tensors operand
aus jedem Prozess
teilt das Reduktionsergebnis entlang von scatter_dimension
in Teile auf
die aufgeteilten Teile zwischen den Prozessen, um die result
zu erzeugen.
Der Vorgang teilt das StableHLO-Prozessraster in process_groups
auf,
wie folgt definiert:
cross_replica(replica_groups)
wennchannel_id <= 0 and use_global_device_ids = false
.cross_replica_and_partition(replica_groups)
wennchannel_id > 0 and use_global_device_ids = false
.flattened_ids(replica_groups)
wennchannel_id > 0 and use_global_device_ids = true
.
Danach gilt in jeder process_group
Folgendes:
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]
für allesender
inprocess_group
, wobeireceiver_index = process_group.index(receiver)
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor pro Tensor | (C1), (C2), (C7), (C8) |
(I2) | scatter_dimension |
Konstante vom Typ si64 |
(C1), (C2), (C8) |
(I3) | replica_groups |
2-dimensionale Tensorkonstante vom Typ si64 |
(C3–C5) |
(I4) | channel_id |
Konstante vom Typ si64 |
(C6) |
(I5) | use_global_device_ids |
Konstante vom Typ i1 |
(C6) |
(I6) | computation |
Funktion | (C7) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C8–C9) |
Einschränkungen
- (C1)
dim(operand, scatter_dimension) % dim(process_groups, 1) = 0
. - (C2)
0 <= scatter_dimension < rank(operand)
. - (C3)
is_unique(replica_groups)
. - (C4)
size(replica_groups)
ist definiert als: <ph type="x-smartling-placeholder">- </ph>
num_replicas
, wenncross_replica
verwendet wird.num_replicas
, wenncross_replica_and_partition
verwendet wird.num_processes
, wennflattened_ids
verwendet wird.
- (C5)
0 <= replica_groups < size(replica_groups)
. - (C6) Wenn
use_global_device_ids = true
, dannchannel_id > 0
. - (C7)
computation
hat den Typ(tensor<E>, tensor<E>) -> (tensor<E>)
, wobeiis_promotable(element_type(operand), E)
- (C8)
shape(result) = shape(operand)
mit Ausnahme von: <ph type="x-smartling-placeholder">- </ph>
dim(result, scatter_dimension) = dim(operand, scatter_dimension) / dim(process_groups, 1)
.
- (C9)
element_type(result) = E
.
Beispiele
// 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
Semantik
Wendet eine Reduktionsfunktion body
auf die Fenster von inputs
und init_values
an
und produziert results
.
Das folgende Diagramm zeigt, wie Elemente in results...
aus
inputs...
anhand eines konkreten Beispiels.
Formeller gesprochen
results...[result_index] = reduce(windows, init_values, axes(inputs...), body)
(siehe Reduzieren), wobei Folgendes gilt:
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)
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | inputs |
variadische Anzahl von Tensoren oder quantisierte Tensoren pro Tensor | (C1-C4), (C6), (C8), (C10), (C12), (C13), (C15) |
(I2) | init_values |
variadische Anzahl von 0-dimensionalen Tensoren oder quantisierten Tensoren pro Tensor | (C1), (C13) |
(I3) | window_dimensions |
Eindimensionale Tensorkonstante vom Typ si64 |
(C4), (C5), (C15) |
(I4) | window_strides |
Eindimensionale Tensorkonstante vom Typ si64 |
(C6), (C7), (C15) |
(I5) | base_dilations |
Eindimensionale Tensorkonstante vom Typ si64 |
(C8), (C9), (C15) |
(I6) | window_dilations |
Eindimensionale Tensorkonstante vom Typ si64 |
(C10), (C11), (C15) |
(I7) | padding |
2-dimensionale Tensorkonstante vom Typ si64 |
(C12), (C15) |
(I8) | body |
Funktion | (C13) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
results |
variadische Anzahl von Tensoren oder quantisierte Tensoren pro Tensor | (C1), (C14-C16) |
Einschränkungen
- (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
hat den Typ(tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ...,
tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>)
, wobeiis_promotable(element_type(inputs[i]), Ei)
. - (C14)
same(shape(results...))
. - (C15)
shape(results[0]) = num_windows
, wobei gilt: <ph type="x-smartling-placeholder">- </ph>
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
für allei
in[0,N)
.
Beispiele
// %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]]
Rest
Semantik
Führt den elementweisen Rest der Tensoren des Dividenden lhs
und des Divisors rhs
durch und
erzeugt einen result
-Tensor.
Formal wird das Vorzeichen des Ergebnisses von der Dividende entnommen, und das
Der absolute Wert des Ergebnisses ist immer kleiner als der absolute Wert des Divisors.
Der Rest wird als lhs - d * rhs
berechnet, wobei d
durch Folgendes gegeben ist:
- Für Ganzzahlen:
stablehlo.divide(lhs, rhs)
. - Für Gleitkommazahlen:
division(lhs, rhs)
aus IEEE-754 mit RundungsattributroundTowardZero
. - Für komplexe Zahlen: TBD (#997)
- Für quantisierte Typen:
<ph type="x-smartling-placeholder">
- </ph>
dequantize_op_quantize(remainder, lhs, rhs, type(result))
.
Bei Gleitkommaelementtypen steht diese Operation im Gegensatz zur
remainder
-Operation aus der IEEE-754-Spezifikation, wobei d
ein ganzzahliger Wert ist
dem Wert von lhs/rhs
am nächsten ist,
bei Gleichheit oder Gleichheit.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor von Ganzzahl, Gleitkomma oder komplexer Typ oder quantisierter Tensor pro Tensor | (C1) |
(I2) | rhs |
Tensor von Ganzzahl, Gleitkomma oder komplexer Typ oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor von Ganzzahl, Gleitkomma oder komplexer Typ oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %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
Semantik
Erzeugt replica_id
des aktuellen Prozesses.
Ausgaben
Name | Typ |
---|---|
result |
0-dimensionaler Tensor vom Typ ui32 |
Beispiele
%result = "stablehlo.replica_id"() : () -> tensor<ui32>
umformen
Semantik
Führt die Umformung des operand
-Tensors in einen result
-Tensor durch. Konzeptionell
dieselbe kanonische Darstellung beibehalten,
jedoch sich möglicherweise ändert,
die Form, z.B. von tensor<2x3xf32>
auf tensor<3x2xf32>
oder tensor<6xf32>
.
Formeller gesprochen: result[result_index] = operand[operand_index]
, wobei
result_index
und operand_index
haben im lexikografischen Feld dieselbe Position.
von index_space(result)
und index_space(operand)
sortiert.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor | (C1–C3) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor | (C1–C3) |
Einschränkungen
- (C1)
element_type(result)
ist gegeben durch: <ph type="x-smartling-placeholder">- </ph>
element_type(operand)
, wenn!is_per_axis_quantized(operand)
.element_type(operand)
mit dem Unterschied, dassquantization_dimension(operand)
und Andernfalls kannquantization_dimension(result)
abweichen.
- (C2)
size(operand) = size(result)
. - (C3) Wenn
is_per_axis_quantized(operand)
: <ph type="x-smartling-placeholder">- </ph>
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)
.
Beispiele
// %operand: [[1, 2, 3], [4, 5, 6]]
%result = "stablehlo.reshape"(%operand) : (tensor<2x3xi32>) -> tensor<3x2xi32>
// %result: [[1, 2], [3, 4], [5, 6]]
reverse
Semantik
Kehrt die Reihenfolge der Elemente im operand
entlang der angegebenen dimensions
um
und erzeugt einen result
-Tensor. Formeller gesprochen
result[result_index] = operand[operand_index]
, wobei gilt:
operand_index[d] = dim(result, d) - result_index[d] - 1
wennd
indimensions
.- Andernfalls
operand_index[d] = result_index[d]
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor pro Tensor | (C1), (C3) |
(I2) | dimensions |
Eindimensionale Tensorkonstante vom Typ si64 |
(C2), (C3) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C1), (C3) |
Einschränkungen
- (C1)
type(operand) = type(result)
. - (C2)
is_unique(dimensions)
. - (C3)
0 <= dimensions < rank(result)
.
Beispiele
// %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]]
Rng
<ph type="x-smartling-placeholder"></ph>
Semantik
Erzeugt Zufallszahlen mit dem rng_distribution
-Algorithmus und erzeugt einen
Tensor result
einer gegebenen Form shape
.
Wenn rng_distribution = UNIFORM
, werden die Zufallszahlen generiert.
nach der gleichmäßigen Verteilung über das Intervall [a, b)
. Wenn a >= b
,
ist das Verhalten nicht definiert.
Wenn rng_distribution = NORMAL
, werden die Zufallszahlen generiert.
folgt der Normalverteilung mit Mittelwert = a
und Standardabweichung = b
.
Wenn b < 0
, ist das Verhalten nicht definiert.
Die genaue Art und Weise, wie Zufallszahlen generiert werden, ist implementierungsdefiniert. Für Sie können, müssen aber deterministisch sein, und sie verwenden ausgeblendet.
In Gesprächen mit vielen Stakeholdern hat sich diese Maßnahme ebenso effektiv wie entfernt werden. Wir planen also, es in Zukunft zu entfernen. (#597)
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | a |
0-dimensionaler Tensor vom Typ „Ganzzahl“, „Boolesch“ oder „Gleitkommazahl“ | (C1), (C2) |
(I2) | b |
0-dimensionaler Tensor vom Typ „Ganzzahl“, „Boolesch“ oder „Gleitkommazahl“ | (C1), (C2) |
(I3) | shape |
Eindimensionale Tensorkonstante vom Typ si64 |
(C3) |
(I4) | rng_distribution |
Aufzählung von UNIFORM und NORMAL |
(C2) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Ganzzahl-, Boolesch- oder Gleitkommatyps | (C1–C3) |
Einschränkungen
- (C1)
element_type(a) = element_type(b) = element_type(result)
. - (C2) Wenn
rng_distribution = NORMAL
, dannis_float(a)
. - (C3)
shape(result) = shape
.
Beispiele
// %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
Semantik
Gibt ein output
zurück, das mit einheitlichen zufälligen Bits und einem aktualisierten Ausgabestatus gefüllt ist
output_state
mit dem Algorithmus zum Pseudozufallszahlen-Generator rng_algorithm
mit dem Anfangszustand initial_state
. Die Ausgabe ist garantiert
deterministische Funktion von initial_state
, aber dies ist nicht garantiert.
deterministisch ist.
rng_algorithm
ist einer der folgenden Werte:
DEFAULT
: Implementierungsdefinierter Algorithmus.THREE_FRY
: Implementierungsdefinierte Variante des Threefry-Algorithmus.*PHILOX
: Implementierungsdefinierte Variante des Philox-Algorithmus.*
* Siehe Salmon et al. SC 2011. Parallele Zufallszahlen: so einfach wie 1, 2, 3.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | rng_algorithm |
Aufzählung von DEFAULT , THREE_FRY und PHILOX |
(C2) |
(I2) | initial_state |
Eindimensionaler Tensor vom Typ ui64 |
(C1), (C2) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
output_state |
Eindimensionaler Tensor vom Typ ui64 |
(C1) |
output |
Tensor des Ganzzahl- oder Gleitkommatyps |
Einschränkungen
- (C1)
type(initial_state) = type(output_state)
. - (C2)
size(initial_state)
ist definiert als: <ph type="x-smartling-placeholder">- </ph>
- Implementierung definiert, wenn
rng_algorithm = DEFAULT
. 2
wennrng_algorithm = THREE_FRY
.2
oder3
, wennrng_algorithm = PHILOX
.
- Implementierung definiert, wenn
Beispiele
// %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
Semantik
Führt eine elementweise Rundung auf die nächste Ganzzahl durch, wodurch Verbindungen getrennt werden
mit dem operand
-Tensor und erzeugt einen result
-Tensor. Implementiert
Den roundToIntegralTiesToAway
-Vorgang aus der IEEE-754-Spezifikation Für
quantisierten Typen quantifiziert,
dequantize_op_quantize(round_nearest_afz, operand, type(result))
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %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
Semantik
Führt eine elementweise Rundung auf die nächste Ganzzahl durch, wobei Gleichstände aufgehoben werden.
in Richtung der geraden Ganzzahl mit dem operand
-Tensor und erzeugt eine result
Tensor. Implementiert den roundToIntegralTiesToEven
-Vorgang aus IEEE-754
Spezifikation zu ändern. Führt für quantisierte Typen durch
dequantize_op_quantize(round_nearest_even, operand, type(result))
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkommatyps oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %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
Semantik
Führt eine elementweise reziproke Quadratwurzeloperation für den operand
-Tensor durch und
erzeugt einen result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für Gleitkommazahlen:
rSqrt
aus IEEE-754. - Für komplexe Zahlen: komplexe reziproke Quadratwurzel.
- Für quantisierte Typen:
dequantize_op_quantize(rsqrt, operand, type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %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]]
Streudiagramm
Semantik
Erzeugt results
-Tensoren, die inputs
-Tensoren entsprechen, mit der Ausnahme,
Mehrere von scatter_indices
angegebene Segmente werden mit den Werten aktualisiert.
updates
mit update_computation
.
Das folgende Diagramm zeigt, wie Elemente in updates...
den Elementen in
results...
anhand eines konkreten Beispiels. Im Diagramm finden Sie
updates...
-Indexe und eine ausführliche Erläuterung der results...
-Indizes
die dem jeweiligen Produkt
entsprechen.
Formeller für alle update_index
in 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
ist definiert als: <ph type="x-smartling-placeholder">- </ph>
scatter_indices[si0, ..., :, ..., siN]
, bei denensi
individuell sind Elemente inupdate_scatter_index
und:
werden an der Indexindex_vector_dim
, wennindex_vector_dim
<rank(scatter_indices)
- Andernfalls
[scatter_indices[update_scatter_index]]
.
- Für
d_input
inaxes(inputs[0])
, <ph type="x-smartling-placeholder">- </ph>
full_start_index[d_input] = start_index[d_start]
wennd_input = scatter_dims_to_operand_dims[d_start]
.- Andernfalls
full_start_index[d_input] = 0
.
- Für
d_input
inaxes(inputs[0])
, <ph type="x-smartling-placeholder">- </ph>
full_batching_index[d_input] = update_scatter_index[d_start - (d_start < index_vector_dim ? 0 : 1)]
wennd_input = input_batching_dims[i_batching]
undd_start = scatter_indices_batching_dims[i_batching]
- Andernfalls
full_batching_index[d_input] = 0
.
update_window_index = update_index[update_window_dims...]
.full_window_index = [wi0, ..., 0, ..., wiN]
, bei denenwi
individuell sind Elemente inupdate_window_index
und0
wird an Indexen voninserted_window_dims
undinput_batching_dims
.result_index = full_start_index + full_batching_index + full_window_index
.
Daher gilt: results = exec(schedule, inputs)
, wobei:
schedule
ist eine implementierungsdefinierte Permutation vonindex_space(updates[0])
.exec([update_index, ...], results) = exec([...], updated_results)
, wobei gilt: <ph type="x-smartling-placeholder">- </ph>
- Wenn
result_index
innerhalb der Grenzen fürshape(results...)
liegt 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
ist eine Kopie vonresults
mitresults...[result_index]
aufupdated_values...
festgelegt.- Andernfalls:
updated_results = results
.
- Wenn
exec([], results) = results
.
Wenn indices_are_sorted
den Wert true
hat, kann die Implementierung davon ausgehen,
scatter_indices
werden nach scatter_dims_to_operand_dims
sortiert,
Andernfalls ist das Verhalten nicht definiert. Formeller für alle i1 < i2
aus
indices(result)
, full_start_index(i1)
<= full_start_index(i2)
.
Wenn unique_indices
den Wert true
hat, kann die Implementierung davon ausgehen, dass alle
Die result_index
-Indizes, in die verteilt werden, sind eindeutig. Wenn unique_indices
gleich
true
aber die verteilten Indexe nicht eindeutig sind, ist das Verhalten
nicht definiert.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | inputs |
variadische Anzahl von Tensoren oder quantisierte Tensoren pro Tensor | (C1), (C2), (C4-C6), (C11), (C13), (C18), (C21), (C23-C24) |
(I2) | scatter_indices |
Tensor des Ganzzahltyps | (C4), (C15), (C19), (C22) |
(I3) | updates |
variadische Anzahl von Tensoren oder quantisierte Tensoren pro Tensor | (C3–C6), (C8) |
(I4) | update_window_dims |
Eindimensionale Tensorkonstante vom Typ si64 |
(C2), (C4), (C7-C8) |
(I5) | inserted_window_dims |
Eindimensionale Tensorkonstante vom Typ si64 |
(C2), (C4), (C9-C11) |
(I6) | input_batching_dims |
Eindimensionale Tensorkonstante vom Typ si64 |
(C2), (C4), (C9), (C12-13), (C17-18), (C20) |
(I7) | scatter_indices_batching_dims |
Eindimensionale Tensorkonstante vom Typ si64 |
(C14–C18) |
(I8) | scatter_dims_to_operand_dims |
Eindimensionale Tensorkonstante vom Typ si64 |
(C19–C21) |
(I9) | index_vector_dim |
Konstante vom Typ si64 |
(C4), (C16), (C19), (C22) |
(I10) | indices_are_sorted |
Konstante vom Typ i1 |
|
(I11) | unique_indices |
Konstante vom Typ i1 |
|
(I12) | update_computation |
Funktion | (C23) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
results |
variadische Anzahl von Tensoren oder quantisierte Tensoren pro Tensor | (C24–C25) |
Einschränkungen
- (C1)
same(shape(inputs...))
. - (C2) `rank(inputs[0]) = size(update_window_dims) + size(eingefügtes_Fenster_Dims)
<ph type="x-smartling-placeholder">
- </ph>
- size(input_batching_dims)`.
- (C3)
same(shape(updates...))
. - (C4)
shape(updates[0]) = combine(update_scatter_dim_sizes, update_window_dim_sizes)
, wobei gilt: <ph type="x-smartling-placeholder">- </ph>
update_scatter_dim_sizes = shape(scatter_indices)
, außer dass die Dimensionsgröße vonscatter_indices
fürindex_vector_dim
ist nicht enthalten.update_window_dim_sizes <= shape(inputs[0])
, außer dass die Dimensionsgrößen ininputs[0]
fürinserted_window_dims
undinput_batching_dims
sind nicht enthalten.combine
platziertupdate_scatter_dim_sizes
auf den Achsen, dieupdate_scatter_dims
undupdate_window_dim_sizes
an den entsprechenden Achsen anupdate_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
hat den Typ(tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ..., tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>)
, Dabei gilt:is_promotable(element_type(inputs[i]), Ei)
. - (C24)
shape(inputs...) = shape(results...)
. - (C25)
element_type(results[i]) = Ei
für allei
in[0,N)
.
Beispiele
// %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]]
// ]
// ]
auswählen
Semantik
Erzeugt einen result
-Tensor, bei dem jedes Element aus on_true
oder
on_false
-Tensor basierend auf dem Wert des entsprechenden Elements von pred
.
Formeller: result[result_index] = pred_element ? on_true[result_index] :
on_false[result_index]
, wobei pred_element = rank(pred) = 0 ? pred[] :
pred[result_index]
. Führt für quantisierte Typen durch
dequantize_select_quantize(pred, on_true, on_false, type(result))
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | pred |
Tensor vom Typ i1 |
(C1) |
(I2) | on_true |
Tensor oder quantisierter Tensor pro Tensor | (C1–C2) |
(I3) | on_false |
Tensor oder quantisierter Tensor pro Tensor | (C2) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C2) |
Einschränkungen
- (C1)
rank(pred) = 0 or shape(pred) = shape(on_true)
. - (C2)
baseline_type(on_true) = baseline_type(on_false) = baseline_type(result)
.
Beispiele
// %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
Semantik
Streut die Werte aus dem Tensor source
unter Verwendung von scatter
basierend auf dem
Ergebnis von reduce_window
des input
-Tensors mit select
und erzeugt
einen result
-Tensor.
Das folgende Diagramm zeigt, wie Elemente in result
aus
operand
und source
anhand eines konkreten Beispiels.
Formeller:
selected_values = reduce_window_without_init(...)
durch die folgenden Eingaben:inputs = [operand].
window_dimensions
,window_strides
undpadding
, die unverändert verwendet werden.base_dilations = windows_dilations = 1
.body
ist definiert als:
def body(arg0: tensor<E>, arg1: tensor<E>) -> tensor<E>: return select(arg0, arg1) ? arg0 : arg1;
wo
E = element_type(operand)
undreduce_window_without_init
funktionieren genau wiereduce_window
, mit dem Unterschied, dass dieschedule
des zugrunde liegendenreduce
(siehe Reduzieren) enthält keine Init-Werte. Aktuell Nicht angegeben, was passiert, wenn das entsprechende Fenster keine Werte enthält (#731)result[result_index] = reduce([source_values], [init_value], [0], scatter)
Dabei gilt:source_values = [source[source_index] for source_index in source_indices]
.selected_index(source_index) = operand_index
wennselected_values[source_index]
hat das Elementoperand
vonoperand_index
.source_indices = [source_index for source_index in indices(source) if selected_index(source_index) = result_index]
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor pro Tensor | (C1-C4), (C6), (C8-C11) |
(I2) | source |
Tensor oder quantisierter Tensor pro Tensor | (C1), (C2) |
(I3) | init_value |
0-dimensionaler Tensor oder quantisierter Tensor pro Tensor | (C3) |
(I4) | window_dimensions |
Eindimensionale Tensorkonstante vom Typ si64 |
(C2), (C4), (C5) |
(I5) | window_strides |
Eindimensionale Tensorkonstante vom Typ si64 |
(C2), (C6), (C7) |
(I6) | padding |
2-dimensionale Tensorkonstante vom Typ si64 |
(C2), (C8) |
(I7) | select |
Funktion | (C9) |
(I8) | scatter |
Funktion | (C10) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C11–C12) |
Einschränkungen
- (C1)
element_type(operand) = element_type(source)
. - (C2)
shape(source) = num_windows
, wobei Folgendes gilt: <ph type="x-smartling-placeholder">- </ph>
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
hat den Typ(tensor<E>, tensor<E>) -> tensor<i1>
, wobeiE = element_type(operand)
- (C10)
scatter
hat den Typ(tensor<E>, tensor<E>) -> tensor<E>
, wobeiis_promotable(element_type(operand), E)
- (C11)
shape(operand) = shape(result)
. - (C12)
element_type(result) = E
.
Beispiele
// %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]]
senden
Semantik
Sendet inputs
an einen Kanal channel_id
und generiert ein result
-Token.
Wenn is_host_transfer
den Wert true
hat, überträgt der Vorgang Daten an den
Host. Andernfalls werden Daten auf ein anderes Gerät übertragen. Das bedeutet,
Implementierung definiert. Dieses Flag dupliziert die in
channel_type
, daher planen wir, in Zukunft nur eine davon zu behalten
(#666)
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | inputs |
variadische Anzahl von Tensoren oder quantisierten Tensoren | |
(I2) | token |
token |
|
(I3) | channel_id |
Konstante vom Typ si64 |
|
(I4) | channel_type |
Aufzählung von DEVICE_TO_DEVICE und DEVICE_TO_HOST |
(C1) |
(I5) | is_host_transfer |
Konstante vom Typ i1 |
(C1) |
Ausgaben
Name | Typ |
---|---|
result |
token |
Einschränkungen
- (C1)
channel_type
ist definiert als: <ph type="x-smartling-placeholder">- </ph>
DEVICE_TO_HOST
, wennis_host_transfer = true
,- Andernfalls
DEVICE_TO_DEVICE
.
Beispiele
%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
Semantik
Führt eine elementweise Linksverschiebung am lhs
-Tensor um die rhs
-Zahl aus
und erzeugt einen result
-Tensor.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor des Ganzzahltyps | (C1) |
(I2) | rhs |
Tensor des Ganzzahltyps | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Ganzzahltyps | (C1) |
Einschränkungen
- (C1)
type(lhs) = type(rhs) = type(result)
.
Beispiele
// %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
Semantik
Führt eine elementweise arithmetische Operation zur Rechtsverschiebung für den lhs
-Tensor durch
rhs
der Anzahl von Bits und erzeugt einen result
-Tensor.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor des Ganzzahltyps | (C1) |
(I2) | rhs |
Tensor des Ganzzahltyps | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Ganzzahltyps | (C1) |
Einschränkungen
- (C1)
type(lhs) = type(rhs) = type(result)
.
Beispiele
// %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
Semantik
Führt eine elementweise logische Rechtsverschiebung für den Tensor lhs
um rhs
durch
Anzahl von Bits und erzeugt einen result
-Tensor.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor des Ganzzahltyps | (C1) |
(I2) | rhs |
Tensor des Ganzzahltyps | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Ganzzahltyps | (C1) |
Einschränkungen
- (C1)
type(lhs) = type(rhs) = type(result)
.
Beispiele
// %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]
Signieren
Semantik
Gibt das Vorzeichen von operand
elementweise zurück und erzeugt einen result
-Tensor.
Formal kann die Semantik für jedes Element x
mit
Python-Syntax:
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)))
Führt für quantisierte Typen durch
dequantize_op_quantize(sign, operand, type(result))
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor eines vorzeichenbehafteten Ganzzahl-, Gleitkomma- oder komplexer Typ oder eines quantisierten Tensors pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor eines vorzeichenbehafteten Ganzzahl-, Gleitkomma- oder komplexer Typ oder eines quantisierten Tensors pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// 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
Semantik
Führt eine elementweise Sinusoperation für den operand
-Tensor durch und erzeugt einen result
-Wert
Tensor. Gehen Sie je nach Elementtyp so vor:
- Für Gleitkommazahlen:
sin
aus IEEE-754. - Für komplexe Zahlen: komplexer Sinus.
- Für quantisierte Typen:
dequantize_op_quantize(sine, operand, type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %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]]
Slice
Semantik
Extrahiert ein Segment aus dem operand
unter Verwendung statisch berechneter Startindexe
und erzeugt einen result
-Tensor. start_indices
enthalten die Startindizes von
das Segment für jede Dimension, limit_indices
enthalten die Endindizes
(ausschließlich) für das Segment für jede Dimension und strides
enthalten die Schritte
für jede Dimension.
Formeller gesprochen: result[result_index] = operand[operand_index]
, wobei
operand_index = start_indices + result_index * strides
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor pro Tensor | (C1–C3), (C5) |
(I2) | start_indices |
Eindimensionale Tensorkonstante vom Typ si64 |
(C2), (C3), (C5) |
(I3) | limit_indices |
Eindimensionale Tensorkonstante vom Typ si64 |
(C2), (C3), (C5) |
(I4) | strides |
Eindimensionale Tensorkonstante vom Typ si64 |
(C2), (C4) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor pro Tensor | (C1), (C5) |
Einschränkungen
- (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)
.
Beispiele
// %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]
// ]
Sortieren
Semantik
Sortiert eindimensionale Segmente von inputs
entlang der Dimension dimension
zusammen,
Laut comparator
und produziert results
.
Im Gegensatz zu ähnlichen Eingaben in anderen Operationen lässt dimension
negative Werte zu,
mit der unten beschriebenen Semantik. Künftig ist dies möglicherweise nicht mehr zulässig.
aus Gründen der Einheitlichkeit
(#1377)
Wenn is_stable
"true" ist, ist die Sortierung stabil, d. h. die relative Reihenfolge von
Elemente, die vom Vergleichsoperator als gleich eingestuft werden, werden beibehalten. Für die Schutzhülle
mit einer einzigen Eingabe, werden die beiden Elemente e1
und e2
als
wenn und nur wenn
comparator(e1, e2) = comparator(e2, e1) = false
Siehe die Formulierung unten
wie dies auf mehrere Eingaben verallgemeinert wird.
Formeller für alle result_index
in index_space(results[0])
:
adjusted_dimension = dimension >= 0 ? dimension : rank(inputs[0]) + dimension
.result_slice = [ri0, ..., :, ..., riR-1]
, bei denenriN
individuell sind Elemente inresult_index
und:
wird beiadjusted_dimension
eingefügt.inputs_together = (inputs[0]..., ..., inputs[N-1]...)
.results_together[result_slice] = sort(inputs_together[result_slice], comparator_together)
.- Dabei sortiert
sort
ein eindimensionales Segment in nicht absteigender Reihenfolge und erwartetcomparator_together
gibttrue
zurück, wenn das Argument auf der linken Seite gleich als das rechte Sekundenargument. 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
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | inputs |
variadische Anzahl von Tensoren oder quantisierte Tensoren pro Tensor | (C1–C5) |
(I2) | dimension |
Konstante vom Typ si64 |
(C4) |
(I3) | is_stable |
Konstante vom Typ i1 |
|
(I4) | comparator |
Funktion | (C5) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
results |
variadische Anzahl von Tensoren oder quantisierte Tensoren pro Tensor | (C2), (C3) |
Einschränkungen
- (C1)
0 < size(inputs)
. - (C2)
type(inputs...) = type(results...)
. - (C3)
same(shape(inputs...) + shape(results...))
. - (C4)
-R <= dimension < R
, wobeiR = rank(inputs[0])
. - (C5)
comparator
hat Typ(tensor<E1>, tensor<E1>, ..., tensor<EN-1>, tensor<EN-1>) -> tensor<i1>
, Dabei gilt:Ei = element_type(inputs[i])
.
Beispiele
// %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]]
Quadrat
Semantik
Führt eine elementweise Quadratwurzeloperation für den operand
-Tensor durch und erzeugt eine
result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für Gleitkommazahlen:
squareRoot
aus IEEE-754. - Für komplexe Zahlen: komplexe Quadratwurzel.
- Für quantisierte Typen:
dequantize_op_quantize(sqrt, operand, type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %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]]
subtract
Semantik
Führt eine elementweise Subtraktion der zwei Tensoren lhs
und rhs
durch und erzeugt einen
result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für Ganzzahlen: Subtraktion von Ganzzahlen.
- Für Gleitkommazahlen:
subtraction
aus IEEE-754. - Für komplexe Zahlen: komplexe Subtraktion.
- Für quantisierte Typen:
<ph type="x-smartling-placeholder">
- </ph>
dequantize_op_quantize(subtract, lhs, rhs, type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor von Ganzzahl, Gleitkomma oder komplexer Typ oder quantisierter Tensor pro Tensor | (C1) |
(I2) | rhs |
Tensor von Ganzzahl, Gleitkomma oder komplexer Typ oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor von Ganzzahl, Gleitkomma oder komplexer Typ oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
.
Beispiele
// %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
Semantik
Führt eine elementweise Tangentenoperation am operand
-Tensor durch und erzeugt eine
result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für Gleitkommazahlen:
tan
aus IEEE-754. - Für komplexe Zahlen: komplexer Tangens.
- Für quantisierte Typen:
dequantize_op_quantize(tan, operand, type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %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
Semantik
Führt eine elementweise hyperbolische Tangente für den operand
-Tensor und
erzeugt einen result
-Tensor. Gehen Sie je nach Elementtyp so vor:
- Für Gleitkommazahlen:
tanh
aus IEEE-754. - Für komplexe Zahlen: komplexer hyperbolischer Tangens.
- Für quantisierte Typen:
<ph type="x-smartling-placeholder">
- </ph>
dequantize_op_quantize(tanh, operand, type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_type(operand) = baseline_type(result)
.
Beispiele
// %operand: [-1.0, 0.0, 1.0]
%result = "stablehlo.tanh"(%operand) : (tensor<3xf32>) -> tensor<3xf32>
// %result: [-0.76159416, 0.0, 0.76159416]
Transponieren
Semantik
Permutet die Abmessungen des Tensors operand
mit permutation
an und erzeugt einen
result
-Tensor. Formeller result[result_index] = operand[operand_index]
Dabei gilt: result_index[d] = operand_index[permutation[d]]
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor oder quantisierter Tensor | (C1–C4) |
(I2) | permutation |
Eindimensionale Tensorkonstante vom Typ si64 |
(C2–C4) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor oder quantisierter Tensor | (C1), (C3–C4) |
Einschränkungen
- (C1)
element_type(result)
ist gegeben durch: <ph type="x-smartling-placeholder">- </ph>
element_type(operand)
, wenn!is_per_axis_quantized(operand)
.element_type(operand)
mit dem Unterschied, dassquantization_dimension(operand)
und Andernfalls kannquantization_dimension(result)
abweichen.
- (C2)
permutation
ist eine Permutation vonrange(rank(operand))
. - (C3)
shape(result) = dim(operand, permutation...)
. - (C4) Wenn
is_per_axis_quantized(result)
, dannquantization_dimension(operand) = permutation(quantization_dimension(result))
.
Beispiele
// %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
Semantik
Löst Batches von linearen Gleichungssystemen mit unterem oder oberem Dreieck Koeffizientenmatrizen.
Formal ist result[i0, ..., iR-3, :, :]
die Lösung, basierend auf a
und b
.
auf op(a[i0, ..., iR-3, :, :]) * x = b[i0, ..., iR-3, :, :]
, wenn left_side
gleich
true
oder x * op(a[i0, ..., iR-3, :, :]) = b[i0, ..., iR-3, :, :]
, wenn
left_side
ist false
, wobei die Variable x
aufgelöst wird, wobei op(a)
bestimmt wird.
von transpose_a
. Dabei kann es sich um einen der folgenden Werte handeln:
NO_TRANSPOSE
: Vorgang mita
unverändert ausführen.TRANSPOSE
: Operation wird beim Transponieren vona
durchgeführt.ADJOINT
: Operation für die konjugierte Transponierung vona
ausführen.
Eingabedaten werden nur aus dem unteren Dreieck von a
gelesen, wenn lower
gleich true
oder
oberes Dreieck von a
andernfalls. Ausgabedaten werden im selben Dreieck zurückgegeben.
werden die Werte im anderen Dreieck
implementierungsdefiniert.
Wenn unit_diagonal
„true“ ist, kann die Implementierung davon ausgehen, dass die diagonale
Elemente von a
gleich 1 sind, ansonsten ist das Verhalten nicht definiert.
Führt für quantisierte Typen durch
dequantize_op_quantize(lambda x, y: triangular_solve(x, y, left_side, lower,
unit_diagonal, transpose_a), a, b, type(result))
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | a |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1–C3) |
(I2) | b |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1–C4) |
(I3) | left_side |
Konstante vom Typ i1 |
(C3) |
(I4) | lower |
Konstante vom Typ i1 |
|
(I5) | unit_diagonal |
Konstante vom Typ i1 |
|
(I6) | transpose_a |
Aufzählung von NO_TRANSPOSE , TRANSPOSE und ADJOINT |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkomma- oder komplexen Typs oder quantisierter Tensor pro Tensor | (C1) |
Einschränkungen
- (C1)
baseline_element_type(a) = baseline_element_type(b)
. - (C2)
2 <= rank(a) = rank(b) = R
. - (C3) Die Beziehung zwischen
shape(a)
undshape(b)
ist so definiert: <ph type="x-smartling-placeholder">- </ph>
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)
.
Beispiele
// %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]
// ]
Tupel
<ph type="x-smartling-placeholder"></ph>
Semantik
Erzeugt ein result
-Tupel aus den Werten val
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | val |
variadische Anzahl von Werten | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tupel | (C1) |
Einschränkungen
- (C1)
result
hat den Typtuple<E0, ..., EN-1>
, wobeiEi = type(val[i])
.
Beispiele
// %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
Semantik
Führt eine elementweise Umwandlung des quantisierten Tensors operand
in einen
Gleitkomma-Tensor result
gemäß den definierten Quantisierungsparametern
nach dem Typ operand
.
Formeller: result = dequantize(operand)
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
quantisierter Tensor | (C1), (C2) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des Gleitkommatyps | (C1), (C2) |
Einschränkungen
- (C1)
shape(operand) = shape(result)
. - (C2)
element_type(result) = expressed_type(operand)
.
Beispiele
// %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
Semantik
Führt eine elementweise Konvertierung von Gleitkomma- oder quantisierten Tensors durch
operand
zu einem quantisierten Tensor result
gemäß der Quantisierung
Parameter, die durch den Typ result
definiert sind.
Formeller gesprochen
- Wenn
is_float(operand)
: <ph type="x-smartling-placeholder">- </ph>
result = quantize(operand, type(result))
.
- Wenn
is_quantized(operand)
: <ph type="x-smartling-placeholder">- </ph>
float_result = dequantize(operand)
.result = quantize(float_result, type(result))
.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
Tensor eines Gleitkomma- oder quantisierten Typs | (C1), (C2) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
quantisierter Tensor | (C1), (C2) |
Einschränkungen
- (C1)
shape(operand) = shape(result)
. - (C2)
expressed_type(result) = is_float(operand) ? element_type(operand) : expressed_type(operand)
.
Beispiele
// %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]
während
Semantik
Erzeugt die Ausgabe, wenn die Funktion body
ein- oder öfter ausgeführt wird, während
Die Funktion cond
gibt true
aus. Formal lässt sich die Semantik zum Ausdruck bringen,
mit der folgenden Python-Syntax:
internal_state = operand
while cond(*internal_state):
internal_state = body(*internal_state)
results = internal_state
Das Verhalten einer Endlosschleife steht noch nicht fest. (#383)
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | operand |
variadische Anzahl von Tensoren, quantisierte Tensoren oder Tokens | (C1–C3) |
(I2) | cond |
Funktion | (C1) |
(I3) | body |
Funktion | (C2) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
results |
variadische Anzahl von Tensoren, quantisierte Tensoren oder Tokens | (C3) |
Einschränkungen
- (C1)
cond
hat den Typ(T0, ..., TN-1) -> tensor<i1>
, wobeiTi = type(operand[i])
- (C2)
body
hat den Typ(T0, ..., TN-1) -> (T0, ..., TN-1)
, wobeiTi = type(operand[i])
- (C3)
type(results...) = type(operand...)
.
Beispiele
// %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
Semantik
Führt eine elementweise XOR-Verknüpfung der beiden Tensoren lhs
und rhs
durch und erzeugt eine result
Tensor. Gehen Sie je nach Elementtyp so vor:
- Für boolesche Werte: logisches XOR.
- Für Ganzzahlen: bitweises XOR.
Eingaben
Label | Name | Typ | Einschränkungen |
---|---|---|---|
(I1) | lhs |
Tensor des booleschen oder Ganzzahltyps | (C1) |
(I2) | rhs |
Tensor des booleschen oder Ganzzahltyps | (C1) |
Ausgaben
Name | Typ | Einschränkungen |
---|---|---|
result |
Tensor des booleschen oder Ganzzahltyps | (C1) |
Einschränkungen
- (C1)
type(lhs) = type(rhs) = type(result)
.
Beispiele
// 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]]
Dialekt-Interop
Derzeit enthalten StableHLO-Programme manchmal Vorgänge, die nicht durch StableHLO definiert.
Modul, Funktion, Aufruf und Rückgabe
StableHLO nutzt vorgelagerte MLIR-Vorgänge für ModuleOp, FuncOp, CallOp und ReturnOp. Dies geschah zur besseren Interoperabilität mit vorhandenen MLIR-Maschinen. nützliche Karten/Tickets enthalten, die auf FuncOp und ModuleOp ausgerichtet sind, erwarten die Pipelines, dass diese Vorgänge vorhanden sind. Vollständige Kompatibilitätsgarantien auf diese Operationen angewendet. Wenn sich an diesen Operationen in einem nicht kompatibel (d.h. entfernt), werden StableHLO-Äquivalente hinzugefügt, um Kompatibilität.
CHLO
Das CHLO-Opset enthält Vorgänge auf höherer Ebene, die in StableHLO zerlegt werden. Derzeit gibt es keine Kompatibilitätsgarantien für CHLO. Aus Gründen der Kompatibilität chlo-legalize-to-stablehlo-Pass vor der Serialisierung verwendet werden.
Formvorgänge
In der Community werden bestimmte Vorgänge häufig verwendet,
MLIR-Dialekte in dynamischen StableHLO-Programmen zur Durchführung von Formberechnungen
Dazu gehören am häufigsten der shape
-Dialekt
Operationen wie shape_of
oder num_elements
, Dialekt tensor
Operationen wie dim
oder from_elements
und den integrierten Typ index
an.
Der Dynamism RFC > O2
werden als ausgeschlossen gekennzeichnet. Einige index
-Typen werden jedoch unterstützt.
zu Interoperabilitätszwecken enthalten. Es gibt keine Kompatibilitätsgarantien für diese
Operationen oder Typen. Der Parameter shape-legalize-to-stablehlo
pass verwendet, um diese Vorgänge in vollständig unterstützte StableHLO-Operationen zu konvertieren.
Verworfene Vorgänge
Es gibt mehrere StableHLO-Vorgänge, die von MHLO die eingestellt wurden und aus der StableHLO entfernt werden. Alle Details zu diesen Entfernungen finden Sie im StableHLO v1.0 Cleanup #2283. Das Tracker-Problem bei diesen Einstellungen ist #2340.
Diese Vorgänge lassen sich in verschiedene Kategorien einteilen:
- „Nicht in HLO“ StableHLO-Operationen gehört haben. Sie waren anfangs Teil
StableHLO-Opset, aber später wurde davon ausgegangen, dass es nicht gut geeignet ist:
broadcast
,create_token
,cross-replica-sum
,dot
,einsum
torch_index_select
,unary_einsum
(3.) - Nicht verwendete Operationen: Diese Vorgänge waren vielleicht irgendwann nützlich, aber der Vorgang
entweder nicht ausgereift oder die Pipelines, die diese Vorgänge nutzen,
so refaktoriert, dass sie nicht mehr benötigt werden. Dazu gehören
map
,tuple
(#598),get_tuple_element
,rng
,complex
Vergleiche Nr. 560, und Faltungwindow_reversal
(#1181).
Einige dieser Operationen lassen sich leicht entfernen, da sie sich mit
vorhandene Vorgänge (broadcast
, create_token
, cross-replica-sum
, dot
,
unary_einsum
) und wird nach Ablauf des bestehenden Kompatibilitätsfensters entfernt.
Pässe (6 Monate). Für andere Nutzer wird derzeit geprüft, ob sie entfernt werden sollen (einsum
,
get_tuple_element
, map
, rng
torch_index_select
, tuple
, complex
Vergleiche, window_reversal
). Ausstehendes Community-Feedback,
Diese Vorgänge werden entweder entfernt oder mit vollständiger Unterstützung der Spezifikation hinzugefügt. Bis
diese Ops-Futures bekannt sind, sind nur 6 Monate Kompatibilität garantiert.
Ausführung
Sequenzielle Ausführung
Ein StableHLO-Programm wird ausgeführt, indem Eingabewerte für die Funktion main
bereitgestellt werden
und Ausgabewerte zu berechnen. Ausgabewerte einer Funktion werden berechnet durch
Ausführen des Graphen von Vorgängen, der im entsprechenden return
-Vorgang basiert
Die Ausführungsreihenfolge ist implementierungsdefiniert, solange sie mit
Dataflow, d. h., wenn Ops vor ihrer Verwendung ausgeführt werden In StableHLO werden alle
Operationen mit Nebeneffekten verbrauchen ein Token und erzeugen ein Token (mehrere Tokens können
über after_all
in ein Token gebündelt werden, sodass die Ausführungsreihenfolge
ist auch auf Dataflow ausgerichtet. Im folgenden Programm
Es gibt zwei mögliche Ausführungsaufträge: %0
→ %1
→ %2
→ return
und
%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>
}
Formal ist ein StableHLO-Prozess eine Kombination aus Folgendem:
1) ein StableHLO-Programm, 2) Vorgangsstatus (noch nicht ausgeführt,
bereits ausgeführt) und 3) Zwischenwerte, an denen der Prozess arbeitet.
Der Prozess beginnt mit Eingabewerten für die main
-Funktion und läuft
Diagramm der Vorgänge, bei denen Vorgangsstatus und Zwischenwerte aktualisiert werden,
mit Ausgabewerten endet. Weitere Formulierung steht noch aus
(#484)
Parallele Ausführung
StableHLO-Programme können parallel ausgeführt werden, in einem 2D-Prozessraster organisiert
von num_replicas
nach num_partitions
, die beide den Typ ui32
haben.
Im StableHLO-Prozessraster, num_replicas * num_partitions
von StableHLO
Prozesse gleichzeitig ausgeführt werden. Jeder Prozess hat eine eigene
process_id = (replica_id, partition_id)
, wobei
replica_id
in replica_ids = range(num_replicas)
und
partition_id
in partition_ids = range(num_partitions)
. Beide haben
Geben Sie ui32
ein.
Die Größe des Prozessrasters ist für jedes Programm (in der
möchten wir dies zu einem expliziten Teil der StableHLO-Programme machen.
#650) und die Position
innerhalb des Prozessrasters ist für jeden Prozess statisch bekannt. Jeder Prozess hat
Zugriff auf seine Position im Prozessraster über replica_id
und
partition_id
Vorgänge
Innerhalb des Prozessrasters können die Programme alle gleich sein (in der Spalte Programm, mehrere Daten“ Stil) können alle unterschiedlich sein (im Multiple-Programm, Mehrere Daten“ Stil) oder etwas dazwischen. Für die Zukunft planen wir, um weitere Redewendungen der Definition paralleler StableHLO-Programme einzuführen, einschließlich GSPMD (#619).
Innerhalb des Prozessrasters sind die Prozesse größtenteils unabhängig voneinander – Sie haben separate Vorgangsstatus und separate Eingabe-/Zwischen-/Ausgabewerte. und die meisten Vorgänge werden getrennt zwischen Prozessen ausgeführt, wobei der Ausnahme von einer kleinen Anzahl von kollektiven Operationen (siehe unten).
Da bei der Ausführung der meisten Operationen nur Werte
ist es in der Regel eindeutig, anhand der Namen auf diese Werte zu verweisen.
Die Semantik kollektiver Operationen ist jedoch unzureichend.
die dazu führt, dass sich die Notation name@process_id
auf den Wert name
bezieht.
eines bestimmten Prozesses. (Aus dieser Perspektive können unqualifizierte name
-Daten
als Abkürzung für name@(replica_id(), partition_id())
angesehen).
Die Ausführungsreihenfolge zwischen den Prozessen ist implementierungsdefiniert, mit Ausnahme der Synchronisierung durch Punkt-zu-Punkt-Kommunikation und kollektive Operationen wie unten beschrieben.
Punkt-zu-Punkt-Kommunikation
StableHLO-Prozesse können über
StableHLO-Kanäle: Ein Channel wird durch eine positive ID des Typs
si64
Über verschiedene Operationen ist es möglich, Werte an Channels zu senden und
von Kanälen erhalten.
Weitere formalisierung, z.B. woher diese Kanal-IDs stammen, wie erkennen Programme darauf und welche Art von Synchronisierung eingeführt wurde, steht noch aus, (#484)
Streaming-Kommunikation
Jeder StableHLO-Prozess hat Zugriff auf zwei Streaming-Schnittstellen:
- In-Feed, aus dem gelesen werden kann.
- Outfeed, in den geschrieben werden kann.
Im Gegensatz zu Channels, die für die Kommunikation zwischen Prozessen haben Prozesse an beiden Enden, In-Feeds und Out-Feeds haben ihre anderen und die Implementierung definieren.
Weitere formalisierung, z.B. wie Streamingkommunikation die Ausführung beeinflusst und welche Art von Synchronisierung damit eingeführt wird, steht noch nicht fest. (#484)
Kollektiv-Operationen
Es gibt sechs kollektive Operationen in StableHLO: all_gather
, all_reduce
,
all_to_all
, collective_broadcast
, collective_permute
und
reduce_scatter
All diese Operationen teilen die Prozesse im StableHLO-Prozess auf
in StableHLO-Prozessgruppen umwandeln und eine gemeinsame Berechnung
unabhängig von anderen Prozessgruppen.
Innerhalb jeder Prozessgruppe können gemeinsame Ops eine Synchronisierung einführen. Hindernisse zu beseitigen. Weitere formalisierung, z.B. Wir gehen noch näher darauf ein, wie genau die Prozesse diese Barriere erreichen, Und was passiert, wenn nicht, steht (#484)
Wenn die Prozessgruppe partitionsübergreifende Kommunikation umfasst, d.h., es gibt
Prozessgruppe mit unterschiedlichen Partitions-IDs erstellt,
der kollektiven Opa benötigt einen Kanal und die
kollektive Opa muss eine
positive channel_id
vom Typ si64
. Replikatübergreifende Kommunikation erfordert keine
Kanäle.
Die von den gemeinsamen Operationen ausgeführten Berechnungen sind spezifisch für die einzelnen Operationen und werden in den einzelnen Vorgangsabschnitten weiter oben beschrieben. Die Strategien der Das Prozessraster ist in Prozessgruppen aufgeteilt. Diese Operationen werden gemeinsam genutzt. und werden in diesem Abschnitt beschrieben. Formal unterstützt StableHLO die anhand von vier Strategien.
cross_replica
Nur replikatübergreifende Kommunikation findet innerhalb jeder Prozessgruppe statt. Dieses
verwendet replica_groups
, eine Liste mit Replikat-IDs, und berechnet
ein kartesisches Produkt von replica_groups
von partition_ids
. replica_groups
muss eindeutige Elemente enthalten und alle replica_ids
abdecken. Formeller sollte die Verwendung
Python-Syntax:
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
Beispiel: Für replica_groups = [[0, 1], [2, 3]]
und num_partitions = 2
:
cross_replica
wird Folgendes produzieren:
[[(0, 0), (1, 0)], [(0, 1), (1, 1)], [(2, 0), (3, 0)], [(2, 1), (3, 1)]]
.
cross_partition
Nur partitionsübergreifende Kommunikation erfolgt innerhalb jeder Prozessgruppe. Dieses
verwendet partition_groups
, eine Liste mit Partitions-IDs,
berechnet ein kartesisches Produkt von partition_groups
nach replica_ids
.
partition_groups
muss eindeutige Elemente enthalten und alle partition_ids
abdecken.
Formal mit der Python-Syntax:
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
Beispiel: Für partition_groups = [[0, 1]]
und num_replicas = 4
:
cross_partition
wird Folgendes produzieren:
[[(0, 0), (0, 1)], [(1, 0), (1, 1)], [(2, 0), (2, 1)], [(3, 0), (3, 1)]]
.
cross_replica_and_partition
Sowohl replikat- als auch partitionsübergreifende Kommunikation
Prozessgruppe. Diese Strategie benötigt replica_groups
– eine Liste mit
Replikat-IDs und berechnet kartesische Produkte jeder replica_group
durch
partition_ids
replica_groups
muss eindeutige Elemente aufweisen und alle
replica_ids
. Formal mit der Python-Syntax:
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
Beispiel: Für replica_groups = [[0, 1], [2, 3]]
und num_partitions = 2
:
cross_replica_and_partition
wird Folgendes produzieren:
[[(0, 0), (1, 0), (0, 1), (1, 1)], [(2, 0), (3, 0), (2, 1), (3, 1)]]
.
flattened_ids
Diese Strategie benötigt flattened_id_groups
, also eine Liste der vereinfachten Listen.
Prozess-IDs im Format replica_id * num_partitions + partition_id
– und
in Prozess-IDs. flattened_id_groups
muss eindeutige Elemente enthalten
und decken alle process_ids
ab. Formal mit der Python-Syntax:
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
Beispiel: Für flattened_id_groups = [[0, 1, 2, 3], [4, 5, 6, 7]]
:
num_replicas = 4
und num_partitions = 2
, flattened_ids
werden
[[(0, 0), (0, 1), (1, 0), (1, 1)], [(2, 0), (2, 1), (3, 0), (3, 1)]]
Genauigkeit
Derzeit gibt StableHLO keine Garantien für numerische Genauigkeit. Dies kann sich aber in Zukunft ändern. (#1156)
Ausführungssemantik eines quantisierten Vorgangs
Die Interpretation quantisierter StableHLO-Operationen kann je nach Hardwareanforderungen und -funktionen. Einige Hardwarekomponenten quantisierte Operationen mit einem „dequantize, perform floatingpoint“ und schließlich quantisieren“ . Andere führen möglicherweise die gesamte mit einer ganzzahligen Arithmetik berechnet. Folglich ist die Auslegung des Begriffs quantisierten StableHLO-Operationen ausschließlich durch die spezifische Implementierung. Die Interpretation der hybriden Quantisierung (#1575) muss auf die in der Spezifikation vorgeschrieben ist (über 1792).
Fehler
StableHLO-Programme werden durch eine Reihe von für einzelne Vorgänge, bei denen viele Fehlerklassen vor der Ausführung ausgeschlossen sind. Fehlerbedingungen sind jedoch weiterhin möglich, z.B. ganzzahlige Überläufe, oder außerhalb des Netzwerkzugriffs. Sofern nicht ausdrücklich anders angegeben, sind all diese Fehler Dies kann zu einem implementierungsdefinierten Verhalten führen. Dies kann sich jedoch Future (#1157)
Gleitkommaausnahmen
Gleitkommaausnahmen in StableHLO-Programmen
ein klar definiertes Verhalten haben. Vorgänge, die zu durch den
IEEE-754-Standard (ungültiger Vorgang, Division durch Null, Überlauf, Unterlauf oder
ungenaue Ausnahmen), erzeugen Standardergebnisse (wie im Standard definiert) und
ohne das entsprechende Status-Flag zu setzen. ähnlich wie
raiseNoFlag
-Ausnahmebehandlung vom Standard. Ausnahmen für nicht standardmäßige
Operationen (z.B. komplexe arithmetische und bestimmte transzendentale Funktionen)
Implementierung definiert.
Formen stimmen nicht überein
StableHLO unterstützt dynamisch geformte Tensoren. Die Formen müssen jedoch Andernfalls ist das Verhalten nicht definiert. StableHLO weist nicht explizit eine Operation bereitstellen, die geltend machen kann, dass ein Tensor zur Laufzeit eine bestimmte Form hat. Für die Generierung des korrekten Codes ist der Ersteller verantwortlich.
Das folgende Programm ist als Beispiel gültig. Während der Laufzeit
Die genauen Formen von %arg0
und %arg1
müssen identisch sein. Andernfalls
Verhalten des Programms ist nicht definiert:
func.func @foo(%arg0: tensor<?xi32>, %arg1: tensor<?xi32>) -> tensor<?xi32> {
%0 = stablehlo.add %arg0, %arg1 : tensor<?xi32>
return %0 : tensor<?xi32>
}
Notation
Zur Beschreibung der Syntax wird in diesem Dokument die modifizierte ISO-Variante von EBNF verwendet.
Syntax (ISO/IEC 14977:1996,
Wikipedia)
mit zwei Änderungen: 1) Regeln werden mit ::=
statt mit =
definiert,
2) Die Verkettung wird durch eine Gegenüberstellung statt mit ,
ausgedrückt.
Zur Beschreibung der Semantik (z.B. in den Abschnitten "Typen", "Konstanten" und "Ops") Wir verwenden Formeln, die auf der um Unterstützung erweiterten Python-Syntax basieren. für prägnante Array-Operationen, wie unten beschrieben. Das funktioniert gut für kleine Code-Snippets. In seltenen Fällen können auch größere verwenden wir einfache Python-Syntax, die immer explizit eingeführt wird.
Formeln
Sehen wir uns die Funktionsweise von Formeln anhand eines Beispiels aus dem dot_general
an.
Spezifikation zu ändern. Eine der Einschränkungen für diesen Vorgang sieht so aus:
dim(lhs, lhs_batching_dimensions...) = dim(rhs, rhs_batching_dimensions...)
Die in dieser Formel verwendeten Namen stammen aus zwei Quellen: 1) globalen Funktionen,
z.B. dim
, 2) Mitgliederdefinitionen des entsprechenden Programmelements, d.h.
Eingaben für lhs
, lhs_batching_dimensions
, rhs
und rhs_batching_dimensions
unter „Eingaben“ Abschnitt von dot_general
.
Wie bereits erwähnt, basiert die Syntax dieser Formel auf Python, prägnanten Erweiterungen. Um die Formel zu verstehen, transformieren wir in einfache Python-Syntax umwandeln.
A) In diesen Formeln verwenden wir =
, um Gleichheit darzustellen. Der erste Schritt
um die Python-Syntax zu erhalten, indem Sie =
durch ==
ersetzen:
dim(lhs, lhs_batching_dimensions...) == dim(rhs, rhs_batching_dimensions...)
B) Außerdem unterstützen diese Formeln Ellipsen (...
), die skalare Ausdrücke umwandeln
in Tensor-Ausdrücke umgewandelt. Kurz gesagt bedeutet f(xs...)
in etwa „für jede
die skalare x
im Tensor xs
, berechnen eine skalare f(x)
und geben dann alle
diese skalaren Ergebnisse zusammen als Tensorergebnis“. Bei der einfachen Python-Syntax
verwandelt sich unsere Beispielformel in:
[dim(lhs, dim1) for dim1 in lhs_batching_dimensions] ==
[dim(rhs, dim2) for dim2 in rhs_batching_dimensions]
Durch Auslassungspunkte ist es oft möglich, Arbeiten auf der Ebene
individuelle Skalare. In einigen kniffligen Fällen können jedoch
untergeordnete halbinformelle
Syntax kann wie in der start_indices[bi0, ..., :, ..., biN]
-Formel verwendet werden
aus der Spezifikation gather
. Was die Präzision angeht,
einen genauen Formalismus für die Übersetzung solcher Syntax in einfaches Python, in
hofft, dass sie immer noch intuitiv für jeden Fall verständlich ist.
Bitte teilen Sie uns mit, wenn bestimmte Formeln undurchsichtig erscheinen. Wir werden dann versuchen,
sie zu verbessern.
Außerdem werden Sie feststellen, dass in Formeln Auslassungspunkte zum Erweitern aller Arten von Listen verwendet werden. einschließlich Tensoren, Listen von Tensoren (die sich beispielsweise aus einer variadischen Anzahl der Tensoren) usw. Dies ist ein weiterer Bereich, in dem wir keine exakte Formalismus (z.B. sind Listen nicht einmal Teil des StableHLO-Typsystems) und sondern auf intuitive Verständlichkeit.
C) Das letzte wichtige notationale Instrument, das wir einsetzen, ist die Broadcasting. Zwar unterstützt das StableHLO-Opset kein implizites Broadcasting, die Formeln auch tun, auch im Dienst der Präzision. Kurz gesagt: Wenn ein Skalar in einem Kontext verwendet wird, in dem ein Tensor erwartet wird, die erwartete Form hat.
Hier ist eine weitere Einschränkung, um mit dem dot_general
-Beispiel fortzufahren:
0 <= lhs_batching_dimensions < rank(lhs)
. Gemäß der Definition in den dot_general
ist lhs_batching_dimensions
ein Tensor, aber sowohl 0
als auch
rank(lhs)
sind Skalare. Nachdem wir implizites Broadcasting angewendet haben,
[0, ..., 0] <= lhs_batching_dimensions < [rank(lhs), ..., rank(lhs)]
werden.
Bei Anwendung auf einen bestimmten dot_general
-Vorgang wird diese Formel
mit einem booleschen Tensor auszuwerten. Wenn Formeln als Einschränkungen verwendet werden,
gilt, wenn die Formel entweder als true
oder als einen Tensor ausgewertet wird,
enthält nur true
-Elemente.
Namen
In Formeln umfasst der lexikalische Anwendungsbereich: 1) globale Funktionen, 2) Mitgliederdefinitionen,
3) lokale Definitionen. Nachfolgend finden Sie eine Liste der globalen Funktionen. Die Liste von Elementdefinitionen hängt von dem Programmelement ab, das die Notation ist. angewendet auf:
- Bei Vorgängen enthalten Mitgliederdefinitionen die in „Eingaben“ eingeführten Namen. und „Ausgaben“ .
- Für alles andere beinhalten die Mitgliedsdefinitionen die strukturellen Teile des
Programmelement, das nach den entsprechenden EBNF-Nicht-Terminals benannt ist. Die meisten
werden die Namen dieser Bauteile durch Umwandeln des
Namen der nicht-terminalen Begriffe in Snake-Case-Schreibweise (z.B.
IntegerLiteral
=>integer_literal
), aber Namen werden dabei manchmal abgekürzt (z.B.QuantizationStorageType
=>storage_type
). In diesem Fall lauten die Namen explizit ähnlich wie "Eingaben" / „Ausgaben“ Abschnitte in Betrieb Spezifikationen. - Darüber hinaus enthalten Mitgliederdefinitionen immer
self
, um auf die entsprechenden Programmelement.
Werte
Beim Auswerten von Formeln funktionieren sie mit den folgenden Arten von Werten:
1) Value
(tatsächliche Werte, z.B. dense<[[1, 2], [3, 4]]> : tensor<2x2xi32>
;
kennen sie immer die Art)
2) Placeholder
(zukünftige Werte, z.B. lhs
, rhs
oder result
; ihre tatsächlichen
Werte noch nicht bekannt sind, nur ihre Typen sind bekannt),
3) Type
(Typen, wie im Abschnitt "Typen" definiert),
4) Function
(globale Funktionen, wie im Abschnitt "Funktionen" definiert).
Je nach Kontext können sich Namen auf unterschiedliche Werte beziehen. Mehr
insbesondere die Semantik, für Vorgänge (und Entsprechungen für andere Programme)
Elemente) definiert die Laufzeitlogik, sodass alle Eingaben als Value
verfügbar sind.
Die „Einschränkungen“ für Vorgänge (und Entsprechungen) definiert
„Compile-Zeit“ Logik, d.h. etwas, das in der Regel vor der Laufzeit ausgeführt wird,
Daher sind nur konstante Eingaben als Value
verfügbar, während andere Eingaben
nur als Placeholder
verfügbar.
Namen | In „Semantics“ | Gehen Sie unter „Einschränkungen“ |
---|---|---|
Globale Funktionen | Function |
Function |
Konstante Eingaben | Value |
Value |
Nicht konstante Eingaben | Value |
Placeholder |
Ausgaben | Value |
Placeholder |
Lokale Definitionen | Hängt von der Definition ab | Hängt von der Definition ab |
Hier ein Beispiel für einen transpose
-Vorgang:
%result = "stablehlo.transpose"(%operand) {
permutation = dense<[2, 1, 0]> : tensor<3xi64>
} : (tensor<2x3x2xi32>) -> tensor<2x3x2xi32>
Bei diesem Vorgang ist permutation
eine Konstante und steht daher als Value
zur Verfügung.
sowohl in Bezug auf Semantik als auch auf Einschränkungen. Im Gegensatz dazu sind operand
und result
in der Semantik als Value
verfügbar, aber nur als Placeholder
in Einschränkungen.
Funktionen
Konstruktion von Typen
Es gibt keine Funktionen, die zum Erstellen von Typen verwendet werden können. Stattdessen haben wir direkt
Typsyntax verwendet, da sie
in der Regel kürzer ist. Beispiel:
(tensor<E>, tensor<E>) -> (tensor<E>)
statt function_type(
[tensor_type([], E), tensor_type([], E)], [tensor_type([], E)])
.
Funktionen für Typen
element_type
wird für Tensortypen und quantisierte Tensortypen definiert. gibtTensorElementType
bzw.QuantizedTensorElementType
zurück. Teil des entsprechendenTensorType
- oderQuantizedTensorType
-Elements.
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
ist eine Verknüpfung füris_quantized(x) and quantization_dimension(x) is not None
.is_per_tensor_quantized(x: Value | Placeholder | Type) -> Value
ist ein Tastenkombination füris_quantized(x) and quantization_dimension(x) is None
.is_promotable(x: Type, y: Type) -> bool
prüft, ob der Typx
hochgestuft werden kann umy
einzugeben. Wennx
undy
den WertQuantizedTensorElementType
haben, wird das Angebot wird nur aufstorage_type
angewendet. Diese spezielle Angebotsversion ist die aktuell im Zusammenhang mit der Berechnung der Reduktion verwendet werden (siehe RFC 2018.
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
ist eine Verknüpfung füris_quantized_tensor_element_type(x)
.is_type_name(x: Value | Placeholder | Type) -> Value
Für alle verfügbar Typen.is_float(x)
gibt beispielsweisetrue
zurück, wennx
eineFloatType
ist. Wennx
ein Wert oder Platzhalter ist, ist diese Funktion ein Kurzbefehl füris_type_name(type(x))
.max_value(x: Type) -> Value
gibt den Maximalwert einesTensorElementType
. Wennx
keinTensorElementType
ist, wirdNone
zurückgegeben.min_value(x: Type) -> Value
gibt den kleinstmöglichen Wert einesTensorElementType
. Wennx
keinTensorElementType
ist, wirdNone
zurückgegeben.member_name(x: Value | Placeholder | Type) -> Any
Für alle Mitglieder verfügbar Definitionenmember_name
aller Typen. Beispiel:tensor_element_type(x)
gibt denTensorElementType
-Teil eines entsprechendenTensorType
zurück. Wennx
ein Wert oder Platzhalter ist, ist diese Funktion ein Kurzbefehl fürmember_name(type(x))
. Wennx
kein Typ mit einem geeigneten Mitglied ist, oder für einen Wert oder Platzhalter eines solchen Typs ist, gibtNone
zurück.is_empty_algorithm(*args: Type)
prüft, ob alle Punktalgorithmusfelder festgelegt sind anNone
. Dies ist erforderlich, da bei Punktalgorithmen die Implementierung definiert ist. Standardverhalten. Daher wäre es falsch, einen Standardwert festzulegen.
Konstruktion von Werten
operation_name(*xs: Value | Type) -> Value
Für alle Vorgänge verfügbar. Beispiel:add(lhs, rhs)
nimmt die beiden Tensorwertelhs
undrhs
an. gibt das Ergebnis der Auswertung deradd
-Operation mit diesen Eingaben zurück. Bei einigen Vorgängen, z.B.broadcast_in_dim
, sind die Ausgabetypen „last-bearing“, d.h. erforderlich, um einen Vorgang auszuwerten. In diesem Fall wird die Funktion verwendet diese Typen als Argumente.
Funktionen für Werte
Alle Operatoren und Funktionen von Python sind verfügbar. Beispiel: beides abo und Slicen Notationen aus Python stehen für die Indexierung in Tensoren, quantisierte Tensoren, zur Verfügung. und Tupeln.
to_destination_type(x: Value, destination_type: Type) -> Value
ist definiert am Tensoren und gibt den konvertierten Wert vonx
basierend auftype(x)
unddestination_type
so:
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)
Es wird im Vorfeld über das Zusammenführen von convert
, uniform_quantize
und
uniform_dequantize
-Vorgänge (#1576)
Nach der Zusammenführung benötigen wir die obige Funktion nicht und können den Vorgangsnamen verwenden.
für convert
.
is_nan(x: Value) -> Value
ist für Tensoren definiert und gibttrue
zurück, wenn Alle Elemente vonx
sindNaN
oderfalse
. Wennx
kein Tensor ist, gibtNone
zurück.is_sorted(x: Value) -> Value
ist für Tensoren definiert und gibttrue
zurück, wenn -Elemente vonx
in aufsteigender Reihenfolge im Hinblick auf die aufsteigende Reihenfolge lexikografische Reihenfolge der Indexe oderfalse
auf andere Weise. Wennx
kein Tensor, gibtNone
zurück.is_unique(x: Value) -> Value
ist für Tensoren definiert und gibttrue
zurück, wennx
hat keine doppelten Elemente oder sonstfalse
. Wennx
kein Tensor ist, gibtNone
zurück.member_name(x: Value) -> Any
ist für alle Mitgliederdefinitionen definiertmember_name
aller Werte. Beispiel:real_part(x)
gibt den WertRealPart
zurück. Teil eines entsprechendenComplexConstant
. Wennx
kein Wert mit einem entsprechendes Mitglied, gibtNone
zurück.same(x: Value) -> Value
ist für Tensoren definiert und gibttrue
zurück, wenn Elemente vonx
sind alle gleich einander oderfalse
anderenfalls. Wenn der Tensor enthält keine Elemente, wird also als "alle gleich einander" gezählt, d.h. gibttrue
zurück. Wennx
kein Tensor ist, wirdNone
zurückgegeben.split(x: Value, num_results: Value, axis: Value) -> Value
ist definiert am Tensoren und gibtnum_results
-Segmente vonx
entlang deraxis
-Achse zurück. Wennx
kein Tensor oderdim(x, axis) % num_results != 0
ist, wirdNone
zurückgegeben.is_defined_in_parent_scope(x: Value) -> Value
ist anhand von Strings definiert und gibttrue
zurück, wennx
der Name einer Funktion ist, die im selben Bereich definiert ist. als übergeordnete Funktion des entsprechenden Vorgangs ein.is_namespaced_op_name(x: Value) -> Value
ist für Strings definiert und gibttrue
, wennx
ein gültiger Vorgangsname ist, d. h., er berücksichtigt die folgende reguläre Ausdruck:[a-zA-Z][a-zA-Z0-9_]*([.][a-zA-Z0-9_$]+)+
Formberechnungen
axes(x: Value | Placeholder | Type) -> Value
ist eine Verknüpfung fürrange(rank(x))
.dim(x: Value | Placeholder | Type, axis: Value) -> Value
ist eine Verknüpfung fürshape(x)[axis]
.dims(x: Value | Placeholder | Type, axes: List) -> List
ist eine Verknüpfung fürlist(map(lambda axis: dim(x, axis), axes))
.index_space(x: Value | Placeholder | Type) -> Value
ist auf Tensoren definiert und gibtsize(x)
-Indizes für die entsprechendeTensorType
zurück, sortiert in aufsteigende lexikografische Reihenfolge, z.B.[0, ..., 0]
,[0, ..., 1]
, ...,shape(x) - 1
Wennx
kein Tensortyp, ein quantisierter Tensortyp oder ein Wert ist oder ein Platzhalter eines dieser Typen gibtNone
zurück.rank(x: Value | Placeholder | Type) -> Value
ist eine Verknüpfung fürsize(shape(x))
.shape(x: Value | Placeholder | Type) -> Value
ist definiert in den Funktionen zu Typen“ Abschnitt übermember_name
.size(x: Value | Placeholder | Type) -> Value
ist eine Verknüpfung fürreduce(lambda x, y: x * y, shape(x))
.
Quantisierungsberechnungen
def baseline_element_type(x: Value | Placeholder | Type) -> Type
ist ein Tastenkombination fürelement_type(baseline_type(x))
.baseline_type
wird für Tensortypen und quantisierte Tensortypen definiert. wandelt sie in eine „Baseline“ um, d.h. einen Typ mit der gleichen Form, aber mit dem Quantisierungsparameter des Elementtyps auf Standardwerte zurückgesetzt. Dies ist als praktischer Trick verwendet, um sowohl Tensor- als auch quantisierte Tensortypen zu vergleichen was häufig erforderlich ist. Bei quantisierten Typen wird dadurch Typen ohne Quantisierungsparameter, d. h.shape
, vergleichenstorage_type
,expressed_type
,storage_min
,storage_max
undquantization_dimension
(für pro-Achse quantisierten Typ) müssen alle übereinstimmen, aberscales
undzero points
können abweichen.
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
wird für quantisierte Tensortypen definiert und wandelt sie in Gleitkomma-Tensortypen. Dies geschieht durch das Konvertieren quantisierter Elemente die ganzzahlige Werte des Speichertyps in entsprechende Gleitkommawerte des ausgedrückten Typs unter Verwendung des Nullpunkts und der Skala die mit dem quantisierten Elementtyp verknüpft sind.
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))
quantize
wird für Gleitkomma-Tensortypen definiert und wandelt sie in quantisierten Tensortypen spezifiziert. Dies geschieht durch die Umwandlung von Gleitkommawerten des ausgedrückten Typs in entsprechende Ganzzahlwerte des Speichertyps umwandeln mit dem Nullpunkt und der Skala des quantisierten Elementtyps.
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)
- Mit
dequantize_op_quantize
werden elementweise Berechnungen für quantisierten Tensoren. Es dequantisiert, d.h. wandelt quantisierte Elemente in ihre Ausgedrückten Typen, führt dann eine Operation durch und quantisiert, d.h. die Ergebnisse in ihre Speichertypen. Derzeit kann diese Funktion nur für die Quantisierung pro Tensor. Die Quantisierung pro Achse ist in Arbeit (#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)
- Mit
hybrid_dequantize_then_op
wird die Nur-Gewichtung-Quantisierung für Hybridbetrieb, der LHs in Gleitkommazahlen und rhs in quantisierten Typen akzeptiert. Es dequantisiert quantisierte Eingaben in ihre expliziten Typen und führt eine Berechnung durch als Gleitkommazahl. Elementtyp des Gleitkommazahl-Tensors und Ausdruckstyp der quantisierten rhs Tensor identisch sein.
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))
Rasterberechnungen
cross_partition(replica_groups: Value) -> Value
"cross_Replikat" ansehen oben.cross_replica(replica_groups: Value) -> Value
"cross_Replikat" ansehen oben.cross_replica_and_partition(replica_groups: Value) -> Value
Weitere Informationen finden Sie in der "cross_replica_and_partition" oben.flattened_ids(replica_groups: Value) -> Value
Siehe den Abschnitt "Flattened_ids" oben.
Dynamik
StableHLO-Werte können dynamische Dimensionsgrößen haben, z.B. tensor<?xi64>
StableHLO-Werte dürfen jedoch keine dynamische Anzahl von Dimensionen (nicht eingestuft) haben
Dynamik, z.B. tensor<*xi64>
. Operanden und Ergebnisse dürfen dynamische Elemente verwenden
auch wenn es Beschränkungen gibt. Einschränkungen werden
wenn möglich statisch überprüft. Andernfalls werden sie auf Laufzeit und
Abweichungen führen zu nicht definiertem Verhalten. Siehe Beispiele unten.
Formenabweichungen bei unären elementweisen Vorgängen
Sehen Sie sich das folgende Spielzeugprogramm an:
func.func @foo(%arg0: tensor<?xf64>) {
%0 = stablehlo.abs %arg0 : (tensor<?xf64>) -> tensor<2xf64>
return
}
Ein solches Programm ist ungewöhnlich, da es nicht üblich ist, die Form des
nicht aber die Form der Eingabe. Trotzdem ist dies ein gültiger StableHLO-Wert,
. Es ist nicht möglich, den abs
-Vorgang in diesem
da die genaue Form des Operanden unbekannt ist. Die Formen sind jedoch
sind sicherlich kompatibel, was statisch überprüft werden kann: ?
könnte sich
zur Laufzeit auf 2
gesetzt und es gäbe kein Problem. ?
könnte jedoch
auch eine andere Ganzzahl, wobei das Verhalten nicht definiert ist.
Wenn eine Dimensionsgröße im Ergebnis dynamisch ist, kann kein undefiniertes Verhalten. Tatsächlich gibt es kein „erwartetes“ kann also kein nicht übereinstimmend.
Nicht übereinstimmende Formen bei binären elementweisen Operationen
Sehen Sie sich das folgende Spielzeugprogramm an:
func.func @foo(%arg0: tensor<?xf64>, %arg1: tensor<?xf64>) {
%0 = stablehlo.add %arg0, %arg0 : (tensor<?xf64>, tensor<?xf64>) -> tensor<?xf64>
return
}
Bei binären elementweisen Operationen sind die Formen der Eingaben Ergebnis muss zur Laufzeit übereinstimmen. Zum Zeitpunkt der Kompilierung müssen statische Dimensionen gleich sein, sonst müssen sie nur kompatibel sein. Wenn irgendeine Dimension in den Eingaben dynamisch ist, sind möglicherweise nicht definierte Dimensionen definiert da die dynamische Größe unter Umständen nicht mit der Größe im anderen Operanden (statisch oder dynamisch) ein. Wenn alle Eingaben statisch ist, spielt es keine Rolle, ob das Ergebnis dynamisch ist oder nicht: statisch. werden bekannte Dimensionen statisch überprüft, dynamische Dimensionen nicht Einschränkungen gelten.
Nicht übereinstimmende Formen bei Vorgängen, die ihre Ausgabeform als Operanden annehmen
Sehen Sie sich das folgende Spielzeugprogramm an:
func.func @foo(%arg0: tensor<2xi32>) {
%0 = stablehlo.dynamic_iota %arg0, dim = 0 : (tensor<2xi32>) -> tensor<3x4xi64>
return
}
Die Werte im Formoperanden müssen zur Laufzeit der Form des Ergebnisses entsprechen.
Andernfalls ist das Verhalten nicht definiert. Das heißt, zur Laufzeit muss %arg0
einen
Wert von dense<[3, 4]> : tensor<2xi32>
Wenn der Formoperanden konstant ist, wird dies
statisch überprüft werden kann. Wenn die Ergebnisform vollständig dynamisch ist,
darf nicht abweichen.