مواصفات StableHLO

‫StableHLO هي مجموعة عمليات للعمليات العالية المستوى (HLO) في نماذج تعلُّم الآلة (ML). تعمل StableHLO كطبقة نقل بين أُطر عمل ومترجمات مختلفة لتعلُّم الآلة: تتوافق أُطر عمل تعلُّم الآلة التي تنتج برامج StableHLO مع مترجمات تعلُّم الآلة التي تستهلك برامج StableHLO.

هدفنا هو تبسيط عملية تطوير تعلُّم الآلة وتسريعها من خلال توفير المزيد من إمكانية التشغيل التفاعلي بين مختلف أُطر عمل تعلُّم الآلة (مثل TensorFlow وJAX وPyTorch) ومترجمات تعلُّم الآلة (مثل XLA وIREE). لتحقيق ذلك، يقدّم هذا المستند مواصفات للغة البرمجة StableHLO.

يحتوي هذا المستند على ثلاثة أقسام رئيسية. أولاً، يصف قسم البرامج بنية برامج StableHLO التي تتألف من دوال StableHLO التي تتألف بدورها من عمليات StableHLO. ضمن هذه البنية، يحدّد القسم العمليات دلالات العمليات الفردية. يوفّر قسم التنفيذ دلالات لجميع العمليات التي يتم تنفيذها معًا ضمن برنامج. أخيرًا، يتناول قسم الترميز الترميز المستخدَم في جميع أنحاء المواصفات.

للاطّلاع على المواصفات من إصدار سابق من StableHLO، افتح المستودع في الإصدار الذي تم وضع علامة عليه. على سبيل المثال، مواصفات الإصدار 0.19.0 من StableHLO. للاطّلاع على التغييرات التي حدثت في كل إصدار ثانوي من StableHLO، يُرجى الرجوع إلى سجلّ الإصدارات في VhloDialect.td.

البرامج

Program ::= {Func}

تتألف برامج StableHLO من عدد عشوائي من دوال StableHLO. في ما يلي مثال على برنامج يتضمّن الدالة @main التي تتضمّن 3 مدخلات (%image و%weights و%bias) ومخرجًا واحدًا. يتضمّن نص الدالة 6 عمليات.

func.func @main(
  %image: tensor<28x28xf32>,
  %weights: tensor<784x10xf32>,
  %bias: tensor<1x10xf32>
) -> tensor<1x10xf32> {
  %0 = "stablehlo.reshape"(%image) : (tensor<28x28xf32>) -> tensor<1x784xf32>
  %1 = "stablehlo.dot"(%0, %weights) : (tensor<1x784xf32>, tensor<784x10xf32>) -> tensor<1x10xf32>
  %2 = "stablehlo.add"(%1, %bias) : (tensor<1x10xf32>, tensor<1x10xf32>) -> tensor<1x10xf32>
  %3 = "stablehlo.constant"() {value = dense<0.0> : tensor<1x10xf32>} : () -> tensor<1x10xf32>
  %4 = "stablehlo.maximum"(%2, %3) : (tensor<1x10xf32>, tensor<1x10xf32>) -> tensor<1x10xf32>
  "func.return"(%4): (tensor<1x10xf32>) -> ()
}

الدوال

Func        ::= 'func' '.' 'func' FuncId FuncInputs FuncOutputs '{' FuncBody '}'
FuncInputs  ::= '(' [FuncInput {',' FuncInput}] `)`
FuncInput   ::= ValueId ':' ValueType
FuncOutputs ::= ['->' FuncOutput, {',' FuncOutput}]
FuncOutput  ::= ValueType
FuncBody    ::= {Op}

تحتوي دوال StableHLO (المعروفة أيضًا باسم الدوال المُسمّاة) على معرّف ومدخلات/مخرجات ونص. في المستقبل، نخطّط لإضافة بيانات وصفية إضافية للدوال من أجل تحقيق توافق أفضل مع HLO (#425 و#626 و#740 و#744).

المعرّفات

FuncId  ::= '@' letter {letter | digit}
ValueId ::= '%' digit {digit}
          | '%' letter {letter | digit}
letter  ::= 'a' | ... | 'z' | 'A' | ... | 'Z' | '_'
digit   ::= '0' | ... | '9'

معرّفات StableHLO تشبه المعرّفات في العديد من لغات البرمجة، ولكن مع اختلافَين: 1) تحتوي جميع المعرّفات على رموز مميزة تميّز أنواع المعرّفات المختلفة، 2) يمكن أن تكون معرّفات القيم رقمية بالكامل لتسهيل إنشاء برامج StableHLO.

الأنواع

Type         ::= ValueType | NonValueType
ValueType    ::= TensorType | QuantizedTensorType | TokenType | TupleType | BufferType
NonValueType ::= TensorElementType | QuantizedTensorElementType | FunctionType | StringType

يتم تصنيف أنواع StableHLO إلى أنواع القيم (التي تُعرف أيضًا باسم أنواع الدرجة الأولى) التي تمثّل قيم StableHLO وأنواع غير القيم التي تصف عناصر البرنامج الأخرى. تتشابه أنواع StableHLO مع الأنواع في العديد من لغات البرمجة، ولكنّ السمة الرئيسية التي تميّزها هي طبيعتها الخاصة بالمجال، ما يؤدي إلى بعض النتائج غير العادية (مثل أنّ الأنواع العددية ليست أنواع قيم).

TensorType ::= 'tensor' '<' Shape TensorElementType '>'
Shape ::= {DimensionSize 'x'}
DimensionSize ::= digit {digit} | '?'

تمثّل أنواع الموتر الموترات، أي المصفوفات المتعددة الأبعاد. تتضمّن هذه المصفوفات شكلاً ونوع عنصر، حيث يمثّل الشكل أحجام الأبعاد غير السالبة أو غير المعروفة بترتيب تصاعدي للأبعاد المقابلة (المعروفة أيضًا باسم المحاور) المرقمة من 0 إلى R-1. يُطلق على عدد الأبعاد R اسم الرتبة. على سبيل المثال، tensor<2x3xf32> هو نوع موتر ذو شكل 2x3 ونوع عنصر f32. يتضمّن هذا المثال بُعدَين (أو محورَين)، وهما البُعد 0 والبُعد 1، ويبلغ حجم كل منهما 2 و3 على التوالي. ويبلغ ترتيبه 2.

يمكن أن تكون الأشكال غير معروفة جزئيًا أو كليًا (ديناميكية)، على سبيل المثال، tensor<?x2xf64> غير معروف جزئيًا وtensor<?x?xf64> غير معروف كليًا. يتم تمثيل أحجام السمات الديناميكية باستخدام ?. لا يمكن إلغاء ترتيب الأشكال.

في المستقبل، نخطّط لاستكشاف إمكانية توسيع أنواع الموتر لتتجاوز أحجام الأبعاد وأنواع العناصر، مثلاً لتشمل التنسيقات (#629) والندرة (#1078).

QuantizedTensorType ::= 'tensor' '<' Shape QuantizedTensorElementType '>'
QuantizedTensorElementType ::= '!quant.uniform' '<'
                  QuantizationStorageType
                  ['<' QuantizationStorageMin ':' QuantizationStorageMax '>']
                  ':' QuantizationExpressedType
                  [':' QuantizationDimension]
                  ',' QuantizationParameters '>'
QuantizationStorageType ::= IntegerType
QuantizationStorageMin ::= IntegerLiteral
QuantizationStorageMax ::= IntegerLiteral
QuantizationExpressedType ::= FloatType
QuantizationDimension ::= IntegerLiteral
QuantizationParameters ::= QuantizationParameter
                         | '{' QuantizationParameter {',' QuantizationParameter} '}'
QuantizationParameter ::= QuantizationScale [':' QuantizationZeroPoint]
QuantizationScale ::= FloatLiteral
QuantizationZeroPoint ::= IntegerLiteral
الاسم النوع القيود
storage_type نوع العدد الصحيح (C1-C3)، (C8)
storage_min ثابت عدد صحيح (C1)، (C3)، (C7)
storage_max ثابت عدد صحيح (C2)، (C3)، (C7)
expressed_type نوع النقطة العائمة (C4)
quantization_dimension ثابت عدد صحيح اختياري (C10-C12)
scales عدد متغير من الثوابت ذات الفاصلة العائمة (C4-C6), (C9), (C10), (C13)
zero_points عدد متغير من الثوابت الصحيحة (C7-C9)

تمثّل أنواع العناصر الكمية قيمًا عددية لنوع التخزين في النطاق من storage_min إلى storage_max (شاملة) التي تتوافق مع قيم الفاصلة العائمة للنوع المعروض. بالنسبة إلى قيمة عدد صحيح معيّنة i، يمكن احتساب قيمة الفاصلة العائمة المقابلة f على النحو f = (i - zero_point) * scale، حيث يُطلق على scale وzero_point اسم مَعلمات التكميم. تكون القيمتان "storage_min" و"storage_max" اختياريتين في قواعد اللغة، ولكن القيمتان التلقائيتان لهما هما "min_value(storage_type)" و"max_value(storage_type)" على التوالي. تتضمّن أنواع العناصر المحدّدة الكمية القيود التالية:

  • (C1) type(storage_min) = storage_type
  • (C2) type(storage_max) = storage_type
  • (C3) min_value(storage_type) <= storage_min < storage_max <= max_value(storage_type)
  • (C4) type(scales...) = expressed_type
  • (C5) 0 < scales.
  • (C6) is_finite(scales...)
  • (C7) storage_min <= zero_points <= storage_max
  • (C8) type(zero_points...) = storage_type
  • (C9) size(scales) = size(zero_points).
  • (C10) إذا كان is_empty(quantization_dimension)، إذًا size(scales) = 1.
  • (C11) 0 <= quantization_dimension.

في الوقت الحالي، QuantizationScale هو ثابت نقطة عائمة، ولكن هناك اهتمام كبير بالمقاييس المستندة إلى الأعداد الصحيحة، والتي يتم تمثيلها باستخدام المضاعفات والتحولات. نخطّط لاستكشاف هذه المشكلة في المستقبل القريب (#1404).

تتواصل المناقشة حول دلالات QuantizationZeroPoint، بما في ذلك النوع والقيم وما إذا كان يمكن أن تكون هناك نقطة صفر واحدة أو نقاط صفر متعددة محتملة في نوع موتر كمي. استنادًا إلى نتائج هذه المناقشة، قد تتغيّر المواصفات المتعلقة بالنقاط الصفرية في المستقبل (#1405).

تتضمّن مناقشة أخرى مستمرة دلالات QuantizationStorageMin وQuantizationStorageMax لتحديد ما إذا كان يجب فرض أي قيود على هذه القيم وعلى قيم الموترات المكمَّمة (#1406).

أخيرًا، نخطّط لاستكشاف طرق لعرض المقاييس غير المعروفة ونقاط الصفر، على غرار ما نخطّط له بشأن استكشاف طرق لعرض أحجام السمات غير المعروفة (#1407).

تمثّل أنواع الموتر الكمي الموترات التي تحتوي على عناصر كمية. تتشابه هذه الموترات تمامًا مع الموترات العادية، باستثناء أنّ عناصرها لها أنواع عناصر محدودة الكمية، بدلاً من أنواع العناصر العادية.

في الموترات الكمية، يمكن أن يكون التكميم لكل موتر، أي أن يكون هناك scale وzero_point واحد لكل الموتر، أو يمكن أن يكون لكل محور، أي أن يكون هناك عدة scales وzero_points، زوج واحد لكل شريحة من بُعد معيّن quantization_dimension. بشكل أكثر رسمية، في موتر t مع التكميم لكل محور، هناك dim(t, quantization_dimension) شرائح من quantization_dimension: t[:, ..., 0, ..., :], t[:, ..., 1, ..., :]، إلخ. تستخدم جميع العناصر في الشريحة i القيمتين scales[i] وzero_points[i] كمعلمات تكميم. تتضمّن أنواع الموتر الكمّي القيود التالية:

  • بالنسبة إلى التكميم على مستوى كل موتر:
    • ما مِن قيود إضافية.
  • بالنسبة إلى التكميم لكل محور:
    • (C12) quantization_dimension < rank(self)
    • (C13) dim(self, quantization_dimension) = size(scales).
TokenType ::= 'token'

تمثّل أنواع الرموز المميزة الرموز المميزة، أي القيم غير الشفافة التي يتم إنتاجها واستهلاكها بواسطة بعض العمليات. تُستخدَم الرموز المميزة لفرض ترتيب التنفيذ على العمليات كما هو موضّح في قسم التنفيذ.

TupleType ::= 'tuple' '<' TupleElementTypes '>'
TupleElementTypes ::= [ValueType {',' ValueType}]

تمثّل أنواع المخزن المؤقت المخازن المؤقتة. على سبيل المثال، في XLA، تكون المخازن المؤقتة عبارة عن صفائف متعدّدة الأبعاد ذات مساحة تخزين متسقة. على غرار أنواع الموتر، تحتوي أنواع المخزن المؤقت على شكل ونوع عنصر، حيث يمثّل الشكل أحجام الأبعاد غير السالبة أو غير المعروفة بترتيب تصاعدي للأبعاد المقابلة (التي تُعرف أيضًا باسم المحاور) المرقمة من 0 إلى R-1. يُطلق على عدد الأبعاد R اسم الرتبة. على سبيل المثال، memref<2x3xf32> هو نوع مخزن مؤقت ذو شكل 2x3 ونوع عنصر f32. تحتوي هذه المصفوفة على بُعدَين (أو محورَين) - البُعد 0 والبُعد 1 - ويبلغ حجم كل منهما 2 و3. ويبلغ ترتيبه 2.

يمكن تخصيص المخازن المؤقتة باستخدام custom_call إلى CreateBuffer أو Pin، ويمكن إلغاء تخصيصها باستخدام custom_call إلى Unpin. يمكن لمشغّلي custom_call فقط قراءة المحتوى وكتابته داخل المخازن المؤقتة. لمزيد من التفاصيل، يُرجى الاطّلاع على custom_call.

تمثّل أنواع الصفوف الصفوف، أي القوائم غير المتجانسة. تُعدّ الصفوف ميزة قديمة تتوفّر فقط للتوافق مع HLO. في HLO، يتم استخدام الصفوف لتمثيل المدخلات والمخرجات المتغيرة العدد. في StableHLO، تتوفّر إمكانية استخدام المدخلات والمخرجات المتغيرة بشكل أصلي، والاستخدام الوحيد للمجموعات في StableHLO هو تمثيل واجهة ABI الخاصة بـ HLO بشكل شامل، حيث يمكن أن تختلف T وtuple<T> وtuple<tuple<T>> بشكل كبير حسب التنفيذ المحدّد. في المستقبل، نخطّط لإجراء تغييرات على HLO ABI التي قد تسمح لنا بإزالة أنواع الصفوف من StableHLO (#598).

TensorElementType ::= BooleanType | IntegerType | FloatType | ComplexType
BooleanType ::= 'i1'
IntegerType ::= SignedIntegerType | UnsignedIntegerType
SignedIntegerType ::= 'si2' | 'si4' | 'si8' | 'si16' | 'si32' | 'si64'
UnsignedIntegerType ::= 'ui2' | 'ui4' | 'ui8' | 'ui16' | 'ui32' | 'ui64'
FloatType ::= 'f4E2M1FN' | 'f6E2M3FN' | 'f6E3M2FN' | 'f8E3M4' | 'f8E4M3'
            | 'f8E4M3FN' | 'f8E4M3FNUZ' | 'f8E4M3B11FNUZ' | 'f8E5M2'
            | 'f8E5M2FNUZ' | 'f8E8M0FNU' | 'bf16' | 'f16' | 'f32' | 'f64'
TensorFloat32 ::= 'tf32'
ComplexType ::= 'complex' '<' ComplexElementType '>'
ComplexElementType ::= 'f32' | 'f64'

تمثّل أنواع العناصر عناصر أنواع الموتر. وعلى عكس العديد من لغات البرمجة، لا تُعدّ هذه الأنواع من الدرجة الأولى في StableHLO. وهذا يعني أنّه لا يمكن لبرامج StableHLO تمثيل قيم هذه الأنواع مباشرةً (نتيجةً لذلك، من الشائع تمثيل القيم العددية من النوع T بقيم موترات صفرية الأبعاد من النوع tensor<T>).

  • يمثّل النوع المنطقي القيم المنطقية true وfalse.
  • يمكن أن تكون أنواع الأعداد الصحيحة إما مُوقَّعة (si) أو غير مُوقَّعة (ui)، ويجب أن يكون لها أحد عروض البتات المتوافقة (2 أو 4 أو 8 أو 16 أو 32 أو 64). تمثّل أنواع siN المُوقَّعة قيم الأعداد الصحيحة من -2^(N-1) إلى 2^(N-1)-1 ضِمنًا، وتمثّل أنواع uiN غير المُوقَّعة قيم الأعداد الصحيحة من 0 إلى 2^N-1 ضِمنًا.
  • يمكن أن تكون أنواع الأعداد العشرية واحدة مما يلي:
  • تمثّل الأنواع المركّبة قيمًا مركّبة تتضمّن جزءًا حقيقيًا وجزءًا تخيليًا من نوع العنصر نفسه. تشمل الأنواع المعقّدة المتوافقة complex<f32> (يكون كلا الجزأين من النوع f32) وcomplex<f64> (يكون كلا الجزأين من النوع f64).
FunctionType ::= '(' InputTypes ')' '->' '(' OutputTypes ')'
InputTypes ::= [ValueType {',' ValueType}]
OutputTypes ::= [ValueType {',' ValueType}]

تمثّل أنواع الدوال الدوال المُسمّاة والدوال المجهولة. وتتضمّن أنواع الإدخال (قائمة الأنواع على الجانب الأيمن من ->) وأنواع الإخراج (قائمة الأنواع على الجانب الأيسر من ->). وفي العديد من لغات البرمجة، تكون أنواع الدوال من الدرجة الأولى، ولكن ليس في StableHLO.

StringType ::= 'string'

يمثّل نوع السلسلة تسلسلات البايتات. على عكس العديد من لغات البرمجة، لا يُعدّ نوع السلسلة من الأنواع الأساسية في StableHLO، ويُستخدم فقط لتحديد البيانات الوصفية الثابتة لعناصر البرنامج.

العمليات

تمثّل عمليات StableHLO (المعروفة أيضًا باسم العمليات) مجموعة مغلقة من العمليات العالية المستوى في نماذج تعلُّم الآلة. كما ذكرنا أعلاه، فإنّ بنية StableHLO مستوحاة بشكل كبير من MLIR، وهي ليست بالضرورة البديل الأكثر ملاءمة، ولكنها الأنسب لتحقيق هدف StableHLO المتمثل في توفير المزيد من إمكانية التشغيل التفاعلي بين أُطر تعلُّم الآلة ومترجمات تعلُّم الآلة.

Op            ::= [OpOutputs] OpName OpInputs ':' OpSignature
OpName        ::= '"' 'stablehlo' '.' OpMnemonic '"'
OpMnemonic    ::= 'abs' | 'add' | ...

عمليات StableHLO (المعروفة أيضًا باسم العمليات) لها اسم ومدخلات/مخرجات وتوقيع. يتألف الاسم من البادئة stablehlo. وعبارة سهلة التذكّر تحدّد بشكل فريد إحدى العمليات المتوافقة. يمكنك الاطّلاع أدناه على قائمة شاملة بجميع العمليات المتوافقة.

OpInputs        ::= OpInputValues OpInputFuncs OpInputAttrs
OpInputValues   ::= '(' [OpInputValue {',' OpInputValue}] ')'
OpInputValue    ::= ValueId
OpInputFuncs    ::= ['(' OpInputFunc {',' OpInputFunc} ')']
OpInputAttrs    ::= ['{' OpInputAttr {',' OpInputAttr} '}']
OpOutputs       ::= [OpOutput {',' OpOutput} '=']
OpOutput        ::= ValueId

تستهلك العمليات مدخلات وتنتج مخرجات. يتم تصنيف المدخلات إلى قيم إدخال (يتم احتسابها أثناء التنفيذ) ودوال إدخال (يتم توفيرها بشكل ثابت، لأنّ الدوال ليست قيمًا من الدرجة الأولى في StableHLO) وسمات إدخال (يتم توفيرها أيضًا بشكل ثابت). يعتمد نوع المدخلات والمخرجات التي تستهلكها العملية وتنتجها على الاختصار الخاص بها. على سبيل المثال، تستهلك العملية add قيمتَي إدخال وتنتج قيمة إخراج واحدة. في المقابل، تستهلك العملية select_and_scatter 3 قيم إدخال و2 من دوال الإدخال و3 سمات إدخال.

OpInputFunc ::= '{' Unused FuncInputs ':' FuncBody '}'
Unused      ::= '^' digit {digit}
              | '^' letter {letter | digit}

دوال الإدخال (المعروفة أيضًا باسم الدوال المجهولة) تشبه إلى حد كبير الدوال المُسمّاة، باستثناء ما يلي: 1) ليس لها معرّف (ومن هنا جاءت تسميتها "مجهولة")، 2) لا تحدّد أنواع الإخراج (يتم استنتاج أنواع الإخراج من عملية return داخل الدالة).

يتضمّن بناء جملة دوال الإدخال جزءًا غير مستخدَم حاليًا (راجِع Unusedالإنتاج أعلاه) وهو مخصّص للتوافق مع MLIR. في MLIR، هناك مفهوم أكثر عمومية يُعرف باسم "المناطق" التي يمكن أن تتضمّن عدة "مجموعات" من العمليات المرتبطة ببعضها البعض من خلال عمليات الانتقال. تحتوي هذه الأقسام على معرّفات تتوافق مع Unused الإنتاج، ما يتيح التمييز بينها. لا يتضمّن StableHLO عمليات الانتقال، لذا فإنّ الجزء المقابل من بنية MLIR غير مستخدَم (ولكنّه لا يزال متوفّرًا).

OpInputAttr      ::= OpInputAttrName '=' OpInputAttrValue
OpInputAttrName  ::= letter {letter | digit}
OpInputAttrValue ::= Constant

تحتوي سمات الإدخال على اسم وقيمة، وتكون القيمة إحدى الثوابت المسموح بها. وهي الطريقة الأساسية لتحديد البيانات الوصفية الثابتة لعناصر البرنامج. على سبيل المثال، تستخدم العملية concatenate السمة dimension لتحديد البُعد الذي يتم فيه ربط قيم الإدخال. وبالمثل، تستخدم العملية slice سمات متعددة، مثل start_indices وlimit_indices، لتحديد الحدود المستخدَمة لتقسيم قيمة الإدخال.

في الوقت الحالي، تحتوي برامج StableHLO المتداولة أحيانًا على سمات غير موضّحة في هذا المستند. في المستقبل، نخطّط إما لدمج هذه السمات في مجموعة عمليات StableHLO أو منع ظهورها في برامج StableHLO. في الوقت الحالي، إليك قائمة بهذه السمات:

  • layout (#629)
  • mhlo.frontend_attributes (#628).
  • mhlo.sharding (#619)
  • output_operand_aliases (#740).
  • بيانات وصفية للموقع الجغرافي (#594)
OpSignature ::= '(' [ValueType {',' ValueType}] ')' '->' '(' [ValueType {',' ValueType}] ')'

تتألف توقيع العملية من أنواع جميع قيم الإدخال (قائمة الأنواع على الجانب الأيمن من ->) وأنواع جميع قيم الإخراج (قائمة الأنواع على الجانب الأيسر من ->). وبشكل عام، تكون أنواع الإدخال غير ضرورية، كما أنّ أنواع الإخراج تكون غير ضرورية في معظم الحالات (لأنّه بالنسبة إلى معظم عمليات StableHLO، يمكن استنتاج أنواع الإخراج من الإدخالات). ومع ذلك، فإنّ توقيع العملية هو جزء من بنية StableHLO عمدًا لتحقيق التوافق مع MLIR.

في ما يلي مثال على عملية لها اختصار select_and_scatter. تستهلك هذه الدالة 3 قيم إدخال (%operand و%source و%init_value) ودالتَي إدخال و3 سمات إدخال (window_dimensions وwindow_strides وpadding). يُرجى العِلم أنّ توقيع العملية يتضمّن فقط أنواع قيم الإدخال (وليس أنواع دوال الإدخال والسمات التي يتم توفيرها في السطر).

%result = "stablehlo.select_and_scatter"(%operand, %source, %init_value) ({
  ^bb0(%arg0: tensor<i32>, %arg1: tensor<i32>):
    %0 = "stablehlo.compare"(%arg0, %arg1) {
      comparison_direction = #stablehlo<comparison_direction GE>
    } : (tensor<i32>, tensor<i32>) -> tensor<i1>
    "stablehlo.return"(%0) : (tensor<i1>) -> ()
}, {
  ^bb0(%arg0: tensor<i32>, %arg1: tensor<i32>):
    %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i32>, tensor<i32>) -> tensor<i32>
    "stablehlo.return"(%0) : (tensor<i32>) -> ()
}) {
  window_dimensions = dense<[3, 1]> : tensor<2xi64>,
  window_strides = dense<[2, 1]> : tensor<2xi64>,
  padding = dense<[[0, 1], [0, 0]]> : tensor<2x2xi64>
} : (tensor<4x2xi32>, tensor<2x2xi32>, tensor<i32>) -> tensor<4x2xi32>

الثوابت

Constant ::= BooleanConstant
           | IntegerConstant
           | FloatConstant
           | ComplexConstant
           | TensorConstant
           | QuantizedTensorConstant
           | StringConstant
           | EnumConstant

تحتوي ثوابت StableHLO على قيمة حرفية ونوع يمثّلان معًا قيمة StableHLO. بشكل عام، يكون النوع جزءًا من بنية الثابت، إلا في الحالات التي يكون فيها النوع واضحًا (على سبيل المثال، يكون للثابت المنطقي النوع i1 بشكل واضح، بينما يمكن أن يكون للثابت الصحيح أنواع متعددة محتملة).

BooleanConstant ::= BooleanLiteral
BooleanLiteral  ::= 'true' | 'false'

تمثّل الثوابت المنطقية القيم المنطقية true وfalse. تكون الثوابت المنطقية من النوع i1.

IntegerConstant   ::= IntegerLiteral ':' IntegerType
IntegerLiteral    ::= ['-' | '+'] DecimalDigits
                    | ['-' | '+'] '0x' HexadecimalDigits
DecimalDigits     ::= decimalDigit {decimalDigit}
HexadecimalDigits ::= hexadecimalDigit {hexadecimalDigit}
decimalDigit      ::= '0' | ... | '9'
hexadecimalDigit  ::= decimalDigit | 'a' | ... | 'f' | 'A' | ... | 'F'

تمثّل الثوابت الصحيحة قيمًا صحيحة من خلال سلاسل تستخدم الترميز العشري أو السداسي العشري. لا تتوفّر قواعد أخرى، مثل الثنائية أو الثمانية. تخضع الثوابت الصحيحة للقيود التالية:

  • (C1) is_wellformed(integer_literal, integer_type)
FloatConstant  ::= FloatLiteral ':' FloatType
FloatLiteral   ::= SignPart IntegerPart FractionalPart ScientificPart
                 | '0x' [HexadecimalDigits]
SignPart       ::= ['-' | '+']
IntegerPart    ::= DecimalDigits
FractionalPart ::= ['.' [DecimalDigits]]
ScientificPart ::= [('e' | 'E') ['-' | '+'] DecimalDigits]

تمثّل الثوابت ذات الفاصلة العائمة قيم الفاصلة العائمة من خلال سلاسل تستخدم الترميز العشري أو العلمي. بالإضافة إلى ذلك، يمكن استخدام الترميز السداسي العشري لتحديد وحدات البت الأساسية مباشرةً في تنسيق الفاصلة العائمة للنوع المقابل. تخضع الثوابت ذات الفاصلة العائمة للقيود التالية:

  • (C1) في حال استخدام ترميز غير سداسي عشري، is_wellformed(float_literal, float_type).
  • (C2) إذا تم استخدام التمثيل بنظام العدّ الستة عشري، size(hexadecimal_digits) = num_bits(float_type) / 4.
ComplexConstant ::= ComplexLiteral ':' ComplexType
ComplexLiteral  ::= '(' RealPart ',' ImaginaryPart ')'
RealPart        ::= FloatLiteral
ImaginaryPart   ::= FloatLiteral

تمثّل الثوابت المركّبة القيم المركّبة باستخدام قوائم تتضمّن جزءًا حقيقيًا (يأتي أولاً) وجزءًا تخيُّليًا (يأتي ثانيًا). على سبيل المثال، يمثّل (1.0, 0.0) : complex<f32> 1.0 + 0.0i، ويمثّل (0.0, 1.0) : complex<f32> 0.0 + 1.0i. ويتم تحديد ترتيب تخزين هذه الأجزاء في الذاكرة حسب التنفيذ. تتضمّن الثوابت المعقّدة القيود التالية:

  • (C1) is_wellformed(real_part, complex_element_type(complex_type))
  • (C2) is_wellformed(imaginary_part, complex_element_type(complex_type))
TensorConstant ::= TensorLiteral ':' TensorType
TensorLiteral  ::= 'dense' '<' (DenseLiteral | ElementLiteral) '>'
DenseLiteral   ::= DenseDimension | DenseElements
DenseDimension ::= '[' [DenseLiteral {',' DenseLiteral}] ']'
DenseElements  ::= [ElementLiteral {',' ElementLiteral}]
ElementLiteral ::= BooleanLiteral | IntegerLiteral | FloatLiteral | ComplexLiteral

تمثّل ثوابت الموتر قيم الموتر باستخدام قوائم متداخلة محدّدة من خلال ترميز NumPy. على سبيل المثال، يمثّل dense<[[1, 2, 3], [4, 5, 6]]> : tensor<2x3xi32> قيمة موتر مع التعيين التالي من الفهارس إلى العناصر: {0, 0} => 1 و{0, 1} => 2 و{0, 2} => 3 و{1, 0} => 4 و{1, 1} => 5 و{1, 2} => 6. ويكون ترتيب تخزين هذه العناصر في الذاكرة بعد ذلك محدّدًا حسب التنفيذ. تخضع ثوابت الموتر للقيود التالية:

  • (C1) has_syntax(tensor_literal, element_type(tensor_type))، حيث:
    • has_syntax(element_literal: Syntax, element_type: Type) = is_wellformed(element_literal, type).
    • has_syntax(tensor_literal: List, element_type: Type) = has_syntax(tensor_literal..., element_type).
  • (C2) has_shape(tensor_literal, shape(tensor_type))، حيث:
    • has_shape(element_literal: Syntax, []) = true.
    • has_shape(tensor_literal: List, shape: List) = size(tensor_literal) = shape[0] and has_shape(tensor_literal..., shape[1:]).
    • في ما عدا ذلك، false.
QuantizedTensorConstant ::= QuantizedTensorLiteral ':' QuantizedTensorType
QuantizedTensorLiteral  ::= 'dense' '<' (DenseLiteral | ElementLiteral) '>'

تمثّل ثوابت الموتر الكمّي قيم الموتر الكمّي باستخدام الترميز نفسه المستخدَم مع ثوابت الموتر، مع تحديد العناصر كثوابت من نوع التخزين. تتضمّن ثوابت الموتر الكمي القيود التالية:

  • (C1) has_syntax(quantized_tensor_literal, storage_type(quantized_tensor_type))
  • (C2) has_shape(quantized_tensor_literal, shape(quantized_tensor_type))
StringConstant  ::= StringLiteral
StringLiteral   ::= '"' {stringCharacter | escapeSequence} '"'
stringCharacter ::= all ASCII characters except '\00', '\01', ... '\1f' and '"'
escapeSequence  ::= '\' ('"' | '\' | 'n' | 't' | (hexadecimalDigit hexadecimalDigit))

تتألف سلاسل الأحرف الحرفية من وحدات بايت محددة باستخدام أحرف ASCII وتسلسلات الهروب. وهي مستقلة عن الترميز، لذا فإنّ تفسير هذه البايتات يعتمد على التنفيذ. تكون السلاسل الحرفية من النوع string.

العمليات

abs

الدلالات

تُجري هذه الدالة عملية القيمة المطلقة لكل عنصر في الموتر operand وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأعداد الصحيحة الموقَّعة: معامل الأعداد الصحيحة.
  • بالنسبة إلى الأرقام العشرية: abs من IEEE-754
  • بالنسبة إلى الأعداد المركّبة: المعامل المركّب
  • بالنسبة إلى الأنواع الكمية: dequantize_op_quantize(abs, operand, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من نوع عدد صحيح موقّع أو عدد نقطة عائمة أو عدد مركّب أو موتر كمّي لكل موتر (C1-C2)

النواتج

الاسم النوع القيود
result متّجه من نوع عدد صحيح موقّع أو عدد نقطة عائمة أو متّجه كمّي لكل متّجه (C1-C2)

القيود

  • (C1) shape(result) = shape(operand)
  • يتم تعريف (C2) baseline_element_type(result) على النحو التالي:
    • complex_element_type(element_type(operand)) إذا is_complex(operand)
    • baseline_element_type(operand) في ما عدا ذلك.

أمثلة

// %operand: [-2, 0, 2]
%result = "stablehlo.abs"(%operand) : (tensor<3xi32>) -> tensor<3xi32>
// %result: [2, 0, 2]

 مزيد من الأمثلة

إضافة

الدلالات

تُجري هذه الدالة عملية جمع على مستوى العناصر لموترَين lhs وrhs وتنتج موترًا result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى القيم المنطقية: OR المنطقية
  • بالنسبة إلى الأعداد الصحيحة: جمع الأعداد الصحيحة
  • بالنسبة إلى الأرقام العشرية: addition من IEEE-754
  • بالنسبة إلى الأعداد المركّبة: الجمع المركّب
  • بالنسبة إلى الأنواع الكمية: dequantize_op_quantize(add, lhs, rhs, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs متّجه أو متّجه كمّي (C1-C6)
(I2) rhs متّجه أو متّجه كمّي (C1-C5)، (C7)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي (C1-C7)

القيود

  • إذا كانت العملية تستخدم موترات غير كمّية:
    • (C1) type(lhs) = type(rhs) = type(result)
  • إذا كانت العملية تستخدم موترات كمية:
    • (C2) is_quantized(lhs) and is_quantized(rhs) and is_quantized(result)
    • (C3) storage_type(lhs) = storage_type(rhs) = storage_type(result)
    • (C4) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
    • (C5) (is_per_axis_quantized(lhs) or is_per_axis_quantized(rhs)) = is_per_axis_quantized(result).
    • (C6) إذا كان is_per_axis_quantized(lhs)، فإن quantization_dimension(lhs) = quantization_dimension(result).
    • (C7) إذا كان is_per_axis_quantized(rhs)، يكون quantization_dimension(rhs) = quantization_dimension(result).

أمثلة

// %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) if channel_id <= 0 and use_global_device_ids = false
  • cross_replica_and_partition(replica_groups) if channel_id > 0 and use_global_device_ids = false
  • flattened_ids(replica_groups) if 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)

القيود

  • (C1) 0 <= all_gather_dim < rank(operands...)
  • (C2) is_unique(replica_groups)
  • يتم تعريف (C3) size(replica_groups) على النحو التالي:
    • num_replicas في حال استخدام cross_replica
    • num_replicas في حال استخدام cross_replica_and_partition
    • num_processes في حال استخدام flattened_ids
  • (C4) 0 <= replica_groups < size(replica_groups)
  • (C5) إذا كان use_global_device_ids = true، يكون channel_id > 0.
  • (C6) type(results...) = type(operands...) باستثناء:
    • dim(results..., all_gather_dim) = dim(operands..., all_gather_dim) * dim(process_groups, 1).

أمثلة

// num_replicas: 2
// num_partitions: 1
// %operand0@(0, 0): [[1, 2], [3, 4]]
// %operand0@(1, 0): [[5, 6], [7, 8]]
// %operand1@(0, 0): [[11, 12], [13, 14]]
// %operand1@(1, 0): [[15, 16], [17, 18]]
%result:2 = "stablehlo.all_gather"(%operand0, %operand1) {
  all_gather_dim = 1 : i64,
  replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>,
  // channel_id = 0
  channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
  // use_global_device_ids = false
} : (tensor<2x2xi64>, tensor<2x2xi64>) -> (tensor<2x4xi64>, tensor<2x4xi64>)
// %result0@(0, 0): [[1, 2, 5, 6], [3, 4, 7, 8]]
// %result0@(1, 0): [[1, 2, 5, 6], [3, 4, 7, 8]]
// %result1@(0, 0): [[11, 12, 15, 16], [13, 14, 17, 18]]
// %result1@(1, 0): [[11, 12, 15, 16], [13, 14, 17, 18]]

 مزيد من الأمثلة

all_reduce

الدلالات

ضمن كل مجموعة عمليات في شبكة عمليات StableHLO، يتم تطبيق دالة تقليل computation على قيم موترات operands من كل عملية، ويتم إنشاء موترات results.

تقسّم العملية شبكة عمليات StableHLO إلى process_groups، ويتم تعريفها على النحو التالي:

  • cross_replica(replica_groups) if channel_id <= 0 and use_global_device_ids = false
  • cross_replica_and_partition(replica_groups) if channel_id > 0 and use_global_device_ids = false
  • flattened_ids(replica_groups) if 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)

القيود

  • (C1) is_unique(replica_groups)
  • يتم تعريف (C2) size(replica_groups) على النحو التالي:
    • num_replicas في حال استخدام cross_replica
    • num_replicas في حال استخدام cross_replica_and_partition
    • num_processes في حال استخدام flattened_ids
  • (C3) 0 <= replica_groups < size(replica_groups)
  • (C4) إذا كان use_global_device_ids = true، فإن channel_id > 0.
  • (C5) computation له النوع (tensor<E>, tensor<E>) -> (tensor<E>) حيث is_promotable(element_type(operand), E).
  • (C6) shape(results...) = shape(operands...)
  • (C7) element_type(results...) = E

أمثلة

// num_replicas: 2
// num_partitions: 1
// %operand0@(0, 0): [1, 2, 3, 4]
// %operand0@(1, 0): [5, 6, 7, 8]
// %operand1@(0, 0): [9, 10, 11, 12]
// %operand1@(1, 0): [13, 14, 15, 16]
%result:2 = "stablehlo.all_reduce"(%operand0, %operand0) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
    "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>,
  // channel_id = 0
  channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
  // use_global_device_ids = false
} : (tensor<4xi64>, tensor<4xi64>) -> (tensor<4xi64>, tensor<4xi64>)
// %result0@(0, 0): [6, 8, 10, 12]
// %result0@(1, 0): [6, 8, 10, 12]
// %result1@(0, 0): [22, 24, 26, 28]
// %result1@(1, 0): [22, 24, 26, 28]

 مزيد من الأمثلة

all_to_all

الدلالات

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)

القيود

  • (C1) 0 <= split_dimension < rank(operands...)
  • (C2) dim(operands..., split_dimension) % split_count = 0
  • (C3) 0 <= concat_dimension < rank(operands...)
  • (C4) 0 < split_count
  • (C5) is_unique(replica_groups).
  • يتم تعريف (C6) size(replica_groups) على النحو التالي:
    • num_replicas في حال استخدام cross_replica
    • num_partitions في حال استخدام cross_partition
  • (C7) 0 <= replica_groups < size(replica_groups)
  • (C8) dim(replica_groups, 1) = split_count
  • (C9) type(results...) = type(operands...) باستثناء الحالات التي يكون فيها split_dimension != concat_dimension:
    • dim(results..., split_dimension) = dim(operands..., split_dimension) / split_count.
    • dim(results..., concat_dimension) = dim(operands..., concat_dimension) * split_count.

أمثلة

// num_replicas: 2
// num_partitions: 1
// %operand1@(0, 0): [[1, 2, 3, 4],
//                    [5, 6, 7, 8]]
// %operand1@(1, 0): [[9, 10, 11, 12],
//                    [13, 14, 15, 16]]
// %operand2@(0, 0): [[17, 18, 19, 20],
//                    [21, 22, 23, 24]]
// %operand2@(1, 0): [[25, 26, 27, 28],
//                    [29, 30, 31, 32]]
%result:2 = "stablehlo.all_to_all"(%operand1, %operand2) {
  split_dimension = 1 : i64,
  concat_dimension = 0 : i64,
  split_count = 2 : i64,
  replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>
  // channel_id = 0
} : (tensor<2x4xi64>, tensor<2x4xi64>) -> (tensor<4x2xi64>, tensor<4x2xi64>)
// %result#0@(0, 0): [[1, 2], [5, 6], [9, 10], [13, 14]]
// %result#0@(1, 0): [[3, 4], [7, 8], [11, 12], [15, 16]]
// %result#1@(0, 0): [[17, 18], [21, 22], [25, 26], [29, 30]]
// %result#1@(1, 0): [[19, 20], [23, 24], [27, 28], [31, 32]]

 مزيد من الأمثلة

و

الدلالات

تُجري هذه الدالة عملية AND على مستوى العناصر في موترَين lhs وrhs، وتنتج موترًا result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى القيم المنطقية: AND المنطقية
  • بالنسبة إلى الأعداد الصحيحة: استخدام AND على مستوى البت.

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs موتر من النوع المنطقي أو العدد الصحيح (C1)
(I2) rhs موتر من النوع المنطقي أو العدد الصحيح (C1)

النواتج

الاسم النوع القيود
result موتر من النوع المنطقي أو العدد الصحيح (C1)

القيود

  • (C1) type(lhs) = type(rhs) = type(result)

أمثلة

// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.and"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[1, 2], [3, 0]]

 مزيد من الأمثلة

atan2

الدلالات

تُجري هذه الدالة عملية atan2 على مستوى العناصر في الموترَين lhs وrhs، وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأرقام العشرية: atan2 من IEEE-754
  • بالنسبة إلى الأعداد المركّبة: دالة atan2 المركّبة.
  • بالنسبة إلى الأنواع الكمية: dequantize_op_quantize(atan2, lhs, rhs, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)
(I2) rhs موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

النواتج

الاسم النوع القيود
result موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)

أمثلة

// %lhs: [0.0, 1.0, -1.0]
// %rhs: [0.0, 0.0, 0.0]
%result = "stablehlo.atan2"(%lhs, %rhs) : (tensor<3xf64>, tensor<3xf64>) -> tensor<3xf64>
// %result: [0.0, 1.57079637, -1.57079637] // [0.0, pi/2, -pi/2]

 مزيد من الأمثلة

batch_norm_grad

الدلالات

تحسب هذه العملية تدرّجات عدة مدخلات من batch_norm_training التي يتم نشرها عكسيًا من grad_output، وتنتج موترات grad_operand وgrad_scale وgrad_offset. بشكل أكثر رسمية، يمكن التعبير عن هذه العملية على أنّها تقسيم إلى عمليات StableHLO حالية باستخدام بنية Python على النحو التالي:

def compute_sum(operand, feature_index):
  (sum,) = reduce(
      inputs=[operand],
      init_values=[constant(0, element_type(operand))],
      dimensions=[i for i in range(rank(operand)) if i != feature_index],
      body=lambda x, y: add(x, y))
  return sum

def compute_mean(operand, feature_index):
  sum = compute_sum(operand, feature_index)
  divisor = constant(size(operand) / dim(operand, feature_index),
                     element_type(operand))
  divisor_bcast = broadcast_in_dim(divisor, [], type(sum))
  return divide(sum, divisor_bcast)

def batch_norm_grad(operand, scale, mean, variance, grad_output, epsilon, feature_index):
  # Broadcast inputs to type(operand)
  scale_bcast = broadcast_in_dim(scale, [feature_index], type(operand))
  mean_bcast = broadcast_in_dim(mean, [feature_index], type(operand))
  variance_bcast = broadcast_in_dim(variance, [feature_index], type(operand))
  epsilon_bcast = broadcast_in_dim(constant(epsilon, element_type(operand)), [],
                                   type(operand))

  # Perform normalization using the provided `mean` and `variance`
  # Intermediate values will be useful for computing gradients
  centered_operand = subtract(operand, mean_bcast)
  stddev = sqrt(add(variance_bcast, epsilon_bcast))
  normalized_operand = divide(centered_operand, stddev)

  # Use the implementation from batchnorm_expander.cc in XLA
  # Temporary variables have exactly the same names as in the C++ code
  elements_per_feature = broadcast_in_dim(
      constant(divide(size(operand), dim(operand, feature_index)),
               element_type(grad_output)),
      [], type(operand))
  i1 = multiply(grad_output, elements_per_feature)
  i2 = broadcast_in_dim(
      compute_sum(grad_output, feature_index), [feature_index], type(operand))
  i3 = broadcast_in_dim(
      compute_sum(multiply(grad_output, centered_operand), feature_index),
      [feature_index], type(operand))
  i4 = multiply(i3, centered_operand)
  i5 = divide(i4, add(variance_bcast, epsilon_bcast))
  i6 = subtract(subtract(i1, i2), i5)

  grad_operand =
      multiply(divide(divide(scale_bcast, stddev), elements_per_feature), i6)
  grad_scale =
      compute_sum(multiply(grad_output, normalized_operand), feature_index)
  grad_offset = compute_sum(grad_output, feature_index)

  return grad_operand, grad_scale, grad_offset

بالنسبة إلى الأنواع المحدَّدة القيمة، يتم تنفيذ dequantize_batch_norm_grad_or_training_quantize(lambda operand, scale, mean, variance, grad_output: batch_norm_grad(operand, scale, mean, variance, grad_output, epsilon, feature_index), operand, scale, mean, variance, grad_output, type(grad_operand), type(grad_scale), type(feature_index)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C1-C3), (C5)
(I2) scale موتر أحادي البُعد من النوع الكمي ذي النقطة العائمة أو النوع الكمي لكل موتر (C2)، (C4)، (C5)
(I3) mean موتر أحادي البُعد من النوع الكمي ذي النقطة العائمة أو النوع الكمي لكل موتر (C2)، (C4)
(I4) variance موتر أحادي البُعد من النوع الكمي ذي النقطة العائمة أو النوع الكمي لكل موتر (C2)، (C4)
(I5) grad_output متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C2), (C3)
(I6) epsilon ثابت من النوع f32
(I7) feature_index ثابت من النوع si64 (C1)، (C5)

النواتج

الاسم النوع القيود
grad_operand متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C2), (C3)
grad_scale موتر أحادي البُعد من النوع الكمي ذي النقطة العائمة أو النوع الكمي لكل موتر (C2)، (C4)
grad_offset موتر أحادي البُعد من النوع الكمي ذي النقطة العائمة أو النوع الكمي لكل موتر (C2)، (C4)

القيود

  • (C1) 0 <= feature_index < rank(operand)
  • (C2) operand وscale وmean وvariance وgrad_output وgrad_operand وgrad_scale وgrad_offset لها baseline_element_type نفسه.
  • (C3) operand وgrad_output وgrad_operand لها الشكل نفسه.
  • (C4) scale وmean وvariance وgrad_scale وgrad_offset لها الشكل نفسه.
  • (C5) size(scale) = dim(operand, feature_index).

أمثلة

// %operand: [
//            [[1.0, 2.0], [3.0, 4.0]],
//            [[3.0, 4.0], [1.0, 2.0]]
//           ]
// %scale: [1.0, 1.0]
// %mean: [2.0, 3.0]
// %variance: [1.0, 1.0]
// %grad_output: [
//                [[0.1, 0.1], [0.1, 0.1]],
//                [[0.1, 0.1], [0.1, 0.1]]
//               ]
%grad_operand, %grad_scale, %grad_offset =
"stablehlo.batch_norm_grad"(%operand, %scale, %mean, %variance, %grad_output) {
  epsilon = 0.0 : f32,
  feature_index = 2 : i64
} : (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>, tensor<2xf64>,
     tensor<2x2x2xf64>) -> (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>)
// %grad_operand: [
//                 [[0.0, 0.0], [0.0, 0.0]],
//                 [[0.0, 0.0], [0.0, 0.0]]
//                ]
// %grad_scale:  [0.0, 0.0]
// %grad_offset: [0.4, 0.4]

batch_norm_inference

الدلالات

تعمل هذه الدالة على تسوية موتر operand على مستوى جميع السمات باستثناء السمة feature_index، وتنتج موتر result. وبشكل أكثر رسمية، يمكن التعبير عن هذه العملية على أنّها تقسيم إلى عمليات StableHLO الحالية باستخدام بنية Python على النحو التالي:

def batch_norm_inference(operand, scale, offset, mean, variance, epsilon, feature_index):
  # Broadcast inputs to shape(operand)
  scale_bcast = broadcast_in_dim(scale, [feature_index], type(operand))
  offset_bcast = broadcast_in_dim(offset, [feature_index], type(operand))
  mean_bcast = broadcast_in_dim(mean, [feature_index], type(operand))
  variance_bcast = broadcast_in_dim(variance, [feature_index], type(operand))
  epsilon_bcast = broadcast_in_dim(constant(epsilon, element_type(operand)), [],
                                   type(operand))

  # Perform normalization using the provided `mean` and `variance` instead of
  # computing them like `batch_norm_training` does.
  centered_operand = subtract(operand, mean_bcast)
  stddev = sqrt(add(variance_bcast, epsilon_bcast))
  normalized_operand = divide(centered_operand, stddev)
  return add(multiply(scale_bcast, normalized_operand), offset_bcast)

بالنسبة إلى الأنواع المحدَّدة القيمة، يتم تنفيذ dequantize_op_quantize(lambda operand, scale, offset, mean, variance: batch_norm_inference(operand, scale, offset, mean, variance, epsilon, feature_index), operand, scale, offset, mean, variance, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C1-C7)
(I2) scale موتر أحادي البُعد من النوع الكمي ذي النقطة العائمة أو النوع الكمي لكل موتر (C2), (C3)
(I3) offset موتر أحادي البُعد من النوع الكمي ذي النقطة العائمة أو النوع الكمي لكل موتر (C2)، (C4)
(I4) mean موتر أحادي البُعد من النوع الكمي ذي النقطة العائمة أو النوع الكمي لكل موتر (C5)
(I5) variance موتر أحادي البُعد من النوع الكمي ذي النقطة العائمة أو النوع الكمي لكل موتر (C2)، (C6)
(I6) epsilon ثابت من النوع f32
(I7) feature_index ثابت من النوع si64 (C1)، (C3-C6)

النواتج

الاسم النوع القيود
result متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C2)، (C7)

القيود

  • (C1) 0 <= feature_index < rank(operand)
  • (C2) تحتوي operand وscale وoffset وmean وvariance وresult على baseline_element_type نفسه.
  • (C3) size(scale) = dim(operand, feature_index)
  • (C4) size(offset) = dim(operand, feature_index)
  • (C5) size(mean) = dim(operand, feature_index).
  • (C6) size(variance) = dim(operand, feature_index)
  • (C7) baseline_type(operand) = baseline_type(result)

أمثلة

// %operand: [
//            [[1.0, 2.0], [3.0, 4.0]],
//            [[3.0, 4.0], [1.0, 2.0]]
//           ]
// %scale: [1.0, 1.0]
// %offset: [1.0, 1.0]
// %mean: [2.0, 3.0]
// %variance: [1.0, 1.0]
%result = "stablehlo.batch_norm_inference"(%operand, %scale, %offset, %mean, %variance) {
  epsilon = 0.0 : f32,
  feature_index = 2 : i64
} : (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>, tensor<2xf64>, tensor<2xf64>) -> tensor<2x2x2xf64>
// %result: [
//           [[0.0, 0.0], [2.0, 2.0]],
//           [[2.0, 2.0], [0.0, 0.0]]
//          ]

batch_norm_training

الدلالات

تحسب هذه الدالة المتوسط والتباين على مستوى جميع السمات باستثناء السمة feature_index، وتُجري تسوية على الموتر operand لإنتاج الموترات output وbatch_mean وbatch_var. بشكل أكثر رسمية، يمكن التعبير عن هذه العملية على أنّها عملية تقسيم إلى عمليات StableHLO حالية باستخدام بنية Python على النحو التالي:

def compute_mean(operand, feature_index):
  (sum,) = reduce(
      inputs=[operand],
      init_values=[constant(0, element_type(operand))],
      dimensions=[i for i in range(rank(operand)) if i != feature_index],
      body=lambda x, y: add(x, y))
  divisor = constant(size(operand) / dim(operand, feature_index),
                     element_type(operand))
  divisor_bcast = broadcast_in_dim(divisor, [], type(sum))
  return divide(sum, divisor_bcast)

def compute_variance(operand, feature_index):
  mean = compute_mean(operand, feature_index)
  mean_bcast = broadcast_in_dim(mean, [feature_index], type(operand))
  centered_operand = subtract(operand, mean_bcast)
  return compute_mean(mul(centered_operand, centered_operand), feature_index)

def batch_norm_training(operand, scale, offset, epsilon, feature_index):
  mean = compute_mean(operand, feature_index)
  variance = compute_variance(operand, feature_index)
  return batch_norm_inference(operand, scale, offset, mean, variance, epsilon,
                              feature_index),
         mean, variance

بالنسبة إلى الأنواع المحدَّدة القيمة، يتم تنفيذ dequantize_batch_norm_grad_or_training_quantize(lambda operand, scale, offset: batch_norm_training(operand, scale, offset, epsilon, feature_index), operand, scale, offset, type(output), type(batch_mean), type(batch_var)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C1)
(I2) scale موتر أحادي البُعد من النوع "نقطة عائمة" أو "تكميم لكل موتر" (C2), (C3)
(I3) offset موتر أحادي البُعد من النوع "نقطة عائمة" أو "تكميم لكل موتر" (C2)، (C4)
(I4) epsilon ثابت من النوع f32 (C1)، (C3-C6)
(I5) feature_index ثابت من النوع si64 (C1)، (C3-C6)

النواتج

الاسم النوع القيود
output متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C7)
batch_mean موتر أحادي البُعد من النوع "نقطة عائمة" أو "تكميم لكل موتر" (C2)، (C5)
batch_var موتر أحادي البُعد من النوع "نقطة عائمة" أو "تكميم لكل موتر" (C2)، (C6)

القيود

  • (C1) 0 <= feature_index < rank(operand)
  • (C2) تحتوي operand وscale وoffset وbatch_mean وbatch_var وoutput على baseline_element_type نفسه.
  • (C3) size(scale) = dim(operand, feature_index)
  • (C4) size(offset) = dim(operand, feature_index)
  • (C5) size(batch_mean) = dim(operand, feature_index).
  • (C6) size(batch_var) = dim(operand, feature_index)
  • (C7) baseline_type(output) = baseline_type(operand)

أمثلة

// %operand: [
//            [[1.0, 2.0], [3.0, 4.0]],
//            [[3.0, 4.0], [1.0, 2.0]]
//           ]
// %scale: [1.0, 1.0]
// %offset: [1.0, 1.0]
%output, %batch_mean, %batch_var = "stablehlo.batch_norm_training"(%operand, %scale, %offset) {
  epsilon = 0.0 : f32,
  feature_index = 2 : i64
} : (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>) ->
    (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>)
// %output: [
//           [[0.0, 0.0], [2.0, 2.0]],
//           [[2.0, 2.0], [0.0, 0.0]]
//          ]
// %batch_mean: [2.0, 3.0]
// %batch_var: [1.0, 1.0]

bitcast_convert

الدلالات

تُجري عملية bitcast على موتر operand وتنتج موتر result حيث تتم إعادة تفسير وحدات البت الخاصة بموتر operand بالكامل باستخدام نوع موتر result.

بشكل أكثر رسمية، إذا توفّرت E = element_type(operand) وE' = element_type(result) وR = rank(operand):

  • إذا كان num_bits(E') < num_bits(E)، bits(result[i0, ..., iR-1, :]) = bits(operand[i0, ..., iR-1]).
  • إذا كان num_bits(E') > num_bits(E)، bits(result[i0, ..., iR-2]) = bits(operand[i0, ..., iR-2, :]).
  • إذا كان num_bits(E') = num_bits(E)، bits(result[i0, ..., iR-1]) = bits(operand[i0, ..., iR-1]).

تعرض bits تمثيلاً في الذاكرة لقيمة معيّنة، ويكون سلوكها محدّدًا حسب التنفيذ لأنّ التمثيل الدقيق للموترات محدّد حسب التنفيذ، والتمثيل الدقيق لأنواع العناصر محدّد حسب التنفيذ أيضًا.

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه أو متّجه كمّي (C1-C2)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي (C1-C2)

القيود

  • (C1) في حال توفّر E = is_quantized(operand) ? storage_type(operand) : element_type(operand) وE' = is_quantized(result) ? storage_type(result) : element_type(result) وR = rank(operand):
    • إذا كان num_bits(E') = num_bits(E)، shape(result) = shape(operand).
    • إذا num_bits(E') < num_bits(E):
    • rank(result) = R + 1.
    • dim(result, i) = dim(operand, i) لكل 0 <= i < 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

الدلالات

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) على التوالي.
  • (C2) size(broadcast_dimensions) = rank(operand)
  • (C3) 0 <= broadcast_dimensions < rank(result)
  • (C4) is_unique(broadcast_dimensions)
  • (C5) لكل d في axes(operand):
    • dim(operand, d) = 1 أو
    • dim(operand, d) = dim(result, broadcast_dimensions[d]).
  • (C6) إذا كان is_per_axis_quantized(result):
    • quantization_dimension(result) = broadcast_dimensions[quantization_dimension(operand)].
    • إذا dim(operand, quantization_dimension(operand)) = 1، إذًا scales(result)[i] = scales(operand)[0] and zero_points(result)[i] = zero_points(operand)[0] for i in range(dim(result, quantization_dimension(result))).

أمثلة

// %operand: [
//            [1, 2, 3]
//           ]
%result = "stablehlo.broadcast_in_dim"(%operand) {
  broadcast_dimensions = array<i64: 2, 1>
} : (tensor<1x3xi32>) -> tensor<2x3x2xi32>
// %result: [
//            [
//             [1, 1],
//             [2, 2],
//             [3, 3]
//            ],
//            [
//             [1, 1],
//             [2, 2],
//             [3, 3]
//            ]
//          ]

 مزيد من الأمثلة

حافظة

الدلالات

تنتج هذه الدالة المخرجات من تنفيذ دالة واحدة بالضبط من branches استنادًا إلى قيمة index. بشكل أكثر رسمية، result = selected_branch() حيث:

  • selected_branch = branches[index] إذا 0 <= index < size(branches)
  • selected_branch = branches[-1] في ما عدا ذلك.

المدخلات

التصنيف الاسم النوع القيود
(I1) index موتر صفري الأبعاد من النوع si32
(I2) branches عدد متغير من الدوال (C1-C4)

النواتج

الاسم النوع القيود
results عدد متغيّر من الموترات أو الموترات الكمية أو الرموز المميزة (C4)

القيود

  • (C1) 0 < size(branches)
  • (C2) input_types(branches...) = []
  • (C3) same(output_types(branches...))
  • (C4) type(results...) = output_types(branches[0])

أمثلة

// %index: -1
// %result_branch0: [0, 0]
// %result_branch1: [1, 1]
%result0, %result1 = "stablehlo.case"(%index) ({
  "stablehlo.return"(%result_branch0, %result_branch0) : (tensor<2xi64>, tensor<2xi64>) -> ()
}, {
  "stablehlo.return"(%result_branch1, %result_branch1) : (tensor<2xi64>, tensor<2xi64>) -> ()
}) : (tensor<i32>) -> (tensor<2xi64>, tensor<2xi64>)
// %result0: [1, 1]
// %result1: [1, 1]

 مزيد من الأمثلة

cbrt

الدلالات

تُجري عملية الجذر التكعيبي على مستوى كل عنصر في الموتر operand وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأرقام العشرية: rootn(x, 3) من IEEE-754.
  • بالنسبة إلى الأعداد المركّبة: الجذر التكعيبي المركّب
  • بالنسبة إلى الأنواع الكمية: dequantize_op_quantize(cbrt, operand, type(result))

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

النواتج

الاسم النوع القيود
result موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %operand: [0.0, 1.0, 8.0, 27.0]
%result = "stablehlo.cbrt"(%operand) : (tensor<4xf64>) -> tensor<4xf64>
// %result: [0.0, 1.0, 2.0, 3.0]

 مزيد من الأمثلة

ceil

الدلالات

تُجري هذه الدالة عملية السقف لكل عنصر في الموتر operand وتنتج الموتر result. تنفيذ عملية roundToIntegralTowardPositive من مواصفات IEEE-754 بالنسبة إلى الأنواع المحدَّدة القيمة، يتم تنفيذ dequantize_op_quantize(ceil, operand, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C1)

النواتج

الاسم النوع القيود
result متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %operand: [-0.8166, -0.2530, 0.2530, 0.8166, 2.0]
%result = "stablehlo.ceil"(%operand) : (tensor<5xf32>) -> tensor<5xf32>
// %result: [-0.0, -0.0, 1.0, 1.0, 2.0]

 مزيد من الأمثلة

cholesky

الدلالات

تحسب هذه الدالة تحليل شوليسكي لمجموعة من المصفوفات.

بشكل أكثر رسمية، لكل i في index_space(result)، result[i0, ..., iR-3, :, :] هو تحليل Cholesky لـ a[i0, ..., iR-3, :, :]، في شكل مصفوفة مثلثة سفلى (إذا كان lower هو true) أو مصفوفة مثلثة عليا (إذا كان lower هو false). تكون قيم الإخراج في المثلث المعاكس، أي المثلث العلوي الصرف أو المثلث السفلي الصرف على التوالي، محددة حسب التنفيذ.

إذا كان هناك i حيث تكون مصفوفة الإدخال ليست مصفوفة هرميتية موجبة محددة، يكون السلوك غير محدّد.

بالنسبة إلى الأنواع المحدَّدة القيمة، يتم تنفيذ dequantize_op_quantize(lambda operand: cholesky(operand, lower), a, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) a موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1-C3)
(I2) lower ثابت من النوع i1

النواتج

الاسم النوع القيود
result موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(a) = baseline_type(result)
  • (C2) 2 <= rank(a)
  • (C3) dim(a, -2) = dim(a, -1)

أمثلة

// %a: [
//      [1.0, 2.0, 3.0],
//      [2.0, 20.0, 26.0],
//      [3.0, 26.0, 70.0]
//     ]
%result = "stablehlo.cholesky"(%a) {
  lower = true
} : (tensor<3x3xf32>) -> tensor<3x3xf64>
// %result: [
//           [1.0, 0.0, 0.0],
//           [2.0, 4.0, 0.0],
//           [3.0, 5.0, 6.0]
//          ]

تثبيت

الدلالات

تثبّت هذه العملية كل عنصر من عناصر موتر operand بين الحد الأدنى والحد الأقصى للقيمة، وتنتج موتر result. بشكل أكثر رسمية، result[result_index] = minimum(maximum(operand[result_index], min_element), max_element)، حيث min_element = rank(min) = 0 ? min[] : min[result_index]، max_element = rank(max) = 0 ? max[] : max[result_index]. بالنسبة إلى الأنواع المحدَّدة القيمة، يتم تنفيذ dequantize_op_quantize(clamp, min, operand, max, type(result)).

يتضمّن فرض ترتيب على الأعداد المركّبة دلالات مفاجئة، لذا نخطّط في المستقبل لإزالة إمكانية استخدام الأعداد المركّبة في هذه العملية (#560).

المدخلات

التصنيف الاسم النوع القيود
(I1) min متّجه أو متّجه كمّي لكل متّجه (C1)، (C3)
(I2) operand متّجه أو متّجه كمّي لكل متّجه (C1-C4)
(I3) max متّجه أو متّجه كمّي لكل متّجه (C2), (C3)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي لكل متّجه (C4)

القيود

  • (C1) rank(min) = 0 or shape(min) = shape(operand)
  • (C2) rank(max) = 0 or shape(max) = shape(operand)
  • (C3) baseline_element_type(min) = baseline_element_type(operand) = baseline_element_type(max)
  • (C4) baseline_type(operand) = baseline_type(result)

أمثلة

// %min: [5, 10, 15]
// %operand: [3, 13, 23]
// %max: [10, 15, 20]
%result = "stablehlo.clamp"(%min, %operand, %max) : (tensor<3xi32>, tensor<3xi32>, tensor<3xi32>) -> tensor<3xi32>
// %result: [5, 13, 20]

 مزيد من الأمثلة

collective_broadcast

الدلالات

ضمن كل مجموعة عمليات في شبكة عمليات StableHLO، أرسِل قيمة الموتر operand من عملية المصدر إلى العمليات المستهدَفة وأنشئ الموتر result.

تقسّم العملية شبكة معالجة StableHLO إلى process_groups، ويتم تعريفها على النحو التالي:

  • cross_replica(replica_groups) إذا channel_id <= 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)

القيود

  • (C1) is_unique(replica_groups)
  • (C2) 0 <= replica_groups < N حيث يتم تعريف N على النحو التالي:
    • num_replicas في حال استخدام cross_replica
    • num_partitions في حال استخدام cross_partition
  • (C3) type(result) = type(operand)

أمثلة

// num_replicas: 4
// num_partitions: 1
// %operand@(0, 0): [[1, 2]]
// %operand@(1, 0): [[3, 4]]
// %operand@(2, 0): [[5, 6]]
// %operand@(3, 0): [[7, 8]]
%result = "stablehlo.collective_broadcast"(%operand) {
  replica_groups = dense<[[2, 1]]> : tensor<1x2xi64>,
  channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
} : (tensor1x2xi64>) -> tensor<1x2xi64>
// %result@(0, 0): [[0, 0]]
// %result@(1, 0): [[5, 6]]
// %result@(2, 0): [[5, 6]]
// %result@(3, 0): [[0, 0]]

collective_permute

الدلالات

ضمن كل مجموعة عمليات في شبكة عمليات StableHLO، يتم إرسال قيمة الموتر operand من عملية المصدر إلى عملية الهدف وإنتاج موتر result.

تقسّم العملية شبكة عمليات StableHLO إلى process_groups، ويتم تعريفها على النحو التالي:

  • 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)

القيود

  • (C1) dim(source_target_pairs, 1) = 2
  • (C2) is_unique(source_target_pairs[:, 0])
  • (C3) is_unique(source_target_pairs[:, 1])
  • (C4) 0 <= source_target_pairs < N، حيث يتم تعريف N على النحو التالي:
    • num_replicas في حال استخدام cross_replica
    • 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، تستخدم العملية الحسابية مجموعة العمليتين totalOrder وcompareQuietEqual من معيار IEEE-754.

بالنسبة إلى أنواع العناصر المعقّدة، تتم المقارنة المعجمية لأزواج (real, imag) باستخدام comparison_direction وcompare_type المقدَّمتَين. يؤدي فرض ترتيب على الأعداد المركّبة إلى دلالات مفاجئة، لذا نخطّط في المستقبل لإيقاف إتاحة الأعداد المركّبة عندما تكون قيمة comparison_direction هي GE أو GT أو LE أو LT (#560).

بالنسبة إلى الأنواع المحدَّدة الكمية، يتم تنفيذ dequantize_compare(lhs, rhs, comparison_direction).

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs متّجه أو متّجه كمّي لكل متّجه (C1-C3)
(I2) rhs متّجه أو متّجه كمّي لكل متّجه (C1-C2)
(I3) comparison_direction تعداد EQ وNE وGE وGT وLE وLT
(I4) compare_type تعداد FLOAT وTOTALORDER وSIGNED وUNSIGNED (C3)

النواتج

الاسم النوع القيود
result متّجه من النوع المنطقي (C2)

القيود

  • (C1) baseline_element_type(lhs) = baseline_element_type(rhs)
  • (C2) shape(lhs) = shape(rhs) = shape(result)
  • يتم تعريف (C3) compare_type على النحو التالي:
    • SIGNED إذا is_signed_integer(element_type(lhs))
    • UNSIGNED إذا is_unsigned_integer(element_type(lhs)) or is_boolean(element_type(lhs))
    • FLOAT أو TOTALORDER إذا is_float(element_type(lhs))
    • FLOAT إذا is_complex(element_type(lhs))

أمثلة

// %lhs: [1.0, 3.0]
// %rhs: [1.1, 2.9]
%result = "stablehlo.compare"(%lhs, %rhs) {
  comparison_direction = #stablehlo<comparison_direction LT>,
  compare_type = #stablehlo<comparison_type FLOAT>
} : (tensor<2xf32>, tensor<2xf32>) -> tensor<2xi1>
// %result: [true, false]

 مزيد من الأمثلة

معقّد

الدلالات

تُجري هذه العملية تحويلاً على مستوى كل عنصر إلى قيمة مركّبة من زوج من القيم الحقيقية والتخيّلية، lhs وrhs، وتنتج موترًا result.

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs موتر من النوع f32 أو f64 (C1-C3)
(I2) rhs موتر من النوع f32 أو f64 (C1)

النواتج

الاسم النوع القيود
result متّجه متعدّد الأبعاد من النوع المركّب (C2), (C3)

القيود

  • (C1) type(lhs) = type(rhs)
  • (C2) shape(result) = shape(lhs)
  • (C3) element_type(result) من النوع complex<E> حيث E = element_type(lhs).

أمثلة

// %lhs: [1.0, 3.0]
// %rhs: [2.0, 4.0]
%result = "stablehlo.complex"(%lhs, %rhs) : (tensor<2xf64>, tensor<2xf64>) -> tensor<2xcomplex<f64>>
// %result: [(1.0, 2.0), (3.0, 4.0)]

 مزيد من الأمثلة

مُركّب

الدلالات

تغليف عملية مكوّنة من عمليات StableHLO أخرى، تأخذ inputs وcomposite_attributes وتنتج results. يتم تنفيذ دلالات العملية من خلال السمة decomposition. يمكن استبدال عملية composite بتفكيكها بدون تغيير دلالات البرنامج. في الحالات التي لا يؤدي فيها تضمين عملية التفكيك إلى توفير دلالات op نفسها، يُفضّل استخدام custom_call.

يُستخدَم الحقل version (القيمة التلقائية هي 0) للإشارة إلى وقت تغيُّر دلالات العنصر المركّب.

المدخلات

التصنيف الاسم النوع
(I1) inputs عدد متغيّر من القيم
(I2) name ثابت من النوع string
(I3) composite_attributes قاموس السمات
(I4) decomposition ثابت من النوع string
(I5) version ثابت من النوع si32

النواتج

الاسم النوع
results عدد متغيّر من القيم

القيود

  • (C1) is_namespaced_op_name(name)
  • (C2) is_defined_in_parent_scope(decomposition)
  • (C3) types(inputs...) == input_types(decomposition)
  • (C4) types(results...) == output_types(decomposition)

أمثلة

%results = "stablehlo.composite"(%input0, %input1) {
  name = "my_namespace.my_op",
  composite_attributes = {
    my_attribute = "my_value"
  },
  decomposition = @my_op,
  version = 1 : i32
} : (tensor<f32>, tensor<f32>) -> tensor<f32>

 مزيد من الأمثلة

دمج

الدلالات

تدمج inputs على طول البُعد dimension بالترتيب نفسه كما هو محدّد في الوسيطات، وتنتج موترًا result. بشكل أكثر رسمية، result[i0, ..., id, ..., iR-1] = inputs[k][i0, ..., kd, ..., iR-1]، حيث:

  1. id = d0 + ... + dk-1 + kd.
  2. d تساوي dimension، وd0، ... هي أحجام السمة رقم d من inputs.

المدخلات

التصنيف الاسم النوع القيود
(I1) inputs عدد متغير من الموترات أو الموترات الكمية لكل موتر (C1-C6)
(I2) dimension ثابت من النوع si64 (C2)، (C4)، (C6)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي لكل متّجه (C5-C6)

القيود

  • (C1) same(element_type(inputs...))
  • (C2) same(shape(inputs...)) باستثناء dim(inputs..., dimension)
  • (C3) 0 < size(inputs)
  • (C4) 0 <= dimension < rank(inputs[0])
  • (C5) element_type(result) = element_type(inputs[0]).
  • (C6) shape(result) = shape(inputs[0]) باستثناء:
    • dim(result, dimension) = dim(inputs[0], dimension) + ....

أمثلة

// %input0: [[1, 2], [3, 4], [5, 6]]
// %input1: [[7, 8]]
%result = "stablehlo.concatenate"(%input0, %input1) {
  dimension = 0 : i64
} : (tensor<3x2xi64>, tensor<1x2xi64>) -> tensor<4x2xi64>
// %result: [[1, 2], [3, 4], [5, 6], [7, 8]]

 مزيد من الأمثلة

الثابت

الدلالات

تُنشئ هذه الدالة موتر output من قيمة ثابتة value.

المدخلات

التصنيف الاسم النوع القيود
(I1) value الثابت (C1)

النواتج

الاسم النوع القيود
output متّجه أو متّجه كمّي (C1)

القيود

  • (C1) type(value) = type(output)

أمثلة

%output = "stablehlo.constant"() {
  value = dense<[[0.0, 1.0], [2.0, 3.0]]> : tensor<2x2xf32>
} : () -> tensor<2x2xf32>
// %output: [[0.0, 1.0], [2.0, 3.0]]

 مزيد من الأمثلة

إجراء إحالة ناجحة

الدلالات

تُجري هذه الدالة عملية تحويل على مستوى كل عنصر من نوع عنصر إلى آخر في الموتر operand، وتنتج الموتر result.

بالنسبة إلى عمليات التحويل من boolean-to-any-supported-type، يتم تحويل القيمة false إلى صفر، ويتم تحويل القيمة true إلى واحد. بالنسبة إلى عمليات التحويل من any-supported-type-to-boolean، يتم تحويل القيمة صفر إلى false، ويتم تحويل القيم غير الصفرية إلى true. يمكنك الاطّلاع أدناه على كيفية عمل ذلك مع الأنواع المعقّدة.

بالنسبة إلى عمليات التحويل التي تتضمّن عددًا صحيحًا إلى عدد صحيح أو عددًا صحيحًا إلى عدد ذي فاصلة عشرية أو عددًا ذا فاصلة عشرية إلى عدد ذي فاصلة عشرية، إذا كان من الممكن تمثيل القيمة المصدرية بدقة في نوع الوجهة، تكون القيمة الناتجة هي التمثيل الدقيق. وفي ما عدا ذلك، سيتم تحديد السلوك لاحقًا (#180).

بالنسبة إلى عمليات التحويل التي تتضمّن floating-point-to-integer، يتم اقتطاع الجزء الكسري. إذا تعذّر تمثيل القيمة المقتطعة في نوع الوجهة، سيتم تحديد السلوك لاحقًا (#180).

تتّبع عمليات التحويل التي تتضمّن أعدادًا مركّبة إلى أعداد مركّبة السلوك نفسه لعمليات التحويل من أعداد ذات فاصلة عائمة إلى أعداد ذات فاصلة عائمة عند تحويل الأجزاء الحقيقية والتخيّلية.

بالنسبة إلى عمليات التحويل من عدد مركّب إلى أي نوع آخر ومن أي نوع آخر إلى عدد مركّب، يتم تجاهل القيمة التخيّلية المصدر أو يتم ضبط القيمة التخيّلية الوجهة على صفر، على التوالي. يتم تحويل الجزء الحقيقي وفقًا لعمليات التحويل الخاصة بالأعداد النقطية العائمة.

من حيث المبدأ، يمكن أن تعبّر هذه العملية عن إزالة التكميم (التحويل من موترات مكَمَّمة إلى موترات عادية) والتكميم (التحويل من موترات عادية إلى موترات مكَمَّمة) وإعادة التكميم (التحويل بين موترات مكَمَّمة)، ولكن لدينا حاليًا عمليات مخصّصة لذلك، وهي uniform_dequantize لحالة الاستخدام الأولى وuniform_quantize لحالتي الاستخدام الثانية والثالثة. في المستقبل، قد يتم دمج هاتين العمليتين في convert (#1576).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه متعدّد الأبعاد (C1)

النواتج

الاسم النوع القيود
result متّجه متعدّد الأبعاد (C1)

القيود

  • (C1) shape(operand) = shape(result)

أمثلة

// %operand: [-1, 0, 1]
%result = "stablehlo.convert"(%operand) : (tensor<3xi64>) -> tensor<3xcomplex<f64>>
// %result: [(-1.0, 0.0), (0.0, 0.0), (1.0, 0.0)]

 مزيد من الأمثلة

الالتفاف

الدلالات

تحسب هذه الدالة حواصل الضرب النقطي بين نوافذ بحجم lhs وشرائح بحجم rhs، وتنتج result. يوضّح المخطّط التالي كيفية احتساب العناصر في result من lhs وrhs باستخدام مثال ملموس.

الالتفاف

بشكل أكثر رسمية، ضع في اعتبارك إعادة صياغة المدخلات التالية من حيث lhs لتتمكّن من التعبير عن نوافذ lhs:

  • lhs_window_dimensions = lhs_shape(dim(lhs, input_batch_dimension), dim(rhs, kernel_spatial_dimensions), dim(lhs, input_feature_dimension)).
  • lhs_window_strides = lhs_shape(1, window_strides, 1).
  • lhs_padding = lhs_shape([0, 0], padding, [0, 0]).
  • lhs_base_dilations = lhs_shape(1, lhs_dilation, 1).
  • lhs_window_dilations = lhs_shape(1, rhs_dilation, 1).

تستخدم إعادة الصياغة هذه وظائف المساعدة التالية:

  • lhs_shape(n, hw, c) = permute([n] + hw + [c], [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]).
  • result_shape(n1, hw, c1) = permute([n1] + hw + [c1], [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]).
  • permute([j0, j1, ..., jR-1], permutation) = [i0, i1, ..., iR-1] حيث j[d] = i[permutation[d]]

إذا كان feature_group_count = 1 وbatch_group_count = 1، يكون ذلك لجميع output_spatial_index في index_space(dim(result, output_spatial_dimensions...))، result[result_shape(:, output_spatial_index, :)] = dot_product حيث:

  • padding_value = constant(0, element_type(lhs)).
  • padded_lhs = pad(lhs, padding_value, lhs_padding[:, 0], lhs_padding[:, 1], lhs_base_dilations - 1).
  • lhs_window_start = lhs_shape(0, output_spatial_index, 0) * lhs_window_strides.
  • lhs_window = slice(padded_lhs, lhs_window_start, lhs_window_start + lhs_window_dimensions, lhs_window_dilations).
  • reversed_lhs_window = reverse(lhs_window, [input_spatial_dimensions[dim] for dim in range(size(window_reversal)) if window_reversal[dim] = true]). يبدو أنّ هذه الميزة غير مستخدَمة، لذا نخطّط لإزالتها في المستقبل (#1181).
  • dot_product = dot_general(reversed_lhs_window, rhs, lhs_batching_dimensions=[], lhs_contracting_dimensions=input_spatial_dimensions + [input_feature_dimension], rhs_batching_dimensions=[], rhs_contracting_dimensions=kernel_spatial_dimensions + [kernel_input_feature_dimension]).

إذا feature_group_count > 1:

  • lhses = split(lhs, feature_group_count, input_feature_dimension).
  • rhses = split(rhs, feature_group_count, kernel_output_feature_dimension).
  • results... = convolution(lhses..., rhses..., ..., feature_group_count=1, ...).
  • result = concatenate(results, output_feature_dimension).

إذا batch_group_count > 1:

  • lhses = split(lhs, batch_group_count, input_batch_dimension).
  • rhses = split(rhs, batch_group_count, kernel_output_feature_dimension).
  • results... = convolution(lhses..., rhses..., ..., batch_group_count=1, ...).
  • result = concatenate(results, output_feature_dimension).

بالنسبة إلى الأنواع المحدَّدة الكمية، يتم تنفيذ dequantize_op_quantize( lambda lhs, rhs: convolution(lhs, rhs, window_strides, padding, lhs_dilation, rhs_dilation, window_reversal, input_batch_dimension, input_feature_dimension, input_spatial_dimensions, kernel_input_feature_dimension, kernel_output_feature_dimension, kernel_spatial_dimensions, output_batch_dimension, output_feature_dimension, output_spatial_dimensions, feature_group_count, batch_group_count, precision_config), lhs, rhs, type(result)).

بالنسبة إلى الأنواع الكمية المختلطة، يتم تنفيذ hybrid_dequantize_then_op( lambda lhs, rhs: convolution(lhs, rhs, window_strides, padding, lhs_dilation, rhs_dilation, window_reversal, input_batch_dimension, input_feature_dimension, input_spatial_dimensions, kernel_input_feature_dimension, kernel_output_feature_dimension, kernel_spatial_dimensions, output_batch_dimension, output_feature_dimension, output_spatial_dimensions, feature_group_count, batch_group_count, precision_config), lhs, rhs).

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs متّجه أو متّجه كمّي لكل متّجه (C1)، و(C10-C11)، و(C14) و(C25)، و(C27-C28)، و(C31-C32)، و(C34)
(I2) rhs متّجه أو متّجه كمّي (C1)، و(C14-C16)، و(C25)، و(C27-C29)، و(C31-C34)
(I3) window_strides ثابت موتر أحادي البُعد من النوع si64 (C2-C3), (C25)
(I4) padding ثابت موتر ثنائي الأبعاد من النوع si64 (C4)، (C25)
(I5) lhs_dilation ثابت موتر أحادي البُعد من النوع si64 (C5-C6)، (C25)
(I6) rhs_dilation ثابت موتر أحادي البُعد من النوع si64 (C7-C8), (C25)
(I7) window_reversal ثابت موتر أحادي البُعد من النوع i1 (C9)
(I8) input_batch_dimension ثابت من النوع si64 (C10) و(C13) و(C25)
(I9) input_feature_dimension ثابت من النوع si64 (C11)، (C13-C14)
(I10) input_spatial_dimensions ثابت موتر أحادي البُعد من النوع si64 (C12)، و(C13)، و(C25)
(I11) kernel_input_feature_dimension ثابت من النوع si64 (C14)، (C18)
(I12) kernel_output_feature_dimension ثابت من النوع si64 (C15-C16)، (C18)، (C25)، (C29)
(I13) kernel_spatial_dimensions ثابت موتر أحادي البُعد من النوع si64 (C17-C18), (C25)
(I14) output_batch_dimension ثابت من النوع si64 (C20)، (C25)
(I15) output_feature_dimension ثابت من النوع si64 (C20) و(C25) و(C30)
(I16) output_spatial_dimensions ثابت موتر أحادي البُعد من النوع si64 (C19-C20), (C25)
(I17) feature_group_count ثابت من النوع si64 (C11)، (C14)، (C16)، (C21)، (C23)
(I18) batch_group_count ثابت من النوع si64 (C10)، (C15)، (C22)، (C23)، (C25)
(I19) precision_config عدد متغير من تعدادات DEFAULT وHIGH وHIGHEST (C24)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي ‫(C25-C28)، و(C30)، و(C32-34)

القيود

  • (C1) N = rank(lhs) = rank(rhs)
  • (C2) size(window_strides) = N - 2
  • (C3) 0 < window_strides
  • (C4) shape(padding) = [N - 2, 2]
  • (C5) size(lhs_dilation) = N - 2.
  • (C6) 0 < lhs_dilation
  • (C7) size(rhs_dilation) = N - 2
  • (C8) 0 < rhs_dilation
  • (C9) size(window_reversal) = N - 2.
  • (C10) dim(lhs, input_batch_dimension) % batch_group_count = 0
  • (C11) dim(lhs, input_feature_dimension) % feature_group_count = 0.
  • (C12) size(input_spatial_dimensions) = N - 2
  • (C13) بالنظر إلى input_dimensions = [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]:
    • is_unique(input_dimensions).
    • 0 <= input_dimensions < N.
  • (C14) dim(rhs, kernel_input_feature_dimension) = dim(lhs, input_feature_dimension) / feature_group_count.
  • (C15) dim(rhs, kernel_output_feature_dimension) % batch_group_count = 0.
  • (C16) dim(rhs, kernel_output_feature_dimension) % feature_group_count = 0
  • (C17) size(kernel_spatial_dimensions) = N - 2.
  • (C18) Given kernel_dimensions = kernel_spatial_dimensions + [kernel_input_feature_dimension] + [kernel_output_feature_dimension]:
    • is_unique(kernel_dimensions).
    • 0 <= kernel_dimensions < N.
  • (C19) size(output_spatial_dimensions) = N - 2.
  • (C20) Given output_dimensions = [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]:
    • is_unique(output_dimensions).
    • 0 <= output_dimensions < N.
  • (C21) 0 < feature_group_count.
  • (C22) 0 < batch_group_count
  • (C23) feature_group_count = 1 or batch_group_count = 1.
  • (C24) size(precision_config) = 2
  • يتم تعريف (C25) dim(result, result_dim) على النحو التالي:
    • dim(lhs, input_batch_dimension) / batch_group_count إذا result_dim = output_batch_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).
  • إذا كانت العملية تستخدم موترات كمية:
    • (C28) is_quantized(lhs) = is_quantized(result) and is_quantized(rhs).
    • (C29) إذا كان is_per_axis_quantized(rhs)، فيكون quantization_dimension(rhs) = kernel_output_feature_dimension.
    • (C30) إذا كان is_per_axis_quantized(result)، إذًا quantization_dimension(result) = output_feature_dimension.
    • إذا is_quantized(lhs):
    • (C31) storage_type(lhs) = storage_type(rhs)
    • (C32) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result).
    • (C33) If is_per_tensor_quantized(rhs), then is_per_tensor_quantized(result).
    • إذا !is_quantized(lhs):
    • (C34) element_type(lhs) = expressed_type(rhs) = element_type(result).

أمثلة

// %lhs: [[
//        [
//          [1], [2], [5], [6]
//        ],
//        [
//          [3], [4], [7], [8]
//        ],
//        [
//          [10], [11], [14], [15]
//        ],
//        [
//          [12], [13], [16], [17]
//        ]
//      ]]
//
// %rhs: [
//        [[[1]], [[1]], [[1]]],
//        [[[1]], [[1]], [[1]]],
//        [[[1]], [[1]], [[1]]]
//       ]
%result = "stablehlo.convolution"(%lhs, %rhs) {
  window_strides = array<i64: 4, 4>,
  padding = dense<0> : tensor<2x2xi64>,
  lhs_dilation = array<i64: 2, 2>,
  rhs_dilation = array<i64: 1, 1>,
  window_reversal = array<i1: false, false>,
  // In the StableHLO dialect, dimension numbers are encoded via:
  // `[<input dimensions>]x[<kernel dimensions>]->[output dimensions]`.
  // "b" is batch dimension, "f" is feature dimension,
  // "i" is input feature dimension, "o" is output feature dimension,
  // "0/1/etc" are spatial dimensions.
  dimension_numbers = #stablehlo.conv<[b, 0, 1, f]x[0, 1, i, o]->[b, 0, 1, f]>,
  batch_group_count = 1 : i64,
  feature_group_count = 1 : i64,
  precision_config = [#stablehlo<precision DEFAULT>, #stablehlo<precision DEFAULT>]
} : (tensor<1x4x4x1xi64>, tensor<3x3x1x1xi64>) -> tensor<1x2x2x1xi64>
// %result: [[
//            [[10], [26]],
//            [[46], [62]]
//          ]]

 مزيد من الأمثلة

جيب التمام

الدلالات

تُجري عملية جيب التمام على مستوى كل عنصر في الموتر operand وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأرقام العشرية: cos من IEEE-754
  • بالنسبة إلى الأعداد المركّبة: جيب التمام المركّب
  • بالنسبة إلى الأنواع الكمية: dequantize_op_quantize(cosine, operand, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

النواتج

الاسم النوع القيود
result موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %operand: [
//            [0.0, 1.57079632],       // [0, pi/2]
//            [3.14159265, 4.71238898] // [pi, 3pi/2]
//           ]
%result = "stablehlo.cosine"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[1.0, 0.0], [-1.0, 0.0]]

 مزيد من الأمثلة

count_leading_zeros

الدلالات

تُجري هذه العملية حسابًا على مستوى العناصر لعدد وحدات البت الصفرية الأولية في موتر operand وتنتج موتر result.

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه متعدّد الأبعاد من نوع عدد صحيح (C1)

النواتج

الاسم النوع القيود
result متّجه متعدّد الأبعاد من نوع عدد صحيح (C1)

القيود

  • (C1) type(operand) = type(result)

أمثلة

// %operand: [[0, 1], [128, -1]]
%result = "stablehlo.count_leading_zeros"(%operand) : (tensor<2x2xi64>) -> tensor<2x2xi64>
// %result: [[64, 63], [56, 0]]

 مزيد من الأمثلة

custom_call

الدلالات

تغليف عملية call_target_name محددة التنفيذ تأخذ inputs وcalled_computations وتنتج results يمكن استخدام has_side_effect وbackend_config وapi_version لتوفير بيانات وصفية إضافية يحدّدها التنفيذ.

في الوقت الحالي، تحتوي هذه العملية على مجموعة غير منظَّمة إلى حدّ ما من البيانات الوصفية التي تعكس التطور الطبيعي للعملية المقابلة في برنامج XLA المجمّع. نخطّط في المستقبل لتوحيد هذه البيانات الوصفية (#741).

المدخلات

التصنيف الاسم النوع
(I1) inputs عدد متغيّر من القيم
(I2) call_target_name ثابت من النوع string
(I3) has_side_effect ثابت من النوع i1
(I4) backend_config ثابت من النوع string أو قاموس السمات
(I5) api_version ثابت من النوع si32
(I6) called_computations عدد متغير من الثوابت من النوع string
(I7) output_operand_aliases تحديد أجزاء التوافق في النتائج والمعاملات

النواتج

الاسم النوع
results عدد متغيّر من القيم

(دعم وحدة معالجة الرسومات في XLA) استهدافات خاصة في custom_call

هناك ثلاثة أنواع خاصة من call_target_name مرتبطة بأنواع buffer: ينشئ CreateBuffer buffer غير مهيأ، وينشئ Pin buffer مهيأ، ويزيل Unpin تخصيص buffer ويعرض محتوى buffer.

%uninitialized_buffer = "stablehlo.custom_call"() {
  call_target_name = "CreateBuffer",
  api_version = 4 : i32,
} : () -> memref<4xf64>

%initialized_buffer = "stablehlo.custom_call"(%init_value) {
  call_target_name = "Pin",
  api_version = 4 : i32,
} : (tensor<4xf64>) -> memref<4xf64>

%dealloc_buffer = "stablehlo.custom_call"(%initialized_buffer) {
  call_target_name = "Unpin",
  api_version = 4 : i32,
} : (memref<4xf64>) -> tensor<4xf64>

الاسم المستعار

قد تتطلّب بعض عمليات custom_call جزءًا في النواتج وجزءًا في المعامِلات لمشاركة الذاكرة نفسها. ويمكن التعبير عن ذلك باستخدام output_operand_aliases. يتألف تمثيل زوج الأسماء المستعارة من قائمة بفهارس الصفوف الناتجة التي تمثّل الجزء الناتج، وفهرس operand_index مع قائمة بفهارس صفوف المعامِلات التي تمثّل جزء المعامِلات. تكون قائمة فهارس مجموعة البيانات الناتجة أو مجموعة البيانات الخاصة بالمعامل فارغة إذا لم يكن النوع المقابل نوع tuple، ويمكن أن تكون طويلة بشكل عشوائي لنوع مجموعة بيانات متداخلة بشكل عشوائي. وهذا مشابه لتمثيل الاسم المستعار XLA.

يجب أن يكون لجزء الإخراج وجزء الإدخال في زوج الاسم المستعار النوع نفسه. بالنسبة إلى عمليات custom_call التي لا تتضمّن استدعاءً إلى CreateBuffer وPin وUnpin، يمكن أن يظهر المعامل buffer في زوج واحد على الأكثر من الأسماء المستعارة، ويجب أن يظهر الناتج buffer في زوج واحد من الأسماء المستعارة.

أمثلة

%results = "stablehlo.custom_call"(%input0) {
  call_target_name = "foo",
  has_side_effect = false,
  backend_config = {bar = 42 : i32},
  api_version = 4 : i32,
  called_computations = [@foo]
} : (tensor<f64>) -> tensor<f64>

%updated_buffer = "stablehlo.custom_call"(%buffer) {
  call_target_name = "Update",
  api_version = 4 : i32,
  output_operand_aliases = [
    #stablehlo.output_operand_alias<output_tuple_indices = [],
      operand_index = 0,
      operand_tuple_indices = []>]
} : (memref<4xf64>) -> memref<4xf64>

قسمة

الدلالات

تُجري هذه الدالة عملية قسمة على مستوى العناصر بين موترَي المقسوم lhs والمقسوم عليه rhs، وتنتج موترًا result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأعداد الصحيحة: القسمة على عدد صحيح التي تنتج خارج القسمة الجبرية مع تجاهل أي جزء كسري
  • بالنسبة إلى الأرقام العشرية: division من IEEE-754.
  • بالنسبة إلى الأعداد المركّبة: القسمة المركّبة
  • بالنسبة إلى الأنواع الكمية:
    • dequantize_op_quantize(divide, lhs, rhs, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs متّجه من نوع عدد صحيح أو عدد ذي فاصلة عشرية أو عدد مركّب أو متّجه كمّي لكل متّجه (C1)
(I2) rhs متّجه من نوع عدد صحيح أو عدد ذي فاصلة عشرية أو عدد مركّب أو متّجه كمّي لكل متّجه (C1)

النواتج

الاسم النوع القيود
result موتر من النوع الصحيح أو النقطة العائمة أو المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)

أمثلة

// %lhs: [17.1, -17.1, 17.1, -17.1]
// %rhs: [3.0, 3.0, -3.0, -3.0]
%result = "stablehlo.divide"(%lhs, %rhs) : (tensor<4xf32>, tensor<4xf32>) -> tensor<4xf32>
// %result: [5.66666651, -5.66666651, -5.66666651, 5.66666651]

 مزيد من الأمثلة

dot_general

الدلالات

تحسب هذه الدالة الضرب النقطي بين شرائح lhs وشرائح rhs، وتنتج موترًا result.

بشكل أكثر رسمية، result[result_index] = dot_product، حيث:

  • lhs_result_dimensions = [d for d in axes(lhs) and d not in lhs_batching_dimensions and d not in lhs_contracting_dimensions].
  • rhs_result_dimensions = [d for d in axes(rhs) and d not in rhs_batching_dimensions and d not in rhs_contracting_dimensions].
  • result_batching_index + result_lhs_index + result_rhs_index = result_index حيث size(result_batching_index) = size(lhs_batching_dimensions) وsize(result_lhs_index) = size(lhs_result_dimensions) وsize(result_rhs_index) = size(rhs_result_dimensions).
  • transposed_lhs = transpose(lhs, lhs_batching_dimensions + lhs_result_dimensions + lhs_contracting_dimensions).
  • transposed_lhs_slice = slice(transposed_lhs, result_batching_index + result_lhs_index + [:, ..., :]).
  • reshaped_lhs_slice = reshape(transposed_lhs_slice, dims(lhs, lhs_contracting_dimensions)).
  • transposed_rhs = transpose(rhs, rhs_batching_dimensions + rhs_result_dimensions + rhs_contracting_dimensions).
  • transposed_rhs_slice = slice(transposed_rhs, result_batching_index + result_rhs_index + [:, ..., :]).
  • reshaped_rhs_slice = reshape(transposed_rhs_slice, dims(rhs, rhs_contracting_dimensions)).
  • dot_product = reduce( inputs=[multiply(reshaped_lhs_slice, reshaped_rhs_slice)], init_values=[constant(0, element_type(result))], dimensions=range(size(lhs_contracting_dimensions)), body=lambda x, y: add(x, y)).

بالنسبة إلى الأنواع المحدَّدة الكمية، يتم تنفيذ dequantize_op_quantize( lambda lhs, rhs: dot_general(lhs, rhs, lhs_batching_dimensions, rhs_batching_dimensions, lhs_contracting_dimensions, rhs_contracting_dimensions, precision_config), lhs, rhs, type(result)).

بالنسبة إلى الأنواع الكمية المختلطة، يتم تنفيذ hybrid_dequantize_then_op( lambda lhs, rhs: dot_general(lhs, rhs, lhs_batching_dimensions, rhs_batching_dimensions, lhs_contracting_dimensions, rhs_contracting_dimensions, precision_config), lhs, rhs).

يتحكّم precision_config في المفاضلة بين السرعة والدقة في العمليات الحسابية التي تتم على الخلفيات المسرّعة. يمكن أن تكون إحدى القيم التالية (في الوقت الحالي، لم يتم تحديد دلالات قيم التعداد هذه بشكل كافٍ، ولكننا نخطط لمعالجة ذلك في #755):

  • DEFAULT: أسرع عملية حسابية، ولكنّها تقدّم أقل تقريب دقيق للعدد الأصلي.
  • HIGH: عملية حسابية أبطأ، ولكنها تقدّم تقريبًا أكثر دقة للرقم الأصلي.
  • HIGHEST: أبطأ عملية حسابية، ولكنّها تقدّم أفضل تقريب للعدد الأصلي.

تحدّد السمة DotAlgorithm الخصائص الرئيسية للخوارزمية المستخدَمة لتنفيذ عملية النقطة، والتي تحدّد أيضًا الدقة. في حال ضبط حقول سمة الخوارزمية، يجب أن تكون قيمة precision_config هي DEFAULT. لا تتضمّن DotAlgorithms قيمة تلقائية، لأنّ المَعلمات التلقائية يتم تحديدها حسب التنفيذ. وبالتالي، يمكن ضبط جميع حقول خوارزمية النقطة على None لتحديد خوارزمية نقطة فارغة، والتي ستستخدم بدلاً من ذلك القيمة precision_config.

تشمل حقول DotAlgorithm ما يلي:

  • lhs_precision_type وrhs_precision_type، وهما الدقتان اللتان يتم تقريب الطرف الأيمن والأيسر من العملية إليهما. تكون أنواع الدقة مستقلة عن أنواع التخزين الخاصة بالمدخلات والمخرجات.
  • accumulation_type الدقة المستخدَمة للتجميع
  • تنطبق lhs_component_count وrhs_component_count وnum_primitive_operations عندما ننفّذ خوارزمية تقسّم الجانب الأيمن و/أو الأيسر إلى عدة مكونات وتنفّذ عمليات ضرب "أولية" متعددة على هذه القيم، وذلك عادةً لمحاكاة دقة أعلى (على سبيل المثال، استخدام نوع بيانات الذكاء الاصطناعي bfloat16 لإجراء عمليات حسابية بدقة أعلى: bf16_6x tf32_3x وما إلى ذلك). بالنسبة إلى الخوارزميات التي لا تتضمّن تحليلًا، يجب ضبط هذه القيم على 1.
  • allow_imprecise_accumulation لتحديد ما إذا كان يُسمح بالتجميع بدقة أقل لبعض الخطوات (مثل CUBLASLT_MATMUL_DESC_FAST_ACCUM).

مثال على سمات DotAlgorithm:

// Inputs are casted to tf32, and then accumulated in f32:
{lhs_precision_type = tf32,
 rhs_precision_type = tf32,
 accumulation_type = f32,
 lhs_component_count = 1,
 rhs_component_count = 1,
 num_primitive_operations = 1,
 allow_imprecise_accumulation = false}


// bf16_6x: each input is decomposed to 3 bf16 components, then 6 dot operations are done on those components, and the result is accumulated in f32.
{lhs_precision_type = bf16,
 rhs_precision_type = bf16,
 accumulation_type = f32,
 lhs_component_count = 3,
 rhs_component_count = 3,
 num_primitive_operations = 6,
 allow_imprecise_accumulation = false}


// Inputs are (casted to) f8e5m2, and we accumulate in f32, but for some steps we may accumulate in lower precision.
{lhs_precision_type = f8e5m2,
 rhs_precision_type = f8e5m2,
 accumulation_type = f32,
 lhs_component_count = 1,
 rhs_component_count = 1,
 num_primitive_operations = 1,
 allow_imprecise_accumulation = true}

ويعود إلى عمليات التنفيذ تحديد المجموعات المتوافقة. بشكل عام، ليس من المضمون أن تتوافق كل خوارزمية مع كل نوع من أدوات التسريع التي يستخدمها المستهلك في StableHLO. إذا كانت خوارزمية معيّنة غير متوافقة، يجب عرض رسالة خطأ بدلاً من استخدام خوارزمية بديلة. ستوفّر عملية التحقّق من StableHLO أفضل جهد ممكن للتحقّق، ما يمنع الخوارزميات التي لا يُعرف أنّها متوافقة مع أي أجهزة.

اطّلِع على xla_data.proto > Algorithm للاطّلاع على بعض قيم الخوارزميات المتوافقة. يتضمّن تذكرة الدعم رقم 2483 خطة لإنشاء مستند مركزي حول الخوارزميات المتوافقة مع الواجهة الخلفية.

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs متّجه أو متّجه كمّي لكل متّجه (C5-C6), (C9-C10), (C12-C14), (C17-C18), (C20)
(I2) rhs متّجه أو متّجه كمّي (C7-C10), (C12-C20)
(I3) lhs_batching_dimensions ثابت موتر أحادي البُعد من النوع si64 (C1)، (C3)، (C5)، (C9)، (C12)
(I4) rhs_batching_dimensions ثابت موتر أحادي البُعد من النوع si64 (C1)، (C4)، (C7)، (C9)
(I5) lhs_contracting_dimensions ثابت موتر أحادي البُعد من النوع si64 (C2)، (C3)، (C6)، (C10)
(I6) rhs_contracting_dimensions ثابت موتر أحادي البُعد من النوع si64 (C2)، (C4)، (C8)، (C10)، (C16)
(I7) precision_config عدد متغير من تعدادات DEFAULT وHIGH وHIGHEST (C11)، (C21)
(I8) lhs_precision_type FloatType أو TensorFloat32 (C21)
(I9) rhs_precision_type FloatType أو TensorFloat32 (C21)
(I10) accumulation_type FloatType أو TensorFloat32 (C21)
(I11) lhs_component_count ثابت من النوع si32 (C21)، (C22)
(I12) rhs_component_count ثابت من النوع si32 (C21)، (C23)
(I13) num_primitive_operations ثابت من النوع si32 (C21)، (C24)
(I14) allow_imprecise_accumulation ثابت من النوع bool (C21)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي (C12)، (C14)، (C18-C20)

القيود

  • (C1) size(lhs_batching_dimensions) = size(rhs_batching_dimensions)
  • (C2) size(lhs_contracting_dimensions) = size(rhs_contracting_dimensions)
  • (C3) is_unique(lhs_batching_dimensions + lhs_contracting_dimensions)
  • (C4) is_unique(rhs_batching_dimensions + rhs_contracting_dimensions)
  • (C5) 0 <= lhs_batching_dimensions < rank(lhs).
  • (C6) 0 <= lhs_contracting_dimensions < rank(lhs)
  • (C7) 0 <= rhs_batching_dimensions < rank(rhs)
  • (C8) 0 <= rhs_contracting_dimensions < rank(rhs)
  • (C9) dim(lhs, lhs_batching_dimensions...) = dim(rhs, rhs_batching_dimensions...).
  • (C10) dim(lhs, lhs_contracting_dimensions...) = dim(rhs, rhs_contracting_dimensions...)
  • (C11) size(precision_config) = 2.
  • (C12) shape(result) = dim(lhs, lhs_batching_dimensions) + dim(lhs, lhs_result_dimensions) + dim(rhs, rhs_result_dimensions)
  • إذا كانت العملية تستخدم موترات غير كمّية:
    • (C13) element_type(lhs) = element_type(rhs).
  • إذا كانت العملية تستخدم موترات كمية:
    • (C14) is_quantized(lhs) = is_quantized(result) and is_quantized(rhs).
    • (C15) zero_points(rhs) = 0.
    • (C16) إذا كان is_per_axis_quantized(rhs)، فإنّ quantization_dimension(rhs) غير مضمّن في rhs_contracting_dimensions.
    • إذا is_quantized(lhs):
    • (C17) storage_type(lhs) = storage_type(rhs).
    • (C18) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
    • (C19) If is_per_tensor_quantized(rhs), then is_per_tensor_quantized(result).
    • إذا !is_quantized(lhs):
    • (C20) element_type(lhs) = expressed_type(rhs) = element_type(result)
  • إذا كان !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

أمثلة

// %lhs: [
//        [[1, 2],
//         [3, 4]],
//        [[5, 6],
//         [7, 8]]
//       ]
// %rhs: [
//        [[1, 0],
//         [0, 1]],
//        [[1, 0],
//         [0, 1]]
//       ]
%result = "stablehlo.dot_general"(%lhs, %rhs) {
  dot_dimension_numbers = #stablehlo.dot<
    lhs_batching_dimensions = [0],
    rhs_batching_dimensions = [0],
    lhs_contracting_dimensions = [2],
    rhs_contracting_dimensions = [1]
  >,
  precision_config = [#stablehlo<precision DEFAULT>, #stablehlo<precision DEFAULT>],
  algorithm = #stablehlo.dot_algorithm<
    lhs_precision_type = tf32,
    rhs_precision_type = tf32,
    accumulation_type = f32,
    lhs_component_count = 1,
    rhs_component_count = 1,
    num_primitive_operations = 1,
    allow_imprecise_accumulation = false
  >
} : (tensor<2x2x2xi64>, tensor<2x2x2xi64>) -> tensor<2x2x2xi64>
// %result: [
//           [[1, 2],
//            [3, 4]],
//           [[5, 6],
//            [7, 8]]
//          ]

 مزيد من الأمثلة

dynamic_broadcast_in_dim

الدلالات

هذه العملية مطابقة وظيفيًا للعملية broadcast_in_dim، ولكن يتم تحديد شكل النتيجة ديناميكيًا من خلال output_dimensions.

تقبل العملية أيضًا السمات الاختيارية known_expanding_dimensions وknown_nonexpanding_dimensions للتعبير عن المعرفة الثابتة بشأن سلوك التوسيع للأبعاد. في حال عدم تحديد هذه السمة، يُفترض أنّ جميع الأبعاد يمكن توسيعها.

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه أو متّجه كمّي (C1-C2), (C5-C6), (C9)
(I2) output_dimensions موتر أحادي البُعد من نوع عدد صحيح (C7)
(I3) broadcast_dimensions موتر ثابت أحادي البُعد من نوع عدد صحيح (C2-C6)
(I4) known_expanding_dimensions موتر ثابت أحادي البُعد من نوع عدد صحيح (C8-C9)
(I5) known_nonexpanding_dimensions موتر ثابت أحادي البُعد من نوع عدد صحيح (C8-C9)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي (C1)، (C3)، (C5-C7)

القيود

  • يتم تحديد (C1) element_type(result) من خلال:
    • element_type(operand)، إذا !is_per_axis_quantized(operand)
    • element_type(operand) باستثناء أنّ quantization_dimension(operand) وscales(operand) وzero_points(operand) قد تختلف عن quantization_dimension(result) وscales(result) وzero_points(result) على التوالي.
  • (C2) size(broadcast_dimensions) = rank(operand)
  • (C3) 0 <= broadcast_dimensions < rank(result)
  • (C4) is_unique(broadcast_dimensions)
  • (C5) لكل d في axes(operand):
    • dim(operand, d) = 1 أو
    • dim(operand, d) = dim(result, broadcast_dimensions[d]).
  • (C6) إذا كان is_per_axis_quantized(result):
    • quantization_dimension(result) = broadcast_dimensions[quantization_dimension(operand)].
    • إذا dim(operand, quantization_dimension(operand)) = 1، إذًا scales(result)[i] = scales(operand)[0] and zero_points(result)[i] = zero_points(operand)[0] for i in range(dim(result, quantization_dimension(result))).
  • (C7) size(output_dimensions) = rank(result)
  • (C8) is_unique(known_expanding_dimensions + known_nonexpanding_dimensions)
  • (C9) 0 <= known_expanding_dimensions < rank(operand).
  • (C10) 0 <= known_nonexpanding_dimensions < rank(operand)

أمثلة

// %operand: [
//            [1, 2, 3]
//           ]
%operand = stablehlo.constant dense<[[1, 2, 3]]> : tensor<1x3xi64>
%output_dimensions = stablehlo.constant dense<[2, 3, 2]> : tensor<3xi64>
%result = "stablehlo.dynamic_broadcast_in_dim"(%operand, %output_dimensions) {
  broadcast_dimensions = array<i64: 2, 1>,
  known_expanding_dimensions = array<i64: 0>,
  known_nonexpanding_dimensions = array<i64: 1>
} : (tensor<1x3xi64>, tensor<3xi64>) -> tensor<2x3x2xi64>
// %result: [
//            [
//             [1, 1],
//             [2, 2],
//             [3, 3]
//            ],
//            [
//             [1, 1],
//             [2, 2],
//             [3, 3]
//            ]
//          ]

 مزيد من الأمثلة

dynamic_conv

الدلالات

هذه العملية مماثلة وظيفيًا لعملية الالتفاف، ولكن يتم تحديد المساحة المتروكة ديناميكيًا من خلال padding.

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs متّجه أو متّجه كمّي لكل متّجه (C1)، (C10-C11)، (C14) (C25)، (C26-C27)، (C30-C31)، (C33)
(I2) rhs متّجه أو متّجه كمّي (C1)، (C14-C16)، (C26-C28)، (C30-C33)
(I3) padding موتر ثنائي الأبعاد من نوع عدد صحيح (C4)
(I4) window_strides ثابت موتر أحادي البُعد من النوع si64 (C2-C3)
(I5) lhs_dilation ثابت موتر أحادي البُعد من النوع si64 (C5-C6)
(I6) rhs_dilation ثابت موتر أحادي البُعد من النوع si64 (C7-C8)
(I7) window_reversal ثابت موتر أحادي البُعد من النوع i1 (C9)
(I8) input_batch_dimension ثابت من النوع si64 (C10)، (C13)
(I9) input_feature_dimension ثابت من النوع si64 (C11)، (C13-C14)
(I10) input_spatial_dimensions ثابت موتر أحادي البُعد من النوع si64 (C12)، (C13)
(I11) kernel_input_feature_dimension ثابت من النوع si64 (C14)، (C18)
(I12) kernel_output_feature_dimension ثابت من النوع si64 (C15-C16), (C18), (C28)
(I13) kernel_spatial_dimensions ثابت موتر أحادي البُعد من النوع si64 (C17-C18)
(I14) output_batch_dimension ثابت من النوع si64 (C20)
(I15) output_feature_dimension ثابت من النوع si64 (C20)، (C29)
(I16) output_spatial_dimensions ثابت موتر أحادي البُعد من النوع si64 (C19-C20)
(I17) feature_group_count ثابت من النوع si64 (C11)، (C14)، (C16)، (C21)، (C23)
(I18) batch_group_count ثابت من النوع si64 (C10)، (C15)، (C22)، (C23)
(I19) precision_config عدد متغير من تعدادات DEFAULT وHIGH وHIGHEST (C24)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي (C25-C27), (C29), (C31-C33)

القيود

  • (C1) N = rank(lhs) = rank(rhs)
  • (C2) size(window_strides) = N - 2
  • (C3) 0 < window_strides
  • (C4) shape(padding) = [N - 2, 2]
  • (C5) size(lhs_dilation) = N - 2.
  • (C6) 0 < lhs_dilation
  • (C7) size(rhs_dilation) = N - 2
  • (C8) 0 < rhs_dilation
  • (C9) size(window_reversal) = N - 2.
  • (C10) dim(lhs, input_batch_dimension) % batch_group_count = 0
  • (C11) dim(lhs, input_feature_dimension) % feature_group_count = 0.
  • (C12) size(input_spatial_dimensions) = N - 2
  • (C13) بالنظر إلى input_dimensions = [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]:
    • is_unique(input_dimensions).
    • 0 <= input_dimensions < N.
  • (C14) dim(rhs, kernel_input_feature_dimension) = dim(lhs, input_feature_dimension) / feature_group_count.
  • (C15) dim(rhs, kernel_output_feature_dimension) % batch_group_count = 0.
  • (C16) dim(rhs, kernel_output_feature_dimension) % feature_group_count = 0
  • (C17) size(kernel_spatial_dimensions) = N - 2.
  • (C18) Given kernel_dimensions = kernel_spatial_dimensions + [kernel_input_feature_dimension] + [kernel_output_feature_dimension]:
    • is_unique(kernel_dimensions).
    • 0 <= kernel_dimensions < N.
  • (C19) size(output_spatial_dimensions) = N - 2.
  • (C20) Given output_dimensions = [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]:
    • is_unique(output_dimensions).
    • 0 <= output_dimensions < N.
  • (C21) 0 < feature_group_count.
  • (C22) 0 < batch_group_count
  • (C23) feature_group_count = 1 or batch_group_count = 1.
  • (C24) size(precision_config) = 2
  • يتم تعريف (C25) dim(result, result_dim) على النحو التالي:
    • dim(lhs, input_batch_dimension) / batch_group_count إذا result_dim = output_batch_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).
  • إذا كانت العملية تستخدم موترات كمية:
    • (C28) is_quantized(lhs) = is_quantized(result) and is_quantized(rhs).
    • (C29) إذا كان is_per_axis_quantized(rhs)، فيكون quantization_dimension(rhs) = kernel_output_feature_dimension.
    • (C30) إذا كان is_per_axis_quantized(result)، إذًا quantization_dimension(result) = output_feature_dimension.
    • إذا is_quantized(lhs):
    • (C31) storage_type(lhs) = storage_type(rhs)
    • (C32) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result).
    • (C33) If is_per_tensor_quantized(rhs), then is_per_tensor_quantized(result).
    • إذا !is_quantized(lhs):
    • (C34) element_type(lhs) = expressed_type(rhs) = element_type(result).

أمثلة

// %lhs: [[
//        [[1], [2], [5], [6]],
//        [[3], [4], [7], [8]],
//        [[10], [11], [14], [15]],
//        [[12], [13], [16], [17]]
//      ]]
//
// %rhs: [
//         [[[1]], [[1]], [[1]]],
//         [[[1]], [[1]], [[1]]],
//         [[[1]], [[1]], [[1]]]
//        ]
// %padding: [[1, 1],
//            [1, 1]]
%result = "stablehlo.dynamic_conv"(%lhs, %rhs, %padding) {
  window_strides = array<i64: 4, 4>,
  lhs_dilation = array<i64: 2, 2>,
  rhs_dilation = array<i64: 1, 1>,
  window_reversal = array<i1: false, false>,
  dimension_numbers = #stablehlo.conv<raw
    input_batch_dimension = 0,
    input_feature_dimension = 3,
    input_spatial_dimensions = [0, 1],
    kernel_input_feature_dimension = 2,
    kernel_output_feature_dimension = 3,
    kernel_spatial_dimensions = [0, 1],
    output_batch_dimension = 0,
    output_feature_dimension = 3,
    output_spatial_dimensions = [1, 2]
  >,
  feature_group_count = 1 : i64,
  batch_group_count = 1 : i64,
  precision_config = [#stablehlo<precision DEFAULT>, #stablehlo<precision DEFAULT>]
} : (tensor<1x4x4x1xi64>, tensor<3x3x1x1xi64>, tensor<2x2xi64>) -> tensor<1x2x2x1xi64>
// %result: [[
//            [[1], [5]],
//            [[10], [14]]
//          ]]

 مزيد من الأمثلة

dynamic_gather

الدلالات

هذه العملية مماثلة وظيفيًا لعملية gather ، مع تحديد slice_sizes بشكل ديناميكي كقيمة.

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه أو متّجه كمّي لكل متّجه (C1)، و(C7)، و(C10-C12)، و(C14)
(I2) start_indices متّجه متعدّد الأبعاد من نوع عدد صحيح (C2)، (C3)، (C13)
(I3) slice_sizes موتر أحادي البُعد من نوع عدد صحيح (C8)، (C11-C13)
(I4) offset_dims ثابت موتر أحادي البُعد من النوع si64 (C1)، (C4-C5)، (C13)
(I5) collapsed_slice_dims ثابت موتر أحادي البُعد من النوع si64 (C1), (C6-C8), (C13)
(I6) start_index_map ثابت موتر أحادي البُعد من النوع si64 (C3)، (C9)، (C10)
(I7) index_vector_dim ثابت من النوع si64 (C2)، (C3)، (C13)
(I8) indices_are_sorted ثابت من النوع i1

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي لكل متّجه (C5)، (C13-C14)

القيود

  • (C1) rank(operand) = size(offset_dims) + size(collapsed_slice_dims)
  • (C2) 0 <= index_vector_dim <= rank(start_indices)
  • (C3) size(start_index_map) = index_vector_dim < rank(start_indices) ? dim(start_indices, index_vector_dim) : 1
  • (C4) is_unique(offset_dims) and is_sorted(offset_dims)
  • (C5) 0 <= offset_dims < rank(result).
  • (C6) is_unique(collapsed_slice_dims) and is_sorted(collapsed_slice_dims)
  • (C7) 0 <= collapsed_slice_dims < rank(operand)
  • (C8) slice_sizes[collapsed_slice_dims...] <= 1
  • (C9) is_unique(start_index_map).
  • (C10) 0 <= start_index_map < rank(operand)
  • (C11) size(slice_sizes) = rank(operand).
  • (C12) 0 <= slice_sizes <= shape(operand)
  • (C13) shape(result) = combine(batch_dim_sizes, offset_dim_sizes) حيث:
    • batch_dim_sizes = shape(start_indices) باستثناء أنّه لم يتم تضمين حجم السمة start_indices الذي يتوافق مع index_vector_dim.
    • offset_dim_sizes = shape(slice_sizes) باستثناء أحجام السمات في slice_sizes المقابلة collapsed_slice_dims غير مضمّنة.
    • تضع الدالة combine batch_dim_sizes في المحاور التي تتوافق مع batch_dims وoffset_dim_sizes في المحاور التي تتوافق مع offset_dims.
  • (C14) element_type(operand) = element_type(result).

أمثلة

// %operand: [
//            [[1, 2], [3, 4], [5, 6], [7, 8]],
//            [[9, 10],[11, 12], [13, 14], [15, 16]],
//            [[17, 18], [19, 20], [21, 22], [23, 24]]
//           ]
// %start_indices: [
//                  [[0, 0], [1, 0], [2, 1]],
//                  [[0, 1], [1, 1], [0, 2]]
//                 ]
// %slize_sizes: [1, 2, 2]
%result = "stablehlo.dynamic_gather"(%operand, %start_indices, %slize_sizes) {
  dimension_numbers = #stablehlo.gather<
    offset_dims = [2, 3],
    collapsed_slice_dims = [0],
    start_index_map = [1, 0],
    index_vector_dim = 2>,
  indices_are_sorted = false
} : (tensor<3x4x2xi64>, tensor<2x3x2xi64>, tensor<3xi64>) -> tensor<2x3x2x2xi64>
// %result: [
//            [
//              [[1, 2], [3, 4]],
//              [[3, 4], [5, 6]],
//              [[13, 14], [15, 16]]
//            ],
//            [
//              [[9, 10], [11, 12]],
//              [[11, 12], [13, 14]],
//              [[17, 18], [19, 20]]
//            ]
//          ]

 مزيد من الأمثلة

dynamic_iota

الدلالات

هذه العملية مماثلة وظيفيًا لعملية iota، ولكن يتم تحديد شكل النتيجة بشكل ديناميكي من خلال output_shape.

المدخلات

التصنيف الاسم النوع القيود
(I1) output_shape موتر أحادي البُعد من نوع عدد صحيح (C1)، (C2)
(I2) iota_dimension si64 (C1)

النواتج

الاسم النوع القيود
result موتر من النوع الصحيح أو النقطة العائمة أو المركّب أو موتر كمّي لكل موتر (C2)

القيود

  • (C1) 0 <= iota_dimension < size(output_shape)
  • (C2) rank(result) = size(output_shape)

أمثلة

%output_shape = stablehlo.constant dense<[4, 5]> : tensor<2xi64>
%result = "stablehlo.dynamic_iota"(%output_shape) {
  iota_dimension = 0 : i64
} : (tensor<2xi64>) -> tensor<4x5xi64>
// %result: [
//           [0, 0, 0, 0, 0],
//           [1, 1, 1, 1, 1],
//           [2, 2, 2, 2, 2],
//           [3, 3, 3, 3, 3]
//          ]

 مزيد من الأمثلة

dynamic_pad

الدلالات

هذه العملية مطابقة وظيفيًا لعملية pad، ولكن مع تحديد edge_padding_low وedge_padding_high وinterior_padding بشكل ديناميكي كقيم.

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه أو متّجه كمّي لكل متّجه (C1)، (C2)، (C4)
(I2) padding_value متّجه صفري الأبعاد أو متّجه كمّي لكل متّجه (C1)
(I3) edge_padding_low موتر أحادي البُعد من نوع عدد صحيح (C1)، (C4)
(I4) edge_padding_high موتر أحادي البُعد من نوع عدد صحيح (C1)، (C4)
(I5) interior_padding موتر أحادي البُعد من نوع عدد صحيح (C2-C4)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي لكل متّجه (C3-C6)

القيود

  • (C1) element_type(operand) = element_type(padding_value) = element_type(result)
  • (C2) size(edge_padding_low) = size(edge_padding_high) = size(interior_padding) = rank(operand)
  • (C3) 0 <= interior_padding
  • (C4) shape(result) = shape(operand) + edge_padding_low + max(shape(operand) - 1, 0) * interior_padding + edge_padding_high

أمثلة

// %operand: [
//            [1, 2, 3],
//            [4, 5, 6]
//           ]
// %padding_value: 0
// %edge_padding_low: [0, 1]
// %edge_padding_high: [2, 1]
// %interior_padding: [1, 2]
%result = "stablehlo.dynamic_pad"(%operand, %padding_value,
  %edge_padding_low, %edge_padding_high, %interior_padding
) : (tensor<2x3xi64>, tensor<i64>, tensor<2xi64>, tensor<2xi64>, tensor<2xi64>) -> tensor<5x9xi64>
// %result: [
//           [0, 1, 0, 0, 2, 0, 0, 3, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0],
//           [0, 4, 0, 0, 5, 0, 0, 6, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0]
//          ]

 مزيد من الأمثلة

dynamic_reshape

الدلالات

هذه العملية مطابقة وظيفيًا لعملية reshape، ولكن يتم تحديد شكل النتيجة بشكل ديناميكي من خلال output_shape.

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه أو متّجه كمّي (C1-C3)
(I2) output_shape موتر أحادي البُعد من نوع عدد صحيح (C4)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي (C1-C4)

القيود

  • يتم تحديد (C1) element_type(result) من خلال:
    • element_type(operand)، إذا !is_per_axis_quantized(operand)
    • element_type(operand) باستثناء أنّ quantization_dimension(operand) وquantization_dimension(result) قد يختلفان.
  • (C2) size(operand) = size(result)
  • (C3) إذا كان is_per_axis_quantized(operand):
    • reduce(dims(operand, [0, 1, ..., quantization_dimension(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [0, 1, ..., quantization_dimension(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y).
    • dim(operand, quantization_dimension(operand)) = dim(result, quantization_dimension(result)).
    • reduce(dims(operand, [quantization_dimension(operand) + 1, ..., rank(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [quantization_dimension(result) + 1, ..., rank(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y).
  • (C4) size(output_shape) = rank(result)

أمثلة

// %operand: [[1, 2, 3], [4, 5, 6]]
// %output_shape: [3, 2]
%result = "stablehlo.dynamic_reshape"(%operand, %output_shape) : (tensor<2x3xi64>, tensor<2xi64>) -> tensor<3x2xi64>
// %result: [[1, 2], [3, 4], [5, 6]]

 مزيد من الأمثلة

dynamic_slice

الدلالات

يستخرِج شريحة من operand باستخدام فهارس بدء محسوبة بشكل ديناميكي وينتج موتر result. تحتوي start_indices على فهارس البداية لشريحة كل سمة قابلة للتعديل، بينما تحتوي slice_sizes على أحجام شريحة كل سمة. بشكل أكثر رسمية، result[result_index] = operand[operand_index] حيث:

  • adjusted_start_indices = clamp(0, start_indices, shape(operand) - slice_sizes).
  • operand_index = adjusted_start_indices + result_index.

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه أو متّجه كمّي لكل متّجه (C1)، (C2)، (C4)
(I2) start_indices عدد متغير من موترات ذات 0 بُعد من النوع الصحيح (C2), (C3)
(I3) slice_sizes ثابت موتر أحادي البُعد من النوع si64 (C2)، (C4)، (C5)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي لكل متّجه (C1)، (C5)

القيود

  • (C1) element_type(operand) = element_type(result)
  • (C2) size(start_indices) = size(slice_sizes) = rank(operand)
  • (C3) same(type(start_indices...))
  • (C4) 0 <= slice_sizes <= shape(operand)
  • (C5) shape(result) = slice_sizes.

أمثلة

// %operand: [
//            [0, 0, 1, 1],
//            [0, 0, 1, 1],
//            [0, 0, 0, 0],
//            [0, 0, 0, 0]
//           ]
// %start_indices0: -1
// %start_indices1: 3
%result = "stablehlo.dynamic_slice"(%operand, %start_indices0, %start_indices1) {
  slice_sizes = array<i64: 2, 2>
} : (tensor<4x4xi32>, tensor<i64>, tensor<i64>) -> tensor<2x2xi32>
// %result: [
//           [1, 1],
//           [1, 1]
//          ]

 مزيد من الأمثلة

dynamic_update_slice

الدلالات

تُنشئ هذه الدالة موتر result يساوي موتر operand، باستثناء أنّه يتم تعديل الشريحة التي تبدأ عند start_indices باستخدام القيم في update. بشكل أكثر رسمية، يتم تعريف result[result_index] على النحو التالي:

  • update[update_index] إذا كان 0 <= update_index < shape(update) حيث:
    • adjusted_start_indices = clamp(0, start_indices, shape(operand) - shape(update)).
    • update_index = result_index - adjusted_start_indices.
  • operand[result_index] في ما عدا ذلك.

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه أو متّجه كمّي لكل متّجه (C1-C4), (C6)
(I2) update متّجه أو متّجه كمّي لكل متّجه (C2)، (C3)، (C6)
(I3) start_indices عدد متغير من موترات ذات 0 بُعد من النوع الصحيح (C4)، (C5)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي لكل متّجه (C1)

القيود

  • (C1) type(operand) = type(result)
  • (C2) element_type(update) = element_type(operand)
  • (C3) rank(update) = rank(operand)
  • (C4) size(start_indices) = rank(operand)
  • (C5) same(type(start_indices...)).
  • (C6) 0 <= shape(update) <= shape(operand)

أمثلة

// %operand: [
//            [1, 1, 0, 0],
//            [1, 1, 0, 0],
//            [1, 1, 1, 1],
//            [1, 1, 1, 1]
//           ]
// %update: [
//           [1, 1],
//           [1, 1]
//          ]
// %start_indices0: -1
// %start_indices1: 3
%result = "stablehlo.dynamic_update_slice"(%operand, %update, %start_indices0, %start_indices1)
  : (tensor<4x4xi32>, tensor<2x2xi32>, tensor<i64>, tensor<i64>) -> tensor<4x4xi32>
// %result: [
//           [1, 1, 1, 1],
//           [1, 1, 1, 1],
//           [1, 1, 1, 1],
//           [1, 1, 1, 1]
//          ]

 مزيد من الأمثلة

دالات أسية

الدلالات

تُجري هذه الدالة عملية أسية على مستوى كل عنصر في الموتر operand وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأرقام العشرية: exp من IEEE-754
  • بالنسبة إلى الأعداد المركّبة: الأس المركّب
  • بالنسبة إلى الأنواع الكمية: dequantize_op_quantize(exponential, operand, type(result))

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

النواتج

الاسم النوع القيود
result موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %operand: [[0.0, 1.0], [2.0, 3.0]]
%result = "stablehlo.exponential"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [[1.0, 2.7182818284590451], [7.3890560989306504, 20.085536923187668]]

 مزيد من الأمثلة

exponential_minus_one

الدلالات

تُجري هذه الدالة عملية طرح أسية بمقدار واحد على موتر operand وتنتج موتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأرقام العشرية: expm1 من IEEE-754
  • بالنسبة إلى الأعداد المركّبة: الأس المركّب ناقص واحد.
  • بالنسبة إلى الأنواع الكمية: dequantize_op_quantize(exponential_minus_one, operand, type(result))

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

النواتج

الاسم النوع القيود
result موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %operand: [0.0, 1.0]
%result = "stablehlo.exponential_minus_one"(%operand) : (tensor<2xf64>) -> tensor<2xf64>
// %result: [0.0, 1.71828187]

 مزيد من الأمثلة

fft

الدلالات

تُجري هذه الدالة عمليات تحويل &quot;فورييه&quot; المباشرة والعكسية للمدخلات والمخرجات الحقيقية والمركّبة.

يجب أن تكون قيمة fft_type إحدى القيم التالية:

  • FFT: تحويل فورييه السريع من عدد مركّب إلى عدد مركّب
  • IFFT: تحويل فورييه العكسي المعقّد إلى المعقّد
  • RFFT: تحويل فورييه السريع من الأعداد الحقيقية إلى الأعداد المركّبة
  • IRFFT: تحويل فورييه العكسي من حقيقي إلى معقّد (أي يأخذ أعدادًا معقّدة ويعرض أعدادًا حقيقية)

بشكل أكثر رسمية، إذا كانت لدينا الدالة fft التي تأخذ موترات أحادية البعد من أنواع معقّدة كمدخلات، وتنتج موترات أحادية البعد من الأنواع نفسها كمخرجات، وتحسب تحويل فورييه المنفصل:

بالنسبة إلى fft_type = FFT، يتم تعريف result على أنّه النتيجة النهائية لسلسلة من العمليات الحسابية L حيث L = size(fft_length). على سبيل المثال، بالنسبة إلى L = 3:

  • result1[i0, ..., :] = fft(operand[i0, ..., :]).
  • result2[i0, ..., :, iR-1] = fft(result1[i0, ..., :, iR-1]).
  • result[i0, ..., :, iR-2, iR-1] = fft(result2[i0, ..., :, iR-2, iR-1]).

بالإضافة إلى ذلك، إذا كانت لدينا الدالة ifft التي تتضمّن توقيع النوع نفسه وتحسب معكوس fft:

بالنسبة إلى fft_type = IFFT، يتم تعريف result على أنّه معكوس العمليات الحسابية الخاصة بـ fft_type = FFT. على سبيل المثال، بالنسبة إلى L = 3:

  • result1[i0, ..., :, iR-2, iR-1] = ifft(operand[i0, ..., :, iR-2, iR-1]).
  • result2[i0, ..., :, iR-1] = ifft(result1[i0, ..., :, iR-1]).
  • result[i0, ..., :] = ifft(result2[i0, ..., :]).

بالإضافة إلى ذلك، لنفترض الدالة rfft التي تأخذ موترات أحادية البُعد من أنواع الفاصلة العائمة، وتنتج موترات أحادية البُعد من أنواع مركّبة لها دلالات الفاصلة العائمة نفسها، وتعمل على النحو التالي:

  • rfft(real_operand) = truncated_result حيث
  • complex_operand... = (real_operand..., 0.0).
  • complex_result = fft(complex_operand).
  • truncated_result = complex_result[:(rank(complex_result) / 2 + 1)].

(عندما يتم حساب تحويل فورييه المنفصل للمعاملات الحقيقية، تحدد العناصر الأولى N/2 + 1 من النتيجة بشكل لا لبس فيه بقية النتيجة، لذلك يتم اقتطاع نتيجة rfft لتجنب حساب العناصر المكررة).

بالنسبة إلى fft_type = RFFT، يتم تعريف result على أنّه النتيجة النهائية لسلسلة من العمليات الحسابية L حيث L = size(fft_length). على سبيل المثال، بالنسبة إلى L = 3:

  • result1[i0, ..., :] = rfft(operand[i0, ..., :]).
  • result2[i0, ..., :, iR-1] = fft(result1[i0, ..., :, iR-1]).
  • result[i0, ..., :, iR-2, iR-1] = fft(result2[i0, ..., :, iR-2, iR-1]).

أخيرًا، بالنظر إلى الدالة irfft التي لها توقيع النوع نفسه وتحسب معكوس rfft:

بالنسبة إلى fft_type = IRFFT، يتم تعريف result على أنّه معكوس العمليات الحسابية الخاصة بـ fft_type = RFFT. على سبيل المثال، بالنسبة إلى L = 3:

  • result1[i0, ..., :, iR-2, iR-1] = ifft(operand[i0, ..., :, iR-2, iR-1]).
  • result2[i0, ..., :, iR-1] = ifft(result1[i0, ..., :, iR-1]).
  • result[i0, ..., :] = irfft(result2[i0, ..., :]).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من النوع النقطة العائمة أو النوع المركّب (C1)، (C2)، (C4)، (C5)
(I2) fft_type تعداد FFT وIFFT وRFFT وIRFFT (C2)، (C5)
(I3) fft_length ثابت موتر أحادي البُعد من النوع si64 (C1)، (C3)، (C4)

النواتج

الاسم النوع القيود
result موتر من النوع النقطة العائمة أو النوع المركّب (C2)، (C4)، (C5)

القيود

  • (C1) size(fft_length) <= rank(operand)
  • (C2) تختلف العلاقة بين نوعَي العنصرَين operand وresult على النحو التالي:
    • إذا كان fft_type = FFT وelement_type(operand) وelement_type(result) من النوع المعقّد نفسه
    • إذا كان fft_type = IFFT وelement_type(operand) وelement_type(result) من النوع المعقّد نفسه
    • إذا كان fft_type = RFFT وelement_type(operand) من أنواع الفاصلة العائمة، وكان element_type(result) من الأنواع المعقّدة التي تتضمّن دلالات الفاصلة العائمة نفسها.
    • إذا كان fft_type = IRFFT، element_type(operand) نوعًا معقّدًا وكان element_type(result) نوعًا ذا فاصلة عائمة يتضمّن دلالات الفاصلة العائمة نفسها.
  • (C3) 1 <= size(fft_length) <= 3
  • (C4) إذا كان هناك موتر real من نوع نقطة عائمة بين operand وresult، يكون الناتج shape(real)[-size(fft_length):] = fft_length.
  • (C5) shape(result) = shape(operand) باستثناء:
    • إذا كان fft_type = RFFT، dim(result, -1) = dim(operand, -1) = 0 ? 0 : dim(operand, -1) / 2 + 1.
    • إذا كان fft_type = IRFFT، dim(operand, -1) = dim(result, -1) = 0 ? 0 : dim(result, -1) / 2 + 1.

أمثلة

// %operand: [(1.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0)]
%result = "stablehlo.fft"(%operand) {
  fft_type = #stablehlo<fft_type FFT>,
  fft_length = array<i64: 4>
} : (tensor<4xcomplex<f32>>) -> tensor<4xcomplex<f32>>
// %result: [(1.0, 0.0), (1.0, 0.0), (1.0, 0.0), (1.0, 0.0)]

الطابق

الدلالات

تُجري هذه الدالة عملية حسابية على كل عنصر من عناصر الموتر operand لإنتاج الموتر result. تنفيذ عملية roundToIntegralTowardNegative من مواصفات IEEE-754 بالنسبة إلى الأنواع المحدَّدة القيمة، يتم تنفيذ dequantize_op_quantize(floor, operand, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C1)

النواتج

الاسم النوع القيود
result متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %operand: [-0.8166, -0.2530, 0.2530, 0.8166, 2.0]
%result = "stablehlo.floor"(%operand) : (tensor<5xf32>) -> tensor<5xf32>
// %result: [-1.0, -1.0, 0.0, 0.0, 2.0]

 مزيد من الأمثلة

جمع

الدلالات

تجمع هذه العملية شرائح من موتر operand من الإزاحات المحددة في start_indices وتنتج موتر result.

يوضّح المخطّط التالي كيفية ربط العناصر في result بالعناصر في operand باستخدام مثال ملموس. يختار المخطط بعض الأمثلة على result الفهارس ويشرح بالتفصيل الفهارس operand التي تتوافق معها.

جمع

بشكل أكثر رسمية، result[result_index] = operand[operand_index] حيث:

  • batch_dims = [d for d in axes(result) and d not in offset_dims].
  • batch_index = result_index[batch_dims...].
  • يتم تعريف start_index على النحو التالي:
    • start_indices[bi0, ..., :, ..., biN] حيث bi هي عناصر فردية في batch_index ويتم إدراج : في الفهرس index_vector_dim، إذا كان index_vector_dim < rank(start_indices).
    • [start_indices[batch_index]] في ما عدا ذلك.
  • بالنسبة إلى d_operand في axes(operand)،
    • full_start_index[d_operand] = clamp(start_index[d_start], 0, dim(operand, d_operand) - slice_sizes[d_operand]) if 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 هي عناصر فردية في offset_index، ويتم إدراج 0 في الفهارس من collapsed_slice_dims وoperand_batching_dims.
  • operand_index = full_start_index + full_batching_index + full_offset_index.

إذا كانت قيمة indices_are_sorted هي true، يمكن أن يفترض التنفيذ أنّ start_indices مرتبة حسب start_index_map، وإلا سيكون السلوك غير محدّد. بشكل أكثر رسمية، لكل i1 < i2 من indices(result)، full_start_index(i1) <= full_start_index(i2).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه أو متّجه كمّي لكل متّجه (C1)، (C8)، (C11)، (C17)، (C19-C21)، (C23)
(I2) start_indices متّجه متعدّد الأبعاد من نوع عدد صحيح (C2-C3), (C14), (C17), (C22)
(I3) offset_dims ثابت موتر أحادي البُعد من النوع si64 (C1)، (C4-C5)، (C22)
(I4) collapsed_slice_dims ثابت موتر أحادي البُعد من النوع si64 (C1)، (C6-C9)، (C22)
(I5) operand_batching_dims ثابت موتر أحادي البُعد من النوع si64 (C1), (C6), (C10-C12), (C16-C18), (C22)
(I6) start_indices_batching_dims ثابت موتر أحادي البُعد من النوع si64 (C13-C17)
(I7) start_index_map ثابت موتر أحادي البُعد من النوع si64 (C3)، (C18-C19)
(I8) index_vector_dim ثابت من النوع si64 (C2-C3)، (C15)، (C22)
(I9) slice_sizes ثابت موتر أحادي البُعد من النوع si64 (C9)، و(C12)، و(C20-C22)
(I10) indices_are_sorted ثابت من النوع i1

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي لكل متّجه (C5)، (C22-C23)

القيود

  • (C1) rank(operand) = size(offset_dims) + size(collapsed_slice_dims) + size(operand_batching_dims)
  • (C2) 0 <= index_vector_dim <= rank(start_indices)
  • (C3) size(start_index_map) = index_vector_dim < rank(start_indices) ? dim(start_indices, index_vector_dim) : 1
  • (C4) is_unique(offset_dims) and is_sorted(offset_dims)
  • (C5) 0 <= offset_dims < rank(result).
  • (C6) is_unique(concatenate(collapsed_slice_dims, operand_batching_dims))
  • (C7) is_sorted(collapsed_slice_dims)
  • (C8) 0 <= collapsed_slice_dims < rank(operand)
  • (C9) slice_sizes[collapsed_slice_dims...] <= 1.
  • (C10) is_sorted(operand_batching_dims)
  • (C11) 0 <= operand_batching_dims < rank(operand).
  • (C12) slice_sizes[operand_batching_dims...] <= 1
  • (C13) is_unique(start_indices_batching_dims).
  • (C14) 0 <= start_indices_batching_dims < rank(start_indices).
  • (C15) index_vector_dim not in start_indices_batching_dims.
  • (C16) size(operand_batching_dims) == size(start_indices_batching_dims)
  • (C17) dim(operand, operand_batching_dims...) = dim(start_indices, start_indices_batching_dims...).
  • (C18) is_unique(concatenate(start_index_map, operand_batching_dims))
  • (C19) 0 <= start_index_map < rank(operand).
  • (C20) size(slice_sizes) = rank(operand)
  • (C21) 0 <= slice_sizes <= shape(operand).
  • (C22) shape(result) = combine(batch_dim_sizes, offset_dim_sizes) حيث:
    • batch_dim_sizes = shape(start_indices) باستثناء أنّه لم يتم تضمين حجم السمة start_indices الذي يتوافق مع index_vector_dim.
    • offset_dim_sizes = slice_sizes باستثناء أحجام السمات في slice_sizes المقابلة collapsed_slice_dims وoperand_batching_dims غير مضمّنة.
    • تضع الدالة 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

القيود

  • (C1) 0 <= dimension < rank(operand)

أمثلة

// %operand: [[1, 2, 3], [4, 5, 6]]
%result = "stablehlo.get_dimension_size"(%operand) {
  dimension = 1 : i64
} : (tensor<2x3xi64>) -> tensor<i32>
// %result: 3

 مزيد من الأمثلة

get_tuple_element

الدلالات

تستخرج هذه الدالة العنصر في الموضع index من الصف operand وتنتج result. بشكل أكثر رسمية، result = operand[index].

المدخلات

التصنيف الاسم النوع القيود
(I1) operand tuple (C1)، (C2)
(I2) index ثابت من النوع si32 (C1)، (C2)

النواتج

الاسم النوع القيود
result أي قيمة (C2)

القيود

  • (C1) 0 <= index < size(operand)
  • (C2) type(result) = tuple_element_types(operand)[index]

أمثلة

// %operand: ([1.0, 2.0], (3))
%result = "stablehlo.get_tuple_element"(%operand) <{index = 0 : i32}> : (tuple<tensor<2xf64>, tuple<tensor<i64>>>) -> tensor<2xf64>
// %result: [1.0, 2.0]

 مزيد من الأمثلة

إذا

الدلالات

تُنتج هذه الدالة المخرجات من تنفيذ دالة واحدة بالضبط من true_branch أو false_branch استنادًا إلى قيمة pred. بشكل أكثر رسمية، result = pred ? true_branch() : false_branch().

المدخلات

التصنيف الاسم النوع القيود
(I1) pred موتر صفري الأبعاد من النوع i1
(I2) true_branch دالة (C1-C3)
(I3) false_branch دالة (C1)، (C2)

النواتج

الاسم النوع القيود
results عدد متغيّر من الموترات أو الموترات الكمية أو الرموز المميزة (C3)

القيود

  • (C1) input_types(true_branch) = input_types(false_branch) = []
  • (C2) output_types(true_branch) = output_types(false_branch)
  • (C3) type(results...) = output_types(true_branch)

أمثلة

// %result_true_branch: 10
// %result_false_branch: 11
// %pred: true
%result = "stablehlo.if"(%pred) ({
  "stablehlo.return"(%result_true_branch) : (tensor<i32>) -> ()
}, {
  "stablehlo.return"(%result_false_branch) : (tensor<i32>) -> ()
}) : (tensor<i1>) -> tensor<i32>
// %result: 10

 مزيد من الأمثلة

imag

الدلالات

يستخرج هذا الإجراء الجزء التخيّلي من operand على مستوى كل عنصر، وينتج عنه موتر result. بشكل أكثر رسمية، لكل عنصر x: imag(x) = is_complex(x) ? imaginary_part(x) : constant(0, element_type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من النوع النقطة العائمة أو النوع المركّب (C1)، (C2)

النواتج

الاسم النوع القيود
result موتر من نوع النقطة العائمة (C1)، (C2)

القيود

  • (C1) shape(result) = shape(operand)
  • يتم تعريف (C2) element_type(result) على النحو التالي:
    • complex_element_type(element_type(operand)) إذا is_complex(operand)
    • element_type(operand) في ما عدا ذلك.

أمثلة

// %operand: [(1.0, 2.0), (3.0, 4.0)]
%result = "stablehlo.imag"(%operand) : (tensor<2xcomplex<f32>>) -> tensor<2xf32>
// %result: [2.0, 4.0]

 مزيد من الأمثلة

infeed

الدلالات

يقرأ البيانات من الخلاصة داخل التطبيق وينتج results.

دلالات infeed_config يحدّدها التنفيذ.

results تتألف من قيم الحمولة التي تأتي أولاً ورمز مميز يأتي أخيرًا. في المستقبل، نخطّط لتقسيم الحمولة والرمز المميّز إلى نتيجتَين منفصلتَين لتحسين الوضوح (#670).

المدخلات

التصنيف الاسم النوع
(I1) token token
(I2) infeed_config ثابت من النوع string

النواتج

الاسم النوع القيود
results عدد متغيّر من الموترات أو الموترات الكمية أو الرموز المميزة (C1-C3)

القيود

  • (C1) 0 < size(results)
  • (C2) is_empty(result[:-1]) أو is_tensor(type(results[:-1]))
  • (C3) is_token(type(results[-1]))

أمثلة

// %token: !stablehlo.token
// infeed_queue[0]: [[1, 2], [3, 4]]
// infeed_queue[1]: [[5, 6], [7, 8]]
%results0:2 = "stablehlo.infeed"(%token) {
  infeed_config = ""
} : (!stablehlo.token) -> (tensor<2x2xi64>, !stablehlo.token)
// results0#0: [[1, 2], [3, 4]]
%results1:2 = "stablehlo.infeed"(%token) {
  infeed_config = ""
} : (!stablehlo.token) -> (tensor<2x2xi64>, !stablehlo.token)
// results1#0: [[5, 6], [7, 8]]

 مزيد من الأمثلة

iota

الدلالات

تعبئة موتر output بالقيم بترتيب تصاعدي بدءًا من الصفر على طول البُعد iota_dimension بشكل أكثر رسمية،

output[output_index] = constant(is_quantized(output) ? quantize(output_index[iota_dimension], element_type(output)) : output_index[iota_dimension], element_type(output)).

المدخلات

التصنيف الاسم النوع القيود
(I1) iota_dimension si64 (C1)

النواتج

الاسم النوع القيود
output موتر من النوع الصحيح أو النقطة العائمة أو المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) 0 <= iota_dimension < rank(output)

أمثلة

%output = "stablehlo.iota"() {
  iota_dimension = 0 : i64
} : () -> tensor<4x5xi32>
// %output: [
//           [0, 0, 0, 0, 0],
//           [1, 1, 1, 1, 1],
//           [2, 2, 2, 2, 2],
//           [3, 3, 3, 3, 3]
//          ]

%output = "stablehlo.iota"() {
  iota_dimension = 1 : i64
} : () -> tensor<4x5xi32>
// %output: [
//           [0, 1, 2, 3, 4],
//           [0, 1, 2, 3, 4],
//           [0, 1, 2, 3, 4],
//           [0, 1, 2, 3, 4]
//          ]

 مزيد من الأمثلة

is_finite

الدلالات

تُجري هذه الدالة عملية تحقّق على مستوى كل عنصر لمعرفة ما إذا كانت القيمة في x محدودة (أي ليست +Inf أو -Inf أو NaN) وتنتج موتر y. تنفيذ عملية isFinite من مواصفات IEEE-754 بالنسبة إلى الأنواع الكمية، تكون النتيجة دائمًا true.

المدخلات

التصنيف الاسم النوع القيود
(I1) x متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C1)

النواتج

الاسم النوع القيود
y متّجه من النوع المنطقي (C1)

القيود

  • (C1) shape(x) = shape(y)

أمثلة

// Logical values: -Inf, +Inf, NaN, ...
// %x: [0xFFF0000000000000, 0x7FF0000000000000, 0x7FF8000000000000, -10.0, -0.0, 0.0, 10.0]
%y = "stablehlo.is_finite"(%x) : (tensor<7xf64) -> tensor<7xi1>
// %y: [false, false, false, true, true, true, true]

 مزيد من الأمثلة

log

الدلالات

تُجري هذه الدالة عملية لوغاريتمية على مستوى كل عنصر في الموتر operand وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأرقام العشرية: log من IEEE-754
  • بالنسبة إلى الأعداد المركّبة: اللوغاريتم المركّب
  • بالنسبة إلى الأنواع الكمية: dequantize_op_quantize(log, operand, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

النواتج

الاسم النوع القيود
result موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %operand: [[1.0, 2.0], [3.0, 4.0]]
%result = "stablehlo.log"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [[0.0, 0.69314718055994529], [1.0986122886681098, 1.3862943611198906]]

 مزيد من الأمثلة

log_plus_one

الدلالات

تُجري عملية اللوغاريتم زائد واحد على مستوى كل عنصر في الموتر operand وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأرقام العشرية: logp1 من IEEE-754
  • بالنسبة إلى الأعداد المركّبة: complex(log(hypot(real(x) + 1, imag(x))), atan2(imag(x), real(x) + 1))
  • بالنسبة إلى الأنواع الكمية: dequantize_op_quantize(log_plus_one, operand, type(result))

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

النواتج

الاسم النوع القيود
result موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %operand: [0.0, -0.999, 7.0, 6.38905621, 15.0]
%result = "stablehlo.log_plus_one"(%operand) : (tensor<5xf64>) -> tensor<5xf64>
// %result: [0.0, -6.90776825, 2.07944155, 2.0, 2.77258873]

 مزيد من الأمثلة

لوجستي

الدلالات

تُجري هذه العملية عملية لوجستية على مستوى العناصر في الموتر operand وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأرقام العشرية: division(1, addition(1, exp(-x))) من IEEE-754.
  • بالنسبة إلى الأعداد المركّبة: دالة لوجستية مركّبة
  • بالنسبة إلى الأنواع الكمية: dequantize_op_quantize(logistic, operand, type(result))

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

النواتج

الاسم النوع القيود
result موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %operand: [[0.0, 1.0], [2.0, 3.0]]
%result = "stablehlo.logistic"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [[0.5, 0.73105858], [0.88079708, 0.95257413]]

 مزيد من الأمثلة

خريطة

الدلالات

تطبِّق دالة الخريطة computation على inputs على طول dimensions وتنتج موترًا result.

بشكل أكثر رسمية، result[result_index] = computation(inputs...[result_index]).

المدخلات

التصنيف الاسم النوع القيود
(I1) inputs عدد متغير من الموترات أو الموترات الكمية لكل موتر (C1-C4)
(I2) dimensions ثابت موتر أحادي البُعد من النوع si64 (C3)
(I3) computation دالة (C4)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي لكل متّجه (C1)، (C4)

القيود

  • (C1) shape(inputs...) = shape(result)
  • (C2) 0 < size(inputs) = N
  • (C3) dimensions = range(rank(inputs[0]))
  • (C4) computation من النوع (tensor<E0>, ..., tensor<EN-1>) -> tensor<E'> حيث Ei = element_type(inputs[i]) وE' = element_type(result)

أمثلة

// %input0: [[0, 1], [2, 3]]
// %input1: [[4, 5], [6, 7]]
%result = "stablehlo.map"(%input0, %input1) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = stablehlo.multiply %arg0, %arg1 : tensor<i64>
    stablehlo.return %0 : tensor<i64>
}) {
  dimensions = array<i64: 0, 1>
} : (tensor<2x2xi64>, tensor<2x2xi64>) -> tensor<2x2xi64>
// %result: [[0, 5], [12, 21]]

 مزيد من الأمثلة

أعلى قيمة

الدلالات

تُجري هذه الدالة عملية الحد الأقصى على مستوى العناصر في الموترَين lhs وrhs، وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى القيم المنطقية: OR المنطقي
  • بالنسبة إلى الأعداد الصحيحة: الحد الأقصى للعدد الصحيح
  • بالنسبة إلى الأرقام العشرية: maximum من IEEE-754.
  • بالنسبة إلى الأعداد المركّبة: الحد الأقصى المعجمي للزوج (real, imaginary) يتضمّن فرض ترتيب على الأعداد المركّبة دلالات مفاجئة، لذا نخطّط في المستقبل لإزالة إمكانية استخدام الأعداد المركّبة في هذه العملية (#560).
  • بالنسبة إلى الأنواع الكمية:
    • dequantize_op_quantize(maximum, lhs, rhs, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs متّجه أو متّجه كمّي لكل متّجه (C1)
(I2) rhs متّجه أو متّجه كمّي لكل متّجه (C1)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي لكل متّجه (C1)

القيود

  • (C1) baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)

أمثلة

// %lhs: [[1, 2], [7, 8]]
// %rhs: [[5, 6], [3, 4]]
%result = "stablehlo.maximum"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 6], [7, 8]]

 مزيد من الأمثلة

الحد الأدنى

الدلالات

تُجري هذه الدالة عملية الحد الأدنى على مستوى العناصر في الموترَين lhs وrhs وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى القيم المنطقية: AND المنطقية
  • بالنسبة إلى الأعداد الصحيحة: الحد الأدنى للعدد الصحيح.
  • بالنسبة إلى الأرقام العشرية: minimum من IEEE-754
  • بالنسبة إلى الأعداد المركّبة: الحد الأدنى المعجمي للزوج (real, imaginary) يتضمّن فرض ترتيب على الأعداد المركّبة دلالات مفاجئة، لذا نخطّط في المستقبل لإزالة إمكانية استخدام الأعداد المركّبة في هذه العملية (#560).
  • بالنسبة إلى الأنواع الكمية:
    • dequantize_op_quantize(minimum, lhs, rhs, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs متّجه أو متّجه كمّي لكل متّجه (C1)
(I2) rhs متّجه أو متّجه كمّي لكل متّجه (C1)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي لكل متّجه (C1)

القيود

  • (C1) baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)

أمثلة

// %lhs: [[1, 2], [7, 8]]
// %rhs: [[5, 6], [3, 4]]
%result = "stablehlo.minimum"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[1, 2], [3, 4]]

 مزيد من الأمثلة

ضرب

الدلالات

تُجري هذه الدالة عملية ضرب العناصر المقابلة في موترَين lhs وrhs، وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى القيم المنطقية: AND المنطقية
  • بالنسبة إلى الأعداد الصحيحة: ضرب الأعداد الصحيحة
  • بالنسبة إلى الأرقام العشرية: multiplication من IEEE-754
  • بالنسبة إلى الأعداد المركّبة: الضرب المركّب
  • بالنسبة إلى الأنواع الكمية:
    • dequantize_op_quantize(multiply, lhs, rhs, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs متّجه أو متّجه كمّي لكل متّجه (C1)
(I2) rhs متّجه أو متّجه كمّي لكل متّجه (C1)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي لكل متّجه (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.multiply"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 12], [21, 32]]

 مزيد من الأمثلة

نفي

الدلالات

تُجري هذه الدالة عملية نفي لكل عنصر في الموتر operand وتنتج موترًا result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأعداد الصحيحة الموقّعة: نفي العدد الصحيح.
  • بالنسبة إلى الأعداد الصحيحة غير الموقّعة: تحويل إلى عدد صحيح موقّع، ونفي العدد الصحيح، والتحويل مرة أخرى إلى عدد صحيح غير موقّع
  • بالنسبة إلى الأرقام العشرية: negate من IEEE-754
  • بالنسبة إلى الأعداد المركّبة: النفي المركّب
  • بالنسبة إلى الأنواع الكمية: dequantize_op_quantize(negate, operand, type(result))

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من النوع الصحيح أو النقطة العائمة أو المركّب أو موتر كمّي لكل موتر (C1)

النواتج

الاسم النوع القيود
result موتر من النوع الصحيح أو النقطة العائمة أو المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// Negation operation with integer Tensors
// %operand: [0, -2]
%result = "stablehlo.negate"(%operand) : (tensor<2xi32>) -> tensor<2xi32>
// %result: [0, 2]

// Negation operation with with complex tensors
// %operand: (2.5, 0.0)
%result = "stablehlo.negate"(%operand) : (tensor<1xcomplex<f32>>) -> tensor<1xcomplex<f32>>
// %result: [-2.5, -0.0]

 مزيد من الأمثلة

ليس

الدلالات

تُجري هذه الدالة عملية NOT على مستوى كل عنصر في الموتر operand وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى القيم المنطقية: المعامل NOT المنطقي
  • بالنسبة إلى الأعداد الصحيحة: استخدام NOT على مستوى البت.

الوسيطات

الاسم النوع القيود
operand موتر من النوع المنطقي أو العدد الصحيح (C1)

النواتج

الاسم النوع القيود
result موتر من النوع المنطقي أو العدد الصحيح (C1)

القيود

  • (C1) type(operand) = type(result)

أمثلة

// Bitwise operation with with integer tensors
// %operand: [[1, 2], [3, 4]]
%result = "stablehlo.not"(%operand) : (tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[-2, -3], [-4, -5]]

// Bitwise operation with with boolean tensors
// %operand: [true, false]
%result = "stablehlo.not"(%operand) : (tensor<2xi1>) -> tensor<2xi1>
// %result: [false, true]

 مزيد من الأمثلة

optimization_barrier

الدلالات

يضمن تنفيذ العمليات التي تنتج operand قبل أي عمليات تعتمد على result، ويمنع عمليات التحويل التي يجريها المترجم من نقل العمليات عبر الحاجز. في ما عدا ذلك، تكون العملية هوية، أي result = operand.

الوسيطات

الاسم النوع القيود
operand عدد متغيّر من الموترات أو الموترات أو الرموز المميزة الكمية لكل موتر (C1)

النواتج

الاسم النوع القيود
result عدد متغيّر من الموترات أو الموترات أو الرموز المميزة الكمية لكل موتر (C1)

القيود

  • (C1) type(operand...) = type(result...)

أمثلة

// %operand0: 0.0
// %operand1: 1.0
%result0, %result1 = "stablehlo.optimization_barrier"(%operand0, %operand1) : (tensor<f32>, tensor<f32>) -> (tensor<f32>, tensor<f32>)
// %result0: 0.0
// %result1: 1.0

 مزيد من الأمثلة

أو

الدلالات

تُجري هذه الدالة عملية OR على العناصر المقابلة في موترَين lhs وrhs، وتنتج موترًا result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى القيم المنطقية: OR المنطقية
  • بالنسبة إلى الأعداد الصحيحة: استخدام OR على مستوى البت.

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs موتر من نوع عدد صحيح أو قيمة منطقية (C1)
(I2) rhs موتر من نوع عدد صحيح أو قيمة منطقية (C1)

النواتج

الاسم النوع القيود
result موتر من نوع عدد صحيح أو قيمة منطقية (C1)

القيود

  • (C1) type(lhs) = type(rhs) = type(result)

أمثلة

// Bitwise operation with with integer tensors
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.or"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 6], [7, 12]]

// Logical operation with with boolean tensors
// %lhs: [[false, false], [true, true]]
// %rhs: [[false, true], [false, true]]
%result = "stablehlo.or"(%lhs, %rhs) : (tensor<2x2xi1>, tensor<2x2xi1>) -> tensor<2x2xi1>
// %result: [[false, true], [true, true]]

 مزيد من الأمثلة

outfeed

الدلالات

تكتب inputs في خلاصة الإخراج وتنتج الرمز المميّز result.

دلالات outfeed_config يحدّدها التنفيذ.

المدخلات

التصنيف الاسم النوع
(I1) inputs عدد متغيّر من الموترات أو الموترات الكمية
(I2) token token
(I3) outfeed_config ثابت من النوع string

النواتج

الاسم النوع
result token

أمثلة

%result = "stablehlo.outfeed"(%input0, %token) {
  outfeed_config = ""
} : (tensor<2x2x2xi64>, !stablehlo.token) -> !stablehlo.token

 مزيد من الأمثلة

وسادة

الدلالات

توسّع هذه الدالة operand من خلال إضافة مساحة متروكة حول الموتر وكذلك بين عناصر الموتر باستخدام padding_value المحدّدة.

تحدّد edge_padding_low وedge_padding_high مقدار المساحة المتروكة المضافة في الطرف الأدنى (بجانب الفهرس 0) والطرف الأعلى (بجانب الفهرس الأعلى) لكل بُعد على التوالي. يمكن أن تكون قيمة المساحة المتروكة سالبة، حيث تشير القيمة المطلقة للمساحة المتروكة السالبة إلى عدد العناصر التي يجب إزالتها من السمة المحدّدة.

تحدّد interior_padding مقدار المساحة المتروكة المضافة بين أي عنصرَين في كل بُعد، ويجب ألا تكون قيمة سالبة. يحدث الحشو الداخلي قبل الحشو على الحواف، وبالتالي سيؤدي الحشو السلبي على الحواف إلى إزالة العناصر من المعامِل المحشو داخليًا.

بشكل أكثر رسمية، يتم تعريف result[result_index] على النحو التالي:

  • operand[operand_index] إذا كان result_index = edge_padding_low + operand_index * (interior_padding + 1).
  • padding_value في ما عدا ذلك.

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه أو متّجه كمّي لكل متّجه (C1)، (C2)، (C4)
(I2) padding_value متّجه صفري الأبعاد أو متّجه كمّي لكل متّجه (C1)
(I3) edge_padding_low ثابت موتر أحادي البُعد من النوع si64 (C1)، (C4)
(I4) edge_padding_high ثابت موتر أحادي البُعد من النوع si64 (C1)، (C4)
(I5) interior_padding ثابت موتر أحادي البُعد من النوع si64 (C2-C4)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي لكل متّجه (C3-C6)

القيود

  • (C1) element_type(operand) = element_type(padding_value) = element_type(result)
  • (C2) size(edge_padding_low) = size(edge_padding_high) = size(interior_padding) = rank(operand)
  • (C3) 0 <= interior_padding
  • (C4) shape(result) = shape(operand) + edge_padding_low + max(shape(operand) - 1, 0) * interior_padding + edge_padding_high

أمثلة

// %operand: [
//            [1, 2, 3],
//            [4, 5, 6]
//           ]
// %padding_value: 0
%result = "stablehlo.pad"(%operand, %padding_value) {
  edge_padding_low = array<i64: 0, 1>,
  edge_padding_high = array<i64: 2, 1>,
  interior_padding = array<i64: 1, 2>
} : (tensor<2x3xi32>, tensor<i32>) -> tensor<5x9xi32>
// %result: [
//           [0, 1, 0, 0, 2, 0, 0, 3, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0],
//           [0, 4, 0, 0, 5, 0, 0, 6, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0],
//           [0, 0, 0, 0, 0, 0, 0, 0, 0]
//          ]

 مزيد من الأمثلة

partition_id

الدلالات

تُنتج partition_id للعملية الحالية.

النواتج

الاسم النوع
result موتر صفري الأبعاد من النوع ui32

أمثلة

%result = "stablehlo.partition_id"() : () -> tensor<ui32>

 مزيد من الأمثلة

popcnt

الدلالات

تُجري هذه العملية حسابًا على مستوى العناصر لعدد البتات المضبوطة في الموتر operand وتنتج الموتر result.

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه متعدّد الأبعاد من نوع عدد صحيح (C1)

النواتج

الاسم النوع القيود
result متّجه متعدّد الأبعاد من نوع عدد صحيح (C1)

القيود

  • (C1) type(operand) = type(result)

أمثلة

// %operand: [0, 1, 2, 127]
%result = "stablehlo.popcnt"(%operand) : (tensor<4xi64>) -> tensor<4xi64>
// %result: [0, 1, 1, 7]

 مزيد من الأمثلة

الطاقة

الدلالات

تُجري هذه العملية عملية الأس على مستوى كل عنصر في الموتر lhs باستخدام الموتر rhs، وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأعداد الصحيحة: الأس الصحيح
  • بالنسبة إلى الأرقام العشرية: pow من IEEE-754
  • بالنسبة إلى الأعداد المركّبة: الأس المركّب
  • بالنسبة إلى الأنواع الكمية: dequantize_op_quantize(power, lhs, rhs, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs موتر من النوع الصحيح أو النقطة العائمة أو المركّب أو موتر كمّي لكل موتر (C1)
(I2) rhs موتر من النوع الصحيح أو النقطة العائمة أو المركّب أو موتر كمّي لكل موتر (C1)

النواتج

الاسم النوع القيود
result موتر من النوع الصحيح أو النقطة العائمة أو المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %lhs: [-2.0, -0.0, -36.0, 5.0, 3.0, 10000.0]
// %rhs: [2.0, 2.0, 1.1, 2.0, -1.0, 10.0]
%result = "stablehlo.power"(%lhs, %rhs) : (tensor<6xf64>, tensor<6xf64>) -> tensor<6xf64>
// %result: [4.0, 0.0, -nan, 25.0, 0.333333343, inf]

 مزيد من الأمثلة

حقيقي

الدلالات

يستخرج هذا الإجراء الجزء الحقيقي من operand على مستوى كل عنصر، وينتج عنه موتر result. بشكل أكثر رسمية، لكل عنصر x: real(x) = is_complex(x) ? real_part(x) : x.

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من النوع النقطة العائمة أو النوع المركّب (C1)، (C2)

النواتج

الاسم النوع القيود
result موتر من نوع النقطة العائمة (C1)، (C2)

القيود

  • (C1) shape(result) = shape(operand)
  • يتم تعريف (C2) element_type(result) على النحو التالي:
    • complex_element_type(element_type(operand)) إذا is_complex(operand)
    • element_type(operand) في ما عدا ذلك.

أمثلة

// %operand: [(1.0, 2.0), (3.0, 4.0)]
%result = "stablehlo.real"(%operand) : (tensor<2xcomplex<f32>>) -> tensor<2xf32>
// %result: [1.0, 3.0]

 مزيد من الأمثلة

recv

الدلالات

يتلقّى البيانات من قناة تتضمّن channel_id وينتج results.

إذا كانت قيمة is_host_transfer هي true، فهذا يعني أنّ العملية تنقل البيانات من المضيف. بخلاف ذلك، ينقل البيانات من جهاز آخر استنادًا إلى قيم source_target_pairs. تكرّر هذه العلامة المعلومات المقدَّمة في channel_type، لذا نخطّط في المستقبل للإبقاء على إحدى العلامتين فقط (#666). إذا كانت قيمة is_host_transfer تساوي false وكانت قيمة source_target_pairs هي None أو فارغة، سيتم اعتبار ذلك سلوكًا غير محدّد.

results تتألف من قيم الحمولة التي تأتي أولاً ورمز مميز يأتي أخيرًا. في المستقبل، نخطّط لتقسيم الحمولة والرمز المميّز إلى نتيجتَين منفصلتَين لتحسين الوضوح (#670).

المدخلات

التصنيف الاسم النوع القيود
(I1) token token
(I2) channel_id ثابت من النوع si64
(I3) channel_type تعداد DEVICE_TO_DEVICE وDEVICE_TO_HOST (C5)
(I4) is_host_transfer ثابت من النوع i1 (C5-C6)
(I5) source_target_pairs ثابت موتر ثنائي الأبعاد من النوع si64 (C1-C4), (C6)

النواتج

الاسم النوع القيود
results عدد متغيّر من الموترات أو الموترات الكمية أو الرموز المميزة (C2-C4)

القيود

  • (C1) dim(source_target_pairs, 1) = 2
  • (C2) is_unique(source_target_pairs[:, 0])
  • (C3) is_unique(source_target_pairs[:, 1])
  • (C4) 0 <= source_target_pairs < N، حيث يتم تعريف N على النحو التالي:
    • num_replicas في حال استخدام cross_replica
    • num_partitions في حال استخدام cross_partition
  • يتم تعريف (C5) channel_type على النحو التالي:
    • DEVICE_TO_HOST إذا is_host_transfer = true،
    • DEVICE_TO_DEVICE في ما عدا ذلك.

أمثلة

%results0, %results1 = "stablehlo.recv"(%token) {
  channel_handle = #stablehlo.channel_handle<handle = 0, type = 1>,
  is_host_transfer = false,
  source_target_pairs = dense<[[0, 1], [1, 2]]> : tensor<2x2xi64>
} : (!stablehlo.token) -> (tensor<2x2xi64>, !stablehlo.token)

 مزيد من الأمثلة

reduce

الدلالات

تطبِّق دالة تقليل body على inputs وinit_values على طول dimensions وتنتج موترات results.

ويتم تحديد ترتيب عمليات الاختزال حسب التنفيذ، ما يعني أنّه يجب أن يشكّل body وinit_values أحاديًا لضمان أن تؤدي العملية إلى النتائج نفسها لجميع المدخلات في جميع عمليات التنفيذ. ومع ذلك، لا ينطبق هذا الشرط على العديد من التخفيضات الشائعة. على سبيل المثال، لا يشكّل جمع الأعداد العشرية العائمة لـ body والقيمة صفر لـ init_values أحاديًا لأنّ جمع الأعداد العشرية العائمة ليس ترابطيًا.

بشكل أكثر رسمية، results...[j0, ..., jR-1] = reduce(input_slices_converted) حيث:

  • input_slices = inputs...[j0, ..., :, ..., jR-1]، حيث يتم إدراج : في 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)

القيود

  • (C1) same(shape(inputs...))
  • (C2) element_type(inputs...) = element_type(init_values...)
  • (C3) 0 < size(inputs) = size(init_values) = size(results) = N
  • (C4) 0 <= dimensions < rank(inputs[0])
  • (C5) is_unique(dimensions).
  • (C6) body من النوع (tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ..., tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>) حيث is_promotable(element_type(inputs[i]), Ei).
  • (C7) shape(results...) = shape(inputs...) باستثناء أحجام السمة inputs... المقابلة لـ dimensions غير مضمّنة.
  • (C8) element_type(results[i]) = Ei لكل i في [0,N)

أمثلة

// %input = [[0, 1, 2, 3, 4, 5]]
// %init_value = 0
%result = "stablehlo.reduce"(%input, %init_value) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
    "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  dimensions = array<i64: 1>
} : (tensor<1x6xi64>, tensor<i64>) -> tensor<1xi64>
// %result = [15]

 مزيد من الأمثلة

reduce_precision

الدلالات

تُجري هذه الدالة عملية تحويل على مستوى العناصر من operand إلى نوع آخر من الأعداد العشرية التي تستخدم exponent_bits وmantissa_bits، ثم تعود إلى نوع الأعداد العشرية الأصلي، وتنتج موترًا من النوع output.

بشكل أكثر رسمية:

  • يتم تعديل أجزاء الأسيّة من القيمة الأصلية لتقريب القيمة الأصلية إلى أقرب قيمة يمكن تمثيلها باستخدام mantissa_bits باستخدام دلالات roundToIntegralTiesToEven.
  • بعد ذلك، إذا كانت قيمة mantissa_bits أصغر من عدد بتات الجزء الكسري للقيمة الأصلية، يتم اقتطاع بتات الجزء الكسري إلى mantissa_bits.
  • بعد ذلك، إذا لم تتناسب بتات الأس الخاصة بالنتيجة الوسيطة مع النطاق الذي توفّره exponent_bits، سيحدث تجاوز للحد الأقصى للنتيجة الوسيطة إلى ما لا نهاية باستخدام الإشارة الأصلية أو تجاوز للحد الأدنى إلى صفر باستخدام الإشارة الأصلية.
  • بالنسبة إلى الأنواع المحدَّدة الكمية، يتم تنفيذ dequantize_op_quantize( lambda operand: reduce_precision(operand, exponent_bits, mantissa_bits), operand, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C1)
(I2) exponent_bits ثابت من النوع si32 (C2)
(I3) mantissa_bits ثابت من النوع si32 (C3)

النواتج

الاسم النوع القيود
output متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(output)
  • (C2) 1 <= exponent_bits
  • (C3) 0 <= mantissa_bits

أمثلة

// Logical values: +Inf, NaN, +Denormal, 0.0, 65519.0, 65520.0
// %operand: [0x7FF0000000000000, 0x7FFFFFFFFFFFFFFF, 0x0000000000000001, 0.0, 65519.0, 65520.0]
%output = "stablehlo.reduce_precision"(%operand) {
  exponent_bits = 5 : i32,
  mantissa_bits = 10 : i32
} : (tensor<6xf64>) -> tensor<6xf64>
// Logical values: +Inf, NaN, 0.0, 0.0, 65504.0, +Inf
// %output: [0x7FF0000000000000, 0x7FFFFFFFFFFFFFFF, 0.0, 0.0, 65504.0, 0x7FF0000000000000]

 مزيد من الأمثلة

reduce_scatter

الدلالات

reduce_scatter

ضمن كل مجموعة عمليات في شبكة عمليات StableHLO، يتم إجراء عملية تقليل باستخدام computations على قيم موتر operand من كل عملية، ويتم تقسيم نتيجة عملية التقليل على طول scatter_dimension إلى أجزاء، ويتم توزيع الأجزاء المقسّمة بين العمليات لإنتاج result.

تقسّم العملية شبكة معالجة StableHLO إلى process_groups، ويتم تعريفها على النحو التالي:

  • cross_replica(replica_groups) if channel_id <= 0 and use_global_device_ids = false
  • cross_replica_and_partition(replica_groups) if channel_id > 0 and use_global_device_ids = false
  • flattened_ids(replica_groups) if 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)

القيود

  • (C1) dim(operand, scatter_dimension) % dim(process_groups, 1) = 0
  • (C2) 0 <= scatter_dimension < rank(operand)
  • (C3) is_unique(replica_groups)
  • يتم تعريف (C4) size(replica_groups) على النحو التالي:
    • num_replicas في حال استخدام cross_replica
    • num_replicas في حال استخدام cross_replica_and_partition
    • num_processes في حال استخدام flattened_ids
  • (C5) 0 <= replica_groups < size(replica_groups).
  • (C6) إذا كان use_global_device_ids = true، فإن channel_id > 0.
  • (C7) computation له النوع (tensor<E>, tensor<E>) -> (tensor<E>) حيث is_promotable(element_type(operand), E).
  • (C8) shape(result) = shape(operand) باستثناء:
    • dim(result, scatter_dimension) = dim(operand, scatter_dimension) / dim(process_groups, 1).
  • (C9) element_type(result) = E.

أمثلة

// num_replicas: 2
// num_partitions: 1
// %operand@(0, 0): [[1, 2, 3, 4],
//                   [5, 6, 7, 8]]
// %operand@(1, 0): [[9, 10, 11, 12],
//                   [13, 14, 15, 16]]
%result = "stablehlo.reduce_scatter"(%operand) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
  %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
  "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  scatter_dimension = 1 : i64,
  replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>,
  channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
} : (tensor<2x4xi64>) -> tensor<2x2xi64>
//
// %result@(0, 0): [[10, 12],
//                  [18, 20]]
// %result@(1, 0): [[14, 16],
//                  [22, 24]]

 مزيد من الأمثلة

reduce_window

الدلالات

تطبِّق هذه الدالة دالة تقليل body على نوافذ بحجم inputs وinit_values وتنتج results.

يوضّح المخطّط التالي كيفية احتساب العناصر في results... من inputs... باستخدام مثال ملموس.

reduce_window

بشكل أكثر رسمية، results...[result_index] = reduce(windows, init_values, axes(inputs...), body) (راجِع reduce) حيث:

  • padded_inputs = pad(inputs..., init_values..., padding[:, 0], padding[:, 1], base_dilations - 1).
  • window_start = result_index * window_strides.
  • window_end = window_start + (window_dimensions - 1) * window_dilations + 1.
  • windows = slice(padded_inputs..., window_start, window_end, window_dilations).

المدخلات

التصنيف الاسم النوع القيود
(I1) inputs عدد متغير من الموترات أو الموترات الكمية لكل موتر (C1-C4)، (C6)، (C8)، (C10)، (C12)، (C13)، (C15)
(I2) init_values عدد متغير من موترات ذات 0 بُعد أو موترات كمّية لكل موتر (C1)، (C13)
(I3) window_dimensions ثابت موتر أحادي البُعد من النوع si64 (C4)، (C5)، (C15)
(I4) window_strides ثابت موتر أحادي البُعد من النوع si64 (C6) و(C7) و(C15)
(I5) base_dilations ثابت موتر أحادي البُعد من النوع si64 (C8)، (C9)، (C15)
(I6) window_dilations ثابت موتر أحادي البُعد من النوع si64 (C10)، (C11)، (C15)
(I7) padding ثابت موتر ثنائي الأبعاد من النوع si64 (C12)، (C15)
(I8) body دالة (C13)

النواتج

الاسم النوع القيود
results عدد متغير من الموترات أو الموترات الكمية لكل موتر (C1)، (C14-C16)

القيود

  • (C1) 0 < size(inputs) = size(init_values) = size(results) = N
  • (C2) same(shape(inputs...))
  • (C3) element_type(inputs...) = element_type(init_values...)
  • (C4) size(window_dimensions) = rank(inputs[0])
  • (C5) 0 < window_dimensions.
  • (C6) size(window_strides) = rank(inputs[0])
  • (C7) 0 < window_strides
  • (C8) size(base_dilations) = rank(inputs[0])
  • (C9) 0 < base_dilations.
  • (C10) size(window_dilations) = rank(inputs[0])
  • (C11) 0 < window_dilations.
  • (C12) shape(padding) = [rank(inputs[0]), 2]
  • (C13) body من النوع (tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ..., tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>) حيث is_promotable(element_type(inputs[i]), Ei).
  • (C14) same(shape(results...)).
  • (C15) shape(results[0]) = num_windows حيث:
    • dilated_input_shape = shape(inputs[0]) = 0 ? 0 : (shape(inputs[0]) - 1) * base_dilations + 1.
    • padded_input_shape = padding[:, 0] + dilated_input_shape + padding[:, 1].
    • dilated_window_shape = (window_dimensions - 1) * window_dilations + 1.
    • is_empty_window = padded_input_shape = 0 || dilated_window_shape > padded_input_shape.
    • num_windows = is_empty_window ? 0 : floor((padded_input_shape - dilated_window_shape) / window_strides) + 1.
  • ‫(C16) element_type(results[i]) = Ei لجميع i في [0,N)

أمثلة

// %input = [[1, 2], [3, 4], [5, 6]]
// %init_value = 0
%result = "stablehlo.reduce_window"(%input, %init_value) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
    "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  window_dimensions = array<i64: 2, 1>,
  window_strides = array<i64: 4, 1>,
  base_dilations = array<i64: 2, 1>,
  window_dilations = array<i64: 3, 1>,
  padding = dense<[[2, 1], [0, 0]]> : tensor<2x2xi64>
} : (tensor<3x2xi64>, tensor<i64>) -> tensor<2x2xi64>
// %result = [[0, 0], [3, 4]]

 مزيد من الأمثلة

حاصل القسمة

الدلالات

تُجري هذه العملية حساب باقي القسمة بين عناصر موترَي المقسوم lhs والمقسوم عليه rhs، وتنتج موترًا result.

وبشكل أكثر رسمية، يتم استخراج إشارة النتيجة من المقسوم، وتكون القيمة المطلقة للنتيجة دائمًا أقل من القيمة المطلقة للمقسوم عليه. يتم احتساب الباقي على النحو التالي: lhs - d * rhs، حيث يتم تحديد d من خلال:

  • بالنسبة إلى الأعداد الصحيحة: stablehlo.divide(lhs, rhs).
  • بالنسبة إلى الأرقام العشرية: division(lhs, rhs) من IEEE-754 مع سمة التقريب roundTowardZero.
  • بالنسبة إلى الأعداد المركّبة: سيتم تحديدها لاحقًا (#997).
  • بالنسبة إلى الأنواع الكمية:
    • dequantize_op_quantize(remainder, lhs, rhs, type(result)).

بالنسبة إلى أنواع العناصر ذات النقطة العائمة، تختلف هذه العملية عن عملية remainder من مواصفات IEEE-754 حيث تكون d قيمة صحيحة الأقرب إلى القيمة الدقيقة لـ lhs/rhs مع تساوي القيمتين.

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs متّجه من نوع عدد صحيح أو عدد ذي فاصلة عشرية أو عدد مركّب أو متّجه كمّي لكل متّجه (C1)
(I2) rhs متّجه من نوع عدد صحيح أو عدد ذي فاصلة عشرية أو عدد مركّب أو متّجه كمّي لكل متّجه (C1)

النواتج

الاسم النوع القيود
result متّجه من نوع عدد صحيح أو عدد ذي فاصلة عشرية أو عدد مركّب أو متّجه كمّي لكل متّجه (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %lhs: [17, -17, 17, -17]
// %rhs: [3, 3, -3, -3]
%result = "stablehlo.remainder"(%lhs, %rhs) : (tensor<4xi64>, tensor<4xi64>) -> tensor<4xi64>
// %result: [2, -2, 2, -2]

 مزيد من الأمثلة

replica_id

الدلالات

تُنتج replica_id للعملية الحالية.

النواتج

الاسم النوع
result موتر صفري الأبعاد من النوع ui32

أمثلة

%result = "stablehlo.replica_id"() : () -> tensor<ui32>

 مزيد من الأمثلة

إعادة التشكيل

الدلالات

تُجري هذه العملية إعادة تشكيل للموتر operand إلى الموتر result. من الناحية المفاهيمية، يشبه ذلك الاحتفاظ بالتمثيل الأساسي نفسه ولكن مع إمكانية تغيير الشكل، مثلاً من tensor<2x3xf32> إلى tensor<3x2xf32> أو tensor<6xf32>.

بشكل أكثر رسمية، result[result_index] = operand[operand_index] حيث result_index وoperand_index لهما الموضع نفسه في الترتيب المعجمي لـ index_space(result) وindex_space(operand).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه أو متّجه كمّي (C1-C3)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي (C1-C3)

القيود

  • يتم تحديد (C1) element_type(result) من خلال:
    • element_type(operand)، إذا !is_per_axis_quantized(operand)
    • element_type(operand) باستثناء أنّ quantization_dimension(operand) وquantization_dimension(result) قد يختلفان.
  • (C2) size(operand) = size(result)
  • (C3) إذا كان is_per_axis_quantized(operand):
    • reduce(dims(operand, [0, 1, ..., quantization_dimension(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [0, 1, ..., quantization_dimension(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y).
    • dim(operand, quantization_dimension(operand)) = dim(result, quantization_dimension(result)).
    • reduce(dims(operand, [quantization_dimension(operand) + 1, ..., rank(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [quantization_dimension(result) + 1, ..., rank(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y).

أمثلة

// %operand: [[1, 2, 3], [4, 5, 6]]
%result = "stablehlo.reshape"(%operand) : (tensor<2x3xi32>) -> tensor<3x2xi32>
// %result: [[1, 2], [3, 4], [5, 6]]

 مزيد من الأمثلة

إلغاء

الدلالات

تعكس هذه الدالة ترتيب العناصر في operand على طول dimensions المحدّد وتنتج موترًا result. بشكل أكثر رسمية، result[result_index] = operand[operand_index] حيث:

  • operand_index[d] = dim(result, d) - result_index[d] - 1 if d in dimensions.
  • operand_index[d] = result_index[d] في ما عدا ذلك.

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه أو متّجه كمّي لكل متّجه (C1)، (C3)
(I2) dimensions ثابت موتر أحادي البُعد من النوع si64 (C2), (C3)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي لكل متّجه (C1)، (C3)

القيود

  • (C1) type(operand) = type(result)
  • (C2) is_unique(dimensions)
  • (C3) 0 <= dimensions < rank(result)

أمثلة

// %operand = [[1, 2], [3, 4], [5, 6]]
%result = "stablehlo.reverse"(%operand) {
  dimensions = array<i64: 1>
} : (tensor<3x2xi32>) -> tensor<3x2xi32>
// %result: [[2, 1], [4, 3], [6, 5]]

 مزيد من الأمثلة

rng

الدلالات

تنشئ هذه الدالة أرقامًا عشوائية باستخدام الخوارزمية rng_distribution وتنتج موترًا result بالشكل shape المحدّد.

إذا كان rng_distribution = UNIFORM، يتم إنشاء الأرقام العشوائية باتّباع التوزيع المنتظم على الفاصل الزمني [a, b). إذا كانت القيمة a >= b، يكون السلوك غير محدّد.

إذا كانت القيمة rng_distribution = NORMAL، يتم إنشاء الأرقام العشوائية باتّباع التوزيع العادي بمتوسط = a وانحراف معياري = b. إذا كانت القيمة b < 0، يكون السلوك غير محدّد.

تختلف طريقة إنشاء الأرقام العشوائية الدقيقة حسب التنفيذ. على سبيل المثال، قد تكون هذه النماذج قطعية أو غير قطعية، وقد تستخدم أو لا تستخدم حالة مخفية.

في محادثات مع العديد من الجهات المعنية، تبيّن أنّ هذه العملية قد تم إيقافها نهائيًا، لذا نخطّط في المستقبل لإزالتها (#597).

المدخلات

التصنيف الاسم النوع القيود
(I1) a موتر ذو 0 بُعد من نوع عدد صحيح أو قيمة منطقية أو عدد نقطة عائمة (C1)، (C2)
(I2) b موتر ذو 0 بُعد من نوع عدد صحيح أو قيمة منطقية أو عدد نقطة عائمة (C1)، (C2)
(I3) shape ثابت موتر أحادي البُعد من النوع si64 (C3)
(I4) rng_distribution تعداد UNIFORM وNORMAL (C2)

النواتج

الاسم النوع القيود
result موتر من نوع عدد صحيح أو قيمة منطقية أو عدد نقطة عائمة (C1-C3)

القيود

  • (C1) element_type(a) = element_type(b) = element_type(result)
  • (C2) إذا كان rng_distribution = NORMAL، يكون is_float(a).
  • (C3) shape(result) = shape

أمثلة

// %a = 0
// %b = 2
// %shape = [3, 3]
%result = "stablehlo.rng"(%a, %b, %shape) {
  rng_distribution = #stablehlo<rng_distribution UNIFORM>
} : (tensor<i32>, tensor<i32>, tensor<2xi64>) -> tensor<3x3xi32>
// %result: [
//           [1, 0, 1],
//           [1, 1, 1],
//           [0, 0, 0]
//          ]

rng_bit_generator

الدلالات

تعرض هذه الدالة output مملوءًا بوحدات بت عشوائية منتظمة وحالة إخراج معدَّلة output_state باستخدام خوارزمية إنشاء أرقام عشوائية زائفة rng_algorithm مع حالة أولية initial_state. من المضمون أن يكون الناتج دالة قطعية لـ initial_state، ولكن ليس من المضمون أن يكون الناتج قطعيًا بين عمليات التنفيذ.

يجب أن تكون قيمة rng_algorithm إحدى القيم التالية:

  • DEFAULT: خوارزمية محدّدة التنفيذ
  • THREE_FRY: صيغة خاصة بالنظام من خوارزمية Threefry.*
  • PHILOX: صيغة من خوارزمية Philox يحدّدها التنفيذ.*

* يُرجى الاطّلاع على: Salmon et al. SC 2011. أرقام عشوائية متوازية: سهلة وبسيطة

المدخلات

التصنيف الاسم النوع القيود
(I1) rng_algorithm تعداد DEFAULT وTHREE_FRY وPHILOX (C2)
(I2) initial_state موتر أحادي البُعد من النوع ui64 (C1)، (C2)

النواتج

الاسم النوع القيود
output_state موتر أحادي البُعد من النوع ui64 (C1)
output موتر من نوع عدد صحيح أو نقطة عائمة

القيود

  • (C1) type(initial_state) = type(output_state)
  • يتم تعريف (C2) size(initial_state) على النحو التالي:
    • تحدِّدها عملية التنفيذ إذا كانت rng_algorithm = DEFAULT.
    • 2 إذا rng_algorithm = THREE_FRY
    • 2 أو 3 إذا rng_algorithm = PHILOX

أمثلة

// %initial_state: [1, 2]
%output_state, %output = "stablehlo.rng_bit_generator"(%initial_state) {
  rng_algorithm = #stablehlo<rng_algorithm THREE_FRY>
} : (tensor<2xui64>) -> (tensor<2xui64>, tensor<2x2xui64>)
// %output_state: [1, 6]
// %output: [
//           [9236835810183407956, 16087790271692313299],
//           [18212823393184779219, 2658481902456610144]
//          ]

round_nearest_afz

الدلالات

تُجري هذه الدالة عملية تقريب على مستوى كل عنصر إلى أقرب عدد صحيح، مع كسر التعادل بعيدًا عن الصفر، وذلك على موتر operand وتنتج موتر result. تنفّذ هذه الدالة العملية roundToIntegralTiesToAway من مواصفات IEEE-754. بالنسبة إلى الأنواع المكمَّمة، يتم تنفيذ dequantize_op_quantize(round_nearest_afz, operand, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C1)

النواتج

الاسم النوع القيود
result متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %operand = [-2.5, 0.4, 0.5, 0.6, 2.5]
%result = "stablehlo.round_nearest_afz"(%operand) : (tensor<5xf64>) -> tensor<5xf64>
// %result: [-3.0, 0.0, 1.0, 1.0, 3.0]

 مزيد من الأمثلة

round_nearest_even

الدلالات

تُجري هذه الدالة عملية تقريب على مستوى كل عنصر إلى أقرب عدد صحيح، مع تسوية حالات التعادل عبر اختيار العدد الصحيح الزوجي، وذلك على موتر operand، وتنتج موتر result. تنفيذ عملية roundToIntegralTiesToEven من مواصفات IEEE-754 بالنسبة إلى الأنواع المحدَّدة القيمة، يتم تنفيذ dequantize_op_quantize(round_nearest_even, operand, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C1)

النواتج

الاسم النوع القيود
result متّجه من نوع النقطة العائمة أو متّجه كمّي لكل متّجه (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %operand = [-2.5, 0.4, 0.5, 0.6, 2.5]
%result = "stablehlo.round_nearest_even"(%operand) : (tensor<5xf64>) -> tensor<5xf64>
// %result: [-2.0, 0.0, 0.0, 1.0, 2.0]

 مزيد من الأمثلة

rsqrt

الدلالات

تُجري عملية الجذر التربيعي المقلوب على مستوى كل عنصر في الموتر operand وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأرقام العشرية: rSqrt من IEEE-754.
  • بالنسبة إلى الأعداد المركّبة: الجذر التربيعي المقلوب المركّب
  • بالنسبة إلى الأنواع الكمية: dequantize_op_quantize(rsqrt, operand, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

النواتج

الاسم النوع القيود
result موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %operand: [[1.0, 4.0], [9.0, 25.0]]
%result = "stablehlo.rsqrt"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[1.0, 0.5], [0.33333343, 0.2]]

 مزيد من الأمثلة

بعثرة

الدلالات

تنتج هذه الدالة موترات results تساوي موترات inputs، إلا أنّه يتم تعديل عدة شرائح محدّدة بواسطة scatter_indices بالقيم updates باستخدام update_computation.

يوضّح المخطّط التالي كيفية ربط العناصر في updates... بالعناصر في results... باستخدام مثال ملموس. يختار المخطّط البياني بعض المؤشرات كمثال updates... ويشرح بالتفصيل المؤشرات results... التي تتوافق معها.

بعثرة

بشكل أكثر رسمية، لكل update_index في index_space(updates[0]):

  • update_scatter_dims = [d for d in axes(updates[0]) and d not in update_window_dims].
  • update_scatter_index = update_index[update_scatter_dims...].
  • يتم تعريف start_index على النحو التالي:
    • scatter_indices[si0, ..., :, ..., siN] حيث si هي عناصر فردية في update_scatter_index ويتم إدراج : في الفهرس index_vector_dim، إذا كان index_vector_dim < rank(scatter_indices).
    • [scatter_indices[update_scatter_index]] في ما عدا ذلك.
  • بالنسبة إلى d_input في axes(inputs[0])،
    • full_start_index[d_input] = start_index[d_start] إذا كان d_input = scatter_dims_to_operand_dims[d_start].
    • full_start_index[d_input] = 0 في ما عدا ذلك.
  • بالنسبة إلى d_input في axes(inputs[0])،
    • full_batching_index[d_input] = update_scatter_index[d_start - (d_start < index_vector_dim ? 0 : 1)] إذا d_input = input_batching_dims[i_batching] و d_start = scatter_indices_batching_dims[i_batching]
    • full_batching_index[d_input] = 0 في ما عدا ذلك.
  • update_window_index = update_index[update_window_dims...].
  • full_window_index = [wi0, ..., 0, ..., wiN] حيث wi هي عناصر فردية في update_window_index، ويتم إدراج 0 في الفهارس من inserted_window_dims وinput_batching_dims.
  • result_index = full_start_index + full_batching_index + full_window_index.

بما أنّ results = exec(schedule, inputs)، حيث:

  • schedule هو تبديل محدّد التنفيذ لـ index_space(updates[0]).
  • exec([update_index, ...], results) = exec([...], updated_results) حيث:
    • إذا كان result_index ضمن حدود shape(results...)
    • updates_converted = to_destination_type( updates...[update_index], type(func_inputs(update_computation) [len(func_inputs(update_computation))//2:])... )
    • updated_values = update_computation(results...[result_index], updates_converted)
    • updated_results هي نسخة من results مع ضبط results...[result_index] على updated_values....
    • ويمكنك بدلاً من ذلك
    • updated_results = results.
  • exec([], results) = results.

إذا كانت قيمة indices_are_sorted هي true، يمكن أن يفترض التنفيذ أنّ scatter_indices مرتبة حسب scatter_dims_to_operand_dims، وإلا سيكون السلوك غير محدّد. بشكل أكثر رسمية، بالنسبة إلى جميع i1 < i2 من indices(result)، يكون full_start_index(i1) <= full_start_index(i2).

إذا كانت قيمة unique_indices هي true، يمكن أن يفترض التنفيذ أنّ جميع فهارس result_index التي يتم توزيعها فريدة. إذا كانت قيمة unique_indices هي true ولكن الفهارس التي يتم توزيعها عليها ليست فريدة، يكون السلوك غير محدّد.

المدخلات

التصنيف الاسم النوع القيود
(I1) inputs عدد متغير من الموترات أو الموترات الكمية لكل موتر (C1)، و(C2)، و(C4-C6)، و(C11)، و(C13)، و(C18)، و(C21)، و(C23-C24)
(I2) scatter_indices متّجه متعدّد الأبعاد من نوع عدد صحيح (C4)، (C15)، (C19)، (C22)
(I3) updates عدد متغير من الموترات أو الموترات الكمية لكل موتر (C3-C6)، (C8)
(I4) update_window_dims ثابت موتر أحادي البُعد من النوع si64 (C2)، (C4)، (C7-C8)
(I5) inserted_window_dims ثابت موتر أحادي البُعد من النوع si64 (C2), (C4), (C9-C11)
(I6) input_batching_dims ثابت موتر أحادي البُعد من النوع si64 (C2)، و(C4)، و(C9)، و(C12-13)، و(C17-18)، و(C20)
(I7) scatter_indices_batching_dims ثابت موتر أحادي البُعد من النوع si64 (C14-C18)
(I8) scatter_dims_to_operand_dims ثابت موتر أحادي البُعد من النوع si64 (C19-C21)
(I9) index_vector_dim ثابت من النوع si64 (C4)، (C16)، (C19)، (C22)
(I10) indices_are_sorted ثابت من النوع i1
(I11) unique_indices ثابت من النوع i1
(I12) update_computation دالة (C23)

النواتج

الاسم النوع القيود
results عدد متغير من الموترات أو الموترات الكمية لكل موتر (C24-C25)

القيود

  • (C1) same(shape(inputs...))
  • (C2) rank(inputs[0]) = size(update_window_dims) + size(inserted_window_dims) + size(input_batching_dims)
  • (C3) same(shape(updates...))
  • (C4) shape(updates[0]) = combine(update_scatter_dim_sizes, update_window_dim_sizes) حيث:
    • update_scatter_dim_sizes = shape(scatter_indices) باستثناء أنّه لا يتم تضمين حجم السمة scatter_indices الذي يتوافق مع index_vector_dim.
    • update_window_dim_sizes <= shape(inputs[0]) باستثناء أنّه لا يتم تضمين أحجام السمات في inputs[0] التي تتوافق مع inserted_window_dims وinput_batching_dims.
    • تضع الدالة combine السلسلة update_scatter_dim_sizes على المحاور التي تتوافق مع update_scatter_dims والسلسلة update_window_dim_sizes على المحاور التي تتوافق مع update_window_dims.
  • (C5) 0 < size(inputs) = size(updates) = N.
  • (C6) element_type(updates...) = element_type(inputs...)
  • (C7) is_unique(update_window_dims) and is_sorted(update_window_dims)
  • (C8) 0 <= update_window_dims < rank(updates[0])
  • (C9) is_unique(concatenate(inserted_window_dims, input_batching_dims))
  • (C10) is_sorted(inserted_window_dims)
  • (C11) 0 <= inserted_window_dims < rank(inputs[0]).
  • (C12) is_sorted(input_batching_dims)
  • (C13) 0 <= input_batching_dims < rank(inputs[0])).
  • (C14) is_unique(scatter_indices_batching_dims).
  • (C15) 0 <= scatter_indices_batching_dims < rank(scatter_indices).
  • (C16) index_vector_dim not in scatter_indices_batching_dims
  • (C17) size(input_batching_dims) == size(scatter_indices_batching_dims).
  • (C18) dim(inputs[0], input_batching_dims...) = dim(scatter_indices, scatter_indices_batching_dims...)
  • (C19) size(scatter_dims_to_operand_dims) = index_vector_dim < rank(scatter_indices) ? dim(scatter_indices, index_vector_dim) : 1.
  • (C20) is_unique(concatenate(scatter_dims_to_operand_dims, input_batching_dims)).
  • (C21) 0 <= scatter_dims_to_operand_dims < rank(inputs[0]).
  • (C22) 0 <= index_vector_dim <= rank(scatter_indices)
  • (C23) update_computation من النوع (tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ..., tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>)، حيث is_promotable(element_type(inputs[i]), Ei).
  • (C24) shape(inputs...) = shape(results...)
  • (C25) element_type(results[i]) = Ei لكل i في [0,N)

أمثلة

// %input: [
//          [
//           [[1, 2], [3, 4], [5, 6], [7, 8]],
//           [[9, 10],[11, 12], [13, 14], [15, 16]],
//           [[17, 18], [19, 20], [21, 22], [23, 24]]
//          ],
//          [
//           [[25, 26], [27, 28], [29, 30], [31, 32]],
//           [[33, 34], [35, 36], [37, 38], [39, 40]],
//           [[41, 42], [43, 44], [45, 46], [47, 48]]
//          ]
//         ]
// %scatter_indices: [
//                    [
//                     [[0, 0], [1, 0], [2, 1]],
//                     [[0, 1], [1, 1], [0, 9]]
//                    ],
//                    [
//                     [[0, 0], [2, 1], [2, 2]],
//                     [[1, 2], [0, 1], [1, 0]]
//                    ]
//                   ]
// %update: [
//           [
//            [[[1, 1], [1, 1]], [[1, 1], [1, 1]], [[1, 1], [1, 1]]],
//            [[[1, 1], [1, 1]], [[1, 1], [1, 1]], [[1, 1], [1, 1]]]
//           ],
//           [
//            [[[1, 1], [1, 1]], [[1, 1], [1, 1]], [[1, 1], [1, 1]]],
//            [[[1, 1], [1, 1]], [[1, 1], [1, 1]], [[1, 1], [1, 1]]]
//           ]
//          ]
%result = "stablehlo.scatter"(%input, %scatter_indices, %update) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
    "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  scatter_dimension_numbers = #stablehlo.scatter<
    update_window_dims = [3, 4],
    inserted_window_dims = [1],
    input_batching_dims = [0],
    scatter_indices_batching_dims = [1],
    scatter_dims_to_operand_dims = [2, 1],
    index_vector_dim = 3>,
  indices_are_sorted = false,
  unique_indices = false
} : (tensor<2x3x4x2xi64>, tensor<2x2x3x2xi64>, tensor<2x2x3x2x2xi64>) -> tensor<2x3x4x2xi64>
// %result: [
//           [
//            [[3, 4], [6, 7], [6, 7], [7, 8]],
//            [[9, 10],[11, 12], [15, 16], [17, 18]],
//            [[17, 18], [19, 20], [22, 23], [24, 25]]
//           ],
//           [
//            [[25, 26], [28, 29], [30, 31], [31, 32]],
//            [[35, 36], [38, 39], [38, 39], [39, 40]],
//            [[41, 42], [44, 45], [46, 47], [47, 48]]
//           ]
//          ]

 مزيد من الأمثلة

اختيار

الدلالات

تُنتج هذه الدالة موتر result يتم فيه اختيار كل عنصر من الموتر on_true أو الموتر on_false استنادًا إلى قيمة العنصر المقابل في pred. بشكل أكثر رسمية، result[result_index] = pred_element ? on_true[result_index] : on_false[result_index]، حيث pred_element = rank(pred) = 0 ? pred[] : pred[result_index]. بالنسبة إلى الأنواع المحدَّدة القيمة، يتم تنفيذ dequantize_select_quantize(pred, on_true, on_false, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) pred متّجه متعدّد الأبعاد من النوع i1 (C1)
(I2) on_true متّجه أو متّجه كمّي لكل متّجه (C1-C2)
(I3) on_false متّجه أو متّجه كمّي لكل متّجه (C2)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي لكل متّجه (C2)

القيود

  • (C1) rank(pred) = 0 or shape(pred) = shape(on_true)
  • (C2) baseline_type(on_true) = baseline_type(on_false) = baseline_type(result)

أمثلة

// %pred: [[false, true], [true, false]]
// %on_true: [[1, 2], [3, 4]]
// %on_false: [[5, 6], [7, 8]]
%result = "stablehlo.select"(%pred, %on_true, %on_false) : (tensor<2x2xi1>, tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 2], [3, 8]]

 مزيد من الأمثلة

select_and_scatter

الدلالات

توزّع هذه العملية القيم من موتر source باستخدام scatter استنادًا إلى نتيجة reduce_window لموتر input باستخدام select، وتنتج موتر result.

يوضّح المخطّط التالي كيفية احتساب العناصر في result من operand وsource باستخدام مثال ملموس.

select_and_scatter

بشكل أكثر رسمية:

  • selected_values = reduce_window_without_init(...) مع الإدخالات التالية:

    • inputs = [operand].
    • window_dimensions وwindow_strides وpadding التي يتم استخدامها كما هي
    • base_dilations = windows_dilations = 1.
    • يتم تعريف body على النحو التالي:
    def body(arg0: tensor<E>, arg1: tensor<E>) -> tensor<E>:
      return select(arg0, arg1) ? arg0 : arg1;
    

    حيث تعمل E = element_type(operand) وreduce_window_without_init تمامًا مثل reduce_window، باستثناء أنّ schedule الأساسي reduce (راجِع reduce) لا يتضمّن قيمًا أولية. لم يتم تحديد الإجراء الذي سيتم اتخاذه في حال عدم توفّر قيم في النافذة المقابلة (#731).

  • result[result_index] = reduce([source_values], [init_value], [0], scatter) المكان:

    • source_values = [source[source_index] for source_index in source_indices].
    • selected_index(source_index) = operand_index إذا كان selected_values[source_index] يتضمّن العنصر operand من operand_index
    • source_indices = [source_index for source_index in indices(source) if selected_index(source_index) = result_index].

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه أو متّجه كمّي لكل متّجه (C1-C4), (C6), (C8-C11)
(I2) source متّجه أو متّجه كمّي لكل متّجه (C1)، (C2)
(I3) init_value متّجه صفري الأبعاد أو متّجه كمّي لكل متّجه (C3)
(I4) window_dimensions ثابت موتر أحادي البُعد من النوع si64 (C2)، (C4)، (C5)
(I5) window_strides ثابت موتر أحادي البُعد من النوع si64 (C2)، (C6)، (C7)
(I6) padding ثابت موتر ثنائي الأبعاد من النوع si64 (C2)، (C8)
(I7) select دالة (C9)
(I8) scatter دالة (C10)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي لكل متّجه (C11-C12)

القيود

  • (C1) element_type(operand) = element_type(source)
  • (C2) shape(source) = num_windows حيث:
    • padded_operand_shape = padding[:, 0] + shape(operand) + padding[:, 1].
    • is_empty_window = padded_operand_shape = 0 || window_dimensions > padded_operand_shape.
    • num_windows = is_empty_window ? 0 : floor((padded_operand_shape - window_dimensions) / window_strides) + 1.
  • (C3) element_type(init_value) = element_type(operand)
  • (C4) size(window_dimensions) = rank(operand)
  • (C5) 0 < window_dimensions.
  • (C6) size(window_strides) = rank(operand)
  • (C7) 0 < window_strides
  • (C8) shape(padding) = [rank(operand), 2]
  • (C9) select من النوع (tensor<E>, tensor<E>) -> tensor<i1> حيث E = element_type(operand).
  • (C10) scatter له النوع (tensor<E>, tensor<E>) -> tensor<E> حيث is_promotable(element_type(operand), E).
  • (C11) shape(operand) = shape(result).
  • (C12) element_type(result) = E

أمثلة

// %operand: [[1, 5], [2, 5], [3, 6], [4, 4]]
// %source: [[5, 6], [7, 8]]
// %init_value: 0
%result = "stablehlo.select_and_scatter"(%operand, %source, %init_value) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.compare"(%arg0, %arg1) {
      comparison_direction = #stablehlo<comparison_direction GE>
    } : (tensor<i64>, tensor<i64>) -> tensor<i1>
    "stablehlo.return"(%0) : (tensor<i1>) -> ()
}, {
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
    "stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
  window_dimensions = array<i64: 3, 1>,
  window_strides = array<i64: 2, 1>,
  padding = dense<[[0, 1], [0, 0]]> : tensor<2x2xi64>
} : (tensor<4x2xi64>, tensor<2x2xi64>, tensor<i64>) -> tensor<4x2xi64>
// %result: [[0, 0], [0, 0], [5, 14], [7, 0]]

 مزيد من الأمثلة

إرسال

الدلالات

إرسال inputs إلى القناة channel_id يتم بعد ذلك إرسال المدخلات إلى الأجهزة الأخرى بالترتيب المحدّد في source_target_pairs. تنتج العملية رمزًا مميزًا result.

إذا كانت قيمة is_host_transfer هي true، فهذا يعني أنّ العملية تنقل البيانات إلى المضيف. بخلاف ذلك، ينقل البيانات إلى جهاز آخر استنادًا إلى قيم source_target_pairs. تكرّر هذه العلامة المعلومات المقدَّمة في channel_type، لذا نخطّط في المستقبل للإبقاء على إحدى العلامتين فقط (#666). إذا كانت قيمة is_host_transfer تساوي false وكانت قيمة source_target_pairs هي None أو فارغة، سيتم اعتبار ذلك سلوكًا غير محدّد.

المدخلات

التصنيف الاسم النوع القيود
(I1) inputs عدد متغيّر من الموترات أو الموترات الكمية
(I2) token token
(I3) channel_id ثابت من النوع si64
(I4) channel_type تعداد DEVICE_TO_DEVICE وDEVICE_TO_HOST (C5)
(I5) is_host_transfer ثابت من النوع i1 (C5-C6)
(I6) source_target_pairs ثابت موتر ثنائي الأبعاد من النوع si64 (C1-C4), (C6)

النواتج

الاسم النوع
result token

القيود

  • (C1) dim(source_target_pairs, 1) = 2
  • (C2) is_unique(source_target_pairs[:, 0])
  • (C3) is_unique(source_target_pairs[:, 1])
  • (C4) 0 <= source_target_pairs < N، حيث يتم تعريف N على النحو التالي:
    • num_replicas في حال استخدام cross_replica
    • num_partitions في حال استخدام cross_partition
  • يتم تعريف (C5) channel_type على النحو التالي:
    • DEVICE_TO_HOST إذا is_host_transfer = true،
    • DEVICE_TO_DEVICE في ما عدا ذلك.

أمثلة

%result = "stablehlo.send"(%operand, %token) {
  channel_handle = #stablehlo.channel_handle<handle = 0, type = 1>,
  is_host_transfer = false,
  source_target_pairs = dense<[[0, 1], [1, 2]]> : tensor<2x2xi64>
} : (tensor<2x2xi64>, !stablehlo.token) -> !stablehlo.token

 مزيد من الأمثلة

shift_left

الدلالات

تُجري عملية إزاحة إلى اليسار على مستوى كل عنصر في الموتر lhs بمقدار rhs من وحدات البت وتنتج الموتر result.

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs متّجه متعدّد الأبعاد من نوع عدد صحيح (C1)
(I2) rhs متّجه متعدّد الأبعاد من نوع عدد صحيح (C1)

النواتج

الاسم النوع القيود
result متّجه متعدّد الأبعاد من نوع عدد صحيح (C1)

القيود

  • (C1) type(lhs) = type(rhs) = type(result)

أمثلة

// %lhs: [-1, 0, 1]
// %rhs: [1, 2, 3]
%result = "stablehlo.shift_left"(%lhs, %rhs): (tensor<3xi64>, tensor<3xi64>) -> tensor<3xi64>
// %result: [-2, 0, 8]

 مزيد من الأمثلة

shift_right_arithmetic

الدلالات

تُجري هذه الدالة عملية إزاحة حسابية لليمين على مستوى كل عنصر في الموتر lhs بمقدار rhs من وحدات البت، وتنتج الموتر result.

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs متّجه متعدّد الأبعاد من نوع عدد صحيح (C1)
(I2) rhs متّجه متعدّد الأبعاد من نوع عدد صحيح (C1)

النواتج

الاسم النوع القيود
result متّجه متعدّد الأبعاد من نوع عدد صحيح (C1)

القيود

  • (C1) type(lhs) = type(rhs) = type(result)

أمثلة

// %lhs: [-1, 0, 8]
// %rhs: [1, 2, 3]
%result = "stablehlo.shift_right_arithmetic"(%lhs, %rhs): (tensor<3xi64>, tensor<3xi64>) -> tensor<3xi64>
// %result: [-1, 0, 1]

 مزيد من الأمثلة

shift_right_logical

الدلالات

تُجري هذه الدالة عملية إزاحة منطقية إلى اليمين على مستوى كل عنصر في الموتر lhs بمقدار rhs من وحدات البت، وتنتج الموتر result.

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs متّجه متعدّد الأبعاد من نوع عدد صحيح (C1)
(I2) rhs متّجه متعدّد الأبعاد من نوع عدد صحيح (C1)

النواتج

الاسم النوع القيود
result متّجه متعدّد الأبعاد من نوع عدد صحيح (C1)

القيود

  • (C1) type(lhs) = type(rhs) = type(result)

أمثلة

// %lhs: [-1, 0, 8]
// %rhs: [1, 2, 3]
%result = "stablehlo.shift_right_logical"(%lhs, %rhs): (tensor<3xi64>, tensor<3xi64>) -> tensor<3xi64>
// %result: [9223372036854775807, 0, 1]

 مزيد من الأمثلة

علامة

الدلالات

تعرض هذه الدالة علامة كل عنصر من عناصر operand وتنتج موترًا result. بشكل أكثر رسمية، يمكن التعبير عن دلالات كل عنصر x باستخدام بنية Python على النحو التالي:

def sign(x):
  if is_integer(x):
    if compare(x, 0, LT, SIGNED): return -1
    if compare(x, 0, EQ, SIGNED): return 0
    return 1
  elif is_float(x):
    if is_nan(x): return NaN
    if compare(x, -0.0, EQ, FLOAT): return -0.0
    if compare(x, +0.0, EQ, FLOAT): return +0.0
    if compare(x, 0.0, LT, FLOAT): return -1.0
    return 1.0
  elif is_complex(x):
    if is_nan(real(x)) or is_nan(imag(x)): return (NaN, NaN)
    if compare(x, (0.0, 0.0), EQ, FLOAT): return (0.0, 0.0)
    return divide(x, convert(abs(x), type(x)))

بالنسبة إلى الأنواع المحدَّدة القيمة، يتم تنفيذ dequantize_op_quantize(sign, operand, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من نوع عدد صحيح موقّع أو عدد نقطة عائمة أو عدد مركّب أو موتر كمّي لكل موتر (C1)

النواتج

الاسم النوع القيود
result موتر من نوع عدد صحيح موقّع أو عدد نقطة عائمة أو عدد مركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// Logical values: +NaN, -1.0, -0.0, +0.0, 1.0
// operand: [0x7FFFFFFFFFFFFFFF, -1.0, -0.0, 0.0, 1.0]
%result = "stablehlo.sign"(%operand) : (tensor<5xf64>) -> tensor<5xf64>
// Logical values: +NaN, -1.0, -0.0, +0.0, 1.0
// %result: [0x7FFFFFFFFFFFFFFF, -1.0, -0.0, 0.0, 1.0]

 مزيد من الأمثلة

جيب الزاوية

الدلالات

تُجري هذه الدالة عملية الجيب على مستوى كل عنصر في الموتر operand وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأرقام العشرية: sin من IEEE-754.
  • بالنسبة إلى الأعداد المركّبة: جيب الزاوية المركّب
  • بالنسبة إلى الأنواع الكمية: dequantize_op_quantize(sine, operand, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

النواتج

الاسم النوع القيود
result موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %operand: [
//            [0.0, 1.57079632],       // [0, pi/2]
//            [3.14159265, 4.71238898] // [pi, 3pi/2]
//           ]
%result = "stablehlo.sine"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[0.0, 1.0], [0.0, -1.0]]

 مزيد من الأمثلة

slice

الدلالات

يستخرِج شريحة من operand باستخدام فهارس البدء المحسوبة بشكل ثابت، وينتج موتر result. تحتوي start_indices على فهارس البداية لشريحة كل سمة، وتحتوي limit_indices على فهارس النهاية (غير شاملة) لشريحة كل سمة، وتحتوي strides على الخطوات لكل سمة.

بشكل أكثر رسمية، result[result_index] = operand[operand_index] حيث operand_index = start_indices + result_index * strides.

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه أو متّجه كمّي لكل متّجه (C1-C3), (C5)
(I2) start_indices ثابت موتر أحادي البُعد من النوع si64 (C2)، (C3)، (C5)
(I3) limit_indices ثابت موتر أحادي البُعد من النوع si64 (C2)، (C3)، (C5)
(I4) strides ثابت موتر أحادي البُعد من النوع si64 (C2)، (C4)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي لكل متّجه (C1)، (C5)

القيود

  • (C1) element_type(operand) = element_type(result)
  • (C2) size(start_indices) = size(limit_indices) = size(strides) = rank(operand)
  • (C3) 0 <= start_indices <= limit_indices <= shape(operand)
  • (C4) 0 < strides
  • (C5) shape(result) = ceil((limit_indices - start_indices) / strides).

أمثلة

// %operand: [
//            [0, 0, 0, 0],
//            [0, 0, 1, 1],
//            [0, 0, 1, 1]
//           ]
%result = "stablehlo.slice"(%operand) {
  start_indices = array<i64: 1, 2>,
  limit_indices = array<i64: 3, 4>,
  strides = array<i64: 1, 1>
} : (tensor<3x4xi64>) -> tensor<2x2xi64>
// % result: [
//            [1, 1],
//            [1, 1]
//           ]

 مزيد من الأمثلة

ترتيب

الدلالات

ترتّب هذه الدالة الشرائح الأحادية البُعد من inputs على طول البُعد dimension معًا، وفقًا لـ comparator، وتنتج results.

على عكس المدخلات المشابهة في العمليات الأخرى، تسمح الدالة dimension بالقيم السالبة، مع الدلالات الموضّحة أدناه. في المستقبل، قد يتم حظر ذلك لأسباب تتعلق بالاتساق (#1377).

إذا كانت قيمة is_stable صحيحة، يكون الترتيب ثابتًا، أي يتم الحفاظ على الترتيب النسبي للعناصر التي يعتبرها عامل المقارنة متساوية. في حالة وجود إدخال واحد، يعتبر عامل المقارنة العنصرين e1 وe2 متساويين إذا وفقط إذا كان comparator(e1, e2) = comparator(e2, e1) = false. يمكنك الاطّلاع على الصيغة أدناه لمعرفة كيفية تعميم ذلك على مدخلات متعددة.

بشكل أكثر رسمية، لكل result_index في index_space(results[0]):

  • adjusted_dimension = dimension >= 0 ? dimension : rank(inputs[0]) + dimension.
  • حيث result_slice = [ri0, ..., :, ..., riR-1] هي riN عناصر فردية في result_index، ويتم إدراج : في adjusted_dimension.
  • inputs_together = (inputs[0]..., ..., inputs[N-1]...).
  • results_together[result_slice] = sort(inputs_together[result_slice], comparator_together).
  • حيث ترتّب sort شريحة أحادية البُعد بترتيب غير تنازلي، مع توقّع أن تعرض comparator_together القيمة true إذا كانت الوسيطة على الجانب الأيسر أصغر من الوسيطة الثانية على الجانب الأيمن.
  • def comparator_together(lhs_together, rhs_together):
      args = []
      for (lhs_el, rhs_el) in zip(lhs_together, rhs_together):
        args.append(lhs_el)
        args.append(rhs_el)
      return comparator(*args)
    
  • (results[0]..., ..., results[N-1]...) = results_together.

المدخلات

التصنيف الاسم النوع القيود
(I1) inputs عدد متغير من الموترات أو الموترات الكمية لكل موتر (C1-C5)
(I2) dimension ثابت من النوع si64 (C4)
(I3) is_stable ثابت من النوع i1
(I4) comparator دالة (C5)

النواتج

الاسم النوع القيود
results عدد متغير من الموترات أو الموترات الكمية لكل موتر (C2), (C3)

القيود

  • (C1) 0 < size(inputs)
  • (C2) type(inputs...) = type(results...)
  • (C3) same(shape(inputs...) + shape(results...))
  • (C4) -R <= dimension < R، حيث R = rank(inputs[0])
  • (C5) comparator له النوع (tensor<E1>, tensor<E1>, ..., tensor<EN-1>, tensor<EN-1>) -> tensor<i1>، حيث Ei = element_type(inputs[i]).

أمثلة

// %input0 = [[1, 2, 3], [3, 2, 1]]
// %input1 = [[3, 2, 1], [1, 2, 3]]
%result0, %result1 = "stablehlo.sort"(%input0, %input1) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>, %arg2: tensor<i64>, %arg3: tensor<i64>):
    %predicate = "stablehlo.compare"(%arg0, %arg1) {
      comparison_direction = #stablehlo<comparison_direction GT>
    } : (tensor<i64>, tensor<i64>) -> tensor<i1>
    "stablehlo.return"(%predicate) : (tensor<i1>) -> ()
}) {
  dimension = 0 : i64,
  is_stable = true
} : (tensor<2x3xi64>, tensor<2x3xi64>) -> (tensor<2x3xi64>, tensor<2x3xi64>)
// %result0 = [[3, 2, 3], [1, 2, 1]]
// %result1 = [[1, 2, 1], [3, 2, 3]]

 مزيد من الأمثلة

sqrt

الدلالات

تُجري هذه الدالة عملية الجذر التربيعي لكل عنصر في الموتر operand وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأرقام العشرية: squareRoot من IEEE-754
  • بالنسبة إلى الأعداد المركّبة: الجذر التربيعي المركّب
  • بالنسبة إلى الأنواع الكمية: dequantize_op_quantize(sqrt, operand, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

النواتج

الاسم النوع القيود
result موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %operand: [[0.0, 1.0], [4.0, 9.0]]
%result = "stablehlo.sqrt"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[0.0, 1.0], [2.0, 3.0]]

 مزيد من الأمثلة

طرح

الدلالات

تُجري هذه الدالة عملية طرح العناصر المقابلة في موترَين lhs وrhs وتنتج موترًا result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأعداد الصحيحة: طرح الأعداد الصحيحة
  • بالنسبة إلى الأرقام العشرية: subtraction من IEEE-754
  • بالنسبة إلى الأعداد المركّبة: الطرح المركّب
  • بالنسبة إلى الأنواع الكمية:
    • dequantize_op_quantize(subtract, lhs, rhs, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs موتر من النوع الصحيح أو النقطة العائمة أو المركّب أو موتر كمّي لكل موتر (C1)
(I2) rhs موتر من النوع الصحيح أو النقطة العائمة أو المركّب أو موتر كمّي لكل موتر (C1)

النواتج

الاسم النوع القيود
result موتر من النوع الصحيح أو النقطة العائمة أو المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)

أمثلة

// %lhs: [[6, 8], [10, 12]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.subtract"(%lhs, %rhs) : (tensor<2x2xf32>, tensor<2x2xf32>) -> (tensor<2x2xf32>)
// %result: [[1, 2], [3, 4]]

 مزيد من الأمثلة

tan

الدلالات

تُجري هذه الدالة عملية الظل لكل عنصر في الموتر operand وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأرقام العشرية: tan من IEEE-754
  • بالنسبة إلى الأعداد المركّبة: ظل الزاوية المركّب
  • بالنسبة إلى الأنواع الكمية: dequantize_op_quantize(tan, operand, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

النواتج

الاسم النوع القيود
result موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %operand: [
//            [0.0, 1.57079632],       // [0, pi/2]
//            [3.14159265, 4.71238898] // [pi, 3pi/2]
//           ]
%result = "stablehlo.tan"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [
//           [0.0, 1.63312e+16],
//           [0.0, 5.44375e+15]
//          ]

 مزيد من الأمثلة

tanh

الدلالات

تُجري عملية الظل الزائدي على مستوى كل عنصر في الموتر operand وتنتج الموتر result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى الأرقام العشرية: tanh من IEEE-754
  • بالنسبة إلى الأعداد المركّبة: ظل الزاوية الزائدي المركّب
  • بالنسبة إلى الأنواع الكمية:
    • dequantize_op_quantize(tanh, operand, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

النواتج

الاسم النوع القيود
result موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_type(operand) = baseline_type(result)

أمثلة

// %operand: [-1.0, 0.0, 1.0]
%result = "stablehlo.tanh"(%operand) : (tensor<3xf32>) -> tensor<3xf32>
// %result: [-0.76159416, 0.0, 0.76159416]

 مزيد من الأمثلة

تبديل

الدلالات

تبدّل هذه العملية ترتيب أبعاد موتر operand باستخدام permutation وتنتج موتر result. بشكل أكثر رسمية، result[result_index] = operand[operand_index] حيث result_index[d] = operand_index[permutation[d]].

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه أو متّجه كمّي (C1-C4)
(I2) permutation ثابت موتر أحادي البُعد من النوع si64 (C2-C4)

النواتج

الاسم النوع القيود
result متّجه أو متّجه كمّي (C1)، (C3-C4)

القيود

  • يتم تحديد (C1) element_type(result) من خلال:
    • element_type(operand)، إذا !is_per_axis_quantized(operand)
    • element_type(operand) باستثناء أنّ quantization_dimension(operand) وquantization_dimension(result) قد يختلفان.
  • (C2) permutation هي تبديل لـ range(rank(operand)).
  • (C3) shape(result) = dim(operand, permutation...)
  • (C4) إذا كان is_per_axis_quantized(result)، فإن quantization_dimension(operand) = permutation(quantization_dimension(result)).

أمثلة

// %operand: [
//            [[1,2], [3,4], [5,6]],
//            [[7,8], [9,10], [11,12]]
//           ]
%result = "stablehlo.transpose"(%operand) {
  permutation = array<i64: 2, 1, 0>
} : (tensor<2x3x2xi32>) -> tensor<2x3x2xi32>
// %result: [
//           [[1,7], [3,9], [5,11]],
//           [[2,8], [4,10], [6,12]]
//          ]

 مزيد من الأمثلة

triangular_solve

الدلالات

تحلّ هذه الدالة مجموعات من أنظمة المعادلات الخطية التي تتضمّن مصفوفات معاملات مثلّثة علوية أو سفلية.

بشكل أكثر رسمية، إذا كان لدينا a وb، فإنّ result[i0, ..., iR-3, :, :] هو الحل للمعادلة op(a[i0, ..., iR-3, :, :]) * x = b[i0, ..., iR-3, :, :] عندما تكون قيمة left_side هي true أو x * op(a[i0, ..., iR-3, :, :]) = b[i0, ..., iR-3, :, :] عندما تكون قيمة left_side هي false، وذلك من خلال حل المتغيّر x حيث يتم تحديد قيمة op(a) من خلال transpose_a، التي يمكن أن تكون إحدى القيم التالية:

  • NO_TRANSPOSE: تنفيذ العملية باستخدام a كما هي
  • TRANSPOSE: تنفيذ عملية على منقولة a
  • ADJOINT: تنفيذ عملية على منقولة مترافقة للمصفوفة a

تتم قراءة بيانات الإدخال من المثلث السفلي لـ a فقط، إذا كان lower هو true أو المثلث العلوي لـ a، بخلاف ذلك. يتم عرض بيانات الإخراج في المثلث نفسه، أما القيم في المثلث الآخر فيتم تحديدها حسب التنفيذ.

إذا كانت قيمة unit_diagonal صحيحة، يمكن أن يفترض التنفيذ أنّ العناصر القطرية في a تساوي 1، وإلا سيكون السلوك غير محدّد.

بالنسبة إلى الأنواع المحدَّدة القيمة، يتم تنفيذ dequantize_op_quantize(lambda x, y: triangular_solve(x, y, left_side, lower, unit_diagonal, transpose_a), a, b, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) a موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1-C3)
(I2) b متّجه من النوع النقطة العائمة أو النوع المركّب أو متّجه كمّي لكل متّجه (C1-C4)
(I3) left_side ثابت من النوع i1 (C3)
(I4) lower ثابت من النوع i1
(I5) unit_diagonal ثابت من النوع i1
(I6) transpose_a تعداد NO_TRANSPOSE وTRANSPOSE وADJOINT

النواتج

الاسم النوع القيود
result موتر من النوع النقطة العائمة أو النوع المركّب أو موتر كمّي لكل موتر (C1)

القيود

  • (C1) baseline_element_type(a) = baseline_element_type(b)
  • (C2) 2 <= rank(a) = rank(b) = R
  • (C3) يتم تحديد العلاقة بين shape(a) وshape(b) على النحو التالي:
    • shape(a)[:-3] = shape(b)[:-3].
    • dim(a, -2) = dim(a, -1) = dim(b, left_side ? -2 : -1).
  • (C4) baseline_type(b) = baseline_type(result)

أمثلة

// %a = [
//       [1.0, 0.0, 0.0],
//       [2.0, 4.0, 0.0],
//       [3.0, 5.0, 6.0]
//      ]
// %b = [
//       [2.0, 0.0, 0.0],
//       [4.0, 8.0, 0.0],
//       [6.0, 10.0, 12.0]
//      ]
%result = "stablehlo.triangular_solve"(%a, %b) {
  left_side = true,
  lower = true,
  unit_diagonal = false,
  transpose_a = #stablehlo<transpose NO_TRANSPOSE>
} : (tensor<3x3xf32>, tensor<3x3xf32>) -> tensor<3x3xf32>
// %result: [
//           [2.0, 0.0, 0.0],
//           [0.0, 2.0, 0.0],
//           [0.0, 0.0, 2.0]
//          ]

tuple

الدلالات

تنشئ هذه الدالة مجموعة result من القيم val.

المدخلات

التصنيف الاسم النوع القيود
(I1) val عدد متغيّر من القيم (C1)

النواتج

الاسم النوع القيود
result tuple (C1)

القيود

  • (C1) result من النوع tuple<E0, ..., EN-1> حيث Ei = type(val[i]).

أمثلة

// %val0: memref[1.0, 2.0]
// %val1: (3)
%result = "stablehlo.tuple"(%val0, %val1) : (memref<2xf32>, tuple<tensor<i32>>) -> tuple<memref<2xf32>, tuple<tensor<i32>>>
// %result: (memref[1.0, 2.0], (3))

 مزيد من الأمثلة

uniform_dequantize

الدلالات

تُجري هذه العملية تحويلاً على مستوى العناصر للموتر الكمّي operand إلى موتر ذي نقطة عائمة result وفقًا لمعلّمات التكميم المحدّدة حسب نوع operand.

بشكل أكثر رسمية، result = dequantize(operand).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand متّجه كمّي (C1)، (C2)

النواتج

الاسم النوع القيود
result موتر من نوع النقطة العائمة (C1)، (C2)

القيود

  • (C1) shape(operand) = shape(result)
  • (C2) element_type(result) = expressed_type(operand)

أمثلة

// %operand: [10, 10]
%result = "stablehlo.uniform_dequantize"(%operand) : (tensor<2x!quant.uniform<i8:f32:0, {0.1:-30,0.5:-20}>>) -> tensor<2xf32>
// %result: [4.0, 15.0]

uniform_quantize

الدلالات

تُجري هذه العملية تحويلاً على مستوى العناصر لموتر نقطة عائمة أو موتر كمّي operand إلى موتر كمّي result وفقًا لمعلمات التكميم المحدّدة حسب نوع result.

بشكل أكثر رسمية،

  • إذا كان is_float(operand):
    • result = quantize(operand, type(result)).
  • إذا كان is_quantized(operand):
    • float_result = dequantize(operand).
    • result = quantize(float_result, type(result)).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand موتر من النوع العشري أو الكمي (C1)، (C2)

النواتج

الاسم النوع القيود
result متّجه كمّي (C1)، (C2)

القيود

  • (C1) shape(operand) = shape(result)
  • (C2) expressed_type(result) = is_float(operand) ? element_type(operand) : expressed_type(operand)

أمثلة

// %operand: [4.0, 15.0]
%result = "stablehlo.uniform_quantize"(%operand) : (tensor<2xf32>) -> tensor<2x!quant.uniform<i8:f32:0, {0.1:-30,0.5:-20}>>
// %result: [10, 10]

// %operand: [10, 10]
%result = "stablehlo.uniform_quantize"(%operand) : (tensor<2x!quant.uniform<i8:f32:0, {0.1:-30,0.5:-20}>>) -> tensor<2x!quant.uniform<i8:f32:0, {0.1:-20,0.2:-30}>>
// %result: [20, 45]

بينما

الدلالات

تنتج هذه الدالة الناتج من تنفيذ الدالة body صفر مرة أو أكثر طالما أنّ الدالة cond تنتج true. وبشكل أكثر رسمية، يمكن التعبير عن الدلالات باستخدام صيغة Python كما يلي:

internal_state = operand
while cond(*internal_state):
  internal_state = body(*internal_state)
results = internal_state

سيتم تحديد سلوك التكرار اللانهائي لاحقًا (#383).

المدخلات

التصنيف الاسم النوع القيود
(I1) operand عدد متغيّر من القيم (C1-C3)
(I2) cond دالة (C1)
(I3) body دالة (C2)

النواتج

الاسم النوع القيود
results عدد متغيّر من القيم (C3)

القيود

  • (C1) cond من النوع (T0, ..., TN-1) -> tensor<i1>، حيث Ti = type(operand[i]).
  • (C2) body من النوع (T0, ..., TN-1) -> (T0, ..., TN-1)، حيث Ti = type(operand[i]).
  • (C3) type(results...) = type(operand...)

أمثلة

// %init_i: 1
// %init_sum: 0
// %one: 1
// %ten: 10
%results0, %results1 = "stablehlo.while"(%init_i, %init_sum) ({
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %cond = "stablehlo.compare"(%arg0, %ten) {
      comparison_direction = #stablehlo<comparison_direction LT>
    } : (tensor<i64>, tensor<i64>) -> tensor<i1>
    stablehlo.return %cond : tensor<i1>
  }, {
  ^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
    %new_sum = stablehlo.add %arg1, %one : tensor<i64>
    %new_i = stablehlo.add %arg0, %one : tensor<i64>
    stablehlo.return %new_i, %new_sum : tensor<i64>, tensor<i64>
}) : (tensor<i64>, tensor<i64>) -> (tensor<i64>, tensor<i64>)
// %results0: 10
// %results1: 10

 مزيد من الأمثلة

xor

الدلالات

تُجري هذه العملية XOR على مستوى العناصر في موترَين lhs وrhs وتنتج موترًا result. استنادًا إلى نوع العنصر، نفِّذ ما يلي:

  • بالنسبة إلى القيم المنطقية: XOR المنطقية
  • بالنسبة إلى الأعداد الصحيحة: استخدام XOR على مستوى البت.

المدخلات

التصنيف الاسم النوع القيود
(I1) lhs موتر من النوع المنطقي أو العدد الصحيح (C1)
(I2) rhs موتر من النوع المنطقي أو العدد الصحيح (C1)

النواتج

الاسم النوع القيود
result موتر من النوع المنطقي أو العدد الصحيح (C1)

القيود

  • (C1) type(lhs) = type(rhs) = type(result)

أمثلة

// Bitwise operation with with integer tensors
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.xor"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[4, 4], [4, 12]]

// Logical operation with with boolean tensors
// %lhs: [[false, false], [true, true]]
// %rhs: [[false, true], [false, true]]
%result = "stablehlo.xor"(%lhs, %rhs) : (tensor<2x2xi1>, tensor<2x2xi1>) -> tensor<2x2xi1>
// %result: [[false, true], [true, false]]

 مزيد من الأمثلة

إمكانية التشغيل التفاعلي للهجات

في الوقت الحالي، تحتوي برامج StableHLO المتداولة أحيانًا على عمليات غير محدّدة في StableHLO.

الوحدة والدالة والاستدعاء والإرجاع

تستخدم StableHLO عمليات MLIR المصدرية لكل من ModuleOp وFuncOp وCallOp وReturnOp. تم إجراء ذلك لتحسين قابلية التشغيل التفاعلي مع آليات MLIR الحالية، لأنّ العديد من عمليات النقل المفيدة مكتوبة لاستهداف FuncOp وModuleOp، وتتوقّع العديد من مسارات التجميع توفّر هاتين العمليتين. يتم تطبيق ضمانات التوافق الكامل على هذه العمليات. إذا حدث أي تغيير على هذه العمليات بطريقة غير متوافقة (أي تمت إزالتها)، ستتم إضافة مكافئات StableHLO للحفاظ على التوافق.

CHLO

تحتوي مجموعة عمليات CHLO على عمليات ذات مستوى أعلى يتم تقسيمها إلى StableHLO. لا تتوفّر حاليًا أي ضمانات توافق مع CHLO. لضمان التوافق، يجب استخدام chlo-legalize-to-stablehlo pass قبل التسلسل.

عمليات الأشكال

من الحالات الشائعة الاستخدام في المنتدى استخدام عمليات معيّنة من لهجات MLIR الأساسية في برامج StableHLO الديناميكية لإجراء عمليات حسابية على الأشكال. تشمل هذه الأنواع عادةً عمليات shape اللهجة مثل shape_of أو num_elements، وعمليات tensor اللهجة مثل dim أو from_elements، والنوع index المضمّن.

تُصنّف مواصفات RFC الخاصة بالديناميكية > O2 هذه الأنواع على أنّها خارج النطاق، ولكن تم تضمين بعض الدعم لأنواع index لأغراض التشغيل التفاعلي. لا تتوفّر أي ضمانات بشأن التوافق مع هذه العمليات أو الأنواع. يمكن استخدام عملية النقل shape-legalize-to-stablehlo لتحويل هذه العمليات إلى عمليات StableHLO متوافقة بالكامل.

العمليات المتوقّفة نهائيًا

هناك العديد من عمليات StableHLO التي تم نقلها من MHLO، وهي متوقّفة نهائيًا وسيتم إزالتها من StableHLO. يمكنك الاطّلاع على التفاصيل الكاملة حول عمليات الإزالة هذه في StableHLO v1.0 Cleanup #2283. يمكنك الاطّلاع على المشكلة في أداة التتبُّع بشأن عمليات الإيقاف النهائي هذه على الرابط #2340.

تندرج هذه العمليات ضمن بضع فئات:

  • فئة "غير متوفرة في HLO" من عمليات StableHLO، والتي كانت في البداية جزءًا من مجموعة عمليات StableHLO ولكن تبيّن لاحقًا أنّها لا تتناسب معها جيدًا: broadcast وcreate_token وcross-replica-sum وdot وeinsum وtorch_index_select وunary_einsum (#3).
  • العمليات غير المستخدَمة: قد تكون هذه العمليات مفيدة في مرحلة ما، ولكن إما أنّها لم تتطوّر بشكل كافٍ، أو تمّت إعادة تصميم خطوط الأنابيب التي تستخدم هذه العمليات لتصبح غير مطلوبة. ويشمل ذلك map وtuple (#598) وget_tuple_element وrng وcomplex مقارنات #560 وwindow_reversal التفاف (#1181).

يمكن إزالة بعض هذه العمليات بسهولة لأنّه يمكن التعبير عنها باستخدام العمليات الحالية (broadcast وcreate_token وcross-replica-sum وdot وunary_einsum)، وسيتم إزالتها بعد انتهاء فترة التوافق الحالية (6 أشهر). لا يزال العمل جاريًا على إزالة بعضها (einsum وget_tuple_element وmap وrng وtorch_index_select وtuple وcomplex والمقارنات وwindow_reversal). وبانتظار ملاحظات المنتدى، سيتم إما إزالة هذه العمليات أو إضافتها إلى المواصفات مع توفير الدعم الكامل لها. إلى أن تصبح هذه العمليات المستقبلية معروفة، لا يمكن ضمان التوافق إلا لمدة 6 أشهر.

التنفيذ

التنفيذ التسلسلي

يتم تنفيذ برنامج StableHLO من خلال تقديم قيم إدخال إلى الدالة main واحتساب قيم الإخراج. يتم احتساب قيم الإخراج للدالة من خلال تنفيذ الرسم البياني للعمليات التي تبدأ من عملية return المقابلة.

ويتم تحديد ترتيب التنفيذ حسب التنفيذ طالما أنّه يتوافق مع تدفق البيانات، أي إذا تم تنفيذ العمليات قبل استخدامها. في StableHLO، تستهلك جميع العمليات التي لها آثار جانبية رمزًا مميزًا واحدًا وتنتج رمزًا مميزًا واحدًا (يمكن دمج رموز مميزة متعددة في رمز مميز واحد باستخدام after_all)، لذا يتوافق ترتيب تنفيذ الآثار الجانبية أيضًا مع تدفق البيانات. على سبيل المثال، في البرنامج أدناه، هناك ترتيبان محتملان للتنفيذ: %0%1%2return و%1%0%2return.

func.func @main() -> tensor<f64> {
  %0 = stablehlo.constant dense<1.0> : tensor<f64>
  %1 = stablehlo.constant dense<2.0> : tensor<f64>
  %2 = stablehlo.add %0, %1 : tensor<f64>
  return %2 : tensor<f64>
}

بشكل أكثر رسمية، عملية StableHLO هي مزيج من: 1) برنامج StableHLO، و2) حالات العمليات (لم يتم تنفيذها بعد، تم تنفيذها)، و3) القيم الوسيطة التي تعمل عليها العملية. تبدأ العملية بقيم إدخال إلى الدالة main، وتتواصل عبر الرسم البياني للعمليات الذي يعدّل حالات العمليات والقيم الوسيطة، وتنتهي بقيم الإخراج. سيتم تحديد المزيد من التفاصيل لاحقًا (#484).

التنفيذ المتوازي

يمكن تنفيذ برامج StableHLO بالتوازي، وتنظيمها في شبكة عمليات ثنائية الأبعاد بحجم num_replicas × num_partitions، وكلاهما من النوع ui32.

في شبكة عمليات StableHLO، يتم تنفيذ num_replicas * num_partitions من عمليات StableHLO في الوقت نفسه. لكل عملية process_id = (replica_id, partition_id) فريد، حيث replica_id في replica_ids = range(num_replicas) وpartition_id في partition_ids = range(num_partitions) وكلاهما من النوع ui32.

يتم تحديد حجم شبكة العمليات بشكل ثابت لكل برنامج (في المستقبل، نخطّط لجعلها جزءًا صريحًا من برامج StableHLO #650)، ويتم تحديد الموضع داخل شبكة العمليات بشكل ثابت لكل عملية. يمكن لكل عملية الوصول إلى موضعها ضمن شبكة العمليات من خلال العمليتَين replica_id وpartition_id.

ضمن شبكة العمليات، يمكن أن تكون جميع البرامج متشابهة (في نمط "برنامج واحد، بيانات متعددة")، أو مختلفة (في نمط "برامج متعددة، بيانات متعددة")، أو مزيجًا من الاثنين. في المستقبل، نخطّط لإتاحة استخدام طرق أخرى لتحديد برامج StableHLO المتوازية، بما في ذلك GSPMD (#619).

في شبكة العمليات، تكون العمليات مستقلة عن بعضها البعض في الغالب، حيث يكون لكل عملية حالات تشغيل وقيم إدخال/وسيطة/إخراج منفصلة، ويتم تنفيذ معظم العمليات بشكل منفصل بين العمليات، باستثناء عدد صغير من العمليات الجماعية الموضّحة أدناه.

بما أنّ تنفيذ معظم العمليات يستخدم قيمًا من العملية نفسها فقط، يكون من الواضح عادةً الإشارة إلى هذه القيم بأسمائها. ومع ذلك، عند وصف دلالات العمليات الجماعية، يكون ذلك غير كافٍ، ويؤدي إلى ظهور الرمز name@process_id للإشارة إلى القيمة name ضمن عملية معيّنة. (من هذا المنظور، يمكن اعتبار name غير المؤهَّلة اختصارًا لـ name@(replica_id(), partition_id())).

ويتم تحديد ترتيب التنفيذ بين العمليات حسب التنفيذ، باستثناء المزامنة التي يتم إجراؤها من خلال الاتصال من نقطة إلى نقطة والعمليات الجماعية كما هو موضح أدناه.

الاتصال من نقطة إلى نقطة

يمكن لعمليات StableHLO التواصل مع بعضها البعض من خلال قنوات StableHLO. يتم تمثيل القناة بمعرّف موجب من النوع si64. من خلال عمليات مختلفة، يمكن إرسال قيم إلى القنوات وتلقّيها منها.

سيتم تحديد المزيد من التفاصيل الرسمية، مثل مصدر أرقام تعريف القنوات هذه وكيفية معرفة البرامج التي تعالجها بها ونوع المزامنة التي توفّرها، في وقت لاحق (#484).

التواصل من خلال البث المباشر

يمكن لكل عملية StableHLO الوصول إلى واجهتَي بث:

  • خلاصة يمكن قراءتها
  • خارج التغذية التي يمكن الكتابة إليها

على عكس القنوات التي تُستخدم للتواصل بين العمليات وبالتالي تتضمّن عمليات في كلا طرفيها، فإنّ تنفيذ الطرف الآخر من الخلاصات الواردة والخارجة يكون محدّدًا حسب التنفيذ.

سيتم تحديد المزيد من التفاصيل الرسمية، مثل كيفية تأثير التواصل المباشر في ترتيب التنفيذ ونوع المزامنة التي يتم تقديمها من خلاله (#484).

العمليات الجماعية

هناك ست عمليات جماعية في StableHLO: all_gather وall_reduce وall_to_all وcollective_broadcast وcollective_permute وreduce_scatter. تعمل كل هذه العمليات على تقسيم العمليات في شبكة عمليات StableHLO إلى مجموعات عمليات StableHLO وتنفيذ عملية حسابية مشتركة داخل كل مجموعة عمليات بشكل مستقل عن مجموعات العمليات الأخرى.

ضمن كل مجموعة عمليات، قد تقدّم العمليات الجماعية حاجزًا للمزامنة. سيتم تحديد المزيد من التفاصيل الرسمية، مثل توضيح وقت حدوث هذه المزامنة بالضبط وكيفية وصول العمليات إلى هذا الحاجز وما يحدث إذا لم تصل إليه (#484).

إذا كانت مجموعة العمليات تتضمّن اتصالاً بين الأقسام، أي أنّ هناك عمليات في مجموعة العمليات تختلف أرقام تعريف الأقسام فيها، فإنّ تنفيذ العملية الجماعية يتطلّب قناة، ويجب أن توفّر العملية الجماعية قيمة موجبة channel_id من النوع si64. لا تحتاج عمليات التواصل بين النسخ المتماثلة إلى قنوات.

تختلف العمليات الحسابية التي تجريها العمليات الجماعية باختلاف العمليات الفردية، ويتم توضيحها في أقسام العمليات الفردية أعلاه. ومع ذلك، فإنّ الاستراتيجيات التي يتم بموجبها تقسيم شبكة العمليات إلى مجموعات عمليات تكون مشتركة بين هذه العمليات، ويتم وصفها في هذا القسم. بشكل أكثر رسمية، تتوافق StableHLO مع الاستراتيجيات الأربع التالية.

cross_replica

تحدث عمليات التواصل بين النسخ المتماثلة فقط داخل كل مجموعة عمليات. تأخذ هذه الاستراتيجية replica_groups، وهي قائمة بقوائم معرّفات النسخ المتماثلة، وتحسب حاصل الضرب الديكارتي لـ replica_groups في partition_ids. يجب أن يحتوي replica_groups على عناصر فريدة وأن يغطي جميع replica_ids. بشكل أكثر رسمية، باستخدام بنية Python:

def cross_replica(replica_groups: List[List[ReplicaId]]) -> List[List[ProcessId]]:
  for replica_group in replica_groups:
    for partition_id in partition_ids:
      process_group = []
      for replica_id in replica_group:
        process_group.append((replica_id, partition_id))
      yield process_group

على سبيل المثال، بالنسبة إلى replica_groups = [[0, 1], [2, 3]] وnum_partitions = 2، ستؤدي cross_replica إلى ظهور [[(0, 0), (1, 0)], [(0, 1), (1, 1)], [(2, 0), (3, 0)], [(2, 1), (3, 1)]].

cross_partition

تحدث عمليات التواصل بين الأقسام فقط داخل كل مجموعة عمليات. تأخذ هذه الاستراتيجية partition_groups، وهي قائمة بقوائم معرّفات الأقسام، وتحسب حاصل الضرب الديكارتي لـ partition_groups في replica_ids. يجب أن يحتوي partition_groups على عناصر فريدة وأن يغطي جميع partition_ids. بشكل أكثر رسمية، باستخدام بنية Python:

def cross_partition(partition_groups: List[List[PartitionId]]) -> List[List[ProcessId]]:
  for partition_group in partition_groups:
    for replica_id in replica_ids:
      process_group = []
      for partition_id in partition_group:
        process_group.append((replica_id, partition_id))
      yield process_group

على سبيل المثال، بالنسبة إلى partition_groups = [[0, 1]] وnum_replicas = 4، ستؤدي cross_partition إلى ظهور [[(0, 0), (0, 1)], [(1, 0), (1, 1)], [(2, 0), (2, 1)], [(3, 0), (3, 1)]].

cross_replica_and_partition

قد يحدث التواصل بين النسخ المتماثلة والمقسّمة ضمن كل مجموعة عمليات. تأخذ هذه الاستراتيجية replica_groups، وهي قائمة بقوائم معرّفات النسخ المتماثلة، وتحسب نواتج ديكارتية لكل replica_group حسب partition_ids. يجب أن يحتوي replica_groups على عناصر فريدة وأن يغطي جميع replica_ids. بشكل أكثر رسمية، باستخدام بنية Python:

def cross_replica_and_partition(replica_groups: List[List[ReplicaId]]) -> List[List[ProcessId]]:
  for replica_group in replica_groups:
    process_group = []
    for partition_id in partition_ids:
      for replica_id in replica_group:
        process_group.append((replica_id, partition_id))
    yield process_group

على سبيل المثال، بالنسبة إلى replica_groups = [[0, 1], [2, 3]] وnum_partitions = 2، ستؤدي cross_replica_and_partition إلى ظهور [[(0, 0), (1, 0), (0, 1), (1, 1)], [(2, 0), (3, 0), (2, 1), (3, 1)]].

flattened_ids

تأخذ هذه الاستراتيجية flattened_id_groups، وهي قائمة بقوائم تتضمّن معرّفات العمليات "المسطّحة" بالتنسيق replica_id * num_partitions + partition_id، وتحوّلها إلى معرّفات عمليات. يجب أن يحتوي flattened_id_groups على عناصر فريدة ويشمل جميع process_ids. بشكل أكثر رسمية، باستخدام بنية Python:

def flattened_ids(flattened_id_groups: List[List[ui32]]) -> List[List[ProcessId]]:
  for flattened_id_group in flattened_id_groups:
    process_group = []
    for flattened_id in flattened_id_group:
      replica_id = flattened_id // num_partitions
      partition_id = flattened_id % num_partitions
      process_group.append((replica_id, partition_id))
    yield process_group

على سبيل المثال، بالنسبة إلى flattened_id_groups = [[0, 1, 2, 3], [4, 5, 6, 7]] وnum_replicas = 4 وnum_partitions = 2، ستنتج flattened_ids [[(0, 0), (0, 1), (1, 0), (1, 1)], [(2, 0), (2, 1), (3, 0), (3, 1)]].

الدقة

في الوقت الحالي، لا يقدّم StableHLO ضمانات بشأن الدقة العددية، ولكن قد يتغيّر ذلك في المستقبل (#1156).

دلالات التنفيذ للعملية الكمية

قد يختلف تفسير عمليات StableHLO المكمَّمة حسب متطلبات الأجهزة وإمكاناتها. على سبيل المثال، قد تختار بعض الأجهزة تفسير العمليات الكمية باستخدام استراتيجية "إزالة التكميم، وتنفيذ عملية الفاصلة العائمة، ثم التكميم أخيرًا". وقد تجري بعضها الحسابات بأكملها باستخدام العمليات الحسابية للأعداد الصحيحة. نتيجةً لذلك، يتم تحديد تفسير عمليات StableHLO المكمَّمة حصريًا من خلال التنفيذ المحدّد. يجب أن يستند تفسير التكميم المختلط (#1575) إلى دلالاته كما هو منصوص عليه في المواصفات (عبر 1792).

الأخطاء

يتم التحقّق من صحة برامج StableHLO من خلال مجموعة شاملة من القيود المفروضة على العمليات الفردية، ما يمنع حدوث العديد من أنواع الأخطاء قبل وقت التشغيل. ومع ذلك، لا يزال من الممكن حدوث أخطاء، مثل تجاوز سعة الأعداد الصحيحة، أو الوصول إلى خارج الحدود، وما إلى ذلك. وما لم يتم توضيح ذلك صراحةً، تؤدي كل هذه الأخطاء إلى سلوك محدّد التنفيذ، ولكن قد يتغير ذلك في المستقبل (#1157).

استثناءات النقطة العائمة

واستثناءً من هذه القاعدة، فإنّ أخطاء الفاصلة العائمة في برامج StableHLO لها سلوك محدّد جيدًا. تؤدي العمليات التي ينتج عنها استثناءات محدّدة بموجب معيار IEEE-754 (استثناءات غير صالحة أو قسمة على صفر أو تجاوز الحد الأقصى أو الحد الأدنى أو غير دقيقة) إلى نتائج تلقائية (كما هو محدّد في المعيار) وتستمر في التنفيذ بدون عرض علامة الحالة المقابلة، على غرار معالجة الاستثناءات raiseNoFlag من المعيار. وتكون الاستثناءات الخاصة بالعمليات غير القياسية (مثل العمليات الحسابية المعقّدة وبعض الدوال المتسامية) محددة حسب التنفيذ.

عدم تطابق الأشكال

تتيح StableHLO استخدام موترات ذات أشكال ديناميكية. ومع ذلك، يجب أن تتطابق الأشكال في وقت التشغيل، وإلا سيكون السلوك غير محدّد. لا توفّر StableHLO بشكل صريح عملية يمكنها التأكّد من أنّ موترًا معيّنًا يتضمّن شكلاً معيّنًا في وقت التشغيل. يتحمّل المنتج مسؤولية إنشاء رمز برمجي صحيح.

كمثال محدّد، البرنامج أدناه صالح. ومع ذلك، يجب أن تكون أشكال %arg0 و%arg1 متطابقة تمامًا في وقت التشغيل، وإلا سيكون سلوك البرنامج غير محدّد:

func.func @foo(%arg0: tensor<?xi32>, %arg1: tensor<?xi32>) -> tensor<?xi32> {
    %0 = stablehlo.add %arg0, %arg1 : tensor<?xi32>
    return %0 : tensor<?xi32>
}

الترميز

لوصف بنية الجملة، يستخدم هذا المستند نسخة معدّلة من معيار ISO لبنية EBNF (ISO/IEC 14977:1996، Wikipedia)، مع تعديلَين: 1) يتم تحديد القواعد باستخدام ::= بدلاً من =،

‫2) يتم التعبير عن التسلسل باستخدام التجاوز بدلاً من ,.

لوصف الدلالات (أي ضمن أقسام "الأنواع" و"الثوابت" و "العمليات")، نستخدم صيغًا تستند إلى بنية Python مع إمكانية التعبير بإيجاز عن عمليات الصفائف كما هو موضّح أدناه. هذه الطريقة فعّالة مع مقتطفات الرموز البرمجية الصغيرة، ولكن في حالات نادرة عندما تكون هناك حاجة إلى مقتطفات أكبر من الرموز البرمجية، نستخدم بنية Python العادية التي يتم تقديمها دائمًا بشكل صريح.

الصيغ

لنستكشف طريقة عمل الصيغ استنادًا إلى مثال من dot_generalالمواصفات. يبدو أحد قيود هذه العملية على النحو التالي: dim(lhs, lhs_batching_dimensions...) = dim(rhs, rhs_batching_dimensions...).

تأتي الأسماء المستخدَمة في هذه الصيغة من مصدرَين: 1) الدوال العامة، أي dim، 2) تعريفات العناصر التابعة لعنصر البرنامج المعني، أي مدخلات lhs وlhs_batching_dimensions وrhs وrhs_batching_dimensions المحدّدة في قسم "المدخلات" من dot_general.

كما ذكرنا أعلاه، يستند بناء جملة هذه الصيغة إلى Python مع بعض الإضافات الموجزة. لفهم الصيغة، لنحوّلها إلى بنية Python عادية.

أ) في هذه الصيغ، نستخدم = لتمثيل المساواة، لذا فإن الخطوة الأولى نحو الحصول على بنية Python هي استبدال = بـ ==، كما يلي: dim(lhs, lhs_batching_dimensions...) == dim(rhs, rhs_batching_dimensions...).

ب) بالإضافة إلى ذلك، تدعم هذه الصيغ علامات الحذف (...) التي تحوّل التعبيرات العددية إلى تعبيرات موترية. باختصار، يعني f(xs...) تقريبًا "لكل قيمة عددية x في الموتر xs، احسب قيمة عددية f(x) ثم أرجِع كل هذه النتائج العددية معًا كموتر". في بنية Python العادية، تتحوّل صيغة المثال إلى: [dim(lhs, dim1) for dim1 in lhs_batching_dimensions] == [dim(rhs, dim2) for dim2 in rhs_batching_dimensions].

وبفضل علامات الحذف، يمكن غالبًا تجنُّب العمل على مستوى القيم العددية الفردية. ومع ذلك، في بعض الحالات المعقّدة، يمكن استخدام صيغة شبه رسمية من المستوى الأدنى، كما هو الحال في صيغة start_indices[bi0, ..., :, ..., biN] من مواصفات gather. بهدف الاختصار، لن نقدّم صيغة رسمية دقيقة لترجمة هذه البنية إلى لغة Python العادية، ونأمل أن تظل مفهومة بشكل بديهي لكل حالة على حدة. يُرجى إعلامنا إذا بدت بعض الصيغ غير واضحة، وسنحاول تحسينها.

ستلاحظ أيضًا أنّ الصيغ تستخدم علامات الحذف لتوسيع جميع أنواع القوائم، بما في ذلك الموترات وقوائم الموترات (التي يمكن أن تنشأ مثلاً من عدد متغير من الموترات)، وما إلى ذلك. هذه منطقة أخرى لا نقدّم فيها شكلاً رسميًا دقيقًا (على سبيل المثال، لا تشكّل القوائم حتى جزءًا من نظام أنواع StableHLO)، بل نعتمد بدلاً من ذلك على إمكانية الفهم البديهي.

ج) آخر أداة تدوين جديرة بالذكر نستخدمها هي البث الضمني. على الرغم من أنّ مجموعة عمليات StableHLO لا تتيح البث الضمني، فإنّ الصيغ تتيحه، وذلك أيضًا بهدف الاختصار. باختصار، إذا تم استخدام عدد قياسي في سياق يُتوقّع فيه موتر، يتم بث العدد القياسي إلى الشكل المتوقّع.

لمواصلة مثال dot_general، إليك قيدًا آخر: 0 <= lhs_batching_dimensions < rank(lhs). وفقًا لما هو محدّد في مواصفات dot_general، فإنّ lhs_batching_dimensions هو موتر، في حين أنّ 0 وrank(lhs) هما كميتان قياسيتان. بعد تطبيق البث الضمني، ستصبح الصيغة [0, ..., 0] <= lhs_batching_dimensions < [rank(lhs), ..., rank(lhs)].

عند تطبيق هذه الصيغة على عملية dot_general معيّنة، سيتم تقييمها إلى موتر من القيم المنطقية. عند استخدام الصيغ كقيود، يكون القيد صالحًا إذا تم تقييم الصيغة على أنّها true أو على أنّها موتر يحتوي على عناصر true فقط.

الأسماء

في الصيغ، يشمل النطاق المعجمي ما يلي: 1) الدوال العامة، 2) تعريفات الأعضاء،

‫3) التعريفات المحلية في ما يلي قائمة بالدوال العامة. تعتمد قائمة تعريفات العناصر على عنصر البرنامج الذي يتم تطبيق الترميز عليه:

  • بالنسبة إلى العمليات، تتضمّن تعريفات الأعضاء الأسماء الواردة في قسمَي "المدخلات" و"المخرجات".
  • بالنسبة إلى كل ما عدا ذلك، تتضمّن تعريفات الأعضاء الأجزاء البنيوية لعنصر البرنامج، والتي تحمل أسماء العناصر غير النهائية المقابلة في EBNF. في معظم الحالات، يتم الحصول على أسماء هذه الأجزاء البنيوية من خلال تحويل أسماء الرموز غير النهائية إلى تنسيق snake case (على سبيل المثال، IntegerLiteral => integer_literal)، ولكن في بعض الأحيان يتم اختصار الأسماء في هذه العملية (على سبيل المثال، QuantizationStorageType => storage_type)، وفي هذه الحالة يتم تقديم الأسماء بشكل صريح على غرار قسمَي "المدخلات" و"المخرجات" في مواصفات العمليات.
  • بالإضافة إلى ذلك، تتضمّن تعريفات الأعضاء دائمًا self للإشارة إلى عنصر البرنامج ذي الصلة.

القيم

عند تقييم الصيغ، تعمل مع أنواع القيم التالية: 1) Value (القيم الفعلية، مثل dense<[[1, 2], [3, 4]]> : tensor<2x2xi32>؛ تعرف هذه القيم دائمًا أنواعها)، 2) Placeholder (القيم المستقبلية، مثل lhs أو rhs أو result؛ لا يُعرف قيمها الفعلية بعد، ولكن يُعرف أنواعها)، 3) Type (الأنواع كما هو محدّد في قسم "الأنواع")، 4) Function (الدوال العامة كما هو محدّد في قسم "الدوال").

استنادًا إلى السياق، قد تشير الأسماء إلى قيم مختلفة. وبشكل أكثر تحديدًا، يحدّد قسم "الدلالات" للعمليات (وما يعادلها لعناصر البرنامج الأخرى) منطق وقت التشغيل، وبالتالي تتوفّر جميع المدخلات كـ Value. في المقابل، يحدّد قسم "القيود" للعمليات (وما شابهها) منطق "وقت الترجمة البرمجية"، أي شيء يتم تنفيذه عادةً قبل وقت التشغيل، وبالتالي لا تتوفّر سوى المدخلات الثابتة كـ Value، ولا تتوفّر المدخلات الأخرى إلا كـ Placeholder.

الأسماء في "الدلالات" في "القيود"
الدوال العامة Function Function
المدخلات الثابتة Value Value
المدخلات غير الثابتة Value Placeholder
النواتج Value Placeholder
التعريفات المحلية حسب التعريف حسب التعريف

لنفكّر في مثال على عملية transpose:

%result = "stablehlo.transpose"(%operand) {
  permutation = dense<[2, 1, 0]> : tensor<3xi64>
} : (tensor<2x3x2xi32>) -> tensor<2x3x2xi32>

بالنسبة إلى هذه العملية، permutation هو ثابت، لذا يتوفّر كـ Value في كل من الدلالات والقيود. في المقابل، يتوفّر operand وresult كـ Value في الدلالات، ولكن كـ Placeholder فقط في القيود.

الدوال

إنشاء أنواع

لا تتوفّر أي دوال يمكن استخدامها لإنشاء أنواع. بدلاً من ذلك، نستخدم بنية الأنواع مباشرةً لأنّها عادةً ما تكون أكثر اختصارًا. على سبيل المثال، استخدِم (tensor<E>, tensor<E>) -> (tensor<E>) بدلاً من function_type( [tensor_type([], E), tensor_type([], E)], [tensor_type([], E)]).

الدوال على الأنواع

  • يتم تحديد element_type لأنواع الموتر وأنواع الموتر الكمي، ويعرض على التوالي الجزء TensorElementType أو QuantizedTensorElementType من TensorType أو QuantizedTensorType المقابل.
def element_type(x: Value | Placeholder | Type):
 if type(x) == TensorType:
    return tensor_element_type(x)
  if type(x) == QuantizedTensorType:
    return quantized_tensor_element_type(x)
  if type(x) is not Type:
    return element_type(type(x))
  • is_per_axis_quantized(x: Value | Placeholder | Type) -> Value هو اختصار لـ is_quantized(x) and quantization_dimension(x) is not None.

  • is_per_tensor_quantized(x: Value | Placeholder | Type) -> Value هو اختصار لـ is_quantized(x) and quantization_dimension(x) is None.

  • تتحقّق is_promotable(x: Type, y: Type) -> bool مما إذا كان يمكن ترقية النوع x إلى النوع y. عندما تكون x و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. على سبيل المثال، تتوفّر كل من صيغ الاشتراك والتقسيم من Python لفهرسة الموترات والموترات الكمية والصفوف.

  • يتم تحديد to_destination_type(x: Value, destination_type: Type) -> Value على الموترات ويعرض القيمة المحوّلة لـ x استنادًا إلى type(x) وdestination_type على النحو التالي:

def to_destination_type(x: Value, destination_type: Type) -> Value:
  if type(x) == destination_type:
    return x

  if is_quantized(destination_type):
    if is_quantized(type(x)):
      return quantize(x, destination_type)
    assert is_float(type(x))
    return quantize(x, destination_type)

  if is_quantized(type(x)):
    assert destination_type = expressed_type(type(x))
    return dequantize(type(x))

  return convert(x, destination_type)

تجري حاليًا مناقشات أولية حول دمج عمليات convert وuniform_quantize وuniform_dequantize (#1576). بعد الدمج، لن نحتاج إلى الدالة أعلاه، ويمكننا استخدام اسم العملية بدلاً من convert.

  • يتم تحديد is_nan(x: Value) -> Value على الموترات ويعرض true إذا كانت جميع عناصر x هي NaN أو false في الحالات الأخرى. إذا لم يكن x موترًا، تعرض الدالة None.

  • يتم تحديد is_sorted(x: Value) -> Value على الموترات ويعرض true إذا تم ترتيب عناصر x بترتيب تصاعدي بالنسبة إلى الترتيب المعجمي التصاعدي لفهارسها أو false بخلاف ذلك. إذا لم يكن x موترًا، تعرض الدالة None.

  • يتم تحديد is_unique(x: Value) -> Value على الموترات وتعرض true إذا لم تتضمّن x عناصر مكرّرة، أو false في الحالات الأخرى. إذا لم يكن x موترًا، تعرض الدالة None.

  • يتم تحديد member_name(x: Value) -> Any لجميع تعريفات الأعضاء member_name لجميع القيم. على سبيل المثال، يعرض الرمز real_part(x) الجزء RealPart من الرمز ComplexConstant المقابل. إذا لم تكن x قيمة تتضمّن عنصرًا مناسبًا، تعرض الدالة None.

  • يتم تحديد same(x: Value) -> Value على الموترات، ويتم عرض true إذا كانت جميع عناصر x متساوية أو false في الحالات الأخرى. إذا لم يكن للموتر عناصر، سيتم احتساب ذلك على أنّه "كل العناصر متساوية"، أي أنّ الدالة ستعرض true. إذا لم يكن x موترًا، تعرض الدالة None.

  • يتم تحديد split(x: Value, num_results: Value, axis: Value) -> Value على الموترات، وتعرض num_results شرائح من x على طول المحور axis. إذا لم يكن x موترًا أو dim(x, axis) % num_results != 0، تعرض الدالة None.

  • يتم تحديد is_defined_in_parent_scope(x: Value) -> Value على السلاسل ويعرض true إذا كان x هو اسم دالة محددة في النطاق نفسه مثل الدالة الرئيسية للعملية ذات الصلة.

  • يتم تحديد is_namespaced_op_name(x: Value) -> Value على السلاسل ويعرض true إذا كان x اسم عملية صالحًا، أي إذا كان يلتزم بالتعبير العادي التالي: [a-zA-Z][a-zA-Z0-9_]*([.][a-zA-Z0-9_$]+)+

عمليات حسابية على الأشكال

  • axes(x: Value | Placeholder | Type) -> Value هو اختصار range(rank(x)).

  • dim(x: Value | Placeholder | Type, axis: Value) -> Value هو اختصار shape(x)[axis].

  • dims(x: Value | Placeholder | Type, axes: List) -> List هو اختصار list(map(lambda axis: dim(x, axis), axes)).

  • يتم تحديد index_space(x: Value | Placeholder | Type) -> Value على الموترات، ويعرض فهارس size(x) للقيم TensorType المقابلة مرتّبة بترتيب معجمي تصاعدي، أي [0, ..., 0] و[0, ..., 1] و... وshape(x) - 1. إذا لم يكن x نوع موتر أو نوع موتر كمي أو قيمة أو عنصر نائب من أحد هذه الأنواع، يتم عرض None.

  • rank(x: Value | Placeholder | Type) -> Value هو اختصار size(shape(x)).

  • يتم تحديد shape(x: Value | Placeholder | Type) -> Value في قسم "الدوال على الأنواع" من خلال member_name.

  • size(x: Value | Placeholder | Type) -> Value هو اختصار reduce(lambda x, y: x * y, shape(x)).

عمليات حسابية حول التكميم

  • def baseline_element_type(x: Value | Placeholder | Type) -> Type هو اختصار لـ element_type(baseline_type(x)).

  • يتم تحديد baseline_type لأنواع الموتر وأنواع الموتر الكمّي ويتم تحويلها إلى "خط أساس"، أي نوع له الشكل نفسه ولكن مع إعادة ضبط مَعلمات التكميم لنوع العنصر إلى القيم التلقائية. يُستخدَم هذا كحيلة مفيدة لمقارنة نوعَي الموتر والموتر الكمّي بشكل موحّد، وهو أمر مطلوب في كثير من الأحيان. بالنسبة إلى الأنواع المكمَّمة، يتيح ذلك مقارنة الأنواع مع تجاهل مَعلمات التكميم، أي يجب أن تتطابق جميع القيم shape وstorage_type وexpressed_type وstorage_min وstorage_max وquantization_dimension (بالنسبة إلى النوع المكمَّم لكل محور)، ولكن قد تختلف القيمتان scales وzero points.

def baseline_type(x: Value | Placeholder | Type) -> Type:
  if type(x) == TensorType:
    return x
  if type(x) == QuantizedTensorType:
    element_type = quantized_tensor_element_type(x)
    baseline_element_type = QuantizedTensorElementType(
      storage_type = storage_type(element_type),
      storage_min = storage_min(element_type),
      storage_max = storage_max(element_type),
      expressed_type = expressed_type(element_type),
      quantization_dimension = quantization_dimension(element_type),
      scales = [constant(1.0, expressed_type(element_type))] * dim(x, quantization_dimension(element_type)),
      zero_points = [constant(0, storage_type(element_type))] * dim(x, quantization_dimension(element_type)))
    return QuantizedTensorType(shape(x), baseline_element_type)
  if type(x) is not Type:
    return baseline_element_type(type(x))
  • يتم تحديد dequantize لأنواع الموتر الكمي ويحوّلها إلى أنواع موترات ذات أعداد عشرية. ويتم ذلك من خلال تحويل العناصر المحددة الكمية التي تمثّل قيمًا صحيحة لنوع التخزين إلى قيم نقطة عائمة مقابلة للنوع المعروض باستخدام نقطة الصفر والمقياس المرتبطين بنوع العنصر المحدد الكمية.
def compute_zero_points(quantized_type, result_type):
  if is_per_tensor_quantized(quantized_type):
    return broadcast_in_dim(constant(zero_point(quantized_type), storage_type(quantized_type)), [], result_type)
  if is_per_axis_quantized(quantized_type):
    for i in index_space(result_type):
      d = quantization_dimension(quantized_type)
      zero_points[i] = zero_points(quantized_type)[i[d]]
    return zero_points

def compute_scales(quantized_type, result_type):
  if is_per_tensor_quantized(quantized_type):
    return broadcast_in_dim(constant(scale(quantized_type), expressed_type(quantized_type)), [],
            type(result_type))
  if is_per_axis_quantized(quantized_type):
    for i in index_space(result_type):
      d = quantization_dimension(quantized_type)
      scales[i] = scales(quantized_type)[i[d]]
    return scales

def dequantize(x: Value) -> Value:
  assert is_quantized(x)
  x_storage = bitcast_convert(x, storage_type(x))
  x_storage_sub = x_storage - compute_zero_points(type(x), type(x_storage))
  x_expressed_sub = convert(x_storage_sub, expressed_type(x))
  return x_expressed_sub * compute_scales(type(x), type(x_expressed_sub))
  • يتم تحديد quantize لأنواع موترات النقطة العائمة وتحويلها إلى أنواع موترات كمية. ويتم ذلك من خلال تحويل قيم النقطة العائمة من النوع المعروض إلى قيم عددية صحيحة مقابلة لنوع التخزين باستخدام نقطة الصفر والمقياس المرتبطين بنوع العنصر الكمي.
def quantize(x: Value, result_type: Type) -> Value:
  assert is_float(x) and is_quantized(result_type)
  zero_points = compute_zero_points(result_type, TensorType(shape(x), storage_type(result_type)))
  converted_zero_points = convert(zero_points, expressed_type(result_type))
  converted_min = convert(storage_min(result_type), expressed_type(result_type))
  converted_max = convert(storage_max(result_type), expressed_type(result_type))

  x_scaled = x / compute_scales(result_type, type(x))
  x_scaled_add_zp = x_scaled + converted_zero_points
  x_clamped = clamp(converted_min, x_scaled_add_zp, converted_max)
  x_rounded = round_nearest_even(x_clamped)
  return convert(x_rounded, result_type)
  • تُستخدَم dequantize_op_quantize لتحديد العمليات الحسابية على مستوى العناصر في الموترات الكمية. ويؤدي ذلك إلى إلغاء التكميم، أي تحويل العناصر المكمَّمة إلى أنواعها المعبر عنها، ثم تنفيذ عملية، ثم التكميم، أي إعادة النتائج إلى أنواع التخزين الخاصة بها. في الوقت الحالي، لا تعمل هذه الدالة إلا مع التكميم لكل موتر. التقسيم على مستوى كل محور هو عمل قيد التقدم (#1574).
def dequantize_op_quantize(op, *inputs_and_output_type):
  inputs = inputs_and_output_type[:-1]
  output_type = inputs_and_output_type[-1]

  float_inputs = map(dequantize, inputs)
  float_result = op(*float_inputs)
  return quantize(float_result, output_type)

def dequantize_batch_norm_grad_or_training_quantize(op, *inputs_and_output_types):
  inputs = inputs_and_output_type[:-3]
  float_inputs = map(dequantize, inputs)
  float_results = op(*float_inputs)
  return map(quantize, float_results, inputs_and_output_type[-3:])

def dequantize_compare(lhs, rhs, comparison_direction):
  float_lhs = dequantize(lhs)
  float_rhs = dequantize(rhs)
  return compare(float_lhs, float_rhs, comparison_direction, FLOAT)

def dequantize_select_quantize(pred, on_true, on_false, output_type):
  float_on_true = dequantize(on_true)
  float_on_false = dequantize(on_false)
  float_result = select(pred, float_on_true, float_on_false)
  return quantize(float_result, output_type)
  • يُستخدَم hybrid_dequantize_then_op لتحديد التكميم الخاص بالوزن فقط في عملية مختلطة تقبل الجانب الأيسر (lhs) بنوع النقطة العائمة والجانب الأيمن (rhs) بأنواع الكميات. تعمل هذه العملية على إلغاء تكميم المدخلات المكمَّمة وتحويلها إلى أنواعها المعبر عنها، ثم تنفّذ العمليات الحسابية باستخدام أرقام الفاصلة العائمة. يجب أن يكون نوع عنصر موتر lhs العائم والنوع المعبر عنه لموتر rhs الكمي متطابقَين.
def hybrid_dequantize_then_op(op, lhs, rhs):
  assert(is_float(lhs) and is_quantized(rhs) and element_type(lhs) == expressed_type(rhs))
  return op(lhs, dequantize(rhs))

عمليات حسابية على الشبكة

  • cross_partition(replica_groups: Value) -> Value. راجِع القسم "cross_replica" أعلاه.

  • cross_replica(replica_groups: Value) -> Value. راجِع القسم "cross_replica" أعلاه.

  • cross_replica_and_partition(replica_groups: Value) -> Value. راجِع القسم "cross_replica_and_partition" أعلاه.

  • flattened_ids(replica_groups: Value) -> Value. راجِع القسم "flattened_ids" أعلاه.

الديناميكية

يمكن أن تحتوي قيم StableHLO على أحجام سمات ديناميكية، مثل tensor<?xi64>. ومع ذلك، لا يمكن أن تتضمّن قيم StableHLO عددًا ديناميكيًا من السمات (ديناميكية غير مصنّفة، مثل tensor<*xi64>). ويُسمح للمعاملات والنتائج باستخدام أحجام السمات الديناميكية، حتى إذا كانت هناك قيود على الأحجام. سيتم التحقّق من القيود بشكل ثابت إذا أمكن ذلك، وإلا سيتم تأجيلها إلى وقت التشغيل، وستؤدي حالات عدم التطابق إلى سلوك غير محدّد. اطّلِع على الأمثلة أدناه.

عدم تطابق الأشكال في العمليات الأحادية على مستوى العناصر

إليك البرنامج التجريبي التالي:

func.func @foo(%arg0: tensor<?xf64>) {
  %0 = stablehlo.abs %arg0 : (tensor<?xf64>) -> tensor<2xf64>
  return
}

هذا النوع من البرامج غير معتاد، لأنّه ليس من الشائع معرفة شكل النتيجة بدون معرفة شكل الإدخال. ومع ذلك، هذا برنامج StableHLO صالح. لا يمكن التحقّق من صحة عملية abs بشكل ثابت في هذا البرنامج، لأنّ الشكل الدقيق للمعامل غير معروف. مع ذلك، تتوافق الأشكال بالتأكيد، ويمكن التحقّق من ذلك بشكل ثابت: قد يتبيّن أنّ ? هو 2 في وقت التشغيل، ولن تحدث أي مشكلة. ومع ذلك، يمكن أن تكون قيمة ? أي عدد صحيح آخر، وفي هذه الحالة يكون السلوك غير محدّد.

يُرجى العِلم أنّه إذا كان حجم السمة ديناميكيًا في النتيجة، لا يمكن أن يكون هناك سلوك غير محدّد. في الواقع، ليس هناك حجم "متوقّع"، لذا لا يمكن أن يكون هناك عدم تطابق.

عدم تطابق الأشكال في العمليات الثنائية على مستوى العناصر

إليك البرنامج التجريبي التالي:

func.func @foo(%arg0: tensor<?xf64>, %arg1: tensor<?xf64>) {
  %0 = stablehlo.add %arg0, %arg0 : (tensor<?xf64>, tensor<?xf64>) -> tensor<?xf64>
  return
}

عندما يتعلّق الأمر بالعمليات الثنائية على مستوى العناصر، يجب أن تتطابق أشكال المدخلات والنتيجة في وقت التشغيل. في وقت الترجمة، يجب أن تكون الأبعاد الثابتة متساوية، وإلا يجب أن تكون متوافقة فقط. إذا كانت أيّ سمة ديناميكية في المدخلات، قد يحدث سلوك غير محدّد في وقت التشغيل، لأنّ الحجم الديناميكي قد لا يتطابق مع الحجم المقابل في المعامِل الآخر (سواء كان ثابتًا أو ديناميكيًا). إذا كانت جميع المدخلات ثابتة، لن يهم ما إذا كانت النتيجة ديناميكية أم لا: سيتم التحقّق من السمات المعروفة بشكل ثابت، ولن تفرض السمات الديناميكية أي قيود.

عدم تطابق الأشكال للعمليات التي تأخذ شكل الناتج كمعامل

إليك البرنامج التجريبي التالي:

func.func @foo(%arg0: tensor<2xi32>) {
  %0 = stablehlo.dynamic_iota %arg0, dim = 0 : (tensor<2xi32>) -> tensor<3x4xi64>
  return
}

يجب أن تتطابق القيم في عامل تشغيل الشكل في وقت التشغيل مع شكل النتيجة، وإلا سيكون السلوك غير محدّد. أي أنّه يجب أن تكون قيمة %arg0 في وقت التشغيل dense<[3, 4]> : tensor<2xi32>. إذا كان عامل تشغيل الشكل ثابتًا، يمكن التحقّق من ذلك بشكل ثابت. إذا كان شكل النتيجة ديناميكيًا بالكامل، لا يمكن أن يحدث عدم تطابق.