StableHLO, मशीन लर्निंग (ML) मॉडल में हाई-लेवल ऑपरेशन (एचएलओ) के लिए सेट किया गया ऑपरेशन है. StableHLO, अलग-अलग एआई फ़्रेमवर्क और एआई कंपाइलर के बीच पोर्टेबिलिटी लेयर के तौर पर काम करता है: StableHLO प्रोग्राम बनाने वाले एआई फ़्रेमवर्क, StableHLO प्रोग्राम का इस्तेमाल करने वाले एआई कंपाइलर के साथ काम करते हैं.
हमारा लक्ष्य, अलग-अलग ML फ़्रेमवर्क (जैसे, TensorFlow, JAX, और PyTorch) और ML कंपाइलर (जैसे, XLA और IREE) के बीच बेहतर इंटरऑपरेबिलिटी बनाकर, ML डेवलपमेंट को आसान और तेज़ बनाना है. इस काम के लिए, इस दस्तावेज़ में StableHLO प्रोग्रामिंग भाषा के बारे में जानकारी दी गई है.
इस स्पेसिफ़िकेशन में तीन मुख्य सेक्शन होते हैं. सबसे पहले, प्रोग्राम सेक्शन में StableHLO प्रोग्राम के स्ट्रक्चर के बारे में बताया गया है. इन प्रोग्राम में StableHLO फ़ंक्शन होते हैं, जिनमें StableHLO ऑपरेशन होते हैं. उस स्ट्रक्चर में, Ops सेक्शन, अलग-अलग ऑपरेशन के सिमेंटिक्स के बारे में बताता है. एक्ज़ीक्यूटिव सेक्शन में, प्रोग्राम में एक साथ लागू किए जाने वाले सभी ऑपरेशन के लिए सिमेंटिक उपलब्ध होते हैं. आखिर में, नोटेशन सेक्शन में, स्पेसिफ़िकेशन में इस्तेमाल किए गए नोटेशन के बारे में बताया गया है.
StableHLO की पिछली रिलीज़ की खास जानकारी देखने के लिए, अपनी पसंद के टैग की गई रिलीज़ पर मौजूद रिपॉज़िटरी खोलें. उदाहरण के लिए, StableHLO v0.19.0 स्पेसिफ़िकेशन. StableHLO के हर छोटे वर्शन में हुए बदलावों को देखने के लिए, VhloDialect.td में वर्शन लॉग देखें.
प्रोग्राम
Program ::= {Func}
StableHLO प्रोग्राम में, StableHLO फ़ंक्शन की कोई भी संख्या हो सकती है.
यहां एक प्रोग्राम का उदाहरण दिया गया है, जिसमें फ़ंक्शन @main
के तीन इनपुट (%image
, %weights
, और %bias
) और एक आउटपुट है. फ़ंक्शन के मुख्य हिस्से में, छह ऑपरेशन हैं.
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>) -> ()
}
फ़ंक्शन
Func ::= 'func' '.' 'func' FuncId FuncInputs FuncOutputs '{' FuncBody '}'
FuncInputs ::= '(' [FuncInput {',' FuncInput}] `)`
FuncInput ::= ValueId ':' ValueType
FuncOutputs ::= ['->' FuncOutput, {',' FuncOutput}]
FuncOutput ::= ValueType
FuncBody ::= {Op}
StableHLO फ़ंक्शन (जिन्हें named functions भी कहा जाता है) में एक आइडेंटिफ़ायर, इनपुट/आउटपुट, और एक बॉडी होता है. आने वाले समय में, हम फ़ंक्शन के लिए अतिरिक्त मेटाडेटा लॉन्च करने की योजना बना रहे हैं. इससे HLO (#425, #626, #740, #744) के साथ बेहतर तरीके से काम किया जा सकेगा.
आइडेंटिफ़ायर
FuncId ::= '@' letter {letter | digit}
ValueId ::= '%' digit {digit}
| '%' letter {letter | digit}
letter ::= 'a' | ... | 'z' | 'A' | ... | 'Z' | '_'
digit ::= '0' | ... | '9'
StableHLO आइडेंटिफ़ायर, कई प्रोग्रामिंग भाषाओं के आइडेंटिफ़ायर से मिलते-जुलते होते हैं. इनकी दो खास बातें होती हैं: 1) सभी आइडेंटिफ़ायर में सिगल हैं, जो अलग-अलग तरह के आइडेंटिफ़ायर में अंतर करते हैं, 2) वैल्यू आइडेंटिफ़ायर, StableHLO प्रोग्राम बनाने की प्रोसेस को आसान बनाने के लिए पूरी तरह से न्यूमेरिक हो सकते हैं.
टाइप
Type ::= ValueType | NonValueType
ValueType ::= TensorType | QuantizedTensorType | TokenType | TupleType
NonValueType ::= TensorElementType | QuantizedTensorElementType | FunctionType | StringType
StableHLO टाइप को वैल्यू टाइप (जिन्हें फ़र्स्ट-क्लास टाइप भी कहा जाता है) में बांटा जाता है. ये StableHLO वैल्यू दिखाते हैं. साथ ही, नॉन-वैल्यू टाइप, प्रोग्राम के अन्य एलिमेंट के बारे में बताते हैं. StableHLO टाइप, कई प्रोग्रामिंग भाषाओं के टाइप से मिलते-जुलते हैं. इनमें मुख्य खास बात यह है कि StableHLO, डोमेन के हिसाब से काम करता है. इसकी वजह से, कुछ असामान्य नतीजे मिलते हैं. उदाहरण के लिए, स्केलर टाइप, वैल्यू टाइप नहीं होते.
TensorType ::= 'tensor' '<' Shape TensorElementType '>'
Shape ::= {DimensionSize 'x'}
DimensionSize ::= digit {digit} | '?'
टेन्सर के टाइप टेंसर यानी कई डाइमेंशन वाले सरणियों को दिखाते हैं. इनमें एक आकार और एक एलिमेंट टाइप होता है. आकार, डाइमेंशन के साइज़ को दिखाता है. ये साइज़, डाइमेंशन (जिन्हें ऐक्सिस भी कहा जाता है) के बढ़ते क्रम में होते हैं. इन डाइमेंशन की संख्या 0
से R-1
तक होती है. डाइमेंशन R
की संख्या को रैंक कहा जाता है. उदाहरण के लिए, tensor<2x3xf32>
एक टेंसर टाइप है, जिसका आकार 2x3
और एलिमेंट टाइप f32
है. इसमें दो डाइमेंशन (या दूसरे शब्दों में, दो ऐक्सिस) हैं - 0वां डाइमेंशन और पहला डाइमेंशन - जिनका साइज़ 2 और 3 है. इसकी रैंक दो है.
आकार, कुछ हद तक या पूरी तरह से अनजान (डाइनैमिक) हो सकते हैं. उदाहरण के लिए, tensor<?x2xf64>
कुछ हद तक अनजान है और tensor<?x?xf64>
पूरी तरह से अनजान है. डाइनैमिक डाइमेंशन के साइज़ को ?
का इस्तेमाल करके दिखाया जाता है. आकारों को रैंक नहीं दिया जा सकता.
आने वाले समय में, हम डाइमेंशन साइज़ और एलिमेंट टाइप के अलावा, टेंसर टाइप को और भी बेहतर बनाने की कोशिश करेंगे. उदाहरण के लिए, लेआउट (#629) और स्पार्सिटी (#1078) को शामिल करने के लिए.
QuantizedTensorType ::= 'tensor' '<' Shape QuantizedTensorElementType '>'
QuantizedTensorElementType ::= '!quant.uniform' '<'
QuantizationStorageType
['<' QuantizationStorageMin ':' QuantizationStorageMax '>']
':' QuantizationExpressedType
[':' QuantizationDimension]
',' QuantizationParameters '>'
QuantizationStorageType ::= IntegerType
QuantizationStorageMin ::= IntegerLiteral
QuantizationStorageMax ::= IntegerLiteral
QuantizationExpressedType ::= FloatType
QuantizationDimension ::= IntegerLiteral
QuantizationParameters ::= QuantizationParameter
| '{' QuantizationParameter {',' QuantizationParameter} '}'
QuantizationParameter ::= QuantizationScale [':' QuantizationZeroPoint]
QuantizationScale ::= FloatLiteral
QuantizationZeroPoint ::= IntegerLiteral
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
storage_type |
इंटेजर टाइप | (C1-C3), (C8) |
storage_min |
पूर्णांक कॉन्सटेंट | (C1), (C3), (C7) |
storage_max |
पूर्णांक कॉन्सटेंट | (C2), (C3), (C7) |
expressed_type |
फ़्लोटिंग-पॉइंट टाइप | (C4) |
quantization_dimension |
वैकल्पिक पूर्णांक कॉन्सटेंट | (C10-C12) |
scales |
फ़्लोटिंग-पॉइंट कॉन्स्टेंट की वैरिएडिक संख्या | (C4-C6), (C9), (C10), (C13) |
zero_points |
वैरिएबल संख्या वाली पूर्णांक स्थिरांक | (C7-C9) |
एलिमेंट के क्वांटाइज़ किए गए टाइप, स्टोरेज टाइप की इंटेजर वैल्यू दिखाते हैं. ये वैल्यू storage_min
से storage_max
(इसमें शामिल) की रेंज में होती हैं. ये वैल्यू, एक्सप्रेशन टाइप की फ़्लोटिंग-पॉइंट वैल्यू से मेल खाती हैं. किसी पूर्णांक वैल्यू i
के लिए,
उससे जुड़ी फ़्लोटिंग-पॉइंट वैल्यू f
का हिसाब f = (i - zero_point) * scale
के तौर पर लगाया जा सकता है. यहां scale
और zero_point
को
क्वांटाइज़ेशन पैरामीटर कहा जाता है. व्याकरण में storage_min
और storage_max
वैकल्पिक हैं, लेकिन उनकी डिफ़ॉल्ट वैल्यू क्रमशः min_value(storage_type)
और max_value(storage_type)
है. क्वांटाइज़ किए गए एलिमेंट टाइप में ये कंस्ट्रेंट होते हैं:
- (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) अगर
is_empty(quantization_dimension)
है, तोsize(scales) = 1
है. - (C11)
0 <= quantization_dimension
.
फ़िलहाल, QuantizationScale
एक फ़्लोटिंग-पॉइंट कॉन्स्टेंट है. हालांकि, कई लोगों की दिलचस्पी पूर्णांक पर आधारित स्केल में है. इन्हें मल्टीप्लायर और शिफ़्ट के साथ दिखाया जाता है. हम आने वाले समय में, इस पर काम करने की योजना बना रहे हैं
(#1404).
QuantizationZeroPoint
के सेमेंटेक्स पर फ़िलहाल चर्चा चल रही है. इसमें टाइप, वैल्यू, और यह भी शामिल है कि क्वांटाइज़ किए गए टेंसर टाइप में सिर्फ़ एक या संभावित रूप से कई शून्य पॉइंट हो सकते हैं या नहीं. इस चर्चा के नतीजों के आधार पर, आने वाले समय में शून्य पॉइंट के बारे में जानकारी बदल सकती है (#1405).
QuantizationStorageMin
और QuantizationStorageMax
के सेमेटिक्स के बारे में भी चर्चा की जा रही है. इससे यह तय किया जा सकेगा कि इन वैल्यू और क्वांटाइज़ किए गए टेंसर की वैल्यू पर कोई पाबंदी लगाई जानी चाहिए या नहीं (#1406).
आखिर में, हम अनजान स्केल और शून्य बिंदुओं को दिखाने के बारे में एक्सप्लोर करने जा रहे हैं. ठीक उसी तरह जैसे हम अनजान डाइमेंशन साइज़ (#1407) को दिखाने के बारे में एक्सप्लोर कर रहे हैं.
क्वांटाइज़्ड टेंसर टाइप, क्वांटाइज़्ड एलिमेंट वाले टेंसर दिखाते हैं. ये टेंसर, सामान्य टेंसर की तरह ही होते हैं. हालांकि, इनके एलिमेंट में सामान्य एलिमेंट टाइप के बजाय, क्वांटाइज़ किए गए एलिमेंट टाइप होते हैं.
क्वांटाइज़ किए गए टेंसर में, क्वांटाइज़ेशन हर टेंसर के लिए हो सकता है. इसका मतलब है कि पूरे टेंसर के लिए एक scale
और zero_point
हो सकता है. इसके अलावा, क्वांटाइज़ेशन हर ऐक्सिस के लिए भी हो सकता है. इसका मतलब है कि एक खास डाइमेंशन quantization_dimension
के हर स्लाइस के लिए एक जोड़ा, कई scales
और zero_points
हो सकते हैं. ज़्यादा औपचारिक तौर पर, हर ऐक्सिस के लिए क्वांटाइज़ेशन वाले टेंसर t
में, quantization_dimension
की dim(t, quantization_dimension)
स्लाइस होती हैं: t[:, ..., 0, ..., :], t[:, ..., 1, ..., :]
वगैरह. i
वीं स्लाइस के सभी एलिमेंट, अपने क्वांटाइज़ेशन पैरामीटर के तौर पर scales[i]
और zero_points[i]
का इस्तेमाल करते हैं. क्वांटाइज़्ड टेंसर टाइप की ये सीमाएं होती हैं:
- हर टेंसर के लिए क्वांटाइज़ेशन के लिए:
- कोई और पाबंदी नहीं.
- हर अक्ष के लिए क्वांटिज़ेशन के लिए:
- (C12)
quantization_dimension < rank(self)
. - (C13)
dim(self, quantization_dimension) = size(scales)
.
- (C12)
TokenType ::= 'token'
टोकन टाइप, टोकन दिखाते हैं. टोकन, ऐसी वैल्यू होती हैं जिन्हें कुछ ऑपरेशन से जनरेट और इस्तेमाल किया जाता है. टोकन का इस्तेमाल कार्रवाइयों को लागू करने का ऑर्डर देने के लिए किया जाता है. जैसा कि लागू करना सेक्शन में बताया गया है.
TupleType ::= 'tuple' '<' TupleElementTypes '>'
TupleElementTypes ::= [ValueType {',' ValueType}]
ट्यूपल टाइप, ट्यूपल यानी अलग-अलग तरह की सूचियों को दिखाते हैं. टपल, एक लेगसी सुविधा है, जो सिर्फ़ एचएलओ (HLO) के साथ काम करने के लिए मौजूद है. एचएलओ में, वैरिएडिक इनपुट और आउटपुट को दिखाने के लिए ट्यूपल का इस्तेमाल किया जाता है. StableHLO में, वैरिएडिक इनपुट और आउटपुट का इस्तेमाल, नेटिव तौर पर किया जा सकता है. StableHLO में ट्यूपल का इस्तेमाल सिर्फ़ HLO ABI को पूरी तरह से दिखाने के लिए किया जाता है. उदाहरण के लिए, किसी खास तरीके से लागू करने पर T
, tuple<T>
, और tuple<tuple<T>>
काफ़ी अलग हो सकते हैं. आने वाले समय में, हम एचएलओ एबीआई में बदलाव करने की योजना बना रहे हैं. इससे हम StableHLO (#598) से टपल टाइप हटा सकते हैं.
TensorElementType ::= BooleanType | IntegerType | FloatType | ComplexType
BooleanType ::= 'i1'
IntegerType ::= SignedIntegerType | UnsignedIntegerType
SignedIntegerType ::= 'si2' | 'si4' | 'si8' | 'si16' | 'si32' | 'si64'
UnsignedIntegerType ::= 'ui2' | 'ui4' | 'ui8' | 'ui16' | 'ui32' | 'ui64'
FloatType ::= 'f4E2M1FN' | 'f6E2M3FN' | 'f6E3M2FN' | 'f8E3M4' | 'f8E4M3'
| 'f8E4M3FN' | 'f8E4M3FNUZ' | 'f8E4M3B11FNUZ' | 'f8E5M2'
| 'f8E5M2FNUZ' | 'f8E8M0FNU' | 'bf16' | 'f16' | 'f32' | 'f64'
TensorFloat32 ::= 'tf32'
ComplexType ::= 'complex' '<' ComplexElementType '>'
ComplexElementType ::= 'f32' | 'f64'
एलिमेंट टाइप, टेंसर टाइप के एलिमेंट दिखाते हैं. कई प्रोग्रामिंग भाषाओं के उलट, StableHLO में ये टाइप फ़र्स्ट क्लास नहीं होते. इसका मतलब है कि
StableHLO प्रोग्राम, सीधे तौर पर इस तरह की वैल्यू नहीं दिखा सकते. इस वजह से,
T
टाइप की अदिश वैल्यू को tensor<T>
टाइप के 0-डाइमेंशन वाले टेंसर वैल्यू के साथ दिखाना मुहावरेदार होता है.
- बूलियन टाइप, बूलियन वैल्यू
true
औरfalse
दिखाता है. - इंटिजर टाइप, साइन वाले (
si
) या बिना साइन वाले (ui
) हो सकते हैं. साथ ही, इनमें इस्तेमाल की जा सकने वाली बिट की चौड़ाई (2
,4
,8
,16
,32
या64
) में से कोई एक होनी चाहिए. साइन वालेsiN
टाइप,-2^(N-1)
से लेकर2^(N-1)-1
तक की इंटिजर वैल्यू दिखाते हैं. वहीं, बिना साइन वालेuiN
टाइप,0
से लेकर2^N-1
तक की इंटिजर वैल्यू दिखाते हैं. - फ़्लोटिंग-पॉइंट टाइप इनमें से कोई एक हो सकता है:
- आईईईई-754 कन्वेंशन के मुताबिक
f8E3M4
,f8E4M3
, औरf8E5M2
8-बिट फ़्लोटिंग पॉइंट नंबर. f8E4M3FN
औरf8E5M2
टाइप, डीप लर्निंग के लिए FP8 फ़ॉर्मैट में बताए गए FP8 फ़ॉर्मैट केE4M3
औरE5M2
कोडिंग से जुड़े होते हैं.- डीप न्यूरल नेटवर्क के लिए 8-बिट नंबर वाले फ़ॉर्मैट में बताए गए, FP8 फ़ॉर्मैट के
E4M3
औरE5M2
एन्कोडिंग से जुड़ेf8E4M3FNUZ
औरf8E5M2FNUZ
टाइप. f8E4M3B11FNUZ
टाइप, FP8 फ़ॉर्मैट केE4M3
एन्कोडिंग से जुड़ा है. इन फ़ॉर्मैट के बारे में हाइब्रिड 8-बिट फ़्लोटिंग पॉइंट (HFP8) ट्रेनिंग और डीप न्यूरल नेटवर्क के लिए अनुमान में बताया गया है.bf16
टाइप,bfloat16
फ़ॉर्मैट से मेल खाता है. इस फ़ॉर्मैट के बारे में BFloat16: Cloud TPUs पर बेहतर परफ़ॉर्मेंस का राज में बताया गया है.f16
,f32
, औरf64
टाइप, IEEE 754 स्टैंडर्ड में बताए गएbinary16
("हफ़्फ़ प्रिसीज़न"),binary32
("सिंगल प्रिसीज़न"), औरbinary64
("डबल प्रिसीज़न") फ़ॉर्मैट के हिसाब से होते हैं.tf32
टाइप, TensorFloat32 फ़ॉर्मैट से जुड़ा होता है और StableHLO में सीमित तौर पर काम करता है.f4E2M1FN
,f6E2M3FN
,f6E3M2FN
, औरf8E8M0FNU
एमएक्स (माइक्रोस्केलिंग) टाइप, जिनके बारे में ओसीपी माइक्रोस्केलिंग फ़ॉर्मैट स्पेसिफ़िकेशन में बताया गया है.
- आईईईई-754 कन्वेंशन के मुताबिक
- कॉम्प्लेक्स टाइप, कॉम्प्लेक्स वैल्यू दिखाते हैं. इनमें एक ही एलिमेंट टाइप के रीयल पार्ट और इमैजरी पार्ट होते हैं. इस्तेमाल किए जा सकने वाले जटिल टाइप में
complex<f32>
(दोनों पार्टf32
टाइप के हैं) औरcomplex<f64>
(दोनों पार्टf64
टाइप के हैं) शामिल हैं.
FunctionType ::= '(' InputTypes ')' '->' '(' OutputTypes ')'
InputTypes ::= [ValueType {',' ValueType}]
OutputTypes ::= [ValueType {',' ValueType}]
फ़ंक्शन टाइप नाम वाले और पहचान छिपाने वाले, दोनों तरह के फ़ंक्शन दिखाते हैं. इनमें इनपुट टाइप (->
की बाईं ओर मौजूद टाइप की सूची) और आउटपुट टाइप (->
की दाईं ओर मौजूद टाइप की सूची) होते हैं. कई प्रोग्रामिंग भाषाओं में, फ़ंक्शन टाइप फ़र्स्ट क्लास होते हैं, लेकिन StableHLO में नहीं.
StringType ::= 'string'
स्ट्रिंग टाइप, बाइट के क्रम को दिखाता है. कई प्रोग्रामिंग भाषाओं के उलट, StableHLO में स्ट्रिंग टाइप फ़र्स्ट क्लास नहीं है. इसका इस्तेमाल सिर्फ़ प्रोग्राम एलिमेंट के लिए स्टैटिक मेटाडेटा तय करने के लिए किया जाता है.
ऑपरेशंस
StableHLO ऑपरेशन (जिन्हें ऑपरेशन भी कहा जाता है), मशीन लर्निंग मॉडल में हाई-लेवल ऑपरेशन के क्लोज़्ड सेट को दिखाते हैं. जैसा कि ऊपर बताया गया है, StableHLO सिंटैक्स काफ़ी हद तक MLIR से प्रेरित है. यह ज़रूरी नहीं है कि यह सबसे बेहतर विकल्प हो, लेकिन यह StableHLO के लक्ष्य के लिए सबसे सही विकल्प है. इस लक्ष्य का मकसद, ML फ़्रेमवर्क और ML कंपाइलर के बीच ज़्यादा इंटरऑपरेबिलिटी बनाना है.
Op ::= [OpOutputs] OpName OpInputs ':' OpSignature
OpName ::= '"' 'stablehlo' '.' OpMnemonic '"'
OpMnemonic ::= 'abs' | 'add' | ...
StableHLO ऑपरेशन (जिन्हें ops भी कहा जाता है) का एक नाम, इनपुट/आउटपुट, और एक सिग्नेचर होता है. नाम में stablehlo.
प्रीफ़िक्स और निमोनिक शामिल है, जो इस्तेमाल किए जा सकने वाले ऑपरेशन में से किसी एक की खास तौर पर पहचान करता है. इस्तेमाल किए जा सकने वाले सभी ऑपरेशन की पूरी सूची देखने के लिए, नीचे देखें.
OpInputs ::= OpInputValues OpInputFuncs OpInputAttrs
OpInputValues ::= '(' [OpInputValue {',' OpInputValue}] ')'
OpInputValue ::= ValueId
OpInputFuncs ::= ['(' OpInputFunc {',' OpInputFunc} ')']
OpInputAttrs ::= ['{' OpInputAttr {',' OpInputAttr} '}']
OpOutputs ::= [OpOutput {',' OpOutput} '=']
OpOutput ::= ValueId
ऑपरेशन, इनपुट का इस्तेमाल करते हैं और आउटपुट जनरेट करते हैं. इनपुट को इन कैटगरी में बांटा जाता है: इनपुट वैल्यू (एक्सीक्यूशन के दौरान कैलकुलेट की जाती हैं), इनपुट फ़ंक्शन (स्टैटिक तौर पर दिए जाते हैं, क्योंकि StableHLO फ़ंक्शन में फ़ंक्शन, फ़र्स्ट-क्लास वैल्यू नहीं होते), और इनपुट एट्रिब्यूट (इन्हें भी स्टैटिक तौर पर दिया जाता है). किसी ऑपरेशन में इस्तेमाल किए जाने वाले इनपुट और आउटपुट का टाइप, उसके मेनिमनिक पर निर्भर करता है. उदाहरण के लिए, add
op, दो इनपुट वैल्यू का इस्तेमाल करता है और एक आउटपुट वैल्यू जनरेट करता है. इसकी तुलना में, select_and_scatter
ऑपरेटर तीन इनपुट वैल्यू, दो इनपुट फ़ंक्शन, और तीन इनपुट एट्रिब्यूट का इस्तेमाल करता है.
OpInputFunc ::= '{' Unused FuncInputs ':' FuncBody '}'
Unused ::= '^' digit {digit}
| '^' letter {letter | digit}
इनपुट फ़ंक्शन (जिन्हें बिना नाम वाले फ़ंक्शन भी कहा जाता है) बहुत हद तक नाम वाले फ़ंक्शन से मिलते-जुलते हैं. हालांकि, इनमें ये अंतर होते हैं: 1) इनमें कोई आइडेंटिफ़ायर नहीं होता (इसलिए इनका नाम "बिना नाम वाला" है), 2) इनमें आउटपुट टाइप का एलान नहीं किया जाता (आउटपुट टाइप का पता, फ़ंक्शन में मौजूद return
ऑपरेटर से लगाया जाता है).
इनपुट फ़ंक्शन के सिंटैक्स में अभी इस्तेमाल न किया गया हिस्सा शामिल है (ऊपर दिया गया Unused
प्रोडक्शन देखें). यह एमएलआईआर के साथ काम करने के लिए मौजूद है. MLIR में, "क्षेत्रों" का एक सामान्य कॉन्सेप्ट है. इनमें जंप ऑपरेशन की मदद से, ऑपरेशन के कई "ब्लॉक" एक साथ जुड़े हो सकते हैं. इन ब्लॉक में ऐसे आईडी होते हैं जो Unused
प्रोडक्शन से जुड़े होते हैं, ताकि उन्हें एक-दूसरे से अलग किया जा सके.
StableHLO में जंप ऑपरेशन नहीं होते, इसलिए MLIR सिंटैक्स का उससे जुड़ा हिस्सा इस्तेमाल नहीं किया जाता (हालांकि, वह अब भी मौजूद है).
OpInputAttr ::= OpInputAttrName '=' OpInputAttrValue
OpInputAttrName ::= letter {letter | digit}
OpInputAttrValue ::= Constant
इनपुट एट्रिब्यूट का एक नाम और वैल्यू होती है, जो काम करने वाली स्थिर वैल्यू में से एक होती है. प्रोग्राम एलिमेंट के लिए स्टैटिक मेटाडेटा तय करने का मुख्य तरीका यही है. उदाहरण के लिए, concatenate
ऑपरेटर, dimension
एट्रिब्यूट का इस्तेमाल करके उस डाइमेंशन की जानकारी देता है जिसके साथ इनपुट वैल्यू को जोड़ा जाता है. इसी तरह, slice
ऑपरेटर, इनपुट वैल्यू को स्लाइस करने के लिए इस्तेमाल किए जाने वाले सीमाओं की जानकारी देने के लिए, start_indices
और limit_indices
जैसे कई एट्रिब्यूट का इस्तेमाल करता है.
फ़िलहाल, आम तौर पर इस्तेमाल किए जा रहे StableHLO प्रोग्राम में कभी-कभी ऐसे एट्रिब्यूट होते हैं जिनके बारे में इस दस्तावेज़ में नहीं बताया गया है. आने वाले समय में, हम इन एट्रिब्यूट को StableHLO ऑपसेट में शामिल करने या StableHLO प्रोग्राम में दिखने से रोकने की योजना बना रहे हैं. इस बीच, यहां इन एट्रिब्यूट की सूची दी गई है:
layout
(#629).mhlo.frontend_attributes
(#628).mhlo.sharding
(#619).output_operand_aliases
(#740).- जगह की जानकारी का मेटाडेटा (#594).
OpSignature ::= '(' [ValueType {',' ValueType}] ')' '->' '(' [ValueType {',' ValueType}] ')'
ऑपरेशन के सिग्नेचर में, सभी इनपुट वैल्यू के टाइप (->
की बाईं ओर टाइप की सूची) और सभी आउटपुट वैल्यू के टाइप (->
की दाईं ओर टाइप की सूची) शामिल होते हैं. असल में, इनपुट टाइप और आउटपुट टाइप, दोनों ही ज़रूरी नहीं होते. ऐसा इसलिए है, क्योंकि ज़्यादातर StableHLO ऑपरेशन के लिए, इनपुट से आउटपुट टाइप का पता लगाया जा सकता है. इसके बावजूद, MLIR के साथ काम करने के लिए, op
signature को StableHLO सिंटैक्स का हिस्सा बनाया गया है.
नीचे एक ऑप का उदाहरण दिया गया है जिसका स्मरणक (स्मरण) select_and_scatter
है. इसमें तीन इनपुट वैल्यू (%operand
, %source
, और %init_value
), दो इनपुट फ़ंक्शन, और तीन इनपुट एट्रिब्यूट (window_dimensions
, window_strides
, और padding
) इस्तेमाल किए जाते हैं. ध्यान दें कि ऑपरेशन के सिग्नेचर में सिर्फ़ इनपुट वैल्यू के टाइप शामिल होते हैं, न कि इनलाइन में दिए गए इनपुट फ़ंक्शन और एट्रिब्यूट के टाइप.
%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>
कॉन्स्टेंट
Constant ::= BooleanConstant
| IntegerConstant
| FloatConstant
| ComplexConstant
| TensorConstant
| QuantizedTensorConstant
| StringConstant
| EnumConstant
StableHLO कॉन्स्टेंट में एक लिटरल और एक टाइप होता है, जो एक साथ StableHLO वैल्यू दिखाता है. आम तौर पर, टाइप कॉन्सटेंट सिंटैक्स का हिस्सा होता है. हालांकि,
जब यह साफ़ तौर पर पता न हो (उदाहरण के लिए, बूलियन कॉन्सटेंट में टाइप i1
होता है,
जबकि इंटीजर कॉन्सटेंट में, कई तरह के संभावित टाइप हो सकते हैं).
BooleanConstant ::= BooleanLiteral
BooleanLiteral ::= 'true' | 'false'
बूलियन कॉन्सटेंट, बूलियन वैल्यू true
और false
को दिखाते हैं. बूलियन कॉन्सटेंट का टाइप i1
होता है.
IntegerConstant ::= IntegerLiteral ':' IntegerType
IntegerLiteral ::= ['-' | '+'] DecimalDigits
| ['-' | '+'] '0x' HexadecimalDigits
DecimalDigits ::= decimalDigit {decimalDigit}
HexadecimalDigits ::= hexadecimalDigit {hexadecimalDigit}
decimalDigit ::= '0' | ... | '9'
hexadecimalDigit ::= decimalDigit | 'a' | ... | 'f' | 'A' | ... | 'F'
पूर्णांक कॉन्स्टेंट, दशमलव या हेक्साडेसिमल नोटेशन का इस्तेमाल करने वाली स्ट्रिंग के ज़रिए पूर्णांक वैल्यू दिखाते हैं. बाइनरी या ऑक्टल जैसे अन्य बेस का इस्तेमाल नहीं किया जा सकता. पूर्णांक के लिए, ये पाबंदियां लागू होती हैं:
- (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]
फ़्लोटिंग-पॉइंट कॉन्स्टेंट, स्ट्रिंग के ज़रिए फ़्लोटिंग-पॉइंट वैल्यू दिखाते हैं. इन स्ट्रिंग में दशमलव या वैज्ञानिक नोटेशन का इस्तेमाल किया जाता है. इसके अलावा, हैक्साडेसिमल नोटेशन का इस्तेमाल करके, संबंधित टाइप के फ़्लोटिंग-पॉइंट फ़ॉर्मैट में, बिट की जानकारी सीधे तौर पर दी जा सकती है. फ़्लोटिंग-पॉइंट की वैल्यू के लिए ये पाबंदियां हैं:
- (C1) अगर हेक्साडेसिमल नोटेशन का इस्तेमाल नहीं किया जाता है, तो
is_wellformed(float_literal, float_type)
. - (C2) अगर हेक्साडेसिमल नोटेशन का इस्तेमाल किया जाता है, तो
size(hexadecimal_digits) = num_bits(float_type) / 4
.
ComplexConstant ::= ComplexLiteral ':' ComplexType
ComplexLiteral ::= '(' RealPart ',' ImaginaryPart ')'
RealPart ::= FloatLiteral
ImaginaryPart ::= FloatLiteral
कॉम्प्लेक्स कॉन्स्टेंट, रीयल पार्ट (पहले आता है) और इमेजरी पार्ट (दूसरे नंबर पर आता है) की सूचियों का इस्तेमाल करके कॉम्प्लेक्स वैल्यू दिखाते हैं. उदाहरण के लिए,
(1.0, 0.0) : complex<f32>
से 1.0 + 0.0i
और
(0.0, 1.0) : complex<f32>
से 0.0 + 1.0i
का पता चलता है. इसके बाद, मेमोरी में इन हिस्सों को सेव करने का क्रम तय होता है. कॉम्प्लेक्स कॉन्स्टेंट के लिए ये पाबंदियां लागू होती हैं:
- (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
टेन्सर कॉन्सटेंट, NumPy नोटेशन के ज़रिए बताई गई नेस्ट की गई सूचियों का इस्तेमाल करके टेंसर वैल्यू दिखाता है. उदाहरण के लिए, dense<[[1, 2, 3], [4, 5, 6]]> : tensor<2x3xi32>
, इंडेक्स से एलिमेंट तक की इस मैपिंग के साथ टेंसर वैल्यू दिखाता है:
{0, 0} => 1
, {0, 1} => 2
, {0, 2} => 3
, {1, 0} => 4
, {1, 1} => 5
,
{1, 2} => 6
. इन एलिमेंट को मेमोरी में किस क्रम में सेव किया जाता है, यह लागू करने के तरीके पर निर्भर करता है. टेन्सर कॉन्सटेंट में ये कंस्ट्रेंट होते हैं:
- (C1)
has_syntax(tensor_literal, element_type(tensor_type))
, जहां: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))
, जहां: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:])
.- इसके अलावा,
false
.
QuantizedTensorConstant ::= QuantizedTensorLiteral ':' QuantizedTensorType
QuantizedTensorLiteral ::= 'dense' '<' (DenseLiteral | ElementLiteral) '>'
क्वांटाइज़ किए गए टेंसर कॉन्स्टेंट, टेंसर कॉन्स्टेंट के जैसे ही नोटेशन का इस्तेमाल करके, क्वांटाइज़ किए गए टेंसर वैल्यू दिखाते हैं. इनमें एलिमेंट को स्टोरेज टाइप के कॉन्स्टेंट के तौर पर दिखाया जाता है. क्वांटाइज़ किए गए टेंसर कॉन्स्टेंट पर ये पाबंदियां लागू होती हैं:
- (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))
स्ट्रिंग लिटरल में, ASCII वर्णों और एस्केप सीक्वेंस का इस्तेमाल करके तय किए गए बाइट होते हैं. ये कोड, कोडिंग के हिसाब से काम नहीं करते. इसलिए, इन बाइट को लागू करने के तरीके के हिसाब से ही इनका मतलब निकाला जा सकता है. स्ट्रिंग लिटरल का टाइप string
होता है.
ऑपरेशन
कुल
सिमैंटिक
operand
टेंसर पर एलिमेंट के हिसाब से, एब्स ऑपरेशन करता है और result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- साइन वाले पूर्णांकों के लिए: पूर्णांक मॉड्यूल.
- फ़्लोट के लिए: IEEE-754 से
abs
. - सम्मिश्र संख्याओं के लिए: जटिल मापांक.
- क्वांटाइज़ किए गए टाइप के लिए:
dequantize_op_quantize(abs, operand, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
साइन वाला पूर्णांक, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (सी1-सी2) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
साइन किए गए पूर्णांक या फ़्लोटिंग-पॉइंट टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (C1-C2) |
कंस्ट्रेंट
- (C1)
shape(result) = shape(operand)
. - (C2)
baseline_element_type(result)
को इस तरह परिभाषित किया गया है:- अगर
is_complex(operand)
है, तोcomplex_element_type(element_type(operand))
. - अगर ऐसा नहीं है, तो
baseline_element_type(operand)
.
- अगर
उदाहरण
// %operand: [-2, 0, 2]
%result = "stablehlo.abs"(%operand) : (tensor<3xi32>) -> tensor<3xi32>
// %result: [2, 0, 2]
जोड़ें
सिमैंटिक
दो टेंसर lhs
और rhs
को एलिमेंट के हिसाब से जोड़ता है और इससे एक result
टेंसर बनता है. एलिमेंट टाइप के आधार पर, ये काम किए जाते हैं:
- बूलियन के लिए: लॉजिकल OR.
- पूर्णांक के लिए: पूर्णांक जोड़ना.
- फ़्लोट के लिए: IEEE-754 से
addition
. - कॉम्प्लेक्स नंबर के लिए: कॉम्प्लेक्स नंबर जोड़ना.
- क्वांटाइज़्ड टाइप के लिए:
dequantize_op_quantize(add, lhs, rhs, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
टेंसर या क्वांटाइज़ किया गया टेंसर | (C1-C6) |
(I2) | rhs |
टेंसर या क्वांटाइज़ किया गया टेंसर | (C1-C5), (C7) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या क्वांटाइज़ किया गया टेंसर | (C1-C7) |
कंस्ट्रेंट
- अगर इस संक्रिया में बिना मात्रा वाले टेंसर का इस्तेमाल किया गया है, तो:
- (C1)
type(lhs) = type(rhs) = type(result)
.
- (C1)
- अगर इस ऑपरेशन में क्वांटाइज़्ड टेंसर का इस्तेमाल किया गया है:
- (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) अगर
is_per_axis_quantized(lhs)
है, तोquantization_dimension(lhs) = quantization_dimension(result)
है. - (C7) अगर
is_per_axis_quantized(rhs)
है, तोquantization_dimension(rhs) = quantization_dimension(result)
है.
- (C2)
उदाहरण
// %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
सिमैंटिक्स
यह पक्का करता है कि inputs
को बनाने वाले ऑपरेशन, result
पर आधारित किसी भी ऑपरेशन से पहले लागू हों. इस ऑपरेशन को लागू करने से कुछ नहीं होता. यह सिर्फ़ result
से inputs
तक डेटा डिपेंडेंसी सेट अप करने के लिए मौजूद है.
इनपुट
लेबल | नाम | टाइप |
---|---|---|
(I1) | inputs |
token की वैरिएड संख्या |
आउटपुट
नाम | टाइप |
---|---|
result |
token |
उदाहरण
// %input0: !stablehlo.token
// %input1: !stablehlo.token
%result = "stablehlo.after_all"(%input0, %input1) : (!stablehlo.token, !stablehlo.token) -> !stablehlo.token
all_gather
सिमैंटिक
StableHLO प्रोसेस ग्रिड के हर प्रोसेस ग्रुप में, all_gather_dim
के साथ हर प्रोसेस के operands
टेंसर की वैल्यू को जोड़ता है और results
टेंसर बनाता है.
यह ऑपरेशन, StableHLO प्रोसेस ग्रिड को process_groups
में बांटता है, जिसे इस तरह परिभाषित किया गया है:
cross_replica(replica_groups)
अगरchannel_id <= 0 and use_global_device_ids = false
है, तो.cross_replica_and_partition(replica_groups)
अगरchannel_id > 0 and use_global_device_ids = false
है, तो.flattened_ids(replica_groups)
अगरchannel_id > 0 and use_global_device_ids = true
है, तो.
इसके बाद, हर process_group
में:
process_group
में सभीreceiver
के लिएoperands...@receiver = [operand@sender for sender in process_group]
.process_group
में सभी के लिएresults...@process = concatenate(operands...@process, all_gather_dim)
process
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operands |
वैरिएबल संख्या वाले टेंसर या हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर | (C1), (C6) |
(I2) | all_gather_dim |
si64 टाइप का कॉन्स्टेंट |
(C1), (C6) |
(I3) | replica_groups |
si64 टाइप का दो डाइमेंशन वाला टेंसर कॉन्सटेंट |
(सी2-सी4) |
(आई4) | channel_id |
si64 टाइप का कॉन्स्टेंट |
(सी5) |
(I5) | use_global_device_ids |
i1 टाइप का कॉन्स्टेंट |
(C5) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
results |
वैरिएबल संख्या वाले टेंसर या हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर | (सी6) |
कंस्ट्रेंट
- (C1)
0 <= all_gather_dim < rank(operands...)
. - (C2)
is_unique(replica_groups)
. - (C3)
size(replica_groups)
की परिभाषा इस तरह दी गई है:num_replicas
, अगरcross_replica
का इस्तेमाल किया गया हो.num_replicas
, अगरcross_replica_and_partition
का इस्तेमाल किया गया हो.num_processes
, अगरflattened_ids
का इस्तेमाल किया गया हो.
- (C4)
0 <= replica_groups < size(replica_groups)
. - (C5) अगर
use_global_device_ids = true
है, तोchannel_id > 0
है. - (C6)
type(results...) = type(operands...)
इनके अलावा:dim(results..., all_gather_dim) = dim(operands..., all_gather_dim) * dim(process_groups, 1)
.
उदाहरण
// 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
सिमैंटिक्स
StableHLO प्रोसेस ग्रिड में मौजूद प्रोसेस ग्रुप के हर प्रोसेस ग्रुप में, हर प्रोसेस से operands
टेंसर की वैल्यू पर रिडक्शन फ़ंक्शन computation
लागू करता है और results
टेंसर बनाता है.
यह ऑपरेशन, StableHLO प्रोसेस ग्रिड को process_groups
में बांटता है, जिसे इस तरह परिभाषित किया गया है:
cross_replica(replica_groups)
अगरchannel_id <= 0 and use_global_device_ids = false
है, तो.cross_replica_and_partition(replica_groups)
अगरchannel_id > 0 and use_global_device_ids = false
है, तो.flattened_ids(replica_groups)
अगरchannel_id > 0 and use_global_device_ids = true
है, तो.
इसके बाद, हर process_group
में:
results...@process[result_index] = exec(schedule)
कुछ बाइनरी ट्री के लिएschedule
जहां:exec(node)
=computation(exec(node.left), exec(node.right))
.exec(leaf)
=leaf.value
.
schedule
, लागू करने के तरीके से तय की गई बाइनरी ट्री है, जिसका क्रम से traversalto_destination_type(operands...@process_group...[result_index], type(func_inputs(computation)[0]))
है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operands |
वैरिएबल संख्या वाले टेंसर या हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर | (C5), (C6) |
(I2) | replica_groups |
si64 टाइप के एक-आयामी टेंसर कॉन्सटेंट की वैरिएडिक संख्या |
(C1-C3) |
(I3) | channel_id |
si64 टाइप का कॉन्स्टेंट |
(C4) |
(I4) | use_global_device_ids |
i1 टाइप का कॉन्स्टेंट |
(C4) |
(I5) | computation |
फ़ंक्शन | (C5) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
results |
वैरिएबल संख्या वाले टेंसर या हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर | (C6-C7) |
कंस्ट्रेंट
- (C1)
is_unique(replica_groups)
. - (C2)
size(replica_groups)
की परिभाषा इस तरह दी गई है:num_replicas
, अगरcross_replica
का इस्तेमाल किया गया हो.num_replicas
, अगरcross_replica_and_partition
का इस्तेमाल किया गया हो.num_processes
, अगरflattened_ids
का इस्तेमाल किया गया हो.
- (C3)
0 <= replica_groups < size(replica_groups)
. - (C4) अगर
use_global_device_ids = true
है, तोchannel_id > 0
है. - (C5)
computation
का टाइप(tensor<E>, tensor<E>) -> (tensor<E>)
है, जहांis_promotable(element_type(operand), E)
. - (C6)
shape(results...) = shape(operands...)
. - (C7)
element_type(results...) = E
.
उदाहरण
// 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
सिमैंटिक
StableHLO प्रोसेस ग्रिड में हर प्रोसेस ग्रुप में, split_dimension
के साथ operands
टेंसर की वैल्यू को अलग-अलग हिस्सों में बांटता है. साथ ही, अलग किए गए हिस्सों को प्रोसेस के बीच में अलग-अलग जगहों पर डालता है. इसके बाद, अलग-अलग जगहों पर डाले गए हिस्सों को concat_dimension
के साथ जोड़ता है और results
टेंसर बनाता है.
यह कार्रवाई StableHLO प्रोसेस ग्रिड को process_groups
में बांट देती है. इसकी जानकारी इस तरह से दी गई है:
- अगर
channel_id <= 0
है, तोcross_replica(replica_groups)
. - अगर
channel_id > 0
है, तोcross_partition(replica_groups)
.
इसके बाद, हर process_group
में:
process_group
में सभीsender
के लिएsplit_parts...@sender = split(operands...@sender, split_count, split_dimension)
.scattered_parts...@receiver = [split_parts...@sender[receiver_index] for sender in process_group]
जहांreceiver_index = process_group.index(receiver)
.results...@process = concatenate(scattered_parts...@process, concat_dimension)
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operands |
वैरिएबल संख्या वाले टेंसर या हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर | (C1-C3), (C9) |
(I2) | split_dimension |
si64 टाइप का कॉन्स्टेंट |
(C1), (C2), (C9) |
(I3) | concat_dimension |
si64 टाइप का कॉन्स्टेंट |
(C3), (C9) |
(I4) | split_count |
si64 टाइप का कॉन्स्टेंट |
(C2), (C4), (C8), (C9) |
(I5) | replica_groups |
si64 टाइप का दो डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C5-C8) |
(I6) | channel_id |
si64 टाइप का कॉन्स्टेंट |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
results |
वैरिएबल संख्या वाले टेंसर या हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर | (C9) |
कंस्ट्रेंट
- (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)
को इस तरह परिभाषित किया गया है:num_replicas
, अगरcross_replica
का इस्तेमाल किया गया हो.- अगर
cross_partition
का इस्तेमाल किया जाता है, तोnum_partitions
.
- (C7)
0 <= replica_groups < size(replica_groups)
. - (C8)
dim(replica_groups, 1) = split_count
. - (C9)
type(results...) = type(operands...)
, अगरsplit_dimension != concat_dimension
:dim(results..., split_dimension) = dim(operands..., split_dimension) / split_count
.dim(results..., concat_dimension) = dim(operands..., concat_dimension) * split_count
.
उदाहरण
// 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]]
और
सिमैंटिक्स
यह दो टेंसर lhs
और rhs
के एलिमेंट के हिसाब से AND फ़ंक्शन करता है और result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- बूलियन के लिए: लॉजिकल ऐंड.
- पूर्णांकों के लिए: बिटवाइज़ AND.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
बूलियन या पूर्णांक टाइप का टेंसर | (सी1) |
(I2) | rhs |
बूलियन या इंटेजर टाइप का टेंसर | (सी1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
बूलियन या इंटेजर टाइप का टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
type(lhs) = type(rhs) = type(result)
.
उदाहरण
// %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
सिमैंटिक्स
lhs
और rhs
टेंसर पर, एलिमेंट के हिसाब से atan2 ऑपरेशन करता है और एक
result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- फ़्लोट के लिए: IEEE-754 से
atan2
. - कॉम्प्लेक्स नंबर के लिए: complex atan2.
- क्वांटाइज़ किए गए टाइप के लिए:
dequantize_op_quantize(atan2, lhs, rhs, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
(I2) | rhs |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
.
उदाहरण
// %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
सिमैंटिक
grad_output
से batch_norm_training
बैकप्रोपगेट करने वाले कई इनपुट के ग्रेडिएंट का हिसाब लगाता है और grad_operand
, grad_scale
, और grad_offset
टेन्सर बनाता है. ज़्यादा औपचारिक तौर पर, इस ऑपरेशन को मौजूदा StableHLO ऑपरेशन के डिकंपोज़िशन के तौर पर दिखाया जा सकता है. इसके लिए, Python सिंटैक्स का इस्तेमाल इस तरह किया जा सकता है:
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
संख्या में दिखाए जा सकने वाले डेटा टाइप के लिए, 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))
करता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1-C3), (C5) |
(I2) | scale |
फ़्लोटिंग-पॉइंट या हर टेंसर की क्वांटाइज़्ड टाइप का 1-डाइमेंशन टेंसर | (C2), (C4), (C5) |
(I3) | mean |
फ़्लोटिंग-पॉइंट या हर टेंसर के हिसाब से क्वांटाइज़ किए गए टाइप का एक डाइमेंशन वाला टेंसर | (C2), (C4) |
(I4) | variance |
फ़्लोटिंग-पॉइंट या हर टेंसर के हिसाब से क्वांटाइज़ किए गए टाइप का एक डाइमेंशन वाला टेंसर | (C2), (C4) |
(I5) | grad_output |
फ़्लोटिंग-पॉइंट टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (C2), (C3) |
(I6) | epsilon |
f32 टाइप का कॉन्स्टेंट |
|
(I7) | feature_index |
si64 टाइप का कॉन्स्टेंट |
(C1), (C5) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
grad_operand |
फ़्लोटिंग-पॉइंट टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C2), (C3) |
grad_scale |
फ़्लोटिंग-पॉइंट या हर टेंसर के हिसाब से क्वांटाइज़ किए गए टाइप का एक डाइमेंशन वाला टेंसर | (C2), (C4) |
grad_offset |
फ़्लोटिंग-पॉइंट या हर टेंसर के हिसाब से क्वांटाइज़ किए गए टाइप का एक डाइमेंशन वाला टेंसर | (C2), (C4) |
कंस्ट्रेंट
- (C1)
0 <= feature_index < rank(operand)
. - (C2)
operand
,scale
,mean
,variance
,grad_output
,grad_operand
,grad_scale
, औरgrad_offset
काbaseline_element_type
एक ही है. - (C3)
operand
,grad_output
, औरgrad_operand
का आकार एक जैसा है. - (C4)
scale
,mean
,variance
,grad_scale
, औरgrad_offset
का आकार एक जैसा है. - (C5)
size(scale) = dim(operand, feature_index)
.
उदाहरण
// %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
सिमैंटिक्स
feature_index
डाइमेंशन को छोड़कर, सभी डाइमेंशन में operand
टेंसर को सामान्य बनाता है और result
टेंसर बनाता है. ज़्यादा औपचारिक तौर पर, इस ऑपरेशन को मौजूदा StableHLO ऑपरेशन के डिकंपोज़िशन के तौर पर दिखाया जा सकता है. इसके लिए, Python सिंटैक्स का इस्तेमाल इस तरह किया जा सकता है:
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)
संख्या में दिखाए जा सकने वाले डेटा टाइप के लिए, 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))
करता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1-C7) |
(I2) | scale |
फ़्लोटिंग-पॉइंट या हर टेंसर की क्वांटाइज़्ड टाइप का 1-डाइमेंशन टेंसर | (C2), (C3) |
(I3) | offset |
फ़्लोटिंग-पॉइंट या हर टेंसर के हिसाब से क्वांटाइज़ किए गए टाइप का एक डाइमेंशन वाला टेंसर | (C2), (C4) |
(I4) | mean |
फ़्लोटिंग-पॉइंट या हर टेंसर के हिसाब से क्वांटाइज़ किए गए टाइप का एक डाइमेंशन वाला टेंसर | (C5) |
(I5) | variance |
फ़्लोटिंग-पॉइंट या हर टेंसर के हिसाब से क्वांटाइज़ किए गए टाइप का एक डाइमेंशन वाला टेंसर | (C2), (C6) |
(I6) | epsilon |
f32 टाइप का कॉन्स्टेंट |
|
(I7) | feature_index |
si64 टाइप का कॉन्स्टेंट |
(C1), (C3-C6) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C2), (C7) |
कंस्ट्रेंट
- (C1)
0 <= feature_index < rank(operand)
. - (C2)
operand
,scale
,offset
,mean
,variance
, औरresult
काbaseline_element_type
एक जैसा है. - (C3)
size(scale) = dim(operand, feature_index)
. - (C4)
size(offset) = dim(operand, feature_index)
. - (C5)
size(mean) = dim(operand, feature_index)
. - (C6)
size(variance) = dim(operand, feature_index)
. - (C7)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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
सिमैंटिक्स
feature_index
डाइमेंशन को छोड़कर, सभी डाइमेंशन के माध्य और वैरियंस कैलकुलेट करता है. साथ ही, output
, batch_mean
, और batch_var
टेंसर बनाने वाले operand
टेंसर को नॉर्मलाइज़ करता है. ज़्यादा औपचारिक तौर पर, इस ऑपरेशन को मौजूदा StableHLO ऑपरेशन के डिकंपोज़िशन के तौर पर दिखाया जा सकता है. इसके लिए, Python सिंटैक्स का इस्तेमाल इस तरह किया जा सकता है:
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
संख्या में दिखाए जा सकने वाले डेटा टाइप के लिए, 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))
करता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
(I2) | scale |
फ़्लोटिंग-पॉइंट या हर टेंसर के हिसाब से क्वांटाइज़ किया गया 1-डाइमेंशनल टेंसर | (C2), (C3) |
(I3) | offset |
फ़्लोटिंग-पॉइंट या हर टेंसर के हिसाब से क्वांटाइज़ किया गया 1-डाइमेंशनल टेंसर | (C2), (C4) |
(I4) | epsilon |
f32 टाइप का कॉन्स्टेंट |
(C1), (C3-C6) |
(I5) | feature_index |
si64 टाइप का कॉन्स्टेंट |
(C1), (C3-C6) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
output |
फ़्लोटिंग-पॉइंट टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (C7) |
batch_mean |
फ़्लोटिंग-पॉइंट या हर टेंसर के हिसाब से क्वांटाइज़ किया गया 1-डाइमेंशनल टेंसर | (C2), (C5) |
batch_var |
फ़्लोटिंग-पॉइंट या हर टेंसर की क्वांटाइज़ किए गए 1-डाइमेंशन टेंसर | (C2), (C6) |
कंस्ट्रेंट
- (C1)
0 <= feature_index < rank(operand)
. - (C2)
operand
,scale
,offset
,batch_mean
,batch_var
, औरoutput
में एक हीbaseline_element_type
है. - (C3)
size(scale) = dim(operand, feature_index)
. - (C4)
size(offset) = dim(operand, feature_index)
. - (C5)
size(batch_mean) = dim(operand, feature_index)
. - (C6)
size(batch_var) = dim(operand, feature_index)
. - (C7)
baseline_type(output) = baseline_type(operand)
.
उदाहरण
// %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
सिमैंटिक्स
operand
टेंसर पर बिटकास्ट कार्रवाई करता है और result
टेंसर बनाता है, जहां पूरे operand
टेंसर के बिट, result
टेंसर के टाइप का इस्तेमाल करके फिर से समझे जाते हैं.
ज़्यादा औपचारिक तौर पर, E = element_type(operand)
, E' = element_type(result)
, और R = rank(operand)
के लिए:
- अगर
num_bits(E') < num_bits(E)
,bits(result[i0, ..., iR-1, :]) = bits(operand[i0, ..., iR-1])
. - अगर
num_bits(E') > num_bits(E)
,bits(result[i0, ..., iR-2]) = bits(operand[i0, ..., iR-2, :])
. - अगर
num_bits(E') = num_bits(E)
,bits(result[i0, ..., iR-1]) = bits(operand[i0, ..., iR-1])
.
bits
, किसी दी गई वैल्यू को मेमोरी में दिखाता है. इसका व्यवहार, लागू करने के तरीके के हिसाब से तय होता है, क्योंकि टेंसर के सटीक तौर पर दिखाए जाने का तरीका, लागू करने के तरीके के हिसाब से तय होता है. साथ ही, एलिमेंट टाइप के सटीक तौर पर दिखाए जाने का तरीका भी, लागू करने के तरीके के हिसाब से तय होता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेंसर या क्वांटाइज़ किया गया टेंसर | (C1-C2) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या क्वांटाइज़ किया गया टेंसर | (C1-C2) |
कंस्ट्रेंट
- (C1)
E = is_quantized(operand) ? storage_type(operand) : element_type(operand)
,E' = is_quantized(result) ? storage_type(result) : element_type(result)
, औरR = rank(operand)
के लिए:- अगर
num_bits(E') = num_bits(E)
,shape(result) = shape(operand)
. - अगर
num_bits(E') < num_bits(E)
: rank(result) = R + 1
.dim(result, i) = dim(operand, i)
के लिए सभी0 <= i < R
.dim(result, R) * num_bits(E') = num_bits(E)
.- अगर
num_bits(E') > num_bits(E)
: rank(result) = R - 1
.dim(result, i) = dim(operand, i)
के लिए सभी0 <= i < R
.dim(operand, R - 1) * num_bits(E) = num_bits(E')
.
- अगर
- (C2) अगर
is_complex(operand) or is_complex(result)
है, तोis_complex(operand) and is_complex(result)
.
उदाहरण
// %operand: 0x0123456789ABCDEF
%result = "stablehlo.bitcast_convert"(%operand) : (tensor<f64>) -> tensor<4xf16>
// %result: [0xCDEF, 0x89AB, 0x4567, 0x0123] // little-endian representation
broadcast_in_dim
सिमैंटिक्स
operand
टेंसर में मौजूद डेटा का डुप्लीकेट बनाकर, किसी इनपुट टेंसर के डाइमेंशन और/या रैंक को बड़ा करता है. साथ ही, result
टेंसर बनाता है. ज़्यादा औपचारिक तौर पर,
result[result_index] = operand[operand_index]
जहां axes(operand)
में सभी d
के लिए:
- अगर
dim(operand, d) = 1
है, तोoperand_index[d] = 0
. operand_index[d] = result_index[broadcast_dimensions[d]]
अन्य मामलों में.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेंसर या क्वांटाइज़्ड टेंसर | (C1-C2), (C5-C6) |
(I2) | broadcast_dimensions |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C2-C6) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या क्वांटाइज़्ड टेंसर | (C1), (C3), (C5-C6) |
कंस्ट्रेंट
- (C1)
element_type(result)
देने वाली कंपनी:- अगर
!is_per_axis_quantized(operand)
है, तोelement_type(operand)
. element_type(operand)
, हालांकिquantization_dimension(operand)
,scales(operand)
, औरzero_points(operand)
,quantization_dimension(result)
,scales(result)
, औरzero_points(result)
से अलग हो सकते हैं.
- अगर
- (C2)
size(broadcast_dimensions) = rank(operand)
. - (C3)
0 <= broadcast_dimensions < rank(result)
. - (C4)
is_unique(broadcast_dimensions)
. - (C5)
axes(operand)
में मौजूद सभीd
के लिए:dim(operand, d) = 1
याdim(operand, d) = dim(result, broadcast_dimensions[d])
.
- (C6) अगर
is_per_axis_quantized(result)
:quantization_dimension(result) = broadcast_dimensions[quantization_dimension(operand)]
.- अगर
dim(operand, quantization_dimension(operand)) = 1
है, तोscales(result)[i] = scales(operand)[0] and zero_points(result)[i] = zero_points(operand)[0] for i in range(dim(result, quantization_dimension(result)))
.
उदाहरण
// %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]
// ]
// ]
केस
सिमैंटिक
index
की वैल्यू के आधार पर, branches
में से किसी एक फ़ंक्शन को चलाकर आउटपुट जनरेट करता है. ज़्यादा औपचारिक तौर पर, result = selected_branch()
जहां:
- अगर
0 <= index < size(branches)
है, तोselected_branch = branches[index]
. - अगर ऐसा नहीं है, तो
selected_branch = branches[-1]
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | index |
si32 टाइप का 0-डाइमेंशनल टेंसर |
|
(I2) | branches |
फ़ंक्शन की वैरिएडिक संख्या | (C1-C4) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
results |
टेंसर की वैरायडिक संख्या, क्वांटाइज़ किए गए टेंसर या टोकन | (सी4) |
कंस्ट्रेंट
- (C1)
0 < size(branches)
. - (C2)
input_types(branches...) = []
. - (C3)
same(output_types(branches...))
. - (C4)
type(results...) = output_types(branches[0])
.
उदाहरण
// %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]
सीबीआरटी
सिमैंटिक
operand
टेंसर के हर एलिमेंट पर घनमूल का ऑपरेशन करता है और result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- फ़्लोट के लिए: IEEE-754 से
rootn(x, 3)
. - कॉम्प्लेक्स नंबर के लिए: कॉम्प्लेक्स क्यूब रूट.
- क्वांटाइज़ किए गए डेटा टाइप के लिए:
dequantize_op_quantize(cbrt, operand, type(result))
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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
सिमैंटिक्स
operand
टेंसर की एलिमेंट के हिसाब से सेल करता है और result
टेंसर बनाता है.
IEEE-754 के स्पेसिफ़िकेशन के हिसाब से, roundToIntegralTowardPositive
ऑपरेशन लागू करता है. क्वांटाइज़्ड टाइप के लिए, dequantize_op_quantize(ceil, operand, type(result))
की मदद से काम करता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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]
चोलस्की
सिमैंटिक्स
मैट्रिक्स के बैच का चॉल्स्की डिकंपोज़िशन कैलकुलेट करता है.
ज़्यादा औपचारिक तौर पर, index_space(result)
में मौजूद सभी i
के लिए,
result[i0, ..., iR-3, :, :]
, a[i0, ..., iR-3, :, :]
का कोलेस्की डिकंपोज़िशन है. यह एक लोअर-ट्राएंगल (अगर lower
true
है) या अपर-ट्राएंगल (अगर lower
false
है) मैट्रिक्स के तौर पर होता है.
इसके उलट, सटीक ऊपरी त्रिभुज या सटीक निचले त्रिभुज में मौजूद आउटपुट वैल्यू, लागू करने के तरीके के हिसाब से तय होती हैं.
अगर i
मौजूद है, जहां इनपुट मैट्रिक्स हर्मिटियन पॉज़िटिव-डेफ़िनिट मैट्रिक्स नहीं है, तो बिहेवियर की जानकारी नहीं होती है.
संख्या में दिखाए जा सकने वाले डेटा टाइप के लिए, dequantize_op_quantize(lambda operand: cholesky(operand, lower), a, type(result))
करता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | a |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1-C3) |
(I2) | lower |
i1 टाइप का 0-डाइमेंशनल टेंसर कॉन्सटेंट |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
baseline_type(a) = baseline_type(result)
. - (C2)
2 <= rank(a)
. - (C3)
dim(a, -2) = dim(a, -1)
.
उदाहरण
// %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]
// ]
क्लैंप
सिमैंटिक
operand
टेंसर के हर एलिमेंट को कम से कम और ज़्यादा से ज़्यादा वैल्यू के बीच जोड़ता है और result
टेंसर बनाता है. औपचारिक रूप से, result[result_index] =
minimum(maximum(operand[result_index], min_element), max_element)
,
जहां min_element = rank(min) = 0 ? min[] : min[result_index]
,
max_element = rank(max) = 0 ? max[] : max[result_index]
. क्वांटिफ़ाइड टाइप के लिए,
dequantize_op_quantize(clamp, min, operand, max, type(result))
की तरह काम करता है.
कॉम्प्लेक्स नंबर का क्रम लागू करने में हैरान करने वाला सिमैंटिक शामिल है. इसलिए, भविष्य में हम इस ऑपरेशन के लिए कॉम्प्लेक्स नंबर (#560) का सपोर्ट हटाने की योजना बना रहे हैं.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | min |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1), (C3) |
(I2) | operand |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (सी1-सी4) |
(I3) | max |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C2), (C3) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C4) |
कंस्ट्रेंट
- (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)
.
उदाहरण
// %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
सिमैंटिक्स
StableHLO प्रोसेस ग्रिड के हर प्रोसेस ग्रुप में, सोर्स प्रोसेस से टारगेट प्रोसेस में operand
टेंसर की वैल्यू भेजें और result
टेंसर बनाएं.
यह ऑपरेशन, StableHLO प्रोसेस ग्रिड को process_groups
में बांटता है, जिसे इस तरह परिभाषित किया गया है:
- अगर
channel_id <= 0
है, तोcross_replica(replica_groups)
. - अगर
channel_id > 0
है, तोcross_partition(replica_groups)
.
इसके बाद, result@process
को:
operand@process_groups[i, 0]
अगर कोई ऐसाi
मौजूद है कि प्रोसेसprocess_groups[i]
में है.- अगर ऐसा नहीं करना है, तो
broadcast_in_dim(constant(is_quantized(result) ? quantize(0, element_type(result)) : 0, element_type(result)), [], type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C3) |
(I2) | replica_groups |
si64 टाइप के एक-आयामी टेंसर कॉन्सटेंट की वैरिएडिक संख्या |
(C1), (C2) |
(I3) | channel_id |
si64 टाइप का कॉन्स्टेंट |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C3) |
कंस्ट्रेंट
- (C1)
is_unique(replica_groups)
. - (C2)
0 <= replica_groups < N
जहांN
को इस तरह परिभाषित किया गया है:- अगर
cross_replica
का इस्तेमाल किया जाता है, तोnum_replicas
. num_partitions
, अगरcross_partition
का इस्तेमाल किया गया हो.
- अगर
- (C3)
type(result) = type(operand)
.
उदाहरण
// 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
सिमैंटिक्स
StableHLO प्रोसेस ग्रिड में मौजूद हर प्रोसेस ग्रुप के अंदर, operand
टेंसर की वैल्यू को सोर्स प्रोसेस से टारगेट प्रोसेस में भेजता है और result
टेंसर बनाता है.
यह ऑपरेशन, StableHLO प्रोसेस ग्रिड को process_groups
में बांटता है, जिसे इस तरह परिभाषित किया गया है:
- अगर
channel_id <= 0
है, तोcross_replica(source_target_pairs)
. - अगर
channel_id > 0
है, तोcross_partition(source_target_pairs)
.
इसके बाद, result@process
को:
operand@process_groups[i, 0]
, अगर कोई ऐसाi
मौजूद है किprocess_groups[i, 1] = process
.broadcast_in_dim(constant(is_quantized(result) ? quantize(0, element_type(result)) : 0, element_type(result)), [], type(result))
अन्य मामलों में.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C5) |
(I2) | source_target_pairs |
si64 टाइप का दो डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C1-C4) |
(I3) | channel_id |
si64 टाइप का कॉन्स्टेंट |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (C1) |
कंस्ट्रेंट
- (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
, जहांN
का मतलब इस तरह है:num_replicas
, अगरcross_replica
का इस्तेमाल किया गया हो.- अगर
cross_partition
का इस्तेमाल किया जाता है, तोnum_partitions
.
- (C5)
type(result) = type(operand)
.
उदाहरण
// 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]]
compare
सिमैंटिक्स
comparison_direction
और compare_type
के मुताबिक, lhs
और rhs
टेंसर की एलिमेंट के हिसाब से तुलना करता है और result
टेंसर बनाता है.
comparison_direction
और compare_type
की वैल्यू के लिए ये मतलब तय किए गए हैं:
बूलियन और इंटीजर टाइप के एलिमेंट के लिए:
EQ
:lhs = rhs
.NE
:lhs != rhs
.GE
:lhs >= rhs
.GT
:lhs > rhs
.LE
:lhs <= rhs
.LT
:lhs < rhs
.
compare_type = FLOAT
वाले फ़्लोटिंग-पॉइंट एलिमेंट के लिए, op
इन IEEE-754 कार्रवाइयों को लागू करता है:
EQ
:compareQuietEqual
.NE
:compareQuietNotEqual
.GE
:compareQuietGreaterEqual
.GT
:compareQuietGreater
.LE
:compareQuietLessEqual
.LT
:compareQuietLess
.
compare_type = TOTALORDER
वाले फ़्लोटिंग-पॉइंट एलिमेंट टाइप के लिए, ऑपरेटर, IEEE-754 से totalOrder
और compareQuietEqual
ऑपरेशन के कॉम्बिनेशन का इस्तेमाल करता है.
जटिल एलिमेंट टाइप के लिए, दिए गए comparison_direction
और compare_type
का इस्तेमाल करके (real, imag)
पेयर की लेक्सिकोग्राफ़िक तुलना की जाती है.
कॉम्प्लेक्स नंबर के लिए क्रम तय करने पर, कुछ आश्चर्यजनक सेमेटिक्स शामिल होते हैं. इसलिए, आने वाले समय में हम comparison_direction
के GE
, GT
, LE
या LT
होने पर, कॉम्प्लेक्स नंबर के लिए सहायता हटाने जा रहे हैं (#560).
संख्या में दिखाए जा सकने वाले डेटा टाइप के लिए, dequantize_compare(lhs, rhs,
comparison_direction)
करता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (C1-C3) |
(I2) | rhs |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1-C2) |
(I3) | comparison_direction |
EQ , NE , GE , GT , LE , और LT का enum |
|
(I4) | compare_type |
FLOAT , TOTALORDER , SIGNED , और UNSIGNED का enum |
(C3) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
बूलियन टाइप का टेंसर | (C2) |
कंस्ट्रेंट
- (C1)
baseline_element_type(lhs) = baseline_element_type(rhs)
. - (C2)
shape(lhs) = shape(rhs) = shape(result)
. - (C3)
compare_type
की परिभाषा इस तरह दी गई है:- अगर
is_signed_integer(element_type(lhs))
है, तोSIGNED
. - अगर
is_unsigned_integer(element_type(lhs)) or is_boolean(element_type(lhs))
है, तोUNSIGNED
. - अगर
is_float(element_type(lhs))
है, तोFLOAT
याTOTALORDER
. - अगर
is_complex(element_type(lhs))
है, तोFLOAT
.
- अगर
उदाहरण
// %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]
जटिल
सिमैंटिक्स
यह फ़ंक्शन, lhs
और rhs
जैसी रीयल और इमेजरी वैल्यू के पेयर से, कॉम्प्लेक्स वैल्यू में एलिमेंट-वाइज़ कन्वर्ज़न करता है. साथ ही, result
टेंसर बनाता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
f32 या f64 टाइप का टेंसर |
(C1-C3) |
(I2) | rhs |
f32 या f64 टाइप का टेंसर |
(सी1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
कॉम्प्लेक्स टाइप का टेंसर | (C2), (C3) |
कंस्ट्रेंट
- (C1)
type(lhs) = type(rhs)
. - (C2)
shape(result) = shape(lhs)
. - (C3)
element_type(result)
का टाइपcomplex<E>
है, जहांE = element_type(lhs)
है.
उदाहरण
// %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)]
मिश्रित
सिमैंटिक
यह StableHLO के अन्य ऑपरेशन से बना ऑपरेशन होता है. इसमें inputs
और composite_attributes
को लेकर results
बनाया जाता है. ऑपरेशन के सिमेंटिक्स को decomposition
एट्रिब्यूट से लागू किया जाता है. प्रोग्राम के सेमेंटेटिक्स में बदलाव किए बिना, composite
ऑपरेशन को उसके डिकंपोज़िशन से बदला जा सकता है. जिन मामलों में डेकंपोज़िशन को इनलाइन करने से, ऑपरेटर के लिए एक ही सेमेंटेटिक्स नहीं मिलता, उनमें custom_call
का इस्तेमाल करें.
version
फ़ील्ड (डिफ़ॉल्ट रूप से 0
पर सेट होता है) का इस्तेमाल, किसी कंपोजिट के सेमैंटिक में बदलाव होने पर जानकारी देने के लिए किया जाता है.
इनपुट
लेबल | नाम | टाइप |
---|---|---|
(I1) | inputs |
वैरिएबल वैल्यू की संख्या |
(I2) | name |
string टाइप का कॉन्स्टेंट |
(I3) | composite_attributes |
एट्रिब्यूट की डिक्शनरी |
(आई4) | decomposition |
string टाइप का कॉन्स्टेंट |
(I5) | version |
si32 टाइप का कॉन्स्टेंट |
आउटपुट
नाम | टाइप |
---|---|
results |
वैरिएबल वैल्यू की संख्या |
कंस्ट्रेंट
- (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)
उदाहरण
%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>
जोड़ें
सिमैंटिक्स
inputs
को dimension
डाइमेंशन के साथ उसी क्रम में जोड़ता है जिस क्रम में दिया गया है. साथ ही, यह result
टेंसर बनाता है. ज़्यादा औपचारिक तौर पर,
result[i0, ..., id, ..., iR-1] = inputs[k][i0, ..., kd, ..., iR-1]
, जहां:
id = d0 + ... + dk-1 + kd
.d
,dimension
के बराबर है. साथ ही,d0
, ...inputs
केd
वें डाइमेंशन के साइज़ हैं.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | inputs |
वैरिएबल संख्या वाले टेंसर या हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर | (C1-C6) |
(I2) | dimension |
si64 टाइप का कॉन्स्टेंट |
(C2), (C4), (C6) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C5-C6) |
कंस्ट्रेंट
- (C1)
same(element_type(inputs...))
. - (C2)
dim(inputs..., dimension)
को छोड़कर,same(shape(inputs...))
. - (C3)
0 < size(inputs)
. - (C4)
0 <= dimension < rank(inputs[0])
. - (C5)
element_type(result) = element_type(inputs[0])
. - (C6)
shape(result) = shape(inputs[0])
, सिवाय:dim(result, dimension) = dim(inputs[0], dimension) + ...
.
उदाहरण
// %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]]
कॉन्सटेंट
सिमैंटिक
कॉन्स्टेंट value
से, output
टेंसर बनाता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | value |
कॉन्सटेंट | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
output |
टेंसर या क्वांटाइज़ किया गया टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
type(value) = type(output)
.
उदाहरण
%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]]
ग्राहक में बदलने वाले
सिमैंटिक
operand
टेंसर पर, एलिमेंट के हिसाब से एक एलिमेंट टाइप से दूसरे एलिमेंट टाइप में बदलाव करता है और result
टेंसर बनाता है.
बुलियन-टू-ऐनी-सपोर्टेड-टाइप कन्वर्ज़न के लिए, वैल्यू false
को शून्य में बदल दिया जाता है और वैल्यू true
को एक में बदल दिया जाता है. any-supported-type-to-boolean कन्वर्ज़न के लिए, शून्य वैल्यू को false
में बदला जाता है और शून्य से बड़ी वैल्यू को true
में बदला जाता है. जटिल टाइप के लिए, यह सुविधा कैसे काम करती है, यह जानने के लिए नीचे देखें.
पूर्णांक से पूर्णांक, पूर्णांक से फ़्लोटिंग-पॉइंट या फ़्लोटिंग-पॉइंट से फ़्लोटिंग-पॉइंट वाले कन्वर्ज़न के लिए, अगर सोर्स वैल्यू को डेस्टिनेशन टाइप में पूरी तरह से दिखाया जा सकता है, तो नतीजा वैल्यू उसी तरह दिखती है. इसके अलावा, इसकी सुविधा कैसे काम करेगी, यह तय नहीं किया गया है (#180).
फ़्लोटिंग-पॉइंट से पूर्णांक में बदलने वाले कन्वर्ज़न के लिए, भिन्नात्मक भाग को छोटा कर दिया जाता है. अगर छोटी की गई वैल्यू को डेस्टिनेशन टाइप में नहीं दिखाया जा सकता, तो व्यवहार अभी तय नहीं है (#180).
कॉम्प्लेक्स-टू-कॉम्प्लेक्स कन्वर्ज़न, रीयल और इमेजरी पार्ट को बदलने के लिए, फ़्लोटिंग-पॉइंट-टू-फ़्लोटिंग-पॉइंट कन्वर्ज़न के जैसे ही काम करते हैं.
कॉम्प्लेक्स से किसी अन्य टाइप और किसी अन्य टाइप से कॉम्प्लेक्स कन्वर्ज़न के लिए, सोर्स की काल्पनिक वैल्यू को अनदेखा किया जाता है या डेस्टिनेशन की काल्पनिक वैल्यू को शून्य कर दिया जाता है. रीयल पार्ट के कन्वर्ज़न के लिए, फ़्लोटिंग-पॉइंट कन्वर्ज़न का इस्तेमाल किया जाता है.
सिद्धांत रूप से, इस ऑपरेशन से डेक्वांटाइज़ेशन (क्विंटाइज़ किए गए टेंसर को सामान्य टेंसर में बदलना), क्विंटाइज़ेशन (सामान्य टेंसर को क्विंटाइज़ किए गए टेंसर में बदलना), और फिर से क्विंटाइज़ेशन (क्विंटाइज़ किए गए टेंसर के बीच बदलाव) किया जा सकता है. हालांकि, फ़िलहाल हमारे पास इसके लिए खास ऑपरेशन हैं - पहले इस्तेमाल के उदाहरण के लिए uniform_dequantize
और दूसरे और तीसरे इस्तेमाल के उदाहरणों के लिए uniform_quantize
. आने वाले समय में, इन दोनों ऑपरेशन को convert
(#1576) में मर्ज किया जा सकता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
shape(operand) = shape(result)
.
उदाहरण
// %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)]
कॉन्वोल्यूशन
सिमैंटिक्स
lhs
की विंडो और rhs
के स्लाइस के बीच डॉट प्रॉडक्ट का हिसाब लगाता है और result
बनाता है. नीचे दिए गए डायग्राम में, उदाहरण के ज़रिए दिखाया गया है कि result
में मौजूद एलिमेंट का हिसाब, lhs
और rhs
से कैसे लगाया जाता है.
lhs
की विंडो दिखाने के लिए, इनपुट को lhs
के हिसाब से फिर से फ़्रेम करें:
lhs_window_dimensions = lhs_shape(dim(lhs, input_batch_dimension), dim(rhs, kernel_spatial_dimensions), dim(lhs, input_feature_dimension))
.lhs_window_strides = lhs_shape(1, window_strides, 1)
.lhs_padding = lhs_shape([0, 0], padding, [0, 0])
.lhs_base_dilations = lhs_shape(1, lhs_dilation, 1)
.lhs_window_dilations = lhs_shape(1, rhs_dilation, 1)
.
रीफ़्रेमिंग के लिए, इन हेल्पर फ़ंक्शन का इस्तेमाल किया जाता है:
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]
जहांj[d] = i[permutation[d]]
.
अगर feature_group_count = 1
और batch_group_count = 1
है, तो index_space(dim(result, output_spatial_dimensions...))
में सभी output_spatial_index
के लिए, result[result_shape(:, output_spatial_index, :)] = dot_product
जहां:
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])
. ऐसा लगता है कि इस सुविधा का इस्तेमाल नहीं किया जा रहा है. इसलिए, हम आने वाले समय में इसे हटाने वाले हैं (#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])
.
अगर 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)
.
अगर 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)
.
क्वांटाइज़्ड टाइप के लिए, 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))
फ़ंक्शन इस्तेमाल करता है.
हाइब्रिड क्वांटाइज़ किए गए टाइप के लिए, 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)
करता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (C1), (C10-C11), (C14) (C25), (C27-C28), (C31-C32), (C34) |
(I2) | rhs |
टेंसर या क्वांटाइज़ किया गया टेंसर | (C1), (C14-C16), (C25), (C27-C29), (C31-C34) |
(I3) | window_strides |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C2-C3), (C25) |
(I4) | padding |
si64 टाइप का दो डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C4), (C25) |
(I5) | lhs_dilation |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C5-C6), (C25) |
(I6) | rhs_dilation |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C7-C8), (C25) |
(I7) | window_reversal |
i1 टाइप का 1-डाइमेंशन टेंसर कॉन्सटेंट |
(सी9) |
(I8) | input_batch_dimension |
si64 टाइप का कॉन्स्टेंट |
(C10), (C13), (C25) |
(I9) | input_feature_dimension |
si64 टाइप का कॉन्स्टेंट |
(C11), (C13-C14) |
(I10) | input_spatial_dimensions |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C12), (C13), (C25) |
(I11) | kernel_input_feature_dimension |
si64 टाइप का कॉन्स्टेंट |
(C14), (C18) |
(I12) | kernel_output_feature_dimension |
si64 टाइप का कॉन्स्टेंट |
(C15-C16), (C18), (C25), (C29) |
(I13) | kernel_spatial_dimensions |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C17-C18), (C25) |
(I14) | output_batch_dimension |
si64 टाइप का कॉन्स्टेंट |
(C20), (C25) |
(I15) | output_feature_dimension |
si64 टाइप का कॉन्स्टेंट |
(C20), (C25), (C30) |
(I16) | output_spatial_dimensions |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C19-C20), (C25) |
(I17) | feature_group_count |
si64 टाइप का कॉन्स्टेंट |
(C11), (C14), (C16), (C21), (C23) |
(I18) | batch_group_count |
si64 टाइप का कॉन्स्टेंट |
(C10), (C15), (C22), (C23), (C25) |
(I19) | precision_config |
DEFAULT , HIGH , और HIGHEST के लिए, वैरिएबल की संख्या |
(C24) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या क्वांटाइज़ किया गया टेंसर | (C25-C28), (C30), (C32-34) |
कंस्ट्रेंट
- (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) दिया गया
input_dimensions = [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]
:is_unique(input_dimensions)
.0 <= input_dimensions < N
.
- (C14)
dim(rhs, kernel_input_feature_dimension) = dim(lhs, input_feature_dimension) / feature_group_count
. - (C15)
dim(rhs, kernel_output_feature_dimension) % batch_group_count = 0
. - (C16)
dim(rhs, kernel_output_feature_dimension) % feature_group_count = 0
. - (C17)
size(kernel_spatial_dimensions) = N - 2
. - (C18)
kernel_dimensions = kernel_spatial_dimensions + [kernel_input_feature_dimension] + [kernel_output_feature_dimension]
को दिया गया:is_unique(kernel_dimensions)
.0 <= kernel_dimensions < N
.
- (C19)
size(output_spatial_dimensions) = N - 2
. - (C20) दिया गया
output_dimensions = [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]
:is_unique(output_dimensions)
.0 <= output_dimensions < N
.
- (C21)
0 < feature_group_count
. - (C22)
0 < batch_group_count
. - (C23)
feature_group_count = 1 or batch_group_count = 1
. - (C24)
size(precision_config) = 2
. - (C25)
dim(result, result_dim)
को इस तरह परिभाषित किया गया है:- अगर
result_dim = output_batch_dimension
है, तोdim(lhs, input_batch_dimension) / batch_group_count
. - अगर
result_dim = output_feature_dimension
है, तोdim(rhs, kernel_output_feature_dimension)
. num_windows
अन्य मामलों में, जहां: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
. - अगर ऑपरेशन में बिना क्वांटाइज़ किए गए टेंसर का इस्तेमाल किया जाता है, तो:
- (C27)
element_type(lhs) = element_type(rhs) = element_type(result)
.
- (C27)
- अगर इस ऑपरेशन में क्वांटाइज़्ड टेंसर का इस्तेमाल किया गया है:
- (C28)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
. - (C29) अगर
is_per_axis_quantized(rhs)
, तोquantization_dimension(rhs) = kernel_output_feature_dimension
. - (C30) अगर
is_per_axis_quantized(result)
है, तोquantization_dimension(result) = output_feature_dimension
. - अगर
is_quantized(lhs)
: - (C31)
storage_type(lhs) = storage_type(rhs)
. - (C32)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
. - (C33) अगर
is_per_tensor_quantized(rhs)
है, तोis_per_tensor_quantized(result)
. - अगर
!is_quantized(lhs)
: - (C34)
element_type(lhs) = expressed_type(rhs) = element_type(result)
.
- (C28)
उदाहरण
// %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]]
// ]]
कोज्या
सिमैंटिक्स
operand
टेंसर पर एलिमेंट के हिसाब से कोसाइन करता है और result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम किए जाते हैं:
- फ़्लोट के लिए: IEEE-754 से
cos
. - कॉम्प्लेक्स नंबर के लिए: कॉम्प्लेक्स कोसाइन.
- क्वांटाइज़ किए गए टाइप के लिए:
dequantize_op_quantize(cosine, operand, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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
सिमैंटिक्स
operand
टेन्सर में, शुरुआत में मौजूद शून्य बिट की संख्या को एलिमेंट के हिसाब से गिनता है और result
टेन्सर बनाता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
पूर्णांक टाइप का टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
पूर्णांक टाइप का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
type(operand) = type(result)
.
उदाहरण
// %operand: [[0, 1], [128, -1]]
%result = "stablehlo.count_leading_zeros"(%operand) : (tensor<2x2xi64>) -> tensor<2x2xi64>
// %result: [[64, 63], [56, 0]]
custom_call
सिमैंटिक्स
लागू करने के तरीके से तय किए गए ऑपरेशन call_target_name
को एन्कैप्सुलेट करता है, जो inputs
और called_computations
लेता है और results
बनाता है. has_side_effect
,
backend_config
, और api_version
का इस्तेमाल, लागू करने के तरीके से जुड़ा अतिरिक्त मेटाडेटा देने के लिए किया जा सकता है.
फ़िलहाल, इस कार्रवाई में मेटाडेटा का एक व्यवस्थित कलेक्शन मौजूद है. यह XA कंपाइलर में इसके वर्शन के अपने-आप हुए विकास को दिखाता है. आने वाले समय में, हम इस मेटाडेटा को एक जैसा बनाने जा रहे हैं (#741).
इनपुट
लेबल | नाम | टाइप |
---|---|---|
(I1) | inputs |
वैरिएबल वैल्यू की संख्या |
(I2) | call_target_name |
string टाइप का कॉन्स्टेंट |
(I3) | has_side_effect |
i1 टाइप का कॉन्स्टेंट |
(I4) | backend_config |
string टाइप का कॉन्स्टेंट या एट्रिब्यूट डिक्शनरी |
(I5) | api_version |
si32 टाइप का कॉन्स्टेंट |
(I6) | called_computations |
string टाइप की वैरिएबल संख्या |
आउटपुट
नाम | टाइप |
---|---|
results |
वैरिएबल वैल्यू की संख्या |
उदाहरण
%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>
विभाजन
सिमैंटिक
भाज्य lhs
और भाजक rhs
टेंसर का एलिमेंट के हिसाब से भाग करता है और result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- पूर्णांकों के लिए: पूर्णांक का भागफल, जो बीजगणितीय भागफल बनाता है और दशमलव के बाद वाले हिस्से को हटा देता है.
- फ़्लोट के लिए: IEEE-754 से
division
. - सम्मिश्र संख्याओं के लिए: जटिल विभाजन.
- क्वांटाइज़ किए गए डेटा टाइप के लिए:
dequantize_op_quantize(divide, lhs, rhs, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
पूर्णांक, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
(I2) | rhs |
इंटीजर, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
पूर्णांक, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
.
उदाहरण
// %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
सिमैंटिक्स
lhs
और rhs
के स्लाइस के बीच डॉट प्रॉडक्ट का हिसाब लगाता है और एक result
टेंसर बनाता है.
ज़्यादा औपचारिक तौर पर, result[result_index] = dot_product
, जहां:
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
जहांsize(result_batching_index) = size(lhs_batching_dimensions)
,size(result_lhs_index) = size(lhs_result_dimensions)
औरsize(result_rhs_index) = size(rhs_result_dimensions)
हैं.transposed_lhs = transpose(lhs, lhs_batching_dimensions + lhs_result_dimensions + lhs_contracting_dimensions)
.transposed_lhs_slice = slice(transposed_lhs, result_batching_index + result_lhs_index + [:, ..., :])
.reshaped_lhs_slice = reshape(transposed_lhs_slice, dims(lhs, lhs_contracting_dimensions))
.transposed_rhs = transpose(rhs, rhs_batching_dimensions + rhs_result_dimensions + rhs_contracting_dimensions)
.transposed_rhs_slice = slice(transposed_rhs, result_batching_index + result_rhs_index + [:, ..., :])
.reshaped_rhs_slice = reshape(transposed_rhs_slice, dims(rhs, rhs_contracting_dimensions))
.dot_product = reduce( inputs=[multiply(reshaped_lhs_slice, reshaped_rhs_slice)], init_values=[constant(0, element_type(result))], dimensions=range(size(lhs_contracting_dimensions)), body=lambda x, y: add(x, y))
.
क्वांटाइज़्ड टाइप के लिए, 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))
फ़ंक्शन इस्तेमाल करता है.
हाइब्रिड क्वांटाइज़ किए गए टाइप के लिए, hybrid_dequantize_then_op(
lambda lhs, rhs: dot_general(lhs, rhs, lhs_batching_dimensions,
rhs_batching_dimensions, lhs_contracting_dimensions,
rhs_contracting_dimensions, precision_config), lhs, rhs)
करता है.
precision_config
, ऐक्सेलरेटर बैकएंड पर, कैलकुलेशन के लिए, स्पीड और सटीक होने के बीच के समझौते को कंट्रोल करता है. यह इनमें से कोई एक हो सकता है (फ़िलहाल, इन वैल्यू के बारे में ज़्यादा जानकारी नहीं दी गई है. हालांकि, हम #755 में इस समस्या को ठीक करने की कोशिश कर रहे हैं):
DEFAULT
: सबसे तेज़ तरीके से हिसाब लगाया जाता है. हालांकि, मूल संख्या के मुकाबले यह अनुमानित संख्या कम सटीक होती है.HIGH
: कैलकुलेशन में ज़्यादा समय लगता है, लेकिन मूल संख्या के करीब का अनुमान ज़्यादा सटीक होता है.HIGHEST
: यह सबसे धीमा तरीका है. हालांकि, इससे मूल संख्या का सबसे सटीक अनुमान लगाया जा सकता है.
DotAlgorithm
, बिंदु वाले ऑपरेशन को लागू करने के लिए इस्तेमाल किए गए एल्गोरिदम की मुख्य प्रॉपर्टी तय करता है. इससे सटीक जानकारी भी मिलती है. अगर एल्गोरिदम एट्रिब्यूट के फ़ील्ड सेट हैं, तो precision_config
को DEFAULT
पर सेट करना होगा. DotAlgorithms
के लिए कोई डिफ़ॉल्ट वैल्यू नहीं होती, क्योंकि डिफ़ॉल्ट पैरामीटर लागू करने के लिए तय किए जाते हैं. इसलिए, खाली बिंदु एल्गोरिदम की जानकारी देने के लिए, सभी बिंदु एल्गोरिदम फ़ील्ड को None
पर सेट किया जा सकता है. इसके बजाय, precision_config
वैल्यू का इस्तेमाल किया जाएगा.
DotAlgorithm
फ़ील्ड में ये शामिल हैं:
lhs_precision_type
औरrhs_precision_type
, वे सटीक हैं जिनसे ऑपरेशन की बाईं और दाईं ओर का सटीक वैल्यू के टाइप, इनपुट और आउटपुट के स्टोरेज टाइप से अलग होते हैं.accumulation_type
इकट्ठा करने के लिए इस्तेमाल की गई सटीक वैल्यू.lhs_component_count
,rhs_component_count
, औरnum_primitive_operations
का इस्तेमाल तब किया जाता है, जब हम ऐसा एल्गोरिदम इस्तेमाल कर रहे हों जो बाईं ओर और/या दाईं ओर मौजूद वैल्यू को कई कॉम्पोनेंट में बांटता है और उन वैल्यू पर कई "प्राइमटिव" डॉट ऑपरेशन करता है.आम तौर पर, ऐसा ज़्यादा सटीक नतीजे पाने के लिए किया जाता है. उदाहरण के लिए, ज़्यादा सटीक गणना के लिए, bfloat16 आर्टिफ़िशियल इंटेलिजेंस डेटाटाइप का इस्तेमाल करना: bf16_6x tf32_3x वगैरह. जिन एल्गोरिदम में डेटा को अलग-अलग हिस्सों में नहीं बांटा जाता है उनके लिए, इन वैल्यू को1
पर सेट किया जाना चाहिए.allow_imprecise_accumulation
, ताकि यह बताया जा सके कि कुछ चरणों (जैसे,CUBLASLT_MATMUL_DESC_FAST_ACCUM
) के लिए कम सटीक जानकारी के इकट्ठा करने की अनुमति है या नहीं.
DotAlgorithm
एट्रिब्यूट के उदाहरण:
// Inputs are casted to tf32, and then accumulated in f32:
{lhs_precision_type = tf32,
rhs_precision_type = tf32,
accumulation_type = f32,
lhs_component_count = 1,
rhs_component_count = 1,
num_primitive_operations = 1,
allow_imprecise_accumulation = false}
// bf16_6x: each input is decomposed to 3 bf16 components, then 6 dot operations are done on those components, and the result is accumulated in f32.
{lhs_precision_type = bf16,
rhs_precision_type = bf16,
accumulation_type = f32,
lhs_component_count = 3,
rhs_component_count = 3,
num_primitive_operations = 6,
allow_imprecise_accumulation = false}
// Inputs are (casted to) f8e5m2, and we accumulate in f32, but for some steps we may accumulate in lower precision.
{lhs_precision_type = f8e5m2,
rhs_precision_type = f8e5m2,
accumulation_type = f32,
lhs_component_count = 1,
rhs_component_count = 1,
num_primitive_operations = 1,
allow_imprecise_accumulation = true}
यह तय करना लागू करने वाले लोगों का काम है कि कौनसे कॉम्बिनेशन काम करते हैं. आम तौर पर, यह गारंटी नहीं है कि StableHLO का इस्तेमाल करने वाला उपभोक्ता, हर एल्गोरिद्म को हर तरह के ऐक्सेलरेटर पर इस्तेमाल कर सकता है. अगर कोई एल्गोरिदम काम नहीं करता है, तो किसी अन्य विकल्प का इस्तेमाल करने के बजाय, गड़बड़ी का मैसेज दिखाया जाना चाहिए. StableHLO की पुष्टि करने की सुविधा, पूरी कोशिश करके पुष्टि करेगी. इससे, ऐसे एल्गोरिदम को रोका जा सकेगा जो किसी हार्डवेयर पर काम नहीं करते.
एल्गोरिदम की इस्तेमाल की जा सकने वाली कुछ वैल्यू के बारे में जानने के लिए, xla_data.proto > Algorithm
पर जाएं. टिकट #2483 एक ऐसा प्लान कैप्चर करता है जो बैकएंड के साथ काम करने वाले एल्गोरिदम पर एक ही जगह पर एक दस्तावेज़ बनाने के लिए ज़रूरी होता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (C5-C6), (C9-C10), (C12-C14), (C17-C18), (C20) |
(I2) | rhs |
टेंसर या क्वांटाइज़ किया गया टेंसर | (C7-C10), (C12-C20) |
(I3) | lhs_batching_dimensions |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C1), (C3), (C5), (C9), (C12) |
(I4) | rhs_batching_dimensions |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C1), (C4), (C7), (C9) |
(I5) | lhs_contracting_dimensions |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C2), (C3), (C6), (C10) |
(I6) | rhs_contracting_dimensions |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C2), (C4), (C8), (C10), (C16) |
(I7) | precision_config |
DEFAULT , HIGH , और HIGHEST के ईनम की अलग-अलग संख्या |
(C11), (C21) |
(I8) | lhs_precision_type |
FloatType या TensorFloat32 | (C21) |
(I9) | rhs_precision_type |
FloatType या TensorFloat32 | (सी21) |
(I10) | accumulation_type |
FloatType या TensorFloat32 | (C21) |
(I11) | lhs_component_count |
si32 टाइप का कॉन्स्टेंट |
(C21), (C22) |
(I12) | rhs_component_count |
si32 टाइप का कॉन्स्टेंट |
(C21), (C23) |
(I13) | num_primitive_operations |
si32 टाइप का कॉन्स्टेंट |
(C21), (C24) |
(I14) | allow_imprecise_accumulation |
bool टाइप का कॉन्स्टेंट |
(C21) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या क्वांटाइज़ किया गया टेंसर | (C12), (C14), (C18-C20) |
कंस्ट्रेंट
- (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)
. - अगर ऑपरेशन में बिना क्वांटाइज़ किए गए टेंसर का इस्तेमाल किया जाता है, तो:
- (C13)
element_type(lhs) = element_type(rhs)
.
- (C13)
- अगर ऑपरेशन में क्वांटाइज़ किए गए टेंसर का इस्तेमाल किया जाता है, तो:
- (C14)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
. - (C15)
zero_points(rhs) = 0
. - (C16) अगर
is_per_axis_quantized(rhs)
है, तोquantization_dimension(rhs)
rhs_contracting_dimensions
में नहीं है. - अगर
is_quantized(lhs)
: - (C17)
storage_type(lhs) = storage_type(rhs)
. - (C18)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
. - (C19) अगर
is_per_tensor_quantized(rhs)
है, तोis_per_tensor_quantized(result)
. - अगर
!is_quantized(lhs)
: - (C20)
element_type(lhs) = expressed_type(rhs) = element_type(result)
.
- (C14)
- अगर
!is_empty_algorithm(lhs_precision_type, rhs_precision_type, accumulation_type, lhs_component_count, rhs_component_count, num_primitive_operations allow_imprecise_accumulation)
:- (C21)
precision_config... = DEFAULT
. - (C22)
0 < lhs_component_count
. - (C23)
0 < rhs_component_count
. - (C24)
0 < num_primitive_operations
.
- (C21)
उदाहरण
// %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
सिमैंटिक
यह ऑपरेशन, broadcast_in_dim ऑपरेशन के काम करने के तरीके से मिलता-जुलता है. हालांकि, नतीजे का शेप output_dimensions
की मदद से डाइनैमिक तौर पर तय किया जाता है.
इस ऑपरेशन में वैकल्पिक एट्रिब्यूट known_expanding_dimensions
, known_nonexpanding_dimensions
भी स्वीकार किए जाते हैं. इनकी मदद से, डाइमेंशन के बढ़ते व्यवहार के बारे में स्टैटिक जानकारी दी जाती है.
अगर जानकारी नहीं दी गई है, तो माना गया है कि सभी डाइमेंशन बड़े हो सकते हैं.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेंसर या क्वांटाइज़्ड टेंसर | (C1-C2), (C5-C6), (C9) |
(I2) | output_dimensions |
इंटेजर टाइप का एक डाइमेंशन वाला टेंसर | (C7) |
(I3) | broadcast_dimensions |
इंटेजर टाइप का एक डाइमेंशन वाला कॉन्स्टेंट टेंसर | (सी2-सी6) |
(आई4) | known_expanding_dimensions |
पूर्णांक टाइप का 1-डाइमेंशन कॉन्सटेंट टेंसर | (सी8-सी9) |
(I5) | known_nonexpanding_dimensions |
इंटेजर टाइप का एक डाइमेंशन वाला कॉन्स्टेंट टेंसर | (सी8-सी9) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या क्वांटाइज़ किया गया टेंसर | (C1), (C3), (C5-C7) |
कंस्ट्रेंट
- (C1)
element_type(result)
देने वाली कंपनी:- अगर
!is_per_axis_quantized(operand)
है, तोelement_type(operand)
. element_type(operand)
, हालांकिquantization_dimension(operand)
,scales(operand)
, औरzero_points(operand)
,quantization_dimension(result)
,scales(result)
, औरzero_points(result)
से अलग हो सकते हैं.
- अगर
- (C2)
size(broadcast_dimensions) = rank(operand)
. - (C3)
0 <= broadcast_dimensions < rank(result)
. - (C4)
is_unique(broadcast_dimensions)
. - (C5)
axes(operand)
में मौजूद सभीd
के लिए:dim(operand, d) = 1
याdim(operand, d) = dim(result, broadcast_dimensions[d])
.
- (C6) अगर
is_per_axis_quantized(result)
:quantization_dimension(result) = broadcast_dimensions[quantization_dimension(operand)]
.- अगर
dim(operand, quantization_dimension(operand)) = 1
है, तोscales(result)[i] = scales(operand)[0] and zero_points(result)[i] = zero_points(operand)[0] for i in range(dim(result, quantization_dimension(result)))
.
- (C7)
size(output_dimensions) = rank(result)
. - (C8)
is_unique(known_expanding_dimensions + known_nonexpanding_dimensions)
. - (C9)
0 <= known_expanding_dimensions < rank(operand)
. - (C10)
0 <= known_nonexpanding_dimensions < rank(operand)
.
उदाहरण
// %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_nonexpanding_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
सिमैंटिक्स
यह ऑपरेशन, कंवोल्यूशन ऑपरेशन के काम करने के तरीके से मिलता-जुलता है. हालांकि, पैडिंग को padding
के ज़रिए डाइनैमिक तौर पर तय किया जाता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (C1), (C10-C11), (C14) (C25), (C26-C27), (C30-C31), (C33) |
(I2) | rhs |
टेंसर या क्वांटाइज़ किया गया टेंसर | (C1), (C14-C16), (C26-C28), (C30-C33) |
(I3) | padding |
इंटेजर टाइप का दो डाइमेंशन वाला टेंसर | (C4) |
(I4) | window_strides |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C2-C3) |
(I5) | lhs_dilation |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C5-C6) |
(I6) | rhs_dilation |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C7-C8) |
(I7) | window_reversal |
i1 टाइप का 1-डाइमेंशन टेंसर कॉन्सटेंट |
(सी9) |
(I8) | input_batch_dimension |
si64 टाइप का कॉन्स्टेंट |
(C10), (C13) |
(I9) | input_feature_dimension |
si64 टाइप का कॉन्स्टेंट |
(C11), (C13-C14) |
(I10) | input_spatial_dimensions |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C12), (C13) |
(I11) | kernel_input_feature_dimension |
si64 टाइप का कॉन्स्टेंट |
(C14), (C18) |
(I12) | kernel_output_feature_dimension |
si64 टाइप का कॉन्स्टेंट |
(C15-C16), (C18), (C28) |
(I13) | kernel_spatial_dimensions |
si64 टाइप का 1-डाइमेंशन टेंसर कॉन्सटेंट |
(C17-C18) |
(I14) | output_batch_dimension |
si64 टाइप का कॉन्स्टेंट |
(C20) |
(I15) | output_feature_dimension |
si64 टाइप का कॉन्स्टेंट |
(C20), (C29) |
(I16) | output_spatial_dimensions |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C19-C20) |
(I17) | feature_group_count |
si64 टाइप का कॉन्स्टेंट |
(C11), (C14), (C16), (C21), (C23) |
(I18) | batch_group_count |
si64 टाइप का कॉन्स्टेंट |
(C10), (C15), (C22), (C23) |
(I19) | precision_config |
DEFAULT , HIGH , और HIGHEST के लिए, वैरिएबल की संख्या |
(C24) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या क्वांटाइज़ किया गया टेंसर | (C25-C27), (C29), (C31-C33) |
कंस्ट्रेंट
- (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) दिया गया
input_dimensions = [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]
:is_unique(input_dimensions)
.0 <= input_dimensions < N
.
- (C14)
dim(rhs, kernel_input_feature_dimension) = dim(lhs, input_feature_dimension) / feature_group_count
. - (C15)
dim(rhs, kernel_output_feature_dimension) % batch_group_count = 0
. - (C16)
dim(rhs, kernel_output_feature_dimension) % feature_group_count = 0
. - (C17)
size(kernel_spatial_dimensions) = N - 2
. - (C18)
kernel_dimensions = kernel_spatial_dimensions + [kernel_input_feature_dimension] + [kernel_output_feature_dimension]
को दिया गया:is_unique(kernel_dimensions)
.0 <= kernel_dimensions < N
.
- (C19)
size(output_spatial_dimensions) = N - 2
. - (C20) दिया गया
output_dimensions = [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]
:is_unique(output_dimensions)
.0 <= output_dimensions < N
.
- (C21)
0 < feature_group_count
. - (C22)
0 < batch_group_count
. - (C23)
feature_group_count = 1 or batch_group_count = 1
. - (C24)
size(precision_config) = 2
. - (C25)
dim(result, result_dim)
को इस तरह परिभाषित किया गया है:- अगर
result_dim = output_batch_dimension
है, तोdim(lhs, input_batch_dimension) / batch_group_count
. - अगर
result_dim = output_feature_dimension
है, तोdim(rhs, kernel_output_feature_dimension)
. num_windows
अन्य मामलों में, जहां: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
. - अगर ऑपरेशन में बिना क्वांटाइज़ किए गए टेंसर का इस्तेमाल किया जाता है, तो:
- (C27)
element_type(lhs) = element_type(rhs) = element_type(result)
.
- (C27)
- अगर इस ऑपरेशन में क्वांटाइज़्ड टेंसर का इस्तेमाल किया गया है:
- (C28)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
. - (C29) अगर
is_per_axis_quantized(rhs)
, तोquantization_dimension(rhs) = kernel_output_feature_dimension
. - (C30) अगर
is_per_axis_quantized(result)
है, तोquantization_dimension(result) = output_feature_dimension
. - अगर
is_quantized(lhs)
: - (C31)
storage_type(lhs) = storage_type(rhs)
. - (C32)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
. - (C33) अगर
is_per_tensor_quantized(rhs)
है, तोis_per_tensor_quantized(result)
. - अगर
!is_quantized(lhs)
: - (C34)
element_type(lhs) = expressed_type(rhs) = element_type(result)
.
- (C28)
उदाहरण
// %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
सिमैंटिक्स
यह ऑपरेशन, gather ऑपरेशन के काम करने के तरीके से मेल खाता है. इसमें slice_sizes
को वैल्यू के तौर पर डाइनैमिक तौर पर तय किया जाता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (C1), (C7), (C10-C12), (C14) |
(I2) | start_indices |
इंटिजर टाइप का टेंसर | (C2), (C3), (C13) |
(I3) | slice_sizes |
इंटेजर टाइप का एक डाइमेंशन वाला टेंसर | (C8), (C11-C13) |
(I4) | offset_dims |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C1), (C4-C5), (C13) |
(I5) | collapsed_slice_dims |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C1), (C6-C8), (C13) |
(I6) | start_index_map |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C3), (C9), (C10) |
(I7) | index_vector_dim |
si64 टाइप का कॉन्स्टेंट |
(C2), (C3), (C13) |
(I8) | indices_are_sorted |
i1 टाइप का कॉन्स्टेंट |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C5), (C13-C14) |
कंस्ट्रेंट
- (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)
जहां:batch_dim_sizes = shape(start_indices)
, लेकिनindex_vector_dim
से जुड़ेstart_indices
के डाइमेंशन साइज़ को शामिल नहीं किया गया है.offset_dim_sizes = shape(slice_sizes)
, सिवाय इसके किcollapsed_slice_dims
से जुड़ेslice_sizes
में डाइमेंशन साइज़ शामिल नहीं किए गए हैं.combine
,batch_dim_sizes
कोbatch_dims
के ऐक्सिस पर औरoffset_dim_sizes
कोoffset_dims
के ऐक्सिस पर डालता है.
- (C14)
element_type(operand) = element_type(result)
.
उदाहरण
// %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
सिमैंटिक्स
यह ऑपरेशन, iota ऑपरेशन के काम करने के तरीके से मिलता-जुलता है. हालांकि, नतीजे का शेप output_shape
की मदद से डाइनैमिक तौर पर तय किया जाता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | output_shape |
इंटेजर टाइप का एक डाइमेंशन वाला टेंसर | (C1), (C2) |
(I2) | iota_dimension |
si64 |
(C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
पूर्णांक, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C2) |
कंस्ट्रेंट
- (C1)
0 <= iota_dimension < size(output_shape)
. - (C2)
rank(result) = size(output_shape)
.
उदाहरण
%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
सिमैंटिक्स
यह ऑपरेशन pad op की तरह है, लेकिन edge_padding_low
, edge_padding_high
, और interior_padding
वैल्यू के तौर पर डाइनैमिक रूप से बताया गया है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1), (C2), (C4) |
(I2) | padding_value |
0-डाइमेंशन टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
(I3) | edge_padding_low |
पूर्णांक प्रकार का 1-विमीय टेंसर | (C1), (C4) |
(आई4) | edge_padding_high |
इंटेजर टाइप का एक डाइमेंशन वाला टेंसर | (C1), (C4) |
(I5) | interior_padding |
पूर्णांक प्रकार का 1-विमीय टेंसर | (C2-C4) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C3-C6) |
कंस्ट्रेंट
- (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
.
उदाहरण
// %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
सिमैंटिक्स
यह ऑपरेशन, reshape ऑपरेशन के काम करने के तरीके से मिलता-जुलता है. हालांकि, नतीजे का शेप output_shape
की मदद से डाइनैमिक तौर पर तय किया जाता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेंसर या क्वांटाइज़्ड टेंसर | (C1-C3) |
(I2) | output_shape |
इंटेजर टाइप का एक डाइमेंशन वाला टेंसर | (C4) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या क्वांटाइज़ किया गया टेंसर | (सी1-सी4) |
कंस्ट्रेंट
- (C1)
element_type(result)
देने वाली कंपनी:- अगर
!is_per_axis_quantized(operand)
है, तोelement_type(operand)
. element_type(operand)
, हालांकिquantization_dimension(operand)
औरquantization_dimension(result)
अलग हो सकते हैं.
- अगर
- (C2)
size(operand) = size(result)
. - (C3) अगर
is_per_axis_quantized(operand)
:reduce(dims(operand, [0, 1, ..., quantization_dimension(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [0, 1, ..., quantization_dimension(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y)
.dim(operand, quantization_dimension(operand)) = dim(result, quantization_dimension(result))
.reduce(dims(operand, [quantization_dimension(operand) + 1, ..., rank(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [quantization_dimension(result) + 1, ..., rank(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y)
.
- (C4)
size(output_shape) = rank(result)
.
उदाहरण
// %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
सिमैंटिक्स
डाइनैमिक तरीके से कैलकुलेट किए गए शुरुआती इंडेक्स का इस्तेमाल करके, operand
से स्लाइस निकालता है और result
टेंसर बनाता है. start_indices
में, हर डाइमेंशन के लिए स्लाइस के शुरुआती इंडेक्स होते हैं, जिनमें बदलाव किया जा सकता है. साथ ही, slice_sizes
में हर डाइमेंशन के लिए स्लाइस के साइज़ होते हैं. ज़्यादा औपचारिक तौर पर,
result[result_index] = operand[operand_index]
जहां:
adjusted_start_indices = clamp(0, start_indices, shape(operand) - slice_sizes)
.operand_index = adjusted_start_indices + result_index
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1), (C2), (C4) |
(I2) | start_indices |
इंटिजर टाइप के 0-डाइमेंशन वाले टेंसर की वैरिएडिक संख्या | (C2), (C3) |
(I3) | slice_sizes |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C2), (C4), (C5) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (C1), (C5) |
कंस्ट्रेंट
- (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
.
उदाहरण
// %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
सिमैंटिक
result
टेंसर बनाता है, जो operand
टेंसर के बराबर होता है. हालांकि, start_indices
से शुरू होने वाला स्लाइस, update
की वैल्यू के साथ अपडेट हो जाता है.
result[result_index]
को ज़्यादा औपचारिक तौर पर इस तरह परिभाषित किया जाता है:
update[update_index]
अगर0 <= update_index < shape(update)
है, तो:adjusted_start_indices = clamp(0, start_indices, shape(operand) - shape(update))
.update_index = result_index - adjusted_start_indices
.
- अगर ऐसा नहीं है, तो
operand[result_index]
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (C1-C4), (C6) |
(I2) | update |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (C2), (C3), (C6) |
(I3) | start_indices |
पूर्णांक टाइप के 0-डाइमेंशन वाले टेंसर की वैरायडिक संख्या | (C4), (C5) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (C1) |
कंस्ट्रेंट
- (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)
.
उदाहरण
// %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]
// ]
घातांकी
सिमैंटिक्स
operand
टेंसर पर एलिमेंट के हिसाब से एक्सपोनेंशियल ऑपरेशन करता है और result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- फ़्लोट के लिए: IEEE-754 से
exp
. - सम्मिश्र संख्याओं के लिए: कॉम्प्लेक्स एक्सपोनेंशियल.
- क्वांटाइज़ किए गए टाइप के लिए:
dequantize_op_quantize(exponential, operand, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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
सिमैंटिक
operand
टेंसर पर, एलिमेंट के हिसाब से एक्सपोनेंशियल माइनस वन ऑपरेशन करता है और result
टेंसर जनरेट करता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- फ़्लोट के लिए: IEEE-754 से
expm1
. - सम्मिश्र संख्याओं के लिए: सम्मिश्र घातांक से एक घटाएं.
- क्वांटाइज़ किए गए टाइप के लिए:
dequantize_op_quantize(exponential_minus_one, operand, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %operand: [0.0, 1.0]
%result = "stablehlo.exponential_minus_one"(%operand) : (tensor<2xf64>) -> tensor<2xf64>
// %result: [0.0, 1.71828187]
fft
सिमैंटिक्स
रीयल और कॉम्प्लेक्स इनपुट/आउटपुट के लिए, फ़ोरवर्ड और इनवर्स फ़ूरियर ट्रांसफ़ॉर्म करता है.
fft_type
इनमें से कोई एक है:
FFT
: फ़ॉरवर्ड कॉम्प्लेक्स-टू-कॉम्प्लेक्स एफ़टीटी.IFFT
: इनवर्स कॉम्प्लेक्स से कॉम्प्लेक्स एफ़एफ़टी.RFFT
: फ़ॉरवर्ड रीयल-टू-कॉम्प्लेक्स एफ़एफ़टी.IRFFT
: रियल-टू-कॉम्प्लेक्स FFT (यानी जटिल लेता है, वास्तविक देता है).
ज़्यादा औपचारिक तौर पर, फ़ंक्शन fft
, इनपुट के तौर पर जटिल टाइप के एक-आयामी टेंसर लेता है और आउटपुट के तौर पर उसी टाइप के एक-आयामी टेंसर बनाता है. साथ ही, डिसक्रेट फ़ोरियर ट्रांसफ़ॉर्म का हिसाब लगाता है:
fft_type = FFT
के लिए, result
को L के क्रम में किए गए कैलकुलेशन का आखिरी नतीजा माना जाता है, जहां L = size(fft_length)
. उदाहरण के लिए, 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])
.
इसके अलावा, दिए गए फ़ंक्शन ifft
में एक ही तरह का हस्ताक्षर होता है और यह fft
के व्युत्क्रम की गिनती करता है:
fft_type = IFFT
के लिए, result
को fft_type = FFT
के लिए किए गए कैलकुलेशन के उलट के तौर पर परिभाषित किया गया है. उदाहरण के लिए, 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, ..., :])
.
इसके अलावा, फ़ंक्शन rfft
, फ़्लोटिंग-पॉइंट टाइप के एक-आयामी टेंसर लेता है और उसी फ़्लोटिंग-पॉइंट सेमेटिक्स के कॉम्प्लेक्स टाइप के एक-आयामी टेंसर बनाता है. यह फ़ंक्शन इस तरह काम करता है:
rfft(real_operand) = truncated_result
wherecomplex_operand... = (real_operand..., 0.0)
.complex_result = fft(complex_operand)
.truncated_result = complex_result[:(rank(complex_result) / 2 + 1)]
.
(जब रीयल ऑपरेंड के लिए अलग फूरिये ट्रांसफ़ॉर्म का हिसाब लगाया जाता है, तो नतीजे के पहले
N/2 + 1
एलिमेंट में साफ़ तौर पर बाकी नतीजे के बारे में बताया जाता है. इसलिए, ग़ैर-ज़रूरी एलिमेंट की गिनती करने से बचने के लिए, rfft
के नतीजे को छोटा किया जाता है.
fft_type = RFFT
के लिए, result
को L कंप्यूटेशन की सीरीज़ का आखिरी नतीजा माना जाता है, जहां L = size(fft_length)
. उदाहरण के लिए, 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])
.
आखिर में, irfft
फ़ंक्शन दिया गया है, जिसका टाइप हस्ताक्षर एक ही है और जो rfft
का इनवर्स कैलकुलेट करता है:
fft_type = IRFFT
के लिए, result
को fft_type = RFFT
के लिए किए गए कैलकुलेशन के उलट के तौर पर परिभाषित किया गया है. उदाहरण के लिए, 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, ..., :])
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर | (C1), (C2), (C4), (C5) |
(I2) | fft_type |
FFT , IFFT , RFFT , और IRFFT का enum |
(C2), (C5) |
(I3) | fft_length |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C1), (C3), (C4) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर | (C2), (C4), (C5) |
कंस्ट्रेंट
- (C1)
size(fft_length) <= rank(operand)
. - (C2)
operand
औरresult
एलिमेंट टाइप के बीच का संबंध अलग-अलग होता है:- अगर
fft_type = FFT
,element_type(operand)
, औरelement_type(result)
के पास एक ही कॉम्प्लेक्स टाइप है. - अगर
fft_type = IFFT
,element_type(operand)
, औरelement_type(result)
के पास एक ही कॉम्प्लेक्स टाइप है. - अगर
fft_type = RFFT
,element_type(operand)
एक फ़्लोटिंग-पॉइंट टाइप है औरelement_type(result)
उसी फ़्लोटिंग-पॉइंट के सेमेंटेटिक्स का कॉम्प्लेक्स टाइप है. - अगर
fft_type = IRFFT
,element_type(operand)
एक कॉम्प्लेक्स टाइप है औरelement_type(result)
, उसी फ़्लोटिंग-पॉइंट सेमेंटेटिक्स का फ़्लोटिंग-पॉइंट टाइप है.
- अगर
- (C3)
1 <= size(fft_length) <= 3
. - (C4) अगर
operand
औरresult
में से कोई एक टेंसरreal
, फ़्लोटिंग-पॉइंट टाइप का है, तोshape(real)[-size(fft_length):] = fft_length
. - (C5)
shape(result) = shape(operand)
, सिवाय:- अगर
fft_type = RFFT
,dim(result, -1) = dim(operand, -1) = 0 ? 0 : dim(operand, -1) / 2 + 1
. - अगर
fft_type = IRFFT
,dim(operand, -1) = dim(result, -1) = 0 ? 0 : dim(result, -1) / 2 + 1
.
- अगर
उदाहरण
// %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)]
फ़्लोर
सिमैंटिक्स
operand
टेंसर के एलिमेंट के हिसाब से फ़्लोर फ़ंक्शन लागू करता है और result
टेंसर बनाता है.
IEEE-754 के स्पेसिफ़िकेशन के हिसाब से, roundToIntegralTowardNegative
ऑपरेशन लागू करता है. क्वांटाइज़्ड टाइप के लिए, dequantize_op_quantize(floor, operand, type(result))
की मदद से काम करता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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]
इकट्ठा करें
सिमैंटिक्स
start_indices
में बताए गए ऑफ़सेट से, operand
टेंसर के स्लाइस इकट्ठा करता है और result
टेंसर बनाता है.
नीचे दिया गया डायग्राम, result
में मौजूद एलिमेंट को operand
में मौजूद एलिमेंट पर मैप करने का तरीका दिखाने के लिए है. इसके लिए, एक ठोस उदाहरण दिया गया है. इस डायग्राम में, उदाहरण के तौर पर कुछ result
इंडेक्स चुने गए हैं. साथ ही, यह भी बताया गया है कि ये इंडेक्स किन operand
इंडेक्स से जुड़े हैं.
ज़्यादा औपचारिक तौर पर, result[result_index] = operand[operand_index]
जहां:
batch_dims = [d for d in axes(result) and d not in offset_dims]
.batch_index = result_index[batch_dims...]
.start_index
को इस तरह परिभाषित किया गया है:start_indices[bi0, ..., :, ..., biN]
जहांbi
,batch_index
में अलग-अलग एलिमेंट हैं और:
कोindex_vector_dim
इंडेक्स पर डाला गया है, अगरindex_vector_dim
<rank(start_indices)
है.[start_indices[batch_index]]
अन्य मामलों में.
axes(operand)
मेंd_operand
के लिए,full_start_index[d_operand] = clamp(start_index[d_start], 0, dim(operand, d_operand) - slice_sizes[d_operand])
अगरd_operand = start_index_map[d_start]
है, तो.full_start_index[d_operand] = 0
अन्य मामलों में.
axes(operand)
मेंd_operand
के लिए,full_batching_index[d_operand] = batch_index[d_start - (d_start < index_vector_dim ? 0 : 1)]
अगरd_operand = operand_batching_dims[i_batching]
औरd_start = start_indices_batching_dims[i_batching]
है.- अगर ऐसा नहीं है, तो
full_batching_index[d_operand] = 0
.
offset_index = result_index[offset_dims...]
.full_offset_index = [oi0, ..., 0, ..., oiN]
, जहांoi
,offset_index
में अलग-अलग एलिमेंट हैं और0
कोcollapsed_slice_dims
औरoperand_batching_dims
के इंडेक्स में डाला गया है.operand_index = full_start_index + full_batching_index + full_offset_index
.
अगर indices_are_sorted
true
है, तो लागू करने के दौरान यह माना जा सकता है कि
start_indices
को start_index_map
के हिसाब से क्रम में लगाया गया है. ऐसा न होने पर,
कार्रवाई की जानकारी नहीं दी जाती. ज़्यादा औपचारिक तौर पर, indices(result)
से लेकर i1 < i2
तक के सभी i1 < i2
के लिए,
full_start_index(i1) <= full_start_index(i2)
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1), (C8), (C11), (C17), (C19-C21), (C23) |
(I2) | start_indices |
इंटिजर टाइप का टेंसर | (C2-C3), (C14), (C17), (C22) |
(I3) | offset_dims |
si64 टाइप का 1-डाइमेंशन टेंसर कॉन्सटेंट |
(C1), (C4-C5), (C22) |
(I4) | collapsed_slice_dims |
si64 टाइप का 1-डाइमेंशन टेंसर कॉन्सटेंट |
(C1), (C6-C9), (C22) |
(I5) | operand_batching_dims |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C1), (C6), (C10-C12), (C16-C18), (C22) |
(I6) | start_indices_batching_dims |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C13-C17) |
(I7) | start_index_map |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C3), (C18-C19) |
(I8) | index_vector_dim |
si64 टाइप का कॉन्स्टेंट |
(C2-C3), (C15), (C22) |
(I9) | slice_sizes |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C9), (C12), (C20-C22) |
(I10) | indices_are_sorted |
i1 टाइप का कॉन्स्टेंट |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C5), (C22-C23) |
कंस्ट्रेंट
- (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)
जहां:batch_dim_sizes = shape(start_indices)
, सिवाय इसके किindex_vector_dim
से जुड़ेstart_indices
के डाइमेंशन साइज़ को शामिल नहीं किया गया है.offset_dim_sizes = slice_sizes
, हालांकिcollapsed_slice_dims
औरoperand_batching_dims
से जुड़ेslice_sizes
में डाइमेंशन साइज़ शामिल नहीं किए गए हैं.combine
,batch_dim_sizes
कोbatch_dims
के ऐक्सिस पर औरoffset_dim_sizes
कोoffset_dims
के ऐक्सिस पर डालता है.
- (C23)
element_type(operand) = element_type(result)
.
उदाहरण
// %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
सिमैंटिक्स
operand
के दिए गए dimension
का साइज़ दिखाता है. ज़्यादा औपचारिक तौर पर,
result = dim(operand, dimension)
. सिमैंटिक सिर्फ़ आकार के घटक से जुड़ा है. एलिमेंट-टाइप कुछ भी हो सकता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेंसर या क्वांटाइज़्ड टेंसर | (C1) |
(I2) | dimension |
si64 टाइप का कॉन्स्टेंट |
(सी1) |
आउटपुट
नाम | टाइप |
---|---|
result |
si32 टाइप का 0-डाइमेंशनल टेंसर |
कंस्ट्रेंट
- (C1)
0 <= dimension < rank(operand)
.
उदाहरण
// %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
सिमैंटिक्स
operand
ट्यूपल की index
पोज़िशन पर मौजूद एलिमेंट को निकालता है और एक result
बनाता है. औपचारिक तौर पर, result = operand[index]
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टपल | (C1), (C2) |
(I2) | index |
si32 टाइप का कॉन्स्टेंट |
(C1), (C2) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
इस तरह की फ़ाइलें इस्तेमाल की जा सकती हैं | (C2) |
कंस्ट्रेंट
- (C1)
0 <= index < size(operand)
. - (C2)
type(result) = tuple_element_types(operand)[index]
.
उदाहरण
// %operand: ([1.0, 2.0], (3))
index = 0 : i32
} : (tuple<tensor<2xf32>, tuple<tensor<i32>>>) -> tensor<2xf32>
// %result: [1.0, 2.0]
अगर
सिमैंटिक्स
pred
की वैल्यू के आधार पर, true_branch
या
false_branch
में से किसी एक फ़ंक्शन को लागू करके आउटपुट जनरेट करता है. ज़्यादा औपचारिक तौर पर, result =
pred ? true_branch() : false_branch()
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | pred |
i1 टाइप का 0-डाइमेंशनल टेंसर |
|
(I2) | true_branch |
फ़ंक्शन | (C1-C3) |
(I3) | false_branch |
फ़ंक्शन | (C1), (C2) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
results |
टेंसर की वैरायडिक संख्या, क्वांटाइज़ किए गए टेंसर या टोकन | (C3) |
कंस्ट्रेंट
- (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)
.
उदाहरण
// %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
imag
सिमैंटिक
एलिमेंट के हिसाब से, operand
से काल्पनिक हिस्से को एक्सट्रैक्ट करता है और result
टेंसर बनाता है. औपचारिक तौर पर, हर एलिमेंट x
के लिए:
imag(x) = is_complex(x) ? imaginary_part(x) :
constant(0, element_type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर | (C1), (C2) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट टाइप का टेंसर | (C1), (C2) |
कंस्ट्रेंट
- (C1)
shape(result) = shape(operand)
. - (C2)
element_type(result)
की परिभाषा इस तरह दी गई है:- अगर
is_complex(operand)
है, तोcomplex_element_type(element_type(operand))
. - अगर ऐसा नहीं है, तो
element_type(operand)
.
- अगर
उदाहरण
// %operand: [(1.0, 2.0), (3.0, 4.0)]
%result = "stablehlo.imag"(%operand) : (tensor<2xcomplex<f32>>) -> tensor<2xf32>
// %result: [2.0, 4.0]
इनफ़ीड
सिमैंटिक
इनफ़ीड से डेटा पढ़ता है और results
जनरेट करता है.
infeed_config
के सिमेंटिक्स, लागू करने के तरीके के हिसाब से तय होते हैं.
results
में, पहले पेलोड वैल्यू और आखिर में एक टोकन होता है. आने वाले समय में, हम पेलोड और टोकन को दो अलग-अलग आउटपुट में बांटने की योजना बना रहे हैं, ताकि उन्हें बेहतर बनाया जा सके (#670).
इनपुट
लेबल | नाम | टाइप |
---|---|---|
(I1) | token |
token |
(I2) | infeed_config |
string टाइप का कॉन्स्टेंट |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
results |
वैरिएडिक संख्या वाले टेंसर, क्वांटाइज़ किए गए टेंसर या टोकन | (C1-C3) |
कंस्ट्रेंट
- (C1)
0 < size(results)
. - (C2)
is_empty(result[:-1])
याis_tensor(type(results[:-1]))
. - (C3)
is_token(type(results[-1]))
.
उदाहरण
// %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]]
iota
सिमैंटिक्स
output
डाइमेंशन के साथ शून्य से शुरू होने वाले बढ़ते क्रम में वैल्यू के साथ iota_dimension
टेंसर को भरता है. औपचारिक तौर पर,
output[output_index] = constant(is_quantized(output) ?
quantize(output_index[iota_dimension], element_type(output)) :
output_index[iota_dimension], element_type(output))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | iota_dimension |
si64 |
(सी1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
output |
पूर्णांक, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
0 <= iota_dimension < rank(output)
.
उदाहरण
%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
सिमैंटिक्स
यह एलिमेंट के हिसाब से जांच करता है कि x
में दी गई वैल्यू सीमित है या नहीं.इसका मतलब है कि वह न तो +Inf, -Inf है और न ही NaN. साथ ही, यह y
टेंसर बनाता है. IEEE-754 स्पेसिफ़िकेशन के हिसाब से, isFinite
ऑपरेशन लागू करता है. क्वांटिफ़ाइड टाइप के लिए, नतीजा
हमेशा true
होता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | x |
फ़्लोटिंग-पॉइंट टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
y |
बूलियन टाइप का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
shape(x) = shape(y)
.
उदाहरण
// 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
सिमैंटिक्स
operand
टेंसर पर एलिमेंट के हिसाब से लॉगरिदम का ऑपरेशन करता है और एक
result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- फ़्लोट के लिए: IEEE-754 से
log
. - कॉम्प्लेक्स नंबर के लिए: कॉम्प्लेक्स लॉगारिद्म.
- क्वांटाइज़्ड टाइप के लिए:
dequantize_op_quantize(log, operand, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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
सिमैंटिक्स
operand
टेंसर पर, एलिमेंट के हिसाब से लॉगारिद्म प्लस वन ऑपरेशन करता है और
result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- फ़्लोट के लिए: IEEE-754 से
logp1
. - सम्मिश्र संख्याओं के लिए: सम्मिश्र लॉगारिद्म प्लस वन.
- क्वांटाइज़ किए गए टाइप के लिए:
dequantize_op_quantize(log_plus_one, operand, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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]
लॉजिस्टिक
सिमैंटिक्स
operand
टेंसर पर एलिमेंट-वाइज़ लॉजिस्टिक ऑपरेशन करता है और एक
result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- फ़्लोट के लिए: IEEE-754 से
division(1, addition(1, exp(-x)))
. - कॉम्प्लेक्स नंबर के लिए: complex logistic.
- क्वांटाइज़ किए गए टाइप के लिए:
dequantize_op_quantize(logistic, operand, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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]]
मैप
सिमैंटिक्स
dimensions
के साथ inputs
पर मैप फ़ंक्शन computation
लागू करता है और
result
टेंसर बनाता है.
ज़्यादा औपचारिक तौर पर, result[result_index] = computation(inputs...[result_index])
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | inputs |
वैरिएबल संख्या वाले टेंसर या हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर | (C1-C4) |
(I2) | dimensions |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C3) |
(I3) | computation |
फ़ंक्शन | (C4) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1), (C4) |
कंस्ट्रेंट
- (C1)
shape(inputs...) = shape(result)
. - (C2)
0 < size(inputs) = N
. - (C3)
dimensions = range(rank(inputs[0]))
. - (C4)
computation
का टाइप(tensor<E0>, ..., tensor<EN-1>) -> tensor<E'>
है जहांEi = element_type(inputs[i])
औरE' = element_type(result)
.
उदाहरण
// %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]]
ज़्यादा से ज़्यादा
सिमैंटिक
टेंसर lhs
और rhs
पर एलिमेंट के हिसाब से सबसे ज़्यादा ऑपरेशन करता है और result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम किए जाते हैं:
- बूलियन के लिए: लॉजिकल OR.
- पूर्णांक के लिए: पूर्णांक की ज़्यादा से ज़्यादा वैल्यू.
- फ़्लोट के लिए: IEEE-754 से
maximum
. - जटिल संख्याओं के लिए:
(real, imaginary)
जोड़े के लिए, शब्दकोश के क्रम में सबसे बड़ी संख्या. सम्मिश्र संख्याओं को क्रम से लगाने के लिए, कुछ खास शब्दों का इस्तेमाल करना पड़ता है. इसलिए, आने वाले समय में हम इस ऑपरेशन के लिए, सम्मिश्र संख्याओं के इस्तेमाल की सुविधा हटाने जा रहे हैं (#560). - क्वांटाइज़्ड टाइप के लिए:
dequantize_op_quantize(maximum, lhs, rhs, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1) |
(I2) | rhs |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
.
उदाहरण
// %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]]
कम से कम
सिमैंटिक्स
टेंसर lhs
और rhs
पर एलिमेंट के हिसाब से सबसे कम वैल्यू ढूंढता है और result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- बूलियन के लिए: लॉजिकल ऐंड.
- पूर्णांक के लिए: पूर्णांक की कम से कम वैल्यू.
- फ़्लोट के लिए: IEEE-754 से
minimum
. - जटिल संख्याओं के लिए:
(real, imaginary)
जोड़े के लिए, शब्दकोश के क्रम में सबसे छोटी संख्या. सम्मिश्र संख्याओं को क्रम से लगाने में, कुछ खास बातें ध्यान में रखनी पड़ती हैं. इसलिए, आने वाले समय में हम इस ऑपरेशन के लिए, सम्मिश्र संख्याओं के साथ काम करने की सुविधा को हटाने वाले हैं (#560). - क्वांटाइज़ किए गए डेटा टाइप के लिए:
dequantize_op_quantize(minimum, lhs, rhs, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1) |
(I2) | rhs |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
.
उदाहरण
// %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]]
गुणा
सिमैंटिक्स
यह दो टेंसर lhs
और rhs
के एलिमेंट के हिसाब से गुणनफल करता है और एक
result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- बूलियन के लिए: लॉजिकल ऐंड.
- पूर्णांकों के लिए: पूर्णांक का गुणा.
- फ़्लोट के लिए: IEEE-754 से
multiplication
. - कॉम्प्लेक्स नंबर के लिए: कॉम्प्लेक्स मल्टिप्लाकेशन.
- क्वांटाइज़्ड टाइप के लिए:
dequantize_op_quantize(multiply, lhs, rhs, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1) |
(I2) | rhs |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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]]
निगेट
सिमैंटिक्स
operand
टेंसर के एलिमेंट के हिसाब से निगेशन करता है और result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम किए जाते हैं:
- साइन किए गए पूर्णांकों के लिए: पूर्णांक निगेशन.
- बिना साइन वाले पूर्णांकों के लिए: साइन वाले पूर्णांक में बिटकस्ट, पूर्णांक का नेगेटिव, बिना साइन वाले पूर्णांक में बिटकस्ट वापस.
- फ़्लोट के लिए: IEEE-754 से
negate
. - कॉम्प्लेक्स निगेशन के लिए: कॉम्प्लेक्स निगेशन.
- क्वांटाइज़ किए गए टाइप के लिए:
dequantize_op_quantize(negate, operand, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
पूर्णांक, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (सी1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
पूर्णांक, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// 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]
नहीं
सिमैंटिक्स
टेंसर operand
के एलिमेंट के हिसाब से NOT फ़ंक्शन लागू करता है और result
टेंसर बनाता है.
एलिमेंट टाइप के आधार पर, ये काम करता है:
- बूलियन के लिए: लॉजिकल NOT.
- पूर्णांकों के लिए: बिटवाइज़ NOT.
तर्क
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
operand |
बूलियन या इंटेजर टाइप का टेंसर | (सी1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
बूलियन या इंटेजर टाइप का टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
type(operand) = type(result)
.
उदाहरण
// 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
सिमैंटिक्स
यह पक्का करता है कि operand
जनरेट करने वाले ऑपरेशन, result
पर निर्भर किसी भी ऑपरेशन से पहले पूरे हो जाएं. साथ ही, यह कंपाइलर ट्रांसफ़ॉर्मेशन को, ऑपरेशन को बैरियर के पार जाने से रोकता है. इसके अलावा, कार्रवाई एक आइडेंटिटी है, जैसे कि result = operand
.
तर्क
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
operand |
वैरिएडिक संख्या वाले टेंसर, हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर या टोकन | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
वैरिएडिक संख्या वाले टेंसर, हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर या टोकन | (C1) |
कंस्ट्रेंट
- (C1)
type(operand...) = type(result...)
.
उदाहरण
// %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
या
सिमैंटिक्स
यह दो टेंसर lhs
और rhs
के एलिमेंट के हिसाब से OR करता है और result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- बूलियन के लिए: लॉजिकल OR.
- पूर्णांक के लिए: बिटवाइज़ OR.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
इंटिजर या बूलियन टाइप का टेंसर | (C1) |
(I2) | rhs |
पूर्णांक या बूलियन टाइप का टेंसर | (सी1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
पूर्णांक या बूलियन टाइप का टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
type(lhs) = type(rhs) = type(result)
.
उदाहरण
// 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]]
आउटफ़ीड
सिमैंटिक
आउटफ़ीड में inputs
लिखता है और result
टोकन जनरेट करता है.
outfeed_config
के सीमैंटिक को लागू किया जाता है.
इनपुट
लेबल | नाम | टाइप |
---|---|---|
(I1) | inputs |
टेंसर या क्वांटाइज़्ड टेंसर की वैरायड संख्या |
(I2) | token |
token |
(I3) | outfeed_config |
string टाइप का कॉन्स्टेंट |
आउटपुट
नाम | टाइप |
---|---|
result |
token |
उदाहरण
%result = "stablehlo.outfeed"(%input0, %token) {
outfeed_config = ""
} : (tensor<2x2x2xi64>, !stablehlo.token) -> !stablehlo.token
पैड
सिमैंटिक्स
दिए गए padding_value
की मदद से, टेंसर के आस-पास और टेंसर के एलिमेंट के बीच पैडिंग करके operand
को बड़ा करता है.
edge_padding_low
और edge_padding_high
से पता चलता है कि हर डाइमेंशन के सबसे कम (इंडेक्स 0 के बगल में) और सबसे ज़्यादा (सबसे बड़े इंडेक्स के बगल में) आखिर में कितनी पैडिंग जोड़ी गई है. पैडिंग की वैल्यू नेगेटिव हो सकती है. नेगेटिव पैडिंग की वैल्यू से पता चलता है कि दिए गए डाइमेंशन से कितने एलिमेंट हटाने हैं.
interior_padding
से पता चलता है कि हर डाइमेंशन में, किसी भी दो एलिमेंट के बीच कितनी पैडिंग जोड़ी गई है. यह पैडिंग नेगेटिव नहीं हो सकती. इंटीरियर पैडिंग, एज पैडिंग से पहले होती है, जिससे नेगेटिव एज पैडिंग, इंटीरियर-पैडेड ऑपरेंड से एलिमेंट हटा देती है.
result[result_index]
को ज़्यादा औपचारिक तौर पर इस तरह परिभाषित किया जाता है:
operand[operand_index]
अगरresult_index = edge_padding_low + operand_index * (interior_padding + 1)
.padding_value
अन्य मामलों में.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1), (C2), (C4) |
(I2) | padding_value |
0-डाइमेंशन टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
(I3) | edge_padding_low |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C1), (C4) |
(I4) | edge_padding_high |
si64 टाइप का 1-डाइमेंशन टेंसर कॉन्सटेंट |
(C1), (C4) |
(I5) | interior_padding |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C2-C4) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C3-C6) |
कंस्ट्रेंट
- (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
.
उदाहरण
// %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
सिमैंटिक्स
मौजूदा प्रोसेस का partition_id
बनाता है.
आउटपुट
नाम | टाइप |
---|---|
result |
ui32 टाइप का 0-डाइमेंशनल टेंसर |
उदाहरण
%result = "stablehlo.partition_id"() : () -> tensor<ui32>
पॉप सीएनटी
सिमैंटिक्स
operand
टेंसर में सेट किए गए बिट की संख्या की एलिमेंट-वाइज़ गिनती करता है और result
टेंसर बनाता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
पूर्णांक टाइप का टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
पूर्णांक टाइप का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
type(operand) = type(result)
.
उदाहरण
// %operand: [0, 1, 2, 127]
%result = "stablehlo.popcnt"(%operand) : (tensor<4xi64>) -> tensor<4xi64>
// %result: [0, 1, 1, 7]
पावर
सिमैंटिक
lhs
टेंसर को rhs
टेंसर से एलिमेंट के हिसाब से घात में ले जाता है और result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- पूर्णांकों के लिए: पूर्णांक घातांक.
- फ़्लोट के लिए: IEEE-754 से
pow
. - सम्मिश्र संख्याओं के लिए: कॉम्प्लेक्स एक्सपोनेंशियेशन.
- क्वांटाइज़ किए गए टाइप के लिए:
dequantize_op_quantize(power, lhs, rhs, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
पूर्णांक, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
(I2) | rhs |
पूर्णांक, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (सी1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
पूर्णांक, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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]
रीयल
सिमैंटिक्स
operand
से एलिमेंट के हिसाब से रीयल पार्ट निकालता है और result
टेंसर बनाता है. ज़्यादा औपचारिक तौर पर, हर एलिमेंट के लिए x
:
real(x) = is_complex(x) ? real_part(x) : x
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर | (C1), (C2) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट टाइप का टेंसर | (C1), (C2) |
कंस्ट्रेंट
- (C1)
shape(result) = shape(operand)
. - (C2)
element_type(result)
की परिभाषा इस तरह दी गई है:- अगर
is_complex(operand)
है, तोcomplex_element_type(element_type(operand))
. - अगर ऐसा नहीं है, तो
element_type(operand)
.
- अगर
उदाहरण
// %operand: [(1.0, 2.0), (3.0, 4.0)]
%result = "stablehlo.real"(%operand) : (tensor<2xcomplex<f32>>) -> tensor<2xf32>
// %result: [1.0, 3.0]
recv
सिमैंटिक
यह channel_id
वाले चैनल से डेटा पाता है और results
जनरेट करता है.
अगर is_host_transfer
true
है, तो ऑपरेशन, होस्ट से डेटा ट्रांसफ़र करता है. ऐसा न होने पर, यह दूसरे डिवाइस से डेटा ट्रांसफ़र करता है. इसका मतलब, लागू करने के लिए
तय किया गया है. यह फ़्लैग, channel_type
में दी गई जानकारी को डुप्लीकेट करता है. इसलिए, आने वाले समय में हम इनमें से सिर्फ़ एक (#666) को रखने की योजना बना रहे हैं.
results
में पेलोड वैल्यू पहले से आती है और आखिरी बार आने वाला टोकन होता है. आने वाले समय में, हम पेलोड और टोकन को दो अलग-अलग आउटपुट में बांटने की योजना बना रहे हैं, ताकि उन्हें बेहतर बनाया जा सके (#670).
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | token |
token |
(C4) |
(I2) | channel_id |
si64 टाइप का कॉन्स्टेंट |
|
(I3) | channel_type |
DEVICE_TO_DEVICE और HOST_TO_DEVICE का enum |
(सी1) |
(आई4) | is_host_transfer |
i1 टाइप का कॉन्स्टेंट |
(C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
results |
टेंसर की वैरायडिक संख्या, क्वांटाइज़ किए गए टेंसर या टोकन | (C2-C4) |
कंस्ट्रेंट
- (C1)
channel_type
को इस तरह परिभाषित किया गया है:HOST_TO_DEVICE
अगरis_host_transfer = true
है, तो- अगर ऐसा नहीं है, तो
DEVICE_TO_DEVICE
.
- (C2)
0 < size(results)
. - (C3)
is_empty(result[:-1])
याis_tensor(type(results[:-1]))
. - (C4)
is_token(type(results[-1]))
.
उदाहरण
%results0, %results1 = "stablehlo.recv"(%token) {
channel_handle = #stablehlo.channel_handle<handle = 1, type = 3>,
is_host_transfer = true
} : (!stablehlo.token) -> (tensor<2x2xi64>, !stablehlo.token)
कम करें
सिमैंटिक्स
dimensions
के साथ inputs
और init_values
पर, रिडक्शन फ़ंक्शन body
लागू करता है और results
टेंसर बनाता है.
घटाने के क्रम को लागू करने के तरीके से तय किया जाता है. इसका मतलब है कि body
और
init_values
को एक मोनोइड बनाना चाहिए, ताकि यह पक्का किया जा सके कि सभी लागू करने के तरीकों पर, सभी इनपुट के लिए ऑपरेशन से एक ही नतीजा मिले. हालांकि, यह शर्त
कई लोकप्रिय गिरावटों पर लागू नहीं होती है. उदाहरण के लिए, body
के लिए फ़्लोटिंग-पॉइंट जोड़ना और init_values
के लिए शून्य जोड़ना, असल में एक मोनोइड नहीं बनाता, क्योंकि फ़्लोटिंग-पॉइंट जोड़ना असोसिएटिव नहीं होता.
ज़्यादा औपचारिक तौर पर, results...[j0, ..., jR-1] = reduce(input_slices_converted)
जहां:
input_slices = inputs...[j0, ..., :, ..., jR-1]
, जहां:
कोdimensions
पर डाला गया है.input_slices_converted = to_destination_type(input_slices..., type(func_inputs(body)[:len(func_inputs(body))//2])...)
.init_values_converted = to_destination_type(init_values..., type(func_inputs(body)[len(func_inputs(body))//2:])...)
.reduce(input_slices_converted) = exec(schedule)
कुछ बाइनरी ट्री के लिएschedule
जहां:exec(node) = body(exec(node.left), exec(node.right))
.exec(leaf) = leaf.value
.
schedule
, लागू करने के तरीके से तय की गई फ़ुल बाइनरी ट्री है. इसमें, क्रम से ट्रैवर्स करने पर ये चीज़ें मिलती हैं:index
की सभी वैल्यू,index_space(input_slices_converted)
मेंindex
के बढ़ते क्रम में.input_slices_converted...[index]
- लागू करने के तरीके के हिसाब से तय की गई जगहों पर, लागू करने के तरीके के हिसाब से तय की गई
init_values_converted
की संख्या के साथ इंटरस्पर्स किया गया.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | inputs |
वैरिएबल संख्या वाले टेंसर या हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर | (C1-C4), (C6), (C7) |
(I2) | init_values |
0-डाइमेंशन वाले टेंसर या हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर की वैरिएडिक संख्या | (C2), (C3) |
(I3) | dimensions |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C4), (C5), (C7) |
(I4) | body |
फ़ंक्शन | (C6) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
results |
वैरिएबल संख्या वाले टेंसर या हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर | (C3), (C7), (C8) |
कंस्ट्रेंट
- (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
का टाइप(tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ...,
tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>)
है, जहांis_promotable(element_type(inputs[i]), Ei)
. - (C7)
shape(results...) = shape(inputs...)
, सिवाय इसके किdimensions
से जुड़ेinputs...
के डाइमेंशन साइज़ शामिल नहीं किए गए हैं. - (C8)
[0,N)
में मौजूद सभीi
के लिएelement_type(results[i]) = Ei
.
उदाहरण
// %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
सिमैंटिक्स
operand
को एलिमेंट के हिसाब से किसी ऐसे फ़्लोटिंग-पॉइंट टाइप में बदलता है जो exponent_bits
और mantissa_bits
का इस्तेमाल करता है. इसके बाद, इसे ओरिजनल फ़्लोटिंग-पॉइंट टाइप में वापस बदलता है और output
टेंसर बनाता है.
ज़्यादा औपचारिक तरीके से:
- मूल वैल्यू के मैन्टीसा बिट को अपडेट किया जाता है, ताकि मूल वैल्यू को
roundToIntegralTiesToEven
सेमेंटेक्स का इस्तेमाल करके,mantissa_bits
के साथ दिखाई जा सकने वाली सबसे करीब की वैल्यू पर राउंड किया जा सके. - इसके बाद, अगर
mantissa_bits
, ओरिजनल वैल्यू के मैन्टिसा बिट की संख्या से कम है, तो मैन्टिसा बिट कोmantissa_bits
पर काट दिया जाता है. - इसके बाद, अगर इंटरमीडिएट नतीजे के एक्सपोनेंट बिट
exponent_bits
की दी गई रेंज में फ़िट नहीं होते, तो इंटरमीडिएट नतीजा, ओरिजनल चिह्न का इस्तेमाल करके इनफ़िनिटी में बदल जाता है या ओरिजनल चिह्न का इस्तेमाल करके अंडरफ़्लो को शून्य कर देता है. - संख्या में दिखाए जा सकने वाले डेटा टाइप के लिए,
dequantize_op_quantize( lambda operand: reduce_precision(operand, exponent_bits, mantissa_bits), operand, type(result))
करता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
(I2) | exponent_bits |
si32 टाइप का कॉन्स्टेंट |
(C2) |
(I3) | mantissa_bits |
si32 टाइप का कॉन्स्टेंट |
(C3) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
output |
फ़्लोटिंग-पॉइंट टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(output)
. - (C2)
1 <= exponent_bits
. - (C3)
0 <= mantissa_bits
.
उदाहरण
// 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
सिमैंटिक्स
StableHLO प्रोसेस ग्रिड में हर प्रोसेस ग्रुप में, computations
का इस्तेमाल करके, हर प्रोसेस के operand
टेंसर की वैल्यू पर रिडक्शन की प्रोसेस की जाती है. साथ ही, रिडक्शन के नतीजे को scatter_dimension
के हिसाब से अलग-अलग हिस्सों में बांटा जाता है. इसके बाद, result
बनाने के लिए, अलग-अलग हिस्सों को प्रोसेस के बीच स्कैटर किया जाता है.
यह कार्रवाई StableHLO प्रोसेस ग्रिड को process_groups
में बांट देती है. इसकी जानकारी इस तरह से दी गई है:
cross_replica(replica_groups)
अगरchannel_id <= 0 and use_global_device_ids = false
है, तो.cross_replica_and_partition(replica_groups)
अगरchannel_id > 0 and use_global_device_ids = false
है, तो.flattened_ids(replica_groups)
अगरchannel_id > 0 and use_global_device_ids = true
है, तो.
इसके बाद, हर process_group
में:
reduced_value = all_reduce(operand, replica_groups, channel_id, use_global_device_ids, computation)
.parts@sender = split(reduced_value@sender, dim(process_groups, 1), scatter_dimension)
.process_group
में सभीsender
के लिएresult@receiver = parts@sender[receiver_index]
, जहांreceiver_index = process_group.index(receiver)
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1), (C2), (C7), (C8) |
(I2) | scatter_dimension |
si64 टाइप का कॉन्स्टेंट |
(C1), (C2), (C8) |
(I3) | replica_groups |
si64 टाइप का दो डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C3-C5) |
(I4) | channel_id |
si64 टाइप का कॉन्स्टेंट |
(C6) |
(I5) | use_global_device_ids |
i1 टाइप का कॉन्स्टेंट |
(C6) |
(I6) | computation |
फ़ंक्शन | (C7) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C8-C9) |
कंस्ट्रेंट
- (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)
को इस तरह परिभाषित किया गया है:num_replicas
, अगरcross_replica
का इस्तेमाल किया गया हो.num_replicas
, अगरcross_replica_and_partition
का इस्तेमाल किया गया हो.num_processes
, अगरflattened_ids
का इस्तेमाल किया गया हो.
- (C5)
0 <= replica_groups < size(replica_groups)
. - (C6) अगर
use_global_device_ids = true
है, तोchannel_id > 0
. - (C7)
computation
का टाइप(tensor<E>, tensor<E>) -> (tensor<E>)
है, जहांis_promotable(element_type(operand), E)
. - (C8)
shape(result) = shape(operand)
इनके अलावा:dim(result, scatter_dimension) = dim(operand, scatter_dimension) / dim(process_groups, 1)
.
- (C9)
element_type(result) = E
.
उदाहरण
// 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
सिमैंटिक्स
inputs
और init_values
की विंडो पर, कम करने वाला फ़ंक्शन body
लागू करता है और results
बनाता है.
नीचे दिए गए डायग्राम में, उदाहरण के ज़रिए दिखाया गया है कि results...
में मौजूद एलिमेंट का हिसाब, inputs...
से कैसे लगाया जाता है.
ज़्यादा औपचारिक तौर पर,
results...[result_index] = reduce(windows, init_values, axes(inputs...), body)
(कम करें देखें) जहां:
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)
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | inputs |
वैरिएबल संख्या वाले टेंसर या हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर | (C1-C4), (C6), (C8), (C10), (C12), (C13), (C15) |
(I2) | init_values |
0-डाइमेंशन वाले टेंसर या हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर की वैरिएडिक संख्या | (C1), (C13) |
(I3) | window_dimensions |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C4), (C5), (C15) |
(I4) | window_strides |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C6), (C7), (C15) |
(I5) | base_dilations |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C8), (C9), (C15) |
(I6) | window_dilations |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C10), (C11), (C15) |
(I7) | padding |
si64 टाइप का दो डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C12), (C15) |
(I8) | body |
फ़ंक्शन | (C13) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
results |
टेंसर की वैरायडिक संख्या या हर टेंसर की क्वांटाइज़्ड टेंसर की संख्या | (C1), (C14-C16) |
कंस्ट्रेंट
- (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
का टाइप(tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ...,
हैtensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>)
जहांis_promotable(element_type(inputs[i]), Ei)
है. - (C14)
same(shape(results...))
. - (C15)
shape(results[0]) = num_windows
जहां: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)
[0,N)
में मौजूद सभीi
के लिएelement_type(results[i]) = Ei
.
उदाहरण
// %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]]
बाकी
सिमैंटिक्स
यह फ़ंक्शन, डिविडेंड lhs
और डिविज़र rhs
टेंसर के एलिमेंट के हिसाब से शेष निकालता है और result
टेंसर बनाता है.
ज़्यादा औपचारिक तौर पर, नतीजे का चिह्न, डिविडेंड से लिया जाता है और नतीजे की पूरी वैल्यू, हमेशा डिविज़र की पूरी वैल्यू से कम होती है.
शेष का हिसाब lhs - d * rhs
के तौर पर लगाया जाता है, जहां d
का हिसाब इस तरह लगाया जाता है:
- पूर्णांक के लिए:
stablehlo.divide(lhs, rhs)
. - फ़्लोट के लिए: राउंडिंग एट्रिब्यूट के साथ IEEE-754 से
division(lhs, rhs)
roundTowardZero
. - कॉम्प्लेक्स नंबर के लिए: अभी तय नहीं है (#997).
- क्वांटाइज़ किए गए डेटा टाइप के लिए:
dequantize_op_quantize(remainder, lhs, rhs, type(result))
.
फ़्लोटिंग-पॉइंट एलिमेंट टाइप के लिए, यह ऑपरेशन, IEEE-754 स्पेसिफ़िकेशन के remainder
ऑपरेशन से अलग है. यहां d
, lhs/rhs
की सटीक वैल्यू के सबसे करीब की इंटिग्रल वैल्यू होती है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
पूर्णांक, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
(I2) | rhs |
इंटीजर, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
पूर्णांक, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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
सिमैंटिक्स
मौजूदा प्रोसेस का replica_id
बनाता है.
आउटपुट
नाम | टाइप |
---|---|
result |
ui32 टाइप का 0-डाइमेंशनल टेंसर |
उदाहरण
%result = "stablehlo.replica_id"() : () -> tensor<ui32>
फिर से आकार देना
सिमैंटिक
operand
टेंसर को result
टेंसर में बदलता है. कॉन्सेप्ट के हिसाब से, इसका मतलब है कि कैननिकल यूआरएल का वही वर्शन इस्तेमाल किया जा रहा है, लेकिन उसका आकार बदला गया है. जैसे, tensor<2x3xf32>
से tensor<3x2xf32>
या tensor<6xf32>
पर.
ज़्यादा औपचारिक तौर पर, result[result_index] = operand[operand_index]
जहां index_space(result)
और index_space(operand)
की शब्दकोश के हिसाब से क्रम में, result_index
और operand_index
की एक ही स्थिति है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेंसर या क्वांटाइज़्ड टेंसर | (C1-C3) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या क्वांटाइज़्ड टेंसर | (C1-C3) |
कंस्ट्रेंट
- (C1)
element_type(result)
देने वाली कंपनी:- अगर
!is_per_axis_quantized(operand)
है, तोelement_type(operand)
. element_type(operand)
, हालांकिquantization_dimension(operand)
औरquantization_dimension(result)
अलग हो सकते हैं.
- अगर
- (C2)
size(operand) = size(result)
. - (C3) अगर
is_per_axis_quantized(operand)
:reduce(dims(operand, [0, 1, ..., quantization_dimension(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [0, 1, ..., quantization_dimension(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y)
.dim(operand, quantization_dimension(operand)) = dim(result, quantization_dimension(result))
.reduce(dims(operand, [quantization_dimension(operand) + 1, ..., rank(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [quantization_dimension(result) + 1, ..., rank(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y)
.
उदाहरण
// %operand: [[1, 2, 3], [4, 5, 6]]
%result = "stablehlo.reshape"(%operand) : (tensor<2x3xi32>) -> tensor<3x2xi32>
// %result: [[1, 2], [3, 4], [5, 6]]
विपरीत करें
सिमैंटिक
यह फ़ंक्शन, तय किए गए dimensions
के हिसाब से operand
में एलिमेंट का क्रम उलट देता है और result
टेंसर बनाता है. ज़्यादा औपचारिक तौर पर,
result[result_index] = operand[operand_index]
जहां:
operand_index[d] = dim(result, d) - result_index[d] - 1
अगरd
,dimensions
में है.operand_index[d] = result_index[d]
अन्य मामलों में.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1), (C3) |
(I2) | dimensions |
si64 टाइप का 1-डाइमेंशन टेंसर कॉन्सटेंट |
(C2), (C3) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1), (C3) |
कंस्ट्रेंट
- (C1)
type(operand) = type(result)
. - (C2)
is_unique(dimensions)
. - (C3)
0 <= dimensions < rank(result)
.
उदाहरण
// %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
सिमैंटिक्स
rng_distribution
एल्गोरिदम का इस्तेमाल करके, रैंडम नंबर जनरेट करता है और दिए गए आकार shape
का result
टेंसर बनाता है.
अगर rng_distribution = UNIFORM
है, तो रैंडम नंबर, इंटरवल [a, b)
में यूनिफ़ॉर्म डिस्ट्रिब्यूशन के हिसाब से जनरेट किए जाते हैं. अगर a >= b
है, तो इसका मतलब है कि व्यवहार की जानकारी नहीं दी गई है.
अगर rng_distribution = NORMAL
है, तो माध्य = a
और मानक विचलन = b
वाले सामान्य वितरण के बाद यादृच्छिक संख्याएं जनरेट होती हैं.
अगर b < 0
, व्यवहार तय नहीं है.
रैंडम नंबर जनरेट करने का सही तरीका लागू करने के तरीके से तय होता है. उदाहरण के लिए, हो सकता है कि वे डिटरमिनिस्टिक हों या न हों. साथ ही, हो सकता है कि वे छिपी हुई स्थिति का इस्तेमाल करें या न करें.
कई हिस्सेदारों के साथ बातचीत में, यह पता चला है कि इस ऑपरेशन का इस्तेमाल अब नहीं किया जा रहा है. इसलिए, आने वाले समय में हम इसे हटाने की कोशिश करेंगे (#597).
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | a |
पूर्णांक, बूलियन या फ़्लोटिंग-पॉइंट टाइप का 0-डाइमेंशन वाला टेंसर | (C1), (C2) |
(I2) | b |
पूर्णांक, बूलियन या फ़्लोटिंग-पॉइंट टाइप का 0-डाइमेंशनल टेंसर | (C1), (C2) |
(I3) | shape |
si64 टाइप का 1-डाइमेंशन टेंसर कॉन्सटेंट |
(C3) |
(I4) | rng_distribution |
UNIFORM और NORMAL का enum |
(C2) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
पूर्णांक, बूलियन या फ़्लोटिंग-पॉइंट टाइप का टेंसर | (सी1-सी3) |
कंस्ट्रेंट
- (C1)
element_type(a) = element_type(b) = element_type(result)
. - (C2) अगर
rng_distribution = NORMAL
है, तोis_float(a)
. - (C3)
shape(result) = shape
.
उदाहरण
// %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
सिमैंटिक्स
शुरुआती स्थिति initial_state
के आधार पर, एक जैसे यादृच्छिक बिट और अपडेट की गई आउटपुट स्थिति
output_state
से भरा output
दिखाता है. इसके लिए, rng_algorithm
के स्यूडोरैंडम नंबर जनरेटर एल्गोरिदम का इस्तेमाल किया जाता है. इस बात की गारंटी है कि आउटपुट, initial_state
के हिसाब से काम करेगा. हालांकि, इस बात की कोई गारंटी नहीं है कि यह अलग-अलग लागू करने के तरीकों के हिसाब से तय होगा.
rng_algorithm
इनमें से एक है:
DEFAULT
: लागू करने के तरीके से तय किया गया एल्गोरिद्म.THREE_FRY
: Threefry एल्गोरिदम का लागू करने का तरीका तय करने वाला वैरिएंट.*PHILOX
: Philox एल्गोरिदम का, लागू करने के तरीके से तय किया गया वैरिएंट.*
* देखें: Salmon et al. SC 2011. पैरलल रैंडम नंबर: बेहद आसान.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | rng_algorithm |
DEFAULT , THREE_FRY , और PHILOX का कुल डेटा |
(C2) |
(I2) | initial_state |
ui64 टाइप का एक डाइमेंशन वाला टेंसर |
(C1), (C2) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
output_state |
ui64 टाइप का एक डाइमेंशन वाला टेंसर |
(सी1) |
output |
इंटीजर या फ़्लोटिंग-पॉइंट टाइप का टेंसर |
कंस्ट्रेंट
- (C1)
type(initial_state) = type(output_state)
. - (C2)
size(initial_state)
की परिभाषा इस तरह दी गई है:- अगर
rng_algorithm = DEFAULT
है, तो लागू करने के बारे में तय किया जाता है. - अगर
rng_algorithm = THREE_FRY
है, तो2
. - अगर
rng_algorithm = PHILOX
है, तो2
या3
.
- अगर
उदाहरण
// %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
सिमैंटिक
operand
टेंसर पर, शून्य से दूर टाई को तोड़ते हुए, एलिमेंट के हिसाब से पूर्णांकन करता है और result
टेंसर बनाता है. IEEE-754 स्पेसिफ़िकेशन के हिसाब से, roundToIntegralTiesToAway
ऑपरेशन लागू करता है. क्वेंटीफ़ाइड टाइप के लिए, dequantize_op_quantize(round_nearest_afz, operand, type(result))
करता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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
सिमैंटिक्स
operand
टेंसर के एलिमेंट को सबसे करीब वाले पूर्णांक तक राउंड करता है. साथ ही, टेंसर में मौजूद एलिमेंट के बराबर होने पर, सम पूर्णांक को प्राथमिकता देता है. इससे result
टेंसर बनता है. IEEE-754 के स्पेसिफ़िकेशन के हिसाब से, roundToIntegralTiesToEven
ऑपरेशन लागू करता है. संख्या में दिखाए जा सकने वाले डेटा टाइप के लिए, dequantize_op_quantize(round_nearest_even, operand, type(result))
करता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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
सिमैंटिक
operand
टेंसर के हर एलिमेंट का वर्गमूल निकालता है और result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम किए जाते हैं:
- फ़्लोट के लिए: IEEE-754 से
rSqrt
. - कॉम्प्लेक्स नंबर के लिए: कॉम्प्लेक्स नंबर का व्युत्क्रम वर्गमूल.
- क्वांटाइज़्ड टाइप के लिए:
dequantize_op_quantize(rsqrt, operand, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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]]
स्कैटर
सिमैंटिक्स
results
टेंसर बनाता है, जो inputs
टेंसर के बराबर होते हैं. हालांकि, scatter_indices
से तय की गई कई स्लाइस को update_computation
का इस्तेमाल करके, updates
वैल्यू के साथ अपडेट किया जाता है.
नीचे दिया गया डायग्राम, updates...
में मौजूद एलिमेंट को results...
में मौजूद एलिमेंट पर मैप करने का तरीका दिखाने के लिए है. इसके लिए, एक ठोस उदाहरण दिया गया है. इस डायग्राम में, updates...
इंडेक्स के कुछ उदाहरण दिए गए हैं. साथ ही, यह भी बताया गया है कि ये इंडेक्स किन results...
इंडेक्स से जुड़े हैं.
index_space(updates[0])
में सभी update_index
के लिए, औपचारिक तौर पर:
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
को इस तरह परिभाषित किया गया है:scatter_indices[si0, ..., :, ..., siN]
, जहांsi
update_scatter_index
में अलग-अलग एलिमेंट हैं और:
कोindex_vector_dim
इंडेक्स में डाला जाता है, अगरindex_vector_dim
<rank(scatter_indices)
है.[scatter_indices[update_scatter_index]]
अन्य मामलों में.
axes(inputs[0])
मेंd_input
के लिए,full_start_index[d_input] = start_index[d_start]
अगरd_input = scatter_dims_to_operand_dims[d_start]
.full_start_index[d_input] = 0
अन्य मामलों में.
axes(inputs[0])
मेंd_input
के लिए,full_batching_index[d_input] = update_scatter_index[d_start - (d_start < index_vector_dim ? 0 : 1)]
अगरd_input = input_batching_dims[i_batching]
औरd_start = scatter_indices_batching_dims[i_batching]
है.full_batching_index[d_input] = 0
अन्य मामलों में.
update_window_index = update_index[update_window_dims...]
.full_window_index = [wi0, ..., 0, ..., wiN]
जहांwi
,update_window_index
में अलग-अलग एलिमेंट होता है और0
कोinserted_window_dims
औरinput_batching_dims
के इंडेक्स में डाला जाता है.result_index = full_start_index + full_batching_index + full_window_index
.
इस आधार पर, results = exec(schedule, inputs)
, जहां:
schedule
,index_space(updates[0])
का ऐसा क्रम है जिसे लागू करने का तरीका तय किया गया है.exec([update_index, ...], results) = exec([...], updated_results)
जहां:- अगर
result_index
,shape(results...)
के लिए तय सीमा के अंदर है updates_converted = to_destination_type( updates...[update_index], type(func_inputs(update_computation) [len(func_inputs(update_computation))//2:])... )
updated_values = update_computation(results...[result_index], updates_converted)
updated_results
,results
की कॉपी है, जिसमेंresults...[result_index]
कोupdated_values...
पर सेट किया गया है.- या फिर
updated_results = results
.
- अगर
exec([], results) = results
.
अगर indices_are_sorted
true
है, तो लागू करने के दौरान यह माना जा सकता है कि
scatter_indices
को scatter_dims_to_operand_dims
के हिसाब से क्रम में लगाया गया है.
अगर ऐसा नहीं है, तो व्यवहार तय नहीं किया जाता. ज़्यादा औपचारिक तौर पर, indices(result)
से लेकर सभी i1 < i2
के लिए, full_start_index(i1)
<= full_start_index(i2)
.
अगर unique_indices
, true
है, तो लागू करने पर यह मान लिया जा सकता है कि
सभी result_index
इंडेक्स यूनीक हैं. अगर unique_indices
की वैल्यू true
है, लेकिन जिन इंडेक्स में डेटा भेजा जा रहा है वे यूनीक नहीं हैं, तो डेटा के व्यवहार की जानकारी नहीं दी जाती.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | inputs |
वैरिएबल संख्या वाले टेंसर या हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर | (C1), (C2), (C4-C6), (C11), (C13), (C18), (C21), (C23-C24) |
(I2) | scatter_indices |
पूर्णांक टाइप का टेंसर | (C4), (C15), (C19), (C22) |
(I3) | updates |
वैरिएबल संख्या वाले टेंसर या हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर | (C3-C6), (C8) |
(I4) | update_window_dims |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C2), (C4), (C7-C8) |
(I5) | inserted_window_dims |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C2), (C4), (C9-C11) |
(I6) | input_batching_dims |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C2), (C4), (C9), (C12-13), (C17-18), (C20) |
(I7) | scatter_indices_batching_dims |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C14-C18) |
(I8) | scatter_dims_to_operand_dims |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C19-C21) |
(I9) | index_vector_dim |
si64 टाइप का कॉन्स्टेंट |
(C4), (C16), (C19), (C22) |
(I10) | indices_are_sorted |
i1 टाइप का कॉन्स्टेंट |
|
(I11) | unique_indices |
i1 टाइप का कॉन्स्टेंट |
|
(I12) | update_computation |
फ़ंक्शन | (C23) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
results |
वैरिएबल संख्या वाले टेंसर या हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर | (C24-C25) |
कंस्ट्रेंट
- (C1)
same(shape(inputs...))
. - (C2) `rank(inputs[0]) = size(update_window_dims) + size(inserted_window_dims)
- size(input_batching_dims)`.
- (C3)
same(shape(updates...))
. - (C4)
shape(updates[0]) = combine(update_scatter_dim_sizes, update_window_dim_sizes)
जहां:update_scatter_dim_sizes = shape(scatter_indices)
, सिवाय इसके किindex_vector_dim
से जुड़ेscatter_indices
के डाइमेंशन साइज़ को शामिल नहीं किया गया है.update_window_dim_sizes <= shape(inputs[0])
को छोड़कर,inserted_window_dims
औरinput_batching_dims
से जुड़ेinputs[0]
में डाइमेंशन के साइज़ को शामिल नहीं किया गया है.combine
,update_scatter_dim_sizes
कोupdate_scatter_dims
के ऐक्सिस पर औरupdate_window_dim_sizes
कोupdate_window_dims
के ऐक्सिस पर डालता है.
- (C5)
0 < size(inputs) = size(updates) = N
. - (C6)
element_type(updates...) = element_type(inputs...)
. - (C7)
is_unique(update_window_dims) and is_sorted(update_window_dims)
. - (C8)
0 <= update_window_dims < rank(updates[0])
. - (C9)
is_unique(concatenate(inserted_window_dims, input_batching_dims))
- (C10)
is_sorted(inserted_window_dims)
. - (C11)
0 <= inserted_window_dims < rank(inputs[0])
. - (C12)
is_sorted(input_batching_dims)
. - (C13)
0 <= input_batching_dims < rank(inputs[0]))
. - (C14)
is_unique(scatter_indices_batching_dims)
. - (C15)
0 <= scatter_indices_batching_dims < rank(scatter_indices)
. - (C16)
index_vector_dim not in scatter_indices_batching_dims
. - (C17)
size(input_batching_dims) == size(scatter_indices_batching_dims)
. - (C18)
dim(inputs[0], input_batching_dims...) = dim(scatter_indices, scatter_indices_batching_dims...)
. - (C19)
size(scatter_dims_to_operand_dims) = index_vector_dim < rank(scatter_indices) ? dim(scatter_indices, index_vector_dim) : 1
. - (C20)
is_unique(concatenate(scatter_dims_to_operand_dims, input_batching_dims))
. - (C21)
0 <= scatter_dims_to_operand_dims < rank(inputs[0])
. - (C22)
0 <= index_vector_dim <= rank(scatter_indices)
. - (C23)
update_computation
का टाइप(tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ..., tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>)
है, जहांis_promotable(element_type(inputs[i]), Ei)
है. - (C24)
shape(inputs...) = shape(results...)
. - (C25)
[0,N)
में मौजूद सभीi
के लिएelement_type(results[i]) = Ei
.
उदाहरण
// %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]]
// ]
// ]
चुनें
सिमैंटिक्स
यह result
टेंसर बनाता है, जिसमें हर एलिमेंट को on_true
या
on_false
टेंसर से चुना जाता है. यह चुने गए एलिमेंट की वैल्यू, pred
के उसी एलिमेंट की वैल्यू पर आधारित होती है.
ज़्यादा औपचारिक तौर पर, result[result_index] = pred_element ? on_true[result_index] :
on_false[result_index]
, जहां pred_element = rank(pred) = 0 ? pred[] :
pred[result_index]
. संख्या में दिखाए जा सकने वाले डेटा टाइप के लिए, dequantize_select_quantize(pred, on_true, on_false, type(result))
करता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | pred |
i1 टाइप का टेंसर |
(C1) |
(I2) | on_true |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1-C2) |
(I3) | on_false |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (सी2) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (सी2) |
कंस्ट्रेंट
- (C1)
rank(pred) = 0 or shape(pred) = shape(on_true)
. - (C2)
baseline_type(on_true) = baseline_type(on_false) = baseline_type(result)
.
उदाहरण
// %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
सिमैंटिक्स
select
का इस्तेमाल करके, input
टेंसर के reduce_window
के नतीजे के आधार पर, scatter
का इस्तेमाल करके source
टेंसर से वैल्यू दिखाता है और result
टेंसर बनाता है.
नीचे दिए गए डायग्राम में, उदाहरण के ज़रिए दिखाया गया है कि result
में मौजूद एलिमेंट का हिसाब, operand
और source
से कैसे लगाया जाता है.
ज़्यादा औपचारिक तौर पर:
selected_values = reduce_window_without_init(...)
के साथ ये इनपुट इस्तेमाल करें:inputs = [operand].
window_dimensions
,window_strides
, औरpadding
, जिनका इस्तेमाल वैसे ही किया जाता है.base_dilations = windows_dilations = 1
.body
को इस तरह परिभाषित किया गया है:
def body(arg0: tensor<E>, arg1: tensor<E>) -> tensor<E>: return select(arg0, arg1) ? arg0 : arg1;
यहां
E = element_type(operand)
औरreduce_window_without_init
,reduce_window
की तरह ही काम करते हैं. हालांकि,reduce
(reduce देखें) केschedule
में, शुरू में दी गई वैल्यू शामिल नहीं होती हैं. फ़िलहाल, यह नहीं बताया गया है कि अगर इससे जुड़ी विंडो में वैल्यू नहीं हैं, तो क्या होगा (#731).result[result_index] = reduce([source_values], [init_value], [0], scatter)
जहां:source_values = [source[source_index] for source_index in source_indices]
.selected_index(source_index) = operand_index
अगरselected_values[source_index]
मेंoperand_index
सेoperand
एलिमेंट है.source_indices = [source_index for source_index in indices(source) if selected_index(source_index) = result_index]
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (C1-C4), (C6), (C8-C11) |
(I2) | source |
टेनसॉर या हर टेनसॉर के हिसाब से क्वांटाइज़ किया गया टेनसॉर | (C1), (C2) |
(I3) | init_value |
0-डाइमेंशन टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (सी3) |
(I4) | window_dimensions |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C2), (C4), (C5) |
(I5) | window_strides |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C2), (C6), (C7) |
(I6) | padding |
si64 टाइप का दो डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C2), (C8) |
(I7) | select |
फ़ंक्शन | (C9) |
(I8) | scatter |
फ़ंक्शन | (C10) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (C11-C12) |
कंस्ट्रेंट
- (C1)
element_type(operand) = element_type(source)
. - (C2)
shape(source) = num_windows
जहां: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
का टाइप(tensor<E>, tensor<E>) -> tensor<i1>
है, जहांE = element_type(operand)
. - (C10)
scatter
का टाइप(tensor<E>, tensor<E>) -> tensor<E>
है, जहांis_promotable(element_type(operand), E)
. - (C11)
shape(operand) = shape(result)
. - (C12)
element_type(result) = E
.
उदाहरण
// %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]]
भेजें
सिमैंटिक्स
यह चैनल channel_id
को inputs
भेजता है और result
टोकन जनरेट करता है.
अगर is_host_transfer
true
है, तो ऑपरेशन, डेटा को होस्ट पर ट्रांसफ़र करता है. ऐसा न होने पर, यह डेटा को किसी दूसरे डिवाइस पर ट्रांसफ़र कर देता है. इसका मतलब है कि इसे लागू करने का तरीका तय करना है. यह फ़्लैग, channel_type
में दी गई जानकारी की डुप्लीकेट कॉपी है. इसलिए, आने वाले समय में हम इनमें से सिर्फ़ एक (#666) को ही सेव करने वाले हैं.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | inputs |
टेंसर या क्वांटाइज़्ड टेंसर की वैरायड संख्या | |
(I2) | token |
token |
|
(I3) | channel_id |
si64 टाइप का कॉन्स्टेंट |
|
(I4) | channel_type |
DEVICE_TO_DEVICE और DEVICE_TO_HOST का enum |
(C1) |
(I5) | is_host_transfer |
i1 टाइप का कॉन्स्टेंट |
(C1) |
आउटपुट
नाम | टाइप |
---|---|
result |
token |
कंस्ट्रेंट
- (C1)
channel_type
को इस तरह परिभाषित किया गया है:DEVICE_TO_HOST
अगरis_host_transfer = true
है, तो- अगर ऐसा नहीं है, तो
DEVICE_TO_DEVICE
.
उदाहरण
%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
सिमैंटिक्स
lhs
टेंसर पर, rhs
बिट की संख्या के हिसाब से, एलिमेंट के हिसाब से बाईं ओर शिफ़्ट करने का ऑपरेशन करता है और result
टेंसर बनाता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
पूर्णांक टाइप का टेंसर | (C1) |
(I2) | rhs |
इंटिजर टाइप का टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
पूर्णांक टाइप का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
type(lhs) = type(rhs) = type(result)
.
उदाहरण
// %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
सिमैंटिक्स
lhs
टेंसर पर, एलिमेंट के हिसाब से दाईं ओर शिफ़्ट करने वाला ऐरिमेटिक ऑपरेशन करता है. यह ऑपरेशन, rhs
बिट की संख्या के हिसाब से किया जाता है. साथ ही, यह result
टेंसर बनाता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
पूर्णांक टाइप का टेंसर | (C1) |
(I2) | rhs |
इंटिजर टाइप का टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
पूर्णांक टाइप का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
type(lhs) = type(rhs) = type(result)
.
उदाहरण
// %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
सिमैंटिक्स
lhs
टेंसर पर, एलिमेंट के हिसाब से दाईं ओर शिफ़्ट करने का लॉजिकल ऑपरेशन करता है. यह ऑपरेशन, rhs
बिट की संख्या के हिसाब से किया जाता है. इससे result
टेंसर बनता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
पूर्णांक टाइप का टेंसर | (C1) |
(I2) | rhs |
इंटिजर टाइप का टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
पूर्णांक टाइप का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
type(lhs) = type(rhs) = type(result)
.
उदाहरण
// %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]
हस्ताक्षर
सिमैंटिक्स
operand
के हर एलिमेंट का साइन दिखाता है और result
टेंसर बनाता है.
ज़्यादा औपचारिक तौर पर, हर एलिमेंट x
के लिए, Python सिंटैक्स का इस्तेमाल करके सेमेटिक्स को इस तरह दिखाया जा सकता है:
def sign(x):
if is_integer(x):
if compare(x, 0, LT, SIGNED): return -1
if compare(x, 0, EQ, SIGNED): return 0
return 1
elif is_float(x):
if is_nan(x): return NaN
if compare(x, -0.0, EQ, FLOAT): return -0.0
if compare(x, +0.0, EQ, FLOAT): return +0.0
if compare(x, 0.0, LT, FLOAT): return -1.0
return 1.0
elif is_complex(x):
if is_nan(real(x)) or is_nan(imag(x)): return (NaN, NaN)
if compare(x, (0.0, 0.0), EQ, FLOAT): return (0.0, 0.0)
return divide(x, convert(abs(x), type(x)))
क्वांटाइज़्ड टाइप के लिए, dequantize_op_quantize(sign, operand, type(result))
की मदद से काम करता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
साइन किए हुए पूर्णांक, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
साइन वाला पूर्णांक, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// 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]
ज्या
सिमैंटिक्स
operand
टेंसर पर एलिमेंट के हिसाब से साइन ऑपरेशन करता है और result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम किए जाते हैं:
- फ़्लोट के लिए: IEEE-754 से
sin
. - सम्मिश्र संख्याओं के लिए: कॉम्प्लेक्स साइन.
- क्वांटाइज़ किए गए टाइप के लिए:
dequantize_op_quantize(sine, operand, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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]]
स्लाइस
सिमैंटिक्स
स्टैटिक तरीके से कैलकुलेट किए गए शुरुआती इंडेक्स का इस्तेमाल करके, operand
से स्लाइस निकालता है और result
टेंसर बनाता है. start_indices
में हर डाइमेंशन के लिए स्लाइस के शुरुआती इंडेक्स होते हैं, limit_indices
में हर डाइमेंशन के लिए स्लाइस के आखिरी इंडेक्स (सिर्फ़) होते हैं, और strides
में हर डाइमेंशन के लिए स्ट्राइड होते हैं.
फ़ॉर्मल तौर पर, result[result_index] = operand[operand_index]
जहां
operand_index = start_indices + result_index * strides
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (C1-C3), (C5) |
(I2) | start_indices |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C2), (C3), (C5) |
(I3) | limit_indices |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C2), (C3), (C5) |
(I4) | strides |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C2), (C4) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या हर टेन्सर क्वांटाइज़्ड टेंसर | (C1), (C5) |
कंस्ट्रेंट
- (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)
.
उदाहरण
// %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]
// ]
क्रम से लगाएं
सिमैंटिक्स
comparator
के हिसाब से, inputs
के एक डाइमेंशन वाले स्लाइस को डाइमेंशन dimension
के साथ क्रम से लगाता है और results
बनाता है.
अन्य ऑपरेशन में मिलते-जुलते इनपुट के उलट, dimension
में नेगेटिव वैल्यू इस्तेमाल की जा सकती हैं. इन वैल्यू के बारे में यहां बताया गया है. आने वाले समय में, ऐसा हो सकता है कि एक जैसा रहने की वजह से
इनकी अनुमति न दी जाए (#1377).
अगर is_stable
सही है, तो क्रम से लगाने की प्रोसेस स्थिर होती है. इसका मतलब है कि तुलना करने वाले फ़ंक्शन के हिसाब से एक जैसे माने गए एलिमेंट का क्रम नहीं बदलता. अगर सिर्फ़ एक इनपुट है, तो तुलना करने वाले फ़ंक्शन के हिसाब से, दो एलिमेंट e1
और e2
एक जैसे होंगे, सिर्फ़ तब जब comparator(e1, e2) = comparator(e2, e1) = false
हो. एक से ज़्यादा इनपुट के लिए, यह कैसे काम करता है, यह जानने के लिए नीचे दी गई जानकारी देखें.
ज़्यादा औपचारिक तौर पर, index_space(results[0])
में मौजूद सभी result_index
के लिए:
adjusted_dimension = dimension >= 0 ? dimension : rank(inputs[0]) + dimension
.result_slice = [ri0, ..., :, ..., riR-1]
जहांriN
,result_index
में अलग-अलग एलिमेंट है और:
कोadjusted_dimension
पर डाला गया है.inputs_together = (inputs[0]..., ..., inputs[N-1]...)
.results_together[result_slice] = sort(inputs_together[result_slice], comparator_together)
.- जहां
sort
, 1-डाइमेंशन वाले स्लाइस को उस क्रम में क्रम से लगाता है जिस क्रम में बाईं ओर मौजूद आर्ग्युमेंट, दाईं ओर मौजूद दूसरे आर्ग्युमेंट से कम होता है. ऐसे में,comparator_together
true
वैल्यू दिखाता है. 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
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | inputs |
वैरिएबल संख्या वाले टेंसर या हर टेंसर के लिए क्वांटाइज़ किए गए टेंसर | (C1-C5) |
(I2) | dimension |
si64 टाइप का कॉन्स्टेंट |
(C4) |
(I3) | is_stable |
i1 टाइप का कॉन्स्टेंट |
|
(I4) | comparator |
फ़ंक्शन | (C5) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
results |
टेंसर की वैरायडिक संख्या या हर टेंसर की क्वांटाइज़्ड टेंसर की संख्या | (C2), (C3) |
कंस्ट्रेंट
- (C1)
0 < size(inputs)
. - (C2)
type(inputs...) = type(results...)
. - (C3)
same(shape(inputs...) + shape(results...))
. - (C4)
-R <= dimension < R
, जहांR = rank(inputs[0])
. - (C5)
comparator
का टाइप(tensor<E1>, tensor<E1>, ..., tensor<EN-1>, tensor<EN-1>) -> tensor<i1>
है, जहांEi = element_type(inputs[i])
.
उदाहरण
// %input0 = [[1, 2, 3], [3, 2, 1]]
// %input1 = [[3, 2, 1], [1, 2, 3]]
%result0, %result1 = "stablehlo.sort"(%input0, %input1) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>, %arg2: tensor<i64>, %arg3: tensor<i64>):
%predicate = "stablehlo.compare"(%arg0, %arg1) {
comparison_direction = #stablehlo<comparison_direction GT>
} : (tensor<i64>, tensor<i64>) -> tensor<i1>
"stablehlo.return"(%predicate) : (tensor<i1>) -> ()
}) {
dimension = 0 : i64,
is_stable = true
} : (tensor<2x3xi64>, tensor<2x3xi64>) -> (tensor<2x3xi64>, tensor<2x3xi64>)
// %result0 = [[3, 2, 3], [1, 2, 1]]
// %result1 = [[1, 2, 1], [3, 2, 3]]
sqrt
सिमैंटिक
operand
टेंसर के हर एलिमेंट का वर्गमूल निकालता है और result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- फ़्लोट के लिए: IEEE-754 से
squareRoot
. - सम्मिश्र संख्याओं के लिए: कॉम्प्लेक्स नंबर का वर्गमूल.
- क्वांटाइज़्ड टाइप के लिए:
dequantize_op_quantize(sqrt, operand, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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]]
घटाना
सिमैंटिक्स
दो टेंसर lhs
और rhs
के एलिमेंट के हिसाब से घटाव करता है और result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- पूर्णांकों के लिए: पूर्णांक घटाना.
- फ़्लोट के लिए: IEEE-754 से
subtraction
. - कॉम्प्लेक्स नंबर के लिए: कॉम्प्लेक्स नंबर घटाना.
- क्वांटाइज़ किए गए डेटा टाइप के लिए:
dequantize_op_quantize(subtract, lhs, rhs, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
पूर्णांक, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
(I2) | rhs |
पूर्णांक, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (सी1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
पूर्णांक, फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
.
उदाहरण
// %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
सिमैंटिक्स
operand
टेंसर पर एलिमेंट-वाइज़ टेंगेंट ऑपरेशन करता है और एक
result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम किए जाते हैं:
- फ़्लोट के लिए: IEEE-754 से
tan
. - सम्मिश्र संख्याओं के लिए: सम्मिश्र टैंजेंट.
- क्वांटाइज़ किए गए टाइप के लिए:
dequantize_op_quantize(tan, operand, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %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
सिमैंटिक्स
operand
टेंसर पर एलिमेंट के हिसाब से हाइपरबोलिक टैंजेंट ऑपरेशन करता है और
result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- फ़्लोट के लिए: IEEE-754 से
tanh
. - कॉम्प्लेक्स नंबर के लिए: कॉम्प्लेक्स हाइपरबोलिक टैंजेंट.
- क्वांटाइज़ किए गए डेटा टाइप के लिए:
dequantize_op_quantize(tanh, operand, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
baseline_type(operand) = baseline_type(result)
.
उदाहरण
// %operand: [-1.0, 0.0, 1.0]
%result = "stablehlo.tanh"(%operand) : (tensor<3xf32>) -> tensor<3xf32>
// %result: [-0.76159416, 0.0, 0.76159416]
ट्रांसपोज़ करें
सिमैंटिक्स
permutation
का इस्तेमाल करके, operand
टेंसर के डाइमेंशन को बदलता है और एक
result
टेंसर बनाता है. औपचारिक रूप से, result[result_index] = operand[operand_index]
जहां result_index[d] = operand_index[permutation[d]]
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
टेंसर या क्वांटाइज़ किया गया टेंसर | (C1-C4) |
(I2) | permutation |
si64 टाइप का एक डाइमेंशन वाला टेंसर कॉन्सटेंट |
(C2-C4) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टेंसर या क्वांटाइज़ किया गया टेंसर | (C1), (C3-C4) |
कंस्ट्रेंट
- (C1)
element_type(result)
देने वाली कंपनी:- अगर
!is_per_axis_quantized(operand)
है, तोelement_type(operand)
. element_type(operand)
, हालांकिquantization_dimension(operand)
औरquantization_dimension(result)
अलग हो सकते हैं.
- अगर
- (C2)
permutation
,range(rank(operand))
का पर्म्यूटेशन है. - (C3)
shape(result) = dim(operand, permutation...)
. - (C4) अगर
is_per_axis_quantized(result)
है, तोquantization_dimension(operand) = permutation(quantization_dimension(result))
.
उदाहरण
// %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
सिमैंटिक
कम या ऊपरी त्रिकोणीय गुणांक वाले आव्यूहों वाले रैखिक समीकरणों के सिस्टम के बैच को हल करता है.
a
और b
के लिए, result[i0, ..., iR-3, :, :]
op(a[i0, ..., iR-3, :, :]) * x = b[i0, ..., iR-3, :, :]
का समाधान है, जब left_side
true
है या x * op(a[i0, ..., iR-3, :, :]) = b[i0, ..., iR-3, :, :]
, जब left_side
false
है. यह वैरिएबल x
का समाधान है, जहां op(a)
का पता transpose_a
से चलता है. transpose_a
इनमें से कोई एक हो सकता है:
NO_TRANSPOSE
:a
को जैसा है वैसा ही इस्तेमाल करके कार्रवाई करें.TRANSPOSE
:a
के ट्रांसपोज़ (पंक्तियों और कॉलम की जगह बदलना) पर कार्रवाई करें.ADJOINT
:a
के कॉंजुगेट ट्रांसपोज़ पर ऑपरेशन करें.
अगर lower
true
है या a
का ऊपरी त्रिभुज है, तो इनपुट डेटा को सिर्फ़ a
के निचले त्रिभुज से पढ़ा जाएगा, नहीं तो. आउटपुट डेटा उसी ट्राएंगल में दिखाया जाता है;
अन्य ट्राएंगल में मौजूद वैल्यू, लागू करने के तरीके के हिसाब से तय होती हैं.
अगर unit_diagonal
की वैल्यू 'सही' है, तो लागू करने के दौरान यह माना जा सकता है कि a
के डायगनल एलिमेंट 1 के बराबर हैं. ऐसा न होने पर, व्यवहार तय नहीं किया जा सकता.
संख्या में दिखाए जा सकने वाले डेटा टाइप के लिए, dequantize_op_quantize(lambda x, y: triangular_solve(x, y, left_side, lower,
unit_diagonal, transpose_a), a, b, type(result))
करता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | a |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप का टेंसर या हर टेंसर के हिसाब से क्वांटाइज़ किया गया टेंसर | (C1-C3) |
(I2) | b |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (C1-C4) |
(I3) | left_side |
i1 टाइप का कॉन्स्टेंट |
(सी3) |
(आई4) | lower |
i1 टाइप का कॉन्स्टेंट |
|
(I5) | unit_diagonal |
i1 टाइप का कॉन्स्टेंट |
|
(I6) | transpose_a |
NO_TRANSPOSE , TRANSPOSE , और ADJOINT का enum |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट या कॉम्प्लेक्स टाइप या हर टेंसर क्वांटाइज़्ड टेंसर का टेंसर | (सी1) |
कंस्ट्रेंट
- (C1)
baseline_element_type(a) = baseline_element_type(b)
. - (C2)
2 <= rank(a) = rank(b) = R
. - (C3)
shape(a)
औरshape(b)
के बीच का संबंध इस तरह बताया गया है:shape(a)[:-3] = shape(b)[:-3]
.dim(a, -2) = dim(a, -1) = dim(b, left_side ? -2 : -1)
.
- (C4)
baseline_type(b) = baseline_type(result)
.
उदाहरण
// %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]
// ]
टपल
सिमैंटिक्स
वैल्यू val
से result
ट्यूपल बनाता है.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | val |
वैरिएबल वैल्यू की संख्या | (सी1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
टपल | (C1) |
कंस्ट्रेंट
- (C1)
result
का टाइपtuple<E0, ..., EN-1>
है, जहांEi = type(val[i])
है.
उदाहरण
// %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
सिमैंटिक
operand
टाइप की ओर से तय किए गए क्वांटाइज़ेशन पैरामीटर के मुताबिक, क्वांटाइज़ किए गए टेंसर operand
के एलिमेंट के हिसाब से कन्वर्ज़न करता है. यह फ़्लोट करने वाले टेंसर result
में होता है.
औपचारिक तौर पर, result = dequantize(operand)
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
क्वांटाइज़ किया गया टेंसर | (C1), (C2) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
फ़्लोटिंग-पॉइंट टाइप का टेंसर | (C1), (C2) |
कंस्ट्रेंट
- (C1)
shape(operand) = shape(result)
. - (C2)
element_type(result) = expressed_type(operand)
.
उदाहरण
// %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
सिमैंटिक्स
यह फ़ंक्शन, result
टाइप के क्वांटाइज़ेशन पैरामीटर के मुताबिक, फ़्लोटिंग-पॉइंट टेंसर या क्वांटाइज़ किए गए टेंसर के operand
एलिमेंट के हिसाब से टेंसर result
में बदलता है.
ज़्यादा औपचारिक तौर पर,
- अगर
is_float(operand)
:result = quantize(operand, type(result))
.
- अगर
is_quantized(operand)
:float_result = dequantize(operand)
.result = quantize(float_result, type(result))
.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
फ़्लोटिंग-पॉइंट या क्वांटाइज़ किए गए टाइप का टेंसर | (C1), (C2) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
क्वांटाइज़ किया गया टेंसर | (C1), (C2) |
कंस्ट्रेंट
- (C1)
shape(operand) = shape(result)
. - (C2)
expressed_type(result) = is_float(operand) ? element_type(operand) : expressed_type(operand)
.
उदाहरण
// %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]
जबकि
सिमैंटिक्स
body
फ़ंक्शन को 0 या उससे ज़्यादा बार चलाने पर आउटपुट जनरेट करता है, जबकि
cond
फ़ंक्शन true
आउटपुट करता है. ज़्यादा औपचारिक तौर पर, Python सिंटैक्स का इस्तेमाल करके सिमैंटिक्स को इस तरह दिखाया जा सकता है:
internal_state = operand
while cond(*internal_state):
internal_state = body(*internal_state)
results = internal_state
अनंत लूप का व्यवहार अभी तय नहीं है (#383).
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | operand |
वैरिएडिक संख्या वाले टेंसर, क्वांटाइज़ किए गए टेंसर या टोकन | (C1-C3) |
(I2) | cond |
फ़ंक्शन | (C1) |
(I3) | body |
फ़ंक्शन | (सी2) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
results |
टेंसर की वैरायडिक संख्या, क्वांटाइज़ किए गए टेंसर या टोकन | (C3) |
कंस्ट्रेंट
- (C1)
cond
का टाइप(T0, ..., TN-1) -> tensor<i1>
है, जहांTi = type(operand[i])
. - (C2)
body
का टाइप(T0, ..., TN-1) -> (T0, ..., TN-1)
है, जहांTi = type(operand[i])
. - (C3)
type(results...) = type(operand...)
.
उदाहरण
// %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
सिमैंटिक्स
दो टेंसर lhs
और rhs
के एलिमेंट के हिसाब से XOR करता है और result
टेंसर बनाता है. एलिमेंट टाइप के आधार पर, ये काम करता है:
- बूलियन के लिए: लॉजिकल XOR.
- पूर्णांक के लिए: बिटवाइज़ XOR.
इनपुट
लेबल | नाम | टाइप | कंस्ट्रेंट |
---|---|---|---|
(I1) | lhs |
बूलियन या पूर्णांक टाइप का टेंसर | (सी1) |
(I2) | rhs |
बूलियन या इंटेजर टाइप का टेंसर | (सी1) |
आउटपुट
नाम | टाइप | कंस्ट्रेंट |
---|---|---|
result |
बूलियन या इंटेजर टाइप का टेंसर | (C1) |
कंस्ट्रेंट
- (C1)
type(lhs) = type(rhs) = type(result)
.
उदाहरण
// 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]]
भाषा सिंक करने की सुविधा
फ़िलहाल, StableHLO प्रोग्राम में कभी-कभी ऐसी कार्रवाइयां शामिल होती हैं जिन्हें StableHLO ने तय नहीं किया है.
मॉड्यूल, फ़ंक्शन, कॉल, और रिटर्न
StableHLO, ModuleOp, FuncOp, CallOp, और ReturnOp के लिए, अपस्ट्रीम MLIR ऑपरेशन का इस्तेमाल करता है. ऐसा मौजूदा MLIR मशीनरी के साथ बेहतर इंटरऑपरेबिलिटी के लिए किया गया था, क्योंकि FuncOp और ModuleOp को टारगेट करने के लिए कई काम के पास लिखे गए हैं. साथ ही, कई कंपाइलेशन लाइन में इन ऑपरेशन के मौजूद होने की उम्मीद की जाती है. इन ऑपरेशन पर, पूरी तरह से काम करने की गारंटी लागू होती है. अगर इन ऑपरेशन में कभी भी ऐसा कोई बदलाव होता है जो काम नहीं करता है, जैसे कि हटाना, तो काम करने के लिए StableHLO के बराबर के ऑपरेशन जोड़े जाएंगे.
CHLO
CHLO ऑपसेट में उच्च स्तर की कार्रवाइयां होती हैं, जो StableHLO में डीकंपोज़ होती हैं. फ़िलहाल, CHLO के साथ काम करने की कोई गारंटी नहीं है. साथ काम करने की गारंटी के लिए, क्रम से लगाने से पहले chlo-legalize-to-stablehlo पास का इस्तेमाल करना ज़रूरी है.
शेप से जुड़ी कार्रवाइयां
कम्यूनिटी में, आकार का हिसाब लगाने के लिए, डाइनैमिक StableHLO प्रोग्राम में कोर MLIR डायलेक्ट के कुछ ऑपरेशन का इस्तेमाल करना एक आम इस्तेमाल का उदाहरण है.
आम तौर पर, इनमें shape_of
या num_elements
जैसे shape
बोली ऑप्ट, dim
या from_elements
जैसे tensor
बोली ऑप्ट, और पहले से मौजूद index
टाइप शामिल होते हैं.
डाइनैमिक RFC > O2 के मुताबिक, ये टाइप दायरे से बाहर हैं. हालांकि, इंटरऑपरेबिलिटी के मकसद से index
टाइप के लिए कुछ सहायता शामिल की गई है. इन विकल्पों या टाइप के काम करने की कोई गारंटी नहीं है. shape-legalize-to-stablehlo पास का इस्तेमाल करके, इन ऑपरेशन को पूरी तरह से काम करने वाले StableHLO ऑपरेशन में बदला जा सकता है.
ऐसी कार्रवाइयां जो अब रोक दी गई हैं
StableHLO के कई ऑपरेशन, MHLO से इनहेरिट किए गए थे. ये ऑपरेशन अब काम नहीं करते और StableHLO से हटा दिए जाएंगे. इन एलिमेंट को हटाने के बारे में पूरी जानकारी, StableHLO v1.0 Cleanup #2283 में देखी जा सकती है. इन सुविधाओं के बंद होने से जुड़ी ट्रैकर समस्या #2340 है.
ये कार्रवाइयां कुछ कैटगरी में आती हैं:
- StableHLO ऑपरेशन की "एचएलओ में नहीं है" कैटगरी - ये शुरू में StableHLO ऑपसेट का हिस्सा थे, लेकिन बाद में यह पाया गया कि ये इसमें सही से फ़िट नहीं होते:
broadcast
,create_token
,cross-replica-sum
,dot
,einsum
,torch_index_select
,unary_einsum
(#3). - इस्तेमाल नहीं किए गए ऑपरेशन - हो सकता है कि ये ऑपरेशन किसी समय काम के रहे हों, लेकिन ऑपरेशन को सही तरीके से डेवलप नहीं किया गया था या इन ऑपरेशन का इस्तेमाल करने वाली पाइपलाइन को फिर से तैयार किया गया हो, ताकि अब इनकी ज़रूरत न पड़े. इसमें
map
,tuple
(#598),get_tuple_element
,rng
,complex
तुलनाएं #560, और कॉन्वोल्यूशनwindow_reversal
(#1181) शामिल हैं.
इनमें से कुछ ऑपरेशन आसानी से हटाए जा सकते हैं, क्योंकि इन्हें मौजूदा ऑपरेशन (broadcast
, create_token
, cross-replica-sum
, dot
,
unary_einsum
) का इस्तेमाल करके दिखाया जा सकता है. साथ ही, ये ऑपरेशन, काम करने की मौजूदा विंडो (छह महीने) खत्म होने के बाद हटा दिए जाएंगे. अन्य अनुरोधों को हटाने के लिए अब भी पता लगाया जा रहा है (einsum
,
get_tuple_element
, map
, rng
torch_index_select
, tuple
, complex
तुलनाएं, window_reversal
). समुदाय से मिले सुझाव बाकी हैं. इन ऑपरेशन को या तो हटा दिया जाएगा या पूरी मदद के साथ स्पेसिफ़िकेशन में शामिल किया जाएगा. जब तक इन ऑपरेशंस के फ़्यूचर के बारे में जानकारी नहीं मिल जाती, तब तक इनके साथ सिर्फ़ छह महीने तक काम करने की गारंटी है.
प्लान लागू करना
क्रम से चलने वाला प्रोसेस
StableHLO प्रोग्राम, main
फ़ंक्शन के लिए इनपुट वैल्यू देकर और आउटपुट वैल्यू की गणना करके किया जाता है. किसी फ़ंक्शन के आउटपुट वैल्यू का हिसाब, उससे जुड़े return
op में रूट किए गए ऑपरेशन के ग्राफ़ का इस्तेमाल करके लगाया जाता है.
ऑपरेशन लागू करने का क्रम, लागू करने के तरीके के हिसाब से तय होता है. हालांकि, यह क्रम डेटाफ़्लो के हिसाब से होना चाहिए. इसका मतलब है कि ऑपरेशन का इस्तेमाल करने से पहले, उन्हें लागू किया जाना चाहिए. StableHLO में, साइड-इफ़ेक्ट वाले सभी ऑपरेशन एक टोकन का इस्तेमाल करते हैं और एक टोकन जनरेट करते हैं. after_all
की मदद से, कई टोकन को एक टोकन में मल्टीप्लेक्स किया जा सकता है. इसलिए, साइड-इफ़ेक्ट के लागू होने का क्रम भी डेटाफ़्लो के साथ अलाइन होता है. उदाहरण के लिए, नीचे दिए गए प्रोग्राम में, निर्देशों को लागू करने के दो संभावित क्रम हैं: %0
→ %1
→ %2
→ return
और %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>
}
ज़्यादा औपचारिक तौर पर, StableHLO प्रोसेस इनका कॉम्बिनेशन है:
1) StableHLO प्रोग्राम, 2) ऑपरेशन के स्टेटस (अभी तक लागू नहीं किए गए,
पहले से लागू हैं), और 3) इंटरमीडिएट वैल्यू जिन पर प्रोसेस काम कर रही है.
यह प्रोसेस, main
फ़ंक्शन के इनपुट वैल्यू से शुरू होती है. इसके बाद, ऑपरेशन की स्थिति और इंटरमीडिएट वैल्यू को अपडेट करने वाले ऑपरेशन के ग्राफ़ के ज़रिए आगे बढ़ती है और आउटपुट वैल्यू के साथ खत्म होती है. इसे आधिकारिक तौर पर कब लागू किया जाएगा, यह तय नहीं है
(#484).
साथ-साथ एक्ज़ीक्यूशन
StableHLO प्रोग्राम को एक साथ चलाया जा सकता है. साथ ही, इन्हें num_replicas
x num_partitions
के 2D प्रोसेस ग्रिड में व्यवस्थित किया जा सकता है. इन दोनों का टाइप ui32
होता है.
StableHLO प्रोसेस ग्रिड में, एक ही समय पर StableHLO की num_replicas * num_partitions
प्रोसेस चल रही हैं. हर प्रोसेस का एक यूनीक process_id = (replica_id, partition_id)
होता है. जैसे, replica_ids = range(num_replicas)
में replica_id
और partition_ids = range(num_partitions)
में partition_id
, दोनों का टाइप ui32
है.
प्रोसेस ग्रिड का साइज़, हर प्रोग्राम के लिए स्टैटिक तौर पर तय होता है. आने वाले समय में, हम इसे StableHLO प्रोग्राम #650 का एक साफ़ तौर पर हिस्सा बनाने जा रहे हैं. साथ ही, प्रोसेस ग्रिड में हर प्रोसेस की स्थिति भी स्टैटिक तौर पर तय होती है. हर प्रोसेस के पास, replica_id
और partition_id
ऑपरेशन की मदद से, प्रोसेस ग्रिड में अपनी जगह का ऐक्सेस होता है.
प्रोसेस ग्रिड में, सभी प्रोग्राम एक जैसे हो सकते हैं ("एक प्रोग्राम, कई डेटा" स्टाइल में), सभी अलग-अलग हो सकते हैं ("कई प्रोग्राम, कई डेटा" स्टाइल में) या इन दोनों के बीच कुछ भी हो सकता है. आने वाले समय में, हम एक साथ चलने वाले StableHLO प्रोग्राम तय करने के लिए, अन्य तरीकों के साथ काम करने की सुविधा उपलब्ध कराएंगे. इनमें GSPMD (#619) भी शामिल है.
प्रोसेस ग्रिड में, प्रोसेस एक-दूसरे से अलग होती हैं - उनकी ऑपरेशन की अलग-अलग स्थितियां होती हैं और इनपुट/इंटरमीडिएट/आउटपुट वैल्यू अलग-अलग होती हैं. साथ ही, ज़्यादातर ऑपरेशन अलग-अलग प्रोसेस के बीच अलग-अलग किए जाते हैं. हालांकि, नीचे बताए गए कुछ ऑपरेशन को छोड़कर, बाकी प्रोसेस अलग-अलग होती हैं.
ज़्यादातर ऑपरेशन को सिर्फ़ एक ही प्रोसेस की वैल्यू का इस्तेमाल करके लागू किया जाता है. इसलिए, आम तौर पर इन वैल्यू को उनके नाम से रेफ़र करना आसान होता है.
हालांकि, एक साथ कई ऑपरेशन करने के बारे में बताने के लिए, यह जानकारी काफ़ी नहीं है. इसलिए, किसी खास प्रोसेस में वैल्यू name
के बारे में बताने के लिए, name@process_id
का इस्तेमाल किया जाता है. इस हिसाब से, बिना शर्त वाले name
को name@(replica_id(), partition_id())
के लिए शॉर्टहैंड के तौर पर देखा जा सकता है.
सभी प्रोसेस को लागू करने का क्रम, लागू करने के तरीके के हिसाब से तय होता है. हालांकि, पॉइंट-टू-पॉइंट कम्यूनिकेशन और एक साथ कई ऑपरेशन की वजह से होने वाले सिंक्रोनाइज़ेशन को छोड़कर, ऐसा होता है.
पॉइंट-टू-पॉइंट कम्यूनिकेशन
StableHLO प्रोसेस, StableHLO चैनलों के ज़रिए एक-दूसरे से संपर्क कर सकती हैं. किसी चैनल को si64
टाइप के पॉज़िटिव आईडी से दिखाया जाता है. अलग-अलग ऑपरेशन की मदद से, चैनलों को वैल्यू भेजी जा सकती हैं और उनसे वैल्यू ली जा सकती हैं.
इस बारे में ज़्यादा जानकारी बाद में दी जाएगी. जैसे, ये चैनल आईडी कहां से आ रहे हैं, प्रोसेस प्रोग्राम को इनके बारे में कैसे पता चलता है, और इनके ज़रिए किस तरह का सिंक किया जाता है (#484).
स्ट्रीमिंग कम्यूनिकेशन
हर StableHLO प्रोसेस के पास दो स्ट्रीमिंग इंटरफ़ेस का ऐक्सेस होता है:
- Infeed, जिससे कॉन्टेंट को पढ़ा जा सकता है.
- ऐसा आउटफ़ीड जिसमें डेटा डाला जा सकता है.
ये चैनल, प्रोसेस के बीच संपर्क करने के लिए इस्तेमाल किए जाते हैं और इनकी वजह से भी प्रोसेस अपने दोनों सिरों पर होती हैं. वहीं, इनफ़ीड और आउटफ़ीड को लागू करने के लिए, अलग-अलग तरीके अपनाए जाते हैं.
इस बारे में आगे की जानकारी अभी उपलब्ध नहीं है. जैसे, स्ट्रीमिंग कम्यूनिकेशन से, निर्देशों को लागू करने के क्रम पर क्या असर पड़ता है और इससे किस तरह का सिंक किया जाता है (#484).
कलेक्टिव ऑपरेशन
StableHLO में छह कलेटिव ऑपरेशन हैं: all_gather
, all_reduce
,
all_to_all
, collective_broadcast
, collective_permute
, और
reduce_scatter
. ये सभी ऑपरेशन, StableHLO प्रोसेस ग्रिड में मौजूद प्रोसेस को StableHLO प्रोसेस ग्रुप में बांटते हैं. साथ ही, हर प्रोसेस ग्रुप में, अन्य प्रोसेस ग्रुप से अलग, एक साथ कैलकुलेशन करते हैं.
हर प्रोसेस ग्रुप में, एक साथ कई ऑपरेशन करने पर सिंक करने में समस्या आ सकती है. इस बारे में ज़्यादा जानकारी अभी नहीं दी जा सकती. जैसे, यह सिंक कब होता है, प्रोसेस इस रुकावट पर कैसे पहुंचती हैं, और अगर वे नहीं पहुंचती हैं, तो क्या होता है (#484).
अगर प्रोसेस ग्रुप में क्रॉस-पार्टिशन कम्यूनिकेशन शामिल है यानी प्रोसेस ग्रुप में ऐसी प्रोसेस हैं जिनके पार्टिशन आईडी अलग हैं, तो कलेक्टिव ऑपरेशन के लिए एक चैनल की ज़रूरत होगी. साथ ही, कलेक्टिव सेशन को si64
टाइप का पॉज़िटिव channel_id
देना होगा. क्रॉस-रेप्लिकेशन कम्यूनिकेशन के लिए चैनलों की ज़रूरत नहीं होती.
एक साथ कई ऑपरेशन करने पर, अलग-अलग ऑपरेशन के हिसाब से कैलकुलेशन की जाती है. इनके बारे में ऊपर दिए गए अलग-अलग ऑपरेशन सेक्शन में बताया गया है. हालांकि, जिन रणनीतियों की मदद से प्रोसेस ग्रिड को प्रोसेस ग्रुप में बांटा जाता है उन्हें इन ऑपरेशंस के बीच शेयर किया जाता है. साथ ही, इनके बारे में इस सेक्शन में बताया गया है. ज़्यादा औपचारिक तौर पर, StableHLO इन चार रणनीतियों के साथ काम करता है.
cross_replica
हर प्रोसेस ग्रुप में, सिर्फ़ क्रॉस-रेप्लिकेशन कम्यूनिकेशन होता है. यह रणनीति, replica_groups
- रेप्लिक आईडी की सूचियों की सूची - लेती है और replica_groups
के हिसाब से partition_ids
का कार्टेशियन प्रॉडक्ट कैलकुलेट करती है. replica_groups
में यूनीक एलिमेंट होने चाहिए और सभी replica_ids
को कवर करना चाहिए. Python सिंटैक्स का इस्तेमाल करके, ज़्यादा औपचारिक तौर पर:
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
उदाहरण के लिए, replica_groups = [[0, 1], [2, 3]]
और num_partitions = 2
के लिए cross_replica
, [[(0, 0), (1, 0)], [(0, 1), (1, 1)], [(2, 0), (3, 0)], [(2, 1), (3, 1)]]
जनरेट करेगा.
cross_partition
हर प्रोसेस ग्रुप में, सिर्फ़ अलग-अलग पार्टीशन के बीच कम्यूनिकेशन होता है. यह रणनीति
partition_groups
की मदद लेती है - जो कि पार्टिशन आईडी की सूची की एक सूची है - और
replica_ids
के partition_groups
के एक कार्टीज़न प्रॉडक्ट की गिनती करती है.
partition_groups
में यूनीक एलिमेंट होने चाहिए और इनमें सभी partition_ids
शामिल होने चाहिए.
Python सिंटैक्स का इस्तेमाल करके, इसे और बेहतर तरीके से लिखा जा सकता है:
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
उदाहरण के लिए, partition_groups = [[0, 1]]
और num_replicas = 4
के लिए cross_partition
, [[(0, 0), (0, 1)], [(1, 0), (1, 1)], [(2, 0), (2, 1)], [(3, 0), (3, 1)]]
जनरेट करेगा.
cross_replica_and_partition
हर प्रोसेस ग्रुप में, क्रॉस-रेप्लिक और क्रॉस-पार्टिशन, दोनों तरह की कम्यूनिकेशन हो सकती है. यह रणनीति, replica_groups
- डुप्लीकेट आईडी की सूचियों की सूची - लेती है और partition_ids
के हिसाब से हर replica_group
के कार्टेशियन प्रॉडक्ट का हिसाब लगाती है. replica_groups
में यूनीक एलिमेंट होने चाहिए और सभी
replica_ids
को कवर करना चाहिए. Python सिंटैक्स का इस्तेमाल करके, इसे और बेहतर तरीके से लिखा जा सकता है:
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
उदाहरण के लिए, replica_groups = [[0, 1], [2, 3]]
और num_partitions = 2
के लिए cross_replica_and_partition
, [[(0, 0), (1, 0), (0, 1), (1, 1)], [(2, 0), (3, 0), (2, 1), (3, 1)]]
जनरेट करेगा.
flattened_ids
यह रणनीति, flattened_id_groups
को लेती है - replica_id * num_partitions + partition_id
के तौर पर "फ़्लैट" प्रोसेस आईडी की सूचियों की सूची - और उन्हें प्रोसेस आईडी में बदल देती है. flattened_id_groups
में यूनीक एलिमेंट होने चाहिए और
सभी process_ids
कवर किए जाने चाहिए. Python सिंटैक्स का इस्तेमाल करके, इसे और बेहतर तरीके से लिखा जा सकता है:
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
उदाहरण के लिए, flattened_id_groups = [[0, 1, 2, 3], [4, 5, 6, 7]]
,
num_replicas = 4
, और num_partitions = 2
के लिए, flattened_ids
से
[[(0, 0), (0, 1), (1, 0), (1, 1)], [(2, 0), (2, 1), (3, 0), (3, 1)]]
बनेगा.
सटीक जानकारी
फ़िलहाल, StableHLO, संख्याओं के सटीक होने की गारंटी नहीं देता. हालांकि, आने वाले समय में ऐसा हो सकता है (#1156).
क्वांटाइज़्ड ऑपरेशन का एक्ज़ीक्यूशन सिमेंटिक्स
हार्डवेयर की ज़रूरी शर्तों और क्षमताओं के आधार पर, क्वांटाइज़ किए गए StableHLO ऑपरेशन का मतलब अलग-अलग हो सकता है. उदाहरण के लिए, कुछ हार्डवेयर "डेक्वंटिज़ करें, फ़्लोटिंग-पॉइंट ऑपरेशन करें, और आखिर में क्वंटिज़ करें" रणनीति का इस्तेमाल करके, क्वंटिज़ किए गए ऑपरेशन को समझने का विकल्प चुन सकते हैं. अन्य, पूर्णांक अंकगणित के साथ ही पूरी गणना कर सकते हैं. इसलिए, क्वांटाइज़ किए गए StableHLO ऑपरेशन का विश्लेषण, खास तौर पर लागू करने के तरीके से तय होता है. हाइब्रिड क्वांटिज़ेशन (#1575) का मतलब, स्पेसिफ़िकेशन में बताए गए सेमेटिक्स के आधार पर होना चाहिए. स्पेसिफ़िकेशन के बारे में ज़्यादा जानने के लिए, 1792 देखें.
गड़बड़ियां
StableHLO प्रोग्राम की पुष्टि, अलग-अलग ऑपरेशन के लिए कई तरह की पाबंदियों के ज़रिए की जाती है. इससे, रन टाइम से पहले कई तरह की गड़बड़ियों की संभावना नहीं रहती. हालांकि, गड़बड़ी की स्थितियां अब भी हो सकती हैं. जैसे, पूर्णांक के ओवरफ़्लो, बाहर के ऐक्सेस वगैरह. जब तक साफ़ तौर पर इन गड़बड़ियों के बारे में नहीं बताया जाता, तब तक इन सभी गड़बड़ियों की वजह से, लागू करने के तरीके के हिसाब से व्यवहार होता है. हालांकि, आने वाले समय में इसमें बदलाव हो सकता है (#1157).
फ़्लोटिंग-पॉइंट अपवाद
इस नियम के अपवाद के तौर पर, StableHLO प्रोग्राम में फ़्लोटिंग-पॉइंट अपवादों का व्यवहार अच्छी तरह से तय होता है. ऐसी कार्रवाइयां जिनके नतीजों में आईईईई-754 स्टैंडर्ड (अमान्य ऑपरेशन, डिवीज़न-बाय-ज़ीरो, ओवरफ़्लो, अंडरफ़्लो या इनएग्ज़ैक्ट अपवाद) के अपवाद होते हैं, वे डिफ़ॉल्ट नतीजे देते हैं (जैसा कि स्टैंडर्ड में बताया गया है) और उनसे जुड़े स्टेटस फ़्लैग को बढ़ाए बिना एक्ज़ीक्यूशन जारी रखना चाहते हैं. ठीक उसी तरह, जैसे स्टैंडर्ड से raiseNoFlag
अपवाद मैनेज करना. गैर-स्टैंडर्ड ऑपरेशन (जैसे, जटिल अंकगणित और कुछ ट्रांससेंडेंटल फ़ंक्शन) के लिए अपवाद, लागू करने के तरीके के हिसाब से तय किए जाते हैं.
आकार मेल नहीं खाते
StableHLO, डाइनैमिक आकार वाले टेंसर के साथ काम करता है. हालांकि, आकारों को रनटाइम पर सहमत होना होगा, नहीं तो व्यवहार तय नहीं होगा. StableHLO में साफ़ तौर पर ऐसा कोई ऑपरेशन उपलब्ध नहीं है जिससे यह पता चल सके कि रनटाइम के दौरान टेंसर का कोई तय शेप है. सही कोड जनरेट करने की ज़िम्मेदारी मैन्युफ़ैक्चरर की है.
उदाहरण के लिए, नीचे दिया गया प्रोग्राम मान्य है. हालांकि, रनटाइम के दौरान, %arg0
और %arg1
के आकार एक जैसे होने चाहिए. ऐसा न होने पर, प्रोग्राम का व्यवहार तय नहीं किया जा सकता:
func.func @foo(%arg0: tensor<?xi32>, %arg1: tensor<?xi32>) -> tensor<?xi32> {
%0 = stablehlo.add %arg0, %arg1 : tensor<?xi32>
return %0 : tensor<?xi32>
}
नोटेशन
सिंटैक्स के बारे में बताने के लिए, इस दस्तावेज़ में ईबीएनएफ़ सिंटैक्स (ISO/IEC 14977:1996,
Wikipedia) के बदले, आईएसओ फ़्लेवर का इस्तेमाल किया गया है. इसमें दो बदलाव किए गए हैं: 1) नियमों को =
के बजाय ::=
का इस्तेमाल करके तय किया गया है,
2) स्ट्रिंग को जोड़ने के लिए, ,
के बजाय, एक साथ लगाने का इस्तेमाल किया जाता है.
"टाइप", "कंसटेंट", और "ऑपरेशन" सेक्शन में सेमेटिक्स (यानी, "टाइप", "कंसटेंट", और "ऑपरेशन" सेक्शन में) के बारे में बताने के लिए, हम ऐसे फ़ॉर्मूले का इस्तेमाल कर रहे हैं जो Python सिंटैक्स पर आधारित हैं. इन फ़ॉर्मूले की मदद से, ऐरे ऑपरेशन को कम शब्दों में बताया जा सकता है. इस बारे में यहां बताया गया है. यह कोड के छोटे स्निपेट के लिए अच्छा काम करता है. हालांकि, जब कोड के बड़े स्निपेट की ज़रूरत होती है, तो हम वैनिला Python सिंटैक्स का इस्तेमाल करते हैं. इसे हमेशा साफ़ तौर पर दिखाया जाता है.
फ़ॉर्मूला
आइए, dot_general
स्पेसिफ़िकेशन के उदाहरण के आधार पर जानें कि फ़ॉर्मूला कैसे काम करते हैं. इस कार्रवाई की एक सीमा यहां दिखती है:
dim(lhs, lhs_batching_dimensions...) = dim(rhs, rhs_batching_dimensions...)
.
इस फ़ॉर्मूला में इस्तेमाल किए गए नाम दो सोर्स से आते हैं: 1) ग्लोबल फ़ंक्शन, जैसे कि dim
, 2) उससे जुड़े प्रोग्राम एलिमेंट के सदस्य की परिभाषाएं, जैसे कि dot_general
के "इनपुट" सेक्शन में बताई गई lhs
, lhs_batching_dimensions
, rhs
, और rhs_batching_dimensions
इनपुट.
जैसा कि ऊपर बताया गया है, इस फ़ॉर्मूला का सिंटैक्स, Python पर आधारित है. इसमें कुछ ऐसे एक्सटेंशन भी शामिल हैं जो कम शब्दों में काम करते हैं. फ़ॉर्मूला को समझने के लिए, इसे वैनिला Python सिंटैक्स में बदलें.
A) इन फ़ॉर्मूला में, हम बराबरी दिखाने के लिए =
का इस्तेमाल कर रहे हैं. इसलिए, Python सिंटैक्स पाने के लिए पहला चरण, =
को ==
से बदलना है. ऐसा करने का तरीका यहां बताया गया है:
dim(lhs, lhs_batching_dimensions...) == dim(rhs, rhs_batching_dimensions...)
.
B) साथ ही, ये फ़ॉर्मूला, एलिप्सिस (...
) के साथ काम करते हैं, जो स्केलर एक्सप्रेशन को टेंसर एक्सप्रेशन में बदल देते हैं. खास तौर पर, f(xs...)
का मतलब है कि "टेंसर xs
में मौजूद हर स्केलर x
के लिए, एक स्केलर f(x)
कैलकुलेट करें. इसके बाद, इन सभी स्केलर नतीजों को एक साथ टेंसर नतीजे के तौर पर दिखाएं". वैनिला Python सिंटैक्स में, हमारे उदाहरण का फ़ॉर्मूला इस तरह दिखता है:
[dim(lhs, dim1) for dim1 in lhs_batching_dimensions] ==
[dim(rhs, dim2) for dim2 in rhs_batching_dimensions]
.
एलिप्सिस की मदद से, अक्सर अलग-अलग स्केलर के लेवल पर काम करने से बचा जा सकता है. हालांकि, कुछ मुश्किल मामलों में, gather
स्पेसिफ़िकेशन के start_indices[bi0, ..., :, ..., biN]
फ़ॉर्मूला की तरह, कम लेवल के सेमी-इंफ़ॉर्मल सिंटैक्स का इस्तेमाल किया जा सकता है. हम इस तरह के सिंटैक्स को वैनिला Python में बदलने के लिए, सटीक फ़ॉर्मलिज्म नहीं देते. ऐसा इसलिए किया जाता है, ताकि हर मामले के हिसाब से इसे आसानी से समझा जा सके.
अगर कुछ फ़ॉर्मूले साफ़ नहीं दिख रहे हैं, तो कृपया हमें बताएं. हम उन्हें बेहतर बनाने की कोशिश करेंगे.
आपको यह भी पता चलेगा कि फ़ॉर्मूला, सभी तरह की सूचियों को बड़ा करने के लिए, बिंदुओं के बीच में तिरछी रेखाओं का इस्तेमाल करते हैं. इनमें टेंसर, टेंसर की सूचियां (जैसे, ये कई टेंसर की संख्या से बन सकती हैं) वगैरह शामिल हैं. यह एक और ऐसा क्षेत्र है जहां हम सटीक फ़ॉर्मलिज्म (उदाहरण के लिए, सूचियां StableHLO टाइप सिस्टम का हिस्सा भी नहीं हैं) नहीं देते. इसके बजाय, हम सहजता से समझने पर भरोसा करते हैं.
C) नोटेशन के लिए इस्तेमाल होने वाली आखिरी सुविधा, इम्प्लीसिट ब्रॉडकास्टिंग है. StableHLO ऑपसेट, इंप्लिसिट ब्रॉडकास्टिंग के साथ काम नहीं करता. हालांकि, फ़ॉर्मूला के साथ यह काम करता है. साथ ही, यह कम शब्दों में भी काम करता है. खास तौर पर, अगर स्केलर का इस्तेमाल ऐसे कॉन्टेक्स्ट में किया जाता है जहां टेंसर की उम्मीद होती है, तो स्केलर को उम्मीद के मुताबिक शेप में ब्रॉडकास्ट किया जाता है.
dot_general
के उदाहरण को जारी रखने के लिए, यहां एक और शर्त दी गई है:
0 <= lhs_batching_dimensions < rank(lhs)
. dot_general
के स्पेसिफ़िकेशन में बताए गए मुताबिक, lhs_batching_dimensions
एक टेंसर है. हालांकि, 0
और
rank(lhs)
, दोनों स्केलर हैं. इंप्लिसिट ब्रॉडकास्ट लागू करने के बाद, फ़ॉर्मूला [0, ..., 0] <= lhs_batching_dimensions < [rank(lhs), ..., rank(lhs)]
हो जाएगा.
किसी खास dot_general
ऑपरेशन पर लागू करने पर, यह फ़ॉर्मूला बूलियन के टेंसर के तौर पर काम करेगा. जब फ़ॉर्मूला का इस्तेमाल पाबंदियों के तौर पर किया जाता है, तो पाबंदी तब लागू होती है, जब फ़ॉर्मूला का आकलन true
या ऐसे टेंसर के तौर पर किया जाता है जिसमें सिर्फ़ true
एलिमेंट हों.
नाम
फ़ॉर्मूला में, लेक्सिकल स्कोप में ये शामिल होते हैं: 1) ग्लोबल फ़ंक्शन, 2) सदस्य की परिभाषाएं,
3) स्थानीय परिभाषाएं. ग्लोबल फ़ंक्शन की सूची यहां दी गई है. एलिमेंट की परिभाषाओं की सूची, उस प्रोग्राम एलिमेंट पर निर्भर करती है जिस पर नोटेशन लागू किया गया है:
- ऑपरेशन के लिए, सदस्य की परिभाषाओं में "इनपुट" और "आउटपुट" सेक्शन में दिए गए नाम शामिल होते हैं.
- बाकी सभी चीज़ों के लिए, सदस्य की परिभाषाओं में प्रोग्राम एलिमेंट के स्ट्रक्चरल हिस्से शामिल होते हैं. इन हिस्सों को, संबंधित ईबीएनएफ़ नॉन-टर्मिनल के नाम पर रखा जाता है. ज़्यादातर, इन संरचनात्मक हिस्सों के नाम, नॉन-टर्मिनल के नाम को सांप के केस में बदलकर (जैसे,
IntegerLiteral
=>integer_literal
) लिए जाते हैं. हालांकि, कभी-कभी प्रक्रिया में नामों को छोटा कर दिया जाता है (जैसे किQuantizationStorageType
=>storage_type
). इस मामले में, इन हिस्सों के नाम, "इनपुट" / "आउटपुट" सेक्शन की तरह ही दिए जाते हैं. - इसके अलावा, सदस्यों की परिभाषाओं में हमेशा
self
शामिल होता है, ताकि प्रोग्राम के उस एलिमेंट का रेफ़रंस दिया जा सके.
वैल्यू
फ़ॉर्मूला का आकलन करने पर, वे इन तरह की वैल्यू के साथ काम करते हैं:
1) Value
(असल वैल्यू, जैसे कि dense<[[1, 2], [3, 4]]> : tensor<2x2xi32>
;
उनके टाइप हमेशा पता होते हैं),
2) Placeholder
(आने वाले समय की वैल्यू, जैसे कि lhs
, rhs
या result
; उनकी असल वैल्यू अभी तक पता नहीं है, सिर्फ़ उनके टाइप पता हैं),
3) Type
(टाइप, जैसा कि "टाइप" सेक्शन में बताया गया है),
4) Function
(ग्लोबल फ़ंक्शन, जैसा कि "फ़ंक्शन" सेक्शन में बताया गया है).
संदर्भ के आधार पर, नाम अलग-अलग वैल्यू का रेफ़रंस दे सकते हैं. खास तौर पर, ऑपरेशन के लिए "सेमैंटिक्स" सेक्शन (और प्रोग्राम के अन्य एलिमेंट के लिए उसके जैसे) रनटाइम लॉजिक के बारे में बताता है. इसलिए, सभी इनपुट, Value
के तौर पर उपलब्ध होते हैं.
वहीं, ऑपरेशन (और इसके बराबर) के "कंस्ट्रेंट" सेक्शन में "कंपाइल-टाइम" लॉजिक के बारे में जानकारी दी जाती है.इसका मतलब है कि कुछ ऐसा है जिसे आम तौर पर रनटाइम से पहले लागू किया जाता है. इसलिए, Value
के तौर पर सिर्फ़ कॉन्सटेंट इनपुट उपलब्ध होते हैं और अन्य इनपुट सिर्फ़ Placeholder
के तौर पर उपलब्ध होते हैं.
नाम | "सेमांटिक" में | "सीमाएं" में |
---|---|---|
ग्लोबल फ़ंक्शन | Function |
Function |
कॉन्स्टेंट इनपुट | Value |
Value |
ऐसे इनपुट जो एक जैसे नहीं होते | Value |
Placeholder |
आउटपुट | Value |
Placeholder |
स्थानीय परिभाषाएं | यह परिभाषा पर निर्भर करता है | यह परिभाषा पर निर्भर करता है |
transpose
ऑपरेशन का एक उदाहरण देखें:
%result = "stablehlo.transpose"(%operand) {
permutation = dense<[2, 1, 0]> : tensor<3xi64>
} : (tensor<2x3x2xi32>) -> tensor<2x3x2xi32>
इस ऑपरेशन के लिए, permutation
एक कॉन्स्टेंट है. इसलिए, यह सेमेंटेक्स और पाबंदियों, दोनों में Value
के तौर पर उपलब्ध है. इसके उलट, operand
और result
, सिमेंटिक्स में Value
के तौर पर उपलब्ध हैं, लेकिन पाबंदियों में सिर्फ़ Placeholder
के तौर पर उपलब्ध हैं.
फ़ंक्शन
अलग-अलग तरह के एलिमेंट बनाना
टाइप बनाने के लिए, कोई फ़ंक्शन इस्तेमाल नहीं किया जा सकता. इसके बजाय, हम सीधे टाइप सिंटैक्स का इस्तेमाल करते हैं, क्योंकि आम तौर पर यह ज़्यादा छोटा होता है. जैसे, function_type(
[tensor_type([], E), tensor_type([], E)], [tensor_type([], E)])
की जगह (tensor<E>, tensor<E>) -> (tensor<E>)
.
टाइप के फ़ंक्शन
element_type
को टेंसर टाइप और क्वांटाइज़ किए गए टेंसर टाइप और रिटर्न के आधार पर दिखाया जाता है. ये इससे जुड़ेTensorType
याQuantizedTensorType
केTensorElementType
याQuantizedTensorElementType
हिस्से पर आधारित होते हैं.
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
,is_quantized(x) and quantization_dimension(x) is not None
का शॉर्टकट है.is_per_tensor_quantized(x: Value | Placeholder | Type) -> Value
,is_quantized(x) and quantization_dimension(x) is None
का शॉर्टकट है.is_promotable(x: Type, y: Type) -> bool
यह जांच करता है कि क्या टाइपx
को,y
टाइप में प्रमोट किया जा सकता है. जबx
औरy
,QuantizedTensorElementType
होते हैं, तो प्रमोशन सिर्फ़storage_type
पर लागू होता है. प्रमोशन के इस वर्शन का इस्तेमाल, फ़िलहाल छूट के हिसाब लगाने के लिए किया जाता है. ज़्यादा जानकारी के लिए, आरएफ़सी देखें.
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
,is_quantized_tensor_element_type(x)
का शॉर्टकट है.is_type_name(x: Value | Placeholder | Type) -> Value
. सभी तरह के ऐप्लिकेशन के लिए उपलब्ध है. उदाहरण के लिए, अगरx
एकFloatType
है, तोis_float(x)
सेtrue
मिलता है. अगरx
कोई वैल्यू या प्लेसहोल्डर है, तो यह फ़ंक्शनis_type_name(type(x))
के लिए शॉर्टकट है.max_value(x: Type) -> Value
,TensorElementType
की सबसे ज़्यादा वैल्यू दिखाता है. अगरx
,TensorElementType
नहीं है, तोNone
दिखाता है.min_value(x: Type) -> Value
, किसीTensorElementType
की सबसे कम संभावित वैल्यू दिखाता है. अगरx
,TensorElementType
नहीं है, तोNone
दिखाता है.member_name(x: Value | Placeholder | Type) -> Any
. यह सभी तरह की सदस्यता की परिभाषाओंmember_name
के लिए उपलब्ध है. उदाहरण के लिए,tensor_element_type(x)
TensorType
केTensorElementType
हिस्से को दिखाता है. अगरx
कोई वैल्यू या प्लेसहोल्डर है, तो यह फ़ंक्शनmember_name(type(x))
के लिए शॉर्टकट है. अगरx
ऐसा टाइप नहीं है जिसमें कोई सही सदस्य, वैल्यू या प्लेसहोल्डर है, तोNone
दिखाया जाता है.is_empty_algorithm(*args: Type)
यह जांच करता है कि सभी बिंदु एल्गोरिदम फ़ील्ड,None
पर सेट हैं या नहीं. ऐसा करना ज़रूरी है, क्योंकि बिंदु वाले एल्गोरिदम के लिए, लागू करने के तरीके के हिसाब से डिफ़ॉल्ट व्यवहार तय होते हैं. इसलिए, डिफ़ॉल्ट वैल्यू तय करना गलत होगा.
वैल्यू का निर्माण
operation_name(*xs: Value | Type) -> Value
. सभी कार्रवाइयों के लिए उपलब्ध है. उदाहरण के लिए,add(lhs, rhs)
दो टेंसर वैल्यूlhs
औरrhs
लेता है और इन इनपुट के साथadd
ऑपरेशन का आकलन करके आउटपुट दिखाता है.broadcast_in_dim
जैसे कुछ ऑपरेशन के लिए, उनके आउटपुट टाइप "लोड होने वाली" हैं. इसका मतलब है कि किसी कार्रवाई का आकलन करने के लिए ये आउटपुट ज़रूरी हैं. इस मामले में, फ़ंक्शन इन टाइप को आर्ग्युमेंट के तौर पर लेता है.
वैल्यू पर फ़ंक्शन
Python के सभी ऑपरेटर और फ़ंक्शन उपलब्ध हैं. उदाहरण के लिए, टेंसर, क्वांटाइज़ किए गए टेंसर, और ट्यूपल में इंडेक्स करने के लिए, Python के सदस्यता और स्लाइस करने वाले नोटेशन, दोनों उपलब्ध हैं.
to_destination_type(x: Value, destination_type: Type) -> Value
को टेंसर पर तय किया गया है. यहx
की बदली गई वैल्यू कोtype(x)
औरdestination_type
के आधार पर दिखाता है. इसके लिए, यह तरीका अपनाया जाता है:
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)
convert
, uniform_quantize
, और uniform_dequantize
ऑपरेशन (#1576) को मर्ज करने के बारे में शुरुआती चर्चा की जा रही है.
मर्ज करने के बाद, हमें ऊपर दिए गए फ़ंक्शन की ज़रूरत नहीं होती. इसके बजाय, convert
के लिए ऑपरेशन के नाम का इस्तेमाल किया जा सकता है.
is_nan(x: Value) -> Value
को टेंसर पर तय किया जाता है. अगरx
के सभी एलिमेंटNaN
हैं, तो यहtrue
दिखाता है. अगर ऐसा नहीं है, तो यहfalse
दिखाता है. अगरx
कोई टेंसर नहीं है, तोNone
दिखाता है.is_sorted(x: Value) -> Value
को टेंसर पर तय किया जाता है. अगरx
के एलिमेंट को उनके इंडेक्स के बढ़ते क्रम के हिसाब से, बढ़ते क्रम में लगाया जाता है, तो यह फ़ंक्शनtrue
दिखाता है. अगर ऐसा नहीं किया जाता है, तो यहfalse
दिखाता है. अगरx
कोई टेन्सर नहीं है, तोNone
दिखाता है.is_unique(x: Value) -> Value
को टेंसर पर तय किया जाता है. अगरx
में डुप्लीकेट एलिमेंट नहीं हैं, तो यहtrue
दिखाता है. अगर डुप्लीकेट एलिमेंट हैं, तो यहfalse
दिखाता है. अगरx
कोई टेंसर नहीं है, तोNone
दिखाता है.member_name(x: Value) -> Any
को सभी वैल्यू के सभी सदस्यों की परिभाषाओं के लिए तय किया गया हैmember_name
. उदाहरण के लिए,real_part(x)
,ComplexConstant
केRealPart
हिस्से को दिखाता है. अगरx
ऐसी वैल्यू नहीं है जिसमें सही सदस्य है, तोNone
दिखाता है.same(x: Value) -> Value
को टेंसर पर तय किया जाता है. अगरx
के सभी एलिमेंट एक-दूसरे के बराबर हैं, तो यहtrue
दिखाता है. अगर ऐसा नहीं है, तो यहfalse
दिखाता है. अगर टेंसर में एलिमेंट नहीं हैं, तो उसे "सभी एक जैसे हैं" माना जाता है. इसका मतलब है कि फ़ंक्शनtrue
दिखाता है. अगरx
कोई टेंसर नहीं है, तोNone
दिखाता है.split(x: Value, num_results: Value, axis: Value) -> Value
को टेंसर पर तय किया जाता है. यह ऐक्सिसaxis
के साथx
कीnum_results
स्लाइस दिखाता है. अगरx
कोई टेंसर याdim(x, axis) % num_results != 0
नहीं है, तोNone
दिखाता है.is_defined_in_parent_scope(x: Value) -> Value
को स्ट्रिंग पर तय किया गया है और अगरx
, उसी स्कोप में तय किए गए फ़ंक्शन का नाम है जो ऑपरेशन के पैरंट फ़ंक्शन के तौर पर तय किया गया है, तो यहtrue
दिखाता है.is_namespaced_op_name(x: Value) -> Value
को स्ट्रिंग के तौर पर तय किया गया है. अगरx
, ऑपरेटर का मान्य नाम है, तो यहtrue
दिखाता है. इसका मतलब है कि यह इस रेगुलर एक्सप्रेशन के मुताबिक है:[a-zA-Z][a-zA-Z0-9_]*([.][a-zA-Z0-9_$]+)+
आकार का हिसाब लगाना
axes(x: Value | Placeholder | Type) -> Value
,range(rank(x))
का शॉर्टकट है.dim(x: Value | Placeholder | Type, axis: Value) -> Value
,shape(x)[axis]
का शॉर्टकट है.dims(x: Value | Placeholder | Type, axes: List) -> List
,list(map(lambda axis: dim(x, axis), axes))
का शॉर्टकट है.index_space(x: Value | Placeholder | Type) -> Value
को टेंसर पर परिभाषित किया गया है और यहTensorType
के लिएsize(x)
इंडेक्स दिखाता है.TensorType
को बढ़ते हुए वर्णमाला क्रम में, यानी[0, ..., 0]
,[0, ..., 1]
, ...,shape(x) - 1
के क्रम में क्रम से लगाया जाता है. अगरx
, टेंसर टाइप, क्वांटाइज़्ड टेंसर टाइप या वैल्यू या इनमें से किसी एक टाइप का प्लेसहोल्डर नहीं है, तो यह वैल्यूNone
दिखाता है.rank(x: Value | Placeholder | Type) -> Value
,size(shape(x))
का शॉर्टकट है.shape(x: Value | Placeholder | Type) -> Value
कोmember_name
की मदद से, "फ़ंक्शन के टाइप" सेक्शन में तय किया जाता है.size(x: Value | Placeholder | Type) -> Value
,reduce(lambda x, y: x * y, shape(x))
का शॉर्टकट है.
क्वांटाइज़ेशन कैलकुलेशन
def baseline_element_type(x: Value | Placeholder | Type) -> Type
,element_type(baseline_type(x))
का शॉर्टकट है.baseline_type
को टेंसर टाइप और क्वांटाइज़ किए गए टेंसर टाइप के आधार पर तय किया जाता है. साथ ही, इन्हें "बेसलाइन" में बदल देता है. इसका मतलब है कि एक ही आकार वाला टाइप, लेकिन एलिमेंट टाइप के क्वांटाइज़ेशन पैरामीटर को डिफ़ॉल्ट वैल्यू पर रीसेट किया गया है. इसे एक आसान ट्रिक के तौर पर, टेंसर और क्वांटाइज़ किए गए टेंसर टाइप की एक ही तरह से तुलना करने के लिए इस्तेमाल किया जाता है. इसकी ज़रूरत अक्सर पड़ती है. क्वांटाइज़ किए गए टाइप के लिए, यह क्वांटाइज़ेशन पैरामीटर को अनदेखा करके, टाइप की तुलना करने की सुविधा देता है. इसका मतलब है किshape
,storage_type
,expressed_type
,storage_min
,storage_max
, औरquantization_dimension
(हर अक्ष के लिए क्वांटाइज़ किए गए टाइप के लिए) सभी मैच होने चाहिए, लेकिनscales
औरzero points
अलग-अलग हो सकते हैं.
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
को क्वांटाइज़ किए गए टेंसर टाइप के आधार पर तय किया जाता है और उन्हें फ़्लोटिंग-पॉइंट टेंसर टाइप में बदल देता है. ऐसा, स्टोरेज टाइप की पूर्णांक वैल्यू दिखाने वाले क्वांटाइज़ किए गए एलिमेंट को, दिखाए गए टाइप की फ़्लोटिंग-पॉइंट वैल्यू में बदलकर किया जाता है. इसके लिए, क्वांटाइज़ किए गए एलिमेंट टाइप के शून्य पॉइंट और स्केल का इस्तेमाल किया जाता है.
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
को फ़्लोटिंग-पॉइंट टेंसर टाइप के आधार पर तय किया जाता है और उन्हें क्वांटाइज़ किए गए टेंसर टाइप में बदल देता है. ऐसा करने के लिए, बताए गए तरह की फ़्लोटिंग-पॉइंट वैल्यू को, स्टोरेज टाइप के पूर्णांक वैल्यू में बदल दिया जाता है. इसके लिए, क्वांटाइज़्ड एलिमेंट टाइप से जुड़े शून्य पॉइंट और स्केल का इस्तेमाल किया जाता है.
def quantize(x: Value, result_type: Type) -> Value:
assert is_float(x) and is_quantized(result_type)
zero_points = compute_zero_points(result_type, TensorType(shape(x), storage_type(result_type)))
converted_zero_points = convert(zero_points, expressed_type(result_type))
converted_min = convert(storage_min(result_type), expressed_type(result_type))
converted_max = convert(storage_max(result_type), expressed_type(result_type))
x_scaled = x / compute_scales(result_type, type(x))
x_scaled_add_zp = x_scaled + converted_zero_points
x_clamped = clamp(converted_min, x_scaled_add_zp, converted_max)
x_rounded = round_nearest_even(x_clamped)
return convert(x_rounded, result_type)
dequantize_op_quantize
का इस्तेमाल, क्वांटाइज़ किए गए टेंसर पर एलिमेंट के हिसाब से कैलकुलेशन तय करने के लिए किया जाता है. यह डिक्वांटाइज़ करता है, यानी क्वांटिफ़ाइड एलिमेंट को उनके लिखे हुए टाइप में बदल देता है, फिर कोई ऑपरेशन करता है, और फिर क्वांट करता है. इसका मतलब है कि नतीजों को फिर से स्टोरेज टाइप में बदल दिया जाता है. फ़िलहाल, यह फ़ंक्शन सिर्फ़ हर टेंसर के लिए क्वांटाइज़ेशन के साथ काम करता है. हर ऐक्सिस के हिसाब से क्वांटिज़ेशन की सुविधा पर काम जारी है (#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)
hybrid_dequantize_then_op
का इस्तेमाल, हाइब्रिड ऑपरेशन के लिए सिर्फ़ वेट क्वांटिज़ेशन तय करने के लिए किया जाता है. यह ऑपरेशन, lhs को फ़्लोटिंग-पॉइंट में और rhs को क्वांटिज़ किए गए टाइप में स्वीकार करता है. यह क्वांटाइज़ किए गए इनपुट को उनके एक्सप्रेशन टाइप में बदल देता है और फ़्लोट में कैलकुलेशन करता है. फ़्लोट एलएच टेंसर का एलिमेंट टाइप और क्वांटाइज़ किए गए rhs टेंसर का बताया गया टाइप एक जैसा होना चाहिए.
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))
ग्रिड कंप्यूटेशन
cross_partition(replica_groups: Value) -> Value
. ऊपर "cross_replica" सेक्शन देखें.cross_replica(replica_groups: Value) -> Value
. ऊपर दिया गया "cross_Replica" सेक्शन देखें.cross_replica_and_partition(replica_groups: Value) -> Value
. ऊपर दिया गया "cross_Replica_and_partition" सेक्शन देखें.flattened_ids(replica_groups: Value) -> Value
. ऊपर दिया गया "flatteted_ids" सेक्शन देखें.
डायनेमिज़म
StableHLO वैल्यू में डाइनैमिक डाइमेंशन साइज़ हो सकते हैं, जैसे कि tensor<?xi64>
.
हालांकि, StableHLO वैल्यू में डाइमेंशन की डाइनैमिक संख्या नहीं हो सकती (रैंक नहीं दी गई डाइनैमिक वैल्यू, जैसे कि tensor<*xi64>
). ऑपरेंड और नतीजों में डाइनैमिक डाइमेंशन साइज़ का इस्तेमाल किया जा सकता है. भले ही, साइज़ पर पाबंदियां हों. अगर संभव हो, तो पाबंदियों की पुष्टि स्टैटिक तौर पर की जाएगी. ऐसा न होने पर, उन्हें रनटाइम तक के लिए टाल दिया जाएगा. साथ ही, मेल न खाने की वजह से, कोई तय व्यवहार नहीं होगा. उदाहरण के लिए नीचे देखें.
एलिमेंट के हिसाब से किए जाने वाले एक आर्ग्युमेंट वाले ऑपरेशन के लिए, आकार मेल नहीं खाना
खिलौने के इस प्रोग्राम पर ध्यान दें:
func.func @foo(%arg0: tensor<?xf64>) {
%0 = stablehlo.abs %arg0 : (tensor<?xf64>) -> tensor<2xf64>
return
}
ऐसा प्रोग्राम असामान्य होता है, क्योंकि आम तौर पर नतीजे का shape पता होता है, लेकिन इनपुट का shape नहीं. फिर भी, यह एक मान्य StableHLO प्रोग्राम है. इस प्रोग्राम में, abs
ऑपरेशन की स्टैटिक तौर पर पुष्टि नहीं की जा सकती, क्योंकि ऑपरेंड का सटीक टाइप पता नहीं है. हालांकि, आकार एक-दूसरे के साथ काम करते हैं और इसकी जांच स्टैटिक तौर पर की जा सकती है: ?
, रनटाइम के दौरान 2
हो सकता है और इसमें कोई समस्या नहीं होगी. हालांकि, ?
कोई दूसरा पूर्णांक भी हो सकता है. ऐसे में, फ़ंक्शन का व्यवहार तय नहीं होता.
ध्यान दें कि अगर नतीजे में डाइमेंशन का साइज़ डाइनैमिक है, तो कोई ऐसा व्यवहार नहीं हो सकता जिसकी जानकारी न दी गई हो. असल में, कोई "अनुमानित" साइज़ नहीं होता, इसलिए साइज़ में कोई अंतर नहीं हो सकता.
एलिमेंट के हिसाब से किए जाने वाले बाइनरी ऑपरेशन के लिए, आकार मेल नहीं खाना
खिलौनों से जुड़े इस कार्यक्रम को ध्यान में रखें:
func.func @foo(%arg0: tensor<?xf64>, %arg1: tensor<?xf64>) {
%0 = stablehlo.add %arg0, %arg0 : (tensor<?xf64>, tensor<?xf64>) -> tensor<?xf64>
return
}
जब बाइनरी एलिमेंट के मुताबिक ऑपरेशन की बात आती है, तो रनटाइम के दौरान इनपुट के साइज़ और नतीजे का एक जैसा होना ज़रूरी है. कंपाइल करते समय, स्टैटिक डाइमेंशन एक जैसे होने चाहिए. नहीं, वे सिर्फ़ काम करने वाले होने चाहिए. अगर इनपुट में कोई डाइमेंशन डाइनैमिक है, तो रनटाइम के दौरान गड़बड़ी हो सकती है. ऐसा इसलिए, क्योंकि हो सकता है कि डाइनैमिक साइज़, दूसरे ऑपरेंड (चाहे वह स्टैटिक हो या डाइनैमिक) के साइज़ से मेल न खाए. अगर सभी इनपुट स्टैटिक हैं, तो नतीजा डाइनैमिक है या नहीं, इससे कोई फ़र्क़ नहीं पड़ता: स्टैटिक तौर पर जाने-पहचाने डाइमेंशन की जांच स्टैटिक तौर पर की जाएगी और डाइनैमिक डाइमेंशन पर कोई पाबंदी नहीं होगी.
उन ऑपरेशन के लिए आकार का मेल न खाना जो अपने आउटपुट के आकार को ऑपरेंड के तौर पर लेते हैं
खिलौने के इस प्रोग्राम पर ध्यान दें:
func.func @foo(%arg0: tensor<2xi32>) {
%0 = stablehlo.dynamic_iota %arg0, dim = 0 : (tensor<2xi32>) -> tensor<3x4xi64>
return
}
रनटाइम के दौरान, शेप ऑपरेंड में मौजूद वैल्यू, नतीजे के शेप से मेल खानी चाहिए. ऐसा न होने पर, शेप का व्यवहार तय नहीं होता. इसका मतलब है कि रनटाइम के दौरान, %arg0
की वैल्यू dense<[3, 4]> : tensor<2xi32>
होनी चाहिए. अगर शेप ऑपरेंड एक जैसा है, तो इसकी पुष्टि स्टैटिक तौर पर की जा सकती है. अगर नतीजे का शेप पूरी तरह से डाइनैमिक है, तो मैच न होने की समस्या नहीं हो सकती.