StableHLO هي مجموعة عمليات للعمليات العالية المستوى (HLO) في نماذج تعلُّم الآلة (ML). تعمل StableHLO كطبقة نقل بين أُطر عمل ومترجمات مختلفة لتعلُّم الآلة: تتوافق أُطر عمل تعلُّم الآلة التي تنتج برامج StableHLO مع مترجمات تعلُّم الآلة التي تستهلك برامج StableHLO.
هدفنا هو تبسيط عملية تطوير تعلُّم الآلة وتسريعها من خلال توفير المزيد من إمكانية التشغيل التفاعلي بين مختلف أُطر عمل تعلُّم الآلة (مثل TensorFlow وJAX وPyTorch) ومترجمات تعلُّم الآلة (مثل XLA وIREE). لتحقيق ذلك، يقدّم هذا المستند مواصفات للغة البرمجة StableHLO.
يحتوي هذا المستند على ثلاثة أقسام رئيسية. أولاً، يصف قسم البرامج بنية برامج StableHLO التي تتألف من دوال StableHLO التي تتألف بدورها من عمليات StableHLO. ضمن هذه البنية، يحدّد القسم العمليات دلالات العمليات الفردية. يوفّر قسم التنفيذ دلالات لجميع العمليات التي يتم تنفيذها معًا ضمن برنامج. أخيرًا، يتناول قسم الترميز الترميز المستخدَم في جميع أنحاء المواصفات.
للاطّلاع على المواصفات من إصدار سابق من StableHLO، افتح المستودع في الإصدار الذي تم وضع علامة عليه. على سبيل المثال، مواصفات الإصدار 0.19.0 من StableHLO. للاطّلاع على التغييرات التي حدثت في كل إصدار ثانوي من StableHLO، يُرجى الرجوع إلى سجلّ الإصدارات في VhloDialect.td.
البرامج
Program ::= {Func}
تتألف برامج StableHLO من عدد عشوائي من دوال StableHLO.
في ما يلي مثال على برنامج يتضمّن الدالة @main التي تتضمّن 3 مدخلات (%image و%weights و%bias) ومخرجًا واحدًا. يتضمّن نص الدالة 6 عمليات.
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 (المعروفة أيضًا باسم الدوال المُسمّاة) على معرّف ومدخلات/مخرجات ونص. في المستقبل، نخطّط لإضافة بيانات وصفية إضافية للدوال من أجل تحقيق توافق أفضل مع 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 | BufferType
NonValueType ::= TensorElementType | QuantizedTensorElementType | FunctionType | StringType
يتم تصنيف أنواع StableHLO إلى أنواع القيم (التي تُعرف أيضًا باسم أنواع الدرجة الأولى) التي تمثّل قيم StableHLO وأنواع غير القيم التي تصف عناصر البرنامج الأخرى. تتشابه أنواع StableHLO مع الأنواع في العديد من لغات البرمجة، ولكنّ السمة الرئيسية التي تميّزها هي طبيعتها الخاصة بالمجال، ما يؤدي إلى بعض النتائج غير العادية (مثل أنّ الأنواع العددية ليست أنواع قيم).
TensorType ::= 'tensor' '<' Shape TensorElementType '>'
Shape ::= {DimensionSize 'x'}
DimensionSize ::= digit {digit} | '?'
تمثّل أنواع الموتر الموترات، أي المصفوفات المتعددة الأبعاد. تتضمّن هذه المصفوفات شكلاً ونوع عنصر، حيث يمثّل الشكل أحجام الأبعاد غير السالبة أو غير المعروفة بترتيب تصاعدي للأبعاد المقابلة (المعروفة أيضًا باسم المحاور) المرقمة من 0 إلى R-1. يُطلق على عدد الأبعاد R اسم الرتبة. على سبيل المثال، tensor<2x3xf32> هو نوع موتر ذو شكل 2x3 ونوع عنصر f32. يتضمّن هذا المثال بُعدَين (أو محورَين)، وهما البُعد 0 والبُعد 1، ويبلغ حجم كل منهما 2 و3 على التوالي. ويبلغ ترتيبه 2.
يمكن أن تكون الأشكال غير معروفة جزئيًا أو كليًا (ديناميكية)، على سبيل المثال، 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 واحد لكل الموتر، أو يمكن أن يكون لكل محور، أي أن يكون هناك عدة scales وzero_points، زوج واحد لكل شريحة من بُعد معيّن quantization_dimension. بشكل أكثر رسمية، في موتر t
مع التكميم لكل محور، هناك dim(t, quantization_dimension) شرائح
من 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}]
تمثّل أنواع المخزن المؤقت المخازن المؤقتة. على سبيل المثال، في XLA، تكون المخازن المؤقتة عبارة عن صفائف متعدّدة الأبعاد ذات مساحة تخزين متسقة. على غرار أنواع الموتر، تحتوي أنواع المخزن المؤقت على شكل ونوع عنصر، حيث يمثّل الشكل أحجام الأبعاد غير السالبة أو غير المعروفة بترتيب تصاعدي للأبعاد المقابلة (التي تُعرف أيضًا باسم المحاور) المرقمة من 0 إلى R-1. يُطلق على عدد الأبعاد R اسم الرتبة. على سبيل المثال،
memref<2x3xf32> هو نوع مخزن مؤقت ذو شكل 2x3 ونوع عنصر f32. تحتوي هذه المصفوفة على بُعدَين (أو محورَين) - البُعد 0 والبُعد 1 - ويبلغ حجم كل منهما 2 و3. ويبلغ ترتيبه 2.
يمكن تخصيص المخازن المؤقتة باستخدام custom_call إلى CreateBuffer أو Pin، ويمكن إلغاء تخصيصها باستخدام custom_call إلى Unpin. يمكن لمشغّلي custom_call فقط قراءة المحتوى وكتابته داخل المخازن المؤقتة. لمزيد من التفاصيل، يُرجى الاطّلاع على custom_call.
تمثّل أنواع الصفوف الصفوف، أي القوائم غير المتجانسة. تُعدّ الصفوف ميزة قديمة
تتوفّر فقط للتوافق مع HLO. في HLO، يتم استخدام الصفوف لتمثيل المدخلات والمخرجات المتغيرة العدد. في StableHLO، تتوفّر إمكانية استخدام المدخلات والمخرجات المتغيرة بشكل أصلي، والاستخدام الوحيد للمجموعات في StableHLO هو تمثيل واجهة ABI الخاصة بـ HLO بشكل شامل، حيث يمكن أن تختلف T وtuple<T> وtuple<tuple<T>> بشكل كبير حسب التنفيذ المحدّد. في المستقبل، نخطّط لإجراء تغييرات على HLO ABI
التي قد تسمح لنا بإزالة أنواع الصفوف من 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>).
- يمثّل النوع المنطقي القيم المنطقية
trueوfalse. - يمكن أن تكون أنواع الأعداد الصحيحة إما مُوقَّعة (
si) أو غير مُوقَّعة (ui)، ويجب أن يكون لها أحد عروض البتات المتوافقة (2أو4أو8أو16أو32أو64). تمثّل أنواعsiNالمُوقَّعة قيم الأعداد الصحيحة من-2^(N-1)إلى2^(N-1)-1ضِمنًا، وتمثّل أنواعuiNغير المُوقَّعة قيم الأعداد الصحيحة من0إلى2^N-1ضِمنًا. - يمكن أن تكون أنواع الأعداد العشرية واحدة مما يلي:
f8E3M4وf8E4M3وf8E5M2هي أرقام نقطة عائمة ذات 8 بتات تتبع معايير IEEE-754.- النوعان
f8E4M3FNوf8E5M2يتوافقان على التوالي مع الترميزينE4M3وE5M2لتنسيق FP8 الموضّح في تنسيقات FP8 للتعلم الآلي العميق. - النوعان
f8E4M3FNUZوf8E5M2FNUZيتوافقان مع الترميزينE4M3وE5M2لتنسيقات FP8 الموضّحة في تنسيقات رقمية 8 بت للشبكات العصبية العميقة. - نوع
f8E4M3B11FNUZيتوافق مع ترميزE4M3لتنسيقات FP8 الموضّحة في التدريب والاستدلال باستخدام النقطة العائمة المختلطة ذات 8 بت (HFP8) للشبكات العصبية العميقة. - نوع
bf16الذي يتوافق مع تنسيقbfloat16الموضّح في BFloat16: The secret to high performance on Cloud TPUs - أنواع
f16وf32وf64التي تتوافق على التوالي مع تنسيقاتbinary16("نصف الدقة") وbinary32("الدقة الفردية") وbinary64("الدقة المزدوجة") الموضّحة في معيار IEEE 754 - يتوافق النوع
tf32مع تنسيق TensorFloat32 ويعمل بشكل محدود في StableHLO. - أنواع
f4E2M1FNوf6E2M3FNوf6E3M2FNوf8E8M0FNUMX (التقليص الدقيق) الموضّحة في مواصفات تنسيقات التقليص الدقيق في OCP
- تمثّل الأنواع المركّبة قيمًا مركّبة تتضمّن جزءًا حقيقيًا وجزءًا تخيليًا من نوع العنصر نفسه. تشمل الأنواع المعقّدة المتوافقة
complex<f32>(يكون كلا الجزأين من النوعf32) وcomplex<f64>(يكون كلا الجزأين من النوعf64).
FunctionType ::= '(' InputTypes ')' '->' '(' OutputTypes ')'
InputTypes ::= [ValueType {',' ValueType}]
OutputTypes ::= [ValueType {',' ValueType}]
تمثّل أنواع الدوال الدوال المُسمّاة والدوال المجهولة. وتتضمّن أنواع الإدخال (قائمة الأنواع على الجانب الأيمن من ->) وأنواع الإخراج (قائمة الأنواع على الجانب الأيسر من ->). وفي العديد من لغات البرمجة، تكون أنواع الدوال من الدرجة الأولى، ولكن ليس في StableHLO.
StringType ::= 'string'
يمثّل نوع السلسلة تسلسلات البايتات. على عكس العديد من لغات البرمجة، لا يُعدّ نوع السلسلة من الأنواع الأساسية في StableHLO، ويُستخدم فقط لتحديد البيانات الوصفية الثابتة لعناصر البرنامج.
العمليات
تمثّل عمليات StableHLO (المعروفة أيضًا باسم العمليات) مجموعة مغلقة من العمليات العالية المستوى في نماذج تعلُّم الآلة. كما ذكرنا أعلاه، فإنّ بنية StableHLO مستوحاة بشكل كبير من MLIR، وهي ليست بالضرورة البديل الأكثر ملاءمة، ولكنها الأنسب لتحقيق هدف StableHLO المتمثل في توفير المزيد من إمكانية التشغيل التفاعلي بين أُطر تعلُّم الآلة ومترجمات تعلُّم الآلة.
Op ::= [OpOutputs] OpName OpInputs ':' OpSignature
OpName ::= '"' 'stablehlo' '.' OpMnemonic '"'
OpMnemonic ::= 'abs' | 'add' | ...
عمليات StableHLO (المعروفة أيضًا باسم العمليات) لها اسم
ومدخلات/مخرجات وتوقيع. يتألف الاسم من البادئة stablehlo. وعبارة سهلة التذكّر تحدّد بشكل فريد إحدى العمليات المتوافقة. يمكنك الاطّلاع أدناه على قائمة شاملة بجميع العمليات المتوافقة.
OpInputs ::= OpInputValues OpInputFuncs OpInputAttrs
OpInputValues ::= '(' [OpInputValue {',' OpInputValue}] ')'
OpInputValue ::= ValueId
OpInputFuncs ::= ['(' OpInputFunc {',' OpInputFunc} ')']
OpInputAttrs ::= ['{' OpInputAttr {',' OpInputAttr} '}']
OpOutputs ::= [OpOutput {',' OpOutput} '=']
OpOutput ::= ValueId
تستهلك العمليات مدخلات وتنتج مخرجات. يتم تصنيف المدخلات إلى قيم إدخال (يتم احتسابها أثناء التنفيذ) ودوال إدخال (يتم توفيرها بشكل ثابت، لأنّ الدوال ليست قيمًا من الدرجة الأولى في StableHLO) وسمات إدخال (يتم توفيرها أيضًا بشكل ثابت). يعتمد نوع المدخلات والمخرجات التي تستهلكها العملية وتنتجها على الاختصار الخاص بها. على سبيل المثال، تستهلك العملية add
قيمتَي إدخال وتنتج قيمة إخراج واحدة. في المقابل، تستهلك العملية
select_and_scatter 3 قيم إدخال و2 من دوال الإدخال و3 سمات إدخال.
OpInputFunc ::= '{' Unused FuncInputs ':' FuncBody '}'
Unused ::= '^' digit {digit}
| '^' letter {letter | digit}
دوال الإدخال (المعروفة أيضًا باسم الدوال المجهولة) تشبه إلى حد كبير الدوال المُسمّاة، باستثناء ما يلي: 1) ليس لها معرّف (ومن هنا جاءت تسميتها "مجهولة")، 2) لا تحدّد أنواع الإخراج (يتم استنتاج أنواع الإخراج من عملية return داخل الدالة).
يتضمّن بناء جملة دوال الإدخال جزءًا غير مستخدَم حاليًا (راجِع Unusedالإنتاج أعلاه) وهو مخصّص للتوافق مع MLIR. في 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، يمكن استنتاج أنواع الإخراج من الإدخالات). ومع ذلك، فإنّ توقيع العملية هو جزء من بنية StableHLO عمدًا لتحقيق التوافق مع MLIR.
في ما يلي مثال على عملية لها اختصار select_and_scatter. تستهلك هذه الدالة 3 قيم إدخال (%operand و%source و%init_value) ودالتَي إدخال و3 سمات إدخال (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.
العمليات
abs
الدلالات
تُجري هذه الدالة عملية القيمة المطلقة لكل عنصر في الموتر operand وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأعداد الصحيحة الموقَّعة: معامل الأعداد الصحيحة.
- بالنسبة إلى الأرقام العشرية:
absمن IEEE-754 - بالنسبة إلى الأعداد المركّبة: المعامل المركّب
- بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(abs, operand, type(result)).
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
موتر من نوع عدد صحيح موقّع أو عدد نقطة عائمة أو عدد مركّب أو موتر كمّي لكل موتر | (C1-C2) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
متّجه من نوع عدد صحيح موقّع أو عدد نقطة عائمة أو متّجه كمّي لكل متّجه | (C1-C2) |
القيود
- (C1)
shape(result) = shape(operand) - يتم تعريف (C2)
baseline_element_type(result)على النحو التالي:complex_element_type(element_type(operand))إذاis_complex(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 المنطقية
- بالنسبة إلى الأعداد الصحيحة: جمع الأعداد الصحيحة
- بالنسبة إلى الأرقام العشرية:
additionمن IEEE-754 - بالنسبة إلى الأعداد المركّبة: الجمع المركّب
- بالنسبة إلى الأنواع الكمية:
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، يتم ربط قيم موترات operands من كل عملية على طول all_gather_dim وإنتاج موترات results.
تقسّم العملية شبكة عمليات StableHLO إلى process_groups، ويتم تعريفها على النحو التالي:
cross_replica(replica_groups)ifchannel_id <= 0 and use_global_device_ids = falsecross_replica_and_partition(replica_groups)ifchannel_id > 0 and use_global_device_ids = falseflattened_ids(replica_groups)ifchannel_id > 0 and use_global_device_ids = true
بعد ذلك، ضمن كل process_group:
-
operands...@receiver = [operand@sender for sender in process_group]لكلreceiverفيprocess_group -
results...@process = concatenate(operands...@process, all_gather_dim)لكلprocessفيprocess_group
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operands |
عدد متغير من الموترات أو الموترات الكمية لكل موتر | (C1)، (C6) |
| (I2) | all_gather_dim |
ثابت من النوع si64 |
(C1)، (C6) |
| (I3) | replica_groups |
ثابت موتر ثنائي الأبعاد من النوع si64 |
(C2-C4) |
| (I4) | channel_id |
ثابت من النوع si64 |
(C5) |
| (I5) | use_global_device_ids |
ثابت من النوع i1 |
(C5) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
results |
عدد متغير من الموترات أو الموترات الكمية لكل موتر | (C6) |
القيود
- (C1)
0 <= all_gather_dim < rank(operands...) - (C2)
is_unique(replica_groups) - يتم تعريف (C3)
size(replica_groups)على النحو التالي:num_replicasفي حال استخدامcross_replicanum_replicasفي حال استخدامcross_replica_and_partitionnum_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، يتم تطبيق دالة تقليل computation على قيم موترات operands من كل عملية، ويتم إنشاء موترات results.
تقسّم العملية شبكة عمليات StableHLO إلى process_groups، ويتم تعريفها على النحو التالي:
cross_replica(replica_groups)ifchannel_id <= 0 and use_global_device_ids = falsecross_replica_and_partition(replica_groups)ifchannel_id > 0 and use_global_device_ids = falseflattened_ids(replica_groups)ifchannel_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هي شجرة ثنائية محدّدة التنفيذ يكون اجتيازها بالترتيب الداخلي هوto_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_replicanum_replicasفي حال استخدامcross_replica_and_partitionnum_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، يتم تقسيم قيم موترات operands على طول split_dimension إلى أجزاء، ويتم توزيع الأجزاء المقسّمة بين العمليات، ثم يتم ربط الأجزاء الموزّعة على طول concat_dimension وإنتاج موترات results.
تقسّم العملية شبكة عمليات StableHLO إلى process_groups، ويتم تعريفها على النحو التالي:
cross_replica(replica_groups)إذاchannel_id <= 0cross_partition(replica_groups)إذاchannel_id > 0
بعد ذلك، ضمن كل process_group:
split_parts...@sender = split(operands...@sender, split_count, split_dimension)لكلsenderفيprocess_groupscattered_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_replicanum_partitionsفي حال استخدامcross_partition
- (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]]
و
الدلالات
تُجري هذه الدالة عملية AND على مستوى العناصر في موترَين lhs وrhs، وتنتج موترًا result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى القيم المنطقية: AND المنطقية
- بالنسبة إلى الأعداد الصحيحة: استخدام AND على مستوى البت.
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | lhs |
موتر من النوع المنطقي أو العدد الصحيح | (C1) |
| (I2) | rhs |
موتر من النوع المنطقي أو العدد الصحيح | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
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
الدلالات
تُجري هذه الدالة عملية atan2 على مستوى العناصر في الموترَين lhs وrhs، وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأرقام العشرية:
atan2من IEEE-754 - بالنسبة إلى الأعداد المركّبة: دالة atan2 المركّبة.
- بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(atan2, lhs, rhs, type(result)).
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | lhs |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
| (I2) | rhs |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
القيود
- (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
الدلالات
تحسب هذه العملية تدرّجات عدة مدخلات من batch_norm_training التي يتم نشرها عكسيًا من grad_output، وتنتج موترات 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 |
موتر أحادي البُعد من النوع الكمي ذي النقطة العائمة أو النوع الكمي لكل موتر | (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
الدلالات
تعمل هذه الدالة على تسوية موتر operand على مستوى جميع السمات باستثناء السمة feature_index، وتنتج موتر 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 |
موتر أحادي البُعد من النوع الكمي ذي النقطة العائمة أو النوع الكمي لكل موتر | (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، وتُجري تسوية على الموتر operand لإنتاج الموترات output وbatch_mean وbatch_var. بشكل أكثر رسمية، يمكن التعبير عن هذه العملية على أنّها عملية تقسيم إلى عمليات 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 |
موتر أحادي البُعد من النوع "نقطة عائمة" أو "تكميم لكل موتر" | (C2), (C3) |
| (I3) | offset |
موتر أحادي البُعد من النوع "نقطة عائمة" أو "تكميم لكل موتر" | (C2)، (C4) |
| (I4) | epsilon |
ثابت من النوع f32 |
(C1)، (C3-C6) |
| (I5) | feature_index |
ثابت من النوع si64 |
(C1)، (C3-C6) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
output |
متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه | (C7) |
batch_mean |
موتر أحادي البُعد من النوع "نقطة عائمة" أو "تكميم لكل موتر" | (C2)، (C5) |
batch_var |
موتر أحادي البُعد من النوع "نقطة عائمة" أو "تكميم لكل موتر" | (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
الدلالات
تُجري عملية bitcast على موتر 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 < Rdim(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 < Rdim(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] حيث إنّه لكل d في
axes(operand):
operand_index[d] = 0إذاdim(operand, d) = 1operand_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)من خلال:-
element_type(operand)، إذا!is_per_axis_quantized(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) لكل
dفيaxes(operand):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]
// ]
// ]
حافظة
الدلالات
تنتج هذه الدالة المخرجات من تنفيذ دالة واحدة بالضبط من branches
استنادًا إلى قيمة index. بشكل أكثر رسمية، result = selected_branch()
حيث:
selected_branch = branches[index]إذا0 <= index < size(branches)selected_branch = branches[-1]في ما عدا ذلك.
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | index |
موتر صفري الأبعاد من النوع si32 |
|
| (I2) | branches |
عدد متغير من الدوال | (C1-C4) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
results |
عدد متغيّر من الموترات أو الموترات الكمية أو الرموز المميزة | (C4) |
القيود
- (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]
cbrt
الدلالات
تُجري عملية الجذر التكعيبي على مستوى كل عنصر في الموتر operand وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأرقام العشرية:
rootn(x, 3)من IEEE-754. - بالنسبة إلى الأعداد المركّبة: الجذر التكعيبي المركّب
- بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(cbrt, operand, type(result))
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
القيود
- (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.
تنفيذ عملية roundToIntegralTowardPositive من مواصفات IEEE-754 بالنسبة إلى الأنواع المحدَّدة القيمة، يتم تنفيذ
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]
cholesky
الدلالات
تحسب هذه الدالة تحليل شوليسكي لمجموعة من المصفوفات.
بشكل أكثر رسمية، لكل i في index_space(result)،
result[i0, ..., iR-3, :, :] هو تحليل Cholesky
لـ 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 |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
القيود
- (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 |
متّجه أو متّجه كمّي لكل متّجه | (C1-C4) |
| (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، ويتم تعريفها على النحو التالي:
cross_replica(replica_groups)إذاchannel_id <= 0cross_partition(replica_groups)إذاchannel_id > 0
بعد ذلك، يتم حساب 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على النحو التالي:num_replicasفي حال استخدامcross_replicanum_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، ويتم تعريفها على النحو التالي:
cross_replica(source_target_pairs)إذاchannel_id <= 0cross_partition(source_target_pairs)إذاchannel_id > 0
بعد ذلك، يتم حساب 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_replicanum_partitionsفي حال استخدامcross_partition
- (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]]
مقارنة
الدلالات
تُجري هذه الدالة مقارنة بين عناصر الموترَين lhs وrhs وفقًا لـ comparison_direction وcompare_type، وتنتج الموتر 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، تنفّذ العملية الحسابية عمليات IEEE-754 التالية:
EQ:compareQuietEqual.NE:compareQuietNotEqual.GE:compareQuietGreaterEqual.GT:compareQuietGreater.LE:compareQuietLessEqual.LT:compareQuietLess.
بالنسبة إلى أنواع العناصر ذات الفاصلة العائمة التي تتضمّن compare_type = TOTALORDER، تستخدم العملية الحسابية مجموعة العمليتين totalOrder وcompareQuietEqual من معيار IEEE-754.
بالنسبة إلى أنواع العناصر المعقّدة، تتم المقارنة المعجمية لأزواج (real, imag) باستخدام comparison_direction وcompare_type المقدَّمتَين.
يؤدي فرض ترتيب على الأعداد المركّبة إلى دلالات مفاجئة، لذا نخطّط في المستقبل لإيقاف إتاحة الأعداد المركّبة عندما تكون قيمة 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 |
|
| (I4) | compare_type |
تعداد FLOAT وTOTALORDER وSIGNED وUNSIGNED |
(C3) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
متّجه من النوع المنطقي | (C2) |
القيود
- (C1)
baseline_element_type(lhs) = baseline_element_type(rhs) - (C2)
shape(lhs) = shape(rhs) = shape(result) - يتم تعريف (C3)
compare_typeعلى النحو التالي:SIGNEDإذاis_signed_integer(element_type(lhs))UNSIGNEDإذاis_unsigned_integer(element_type(lhs)) or is_boolean(element_type(lhs))-
FLOATأوTOTALORDERإذاis_float(element_type(lhs)) FLOATإذاis_complex(element_type(lhs))
أمثلة
// %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 |
(C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
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 بتفكيكها بدون تغيير دلالات البرنامج. في الحالات التي لا يؤدي فيها تضمين عملية التفكيك إلى توفير دلالات op نفسها، يُفضّل استخدام custom_call.
يُستخدَم الحقل version (القيمة التلقائية هي 0) للإشارة إلى وقت تغيُّر دلالات العنصر المركّب.
المدخلات
| التصنيف | الاسم | النوع |
|---|---|---|
| (I1) | inputs |
عدد متغيّر من القيم |
| (I2) | name |
ثابت من النوع string |
| (I3) | composite_attributes |
قاموس السمات |
| (I4) | 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، ... هي أحجام السمة رقمdمنinputs.
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | inputs |
عدد متغير من الموترات أو الموترات الكمية لكل موتر | (C1-C6) |
| (I2) | dimension |
ثابت من النوع si64 |
(C2)، (C4)، (C6) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
متّجه أو متّجه كمّي لكل متّجه | (C5-C6) |
القيود
- (C1)
same(element_type(inputs...)) - (C2)
same(shape(inputs...))باستثناءdim(inputs..., dimension) - (C3)
0 < size(inputs) - (C4)
0 <= dimension < rank(inputs[0]) - (C5)
element_type(result) = element_type(inputs[0]). - (C6)
shape(result) = shape(inputs[0])باستثناء: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]]
الثابت
الدلالات
تُنشئ هذه الدالة موتر output من قيمة ثابتة value.
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | value |
الثابت | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
output |
متّجه أو متّجه كمّي | (C1) |
القيود
- (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.
بالنسبة إلى عمليات التحويل من boolean-to-any-supported-type، يتم تحويل القيمة false إلى صفر، ويتم تحويل القيمة true إلى واحد. بالنسبة إلى عمليات التحويل من
any-supported-type-to-boolean، يتم تحويل القيمة صفر إلى
false، ويتم تحويل القيم غير الصفرية إلى true. يمكنك الاطّلاع أدناه على كيفية عمل ذلك مع الأنواع المعقّدة.
بالنسبة إلى عمليات التحويل التي تتضمّن عددًا صحيحًا إلى عدد صحيح أو عددًا صحيحًا إلى عدد ذي فاصلة عشرية أو عددًا ذا فاصلة عشرية إلى عدد ذي فاصلة عشرية، إذا كان من الممكن تمثيل القيمة المصدرية بدقة في نوع الوجهة، تكون القيمة الناتجة هي التمثيل الدقيق. وفي ما عدا ذلك، سيتم تحديد السلوك لاحقًا (#180).
بالنسبة إلى عمليات التحويل التي تتضمّن floating-point-to-integer، يتم اقتطاع الجزء الكسري. إذا تعذّر تمثيل القيمة المقتطعة في نوع الوجهة، سيتم تحديد السلوك لاحقًا (#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، يكون ذلك لجميع
output_spatial_index في index_space(dim(result, output_spatial_dimensions...))،
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 |
(C9) |
| (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) Given
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) Given
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)على النحو التالي:dim(lhs, input_batch_dimension) / batch_group_countإذاresult_dim = output_batch_dimensiondim(rhs, kernel_output_feature_dimension)إذاresult_dim = output_feature_dimensionnum_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) If
is_per_tensor_quantized(rhs), thenis_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. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأرقام العشرية:
cosمن IEEE-754 - بالنسبة إلى الأعداد المركّبة: جيب التمام المركّب
- بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(cosine, operand, type(result)).
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
القيود
- (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 |
متّجه متعدّد الأبعاد من نوع عدد صحيح | (C1) |
القيود
- (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 لتوفير بيانات وصفية إضافية يحدّدها التنفيذ.
في الوقت الحالي، تحتوي هذه العملية على مجموعة غير منظَّمة إلى حدّ ما من البيانات الوصفية التي تعكس التطور الطبيعي للعملية المقابلة في برنامج XLA المجمّع. نخطّط في المستقبل لتوحيد هذه البيانات الوصفية (#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 |
| (I7) | output_operand_aliases |
تحديد أجزاء التوافق في النتائج والمعاملات |
النواتج
| الاسم | النوع |
|---|---|
results |
عدد متغيّر من القيم |
(دعم وحدة معالجة الرسومات في XLA) استهدافات خاصة في custom_call
هناك ثلاثة أنواع خاصة من call_target_name مرتبطة بأنواع buffer:
ينشئ CreateBuffer buffer غير مهيأ، وينشئ Pin buffer مهيأ،
ويزيل Unpin تخصيص buffer ويعرض محتوى buffer.
%uninitialized_buffer = "stablehlo.custom_call"() {
call_target_name = "CreateBuffer",
api_version = 4 : i32,
} : () -> memref<4xf64>
%initialized_buffer = "stablehlo.custom_call"(%init_value) {
call_target_name = "Pin",
api_version = 4 : i32,
} : (tensor<4xf64>) -> memref<4xf64>
%dealloc_buffer = "stablehlo.custom_call"(%initialized_buffer) {
call_target_name = "Unpin",
api_version = 4 : i32,
} : (memref<4xf64>) -> tensor<4xf64>
الاسم المستعار
قد تتطلّب بعض عمليات custom_call جزءًا في النواتج وجزءًا في المعامِلات لمشاركة الذاكرة نفسها. ويمكن التعبير عن ذلك باستخدام output_operand_aliases. يتألف تمثيل زوج الأسماء المستعارة من قائمة بفهارس الصفوف الناتجة التي تمثّل الجزء الناتج، وفهرس operand_index مع قائمة بفهارس صفوف المعامِلات التي تمثّل جزء المعامِلات. تكون قائمة فهارس مجموعة البيانات الناتجة أو مجموعة البيانات الخاصة بالمعامل فارغة إذا لم يكن النوع المقابل نوع tuple، ويمكن أن تكون طويلة بشكل عشوائي لنوع مجموعة بيانات متداخلة بشكل عشوائي. وهذا مشابه لتمثيل الاسم المستعار XLA.
يجب أن يكون لجزء الإخراج وجزء الإدخال في زوج الاسم المستعار النوع نفسه. بالنسبة إلى عمليات
custom_call التي لا تتضمّن استدعاءً إلى CreateBuffer وPin وUnpin، يمكن أن يظهر المعامل buffer في زوج واحد على الأكثر من الأسماء المستعارة، ويجب أن يظهر الناتج buffer في زوج واحد من الأسماء المستعارة.
أمثلة
%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>
%updated_buffer = "stablehlo.custom_call"(%buffer) {
call_target_name = "Update",
api_version = 4 : i32,
output_operand_aliases = [
#stablehlo.output_operand_alias<output_tuple_indices = [],
operand_index = 0,
operand_tuple_indices = []>]
} : (memref<4xf64>) -> memref<4xf64>
قسمة
الدلالات
تُجري هذه الدالة عملية قسمة على مستوى العناصر بين موترَي المقسوم lhs والمقسوم عليه rhs، وتنتج موترًا result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأعداد الصحيحة: القسمة على عدد صحيح التي تنتج خارج القسمة الجبرية مع تجاهل أي جزء كسري
- بالنسبة إلى الأرقام العشرية:
divisionمن IEEE-754. - بالنسبة إلى الأعداد المركّبة: القسمة المركّبة
- بالنسبة إلى الأنواع الكمية:
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 | (C21) |
| (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) If
is_per_tensor_quantized(rhs), thenis_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 |
موتر ثابت أحادي البُعد من نوع عدد صحيح | (C2-C6) |
| (I4) | known_expanding_dimensions |
موتر ثابت أحادي البُعد من نوع عدد صحيح | (C8-C9) |
| (I5) | known_nonexpanding_dimensions |
موتر ثابت أحادي البُعد من نوع عدد صحيح | (C8-C9) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
متّجه أو متّجه كمّي | (C1)، (C3)، (C5-C7) |
القيود
- يتم تحديد (C1)
element_type(result)من خلال:-
element_type(operand)، إذا!is_per_axis_quantized(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) لكل
dفيaxes(operand):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 |
(C9) |
| (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 |
(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) Given
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) Given
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)على النحو التالي:dim(lhs, input_batch_dimension) / batch_group_countإذاresult_dim = output_batch_dimensiondim(rhs, kernel_output_feature_dimension)إذاresult_dim = output_feature_dimensionnum_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) If
is_per_tensor_quantized(rhs), thenis_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)باستثناء أنّه لم يتم تضمين حجم السمةstart_indicesالذي يتوافق معindex_vector_dim.offset_dim_sizes = shape(slice_sizes)باستثناء أحجام السمات فيslice_sizesالمقابلةcollapsed_slice_dimsغير مضمّنة.- تضع الدالة
combinebatch_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، ولكن مع تحديد edge_padding_low وedge_padding_high وinterior_padding
بشكل ديناميكي كقيم.
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
متّجه أو متّجه كمّي لكل متّجه | (C1)، (C2)، (C4) |
| (I2) | padding_value |
متّجه صفري الأبعاد أو متّجه كمّي لكل متّجه | (C1) |
| (I3) | edge_padding_low |
موتر أحادي البُعد من نوع عدد صحيح | (C1)، (C4) |
| (I4) | edge_padding_high |
موتر أحادي البُعد من نوع عدد صحيح | (C1)، (C4) |
| (I5) | interior_padding |
موتر أحادي البُعد من نوع عدد صحيح | (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 |
متّجه أو متّجه كمّي | (C1-C4) |
القيود
- يتم تحديد (C1)
element_type(result)من خلال:-
element_type(operand)، إذا!is_per_axis_quantized(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. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأرقام العشرية:
expمن IEEE-754 - بالنسبة إلى الأعداد المركّبة: الأس المركّب
- بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(exponential, operand, type(result))
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
القيود
- (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. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأرقام العشرية:
expm1من IEEE-754 - بالنسبة إلى الأعداد المركّبة: الأس المركّب ناقص واحد.
- بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(exponential_minus_one, operand, type(result))
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
القيود
- (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_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حيثcomplex_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 |
(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) إذا كان هناك موتر
realمن نوع نقطة عائمة بينoperandوresult، يكون الناتج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.
تنفيذ عملية roundToIntegralTowardNegative من مواصفات IEEE-754 بالنسبة إلى الأنواع المحدَّدة القيمة، يتم تنفيذ
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]
جمع
الدلالات
تجمع هذه العملية شرائح من موتر operand من الإزاحات المحددة في start_indices وتنتج موتر 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]]في ما عدا ذلك.
- بالنسبة إلى
d_operandفيaxes(operand)،full_start_index[d_operand] = clamp(start_index[d_start], 0, dim(operand, d_operand) - slice_sizes[d_operand])ifd_operand = start_index_map[d_start]full_start_index[d_operand] = 0في ما عدا ذلك.
- بالنسبة إلى
d_operandفيaxes(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، وإلا سيكون السلوك غير محدّد. بشكل أكثر رسمية، لكل i1 < i2 من indices(result)،
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 |
(C1)، (C4-C5)، (C22) |
| (I4) | collapsed_slice_dims |
ثابت موتر أحادي البُعد من النوع si64 |
(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)باستثناء أنّه لم يتم تضمين حجم السمةstart_indicesالذي يتوافق معindex_vector_dim.offset_dim_sizes = slice_sizesباستثناء أحجام السمات فيslice_sizesالمقابلةcollapsed_slice_dimsوoperand_batching_dimsغير مضمّنة.- تضع الدالة
combinebatch_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
الدلالات
تعرض هذه الدالة حجم dimension المحدّد من operand. بشكل أكثر رسمية،
result = dim(operand, dimension). لا تهتم الدلالات إلا بمكوّن الشكل الخاص بالنوع. يمكن أن يكون نوع العنصر أي شيء.
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
متّجه أو متّجه كمّي | (C1) |
| (I2) | dimension |
ثابت من النوع si64 |
(C1) |
النواتج
| الاسم | النوع |
|---|---|
result |
موتر صفري الأبعاد من النوع si32 |
القيود
- (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
الدلالات
تستخرج هذه الدالة العنصر في الموضع index من الصف operand وتنتج result. بشكل أكثر رسمية، result = operand[index].
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
tuple | (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))
%result = "stablehlo.get_tuple_element"(%operand) <{index = 0 : i32}> : (tuple<tensor<2xf64>, tuple<tensor<i64>>>) -> tensor<2xf64>
// %result: [1.0, 2.0]
إذا
الدلالات
تُنتج هذه الدالة المخرجات من تنفيذ دالة واحدة بالضبط من true_branch أو false_branch استنادًا إلى قيمة pred. بشكل أكثر رسمية، result =
pred ? true_branch() : false_branch().
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | pred |
موتر صفري الأبعاد من النوع i1 |
|
| (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)على النحو التالي:complex_element_type(element_type(operand))إذاis_complex(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]
infeed
الدلالات
يقرأ البيانات من الخلاصة داخل التطبيق وينتج 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 |
(C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
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. تنفيذ عملية isFinite
من مواصفات IEEE-754 بالنسبة إلى الأنواع الكمية، تكون النتيجة دائمًا true.
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | x |
متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
y |
متّجه من النوع المنطقي | (C1) |
القيود
- (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. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأرقام العشرية:
logمن IEEE-754 - بالنسبة إلى الأعداد المركّبة: اللوغاريتم المركّب
- بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(log, operand, type(result)).
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
القيود
- (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. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأرقام العشرية:
logp1من IEEE-754 - بالنسبة إلى الأعداد المركّبة:
complex(log(hypot(real(x) + 1, imag(x))), atan2(imag(x), real(x) + 1)) - بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(log_plus_one, operand, type(result))
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
القيود
- (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. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأرقام العشرية:
division(1, addition(1, exp(-x)))من IEEE-754. - بالنسبة إلى الأعداد المركّبة: دالة لوجستية مركّبة
- بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(logistic, operand, type(result))
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
القيود
- (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]]
خريطة
الدلالات
تطبِّق دالة الخريطة computation على inputs على طول dimensions وتنتج موترًا 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 المنطقي
- بالنسبة إلى الأعداد الصحيحة: الحد الأقصى للعدد الصحيح
- بالنسبة إلى الأرقام العشرية:
maximumمن IEEE-754. - بالنسبة إلى الأعداد المركّبة: الحد الأقصى المعجمي للزوج
(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. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى القيم المنطقية: AND المنطقية
- بالنسبة إلى الأعداد الصحيحة: الحد الأدنى للعدد الصحيح.
- بالنسبة إلى الأرقام العشرية:
minimumمن IEEE-754 - بالنسبة إلى الأعداد المركّبة: الحد الأدنى المعجمي للزوج
(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. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى القيم المنطقية: AND المنطقية
- بالنسبة إلى الأعداد الصحيحة: ضرب الأعداد الصحيحة
- بالنسبة إلى الأرقام العشرية:
multiplicationمن IEEE-754 - بالنسبة إلى الأعداد المركّبة: الضرب المركّب
- بالنسبة إلى الأنواع الكمية:
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. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأعداد الصحيحة الموقّعة: نفي العدد الصحيح.
- بالنسبة إلى الأعداد الصحيحة غير الموقّعة: تحويل إلى عدد صحيح موقّع، ونفي العدد الصحيح، والتحويل مرة أخرى إلى عدد صحيح غير موقّع
- بالنسبة إلى الأرقام العشرية:
negateمن IEEE-754 - بالنسبة إلى الأعداد المركّبة: النفي المركّب
- بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(negate, operand, type(result))
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
موتر من النوع الصحيح أو النقطة العائمة أو المركّب أو موتر كمّي لكل موتر | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
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]
ليس
الدلالات
تُجري هذه الدالة عملية NOT على مستوى كل عنصر في الموتر operand وتنتج الموتر result.
استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى القيم المنطقية: المعامل NOT المنطقي
- بالنسبة إلى الأعداد الصحيحة: استخدام NOT على مستوى البت.
الوسيطات
| الاسم | النوع | القيود |
|---|---|---|
operand |
موتر من النوع المنطقي أو العدد الصحيح | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
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
أو
الدلالات
تُجري هذه الدالة عملية OR على العناصر المقابلة في موترَين lhs وrhs، وتنتج موترًا result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى القيم المنطقية: OR المنطقية
- بالنسبة إلى الأعداد الصحيحة: استخدام OR على مستوى البت.
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | lhs |
موتر من نوع عدد صحيح أو قيمة منطقية | (C1) |
| (I2) | rhs |
موتر من نوع عدد صحيح أو قيمة منطقية | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
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]]
outfeed
الدلالات
تكتب 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
وسادة
الدلالات
توسّع هذه الدالة operand من خلال إضافة مساحة متروكة حول الموتر وكذلك بين عناصر الموتر باستخدام padding_value المحدّدة.
تحدّد 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 |
متّجه صفري الأبعاد أو متّجه كمّي لكل متّجه | (C1) |
| (I3) | edge_padding_low |
ثابت موتر أحادي البُعد من النوع si64 |
(C1)، (C4) |
| (I4) | edge_padding_high |
ثابت موتر أحادي البُعد من النوع si64 |
(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 |
أمثلة
%result = "stablehlo.partition_id"() : () -> tensor<ui32>
popcnt
الدلالات
تُجري هذه العملية حسابًا على مستوى العناصر لعدد البتات المضبوطة في الموتر operand وتنتج الموتر result.
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
متّجه متعدّد الأبعاد من نوع عدد صحيح | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
متّجه متعدّد الأبعاد من نوع عدد صحيح | (C1) |
القيود
- (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. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأعداد الصحيحة: الأس الصحيح
- بالنسبة إلى الأرقام العشرية:
powمن IEEE-754 - بالنسبة إلى الأعداد المركّبة: الأس المركّب
- بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(power, lhs, rhs, type(result)).
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | lhs |
موتر من النوع الصحيح أو النقطة العائمة أو المركّب أو موتر كمّي لكل موتر | (C1) |
| (I2) | rhs |
موتر من النوع الصحيح أو النقطة العائمة أو المركّب أو موتر كمّي لكل موتر | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
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)على النحو التالي:complex_element_type(element_type(operand))إذاis_complex(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، فهذا يعني أنّ العملية تنقل البيانات من المضيف. بخلاف ذلك، ينقل البيانات من جهاز آخر استنادًا إلى قيم
source_target_pairs. تكرّر هذه العلامة المعلومات المقدَّمة في
channel_type، لذا نخطّط في المستقبل للإبقاء على إحدى العلامتين فقط
(#666). إذا كانت قيمة is_host_transfer تساوي false وكانت قيمة source_target_pairs هي None أو فارغة، سيتم اعتبار ذلك سلوكًا غير محدّد.
results تتألف من قيم الحمولة التي تأتي أولاً ورمز مميز يأتي
أخيرًا. في المستقبل، نخطّط لتقسيم الحمولة والرمز المميّز إلى نتيجتَين منفصلتَين لتحسين الوضوح
(#670).
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | token |
token |
|
| (I2) | channel_id |
ثابت من النوع si64 |
|
| (I3) | channel_type |
تعداد DEVICE_TO_DEVICE وDEVICE_TO_HOST |
(C5) |
| (I4) | is_host_transfer |
ثابت من النوع i1 |
(C5-C6) |
| (I5) | source_target_pairs |
ثابت موتر ثنائي الأبعاد من النوع si64 |
(C1-C4), (C6) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
results |
عدد متغيّر من الموترات أو الموترات الكمية أو الرموز المميزة | (C2-C4) |
القيود
- (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_replicanum_partitionsفي حال استخدامcross_partition
- يتم تعريف (C5)
channel_typeعلى النحو التالي:-
DEVICE_TO_HOSTإذاis_host_transfer = true، DEVICE_TO_DEVICEفي ما عدا ذلك.
-
أمثلة
%results0, %results1 = "stablehlo.recv"(%token) {
channel_handle = #stablehlo.channel_handle<handle = 0, type = 1>,
is_host_transfer = false,
source_target_pairs = dense<[[0, 1], [1, 2]]> : tensor<2x2xi64>
} : (!stablehlo.token) -> (tensor<2x2xi64>, !stablehlo.token)
reduce
الدلالات
تطبِّق دالة تقليل body على inputs وinit_values على طول dimensions وتنتج موترات results.
ويتم تحديد ترتيب عمليات الاختزال حسب التنفيذ، ما يعني أنّه يجب أن يشكّل body وinit_values أحاديًا لضمان أن تؤدي العملية إلى النتائج نفسها لجميع المدخلات في جميع عمليات التنفيذ. ومع ذلك، لا ينطبق هذا الشرط على العديد من التخفيضات الشائعة. على سبيل المثال، لا يشكّل جمع الأعداد العشرية العائمة لـ body والقيمة صفر لـ init_values أحاديًا لأنّ جمع الأعداد العشرية العائمة ليس ترابطيًا.
بشكل أكثر رسمية، results...[j0, ..., jR-1] = reduce(input_slices_converted) حيث:
input_slices = inputs...[j0, ..., :, ..., jR-1]، حيث يتم إدراج:فيdimensionsinput_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هي شجرة ثنائية كاملة يحدّدها التنفيذ، ويتألف اجتيازها بالترتيب من:- قيم
input_slices_converted...[index]، لكلindexفيindex_space(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...)باستثناء أحجام السمةinputs...المقابلة لـdimensionsغير مضمّنة. - (C8)
element_type(results[i]) = Eiلكلiفي[0,N)
أمثلة
// %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.
بشكل أكثر رسمية:
- يتم تعديل أجزاء الأسيّة من القيمة الأصلية لتقريب القيمة الأصلية إلى أقرب قيمة يمكن تمثيلها باستخدام
mantissa_bitsباستخدام دلالاتroundToIntegralTiesToEven. - بعد ذلك، إذا كانت قيمة
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)ifchannel_id <= 0 and use_global_device_ids = falsecross_replica_and_partition(replica_groups)ifchannel_id > 0 and use_global_device_ids = falseflattened_ids(replica_groups)ifchannel_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).-
result@receiver = parts@sender[receiver_index]لجميعsenderفيprocess_group، حيث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_replicanum_replicasفي حال استخدامcross_replica_and_partitionnum_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
الدلالات
تطبِّق هذه الدالة دالة تقليل body على نوافذ بحجم inputs وinit_values
وتنتج results.
يوضّح المخطّط التالي كيفية احتساب العناصر في results... من inputs... باستخدام مثال ملموس.
بشكل أكثر رسمية،
results...[result_index] = reduce(windows, init_values, axes(inputs...), body)
(راجِع reduce) حيث:
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)
element_type(results[i]) = Eiلجميعiفي[0,N)
أمثلة
// %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). - بالنسبة إلى الأرقام العشرية:
division(lhs, rhs)من IEEE-754 مع سمة التقريبroundTowardZero. - بالنسبة إلى الأعداد المركّبة: سيتم تحديدها لاحقًا (#997).
- بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(remainder, lhs, rhs, type(result)).
بالنسبة إلى أنواع العناصر ذات النقطة العائمة، تختلف هذه العملية عن عملية remainder من مواصفات IEEE-754 حيث تكون d قيمة صحيحة
الأقرب إلى القيمة الدقيقة لـ lhs/rhs مع تساوي القيمتين.
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | lhs |
متّجه من نوع عدد صحيح أو عدد ذي فاصلة عشرية أو عدد مركّب أو متّجه كمّي لكل متّجه | (C1) |
| (I2) | rhs |
متّجه من نوع عدد صحيح أو عدد ذي فاصلة عشرية أو عدد مركّب أو متّجه كمّي لكل متّجه | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
متّجه من نوع عدد صحيح أو عدد ذي فاصلة عشرية أو عدد مركّب أو متّجه كمّي لكل متّجه | (C1) |
القيود
- (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 |
أمثلة
%result = "stablehlo.replica_id"() : () -> tensor<ui32>
إعادة التشكيل
الدلالات
تُجري هذه العملية إعادة تشكيل للموتر operand إلى الموتر result. من الناحية المفاهيمية، يشبه ذلك الاحتفاظ بالتمثيل الأساسي نفسه ولكن مع إمكانية تغيير الشكل، مثلاً من tensor<2x3xf32> إلى tensor<3x2xf32> أو tensor<6xf32>.
بشكل أكثر رسمية، result[result_index] = operand[operand_index] حيث
result_index وoperand_index لهما الموضع نفسه في الترتيب المعجمي
لـ index_space(result) وindex_space(operand).
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
متّجه أو متّجه كمّي | (C1-C3) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
متّجه أو متّجه كمّي | (C1-C3) |
القيود
- يتم تحديد (C1)
element_type(result)من خلال:-
element_type(operand)، إذا!is_per_axis_quantized(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]]
إلغاء
الدلالات
تعكس هذه الدالة ترتيب العناصر في operand على طول dimensions المحدّد
وتنتج موترًا result. بشكل أكثر رسمية،
result[result_index] = operand[operand_index] حيث:
operand_index[d] = dim(result, d) - result_index[d] - 1ifdindimensions.operand_index[d] = result_index[d]في ما عدا ذلك.
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
متّجه أو متّجه كمّي لكل متّجه | (C1)، (C3) |
| (I2) | dimensions |
ثابت موتر أحادي البُعد من النوع si64 |
(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 وتنتج موترًا result بالشكل shape المحدّد.
إذا كان 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 |
(C3) |
| (I4) | rng_distribution |
تعداد UNIFORM وNORMAL |
(C2) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
موتر من نوع عدد صحيح أو قيمة منطقية أو عدد نقطة عائمة | (C1-C3) |
القيود
- (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
الدلالات
تعرض هذه الدالة output مملوءًا بوحدات بت عشوائية منتظمة وحالة إخراج معدَّلة
output_state باستخدام خوارزمية إنشاء أرقام عشوائية زائفة rng_algorithm
مع حالة أولية initial_state. من المضمون أن يكون الناتج دالة قطعية لـ 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 |
(C1) |
output |
موتر من نوع عدد صحيح أو نقطة عائمة |
القيود
- (C1)
type(initial_state) = type(output_state) - يتم تعريف (C2)
size(initial_state)على النحو التالي:- تحدِّدها عملية التنفيذ إذا كانت
rng_algorithm = DEFAULT. 2إذاrng_algorithm = THREE_FRY-
2أو3إذاrng_algorithm = PHILOX
- تحدِّدها عملية التنفيذ إذا كانت
أمثلة
// %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. تنفّذ هذه الدالة العملية roundToIntegralTiesToAway من مواصفات IEEE-754. بالنسبة إلى الأنواع المكمَّمة، يتم تنفيذ 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. تنفيذ عملية roundToIntegralTiesToEven من مواصفات IEEE-754 بالنسبة إلى الأنواع المحدَّدة القيمة، يتم تنفيذ
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. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأرقام العشرية:
rSqrtمن IEEE-754. - بالنسبة إلى الأعداد المركّبة: الجذر التربيعي المقلوب المركّب
- بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(rsqrt, operand, type(result)).
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
القيود
- (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 بالقيم updates باستخدام update_computation.
يوضّح المخطّط التالي كيفية ربط العناصر في updates... بالعناصر في results... باستخدام مثال ملموس. يختار المخطّط البياني بعض المؤشرات كمثال
updates... ويشرح بالتفصيل المؤشرات results... التي تتوافق معها.
بشكل أكثر رسمية، لكل update_index في index_space(updates[0]):
update_scatter_dims = [d for d in axes(updates[0]) and d not in update_window_dims].update_scatter_index = update_index[update_scatter_dims...].- يتم تعريف
start_indexعلى النحو التالي:scatter_indices[si0, ..., :, ..., siN]حيثsiهي عناصر فردية فيupdate_scatter_indexويتم إدراج:في الفهرسindex_vector_dim، إذا كانindex_vector_dim<rank(scatter_indices).[scatter_indices[update_scatter_index]]في ما عدا ذلك.
- بالنسبة إلى
d_inputفيaxes(inputs[0])،full_start_index[d_input] = start_index[d_start]إذا كانd_input = scatter_dims_to_operand_dims[d_start].full_start_index[d_input] = 0في ما عدا ذلك.
- بالنسبة إلى
d_inputفيaxes(inputs[0])،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، وإلا سيكون السلوك غير محدّد. بشكل أكثر رسمية، بالنسبة إلى جميع i1 < i2 من indices(result)، يكون 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)باستثناء أنّه لا يتم تضمين حجم السمةscatter_indicesالذي يتوافق معindex_vector_dim.update_window_dim_sizes <= shape(inputs[0])باستثناء أنّه لا يتم تضمين أحجام السمات فيinputs[0]التي تتوافق معinserted_window_dimsوinput_batching_dims.- تضع الدالة
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)
element_type(results[i]) = Eiلكلiفي[0,N)
أمثلة
// %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]]]
// ],
// [
// [[[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 |
متّجه أو متّجه كمّي لكل متّجه | (C2) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
متّجه أو متّجه كمّي لكل متّجه | (C2) |
القيود
- (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
الدلالات
توزّع هذه العملية القيم من موتر source باستخدام scatter استنادًا إلى نتيجة reduce_window لموتر input باستخدام select، وتنتج موتر 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، باستثناء أنّscheduleالأساسيreduce(راجِع reduce) لا يتضمّن قيمًا أولية. لم يتم تحديد الإجراء الذي سيتم اتخاذه في حال عدم توفّر قيم في النافذة المقابلة (#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منoperand_index 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 |
متّجه صفري الأبعاد أو متّجه كمّي لكل متّجه | (C3) |
| (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]]
إرسال
الدلالات
إرسال inputs إلى القناة channel_id يتم بعد ذلك إرسال المدخلات إلى الأجهزة الأخرى
بالترتيب المحدّد في source_target_pairs. تنتج العملية رمزًا مميزًا
result.
إذا كانت قيمة is_host_transfer هي true، فهذا يعني أنّ العملية تنقل البيانات إلى المضيف. بخلاف ذلك، ينقل البيانات إلى جهاز آخر استنادًا إلى قيم
source_target_pairs. تكرّر هذه العلامة المعلومات المقدَّمة في
channel_type، لذا نخطّط في المستقبل للإبقاء على إحدى العلامتين فقط
(#666). إذا كانت قيمة is_host_transfer تساوي false وكانت قيمة source_target_pairs هي None أو فارغة، سيتم اعتبار ذلك سلوكًا غير محدّد.
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | inputs |
عدد متغيّر من الموترات أو الموترات الكمية | |
| (I2) | token |
token |
|
| (I3) | channel_id |
ثابت من النوع si64 |
|
| (I4) | channel_type |
تعداد DEVICE_TO_DEVICE وDEVICE_TO_HOST |
(C5) |
| (I5) | is_host_transfer |
ثابت من النوع i1 |
(C5-C6) |
| (I6) | source_target_pairs |
ثابت موتر ثنائي الأبعاد من النوع si64 |
(C1-C4), (C6) |
النواتج
| الاسم | النوع |
|---|---|
result |
token |
القيود
- (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_replicanum_partitionsفي حال استخدامcross_partition
- يتم تعريف (C5)
channel_typeعلى النحو التالي:-
DEVICE_TO_HOSTإذاis_host_transfer = true، DEVICE_TO_DEVICEفي ما عدا ذلك.
-
أمثلة
%result = "stablehlo.send"(%operand, %token) {
channel_handle = #stablehlo.channel_handle<handle = 0, type = 1>,
is_host_transfer = false,
source_target_pairs = dense<[[0, 1], [1, 2]]> : tensor<2x2xi64>
} : (tensor<2x2xi64>, !stablehlo.token) -> !stablehlo.token
shift_left
الدلالات
تُجري عملية إزاحة إلى اليسار على مستوى كل عنصر في الموتر lhs بمقدار rhs من وحدات البت وتنتج الموتر result.
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | lhs |
متّجه متعدّد الأبعاد من نوع عدد صحيح | (C1) |
| (I2) | rhs |
متّجه متعدّد الأبعاد من نوع عدد صحيح | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
متّجه متعدّد الأبعاد من نوع عدد صحيح | (C1) |
القيود
- (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 |
متّجه متعدّد الأبعاد من نوع عدد صحيح | (C1) |
القيود
- (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 |
متّجه متعدّد الأبعاد من نوع عدد صحيح | (C1) |
القيود
- (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. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأرقام العشرية:
sinمن IEEE-754. - بالنسبة إلى الأعداد المركّبة: جيب الزاوية المركّب
- بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(sine, operand, type(result)).
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
القيود
- (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]]
slice
الدلالات
يستخرِج شريحة من 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]
// ]
ترتيب
الدلالات
ترتّب هذه الدالة الشرائح الأحادية البُعد من inputs على طول البُعد dimension معًا،
وفقًا لـ comparator، وتنتج results.
على عكس المدخلات المشابهة في العمليات الأخرى، تسمح الدالة dimension بالقيم السالبة،
مع الدلالات الموضّحة أدناه. في المستقبل، قد يتم حظر ذلك لأسباب تتعلق بالاتساق
(#1377).
إذا كانت قيمة is_stable صحيحة، يكون الترتيب ثابتًا، أي يتم الحفاظ على الترتيب النسبي للعناصر التي يعتبرها عامل المقارنة متساوية. في حالة وجود إدخال واحد، يعتبر عامل المقارنة العنصرين e1 وe2 متساويين إذا وفقط إذا كان comparator(e1, e2) = comparator(e2, e1) = false. يمكنك الاطّلاع على الصيغة أدناه
لمعرفة كيفية تعميم ذلك على مدخلات متعددة.
بشكل أكثر رسمية، لكل result_index في index_space(results[0]):
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شريحة أحادية البُعد بترتيب غير تنازلي، مع توقّع أن تعرض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. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأرقام العشرية:
squareRootمن IEEE-754 - بالنسبة إلى الأعداد المركّبة: الجذر التربيعي المركّب
- بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(sqrt, operand, type(result)).
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
القيود
- (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. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأعداد الصحيحة: طرح الأعداد الصحيحة
- بالنسبة إلى الأرقام العشرية:
subtractionمن IEEE-754 - بالنسبة إلى الأعداد المركّبة: الطرح المركّب
- بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(subtract, lhs, rhs, type(result)).
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | lhs |
موتر من النوع الصحيح أو النقطة العائمة أو المركّب أو موتر كمّي لكل موتر | (C1) |
| (I2) | rhs |
موتر من النوع الصحيح أو النقطة العائمة أو المركّب أو موتر كمّي لكل موتر | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
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. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأرقام العشرية:
tanمن IEEE-754 - بالنسبة إلى الأعداد المركّبة: ظل الزاوية المركّب
- بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(tan, operand, type(result)).
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
القيود
- (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. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأرقام العشرية:
tanhمن IEEE-754 - بالنسبة إلى الأعداد المركّبة: ظل الزاوية الزائدي المركّب
- بالنسبة إلى الأنواع الكمية:
dequantize_op_quantize(tanh, operand, type(result)).
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | operand |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
القيود
- (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]
تبديل
الدلالات
تبدّل هذه العملية ترتيب أبعاد موتر operand باستخدام permutation وتنتج موتر 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)من خلال:-
element_type(operand)، إذا!is_per_axis_quantized(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، التي يمكن أن تكون إحدى القيم التالية:
-
NO_TRANSPOSE: تنفيذ العملية باستخدامaكما هي -
TRANSPOSE: تنفيذ عملية على منقولةa -
ADJOINT: تنفيذ عملية على منقولة مترافقة للمصفوفةa
تتم قراءة بيانات الإدخال من المثلث السفلي لـ a فقط، إذا كان lower هو true أو المثلث العلوي لـ 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 |
(C3) |
| (I4) | lower |
ثابت من النوع i1 |
|
| (I5) | unit_diagonal |
ثابت من النوع i1 |
|
| (I6) | transpose_a |
تعداد NO_TRANSPOSE وTRANSPOSE وADJOINT |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر | (C1) |
القيود
- (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]
// ]
tuple
الدلالات
تنشئ هذه الدالة مجموعة result من القيم val.
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | val |
عدد متغيّر من القيم | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
result |
tuple | (C1) |
القيود
- (C1)
resultمن النوعtuple<E0, ..., EN-1>حيثEi = type(val[i]).
أمثلة
// %val0: memref[1.0, 2.0]
// %val1: (3)
%result = "stablehlo.tuple"(%val0, %val1) : (memref<2xf32>, tuple<tensor<i32>>) -> tuple<memref<2xf32>, tuple<tensor<i32>>>
// %result: (memref[1.0, 2.0], (3))
uniform_dequantize
الدلالات
تُجري هذه العملية تحويلاً على مستوى العناصر للموتر الكمّي operand إلى موتر ذي نقطة عائمة result وفقًا لمعلّمات التكميم المحدّدة حسب نوع operand.
بشكل أكثر رسمية، 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
الدلالات
تُجري هذه العملية تحويلاً على مستوى العناصر لموتر نقطة عائمة أو موتر كمّي
operand إلى موتر كمّي result وفقًا لمعلمات التكميم
المحدّدة حسب نوع 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 صفر مرة أو أكثر طالما أنّ الدالة 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 |
دالة | (C2) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
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
الدلالات
تُجري هذه العملية XOR على مستوى العناصر في موترَين lhs وrhs وتنتج موترًا result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى القيم المنطقية: XOR المنطقية
- بالنسبة إلى الأعداد الصحيحة: استخدام XOR على مستوى البت.
المدخلات
| التصنيف | الاسم | النوع | القيود |
|---|---|---|---|
| (I1) | lhs |
موتر من النوع المنطقي أو العدد الصحيح | (C1) |
| (I2) | rhs |
موتر من النوع المنطقي أو العدد الصحيح | (C1) |
النواتج
| الاسم | النوع | القيود |
|---|---|---|
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 عمليات MLIR المصدرية لكل من ModuleOp وFuncOp وCallOp وReturnOp. تم إجراء ذلك لتحسين قابلية التشغيل التفاعلي مع آليات MLIR الحالية، لأنّ العديد من عمليات النقل المفيدة مكتوبة لاستهداف FuncOp وModuleOp، وتتوقّع العديد من مسارات التجميع توفّر هاتين العمليتين. يتم تطبيق ضمانات التوافق الكامل على هذه العمليات. إذا حدث أي تغيير على هذه العمليات بطريقة غير متوافقة (أي تمت إزالتها)، ستتم إضافة مكافئات StableHLO للحفاظ على التوافق.
CHLO
تحتوي مجموعة عمليات CHLO على عمليات ذات مستوى أعلى يتم تقسيمها إلى StableHLO. لا تتوفّر حاليًا أي ضمانات توافق مع CHLO. لضمان التوافق، يجب استخدام chlo-legalize-to-stablehlo pass قبل التسلسل.
عمليات الأشكال
من الحالات الشائعة الاستخدام في المنتدى استخدام عمليات معيّنة من لهجات MLIR الأساسية في برامج StableHLO الديناميكية لإجراء عمليات حسابية على الأشكال.
تشمل هذه الأنواع عادةً عمليات shape اللهجة مثل shape_of أو num_elements، وعمليات tensor اللهجة مثل dim أو from_elements، والنوع index المضمّن.
تُصنّف مواصفات RFC الخاصة بالديناميكية > O2
هذه الأنواع على أنّها خارج النطاق، ولكن تم تضمين بعض الدعم لأنواع index لأغراض التشغيل التفاعلي. لا تتوفّر أي ضمانات بشأن التوافق مع هذه العمليات أو الأنواع. يمكن استخدام عملية النقل shape-legalize-to-stablehlo
لتحويل هذه العمليات إلى عمليات StableHLO متوافقة بالكامل.
العمليات المتوقّفة نهائيًا
هناك العديد من عمليات StableHLO التي تم نقلها من MHLO، وهي متوقّفة نهائيًا وسيتم إزالتها من StableHLO. يمكنك الاطّلاع على التفاصيل الكاملة حول عمليات الإزالة هذه في StableHLO v1.0 Cleanup #2283. يمكنك الاطّلاع على المشكلة في أداة التتبُّع بشأن عمليات الإيقاف النهائي هذه على الرابط #2340.
تندرج هذه العمليات ضمن بضع فئات:
- فئة "غير متوفرة في HLO" من عمليات 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)، وسيتم إزالتها بعد انتهاء فترة التوافق الحالية (6 أشهر). لا يزال العمل جاريًا على إزالة بعضها (einsum وget_tuple_element وmap وrng وtorch_index_select وtuple وcomplex والمقارنات وwindow_reversal). وبانتظار ملاحظات المنتدى، سيتم إما إزالة هذه العمليات أو إضافتها إلى المواصفات مع توفير الدعم الكامل لها. إلى أن تصبح هذه العمليات المستقبلية معروفة، لا يمكن ضمان التوافق إلا لمدة 6 أشهر.
التنفيذ
التنفيذ التسلسلي
يتم تنفيذ برنامج StableHLO من خلال تقديم قيم إدخال إلى الدالة main واحتساب قيم الإخراج. يتم احتساب قيم الإخراج للدالة من خلال تنفيذ الرسم البياني للعمليات التي تبدأ من عملية return المقابلة.
ويتم تحديد ترتيب التنفيذ حسب التنفيذ طالما أنّه يتوافق مع تدفق البيانات، أي إذا تم تنفيذ العمليات قبل استخدامها. في 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 × num_partitions، وكلاهما من النوع ui32.
في شبكة عمليات StableHLO، يتم تنفيذ num_replicas * num_partitions من عمليات StableHLO في الوقت نفسه. لكل عملية process_id = (replica_id, partition_id) فريد، حيث replica_id في replica_ids = range(num_replicas) وpartition_id في partition_ids = range(num_partitions) وكلاهما من النوع ui32.
يتم تحديد حجم شبكة العمليات بشكل ثابت لكل برنامج (في المستقبل، نخطّط لجعلها جزءًا صريحًا من برامج StableHLO
#650)، ويتم تحديد الموضع
داخل شبكة العمليات بشكل ثابت لكل عملية. يمكن لكل عملية الوصول إلى موضعها ضمن شبكة العمليات من خلال العمليتَين replica_id وpartition_id.
ضمن شبكة العمليات، يمكن أن تكون جميع البرامج متشابهة (في نمط "برنامج واحد، بيانات متعددة")، أو مختلفة (في نمط "برامج متعددة، بيانات متعددة")، أو مزيجًا من الاثنين. في المستقبل، نخطّط لإتاحة استخدام طرق أخرى لتحديد برامج StableHLO المتوازية، بما في ذلك GSPMD (#619).
في شبكة العمليات، تكون العمليات مستقلة عن بعضها البعض في الغالب، حيث يكون لكل عملية حالات تشغيل وقيم إدخال/وسيطة/إخراج منفصلة، ويتم تنفيذ معظم العمليات بشكل منفصل بين العمليات، باستثناء عدد صغير من العمليات الجماعية الموضّحة أدناه.
بما أنّ تنفيذ معظم العمليات يستخدم قيمًا من العملية نفسها فقط، يكون من الواضح عادةً الإشارة إلى هذه القيم بأسمائها.
ومع ذلك، عند وصف دلالات العمليات الجماعية، يكون ذلك غير كافٍ، ويؤدي إلى ظهور الرمز name@process_id للإشارة إلى القيمة name ضمن عملية معيّنة. (من هذا المنظور، يمكن اعتبار name غير المؤهَّلة اختصارًا لـ name@(replica_id(), partition_id())).
ويتم تحديد ترتيب التنفيذ بين العمليات حسب التنفيذ، باستثناء المزامنة التي يتم إجراؤها من خلال الاتصال من نقطة إلى نقطة والعمليات الجماعية كما هو موضح أدناه.
الاتصال من نقطة إلى نقطة
يمكن لعمليات StableHLO التواصل مع بعضها البعض من خلال قنوات StableHLO. يتم تمثيل القناة بمعرّف موجب من النوع
si64. من خلال عمليات مختلفة، يمكن إرسال قيم إلى القنوات وتلقّيها منها.
سيتم تحديد المزيد من التفاصيل الرسمية، مثل مصدر أرقام تعريف القنوات هذه وكيفية معرفة البرامج التي تعالجها بها ونوع المزامنة التي توفّرها، في وقت لاحق (#484).
التواصل من خلال البث المباشر
يمكن لكل عملية StableHLO الوصول إلى واجهتَي بث:
- خلاصة يمكن قراءتها
- خارج التغذية التي يمكن الكتابة إليها
على عكس القنوات التي تُستخدم للتواصل بين العمليات وبالتالي تتضمّن عمليات في كلا طرفيها، فإنّ تنفيذ الطرف الآخر من الخلاصات الواردة والخارجة يكون محدّدًا حسب التنفيذ.
سيتم تحديد المزيد من التفاصيل الرسمية، مثل كيفية تأثير التواصل المباشر في ترتيب التنفيذ ونوع المزامنة التي يتم تقديمها من خلاله (#484).
العمليات الجماعية
هناك ست عمليات جماعية في StableHLO: all_gather وall_reduce وall_to_all وcollective_broadcast وcollective_permute وreduce_scatter. تعمل كل هذه العمليات على تقسيم العمليات في شبكة عمليات StableHLO إلى مجموعات عمليات StableHLO وتنفيذ عملية حسابية مشتركة داخل كل مجموعة عمليات بشكل مستقل عن مجموعات العمليات الأخرى.
ضمن كل مجموعة عمليات، قد تقدّم العمليات الجماعية حاجزًا للمزامنة. سيتم تحديد المزيد من التفاصيل الرسمية، مثل توضيح وقت حدوث هذه المزامنة بالضبط وكيفية وصول العمليات إلى هذا الحاجز وما يحدث إذا لم تصل إليه (#484).
إذا كانت مجموعة العمليات تتضمّن اتصالاً بين الأقسام، أي أنّ هناك عمليات في مجموعة العمليات تختلف أرقام تعريف الأقسام فيها، فإنّ تنفيذ العملية الجماعية يتطلّب قناة، ويجب أن توفّر العملية الجماعية قيمة موجبة channel_id من النوع si64. لا تحتاج عمليات التواصل بين النسخ المتماثلة إلى قنوات.
تختلف العمليات الحسابية التي تجريها العمليات الجماعية باختلاف العمليات الفردية، ويتم توضيحها في أقسام العمليات الفردية أعلاه. ومع ذلك، فإنّ الاستراتيجيات التي يتم بموجبها تقسيم شبكة العمليات إلى مجموعات عمليات تكون مشتركة بين هذه العمليات، ويتم وصفها في هذا القسم. بشكل أكثر رسمية، تتوافق 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، وهي قائمة بقوائم معرّفات الأقسام، وتحسب حاصل الضرب الديكارتي لـ partition_groups في replica_ids.
يجب أن يحتوي 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، وهي قائمة بقوائم
معرّفات النسخ المتماثلة، وتحسب نواتج ديكارتية لكل replica_group حسب
partition_ids. يجب أن يحتوي 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 لها سلوك محدّد جيدًا. تؤدي العمليات التي ينتج عنها استثناءات محدّدة بموجب معيار IEEE-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 لبنية EBNF (ISO/IEC 14977:1996،
Wikipedia)،
مع تعديلَين: 1) يتم تحديد القواعد باستخدام ::= بدلاً من =،
2) يتم التعبير عن التسلسل باستخدام التجاوز بدلاً من ,.
لوصف الدلالات (أي ضمن أقسام "الأنواع" و"الثوابت" و "العمليات")، نستخدم صيغًا تستند إلى بنية Python مع إمكانية التعبير بإيجاز عن عمليات الصفائف كما هو موضّح أدناه. هذه الطريقة فعّالة مع مقتطفات الرموز البرمجية الصغيرة، ولكن في حالات نادرة عندما تكون هناك حاجة إلى مقتطفات أكبر من الرموز البرمجية، نستخدم بنية Python العادية التي يتم تقديمها دائمًا بشكل صريح.
الصيغ
لنستكشف طريقة عمل الصيغ استنادًا إلى مثال من dot_generalالمواصفات. يبدو أحد قيود هذه العملية على النحو التالي:
dim(lhs, lhs_batching_dimensions...) = dim(rhs, rhs_batching_dimensions...).
تأتي الأسماء المستخدَمة في هذه الصيغة من مصدرَين: 1) الدوال العامة، أي dim، 2) تعريفات العناصر التابعة لعنصر البرنامج المعني، أي مدخلات lhs وlhs_batching_dimensions وrhs وrhs_batching_dimensions المحدّدة في قسم "المدخلات" من dot_general.
كما ذكرنا أعلاه، يستند بناء جملة هذه الصيغة إلى Python مع بعض الإضافات الموجزة. لفهم الصيغة، لنحوّلها إلى بنية Python عادية.
أ) في هذه الصيغ، نستخدم = لتمثيل المساواة، لذا فإن الخطوة الأولى
نحو الحصول على بنية Python هي استبدال = بـ ==، كما يلي:
dim(lhs, lhs_batching_dimensions...) == dim(rhs, rhs_batching_dimensions...).
ب) بالإضافة إلى ذلك، تدعم هذه الصيغ علامات الحذف (...) التي تحوّل التعبيرات العددية إلى تعبيرات موترية. باختصار، يعني f(xs...) تقريبًا "لكل قيمة عددية x في الموتر xs، احسب قيمة عددية f(x) ثم أرجِع كل هذه النتائج العددية معًا كموتر". في بنية Python العادية،
تتحوّل صيغة المثال إلى:
[dim(lhs, dim1) for dim1 in lhs_batching_dimensions] ==
[dim(rhs, dim2) for dim2 in rhs_batching_dimensions].
وبفضل علامات الحذف، يمكن غالبًا تجنُّب العمل على مستوى القيم العددية الفردية. ومع ذلك، في بعض الحالات المعقّدة، يمكن استخدام صيغة شبه رسمية من المستوى الأدنى، كما هو الحال في صيغة start_indices[bi0, ..., :, ..., biN] من مواصفات gather. بهدف الاختصار، لن نقدّم صيغة رسمية دقيقة لترجمة هذه البنية إلى لغة Python العادية، ونأمل أن تظل مفهومة بشكل بديهي لكل حالة على حدة.
يُرجى إعلامنا إذا بدت بعض الصيغ غير واضحة، وسنحاول تحسينها.
ستلاحظ أيضًا أنّ الصيغ تستخدم علامات الحذف لتوسيع جميع أنواع القوائم، بما في ذلك الموترات وقوائم الموترات (التي يمكن أن تنشأ مثلاً من عدد متغير من الموترات)، وما إلى ذلك. هذه منطقة أخرى لا نقدّم فيها شكلاً رسميًا دقيقًا (على سبيل المثال، لا تشكّل القوائم حتى جزءًا من نظام أنواع StableHLO)، بل نعتمد بدلاً من ذلك على إمكانية الفهم البديهي.
ج) آخر أداة تدوين جديرة بالذكر نستخدمها هي البث الضمني. على الرغم من أنّ مجموعة عمليات 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) التعريفات المحلية في ما يلي قائمة بالدوال العامة. تعتمد قائمة تعريفات العناصر على عنصر البرنامج الذي يتم تطبيق الترميز عليه:
- بالنسبة إلى العمليات، تتضمّن تعريفات الأعضاء الأسماء الواردة في قسمَي "المدخلات" و"المخرجات".
- بالنسبة إلى كل ما عدا ذلك، تتضمّن تعريفات الأعضاء الأجزاء البنيوية لعنصر البرنامج، والتي تحمل أسماء العناصر غير النهائية المقابلة في EBNF. في معظم الحالات، يتم الحصول على أسماء هذه الأجزاء البنيوية من خلال تحويل أسماء الرموز غير النهائية إلى تنسيق snake case (على سبيل المثال،
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 فقط في القيود.
الدوال
إنشاء أنواع
لا تتوفّر أي دوال يمكن استخدامها لإنشاء أنواع. بدلاً من ذلك، نستخدم بنية الأنواع مباشرةً لأنّها عادةً ما تكون أكثر اختصارًا. على سبيل المثال، استخدِم (tensor<E>, tensor<E>) -> (tensor<E>) بدلاً من function_type(
[tensor_type([], E), tensor_type([], E)], [tensor_type([], E)]).
الدوال على الأنواع
- يتم تحديد
element_typeلأنواع الموتر وأنواع الموتر الكمي، ويعرض على التوالي الجزءTensorElementTypeأوQuantizedTensorElementTypeمنTensorTypeأوQuantizedTensorTypeالمقابل.
def element_type(x: Value | Placeholder | Type):
if type(x) == TensorType:
return tensor_element_type(x)
if type(x) == QuantizedTensorType:
return quantized_tensor_element_type(x)
if type(x) is not Type:
return element_type(type(x))
is_per_axis_quantized(x: Value | Placeholder | Type) -> Valueهو اختصار لـ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وyQuantizedTensorElementType، لا يتم تطبيق العرض الترويجي إلا علىstorage_type. يُستخدَم هذا الإصدار المحدّد من الترويج حاليًا في سياق احتساب التخفيض (يُرجى الرجوع إلى RFC لمزيد من التفاصيل).
def is_promotable(x: Type, y: Type) -> Value:
is_same_type = (is_bool(x) and is_bool(y)) or
(is_integer(x) and is_integer(y)) or (is_float(x) and is_float(y)) or
(is_complex(x) and is_complex(y)) or
(is_quantized(x) and is_quantized(y) and expressed_type(x) = expressed_type(y))
if is_same_type == False:
return False
if is_integer(x) or is_float(x):
return bitwidth(x) <= bitwidth(y)
if is_complex(x):
return bitwidth(element_type(x)) <= bitwidth(element_type(y))
if is_quantized(x):
return bitwidth(storage_type(x)) <= bitwidth(storage_type(y))
return false
is_quantized(x: Value | Placeholder | Type) -> Valueهو اختصارis_quantized_tensor_element_type(x).
is_type_name(x: Value | Placeholder | Type) -> Value: متاحة لجميع الأنواع. على سبيل المثال، تعرض الدالةis_float(x)القيمةtrueإذا كانتxعبارة عنFloatType. إذا كانتxقيمة أو عنصر نائب، تكون هذه الدالة اختصارًا للدالةis_type_name(type(x)).تعرض الدالة
max_value(x: Type) -> Valueالحد الأقصى لقيمةTensorElementType. إذا لم يكنxTensorElementType، تعرض الدالةNone.تعرض الدالة
min_value(x: Type) -> Valueالحد الأدنى للقيمة الممكنة لـTensorElementType. إذا لم يكنxTensorElementType، تعرض الدالةNone.member_name(x: Value | Placeholder | Type) -> Any. وهي متاحة لجميع تعريفات الأعضاءmember_nameمن جميع الأنواع. على سبيل المثال، يعرضtensor_element_type(x)الجزءTensorElementTypeمنTensorTypeمطابق. إذا كانت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على الموترات ويعرضtrueإذا كانت جميع عناصرxهيNaNأوfalseفي الحالات الأخرى. إذا لم يكنxموترًا، تعرض الدالةNone.يتم تحديد
is_sorted(x: Value) -> Valueعلى الموترات ويعرضtrueإذا تم ترتيب عناصرxبترتيب تصاعدي بالنسبة إلى الترتيب المعجمي التصاعدي لفهارسها أوfalseبخلاف ذلك. إذا لم يكنxموترًا، تعرض الدالةNone.يتم تحديد
is_unique(x: Value) -> Valueعلى الموترات وتعرضtrueإذا لم تتضمّنxعناصر مكرّرة، أوfalseفي الحالات الأخرى. إذا لم يكنxموترًا، تعرض الدالةNone.يتم تحديد
member_name(x: Value) -> Anyلجميع تعريفات الأعضاءmember_nameلجميع القيم. على سبيل المثال، يعرض الرمزreal_part(x)الجزءRealPartمن الرمزComplexConstantالمقابل. إذا لم تكنxقيمة تتضمّن عنصرًا مناسبًا، تعرض الدالةNone.يتم تحديد
same(x: Value) -> Valueعلى الموترات، ويتم عرضtrueإذا كانت جميع عناصرxمتساوية أوfalseفي الحالات الأخرى. إذا لم يكن للموتر عناصر، سيتم احتساب ذلك على أنّه "كل العناصر متساوية"، أي أنّ الدالة ستعرضtrue. إذا لم يكنxموترًا، تعرض الدالةNone.يتم تحديد
split(x: Value, num_results: Value, axis: Value) -> Valueعلى الموترات، وتعرضnum_resultsشرائح منxعلى طول المحورaxis. إذا لم يكنxموترًا أوdim(x, axis) % num_results != 0، تعرض الدالةNone.يتم تحديد
is_defined_in_parent_scope(x: Value) -> Valueعلى السلاسل ويعرضtrueإذا كانxهو اسم دالة محددة في النطاق نفسه مثل الدالة الرئيسية للعملية ذات الصلة.يتم تحديد
is_namespaced_op_name(x: Value) -> Valueعلى السلاسل ويعرضtrueإذا كانxاسم عملية صالحًا، أي إذا كان يلتزم بالتعبير العادي التالي:[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على الموترات، ويعرض فهارس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) بأنواع الكميات. تعمل هذه العملية على إلغاء تكميم المدخلات المكمَّمة وتحويلها إلى أنواعها المعبر عنها، ثم تنفّذ العمليات الحسابية باستخدام أرقام الفاصلة العائمة. يجب أن يكون نوع عنصر موتر lhs العائم والنوع المعبر عنه لموتر 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. راجِع القسم "flattened_ids" أعلاه.
الديناميكية
يمكن أن تحتوي قيم StableHLO على أحجام سمات ديناميكية، مثل tensor<?xi64>.
ومع ذلك، لا يمكن أن تتضمّن قيم StableHLO عددًا ديناميكيًا من السمات (ديناميكية غير مصنّفة، مثل tensor<*xi64>). ويُسمح للمعاملات والنتائج باستخدام أحجام السمات الديناميكية، حتى إذا كانت هناك قيود على الأحجام. سيتم التحقّق من القيود بشكل ثابت إذا أمكن ذلك، وإلا سيتم تأجيلها إلى وقت التشغيل، وستؤدي حالات عدم التطابق إلى سلوك غير محدّد. اطّلِع على الأمثلة أدناه.
عدم تطابق الأشكال في العمليات الأحادية على مستوى العناصر
إليك البرنامج التجريبي التالي:
func.func @foo(%arg0: tensor<?xf64>) {
%0 = stablehlo.abs %arg0 : (tensor<?xf64>) -> tensor<2xf64>
return
}
هذا النوع من البرامج غير معتاد، لأنّه ليس من الشائع معرفة شكل النتيجة بدون معرفة شكل الإدخال. ومع ذلك، هذا برنامج 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>. إذا كان عامل تشغيل الشكل ثابتًا، يمكن التحقّق من ذلك بشكل ثابت. إذا كان شكل النتيجة ديناميكيًا بالكامل، لا يمكن أن يحدث عدم تطابق.