StableHLO هي مجموعة عمليات للعمليات العالية المستوى (HLO) في نماذج تعلُّم الآلة (ML). تعمل StableHLO كطبقة لنقل البرامج بين مختلف إطارات عمل تعلُّم الآلة وبرامج التحويل الخاصة به: إنّ إطارات عمل تعلُّم الآلة التي تُنشئ برامج StableHLO تكون متوافقة مع برامج التحويل الخاصة بتعلُّم الآلة التي تستخدِم برامج StableHLO.
هدفنا هو تبسيط تطوير تعلُّم الآلة وتسريعه من خلال توفير المزيد من إمكانية التشغيل التفاعلي بين أُطر عمل تعلُّم الآلة المختلفة (مثل TensorFlow وJAX و PyTorch) وبرامج تجميع تعلُّم الآلة (مثل XLA وIREE). لتحقيق هذا الهدف، يقدّم هذا المستند مواصفات لغة البرمجة StableHLO.
تحتوي هذه المواصفة على ثلاثة أقسام رئيسية. أولاً، يصف قسم البرامج بنية برامج StableHLO التي تتألف من دوالّ StableHLO التي تتألف بدورها من عمليات StableHLO. ضمن هذه البنية، يحدد قسم العمليات دلالات العمليات الفردية. يقدّم قسم التنفيذ دلالات لجميع عمليات التنفيذ هذه التي يتم تنفيذها معًا ضمن برنامج. وأخيرًا، يناقش قسم التدوين العلامة المستخدمة في المواصفات.
للاطّلاع على المواصفات من إصدار سابق من StableHLO، افتح المستودع في الإصدار الذي يحمل علامة من الاهتمام. على سبيل المثال، مواصفات StableHLO v0.19.0. لعرض التغييرات التي حدثت في كل إصدار ثانوي من 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'
تتشابه معرّفات SttableHLO مع المعرّفات في العديد من لغات البرمجة، مع خاصيتَين هما: 1) كل المعرِّفات تتضمن sigil تميّز الأنواع المختلفة من المعرّفات، 2) ويمكن أن تكون معرّفات القيمة رقمية بالكامل لتبسيط عملية إنشاء برامج StableHLO.
الأنواع
Type ::= ValueType | NonValueType
ValueType ::= TensorType | QuantizedTensorType | TokenType | TupleType
NonValueType ::= TensorElementType | QuantizedTensorElementType | FunctionType | StringType
يتم تصنيف أنواع StableHLO إلى أنواع القيم (التي تُعرف أيضًا باسم أنواع الفئة الأولى) التي تمثّل قيم StableHLO وأنواع القيم غير المحدّدة التي تصف عناصر البرنامج الأخرى. وتشبه أنواع StableHLO الأنواع في العديد من لغات البرمجة، حيث تتمثّل الخصوصية الرئيسية في طبيعة StableHLO الخاصة بالنطاق والتي ينتج عنها بعض النتائج غير المعتادة (على سبيل المثال، لا تكون الأنواع العددية أنواعًا من القيم).
TensorType ::= 'tensor' '<' Shape TensorElementType '>'
Shape ::= {DimensionSize 'x'}
DimensionSize ::= digit {digit} | '?'
تمثّل أنواع مصفوفات السلاسل المتسلسلة مصفوفات السلاسل المتسلسلة، أي الصفائف متعددة الأبعاد. وتتضمّن
شكلًا ونوع عنصر، حيث يمثّل الشكل أحجام السمات غير السالبة أو
غير المعروفة بترتيب تصاعدي للسمات (المعروفة أيضًا باسم المحاور) التي تتراوح أرقامها من 0
إلى R-1
. يُطلق على
عدد السمات R
اسم الرتبة. على سبيل المثال، tensor<2x3xf32>
هو
نوع مصفوفة سِنتر مع شكل 2x3
ونوع العنصر f32
. ويحتوي على بُعدين
(أو بعبارةٍ أخرى محوران) - البعد 0 والبعد الأول،
وحجمهما 2 و3. ترتيبها هو 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)
التلقائيتَين على التوالي. تخضع أنواع العناصر الكمية
للقيود التالية:
- (ج1)
type(storage_min) = storage_type
. - (ج2)
type(storage_max) = storage_type
. - (ج3)
min_value(storage_type) <= storage_min < storage_max <= max_value(storage_type)
. - (C4)
type(scales...) = expressed_type
. - (ج5)
0 < scales
. - (C6)
is_finite(scales...)
. - (ج7)
storage_min <= zero_points <= storage_max
. - (C8)
type(zero_points...) = storage_type
. - (ج9)
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}]
تمثّل أنواع المجموعات المجموعات، أي القوائم غير المتجانسة. مجموعات السلاسل هي ميزة
قديمة لا تتوفر إلا للتوافق مع 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: سر الأداء العالي على وحدات Cloud TPUs - أنواع
f16
وf32
وf64
التي تتوافق مع التنسيقاتbinary16
("نصف الدقة") وbinary32
("دقة واحدة") وbinary64
("دقة مزدوجة") الموضّحة في معيار IEEE 754 - يتوافق نوع
tf32
مع تنسيق TensorFloat32 ويكون متوافقًا بشكل محدود مع StableHLO. - أنواع MX (التصغير)
f4E2M1FN
وf6E2M3FN
وf6E3M2FN
وf8E8M0FNU
описанные в описанные в OCP Microscaling Formats Specification
-
- تمثّل الأنواع المعقدة القيم المعقدة التي تحتوي على جزء حقيقي
وجزء تخيلي من نوع العنصر نفسه. الأنواع المركّبة المسموح بها هي
complex<f32>
(كلا الجزأين من النوعf32
) وcomplex<f64>
(كلا الجزأين من النوعf64
).
FunctionType ::= '(' InputTypes ')' '->' '(' OutputTypes ')'
InputTypes ::= [ValueType {',' ValueType}]
OutputTypes ::= [ValueType {',' ValueType}]
تمثّل أنواع الدوال كلًّا من الدوال المُسمّاة والدوالّ المجهولة. وتتضمّن أنواع الإدخال (قائمة الأنواع على الجانب الأيسر من ->
) وأنواع النتائج
(قائمة الأنواع على الجانب الأيمن من ->
). وفي العديد من لغات البرمجة، تكون أنواع الدوال من الفئة الأولى، ولكنها ليست في StableHLO.
StringType ::= 'string'
يمثّل نوع السلسلة تسلسلات البايتات. على عكس العديد من لغات البرمجة، لا يُعدّ نوع السلسلة من الفئة الأولى في StableHLO، ولا يُستخدم إلا لتحديد بيانات وصفية ثابتة لعناصر البرنامج.
العمليات
تمثّل عمليات StableHLO (المعروفة أيضًا باسم ops) مجموعة مغلقة من العمليات العالية المستوى في نماذج تعلُّم الآلة. كما ناقشنا أعلاه، يستوحي أسلوب StableHLO من MLIR بشكل كبير، وهو ليس بالضرورة البديل الأكثر ملاءمةً للاستخدام، ولكن يمكن القول إنّه الأنسب لتحقيق هدف StableHLO المتمثل في زيادة إمكانية التشغيل التفاعلي بين إطارات عمل تعلُّم الآلة وبرامج التحويل الخاصة به.
Op ::= [OpOutputs] OpName OpInputs ':' OpSignature
OpName ::= '"' 'stablehlo' '.' OpMnemonic '"'
OpMnemonic ::= 'abs' | 'add' | ...
تتضمّن عمليات StableHLO (المعروفة أيضًا باسم ops) اسمًا،
ومدخلات/مخرجات وتوقيعًا. يتألّف الاسم من البادئة stablehlo.
و
عبارة مساعدة تُحدِّد بشكل فريد إحدى العمليات المتوافقة. يمكنك الاطّلاع أدناه على
قائمة شاملة بكلّ العمليات المتوافقة.
OpInputs ::= OpInputValues OpInputFuncs OpInputAttrs
OpInputValues ::= '(' [OpInputValue {',' OpInputValue}] ')'
OpInputValue ::= ValueId
OpInputFuncs ::= ['(' OpInputFunc {',' OpInputFunc} ')']
OpInputAttrs ::= ['{' OpInputAttr {',' OpInputAttr} '}']
OpOutputs ::= [OpOutput {',' OpOutput} '=']
OpOutput ::= ValueId
تستهلك العمليات الإدخالات وتُنتج المخرجات. يتم تصنيف المدخلات إلى
قيم المدخلات (المحسوبة أثناء التنفيذ) ودوال المدخلات (المقدَّمة
بشكل ثابت، لأنّ الدوال في StableHLO ليست قيمًا من الدرجة الأولى)
وسمات المدخلات (المقدَّمة أيضًا بشكل ثابت). يعتمد نوع المدخلات والمخرجات التي يتم استهلاكها
وإنتاجها على العملية على الذاكرة. على سبيل المثال، تستهلك العملية add
قيمتَي إدخال وينتج عنها قيمة واحدة للمخرجات. في المقابل، تستهلك عملية
select_and_scatter
3 قيم إدخال ودالتَي إدخال و
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}] ')'
يتكون توقيع Op من أنواع جميع قيم الإدخال (قائمة الأنواع التي تظهر على الجانب الأيسر من ->
) وأنواع كل قيم الإخراج (قائمة الأنواع على الجانب الأيمن من ->
). بالإضافة إلى ذلك، تكون أنواع الإدخالات متكررة، كما أن أنواع الإخراج تكون متكررة دائمًا تقريبًا أيضًا (لأنّه بالنسبة إلى معظم عمليات HLO الثابتة، يمكن استنتاج أنواع الإخراج من الإدخالات). ومع ذلك، يعد توقيع العملية جزءًا
عن قصد من بناء جملة 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'
تمثِّل الثوابت الصحيحة القيم الصحيحة من خلال سلاسل تستخدم الترميز العشري أو السداسي العشري. ولا يمكن استخدام القواعد الأخرى، مثل النظام الثنائي أو الثماني. تنطبق القيود التالية على الثوابت الصحيحة:
- (ج1)
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]
تمثل الثوابت العائمة قيم النقطة العائمة من خلال السلاسل التي تستخدم الترميز العشري أو العلمي. بالإضافة إلى ذلك، يمكن استخدام الترميز السداسي عشري لتحديد وحدات البت الأساسية مباشرةً في تنسيق النقطة العائمة للنوع المقابل. تنطبق القيود التالية على الثوابت الكسورية:
- (ج1) في حال استخدام ترميز غير سداسي عشري،
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
. إنّ ترتيب تخزين هذه
الأجزاء في الذاكرة يعتمد على طريقة التنفيذ. تنطبق القيود التالية على الثوابت المعقدة:
- (ج1)
is_wellformed(real_part, complex_element_type(complex_type))
. - (ج2)
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) '>'
تمثّل ثوابت مصفوفات الكمّية المُعدَّلة قيم مصفوفات الكمّية المُعدَّلة باستخدام الترميز نفسه المُستخدَم في الثوابت المصفوفات، مع تحديد العناصر ثوابتًا لنوع تخزينها. تنطبق القيود التالية على الثوابت المتسلسلة المقسّمة:
- (ج1)
has_syntax(quantized_tensor_literal, storage_type(quantized_tensor_type))
. - (ج2)
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) |
القيود
- (ج1)
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
. بناءً على نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى القيم المنطقية: "أو" منطقي
- بالنسبة للأعداد الصحيحة: إضافة عدد صحيح.
- بالنسبة إلى الأعداد العشرية العائمة:
addition
من IEEE-754 - بالنسبة إلى الأعداد المركبة: جمع الأعداد المركبة
- بالنسبة إلى الأنواع الكَمية:
dequantize_op_quantize(add, lhs, rhs, type(result))
.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | lhs |
متّجه أو متّجه مُكمّر | (C1-C6) |
(I2) | rhs |
متّجه أو متّجه مُكمّر | (C1-C5)، (C7) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
متّجه أو متّجه مُكمّر | (C1-C7) |
القيود
- إذا كانت العملية تستخدم مصفوفات غير مُكثَّفة:
- (ج1)
type(lhs) = type(rhs) = type(result)
.
- (ج1)
- إذا كانت العملية تستخدم مصفوفات كمية:
- (ج2)
is_quantized(lhs) and is_quantized(rhs) and is_quantized(result)
. - (ج3)
storage_type(lhs) = storage_type(rhs) = storage_type(result)
. - (C4)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
. - (ج5)
(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)
.
- (ج2)
أمثلة
// %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)
إذاchannel_id <= 0 and use_global_device_ids = false
.cross_replica_and_partition(replica_groups)
إذاchannel_id > 0 and use_global_device_ids = false
.flattened_ids(replica_groups)
إذاchannel_id > 0 and use_global_device_ids = true
.
بعد ذلك، ضمن كل process_group
:
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) |
القيود
- (ج1)
0 <= all_gather_dim < rank(operands...)
. - (ج2)
is_unique(replica_groups)
. - (C3) يتم تعريف
size(replica_groups)
على النحو التالي:num_replicas
في حال استخدامcross_replica
.num_replicas
في حال استخدامcross_replica_and_partition
num_processes
في حال استخدامflattened_ids
- (C4)
0 <= replica_groups < size(replica_groups)
. - (ج5) إذا كان
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)
إذاchannel_id <= 0 and use_global_device_ids = false
.cross_replica_and_partition(replica_groups)
إذاchannel_id > 0 and use_global_device_ids = false
.flattened_ids(replica_groups)
إذاchannel_id > 0 and use_global_device_ids = true
.
بعد ذلك، ضمن كل process_group
:
results...@process[result_index] = exec(schedule)
لبعض الشجرة الثنائيةschedule
حيث:exec(node)
=computation(exec(node.left), exec(node.right))
.exec(leaf)
=leaf.value
.
schedule
عبارة عن شجرة ثنائية معرفة تنفيذية يكون اجتيازها بالترتيب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) |
القيود
- (ج1)
is_unique(replica_groups)
. - (C2) يتم تعريف
size(replica_groups)
على النحو التالي:num_replicas
في حال استخدامcross_replica
num_replicas
في حال استخدامcross_replica_and_partition
num_processes
في حال استخدامflattened_ids
- (ج3)
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 <= 0
cross_partition(replica_groups)
إذاchannel_id > 0
.
بعد ذلك، ضمن كل process_group
:
split_parts...@sender = split(operands...@sender, split_count, split_dimension)
لجميعsender
فيprocess_group
scattered_parts...@receiver = [split_parts...@sender[receiver_index] for sender in process_group]
حيثreceiver_index = process_group.index(receiver)
.results...@process = concatenate(scattered_parts...@process, concat_dimension)
.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | operands |
عدد متغيّر من مصفوفات السلاسل أو مصفوفات سلاسل مُشفَّرة لكلّ مصفوفة سلسلة | (C1-C3)، (C9) |
(I2) | split_dimension |
ثابت من النوع si64 |
(C1)، (C2)، (C9) |
(I3) | concat_dimension |
ثابت من النوع si64 |
(C3)، (C9) |
(I4) | split_count |
ثابت من النوع si64 |
(C2)، (C4)، (C8)، (C9) |
(I5) | replica_groups |
ثابت التوتر الثنائي الأبعاد من النوع si64 |
(C5-C8) |
(I6) | channel_id |
ثابت من النوع si64 |
النواتج
الاسم | النوع | القيود |
---|---|---|
results |
عدد متغيّر من مصفوفات السلاسل أو مصفوفات سلاسل مُشفَّرة لكلّ مصفوفة سلسلة | (C9) |
القيود
- (ج1)
0 <= split_dimension < rank(operands...)
. - (C2)
dim(operands..., split_dimension) % split_count = 0
. - (ج3)
0 <= concat_dimension < rank(operands...)
. - (C4)
0 < split_count
. - (ج5)
is_unique(replica_groups)
. - (C6) يتم تعريف
size(replica_groups)
على النحو التالي:num_replicas
في حال استخدامcross_replica
.num_partitions
في حال استخدامcross_partition
.
- (ج7)
0 <= replica_groups < size(replica_groups)
. - (C8)
dim(replica_groups, 1) = split_count
. - (C9)
type(results...) = type(operands...)
باستثناء، إذاsplit_dimension != concat_dimension
:dim(results..., split_dimension) = dim(operands..., split_dimension) / split_count
.dim(results..., concat_dimension) = dim(operands..., concat_dimension) * split_count
.
أمثلة
// num_replicas: 2
// num_partitions: 1
// %operand1@(0, 0): [[1, 2, 3, 4],
// [5, 6, 7, 8]]
// %operand1@(1, 0): [[9, 10, 11, 12],
// [13, 14, 15, 16]]
// %operand2@(0, 0): [[17, 18, 19, 20],
// [21, 22, 23, 24]]
// %operand2@(1, 0): [[25, 26, 27, 28],
// [29, 30, 31, 32]]
%result:2 = "stablehlo.all_to_all"(%operand1, %operand2) {
split_dimension = 1 : i64,
concat_dimension = 0 : i64,
split_count = 2 : i64,
replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>
// channel_id = 0
} : (tensor<2x4xi64>, tensor<2x4xi64>) -> (tensor<4x2xi64>, tensor<4x2xi64>)
// %result#0@(0, 0): [[1, 2], [5, 6], [9, 10], [13, 14]]
// %result#0@(1, 0): [[3, 4], [7, 8], [11, 12], [15, 16]]
// %result#1@(0, 0): [[17, 18], [21, 22], [25, 26], [29, 30]]
// %result#1@(1, 0): [[19, 20], [23, 24], [27, 28], [31, 32]]
و
الدلالات
تُنفِّذ عملية "و" بين مصفوفتَي lhs
وrhs
على مستوى العنصر الواحد، وتُنشئ مصفوفة result
. بناءً على نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى القيم المنطقية: "الواو المنطقي"
- بالنسبة إلى الأعداد الصحيحة: استخدام AND على مستوى البت
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | lhs |
مصفوفة كثيفة من النوع المنطقي أو الصحيح | (C1) |
(I2) | rhs |
مصفوفة كثيفة من النوع المنطقي أو الصحيح | (C1) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
مصفوفة كثيفة من النوع المنطقي أو الصحيح | (C1) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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
backpropagating
من 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) |
القيود
- (ج1)
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
لها الشكل نفسه. - (ج5)
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) |
القيود
- (ج1)
0 <= feature_index < rank(operand)
. - (ج2) تحتوي
operand
وscale
وoffset
وmean
وvariance
وresult
علىbaseline_element_type
نفسه. - (ج3)
size(scale) = dim(operand, feature_index)
. - (C4)
size(offset) = dim(operand, feature_index)
. - (ج5)
size(mean) = dim(operand, feature_index)
. - (C6)
size(variance) = dim(operand, feature_index)
. - (ج7)
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) |
القيود
- (ج1)
0 <= feature_index < rank(operand)
. - (C2)
operand
وscale
وoffset
وbatch_mean
وbatch_var
وoutput
لديهمbaseline_element_type
نفسها. - (ج3)
size(scale) = dim(operand, feature_index)
. - (C4)
size(offset) = dim(operand, feature_index)
. - (ج5)
size(batch_mean) = dim(operand, feature_index)
. - (C6)
size(batch_var) = dim(operand, feature_index)
. - (ج7)
baseline_type(output) = baseline_type(operand)
.
أمثلة
// %operand: [
// [[1.0, 2.0], [3.0, 4.0]],
// [[3.0, 4.0], [1.0, 2.0]]
// ]
// %scale: [1.0, 1.0]
// %offset: [1.0, 1.0]
%output, %batch_mean, %batch_var = "stablehlo.batch_norm_training"(%operand, %scale, %offset) {
epsilon = 0.0 : f32,
feature_index = 2 : i64
} : (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>) ->
(tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>)
// %output: [
// [[0.0, 0.0], [2.0, 2.0]],
// [[2.0, 2.0], [0.0, 0.0]]
// ]
// %batch_mean: [2.0, 3.0]
// %batch_var: [1.0, 1.0]
bitcast_convert
دلالات الألفاظ
تُنفِّذ عملية تحويل بت على مصفوفة operand
وتُنشئ مصفوفة result
حيث تتم إعادة تفسير بتات مصفوفة operand
بالكامل باستخدام نوع مصفوفة result
.
وبشكل رسمي، استنادًا إلى E = element_type(operand)
وE' = element_type(result)
وR = rank(operand)
:
- إذا
num_bits(E') < num_bits(E)
،bits(result[i0, ..., iR-1, :]) = bits(operand[i0, ..., iR-1])
. - إذا
num_bits(E') > num_bits(E)
،bits(result[i0, ..., iR-2]) = bits(operand[i0, ..., iR-2, :])
. - إذا كان
num_bits(E') = num_bits(E)
،bits(result[i0, ..., iR-1]) = bits(operand[i0, ..., iR-1])
.
تُعرِض دالة bits
تمثيلًا في الذاكرة لقيمة معيّنة، ويكون سلوكها
محددًا من خلال التنفيذ لأنّ التمثيل الدقيق للمتسلسلات العقدية
يكون محددًا من خلال التنفيذ، والتمثيل الدقيق لأنواع العناصر
يكون محددًا من خلال التنفيذ أيضًا.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | operand |
متّجه أو متّجه مُكمّر | (C1-C2) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
متّجه أو متّجه مُكمّر | (C1-C2) |
القيود
- (ج1) بالنظر إلى
E = is_quantized(operand) ? storage_type(operand) : element_type(operand)
وE' = is_quantized(result) ? storage_type(result) : element_type(result)
وR = rank(operand)
:- إذا
num_bits(E') = num_bits(E)
،shape(result) = shape(operand)
. - إذا
num_bits(E') < num_bits(E)
: rank(result) = R + 1
.dim(result, i) = dim(operand, i)
لجميع0 <= i < R
dim(result, R) * num_bits(E') = num_bits(E)
.- إذا
num_bits(E') > num_bits(E)
: rank(result) = R - 1
.dim(result, i) = dim(operand, i)
لجميع0 <= i < R
dim(operand, R - 1) * num_bits(E) = num_bits(E')
.
- إذا
- (C2) إذا كان
is_complex(operand) or is_complex(result)
، ثمis_complex(operand) and is_complex(result)
.
أمثلة
// %operand: 0x0123456789ABCDEF
%result = "stablehlo.bitcast_convert"(%operand) : (tensor<f64>) -> tensor<4xf16>
// %result: [0xCDEF, 0x89AB, 0x4567, 0x0123] // little-endian representation
broadcast_in_dim
الدلالات
لتوسيع أبعاد و/أو ترتيب مُتّصِل الإدخال عن طريق تكرار البيانات في موجِّه operand
وينتج موجِّه result
. بشكل رسمي،
result[result_index] = operand[operand_index]
في ما يتعلق بجميع d
في
axes(operand)
:
operand_index[d] = 0
إذاdim(operand, d) = 1
operand_index[d] = result_index[broadcast_dimensions[d]]
بخلاف ذلك
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | operand |
متّجه أو متّجه مُكمّر | (C1-C2)، (C5-C6) |
(I2) | broadcast_dimensions |
ثابت مصفوفة لاتّجاه واحد من النوع si64 |
(C2-C6) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
متّجه أو متّجه مُكمّر | (C1)، و(C3)، و(C5-C6) |
القيود
- (C1) يتم الحصول على
element_type(result)
من خلال: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)
. بخلاف ذلك.
- (ج2)
size(broadcast_dimensions) = rank(operand)
. - (ج3)
0 <= broadcast_dimensions < rank(result)
. - (C4)
is_unique(broadcast_dimensions)
. - (ج5) بالنسبة إلى جميع
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) |
القيود
- (ج1)
0 < size(branches)
. - (ج2)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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
الدلالات
تحسب هذه الدالة عملية تقسيم Cholesky لمجموعة من المصفوفات.
وبشكل أكثر رسمًا، بالنسبة إلى جميع i
في index_space(result)
،
يشير result[i0, ..., iR-3, :, :]
إلى انحلال كلسكي بقيمة a[i0, ..., iR-3, :, :]
، في شكل مصفوفة مثلث صغيرة
(إذا كانت lower
هي true
) أو مصفوفة مثلثية أعلى (إذا كانت lower
هي false
).
إنّ قيم الإخراج في المثلث المقابل، أي المثلث العلوي الصارم أو
المثلث السفلي الصارم وفقًا لذلك، يتم تحديدها من خلال التنفيذ.
إذا كان هناك i
حيث تكون مصفوفة الإدخال ليست
مصفوفة هيرميتية موجبة القيمة، يكون السلوك غير محدّد.
بالنسبة إلى الأنواع المحدَّدة القيمة، يتم تنفيذ
dequantize_op_quantize(lambda operand: cholesky(operand, lower), a, type(result))
.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | a |
مأخذ النقطة العائمة أو النوع المركّب أو المتسّع الكمّي حسب كل متسابق | (C1-C3) |
(I2) | lower |
ثابت مصفوفة لاتّجاه 0 من النوع i1 |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
مأخذ النقطة العائمة أو النوع المركّب أو المتسّع الكمّي حسب كل متسابق | (C1) |
القيود
- (ج1)
baseline_type(a) = baseline_type(result)
. - (ج2)
2 <= rank(a)
. - (ج3)
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) |
القيود
- (ج1)
rank(min) = 0 or shape(min) = shape(operand)
. - (ج2)
rank(max) = 0 or shape(max) = shape(operand)
. - (ج3)
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 <= 0
cross_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) |
القيود
- (ج1)
is_unique(replica_groups)
. - (C2)
0 <= replica_groups < N
حيث يتم تعريفN
على النحو التالي:num_replicas
في حال استخدامcross_replica
num_partitions
في حال استخدامcross_partition
- (ج3)
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 <= 0
cross_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) |
القيود
- (ج1)
dim(source_target_pairs, 1) = 2
. - (ج2)
is_unique(source_target_pairs[:, 0])
. - (ج3)
is_unique(source_target_pairs[:, 1])
. - (C4)
0 <= source_target_pairs < N
، حيث يتم تعريفN
على النحو التالي:num_replicas
في حال استخدامcross_replica
.num_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
، يستخدم op
مزيجًا من عمليتَي 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 |
enum من EQ وNE وGE وGT وLE وLT |
|
(I4) | compare_type |
enum من FLOAT وTOTALORDER وSIGNED وUNSIGNED |
(C3) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
متّصِل من النوع المنطقي | (C2) |
القيود
- (ج1)
baseline_element_type(lhs) = baseline_element_type(rhs)
. - (ج2)
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) |
القيود
- (ج1)
type(lhs) = type(rhs)
. - (ج2)
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)
- (ج2)
is_defined_in_parent_scope(decomposition)
- (C3)
types(inputs...) == input_types(decomposition)
- (ج4)
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) |
القيود
- (ج1)
same(element_type(inputs...))
. - (C2)
same(shape(inputs...))
باستثناءdim(inputs..., dimension)
. - (ج3)
0 < size(inputs)
. - (C4)
0 <= dimension < rank(inputs[0])
. - (ج5)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
N = rank(lhs) = rank(rhs)
. - (C2)
size(window_strides) = N - 2
. - (ج3)
0 < window_strides
. - (C4)
shape(padding) = [N - 2, 2]
. - (ج5)
size(lhs_dilation) = N - 2
. - (C6)
0 < lhs_dilation
. - (ج7)
size(rhs_dilation) = N - 2
. - (ج8)
0 < rhs_dilation
. - (ج9)
size(window_reversal) = N - 2
. - (C10)
dim(lhs, input_batch_dimension) % batch_group_count = 0
. - (C11)
dim(lhs, input_feature_dimension) % feature_group_count = 0
. - (C12)
size(input_spatial_dimensions) = N - 2
. - (C13) بالنظر إلى
input_dimensions = [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]
:is_unique(input_dimensions)
.0 <= input_dimensions < N
.
- (C14)
dim(rhs, kernel_input_feature_dimension) = dim(lhs, input_feature_dimension) / feature_group_count
. - (C15)
dim(rhs, kernel_output_feature_dimension) % batch_group_count = 0
. - (C16)
dim(rhs, kernel_output_feature_dimension) % feature_group_count = 0
. - (C17)
size(kernel_spatial_dimensions) = N - 2
. - (C18) بالنظر إلى
kernel_dimensions = kernel_spatial_dimensions + [kernel_input_feature_dimension] + [kernel_output_feature_dimension]
:is_unique(kernel_dimensions)
.0 <= kernel_dimensions < N
.
- (C19)
size(output_spatial_dimensions) = N - 2
. - (C20) بالنظر إلى
output_dimensions = [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]
:is_unique(output_dimensions)
.0 <= output_dimensions < N
.
- (C21)
0 < feature_group_count
. - (C22)
0 < batch_group_count
. - (C23)
feature_group_count = 1 or batch_group_count = 1
. - (C24)
size(precision_config) = 2
. - (C25) يتم تعريف
dim(result, result_dim)
على النحو التالي:dim(lhs, input_batch_dimension) / batch_group_count
إذاresult_dim = output_batch_dimension
dim(rhs, kernel_output_feature_dimension)
إذاresult_dim = output_feature_dimension
num_windows
في الحالات الأخرى، حيث:output_spatial_dimensions[spatial_dim] = result_dim
.lhs_dim = input_spatial_dimensions[spatial_dim]
.rhs_dim = kernel_spatial_dimensions[spatial_dim]
.dilated_input_shape[lhs_dim] = dim(lhs, lhs_dim) = 0 ? 0 : (dim(lhs, lhs_dim) - 1) * lhs_dilation[spatial_dim] + 1
.padded_input_shape[lhs_dim] = padding[spatial_dim, 0] + dilated_input_shape[lhs_dim] + padding[spatial_dim, 1]
.dilated_window_shape[lhs_dim] = dim(rhs, rhs_dim) = 0 ? 0 : (dim(rhs, rhs_dim) - 1) * rhs_dilation[spatial_dim] + 1
.is_empty_window[lhs_dim] = padded_input_shape[lhs_dim] = 0 || dilated_window_shape[lhs_dim] > padded_input_shape[lhs_dim]
.num_windows = is_empty_window[lhs_dim] ? 0 : floor((padded_input_shape[lhs_dim] - dilated_window_shape[lhs_dim]) / window_strides[spatial_dim]) + 1
.
- (C26)
rank(result) = N
. - إذا كانت العملية تستخدم مصفوفات غير مُكثَّفة:
- (C27)
element_type(lhs) = element_type(rhs) = element_type(result)
.
- (C27)
- إذا كانت العملية تستخدم موتّرات كميّة:
- (C28)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
. - (C29) إذا كانت
is_per_axis_quantized(rhs)
، ثمquantization_dimension(rhs) = kernel_output_feature_dimension
. - (C30) إذا كانت السمة
is_per_axis_quantized(result)
، يتم عندهاquantization_dimension(result) = output_feature_dimension
. - إذا
is_quantized(lhs)
: - (C31)
storage_type(lhs) = storage_type(rhs)
. - (C32)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
. - (C33) إذا كان
is_per_tensor_quantized(rhs)
، ثمis_per_tensor_quantized(result)
. - إذا
!is_quantized(lhs)
: - (C34)
element_type(lhs) = expressed_type(rhs) = element_type(result)
.
- (C28)
أمثلة
// %lhs: [[
// [
// [1], [2], [5], [6]
// ],
// [
// [3], [4], [7], [8]
// ],
// [
// [10], [11], [14], [15]
// ],
// [
// [12], [13], [16], [17]
// ]
// ]]
//
// %rhs: [
// [[[1]], [[1]], [[1]]],
// [[[1]], [[1]], [[1]]],
// [[[1]], [[1]], [[1]]]
// ]
%result = "stablehlo.convolution"(%lhs, %rhs) {
window_strides = array<i64: 4, 4>,
padding = dense<0> : tensor<2x2xi64>,
lhs_dilation = array<i64: 2, 2>,
rhs_dilation = array<i64: 1, 1>,
window_reversal = array<i1: false, false>,
// In the StableHLO dialect, dimension numbers are encoded via:
// `[<input dimensions>]x[<kernel dimensions>]->[output dimensions]`.
// "b" is batch dimension, "f" is feature dimension,
// "i" is input feature dimension, "o" is output feature dimension,
// "0/1/etc" are spatial dimensions.
dimension_numbers = #stablehlo.conv<[b, 0, 1, f]x[0, 1, i, o]->[b, 0, 1, f]>,
batch_group_count = 1 : i64,
feature_group_count = 1 : i64,
precision_config = [#stablehlo<precision DEFAULT>, #stablehlo<precision DEFAULT>]
} : (tensor<1x4x4x1xi64>, tensor<3x3x1x1xi64>) -> tensor<1x2x2x1xi64>
// %result: [[
// [[10], [26]],
// [[46], [62]]
// ]]
جيب التمام
الدلالات
تؤدي هذه الدالة عملية جيب التمام بناءً على العناصر في موتر operand
وينتج عنها متسابق result
. حسب نوع العنصر، يتم إجراء ما يلي:
- بالنسبة إلى الأعداد العشرية العائمة:
cos
من IEEE-754 - للأعداد المركبة: جيب التمام المركب
- بالنسبة إلى الأنواع التي تم تحويلها إلى قيم عددية:
dequantize_op_quantize(cosine, operand, type(result))
.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | operand |
مصفوفة متّجه من النوع الكسري أو المركّب أو مصفوفة متّجه مُشفَّرة لكل متّجه | (C1) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
مأخذ النقطة العائمة أو النوع المركّب أو المتسّع الكمّي حسب كل متسابق | (C1) |
القيود
- (ج1)
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]]
عدد_الأصفار_الأولية
الدلالات
تُجري عملية احتساب عدد الوحدات الثنائية الصفرية الأولى في مصفوفة operand
وتنشئ مصفوفة result
.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | operand |
مصفوفة من النوع الصحيح | (C1) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
متوتر لنوع العدد الصحيح | (C1) |
القيود
- (ج1)
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
لتوفير بيانات وصفية إضافية تحدّدها عملية التنفيذ.
في الوقت الحالي، تحتوي هذه العملية على مجموعة غير منظمة إلى حدٍ كبير من البيانات الوصفية التي تعكس التطور العضوي لعملية المعالجة المقابلة لها في compiler (المجمّع) 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 |
النواتج
الاسم | النوع |
---|---|
results |
عدد متغيّر من القيم |
أمثلة
%results = "stablehlo.custom_call"(%input0) {
call_target_name = "foo",
has_side_effect = false,
backend_config = {bar = 42 : i32},
api_version = 4 : i32,
called_computations = [@foo]
} : (tensor<f64>) -> tensor<f64>
قسمة
الدلالات
تُجري عملية قسمة العناصر من حيث العناصر لمعادلات المقسم lhs
وقسمة القاسم rhs
وينتج مضارب result
. بناءً على نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى الأعداد الصحيحة: قسمة الأعداد الصحيحة التي تؤدي إلى الناتج الجبري مع تجاهل أي جزء كسري
- بالنسبة إلى الأعداد العشرية العائمة:
division
من IEEE-754 - بالنسبة إلى الأعداد المركّبة: القسمة المركّبة.
- بالنسبة إلى الأنواع التي تم تحويلها إلى قيم عددية:
dequantize_op_quantize(divide, lhs, rhs, type(result))
.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | lhs |
مصفوفة من النوع الصحيح أو الكسور العشرية أو المركّب أو مصفوفة مُعدَّلة لكل مصفوفة | (C1) |
(I2) | rhs |
مصفوفة من النوع الصحيح أو الكسور العشرية أو المركّب أو مصفوفة مُعدَّلة لكل مصفوفة | (C1) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
مصفوفة من النوع الصحيح أو الكسور العشرية أو المركّب أو مصفوفة مُشفَّرة لكل مصفوفة | (C1) |
القيود
- (ج1)
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) |
القيود
- (ج1)
size(lhs_batching_dimensions) = size(rhs_batching_dimensions)
. - (C2)
size(lhs_contracting_dimensions) = size(rhs_contracting_dimensions)
. - (ج3)
is_unique(lhs_batching_dimensions + lhs_contracting_dimensions)
. - (C4)
is_unique(rhs_batching_dimensions + rhs_contracting_dimensions)
. - (ج5)
0 <= lhs_batching_dimensions < rank(lhs)
. - (C6)
0 <= lhs_contracting_dimensions < rank(lhs)
. - (ج7)
0 <= rhs_batching_dimensions < rank(rhs)
. - (C8)
0 <= rhs_contracting_dimensions < rank(rhs)
. - (ج9)
dim(lhs, lhs_batching_dimensions...) = dim(rhs, rhs_batching_dimensions...)
. - (C10)
dim(lhs, lhs_contracting_dimensions...) = dim(rhs, rhs_contracting_dimensions...)
. - (C11)
size(precision_config) = 2
. - (C12)
shape(result) = dim(lhs, lhs_batching_dimensions) + dim(lhs, lhs_result_dimensions) + dim(rhs, rhs_result_dimensions)
. - إذا كانت العملية تستخدم مصفوفات غير مُكثَّفة:
- (C13)
element_type(lhs) = element_type(rhs)
.
- (C13)
- إذا كانت العملية تستخدم مصفوفات كمية:
- (C14)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
. - (C15)
zero_points(rhs) = 0
. - (C16) إذا كانت القيمة
is_per_axis_quantized(rhs)
، وليسquantization_dimension(rhs)
فيrhs_contracting_dimensions
. - إذا
is_quantized(lhs)
: - (C17)
storage_type(lhs) = storage_type(rhs)
. - (C18)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
. - (C19) إذا كان
is_per_tensor_quantized(rhs)
، ثمis_per_tensor_quantized(result)
. - إذا
!is_quantized(lhs)
: - (C20)
element_type(lhs) = expressed_type(rhs) = element_type(result)
.
- (C14)
- في حال
!is_empty_algorithm(lhs_precision_type, rhs_precision_type, accumulation_type, lhs_component_count, rhs_component_count, num_primitive_operations allow_imprecise_accumulation)
:- (C21)
precision_config... = DEFAULT
. - (C22)
0 < lhs_component_count
. - (C23)
0 < rhs_component_count
. - (C24)
0 < num_primitive_operations
.
- (C21)
أمثلة
// %lhs: [
// [[1, 2],
// [3, 4]],
// [[5, 6],
// [7, 8]]
// ]
// %rhs: [
// [[1, 0],
// [0, 1]],
// [[1, 0],
// [0, 1]]
// ]
%result = "stablehlo.dot_general"(%lhs, %rhs) {
dot_dimension_numbers = #stablehlo.dot<
lhs_batching_dimensions = [0],
rhs_batching_dimensions = [0],
lhs_contracting_dimensions = [2],
rhs_contracting_dimensions = [1]
>,
precision_config = [#stablehlo<precision DEFAULT>, #stablehlo<precision DEFAULT>],
algorithm = #stablehlo.dot_algorithm<
lhs_precision_type = tf32,
rhs_precision_type = tf32,
accumulation_type = f32,
lhs_component_count = 1,
rhs_component_count = 1,
num_primitive_operations = 1,
allow_imprecise_accumulation = false
>
} : (tensor<2x2x2xi64>, tensor<2x2x2xi64>) -> tensor<2x2x2xi64>
// %result: [
// [[1, 2],
// [3, 4]],
// [[5, 6],
// [7, 8]]
// ]
dynamic_broadcast_in_dim
الدلالات
هذه العملية متطابقة من الناحية الوظيفية مع عملية
broadcast_in_dim
، ولكن يتم تحديد شكل النتيجة ديناميكيًا من خلال output_dimensions
.
تقبل العملية أيضًا السمتَين الاختياريتين known_expanding_dimensions
وknown_nonexpanding_dimensions
لالتعبير عن المعرفة الثابتة حول سلوك توسيع السمات.
في حال عدم تحديدها، يُفترض أنّ جميع السمات قابلة للتوسيع.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | operand |
متّجه أو متّجه مُكمّر | (C1-C2)، (C5-C6)، (C9) |
(I2) | output_dimensions |
مصفوفة لاتّجاه واحد من النوع الصحيح | (C7) |
(I3) | broadcast_dimensions |
مصفوفة لاتّصال ثابت أحادي الأبعاد من النوع الصحيح | (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)
. بخلاف ذلك.
- (ج2)
size(broadcast_dimensions) = rank(operand)
. - (ج3)
0 <= broadcast_dimensions < rank(result)
. - (C4)
is_unique(broadcast_dimensions)
. - (ج5) بالنسبة إلى جميع
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)))
.
- (ج7)
size(output_dimensions) = rank(result)
. - (ج8)
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
الدلالات
هذه العملية متطابقة من الناحية الوظيفية مع عملية
التفاف
op، ولكن يتم تحديد الحشو ديناميكيًا من خلال 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) |
القيود
- (ج1)
N = rank(lhs) = rank(rhs)
. - (C2)
size(window_strides) = N - 2
. - (ج3)
0 < window_strides
. - (C4)
shape(padding) = [N - 2, 2]
. - (ج5)
size(lhs_dilation) = N - 2
. - (C6)
0 < lhs_dilation
. - (ج7)
size(rhs_dilation) = N - 2
. - (ج8)
0 < rhs_dilation
. - (ج9)
size(window_reversal) = N - 2
. - (C10)
dim(lhs, input_batch_dimension) % batch_group_count = 0
. - (C11)
dim(lhs, input_feature_dimension) % feature_group_count = 0
. - (C12)
size(input_spatial_dimensions) = N - 2
. - (C13) بالنظر إلى
input_dimensions = [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]
:is_unique(input_dimensions)
.0 <= input_dimensions < N
.
- (C14)
dim(rhs, kernel_input_feature_dimension) = dim(lhs, input_feature_dimension) / feature_group_count
. - (C15)
dim(rhs, kernel_output_feature_dimension) % batch_group_count = 0
. - (C16)
dim(rhs, kernel_output_feature_dimension) % feature_group_count = 0
. - (C17)
size(kernel_spatial_dimensions) = N - 2
. - (C18) بالنظر إلى
kernel_dimensions = kernel_spatial_dimensions + [kernel_input_feature_dimension] + [kernel_output_feature_dimension]
:is_unique(kernel_dimensions)
.0 <= kernel_dimensions < N
.
- (C19)
size(output_spatial_dimensions) = N - 2
. - (C20) بالنظر إلى
output_dimensions = [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]
:is_unique(output_dimensions)
.0 <= output_dimensions < N
.
- (C21)
0 < feature_group_count
. - (C22)
0 < batch_group_count
. - (C23)
feature_group_count = 1 or batch_group_count = 1
. - (C24)
size(precision_config) = 2
. - (C25) يتم تعريف
dim(result, result_dim)
على النحو التالي:dim(lhs, input_batch_dimension) / batch_group_count
إذاresult_dim = output_batch_dimension
dim(rhs, kernel_output_feature_dimension)
إذاresult_dim = output_feature_dimension
num_windows
في الحالات الأخرى، حيث:output_spatial_dimensions[spatial_dim] = result_dim
.lhs_dim = input_spatial_dimensions[spatial_dim]
.rhs_dim = kernel_spatial_dimensions[spatial_dim]
.dilated_input_shape[lhs_dim] = dim(lhs, lhs_dim) = 0 ? 0 : (dim(lhs, lhs_dim) - 1) * lhs_dilation[spatial_dim] + 1
.padded_input_shape[lhs_dim] = padding[spatial_dim, 0] + dilated_input_shape[lhs_dim] + padding[spatial_dim, 1]
.dilated_window_shape[lhs_dim] = dim(rhs, rhs_dim) = 0 ? 0 : (dim(rhs, rhs_dim) - 1) * rhs_dilation[spatial_dim] + 1
.is_empty_window[lhs_dim] = padded_input_shape[lhs_dim] = 0 || dilated_window_shape[lhs_dim] > padded_input_shape[lhs_dim]
.num_windows = is_empty_window[lhs_dim] ? 0 : floor((padded_input_shape[lhs_dim] - dilated_window_shape[lhs_dim]) / window_strides[spatial_dim]) + 1
.
- (C26)
rank(result) = N
. - إذا كانت العملية تستخدم مصفوفات غير مُكثَّفة:
- (C27)
element_type(lhs) = element_type(rhs) = element_type(result)
.
- (C27)
- إذا كانت العملية تستخدم موتّرات كميّة:
- (C28)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
. - (C29) إذا كانت
is_per_axis_quantized(rhs)
، ثمquantization_dimension(rhs) = kernel_output_feature_dimension
. - (C30) إذا كانت السمة
is_per_axis_quantized(result)
، يتم عندهاquantization_dimension(result) = output_feature_dimension
. - إذا
is_quantized(lhs)
: - (C31)
storage_type(lhs) = storage_type(rhs)
. - (C32)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
. - (C33) إذا كان
is_per_tensor_quantized(rhs)
، ثمis_per_tensor_quantized(result)
. - إذا
!is_quantized(lhs)
: - (C34)
element_type(lhs) = expressed_type(rhs) = element_type(result)
.
- (C28)
أمثلة
// %lhs: [[
// [[1], [2], [5], [6]],
// [[3], [4], [7], [8]],
// [[10], [11], [14], [15]],
// [[12], [13], [16], [17]]
// ]]
//
// %rhs: [
// [[[1]], [[1]], [[1]]],
// [[[1]], [[1]], [[1]]],
// [[[1]], [[1]], [[1]]]
// ]
// %padding: [[1, 1],
// [1, 1]]
%result = "stablehlo.dynamic_conv"(%lhs, %rhs, %padding) {
window_strides = array<i64: 4, 4>,
lhs_dilation = array<i64: 2, 2>,
rhs_dilation = array<i64: 1, 1>,
window_reversal = array<i1: false, false>,
dimension_numbers = #stablehlo.conv<raw
input_batch_dimension = 0,
input_feature_dimension = 3,
input_spatial_dimensions = [0, 1],
kernel_input_feature_dimension = 2,
kernel_output_feature_dimension = 3,
kernel_spatial_dimensions = [0, 1],
output_batch_dimension = 0,
output_feature_dimension = 3,
output_spatial_dimensions = [1, 2]
>,
feature_group_count = 1 : i64,
batch_group_count = 1 : i64,
precision_config = [#stablehlo<precision DEFAULT>, #stablehlo<precision DEFAULT>]
} : (tensor<1x4x4x1xi64>, tensor<3x3x1x1xi64>, tensor<2x2xi64>) -> tensor<1x2x2x1xi64>
// %result: [[
// [[1], [5]],
// [[10], [14]]
// ]]
dynamic_gather
الدلالات
هذه العملية متطابقة من الناحية الوظيفية مع
gather
op، مع تحديد 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) |
القيود
- (ج1)
rank(operand) = size(offset_dims) + size(collapsed_slice_dims)
. - (ج2)
0 <= index_vector_dim <= rank(start_indices)
. - (ج3)
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)
. - (ج5)
0 <= offset_dims < rank(result)
. - (C6)
is_unique(collapsed_slice_dims) and is_sorted(collapsed_slice_dims)
. - (C7)
0 <= collapsed_slice_dims < rank(operand)
. - (ج8)
slice_sizes[collapsed_slice_dims...] <= 1
. - (ج9)
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
.- تضع
combine
batch_dim_sizes
على المحاور المقابلة لـbatch_dims
وoffset_dim_sizes
على المحاور المقابلة لـoffset_dims
.
- (C14)
element_type(operand) = element_type(result)
.
أمثلة
// %operand: [
// [[1, 2], [3, 4], [5, 6], [7, 8]],
// [[9, 10],[11, 12], [13, 14], [15, 16]],
// [[17, 18], [19, 20], [21, 22], [23, 24]]
// ]
// %start_indices: [
// [[0, 0], [1, 0], [2, 1]],
// [[0, 1], [1, 1], [0, 2]]
// ]
// %slize_sizes: [1, 2, 2]
%result = "stablehlo.dynamic_gather"(%operand, %start_indices, %slize_sizes) {
dimension_numbers = #stablehlo.gather<
offset_dims = [2, 3],
collapsed_slice_dims = [0],
start_index_map = [1, 0],
index_vector_dim = 2>,
indices_are_sorted = false
} : (tensor<3x4x2xi64>, tensor<2x3x2xi64>, tensor<3xi64>) -> tensor<2x3x2x2xi64>
// %result: [
// [
// [[1, 2], [3, 4]],
// [[3, 4], [5, 6]],
// [[13, 14], [15, 16]]
// ],
// [
// [[9, 10], [11, 12]],
// [[11, 12], [13, 14]],
// [[17, 18], [19, 20]]
// ]
// ]
dynamic_iota
الدلالات
هذه العملية متطابقة من الناحية الوظيفية مع عملية
iota
op، ولكن يتم تحديد شكل النتيجة ديناميكيًا من خلال output_shape
.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | output_shape |
مصفوفة لاتّجاه واحد من النوع الصحيح | (C1)، (C2) |
(I2) | iota_dimension |
si64 |
(C1) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
مصفوفة من النوع الصحيح أو الكسور العشرية أو المركّب أو مصفوفة مُشفَّرة لكل مصفوفة | (C2) |
القيود
- (ج1)
0 <= iota_dimension < size(output_shape)
. - (ج2)
rank(result) = size(output_shape)
.
أمثلة
%output_shape = stablehlo.constant dense<[4, 5]> : tensor<2xi64>
%result = "stablehlo.dynamic_iota"(%output_shape) {
iota_dimension = 0 : i64
} : (tensor<2xi64>) -> tensor<4x5xi64>
// %result: [
// [0, 0, 0, 0, 0],
// [1, 1, 1, 1, 1],
// [2, 2, 2, 2, 2],
// [3, 3, 3, 3, 3]
// ]
dynamic_pad
دلالات الألفاظ
هذه العملية متطابقة من الناحية الوظيفية مع دالة
pad
op، ولكن مع تحديد edge_padding_low
وedge_padding_high
وinterior_padding
ديناميكيًا كقيم.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | operand |
متوتّر أو مستنسر كمي لكلّ متسابق | (C1)، و(C2)، و(C4) |
(I2) | padding_value |
مصفوفة متّجهية من الدرجة 0 أو مصفوفة متّجهية مُشفَّرة لكلّ متّجه | (C1) |
(I3) | edge_padding_low |
مصفوفة لاتّجاه واحد من النوع الصحيح | (C1)، (C4) |
(I4) | edge_padding_high |
مصفوفة لاتّجاه واحد من النوع الصحيح | (C1)، (C4) |
(I5) | interior_padding |
مصفوفة لاتّجاه واحد من النوع الصحيح | (C2-C4) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
متّجه متعدّد الأبعاد أو متّجه متعدّد الأبعاد مُشفَّر على مستوى كل متّجه | (C3-C6) |
القيود
- (ج1)
element_type(operand) = element_type(padding_value) = element_type(result)
. - (ج2)
size(edge_padding_low) = size(edge_padding_high) = size(interior_padding) = rank(operand)
. - (ج3)
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)
قد تختلف عن الأخرى.
- (ج2)
size(operand) = size(result)
. - (ج3) إذا كان
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) |
القيود
- (ج1)
element_type(operand) = element_type(result)
. - (C2)
size(start_indices) = size(slice_sizes) = rank(operand)
. - (ج3)
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) |
القيود
- (ج1)
type(operand) = type(result)
. - (ج2)
element_type(update) = element_type(operand)
. - (C3)
rank(update) = rank(operand)
: - (C4)
size(start_indices) = rank(operand)
. - (ج5)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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 |
enum من FFT وIFFT وRFFT وIRFFT |
(C2)، (C5) |
(I3) | fft_length |
ثابت التوتر ذو البعد الواحد من النوع si64 |
(C1)، و(C3)، و(C4) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
مصفوفة من النوع النقطة العائمة أو النوع المركّب | (C2)، (C4)، (C5) |
القيود
- (ج1)
size(fft_length) <= rank(operand)
. - (ج2) تختلف العلاقة بين نوعَي العنصرَين
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)
هو نوع نقطة عائمة من نفس دلالات النقطة العائمة.
- إذا كانت
- (ج3)
1 <= size(fft_length) <= 3
. - (C4) إذا كان يوجد بين
operand
وresult
، هناك متسابقreal
من نوع نقطة عائمة، ثمshape(real)[-size(fft_length):] = fft_length
. - (C5)
shape(result) = shape(operand)
باستثناء:- إذا كان
fft_type = RFFT
،dim(result, -1) = dim(operand, -1) = 0 ? 0 : dim(operand, -1) / 2 + 1
. - إذا كان
fft_type = IRFFT
،dim(operand, -1) = dim(result, -1) = 0 ? 0 : dim(result, -1) / 2 + 1
.
- إذا كان
أمثلة
// %operand: [(1.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0)]
%result = "stablehlo.fft"(%operand) {
fft_type = #stablehlo<fft_type FFT>,
fft_length = array<i64: 4>
} : (tensor<4xcomplex<f32>>) -> tensor<4xcomplex<f32>>
// %result: [(1.0, 0.0), (1.0, 0.0), (1.0, 0.0), (1.0, 0.0)]
الطابق
الدلالات
تُجري هذه الدالة عملية تقريب للأعلى للعنصر في مصفوفة operand
وتُنشئ مصفوفة result
.
تُنفِّذ عملية roundToIntegralTowardNegative
من مواصفات IEEE-754
. بالنسبة إلى الأنواع الكَمية، يتم تنفيذ
dequantize_op_quantize(floor, operand, type(result))
.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | operand |
متّصِل من نوع النقطة العائمة أو متوتر كمي حسب كل متسابق | (C1) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
مصفوفة من النوع العشري أو مصفوفة مُشفَّرة لكل مصفوفة | (C1) |
القيود
- (ج1)
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])
إذاd_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
عناصر individual في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) |
القيود
- (ج1)
rank(operand) = size(offset_dims) + size(collapsed_slice_dims) + size(operand_batching_dims)
. - (ج2)
0 <= index_vector_dim <= rank(start_indices)
. - (ج3)
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)
. - (ج5)
0 <= offset_dims < rank(result)
. - (C6)
is_unique(concatenate(collapsed_slice_dims, operand_batching_dims))
- (ج7)
is_sorted(collapsed_slice_dims)
. - (ج8)
0 <= collapsed_slice_dims < rank(operand)
. - (ج9)
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
.- تضع
combine
batch_dim_sizes
على المحاور المقابلة لـbatch_dims
وoffset_dim_sizes
على المحاور المقابلة لـoffset_dims
.
- (C23)
element_type(operand) = element_type(result)
.
أمثلة
// %operand: [
// [
// [[1, 2], [3, 4], [5, 6], [7, 8]],
// [[9, 10],[11, 12], [13, 14], [15, 16]],
// [[17, 18], [19, 20], [21, 22], [23, 24]]
// ],
// [
// [[25, 26], [27, 28], [29, 30], [31, 32]],
// [[33, 34], [35, 36], [37, 38], [39, 40]],
// [[41, 42], [43, 44], [45, 46], [47, 48]]
// ]
// ]
// %start_indices: [
// [
// [[0, 0], [1, 0], [2, 1]],
// [[0, 1], [1, 1], [0, 9]]
// ],
// [
// [[0, 0], [2, 1], [2, 2]],
// [[1, 2], [0, 1], [1, 0]]
// ]
// ]
%result = "stablehlo.gather"(%operand, %start_indices) {
dimension_numbers = #stablehlo.gather<
offset_dims = [3, 4],
collapsed_slice_dims = [1],
operand_batching_dims = [0],
start_indices_batching_dims = [1],
start_index_map = [2, 1],
index_vector_dim = 3>,
slice_sizes = array<i64: 1, 1, 2, 2>,
indices_are_sorted = false
} : (tensor<2x3x4x2xi32>, tensor<2x2x3x2xi64>) -> tensor<2x2x3x2x2xi32>
// %result: [
// [
// [
// [[1, 2], [3, 4]],
// [[3, 4], [5, 6]],
// [[13, 14], [15, 16]]
// ],
// [
// [[33, 34], [35, 36]],
// [[35, 36], [37, 38]],
// [[41, 42], [43, 44]]
// ]
// ],
// [
// [
// [[1, 2], [3, 4]],
// [[13, 14], [15, 16]],
// [[21, 22], [23, 24]]
// ],
// [
// [[43, 44], [45, 46]],
// [[33, 34], [35, 36]],
// [[27, 28], [29, 30]]
// ]
// ]
// ]
get_dimension_size
الدلالات
تُنشئ هذه الدالة حجم dimension
المحدّد من operand
. مع أطيب التحيّات،
result = dim(operand, dimension)
. لا تهتم الدلالات إلا بمكوّن شكل
النوع. يمكن أن يكون نوع العنصر أيّ شيء.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | operand |
متّجه أو متّجه مُكمّر | (C1) |
(I2) | dimension |
ثابت من النوع si64 |
(C1) |
النواتج
الاسم | النوع |
---|---|
result |
متسابق أحادي الأبعاد من النوع si32 |
القيود
- (ج1)
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) |
القيود
- (ج1)
0 <= index < size(operand)
. - (ج2)
type(result) = tuple_element_types(operand)[index]
.
أمثلة
// %operand: ([1.0, 2.0], (3))
index = 0 : i32
} : (tuple<tensor<2xf32>, tuple<tensor<i32>>>) -> tensor<2xf32>
// %result: [1.0, 2.0]
إذا
دلالات الألفاظ
تُنشئ هذه الدالة ناتجًا من تنفيذ دالة واحدة بالضبط من 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) |
القيود
- (ج1)
input_types(true_branch) = input_types(false_branch) = []
. - (ج2)
output_types(true_branch) = output_types(false_branch)
. - (ج3)
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) |
القيود
- (ج1)
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]
الخلاصة
الدلالات
تقرأ البيانات من الخلاصة وتُنشئ results
.
يتم تحديد دلالات infeed_config
.
results
تتألف من قيم الحمولة التي تأتي أولاً ورمز مميّز يأتي
في النهاية. في المستقبل، نخطّط لتقسيم الحمولة والرمز المميّز إلى مجرّدَين
مخرجَين منفصلَين لتحسين الوضوح
(#670).
مدخلات
التصنيف | الاسم | النوع |
---|---|---|
(I1) | token |
token |
(I2) | infeed_config |
ثابت من النوع string |
النواتج
الاسم | النوع | القيود |
---|---|---|
results |
عدد متغيّر من مصفوفات الخلاصات أو مصفوفات الخلاصات الكمّية أو الرموز | (C1-C3) |
القيود
- (ج1)
0 < size(results)
. - (C2)
is_empty(result[:-1])
أوis_tensor(type(results[:-1]))
. - (ج3)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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 - بالنسبة إلى الأعداد المركبة: اللوغاريتم المركب بالإضافة إلى واحد
- بالنسبة إلى الأنواع المحوَّلة إلى قيم رقمية:
dequantize_op_quantize(log_plus_one, operand, type(result))
.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | operand |
مصفوفة متّجه من النوع الكسري أو المركّب أو مصفوفة متّجه مُشفَّرة لكل متّجه | (C1) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
مأخذ النقطة العائمة أو النوع المركّب أو المتسّع الكمّي حسب كل متسابق | (C1) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
shape(inputs...) = shape(result)
. - (C2)
0 < size(inputs) = N
. - (ج3)
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
. حسب نوع العنصر، يتم إجراء ما يلي:
- بالنسبة إلى القيم المنطقية: "أو" منطقي
- بالنسبة إلى الأعداد الصحيحة: الحد الأقصى للعدد الصحيح
- بالنسبة إلى الأعداد العشرية العائمة:
maximum
من IEEE-754 - بالنسبة إلى الأعداد المركبة: الحد الأقصى للترتيب الأبجدي لزوج
(real, imaginary)
. إنّ فرض ترتيب على الأرقام المركبة يتضمن دلالات مفاجئة، لذلك نخطّط في المستقبل لإزالة إمكانية استخدام الأرقام المركبة في هذه العملية (#560). - بالنسبة إلى الأنواع التي تم تحويلها إلى قيم عددية:
dequantize_op_quantize(maximum, lhs, rhs, type(result))
.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | lhs |
متوتّر أو مستنسر كمي لكلّ متسابق | (C1) |
(I2) | rhs |
متّجه متعدّد الأبعاد أو متّجه متعدّد الأبعاد مُشفَّر على مستوى كل متّجه | (C1) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
متّجه متعدّد الأبعاد أو متّجه متعدّد الأبعاد مُشفَّر على مستوى كل متّجه | (C1) |
القيود
- (ج1)
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
. بناءً على نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى القيم المنطقية: "الواو المنطقي"
- بالنسبة إلى الأعداد الصحيحة: الحد الأدنى لطول العدد الصحيح
- بالنسبة إلى الأعداد العشرية العائمة:
minimum
من IEEE-754 - بالنسبة إلى الأعداد المركّبة: الحد الأدنى للترتيب الأبجدي لزوج
(real, imaginary)
إنّ فرض ترتيب على الأرقام المركبة يتضمن دلالات مفاجئة، لذلك نخطّط في المستقبل لإزالة إمكانية استخدام الأرقام المركبة في هذه العملية (#560). - بالنسبة إلى الأنواع التي تم تحويلها إلى قيم عددية:
dequantize_op_quantize(minimum, lhs, rhs, type(result))
.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | lhs |
متوتّر أو مستنسر كمي لكلّ متسابق | (C1) |
(I2) | rhs |
متّجه متعدّد الأبعاد أو متّجه متعدّد الأبعاد مُشفَّر على مستوى كل متّجه | (C1) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
متّجه متعدّد الأبعاد أو متّجه متعدّد الأبعاد مُشفَّر على مستوى كل متّجه | (C1) |
القيود
- (ج1)
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
. بناءً على نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى القيم المنطقية: "الواو المنطقي"
- بالنسبة إلى الأعداد الصحيحة: ضرب الأعداد الصحيحة
- بالنسبة إلى الأعداد العشرية العائمة:
multiplication
من IEEE-754 - بالنسبة إلى الأعداد المركبة: الضرب المركب
- بالنسبة إلى الأنواع التي تم تحويلها إلى قيم عددية:
dequantize_op_quantize(multiply, lhs, rhs, type(result))
.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | lhs |
متوتّر أو مستنسر كمي لكلّ متسابق | (C1) |
(I2) | rhs |
متّجه متعدّد الأبعاد أو متّجه متعدّد الأبعاد مُشفَّر على مستوى كل متّجه | (C1) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
متّجه متعدّد الأبعاد أو متّجه متعدّد الأبعاد مُشفَّر على مستوى كل متّجه | (C1) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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
.
بناءً على نوع العنصر، نفِّذ ما يلي:
- بالنسبة إلى القيم المنطقية: النفي المنطقي
- للأعداد الصحيحة: النفي على مستوى البت
الوسيطات
الاسم | النوع | القيود |
---|---|---|
operand |
مصفوفة كثيفة من النوع المنطقي أو الصحيح | (C1) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
مصفوفة كثيفة من النوع المنطقي أو الصحيح | (C1) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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 على مستوى البت.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | lhs |
مصفوفة من النوع الصحيح أو المنطقي | (C1) |
(I2) | rhs |
مصفوفة من النوع الصحيح أو المنطقي | (C1) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
مصفوفة من النوع الصحيح أو المنطقي | (C1) |
القيود
- (ج1)
type(lhs) = type(rhs) = type(result)
.
أمثلة
// Bitwise operation with with integer tensors
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.or"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 6], [7, 12]]
// Logical operation with with boolean tensors
// %lhs: [[false, false], [true, true]]
// %rhs: [[false, true], [false, true]]
%result = "stablehlo.or"(%lhs, %rhs) : (tensor<2x2xi1>, tensor<2x2xi1>) -> tensor<2x2xi1>
// %result: [[false, true], [true, true]]
الخلاصة
الدلالات
تُسجِّل inputs
في خلاصة الإخراج وتُنشئ رمزًا مميّزًا result
.
يتم تحديد دلالات outfeed_config
.
مدخلات
التصنيف | الاسم | النوع |
---|---|---|
(I1) | inputs |
عدد متغيّر من مصفوفات السلاسل أو مصفوفات السلاسل الكمّية |
(I2) | token |
token |
(I3) | outfeed_config |
ثابت من النوع string |
النواتج
الاسم | النوع |
---|---|
result |
token |
أمثلة
%result = "stablehlo.outfeed"(%input0, %token) {
outfeed_config = ""
} : (tensor<2x2x2xi64>, !stablehlo.token) -> !stablehlo.token
لبادة
الدلالات
توسيع 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 |
مصفوفة متّجهية من الدرجة 0 أو مصفوفة متّجهية مُشفَّرة لكلّ متّجه | (C1) |
(I3) | edge_padding_low |
ثابت التوتر ذو البعد الواحد من النوع si64 |
(C1)، (C4) |
(I4) | edge_padding_high |
ثابت مصفوفة لاتّجاه واحد من النوع si64 |
(C1)، (C4) |
(I5) | interior_padding |
ثابت مصفوفة لاتّجاه واحد من النوع si64 |
(C2-C4) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
متّجه متعدّد الأبعاد أو متّجه متعدّد الأبعاد مُشفَّر على مستوى كل متّجه | (C3-C6) |
القيود
- (ج1)
element_type(operand) = element_type(padding_value) = element_type(result)
. - (ج2)
size(edge_padding_low) = size(edge_padding_high) = size(interior_padding) = rank(operand)
. - (ج3)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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
، تنقل العملية البيانات من
المضيف. وإلا، سيتم نقل البيانات من جهاز آخر. وهذا يعني أنّه
يتم تحديده أثناء التنفيذ. تكرّر هذه العلامة المعلومات المقدّمة في
channel_type
، لذا نخطّط في المستقبل للاحتفاظ بواحد منها فقط
(#666).
results
تتألف من قيم الحمولة التي تأتي أولاً ورمز مميّز يأتي
في النهاية. في المستقبل، نخطّط لتقسيم الحمولة والرمز المميّز إلى مجرّدَين
مخرجَين منفصلَين لتحسين الوضوح
(#670).
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | token |
token |
(C4) |
(I2) | channel_id |
ثابت من النوع si64 |
|
(I3) | channel_type |
عدد أنواع DEVICE_TO_DEVICE وHOST_TO_DEVICE |
(C1) |
(I4) | is_host_transfer |
ثابت من النوع i1 |
(C1) |
النواتج
الاسم | النوع | القيود |
---|---|---|
results |
الأعداد المتباينة من متساعات التوتر أو العشرات الكمية أو الرموز المميزة | (C2-C4) |
القيود
- يتم تعريف (C1)
channel_type
على النحو التالي:HOST_TO_DEVICE
إذاis_host_transfer = true
،DEVICE_TO_DEVICE
بخلاف ذلك.
- (ج2)
0 < size(results)
. - (ج3)
is_empty(result[:-1])
أوis_tensor(type(results[:-1]))
- (C4)
is_token(type(results[-1]))
.
أمثلة
%results0, %results1 = "stablehlo.recv"(%token) {
channel_handle = #stablehlo.channel_handle<handle = 1, type = 3>,
is_host_transfer = true
} : (!stablehlo.token) -> (tensor<2x2xi64>, !stablehlo.token)
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]
، حيث يتم إدراج:
عندdimensions
.input_slices_converted = to_destination_type(input_slices..., type(func_inputs(body)[:len(func_inputs(body))//2])...)
.init_values_converted = to_destination_type(init_values..., type(func_inputs(body)[len(func_inputs(body))//2:])...)
.reduce(input_slices_converted) = exec(schedule)
لبعض الأشجار الثنائيةschedule
حيث:exec(node) = body(exec(node.left), exec(node.right))
.exec(leaf) = leaf.value
.
schedule
هي شجرة ثنائية كاملة محدّدة التنفيذ تتألف من الخطوات التالية عند التوغّل فيها بترتيب تصاعدي:- قيم
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) |
القيود
- (ج1)
same(shape(inputs...))
. - (ج2)
element_type(inputs...) = element_type(init_values...)
. - (ج3)
0 < size(inputs) = size(init_values) = size(results) = N
. - (C4)
0 <= dimensions < rank(inputs[0])
. - (ج5)
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)
. - (ج7)
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 |
(ج3) |
النواتج
الاسم | النوع | القيود |
---|---|---|
output |
مصفوفة من النوع العشري أو مصفوفة مُشفَّرة لكل مصفوفة | (C1) |
القيود
- (ج1)
baseline_type(operand) = baseline_type(output)
. - (C2)
1 <= exponent_bits
. - (ج3)
0 <= mantissa_bits
.
أمثلة
// Logical values: +Inf, NaN, +Denormal, 0.0, 65519.0, 65520.0
// %operand: [0x7FF0000000000000, 0x7FFFFFFFFFFFFFFF, 0x0000000000000001, 0.0, 65519.0, 65520.0]
%output = "stablehlo.reduce_precision"(%operand) {
exponent_bits = 5 : i32,
mantissa_bits = 10 : i32
} : (tensor<6xf64>) -> tensor<6xf64>
// Logical values: +Inf, NaN, 0.0, 0.0, 65504.0, +Inf
// %output: [0x7FF0000000000000, 0x7FFFFFFFFFFFFFFF, 0.0, 0.0, 65504.0, 0x7FF0000000000000]
reduce_scatter
الدلالات
ضمن كل مجموعة عمليات في شبكة عمليات StableHLO، يتم إجراء عملية تقليل،
باستخدام computations
، على قيم مصفوفة operand
من كل عملية، ويؤدي ذلك إلى хувين نتيجة التقليل على طول scatter_dimension
إلى أجزاء، ثم ينثر
الأجزاء المجزّأة بين العمليات لإنشاء result
.
تقسم العملية شبكة عملية StableHLO إلى process_groups
التي يتم
تعريفها على النحو التالي:
cross_replica(replica_groups)
إذاchannel_id <= 0 and use_global_device_ids = false
.cross_replica_and_partition(replica_groups)
إذاchannel_id > 0 and use_global_device_ids = false
.flattened_ids(replica_groups)
إذاchannel_id > 0 and use_global_device_ids = true
.
بعد ذلك، ضمن كل process_group
:
reduced_value = all_reduce(operand, replica_groups, channel_id, use_global_device_ids, computation)
.parts@sender = split(reduced_value@sender, dim(process_groups, 1), scatter_dimension)
.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) |
القيود
- (ج1)
dim(operand, scatter_dimension) % dim(process_groups, 1) = 0
. - (ج2)
0 <= scatter_dimension < rank(operand)
. - (C3)
is_unique(replica_groups)
: - (C4) يتم تعريف
size(replica_groups)
على النحو التالي:num_replicas
في حال استخدامcross_replica
num_replicas
في حال استخدامcross_replica_and_partition
num_processes
في حال استخدامflattened_ids
- (ج5)
0 <= replica_groups < size(replica_groups)
. - (ج6) إذا كان
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)
.
- (ج9)
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)
(راجِع تقليل) حيث:
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) |
القيود
- (ج1)
0 < size(inputs) = size(init_values) = size(results) = N
. - (C2)
same(shape(inputs...))
. - (ج3)
element_type(inputs...) = element_type(init_values...)
. - (C4)
size(window_dimensions) = rank(inputs[0])
. - (ج5)
0 < window_dimensions
. - (C6)
size(window_strides) = rank(inputs[0])
. - (ج7)
0 < window_strides
. - (C8)
size(base_dilations) = rank(inputs[0])
. - (ج9)
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) |
القيود
- (ج1)
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)
قد تختلف عن الأخرى.
- (ج2)
size(operand) = size(result)
. - (ج3) إذا كان
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] - 1
إذاd
فيdimensions
.operand_index[d] = result_index[d]
بخلاف ذلك
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | operand |
متّجه متعدّد الأبعاد أو متّجه متعدّد الأبعاد مُشفَّر على مستوى كل متّجه | (C1)، (C3) |
(I2) | dimensions |
ثابت مصفوفة لاتّجاه واحد من النوع si64 |
(C2)، (C3) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
متّجه متعدّد الأبعاد أو متّجه متعدّد الأبعاد مُشفَّر على مستوى كل متّجه | (C1)، (C3) |
القيود
- (ج1)
type(operand) = type(result)
. - (ج2)
is_unique(dimensions)
. - (ج3)
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_distribution
وتُنشئ كثافة result
ذات شكل معيّن shape
.
إذا كان rng_distribution = UNIFORM
، يتم إنشاء الأرقام العشوائية
باستخدام التوزيع الموحّد على الفاصل الزمني [a, b)
. إذا كانت a >= b
،
سيكون السلوك غير محدد.
إذا كانت القيمة rng_distribution = NORMAL
، يتم إنشاء الأرقام العشوائية
باستخدام التوزيع العادي الذي يكون فيه المتوسط = a
والانحراف المعياري = b
.
إذا كان b < 0
، يكون السلوك غير محدّد.
إنّ الطريقة الدقيقة لإنشاء الأرقام العشوائية يتم تحديدها من خلال التنفيذ. على سبيل المثال، قد تكون حاسمة أو لا تكون حاسمة، وقد تستخدم أو لا تستخدم الحالة المخفية.
في محادثاتنا مع العديد من الجهات المعنية، تبيّن أنّ هذه العملية قد تمّت إيقافها نهائيًا، لذا نخطّط في المستقبل لاستكشاف إمكانية إزالتها (#597).
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | a |
مصفوفة ثلاثية الأبعاد من النوع الصحيح أو المنطقي أو النوع ذو النقطة العائمة | (C1)، (C2) |
(I2) | b |
متين عشري الأبعاد للعدد الصحيح أو النوع المنطقي أو نوع النقطة العائمة | (C1)، (C2) |
(I3) | shape |
ثابت التوتر ذو البعد الواحد من النوع si64 |
(ج3) |
(I4) | rng_distribution |
عدد أنواع UNIFORM وNORMAL |
(C2) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
مصفوفة من النوع الصحيح أو المنطقي أو الكسور العشرية | (C1-C3) |
القيود
- (ج1)
element_type(a) = element_type(b) = element_type(result)
. - (C2) إذا كان
rng_distribution = NORMAL
، ثمis_float(a)
. - (ج3)
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 |
enum من DEFAULT وTHREE_FRY وPHILOX |
(C2) |
(I2) | initial_state |
مصفوفة لاتّجاه واحد من النوع ui64 |
(C1)، (C2) |
النواتج
الاسم | النوع | القيود |
---|---|---|
output_state |
مصفوفة لاتّجاه واحد من النوع ui64 |
(C1) |
output |
مصفوفة من النوع الصحيح أو النوع الثنائي النقطة العائمة |
القيود
- (ج1)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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
عناصر individual في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
عناصر individual في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) |
القيود
- (ج1)
same(shape(inputs...))
. - (C2) rank(inputs[0]) = size(update_window_dims) + size(inserted_window_dims)
- size(input_batching_dims)`.
- (ج3)
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
.
- (ج5)
0 < size(inputs) = size(updates) = N
. - (C6)
element_type(updates...) = element_type(inputs...)
. - (ج7)
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]]
// ]
// ]
%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) |
القيود
- (ج1)
rank(pred) = 0 or shape(pred) = shape(on_true)
. - (ج2)
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 |
مصفوفة متّجهية من الدرجة 0 أو مصفوفة متّجهية مُشفَّرة لكلّ متّجه | (ج3) |
(I4) | window_dimensions |
ثابت مصفوفة لاتّجاه واحد من النوع si64 |
(C2)، (C4)، (C5) |
(I5) | window_strides |
ثابت مصفوفة لاتّجاه واحد من النوع si64 |
(C2)، (C6)، (C7) |
(I6) | padding |
ثابت التوتر الثنائي الأبعاد من النوع si64 |
(C2)، (C8) |
(I7) | select |
دالة | (C9) |
(I8) | scatter |
دالة | (C10) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
متّجه متعدّد الأبعاد أو متّجه متعدّد الأبعاد مُشفَّر على مستوى كل متّجه | (C11-C12) |
القيود
- (ج1)
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
.
- (ج3)
element_type(init_value) = element_type(operand)
. - (C4)
size(window_dimensions) = rank(operand)
. - (ج5)
0 < window_dimensions
. - (C6)
size(window_strides) = rank(operand)
. - (C7)
0 < window_strides
. - (ج8)
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
وإنشاء رمز مميّز result
إذا كان is_host_transfer
هو true
، تنقل العملية البيانات إلى
المضيف. وإلا، يتم نقل البيانات إلى جهاز آخر. وهذا يعني أنّه
يتم تحديده أثناء التنفيذ. تكرّر هذه العلامة المعلومات المقدّمة في
channel_type
، لذا نخطّط في المستقبل للاحتفاظ بواحد منها فقط
(#666).
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | inputs |
عدد متغيّر من مصفوفات السلاسل أو مصفوفات السلاسل الكمّية | |
(I2) | token |
token |
|
(I3) | channel_id |
ثابت من النوع si64 |
|
(I4) | channel_type |
عدد أنواع DEVICE_TO_DEVICE وDEVICE_TO_HOST |
(C1) |
(I5) | is_host_transfer |
ثابت من النوع i1 |
(C1) |
النواتج
الاسم | النوع |
---|---|
result |
token |
القيود
- يتم تعريف (C1)
channel_type
على النحو التالي:DEVICE_TO_HOST
إذاis_host_transfer = true
،DEVICE_TO_DEVICE
بخلاف ذلك
أمثلة
%result = "stablehlo.send"(%operand, %token) {
channel_handle = #stablehlo.channel_handle<handle = 1, type = 2>,
is_host_transfer = true
} : (tensor<2x2xi64>, !stablehlo.token) -> !stablehlo.token
shift_left
الدلالات
تُجري عملية انزياح يسار على عنصر مصفوفة lhs
بعدد rhs
بت، وتُنشئ مصفوفة result
.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | lhs |
مصفوفة من النوع الصحيح | (C1) |
(I2) | rhs |
مصفوفة من النوع الصحيح | (C1) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
متوتر لنوع العدد الصحيح | (C1) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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
. - (ج5)
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
عناصر individual في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) |
القيود
- (ج1)
0 < size(inputs)
. - (ج2)
type(inputs...) = type(results...)
. - (ج3)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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) |
القيود
- (ج1)
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)
قد يختلفان، بخلاف ذلك.
- (ج2)
permutation
هو تبديلrange(rank(operand))
. - (ج3)
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 |
(ج3) |
(I4) | lower |
ثابت من النوع i1 |
|
(I5) | unit_diagonal |
ثابت من النوع i1 |
|
(I6) | transpose_a |
تعداد NO_TRANSPOSE وTRANSPOSE وADJOINT |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
مأخذ النقطة العائمة أو النوع المركّب أو المتسّع الكمّي حسب كل متسابق | (C1) |
القيود
- (ج1)
baseline_element_type(a) = baseline_element_type(b)
. - (ج2)
2 <= rank(a) = rank(b) = R
. - (ج3) يتم تعريف العلاقة بين
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: [1.0, 2.0]
// %val1: (3)
%result = "stablehlo.tuple"(%val0, %val1) : (tensor<2xf32>, tuple<tensor<i32>>) -> tuple<tensor<2xf32>, tuple<tensor<i32>>>
// %result: ([1.0, 2.0], (3))
uniform_dequantize
الدلالات
تُجري عملية تحويل عنصرية لمجموعة السلاسل التسلسلية المحوَّلة إلى كثافة operand
إلى مجموعة سلاسل تسلسلية result
ذات نقطة عائمة وفقًا لمَعلمات التكثيف التي يحدّدها نوع operand
.
اسم "result = dequantize(operand)
" رسميًا.
مدخلات
التصنيف | الاسم | النوع | القيود |
---|---|---|---|
(I1) | operand |
متّجه مُكمّر | (C1)، (C2) |
النواتج
الاسم | النوع | القيود |
---|---|---|
result |
مصفوفة من النوع النقطة العائمة | (C1)، (C2) |
القيود
- (ج1)
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) |
القيود
- (ج1)
shape(operand) = shape(result)
. - (ج2)
expressed_type(result) = is_float(operand) ? element_type(operand) : expressed_type(operand)
.
أمثلة
// %operand: [4.0, 15.0]
%result = "stablehlo.uniform_quantize"(%operand) : (tensor<2xf32>) -> tensor<2x!quant.uniform<i8:f32:0, {0.1:-30,0.5:-20}>>
// %result: [10, 10]
// %operand: [10, 10]
%result = "stablehlo.uniform_quantize"(%operand) : (tensor<2x!quant.uniform<i8:f32:0, {0.1:-30,0.5:-20}>>) -> tensor<2x!quant.uniform<i8:f32:0, {0.1:-20,0.2:-30}>>
// %result: [20, 45]
بينما
الدلالات
تُنشئ القيمة الناتجة من تنفيذ الدالة body
0 مرة أو أكثر بينما تعرِض الدالة
cond
القيمة true
. بشكل أكثر رسمية، يمكن التعبير عن الدلالة باستخدام بناء جملة بايثون على النحو التالي:
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])
. - (ج3)
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) |
القيود
- (ج1)
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 قبل التسلسل.
عمليات الأشكال
من حالات الاستخدام الشائعة في المنتدى استخدام عمليات معيّنة من لهجات "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 بعضها مع بعض من خلال قنوات SttableHLO. يتم تمثيل القناة بمعرّف موجب من النوع
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>
}
الرمز الموسيقي
لوصف البنية، يستخدم هذا المستند بنية EBNF
المعدَّلة وفقًا لمعيار ISO (ISO/IEC 14977:1996،
Wikipedia)،
مع إجراء تعديلَين: 1) يتم تعريف القواعد باستخدام ::=
بدلاً من =
،
2) يتم التعبير عن التسلسل باستخدام الترادف بدلاً من ,
.
لوصف الدلالات (أي ضمن أقسام "الأنواع" و"الثوابت" و"العمليات")، نستخدم صِيَغًا تستند إلى بنية بايثون الموسّعة مع إتاحة التعبير بدقة عن عمليات الصفيف كما هو موضّح أدناه. يفيد ذلك في التعامل مع مقتطفات الرمز البرمجي الصغيرة، ولكن في بعض الحالات النادرة التي نحتاج فيها إلى مقتطفات أكبر من التعليمات البرمجية، نستخدم بناء جملة vanilla 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)]
هو [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
وy
هيQuantizedTensorElementType
، لا يسري العرض الترويجي إلا على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
. إذا لم تكنx
TensorElementType
، يتم عرضNone
.تعرِض الدالة
min_value(x: Type) -> Value
الحد الأدنى للقيمة الممكنة لسمةTensorElementType
. إذا لم تكنx
TensorElementType
، يتم عرضNone
.member_name(x: Value | Placeholder | Type) -> Any
. متاحة لجميع تعريفات الأعضاءmember_name
من جميع الأنواع. على سبيل المثال، تعرضtensor_element_type(x)
الجزء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 للفهرسة في مصفوفات الخلاصات والمصفوفات المقسّمة والصفوف.
يتمّ تعريف
to_destination_type(x: Value, destination_type: Type) -> Value
على كثافات تشكلت من tensors، وتُعرِض القيمة المحوَّلة لـ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
في الحالات الأخرى. إذا لم يكُن هناك عناصر في Tensor، يُحتسب ذلك على أنّه "جميعها متساوية بعضها مع بعض"، أي أنّ الدالة تعرض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 في الأنواع التي تم تقليلها كميًا. يحدد المدخلات الكميّة في أنواعها المعب عنها ويجري الحساب في عدد عائم. يجب أن يكون نوع العنصر float lhs tensor والنوع الموضح لنسور 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>
. إذا كان معامل الشكل ثابتًا،
يمكن التحقق من ذلك بشكل ثابت. إذا كان شكل النتيجة ديناميكيًا بالكامل، لن يكون هناك
عدم تطابق.