מפרט StableHLO

StableHLO היא פעולה שהוגדרה לפעולות ברמה גבוהה (HLO) במכונה למידת מכונה. StableHLO פועלת כשכבת ניידות בין frameworks של למידת מכונה ומהדרים של למידת מכונה: frameworks של ML שמייצרות תוכנות StableHLO תואמים למהדרים של למידת מכונה שצורכים תוכניות StableHLO.

המטרה שלנו היא לפשט ולהאיץ את הפיתוח של למידת מכונה על ידי יצירת גם יכולת פעולה הדדית בין מסגרות למידת מכונה שונות (כמו TensorFlow, JAX PyTorch) ו-ML מהדרים (כמו XLA ו-IREE). ולבסוף, מסמך שמספק מפרט לשפת התכנות StableHLO.

המפרט הזה כולל שלושה קטעים עיקריים. קודם כל, בקטע תוכניות מתואר המבנה של תוכניות StableHLO שמורכבות מפונקציות StableHLO שמכילות בעצמן פעולות StableHLO. במבנה הזה, הקטע Ops מציין את הסמנטיקה של פעולות בודדות בקטע ביצוע מוצג סמנטיקה לכולם את הפעולות האלה מבצעות יחד במסגרת תוכנית. לבסוף, הקטע סימון עוסק בסימון שנעשה בו שימוש למפרט.

כדי להציג את המפרט מגרסה קודמת של StableHLO, פותחים את המאגר בכתובת גרסה מתויגת של תחום עניין. לדוגמה, המפרט של StableHLO v0.19.0. כדי לראות את השינויים שהתרחשו בכל העברה משנית של StableHLO, יש לעיין ב את הגרסה נכנסים לכתובת VhloDialect.td.

תוכניות

Program ::= {Func}

תוכנות StableHLO כוללות מספר שרירותי של פונקציות StableHLO. למטה מוצגת תוכנית לדוגמה עם פונקציה @main שכוללת 3 ערכי קלט (%image, %weights ו-%bias) ופלט אחד. גוף הפונקציה יש 6 פעולות.

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

פונקציות

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

פונקציות StableHLO (שנקראות גם פונקציות בעלות שם) מזהה, קלט/פלט וגוף. בעתיד, אנחנו מתכננים הצגת מטא-נתונים נוספים לפונקציות כדי להשיג תאימות טובה יותר עם HLO (#425, #626, #740, #744).

מזהים

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

מזהי StableHLO דומים למזהים בתכנות רבות של השפות האלה, עם שתי מאפיינים: 1) לכל המזהים יש תגים הבחנה בין סוגים שונים של מזהים, 2) מזהי ערכים. מספריים לחלוטין כדי ליצור תוכניות StableHLO פשוטות יותר.

סוגים

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

סוגי StableHLO מסווגים לסוגי ערכים (שנקראים גם סוגים של מחלקה ראשונה) שמייצגים ערכי StableHLO וסוגים שאינם ערכים שמתארים רכיבים אחרים בתוכנית. סוגי StableHLO דומים לסוגים ב- רבות משפות התכנות, וההתמחות העיקרית שלהן היא אופי ספציפי לדומיין שמוביל לתוצאות חריגות (למשל, סוגים סקלריים) הם לא סוגי ערכים).

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

סוגים של חיישנים מייצגים טנסטורים, כלומר מערכים רב-מימדיים. יש להם הצורה וסוג רכיב, שבהם צורה מייצגת מספר לא שלילי או גדלי מאפיינים לא ידועים בסדר עולה לפי הסדר מאפיינים (שנקראים גם צירים) ממוספרים מ-0 עד R-1. מספר המאפיינים R נקרא דירוג. לדוגמה, tensor<2x3xf32> הוא סוג img_tensor עם הצורה 2x3 וסוג הרכיב f32. יש לו שני מאפיינים (או, במילים אחרות, שני צירים) - מימד 0 ומימד ראשון - שגודלו הם 2 ו-3. הדירוג שלו הוא 2.

צורות יכולות להיות לא ידועות באופן חלקי או מלא (דינמיות), למשל: tensor<?x2xf64> לא ידוע באופן חלקי ו-tensor<?x?xf64> אינו ידוע כלל. דינמיות גדלים של מאפיינים מיוצגים באמצעות ?. לא ניתן לבטל את הדירוג של צורות.

בעתיד, אנחנו מתכננים לבדוק את הרחבת סוגי הטנסטור מעבר ל- גודלי מאפיינים וסוגי רכיבים, לדוגמה, כדי לכלול פריסות (#629) ומעילות (#1078).

QuantizedTensorType ::= 'tensor' '<' Shape QuantizedTensorElementType '>'
QuantizedTensorElementType ::= '!quant.uniform' '<'
                  QuantizationStorageType
                  ['<' QuantizationStorageMin ':' QuantizationStorageMax '>']
                  ':' QuantizationExpressedType
                  [':' QuantizationDimension]
                  ',' QuantizationParameters '>'
QuantizationStorageType ::= IntegerType
QuantizationStorageMin ::= IntegerConstant
QuantizationStorageMax ::= IntegerConstant
QuantizationExpressedType ::= FloatType
QuantizationDimension ::= IntegerConstant
QuantizationParameters ::= QuantizationParameter
                         | '{' QuantizationParameter {',' QuantizationParameter} '}'
QuantizationParameter ::= QuantizationScale ':' QuantizationZeroPoint
QuantizationScale ::= FloatConstant
QuantizationZeroPoint ::= IntegerConstant
שם סוג מגבלות
storage_type סוג מספר שלם (C1-C3), (C8)
storage_min מספר שלם קבוע (C1), (C3), (C7)
storage_max מספר שלם קבוע (C2), (C3), (C7)
expressed_type סוג נקודה צפה (floating-point) (C4)
quantization_dimension מספר שלם אופציונלי (C10-C12)
scales מספר וריאנטים של קבועים עם נקודה צפה (floating-point) (C4-C6), (C9), (C10), (C13)
zero_points מספר משתנה של קבועים של מספרים (C7-C9)

סוגי רכיבים בכמות גדולה מייצגים ערכים שלמים של סוג האחסון ב- הטווח בין storage_min ל-storage_max (כולל) שתואם ל- ערכי נקודה צפה (floating-point) מסוג מבטא. במספר שלם נתון 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 הוא קבוע מסוג נקודה צפה (floating-point), אבל יש עניין רב בסולמות המבוססים על מספרים שלמים, המיוצגים באמצעות מכפילים אנחנו מתכננים לבדוק את זה בעתיד הקרוב (#1404).

יש דיון מתמשך על הסמנטיקה של QuantizationZeroPoint, כולל הסוג, הערכים והאם הם יכולים להיות רק עשויות להיות כמה אפס נקודות בסוג t tensor (טנזור) כמותי. מבוסס על של הדיון הזה, המפרט סביב אפס נקודות עשוי להשתנות בעתיד (#1405).

דיון מתמשך אחר כולל את הסמנטיקה של QuantizationStorageMin ו-QuantizationStorageMax כדי לקבוע אם יש מגבלות שנכפים על הערכים האלו ועל הערכים של טנסטורים כמותיים (#1406).

לבסוף, אנחנו מתכננים לחקור שמייצגים סולמות לא ידועים ואפס בדומה לאופן שבו אנחנו מתכננים לחקור, כדי לייצג גדלים של מאפיינים (#1407).

סוגים של רכיבי img_tensor מייצגים tensors עם רכיבים כמותיים. האלה את Tensors בדיוק זהים לטינורים רגילים, מלבד יש סוגים של אלמנטים כמותיים, במקום סוגי אלמנטים רגילים.

במקצבים כמותיים, הקוונטיזציה יכולה להיות per-tensor, כלומר אחד scale ו-zero_point לכל ה-tensor או לכל ציר, כלומר, יש כמה scales ו-zero_points, זוג אחד לכל פרוסה מאפיין מסוים quantization_dimension. בצורה רשמית יותר, בטנזור t עם כימות לפי ציר, יש dim(t, quantization_dimension) פרוסות של quantization_dimension: t[:, ..., 0, ..., :], t[:, ..., 1, ..., :], וכו'. כל הרכיבים במקטע i משתמשים ב-scales[i] וב-zero_points[i] בתור את הפרמטרים שלהם לכימות. לסוגים של tensor_בכמות: מגבלות:

  • לכימות לפי טנזור:
    • ללא מגבלות נוספות.
  • לכימות לפי ציר:
    • (C12) quantization_dimension < rank(self).
    • (C13) dim(self, quantization_dimension) = size(scales).
TokenType ::= 'token'

סוגי אסימונים מייצגים אסימונים, כלומר ערכים אטומים שנוצרו ונצרכו על ידי פעולות מסוימות. האסימונים משמשים ליצירת צו ביצוע לפעולות כפי שמתואר בקטע ביצוע.

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

סוגי צמדים מייצגים צמדים, כלומר רשימות הטרוגניות. קפלים הם מורשת לתאימות ל-HLO בלבד. ב-HLO, צמדים משמש לייצוג קלט ופלט משתנים. ב-StableHLO, קלט ווריאציה יש תמיכה בפלט באופן מקורי, והשימוש היחיד ב-Tuples ב-StableHLO הוא לייצג באופן מקיף את ממשק ה-ABI של HLO, T, tuple<T> וגם הערך של tuple<tuple<T>> עשוי להיות שונה באופן מהותי בהתאם לישות מסוימת יישום בפועל. בעתיד, אנחנו מתכננים לבצע שינויים ב-HLO ABI ועשויה להסיר מ-StableHLO סוגים שונים של tuple (#598).

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

סוגי רכיבים מייצגים אלמנטים מסוג טנזור. בניגוד לסוגים רבים של תכנות שפות אחרות, הסוגים האלה אינם מהמחלקה הראשונה ב-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'

String type מייצג רצפים של בייטים. בניגוד לסוגים רבים של תכנות שפות, סוג המחרוזת הוא לא המחלקה הראשונה ב-StableHLO והוא משמש רק לציין מטא-נתונים סטטיים לרכיבי תוכנה.

תפעול

פעולות StableHLO (שנקראות גם ops) מייצגות קבוצה סגורה של פעולות ברמה גבוהה במודלים של למידת מכונה. כפי שהסברנו למעלה, התחביר של StableHLO מושפע מאוד מ-MLIR, אבל לא בהכרח חלופה ארגונומית, אבל אין ספק שזו ההתאמה הטובה ביותר ליעד של StableHLO יצירת יכולת פעולה הדדית נוספת בין frameworks של למידת מכונה ומהדרים של למידת מכונה.

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

לפעולות StableHLO (שנקראות גם ops) יש שם, קלט/פלט וחתימה. השם מורכב מהתחילית stablehlo. ו אזכור שמזהה באופן ייחודי את אחת מהפעולות הנתמכות. בהמשך מופיע מידע רשימה מקיפה של כל הפעולות הנתמכות.

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

פעולות התפעול צורכות קלט ומפיקות פלטים. הפריטים קלט מסווגים לפי ערכי קלט (שמחושבים במהלך הביצוע), פונקציות קלט (סופקו) באופן סטטי, ובגלל שב-StableHLO פונקציות הן לא ערכים מהמחלקה הראשונה, במאפייני הקלט (שמופיעים גם בצורה סטטית). סוגי הקלט והפלט צריכה ומופקת על ידי אופ תלוי במנמן שלו. לדוגמה, add הפונקציה op צריכה 2 ערכי קלט ומפיקה ערך פלט אחד. לצורך השוואה, פעולה 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), 2 פונקציות קלט ו-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]

קבועים של נקודה צפה (floating-point) מייצגים ערכי נקודה צפה באמצעות מחרוזות להשתמש בסימון עשרוני או מדעי. בנוסף, סימון הקסדצימלי יכול להיות משמש לציון ישיר את הביטים הבסיסיים בפורמט הנקודה הצפה של מהסוג המתאים. קבועים של נקודה צפה כוללים את המגבלות הבאות:

  • (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>. מייצג ערך t e n s o r f l o עם המיפוי הבא מאינדקסים לאלמנטים: {0, 0} => 1, {0, 1} => 2, {0, 2} => 3, {1, 0} => 4, {1, 1} => 5, {1, 2} => 6. הסדר שבו רכיבים אלה מאוחסנים לאחר מכן בזיכרון הוא מוגדר מראש. קבועים של Tensor כוללים את המגבלות הבאות:

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

קבועים של טינזור בכמות גדולה מייצגים ערכים של טנזור כמותי באמצעות אותם וסימון כקבועים של tens, עם אלמנטים שמצוינים כקבועים של סוג האחסון. קבועים של טנזור כמות הם כפופים למגבלות הבאות:

  • (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, רצפים של תו בריחה (escape). הם לא תלויים בקידוד, לכן הפרשנות שלהם בייטים מוגדרים על ידי ההטמעה. ליטרלים של מחרוזות הם מסוג string.

תפעול

בטן

סמנטיקה

מבצע פעולת AB ברמת הרכיב על טנזור operand ומפיק result את Tensor. בהתאם לסוג הרכיב, הפעולות הבאות:

  • למספרים שלמים חתומים: מודול של מספרים שלמים.
  • לצפים: abs מ-IEEE-754.
  • למספרים מרוכבים: מודולוס מרוכב.
  • לסוגים הבאים לפי כמות: dequantize_op_quantize(abs, operand, type(result)).

קלט

תווית שם סוג מגבלות
(I1) operand Tensor של מספר שלם חתום, נקודה צפה (float-point) או סוג מרוכב, או t e n s o r f l o w (C1-C2)

פלט

שם סוג מגבלות
result Tensor של מספר שלם חתום או סוג נקודה צפה (floating-point) או טנזור כמותי לכל טנזור (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]

דוגמאות נוספות

add

סמנטיקה

מבצעת הוספה מבוססת-רכיבים של שני טנזורים lhs ו-rhs ומפיקה טנזור result. בהתאם לסוג הרכיב, הפעולות הבאות:

  • בערך בוליאני: OR.
  • למספרים שלמים: חיבור של מספרים שלמים.
  • לצפים: addition מ-IEEE-754.
  • למספרים מרוכבים: חיבור מרוכב.
  • לסוגים הבאים לפי כמות: dequantize_op_quantize(add, lhs, rhs, type(result)).

קלט

תווית שם סוג מגבלות
(I1) lhs t e n s o r f l o w, או t e n s o r f l o w, (C1-C6)
(I2) rhs t e n s o r f l o w, או t e n s o r f l o w, (C1-C5), (C7)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, (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 רכיבי tensor.

הפעולה מפצלת את רשת התהליך של StableHLO ל-process_groups, מוגדר כך:

  • cross_replica(replica_groups) אם channel_id <= 0 and use_global_device_ids = false.
  • cross_replica_and_partition(replica_groups) אם channel_id > 0 and use_global_device_ids = false.
  • flattened_ids(replica_groups) אם channel_id > 0 and use_global_device_ids = true.

לאחר מכן, בכל process_group:

  • operands...@receiver = [operand@sender for sender in process_group] לכולם receiver באפליקציית process_group.
  • results...@process = concatenate(operands...@process, all_gather_dim) לכולם process באפליקציית process_group.

קלט

תווית שם סוג מגבלות
(I1) operands מספר משתנה של טנסטורים או טנזורים כמותיים לכל טנזור (C1), (C6)
(I2) all_gather_dim קבוע מסוג si64 (C1), (C6)
(I3) replica_groups קבוע דו-ממדי של img_tensor מסוג 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) אם channel_id <= 0 and use_global_device_ids = false.
  • cross_replica_and_partition(replica_groups) אם channel_id > 0 and use_global_device_ids = false.
  • flattened_ids(replica_groups) אם channel_id > 0 and use_global_device_ids = true.

לאחר מכן, בכל process_group:

  • results...@process[result_index] = exec(schedule) עבור עץ בינארי מסוים schedule כאשר:
    • exec(node) = computation(exec(node.left), exec(node.right)).
    • exec(leaf) = leaf.value.
  • schedule הוא עץ בינארי שמוגדר על ידי ההטמעה, שבסדר המעבר הוא to_destination_type(operands...@process_group...[result_index], type(func_inputs(computation)[0])).

קלט

תווית שם סוג מגבלות
(I1) operands מספר משתנה של טנסטורים או טנזורים כמותיים לכל טנזור (C5), (C6)
(I2) replica_groups מספר משתנה של קבועי טינזור חד-ממדיים מסוג si64 (C1-C3)
(I3) channel_id קבוע מסוג si64 (C4)
(I4) use_global_device_ids קבוע מסוג i1 (C4)
(I5) computation פונקציה (C5)

פלט

שם סוג מגבלות
results מספר משתנה של טנסטורים או טנזורים כמותיים לכל טנזור (C6-C7)

מגבלות

  • (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 קבוע דו-ממדי של img_tensor מסוג 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 (וגם) מבחינת רכיבי AND של שני טנסורים lhs ו-rhs ומפיק result את Tensor. בהתאם לסוג הרכיב, הפעולות הבאות:

  • לערך בוליאני: לוגי AND.
  • למספרים שלמים: AND ברמת הסיביות.

קלט

תווית שם סוג מגבלות
(I1) lhs tensor מסוג בוליאני או מספר שלם (C1)
(I2) rhs tensor מסוג בוליאני או מספר שלם (C1)

פלט

שם סוג מגבלות
result tensor מסוג בוליאני או מספר שלם (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 t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1)
(I2) rhs t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (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 את Tensors. באופן רשמי יותר, ניתן לבטא פעולה זו כפירוק פעולות 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 t e n s o r f l o w או t e n s o r f l o w, (C1-C3), (C5)
(I2) scale טנזור חד-ממדי של נקודה צפה (floating-point) או סוג כמותי לכל טנזור (C2), (C4), (C5)
(I3) mean טנזור חד-ממדי של נקודה צפה (floating-point) או סוג כמותי לכל טנזור (C2), (C4)
(I4) variance טנזור חד-ממדי של נקודה צפה (floating-point) או סוג כמותי לכל טנזור (C2), (C4)
(I5) grad_output t e n s o r f l o w או t e n s o r f l o w, (C2), (C3)
(I6) epsilon קבוע מסוג f32
(I7) feature_index קבוע מסוג si64 (C1), (C5)

פלט

שם סוג מגבלות
grad_operand t e n s o r f l o w או t e n s o r f l o w, (C2), (C3)
grad_scale טנזור חד-ממדי של נקודה צפה (floating-point) או סוג כמותי לכל טנזור (C2), (C4)
grad_offset טנזור חד-ממדי של נקודה צפה (floating-point) או סוג כמותי לכל טנזור (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 t e n s o r f l o w או t e n s o r f l o w, (C1-C7)
(I2) scale טנזור חד-ממדי של נקודה צפה (floating-point) או סוג כמותי לכל טנזור (C2), (C3)
(I3) offset טנזור חד-ממדי של נקודה צפה (floating-point) או סוג כמותי לכל טנזור (C2), (C4)
(I4) mean טנזור חד-ממדי של נקודה צפה (floating-point) או סוג כמותי לכל טנזור (C5)
(I5) variance טנזור חד-ממדי של נקודה צפה (floating-point) או סוג כמותי לכל טנזור (C2), (C6)
(I6) epsilon קבוע מסוג f32
(I7) feature_index קבוע מסוג si64 (C1), (C3-C6)

פלט

שם סוג מגבלות
result t e n s o r f l o w או t e n s o r f l o w, (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 ומנורמל את ערך ה-tensor operand, שמפיק output, batch_mean ו-batch_var tensor. יותר רשמי, אפשר לבטא את הפעולה הזו פירוק לפעולות 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 t e n s o r f l o w או t e n s o r f l o w, (C1)
(I2) scale טנזור חד-ממדי של נקודה צפה (floating-point) או לכל טנזור כמותי (C2), (C3)
(I3) offset טנזור חד-ממדי של נקודה צפה (floating-point) או לכל טנזור כמותי (C2), (C4)
(I4) epsilon קבוע מסוג f32 (C1), (C3-C6)
(I5) feature_index קבוע מסוג si64 (C1), (C3-C6)

פלט

שם סוג מגבלות
output t e n s o r f l o w או t e n s o r f l o w, (C7)
batch_mean טנזור חד-ממדי של נקודה צפה (floating-point) או לכל טנזור כמותי (C2), (C5)
batch_var טנזור חד-ממדי של נקודה צפה (floating-point) או לכל טנזור כמותי (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

סמנטיקה

מבצע פעולת ביט-cast על טנזור 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 מחזירה ייצוג בזיכרון של ערך נתון, ואת ההתנהגות שלו מוגדר ביישום כי הייצוג המדויק של tensor והייצוג המדויק של סוגי הרכיבים, מוגדר גם כן.

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w, או t e n s o r f l o w, (C1-C2)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, (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

סמנטיקה

הרחבת המאפיינים ו/או הדירוג של רכיב קלט (tensor) על ידי שכפול הנתונים בטנזור 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 t e n s o r f l o w, או t e n s o r f l o w, (C1-C2), (C5-C6)
(I2) broadcast_dimensions קבוע מפריד חד-ממדי מסוג si64 (C2-C6)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, (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) resp, אחרת.
  • (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

סמנטיקה

מבצעת פעולת שורש ממעלה שלישית ברמת הרכיב ב-Tensor operand ומפיקה result t e n s o r f l o w. בהתאם לסוג הרכיב, הפעולות הבאות:

  • לצפים: rootn(x, 3) מ-IEEE-754.
  • למספרים מרוכבים: שורש מעוקבים מורכב.
  • לסוגים מותאמים אישית: dequantize_op_quantize(cbrt, operand, type(result))

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (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

סמנטיקה

מבצע ceil מקודד ברמת היסוד של operand tenor ומפיק result טנזור. יישום הפעולה roundToIntegralTowardPositive מ-IEEE-754 למפרט. בסוגים כמותיים, הפונקציה dequantize_op_quantize(ceil, operand, type(result))

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w או t e n s o r f l o w, (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w או t e n s o r f l o w, (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]

דוגמאות נוספות

צ'ולסקי

סמנטיקה

מחשבת את הפרוק של כללסקי של קבוצת מטריצות.

באופן רשמי יותר, לכל הi בindex_space(result), result[i0, ..., iR-3, :, :] הוא פירוק של כללסקי a[i0, ..., iR-3, :, :], בצורת משולש נמוך יותר (אם lower הוא true) או מטריצה משולשת-עליונה (אם lower הוא false). ערכי הפלט במשולש הנגדי, כלומר המשולש העליון המחמיר או משולשים נמוכים יותר בהתאמה, ניתנים להגדרה.

אם קיימת i כאשר מטריצת הקלט היא לא ערך חיובי בהרמיטיה ב-מטריצה, ההתנהגות לא מוגדרת.

בסוגים כמותיים, הפונקציה dequantize_op_quantize(lambda operand: cholesky(operand, lower), a, type(result))

קלט

תווית שם סוג מגבלות
(I1) a t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1-C3)
(I2) lower קבוע מפריד דו-ממדי מסוג i1

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (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 t e n s o r f l o w.

הפעולה מפצלת את רשת התהליך של 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, שולח הערך של Tensor של operand מתהליך המקור לתהליך היעד ומפיק result t e n s o r f l o w.

הפעולה מפצלת את רשת התהליך של 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 קבוע דו-ממדי של img_tensor מסוג 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 Tensors בהתאם 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.

לסוגי אלמנטים של נקודה צפה (floating-point) עם compare_type = FLOAT, הפונקציה מממשת את פעולות IEEE-754 הבאות:

  • EQ: compareQuietEqual.
  • NE: compareQuietNotEqual.
  • GE: compareQuietGreaterEqual.
  • GT: compareQuietGreater.
  • LE: compareQuietLessEqual.
  • LT: compareQuietLess.

לסוגי אלמנטים של נקודה צפה (floating-point) עם 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 טיפוסים בני מנייה (enum) של EQ, NE, GE, GT, LE ו-LT
(I4) compare_type טיפוסים בני מנייה (enum) של FLOAT, TOTALORDER, SIGNED ו-UNSIGNED (C3)

פלט

שם סוג מגבלות
result tenor מסוג בוליאני (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 tensor מסוג f32 או f64 (C1-C3)
(I2) rhs tensor מסוג f32 או f64 (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w (C2), (C3)

מגבלות

  • (C1) type(lhs) = type(rhs).
  • (C2) shape(result) = shape(lhs).
  • (C3) element_type(result) מסוג complex<E> כאשר E = element_type(lhs).

דוגמאות

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

דוגמאות נוספות

מורכב

סמנטיקה

כולל פעולה שמורכבת מפעולות StableHLO אחרות, לוקחים את inputs ו-composite_attributes ומפיקים את results. הסמנטיקה של המבצע מוטמעת באמצעות המאפיין decomposition. אפשר להחליף את הפעולה composite בפרוק שלה בלי לשנות את התוכנית סמנטיקה. במקרים שבהם הטבעת הפירוק לא מספקת סמנטיקה של תפעול, עדיף להשתמש ב-custom_call.

השדה version (ברירת המחדל היא 0) משמש כדי לציין מתי שינוי סמנטיקה.

קלט

תווית שם סוג
(I1) inputs מספר משתנה של ערכים
(I2) name קבוע מסוג string
(I3) composite_attributes מילון מאפיינים
(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 t e n s o r f l o w, או t e n s o r f l o w, (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]]

דוגמאות נוספות

להשלים המרה

סמנטיקה

מבצע המרה ברמת הרכיבים מסוג רכיב אחד לסוג אחר ב- t e n s o r f l o w, ומפיק טנזור result.operand

להמרות מסוג boolean-to-any-supported-type, הערך false הוא הומר לאפס, והערך true מומר ל-1. עבור המרות מסוג any-supported-type-to-boolean, ערך אפס מומר ל false וערכים שאינם אפס מומרים ל-true. בהמשך מוסבר איך מתאימות לסוגים מורכבים.

להמרות שכוללות מספר שלם למספר שלם, integer-ל-floating-point או floating-point-to-floating-point, אם ערך המקור יכול להיות בדיוק שמיוצג בסוג היעד, ערך התוצאה הוא אותו ערך בווקטור יהיה זהה, אחרת, אופן הפעולה טרם נקבע (#180).

בהמרות שכוללות floating-point-to-integer, החלק השבר הוא נחתך. אם אי אפשר לייצג את הערך החתוך בסוג היעד, עדיין טרם נקבעה (#180).

המרות מסוג מורכב למורכבות פועלות באותה התנהגות המרות floating-point-to-floating-point שמיועדות להמרות ריאליות חלקים דמיוניים.

בהמרות מסוג complex-to-any-other-type וגם בהמרות complex-to-any-other-type, המערכת מתעלמת מהערך המדומה של המקור או מהערך הדמיוני של היעד אפס, בהתאמה. ההמרה של החלק האמיתי מתבצעת המרות מנקודה צפה (floating-point).

בעיקרון, פעולה זו יכולה לבטא את ביטול החלוקה (המרה מ- טנסטורים כמותיים לטינורים רגילים), קוונטיזציה (המרה מהגדרות רגילות) img_tensors to quantor tensors (טנזטורים כמותיים) ו-requantization (המרה בין ערכים כמותיים) את Tensors), אבל כרגע יש לנו פעולות ייעודיות לכך, 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 t e n s o r f l o w, או t e n s o r f l o w, (C1), (C14-C16), (C25), (C27-C29) (C31-C34)
(I3) window_strides קבוע מפריד חד-ממדי מסוג si64 (C2-C3), (C25)
(I4) padding קבוע דו-ממדי של Tensor מסוג 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 מספר ושונות של טיפוסים בני מנייה (enum) של DEFAULT, HIGH ו-HIGHEST (C24)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, (C25-C28), (C30), (C32-34)

מגבלות

  • (C1) N = rank(lhs) = rank(rhs).
  • (C2) size(window_strides) = N - 2.
  • (C3) 0 < window_strides.
  • (C4) shape(padding) = [N - 2, 2].
  • (C5) size(lhs_dilation) = N - 2.
  • (C6) 0 < lhs_dilation.
  • (C7) size(rhs_dilation) = N - 2.
  • (C8) 0 < rhs_dilation.
  • (C9) size(window_reversal) = N - 2.
  • (C10) dim(lhs, input_batch_dimension) % batch_group_count = 0.
  • (C11) dim(lhs, input_feature_dimension) % feature_group_count = 0.
  • (C12) size(input_spatial_dimensions) = N - 2.
  • (C13) ניתן input_dimensions = [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]:
    • is_unique(input_dimensions).
    • 0 <= input_dimensions < N.
  • (C14) dim(rhs, kernel_input_feature_dimension) = dim(lhs, input_feature_dimension) / feature_group_count.
  • (C15) dim(rhs, kernel_output_feature_dimension) % batch_group_count = 0.
  • (C16) dim(rhs, kernel_output_feature_dimension) % feature_group_count = 0.
  • (C17) size(kernel_spatial_dimensions) = N - 2.
  • (C18) בהינתן kernel_dimensions = kernel_spatial_dimensions + [kernel_input_feature_dimension] + [kernel_output_feature_dimension]:
    • is_unique(kernel_dimensions).
    • 0 <= kernel_dimensions < N.
  • (C19) size(output_spatial_dimensions) = N - 2.
  • (C20) בהינתן output_dimensions = [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]:
    • is_unique(output_dimensions).
    • 0 <= output_dimensions < N.
  • (C21) 0 < feature_group_count.
  • (C22) 0 < batch_group_count.
  • (C23) feature_group_count = 1 or batch_group_count = 1.
  • (C24) size(precision_config) = 2.
  • (C25) dim(result, result_dim) מוגדר כך:
    • 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) אם is_per_tensor_quantized(rhs), אז 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 t e n s o r f l o w. בהתאם לסוג הרכיב, הפעולות הבאות:

  • לצפים: cos מ-IEEE-754.
  • למספרים מרוכבים: קוסינוס מרוכב.
  • לסוגים הבאים לפי כמות: dequantize_op_quantize(cosine, operand, type(result)).

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (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 Tensor ומפיק טנזור result.

קלט

תווית שם סוג מגבלות
(I1) operand Tensor מסוג מספר שלם (C1)

פלט

שם סוג מגבלות
result Tensor מסוג מספר שלם (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 כדי לספק עוד מטא-נתונים שהוגדרו על ידי ההטמעה.

כרגע, פעולה זו מכילה אוסף לא מאורגן למדי של מטא-נתונים שמשקפים את ההתפתחות האורגנית של הפעולה המקבילה מהדר (compiler) XLA. בעתיד אנחנו מתכננים לאחד את המטא-נתונים האלה (#741).

קלט

תווית שם סוג
(I1) inputs מספר משתנה של ערכים
(I2) call_target_name קבוע מסוג string
(I3) has_side_effect קבוע מסוג i1
(I4) backend_config קבוע מסוג string או מילון מאפיינים
(I5) api_version קבוע מסוג si32
(I6) called_computations מספר משתנה של משתנים מסוג string

פלט

שם סוג
results מספר משתנה של ערכים

דוגמאות

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

חילוק

סמנטיקה

מבצע חלוקה חכמה של הרכיבים של lhs ומחלק rhs טנסורים ו- מפיקה את הטנזור result. בהתאם לסוג הרכיב, הפעולות הבאות:

  • במספרים שלמים: חילוק של מספרים שלמים שיוצר את המנה האלגברית עם כל החלק היחסי נמחק.
  • לצפים: division מ-IEEE-754.
  • למספרים מרוכבים: חילוק מרוכב.
  • בסוגים כמותיים:
    • dequantize_op_quantize(divide, lhs, rhs, type(result)).

קלט

תווית שם סוג מגבלות
(I1) lhs t e n s o r f l o w, t e n s o r f l o w, או t e tensor quantor, (C1)
(I2) rhs t e n s o r f l o w, t e n s o r f l o w, או t e tensor quantor, (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, t e n s o r f l o w, או t e n s o r f l o w, (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 קובע את יחסי הגומלין בין מהירות לדיוק עבור בקצוות העורפיים של מאיצים. הוא יכול להיות אחד מהבאים ( הסמנטיקה של ערכי 'טיפוסים בני מנייה (enum)' לא מצוינת, בנושא הזה #755):

  • DEFAULT: החישוב המהיר ביותר, אבל ההערכה הכי פחות מדויקת המספר המקורי.
  • HIGH: חישוב איטי יותר, אבל הערכה מדויקת יותר המספר המקורי.
  • HIGHEST: החישוב האיטי ביותר, אבל ההשערה המדויקת ביותר המספר המקורי.

DotAlgorithm מגדיר את המאפיינים העיקריים של האלגוריתם שמשמש להטמעה פעולת הנקודה, שמגדירה גם את הדיוק. אם המאפיין של האלגוריתם הוגדרו שדות, וה-precision_config חייב להיות DEFAULT. DotAlgorithms אין ערך ברירת מחדל, כי הפרמטרים של ברירת המחדל מוגדר. לכן, כל שדות האלגוריתם של הנקודות יכולים להיות מוגדרים ל-None כדי לציין משתמש באלגוריתם precision_config ריק.

השדות DotAlgorithm כוללים את אלה:

  • lhs_precision_type ו-rhs_precision_type, הדיוק של ה-LHS RHS של הפעולה מעוגל ל. סוגי הדיוק אינם תלויים של סוגי הקלט והפלט.
  • accumulation_type רמת הדיוק שמשמשת לצבירה.
  • lhs_component_count, rhs_component_count וגם num_primitive_operations כאשר אנחנו מבצעים אלגוריתם שמפרק את ה-LHS ו/או ה-RHS ויוצרת ריבוי רכיבים פעולות בנקודות - בדרך כלל כדי לבצע אמולציה של רמת דיוק גבוהה יותר (למשל ניצול סוג הנתונים bfloat16 של בינה מלאכותית (AI) לביצוע חישובים מדויקים יותר: 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 t e n s o r f l o w, או t e n s o r f l o w, (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 מספר ושונות של טיפוסים בני מנייה (enum) של 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 t e n s o r f l o w, או t e n s o r f l o w, (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) אם is_per_tensor_quantized(rhs), אז 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_non_expanding_dimensions להביע ידע סטטי לגבי ההתנהגות המתרחבת של מאפיינים. אם לא מציינים זאת, ההנחה היא שכל המאפיינים מתרחבים.

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w, או t e n s o r f l o w, (C1-C2), (C5-C6), (C9)
(I2) output_dimensions זווית חד-ממדית של מספר שלם מסוג מספר שלם (C7)
(I3) broadcast_dimensions טנזור קבוע חד-ממדי מסוג מספר שלם (C2-C6)
(I4) known_expanding_dimensions טנזור קבוע חד-ממדי מסוג מספר שלם (C8-C9)
(I5) known_non_expanding_dimensions טנזור קבוע חד-ממדי מסוג מספר שלם (C8-C9)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, (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) resp, אחרת.
  • (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_non_expanding_dimensions).
  • (C9) 0 <= known_expanding_dimensions < rank(operand).
  • (C10) 0 <= known_non_expanding_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_non_expanding_dimensions = array<i64: 1>
} : (tensor<1x3xi64>, tensor<3xi64>) -> tensor<2x3x2xi64>
// %result: [
//            [
//             [1, 1],
//             [2, 2],
//             [3, 3]
//            ],
//            [
//             [1, 1],
//             [2, 2],
//             [3, 3]
//            ]
//          ]

דוגמאות נוספות

dynamic_conv

סמנטיקה

הפעולה הזו זהה מבחינה פונקציונלית ל- קונבולציה אופס, אבל המרווח הפנימי מצוין באופן דינמי דרך padding.

קלט

תווית שם סוג מגבלות
(I1) lhs טנזור או לכל טנזור קוונטי (C1), (C10-C11), (C14) (C25), (C26-C27), (C30-C31) (C33)
(I2) rhs t e n s o r f l o w, או t e n s o r f l o w, (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 מספר ושונות של טיפוסים בני מנייה (enum) של DEFAULT, HIGH ו-HIGHEST (C24)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, (C25-C27), (C29), (C31-C33)

מגבלות

  • (C1) N = rank(lhs) = rank(rhs).
  • (C2) size(window_strides) = N - 2.
  • (C3) 0 < window_strides.
  • (C4) shape(padding) = [N - 2, 2].
  • (C5) size(lhs_dilation) = N - 2.
  • (C6) 0 < lhs_dilation.
  • (C7) size(rhs_dilation) = N - 2.
  • (C8) 0 < rhs_dilation.
  • (C9) size(window_reversal) = N - 2.
  • (C10) dim(lhs, input_batch_dimension) % batch_group_count = 0.
  • (C11) dim(lhs, input_feature_dimension) % feature_group_count = 0.
  • (C12) size(input_spatial_dimensions) = N - 2.
  • (C13) ניתן input_dimensions = [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]:
    • is_unique(input_dimensions).
    • 0 <= input_dimensions < N.
  • (C14) dim(rhs, kernel_input_feature_dimension) = dim(lhs, input_feature_dimension) / feature_group_count.
  • (C15) dim(rhs, kernel_output_feature_dimension) % batch_group_count = 0.
  • (C16) dim(rhs, kernel_output_feature_dimension) % feature_group_count = 0.
  • (C17) size(kernel_spatial_dimensions) = N - 2.
  • (C18) בהינתן kernel_dimensions = kernel_spatial_dimensions + [kernel_input_feature_dimension] + [kernel_output_feature_dimension]:
    • is_unique(kernel_dimensions).
    • 0 <= kernel_dimensions < N.
  • (C19) size(output_spatial_dimensions) = N - 2.
  • (C20) בהינתן output_dimensions = [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]:
    • is_unique(output_dimensions).
    • 0 <= output_dimensions < N.
  • (C21) 0 < feature_group_count.
  • (C22) 0 < batch_group_count.
  • (C23) feature_group_count = 1 or batch_group_count = 1.
  • (C24) size(precision_config) = 2.
  • (C25) dim(result, result_dim) מוגדר כך:
    • 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) אם is_per_tensor_quantized(rhs), אז 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

סמנטיקה

הפעולה הזו זהה מבחינה פונקציונלית ל- לאסוף פעולה, כאשר slice_sizes מצוין באופן דינמי כערך.

קלט

תווית שם סוג מגבלות
(I1) operand טנזור או לכל טנזור קוונטי (C1), (C7), (C10-C12), (C14)
(I2) start_indices Tensor מסוג מספר שלם (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 t e n s o r f l o w, t e n s o r f l o w, או t e n s o r f l o w, (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 t e n s o r f l o w, או t e וגם tensor tensor זרוע t (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

סמנטיקה

הפעולה הזו זהה מבחינה פונקציונלית ל- לעצב מחדש אופס, אבל צורת התוצאה מצוינת באופן דינמי דרך output_shape.

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w, או t e n s o r f l o w, (C1-C3)
(I2) output_shape זווית חד-ממדית של מספר שלם מסוג מספר שלם (C4)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, (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 t e n s o r f l o w. בהתאם לסוג הרכיב, הפעולות הבאות:

  • לצפים: exp מ-IEEE-754.
  • למספרים מרוכבים: מעריכי מרוכבים.
  • בסוגים כמותיים: dequantize_op_quantize(exponential, operand, type(result))

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (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.
  • למספרים מרוכבים: מעריכי מרוכבים פחות 1.
  • בסוגים כמותיים: dequantize_op_quantize(exponential_minus_one, operand, type(result))

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1)

מגבלות

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

דוגמאות

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

דוגמאות נוספות

fft

סמנטיקה

מבצע את טרנספורמציות פורייה קדימה והופכות עבור ממשיות ומורכבות של קלט/פלט.

fft_type הוא אחד מהבאים:

  • FFT: העברת קובץ FFT מפורמט מורכב למורכב.
  • IFFT: המרה מרוכבת מרוכבת למורכבת (FFT).
  • RFFT: העברה מ-Real-to-Complex FFT.
  • IRFFT: רכיב FFT מסוג 'Real-to-complex' (כלומר, מקבל מורכב, מחזיר TRUE).

בצורה רשמית יותר, בהינתן הפונקציה 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, שמקבלת טנזטורים חד-ממדיים של סוגי נקודה צפה (floating-point), מייצרים טנסטורים חד-ממדיים של סוגים מורכבים אותה סמנטיקה של נקודה צפה (floating-point), והיא פועלת באופן הבא:

  • 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 Tensor של נקודה צפה (floating-point) או סוג מורכב (C1), (C2), (C4), (C5)
(I2) fft_type טיפוסים בני מנייה (enum) של FFT, IFFT, RFFT ו-IRFFT (C2), (C5)
(I3) fft_length קבוע מפריד חד-ממדי מסוג si64 (C1), (C3), (C4)

פלט

שם סוג מגבלות
result Tensor של נקודה צפה (floating-point) או סוג מורכב (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) הוא מסוג נקודה צפה (floating-point) ו element_type(result) הוא סוג מורכב של אותה נקודה צפה (floating-point) סמנטיקה.
    • אם הערך הוא fft_type = IRFFT, הערך element_type(operand) הוא סוג מורכב element_type(result) הוא סוג נקודה צפה (floating-point) של אותה נקודה צפה (floating-point) סמנטיקה.
  • (C3) 1 <= size(fft_length) <= 3.
  • (C4) אם בין operand ו-result, יש טינזור real נקודה צפה (floating-point), ואז 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 t e n s o r f l o w או t e n s o r f l o w, (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w או t e n s o r f l o w, (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]) אם 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 Tensor מסוג מספר שלם (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 t e n s o r f l o w, או t e n s o r f l o w, (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 טמפל (C1), (C2)
(I2) index קבוע מסוג si32 (C1), (C2)

פלט

שם סוג מגבלות
result כל סוג נתמך (C2)

מגבלות

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

דוגמאות

// %operand: ([1.0, 2.0], (3))
  index = 0 : i32
} : (tuple<tensor<2xf32>, tuple<tensor<i32>>>) -> tensor<2xf32>
// %result: [1.0, 2.0]

דוגמאות נוספות

אם

סמנטיקה

מפיקה את הפלט מביצוע פונקציה אחת בלבד מ-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

דוגמאות נוספות

דמיון

סמנטיקה

מחלצת את החלק הדמיוני, ברמת היסוד, מה-operand ומפיקה result t e n s o r f l o w. באופן רשמי יותר, עבור כל רכיב x: imag(x) = is_complex(x) ? imaginary_part(x) : constant(0, element_type(result)).

קלט

תווית שם סוג מגבלות
(I1) operand Tensor של נקודה צפה (floating-point) או סוג מורכב (C1), (C2)

פלט

שם סוג מגבלות
result Tensor מסוג נקודה צפה (floating-point) (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]

דוגמאות נוספות

בגוף הפיד

סמנטיקה

קורא נתונים מתוך הפיד ומפיק results.

הסמנטיקה של infeed_config מוגדרת על ידי ההטמעה.

results מכיל ערכים של מטען ייעודי (payload) שקודם כל ואסימון שמגיע האחרון. בעתיד, אנחנו מתכננים לפצל את המטען הייעודי (payload) ואת האסימון לשניים כדי ליצור פלט ברור יותר (#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 t e n s o r f l o w, t e n s o r f l o w, או t e n s o r f l o w, (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 t e n s o r f l o w או t e n s o r f l o w, (C1)

פלט

שם סוג מגבלות
y tenor מסוג בוליאני (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

סמנטיקה

מבצעת פעולת לוגריתם ברמת הרכיבים ב-Tensor operand ומפיקה result t e n s o r f l o w. בהתאם לסוג הרכיב, הפעולות הבאות:

  • לצפים: log מ-IEEE-754.
  • למספרים מרוכבים: לוגריתם מרוכב.
  • לסוגים הבאים לפי כמות: dequantize_op_quantize(log, operand, type(result)).

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (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 tensor מפיק את הפרמטר result. בהתאם לסוג הרכיב, הפעולות הבאות:

  • לצפים: logp1 מ-IEEE-754.
  • למספרים מרוכבים: לוגריתם מרוכב ועוד 1.
  • בסוגים כמותיים: dequantize_op_quantize(log_plus_one, operand, type(result))

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (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]

דוגמאות נוספות

לוגיסטי

סמנטיקה

מבצע פעולה לוגיסטית מבחינת הרכיבים ב-Tensor operand ומפיק result t e n s o r f l o w. בהתאם לסוג הרכיב, הפעולות הבאות:

  • לצפים: division(1, addition(1, exp(-x))) מ-IEEE-754.
  • למספרים מורכבים: לוגיסטי מורכבת.
  • בסוגים כמותיים: dequantize_op_quantize(logistic, operand, type(result))

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (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 tensor ומפיק result את Tensor. בהתאם לסוג הרכיב, הפעולות הבאות:

  • למספרים שלמים חתומים: השלילה של מספרים שלמים.
  • למספרים שלמים לא חתומים: bitcast למספר שלם חתום, השמטת מספרים שלמים, bitcast בחזרה למספר שלם לא רשום.
  • לצפים: negate מ-IEEE-754.
  • למספרים מרוכבים: שלילה מרוכבת.
  • בסוגים כמותיים: dequantize_op_quantize(negate, operand, type(result))

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w, t e n s o r f l o w, או t e n s o r f l o w, (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, t e n s o r f l o w, או t e n s o r f l o w, (C1)

מגבלות

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

דוגמאות

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

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

דוגמאות נוספות

לא

סמנטיקה

מבצעת את הפונקציה לא של טנזור operand מבחינת יסודות ומפיקה טנזור result. בהתאם לסוג הרכיב, הפעולות הבאות:

  • לערך בוליאני: לוגי NOT.
  • למספרים שלמים: NOT [סיביות].

ארגומנטים

שם סוג מגבלות
operand tensor מסוג בוליאני או מספר שלם (C1)

פלט

שם סוג מגבלות
result tensor מסוג בוליאני או מספר שלם (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 את Tensor. בהתאם לסוג הרכיב, הפעולות הבאות:

  • בערך בוליאני: OR.
  • למספרים שלמים: OR.

קלט

תווית שם סוג מגבלות
(I1) lhs tensor של מספר שלם או סוג בוליאני (C1)
(I2) rhs tensor של מספר שלם או סוג בוליאני (C1)

פלט

שם סוג מגבלות
result tensor של מספר שלם או סוג בוליאני (C1)

מגבלות

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

דוגמאות

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

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

דוגמאות נוספות

פיד שגוי

סמנטיקה

הפונקציה כותבת את הערך inputs בפיד החיצוני ויוצרת אסימון result.

הסמנטיקה של outfeed_config מוגדרת על ידי ההטמעה.

קלט

תווית שם סוג
(I1) inputs מספר משתנה של טנזורים או טנזורים כמותיים
(I2) token token
(I3) outfeed_config קבוע מסוג string

פלט

שם סוג
result token

דוגמאות

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

דוגמאות נוספות

משטח

סמנטיקה

התכונה הזו מרחיבה את 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 t e n s o r f l o w, או t e וגם tensor tensor זרוע t (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 Tensor מסוג מספר שלם (C1)

פלט

שם סוג מגבלות
result Tensor מסוג מספר שלם (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 t e n s o r f l o w, t e n s o r f l o w, או t e n s o r f l o w, (C1)
(I2) rhs t e n s o r f l o w, t e n s o r f l o w, או t e n s o r f l o w, (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, t e n s o r f l o w, או t e n s o r f l o w, (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 את Tensor. באופן רשמי יותר, עבור כל רכיב x: real(x) = is_complex(x) ? real_part(x) : x.

קלט

תווית שם סוג מגבלות
(I1) operand Tensor של נקודה צפה (floating-point) או סוג מורכב (C1), (C2)

פלט

שם סוג מגבלות
result Tensor מסוג נקודה צפה (floating-point) (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]

דוגמאות נוספות

הקלטה

סמנטיקה

מקבל נתונים מערוץ עם channel_id ומפיק results.

אם הערך של is_host_transfer הוא true, הפעולה מעבירה נתונים מארח. אחרת, הוא מעביר נתונים ממכשיר אחר. המשמעות היא מוגדר מראש. הסימון הזה משכפל את המידע שסופק channel_type, לכן בעתיד אנחנו מתכננים לשמור רק אחד מהם (#666).

results מכיל ערכים של מטען ייעודי (payload) שקודם כל ואסימון שמגיע האחרון. בעתיד, אנחנו מתכננים לפצל את המטען הייעודי (payload) ואת האסימון לשניים כדי ליצור פלט ברור יותר (#670).

קלט

תווית שם סוג מגבלות
(I1) token token (C4)
(I2) channel_id קבוע מסוג si64
(I3) channel_type טיפוסים בני מנייה (enum) של DEVICE_TO_DEVICE ו-HOST_TO_DEVICE (C1)
(I4) is_host_transfer קבוע מסוג i1 (C1)

פלט

שם סוג מגבלות
results מספר משתנה של טנזורים, טנזורים כמותיים או אסימונים (C2-C4)

מגבלות

  • (C1) channel_type מוגדר כך:
    • HOST_TO_DEVICE אם is_host_transfer = true,
    • אחרת, DEVICE_TO_DEVICE.
  • (C2) 0 < size(results).
  • (C3) is_empty(result[:-1]) או is_tensor(type(results[:-1])).
  • (C4) is_token(type(results[-1])).

דוגמאות

%results0, %results1 = "stablehlo.recv"(%token) {
  channel_handle = #stablehlo.channel_handle<handle = 1, type = 3>,
  is_host_transfer = true
} : (!stablehlo.token) -> (tensor<2x2xi64>, !stablehlo.token)

דוגמאות נוספות

הקטנה

סמנטיקה

חלה פונקציית הפחתה body על inputs ו-init_values לאורך dimensions ומפיק results טנסטורים.

סדר ההפחתה מוגדר ביישום, כלומר body וגם init_values חייב ליצור חד-חמצני כדי להבטיח שהפעולה מייצרת את תוצאות זהות לכל ערכי הקלט בכל ההטמעה. אבל התנאי הזה לא רלוונטי להנחות פופולריות רבות. לדוגמה חיבור נקודה צפה (floating-point) body ואפס עבור init_values לא יוצרים למעשה חד-משמעית כי חיבור נקודה צפה (floating-point) אינו אסוציאטיבי.

באופן רשמי יותר, 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 מספר משתנה של טנסטורים דו-ממדיים (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 לסוג אחר של נקודה צפה (floating-point) שמשתמש ב-exponent_bits וב-mantissa_bits וחוזר למקור מסוג נקודה צפה (floating-point) ומפיק טנזור 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 t e n s o r f l o w או t e n s o r f l o w, (C1)
(I2) exponent_bits קבוע מסוג si32 (C2)
(I3) mantissa_bits קבוע מסוג si32 (C3)

פלט

שם סוג מגבלות
output t e n s o r f l o w או t e n s o r f l o w, (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) אם channel_id <= 0 and use_global_device_ids = false.
  • cross_replica_and_partition(replica_groups) אם channel_id > 0 and use_global_device_ids = false.
  • flattened_ids(replica_groups) אם channel_id > 0 and use_global_device_ids = true.

לאחר מכן, בכל process_group:

  • reduced_value = all_reduce(operand, replica_groups, channel_id, use_global_device_ids, computation).
  • parts@sender = split(reduced_value@sender, dim(process_groups, 1), scatter_dimension).
  • result@receiver = parts@sender[receiver_index] לכל ה-sender ב- process_group, כאשר receiver_index = process_group.index(receiver).

קלט

תווית שם סוג מגבלות
(I1) operand טנזור או לכל טנזור קוונטי (C1), (C2), (C7), (C8)
(I2) scatter_dimension קבוע מסוג si64 (C1), (C2), (C8)
(I3) replica_groups קבוע דו-ממדי של img_tensor מסוג 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) (ראו צמצום) כאשר:

  • 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 מספר משתנה של טנסטורים דו-ממדיים (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 קבוע דו-ממדי של img_tensor מסוג 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.
  • למספרים מרוכבים: TBD (#997).
  • בסוגים כמותיים:
    • dequantize_op_quantize(remainder, lhs, rhs, type(result)).

בסוגי אלמנטים עם נקודה צפה (floating-point), הפעולה הזו הפוכה פעולת remainder ממפרט IEEE-754 כאשר d הוא ערך אינטגרלי הקרוב ביותר לערך המדויק של lhs/rhs עם קשרים ל-זוגי.

קלט

תווית שם סוג מגבלות
(I1) lhs t e n s o r f l o w, t e n s o r f l o w, או t e tensor quantor, (C1)
(I2) rhs t e n s o r f l o w, t e n s o r f l o w, או t e tensor quantor, (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, t e n s o r f l o w, או t e tensor quantor, (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 Tentor ל-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 t e n s o r f l o w, או t e n s o r f l o w, (C1-C3)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, (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 אם d ב-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_distribution טנזור result של צורה נתונה shape.

אם הערך שלו הוא rng_distribution = UNIFORM, נוצרים מספרים אקראיים. אחרי ההתפלגות האחידה בקטע [a, b). אם a >= b, ההתנהגות לא מוגדרת.

אם הערך שלו הוא rng_distribution = NORMAL, נוצרים מספרים אקראיים. אחרי ההתפלגות הנורמלית עם ממוצע = a וסטיית תקן = b. אם הערך שלו הוא b < 0, ההתנהגות לא מוגדרת.

הדרך המדויקת שבה נוצרים מספרים אקראיים מוגדרת ביישום. עבור הם עשויים להיות דטרמיניסטיים וייתכן שלא, והם עשויים להשתמש למצב סמוי.

בשיחות עם בעלי עניין רבים, הפעולה הזו הוכיחה את עצמה ולכן בעתיד אנחנו מתכננים להסיר אותו (#597).

קלט

תווית שם סוג מגבלות
(I1) a טנזור 0-ממדי של מספר שלם, בוליאני או סוג נקודה צפה (floating-point) (C1), (C2)
(I2) b טנזור 0-ממדי של מספר שלם, בוליאני או סוג נקודה צפה (floating-point) (C1), (C2)
(I3) shape קבוע מפריד חד-ממדי מסוג si64 (C3)
(I4) rng_distribution טיפוסים בני מנייה (enum) של UNIFORM ו-NORMAL (C2)

פלט

שם סוג מגבלות
result Tensor של מספר שלם, בוליאני או סוג נקודה צפה (floating-point) (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. מספרים אקראיים מקבילים: קל ליצור את המספרים 1, 2, 3.

קלט

תווית שם סוג מגבלות
(I1) rng_algorithm טיפוסים בני מנייה (enum) של DEFAULT, THREE_FRY, ו-PHILOX (C2)
(I2) initial_state זווית חד-ממדית אחת מסוג ui64 (C1), (C2)

פלט

שם סוג מגבלות
output_state זווית חד-ממדית אחת מסוג ui64 (C1)
output Tensor של מספר שלם או מסוג נקודה צפה (floating-point)

מגבלות

  • (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 t e n s o r f l o w או t e n s o r f l o w, (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w או t e n s o r f l o w, (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 את Tensor. יישום הפעולה roundToIntegralTiesToEven מ-IEEE-754 למפרט. בסוגים כמותיים, הפונקציה dequantize_op_quantize(round_nearest_even, operand, type(result))

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w או t e n s o r f l o w, (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w או t e n s o r f l o w, (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

סמנטיקה

מבצע פעולת שורש ריבועית הדדית ברמת הרכיב ב-Tensor operand וב- מפיק את הפרמטר result. בהתאם לסוג הרכיב, הפעולות הבאות:

  • לצפים: rSqrt מ-IEEE-754.
  • למספרים מרוכבים: שורש ריבועי מורכב הפוך.
  • לסוגים הבאים לפי כמות: dequantize_op_quantize(rsqrt, operand, type(result)).

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (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 Tensor מסוג מספר שלם (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]]
//           ]
//          ]
%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 או Tensor 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 Tensor מסוג 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 t e n s o r f l o w.

בתרשים הבא מוצג האופן שבו מחושבים יסודות ב-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 (ראו צמצום) לא כולל ערכי אתחול. כרגע זה לא צוין מה קורה אם החלון המתאים לא מכיל ערכים (#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 t e n s o r f l o w, או t e וגם tensor tensor זרוע t (C3)
(I4) window_dimensions קבוע מפריד חד-ממדי מסוג si64 (C2), (C4), (C5)
(I5) window_strides קבוע מפריד חד-ממדי מסוג si64 (C2), (C6), (C7)
(I6) padding קבוע דו-ממדי של img_tensor מסוג 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 ומפיקה אסימון result.

אם הערך של is_host_transfer הוא true, הפעולה מעבירה נתונים מארח. אחרת, הוא מעביר את הנתונים למכשיר אחר. המשמעות היא מוגדר מראש. הדגל הזה משכפל את המידע שסופק channel_type, לכן בעתיד אנחנו מתכננים לשמור רק אחד מהם (#666).

קלט

תווית שם סוג מגבלות
(I1) inputs מספר משתנה של טנזורים או טנזורים כמותיים
(I2) token token
(I3) channel_id קבוע מסוג si64
(I4) channel_type טיפוסים בני מנייה (enum) של DEVICE_TO_DEVICE ו-DEVICE_TO_HOST (C1)
(I5) is_host_transfer קבוע מסוג i1 (C1)

פלט

שם סוג
result token

מגבלות

  • (C1) channel_type מוגדר כך:
    • DEVICE_TO_HOST אם is_host_transfer = true,
    • אחרת, DEVICE_TO_DEVICE.

דוגמאות

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

דוגמאות נוספות

shift_left

סמנטיקה

מבצעת פעולת הזזה שמאלה ברמת הרכיבים בטינזור lhs לפי מספר rhs של ביטים ומפיק טנזור result.

קלט

תווית שם סוג מגבלות
(I1) lhs Tensor מסוג מספר שלם (C1)
(I2) rhs Tensor מסוג מספר שלם (C1)

פלט

שם סוג מגבלות
result Tensor מסוג מספר שלם (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

סמנטיקה

מבצע פעולת אריתמטית ימינה ברמת הרכיבים ב-Tensor lhs, מספר rhs ביטים ומפיק טנזור result.

קלט

תווית שם סוג מגבלות
(I1) lhs Tensor מסוג מספר שלם (C1)
(I2) rhs Tensor מסוג מספר שלם (C1)

פלט

שם סוג מגבלות
result Tensor מסוג מספר שלם (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

סמנטיקה

מבצע פעולה לוגית של הזזה ימינה לוגית ברמת הרכיבים ב-Tensor lhs על ידי rhs מספר הביטים ויוצר טנזור result.

קלט

תווית שם סוג מגבלות
(I1) lhs Tensor מסוג מספר שלם (C1)
(I2) rhs Tensor מסוג מספר שלם (C1)

פלט

שם סוג מגבלות
result Tensor מסוג מספר שלם (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 Tensor של מספר שלם חתום, נקודה צפה (float-point) או סוג מרוכב, או t e n s o r f l o w (C1)

פלט

שם סוג מגבלות
result Tensor של מספר שלם חתום, נקודה צפה (float-point) או סוג מרוכב, או t e n s o r f l o w (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 את Tensor. בהתאם לסוג הרכיב, הפעולות הבאות:

  • לצפים: sin מ-IEEE-754.
  • למספרים מרוכבים: סינוס מרוכב.
  • לסוגים הבאים לפי כמות: dequantize_op_quantize(sine, operand, type(result)).

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (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

סמנטיקה

מבצעת פעולת שורש ריבועית ברמת הרכיב ב-Tensor operand ומפיקה result t e n s o r f l o w. בהתאם לסוג הרכיב, הפעולות הבאות:

  • לצפים: squareRoot מ-IEEE-754.
  • למספרים מרוכבים: שורש ריבועי מורכב.
  • לסוגים הבאים לפי כמות: dequantize_op_quantize(sqrt, operand, type(result)).

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (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 t e n s o r f l o w, t e n s o r f l o w, או t e n s o r f l o w, (C1)
(I2) rhs t e n s o r f l o w, t e n s o r f l o w, או t e n s o r f l o w, (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, t e n s o r f l o w, או t e n s o r f l o w, (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 t e n s o r f l o w. בהתאם לסוג הרכיב, הפעולות הבאות:

  • לצפים: tan מ-IEEE-754.
  • למספרים מרוכבים: טנגנס מרוכב.
  • לסוגים הבאים לפי כמות: dequantize_op_quantize(tan, operand, type(result)).

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (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]
//          ]

דוגמאות נוספות

טאן

סמנטיקה

מבצע פעולה היפרבולי טנגנס היפרבולית מבחינת רכיבים על טנגנס operand ו מפיק את הפרמטר result. בהתאם לסוג הרכיב, הפעולות הבאות:

  • לצפים: tanh מ-IEEE-754.
  • למספרים מרוכבים: טנגנס היפרבולי מרוכב.
  • בסוגים כמותיים:
    • dequantize_op_quantize(tanh, operand, type(result)).

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (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 tensor באמצעות permutation ומפיקה טנזור result. יותר רשמי: result[result_index] = operand[operand_index] כאשר result_index[d] = operand_index[permutation[d]].

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w, או t e n s o r f l o w, (C1-C4)
(I2) permutation קבוע מפריד חד-ממדי מסוג si64 (C2-C4)

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, (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 t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1-C3)
(I2) b t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (C1-C4)
(I3) left_side קבוע מסוג i1 (C3)
(I4) lower קבוע מסוג i1
(I5) unit_diagonal קבוע מסוג i1
(I6) transpose_a טיפוסים בני מנייה (enum) של NO_TRANSPOSE, TRANSPOSE, ו-ADJOINT

פלט

שם סוג מגבלות
result t e n s o r f l o w, או t e n s o r f l o w, או t e tensor tenor (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 טמפל (C1)

מגבלות

  • (C1) result מסוג tuple<E0, ..., EN-1> כאשר Ei = type(val[i]).

דוגמאות

// %val0: [1.0, 2.0]
// %val1: (3)
%result = "stablehlo.tuple"(%val0, %val1) : (tensor<2xf32>, tuple<tensor<i32>>) -> tuple<tensor<2xf32>, tuple<tensor<i32>>>
// %result: ([1.0, 2.0], (3))

דוגמאות נוספות

uniform_dequantize

סמנטיקה

מבצע המרה ברמת הרכיבים של טנזור כמותי operand ל- Tensor נקודה צפה result בהתאם לפרמטרים של הקונטיזציה שהוגדרו מסוג operand.

יותר רשמי: result = dequantize(operand).

קלט

תווית שם סוג מגבלות
(I1) operand t e n s o r f l o w (C1), (C2)

פלט

שם סוג מגבלות
result Tensor מסוג נקודה צפה (floating-point) (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

סמנטיקה

מבצעת המרה ברמת הרכיבים של טינזור נקודה צפה (floating-point) או טנזור כמותי 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 Tensor מסוג נקודה צפה (floating-point) או סוג כמותי (C1), (C2)

פלט

שם סוג מגבלות
result t e n s o r f l o w (C1), (C2)

מגבלות

  • (C1) shape(operand) = shape(result).
  • (C2) expressed_type(result) = is_float(operand) ? element_type(operand) : expressed_type(operand).

דוגמאות

// %operand: [4.0, 15.0]
%result = "stablehlo.uniform_quantize"(%operand) : (tensor<2xf32>) -> tensor<2x!quant.uniform<i8:f32:0, {0.1:-30,0.5:-20}>>
// %result: [10, 10]

// %operand: [10, 10]
%result = "stablehlo.uniform_quantize"(%operand) : (tensor<2x!quant.uniform<i8:f32:0, {0.1:-30,0.5:-20}>>) -> tensor<2x!quant.uniform<i8:f32:0, {0.1:-20,0.2:-30}>>
// %result: [20, 45]

בזמן

סמנטיקה

מפיקה את הפלט מהפעלת הפונקציה body 0 פעמים או יותר בזמן הפלט של הפונקציה cond הוא true. בצורה רשמית יותר, אפשר לבטא את הסמנטיקה באמצעות תחביר Python באופן הבא:

internal_state = operand
while cond(*internal_state):
  internal_state = body(*internal_state)
results = internal_state

טרם נקבעה ההתנהגות של לולאה אינסופית (#383).

קלט

תווית שם סוג מגבלות
(I1) operand מספר משתנה של טנזורים, טנזורים כמותיים או אסימונים (C1-C3)
(I2) cond פונקציה (C1)
(I3) body פונקציה (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 את Tensor. בהתאם לסוג הרכיב, הפעולות הבאות:

  • לערך בוליאני: XOR לוגי.
  • למספרים שלמים: XOR ברמת הסיביות.

קלט

תווית שם סוג מגבלות
(I1) lhs tensor מסוג בוליאני או מספר שלם (C1)
(I2) rhs tensor מסוג בוליאני או מספר שלם (C1)

פלט

שם סוג מגבלות
result tensor מסוג בוליאני או מספר שלם (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 ב-upstream עבור ModuleOp, FuncOp, CallOp ו- החזרה עשינו זאת כדי לשפר את יכולת הפעולה ההדדית עם מכונות MLIR קיימות, כרטיסים שימושיים כתובים שמטורגטים ל-FuncOp ול-ModuleOp, ויש הרבה אוספים צינורות עיבוד נתונים מצפים לתפעול כזה. אחריות מלאה לתאימות היא שהוחלו על הפעולות האלה. אם משהו ישתנה לגבי הפעולות האלה בדרך שלא תואמת (כלומר הסרה), נוסיף מקבילה של StableHLO כדי לשמור בתאימות מלאה.

CHLO

באובייקט CHLO יש פעולות ברמה גבוהה יותר שמפרקות ל-StableHLO. בשלב הזה אין התחייבויות לתאימות ל-CHLO. לתאימות באחריות, האישור של chlo-legalize-to-stablehlo לפני ביצוע סריאליזציה.

פעולות על צורות

זהו תרחיש לדוגמה נפוץ בקהילה של שימוש בפעולות ליבה מסוימות ניבים של MLIR בתוכנות StableHLO דינמיות לביצוע חישובי צורות. לרוב נכללים בכך ניב אחד (shape) פעולות כמו shape_of או num_elements, ניב של tensor פעולות כמו dim או from_elements, וסוג index המובנה.

The Dynamism 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 ומחשוב ערכי פלט. ערכי פלט של פונקציה מחושבים על ידי הרצת תרשים של פעולות שעברו תהליך רוט (Root) בפעולת return המתאימה.

סדר הביצוע מוגדר ליישום כל עוד הוא תואם dataflow, כלומר אם פעולות מבוצעות לפני השימושים שלהן. ב-StableHLO, כל פעולות שמשפיעות על הצד השלישי צורכות אסימון אחד ומפיקות אסימון אחד (מספר אסימונים יכולים יוכפל לאסימון אחד דרך after_all), כך שסדר הביצוע של הצד האפקטים תואמים גם הם ל-dataflow. לדוגמה, בתוכנית הבאה יש שתי הזמנות ביצוע אפשריות: %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 כמותיות עשויה להשתנות בהתאם דרישות ויכולות חומרה. לדוגמה, חלק מהציוד עשוי לבחור פרשו פעולות כמותיות באמצעות פונקציית "מפענחים", מבצעים נקודה צפה (floating-point) ולבסוף לכמת" את האסטרטגיה שלנו. אחרים יכולים לבצע את כל הפעולות פעולות חישוביות במספרים שלמים. כתוצאה מכך, הפרשנות של פעולות StableHLO כמותיות נקבעות אך ורק על ידי יישום בפועל. פירוש של קוונטיזציה היברידית (#1575) צריכים להתבסס על את הסמנטיקה כפי שמתואר במפרט (דרך 1792).

שגיאות

תוכניות StableHLO מאומתות באמצעות מערכת אילוצים מקיפה כל אחת מהפעולות, שפוסלות סוגים רבים של שגיאות לפני זמן הריצה. עם זאת, עדיין אפשר להציג תנאים של שגיאות, למשל: דרך אפשרויות נוספות של מספרים שלמים, גישות מחוץ לטווח וכו'. אלא אם צוין אחרת במפורש, כל השגיאות האלה עשויות להוביל להתנהגות מוגדרת על ידי הטמעה, אבל זה עשוי להשתנות העתידי (#1157).

חריגים ברמה של נקודה צפה (floating-point)

יוצאי דופן לכלל הזה, חריגים מסוג נקודה צפה (floating-point) בתוכניות 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, ויקיפדיה), עם שני שינויים: 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...).

ב) בנוסף, הנוסחאות האלה תומכות בשלוש נקודות (...) שהופכות ביטויים סקלריים לביטויים של tenor. בקיצור, f(xs...) פירושו פחות או יותר "עבור כל סקלר x במקצב xs, מחשבים סקלר f(x) ואז מחזירים את כל את התוצאות הסקלריות האלה יחד כתוצאה של img_tensor". בתחביר וניל 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 opset לא תומך בשידור מרומז, הנוסחאות כן, גם לצורך תמציתיות. בקיצור, אם סקלר משמש בהקשר שבו צפוי טנזור, הסקלרי משודר אל את הצורה הרצויה.

כדי להמשיך את הדוגמה 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 מסוימת, צריכים להיות t tensor של בוליאני. כשמשתמשים בנוסחאות כמגבלות, אילוץ מחזיק אם הנוסחה שווה ל-true או ל-Tensor יש רק רכיבי true.

שמות

בנוסחאות, ההיקף המילוני כולל: 1) פונקציות גלובליות, 2) הגדרות של משתמשים,

3) הגדרות מקומיות. רשימת הפונקציות הגלובליות מוצגת בהמשך. הרשימה של הגדרות הרכיבים תלוי ברכיב התוכנית שהסימון חל על:

  • לפעולות, הגדרות החברים כוללות שמות שנוספו ל'קלט' וגם 'פלטים' .
  • לגבי כל דבר אחר, הגדרות החברים כוללות חלקים מבניים של של רכיב תוכנת, על שם רכיב ה-EBNF התואם שאינו מסוף. רוב הזמן, השמות של החלקים המבניים האלה מתקבלים על-ידי המרת שמות של פריטים שאינם טרמינלים לאותיות נחש (למשל 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. לעומת זאת, העמודה 'מגבלות' של פעולות (ופעולות שוות ערך) מגדירות "compile-time" לוגיקה, כלומר משהו שמבוצע בדרך כלל לפני סביבת זמן הריצה, כך שרק קלט קבוע זמינים בתור 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 מוגדר בסוגי t e n s o r f l o w, t e n s o l o w, מחזירה, בהתאמה, את הערך 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 הוא ערך או placeholder, הפונקציה הזו היא קיצור דרך של 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 הוא ערך או placeholder, הפונקציה הזו היא קיצור דרך של member_name(type(x)). אם x הוא לא מסוג שכולל חבר מועדון מתאים, או ערך או placeholder מסוג כזה, הפונקציה מחזירה None.

  • הפונקציה is_empty_algorithm(*args: Type) בודקת אם כל שדות האלגוריתם של הנקודות מוגדרים אל None. הפעולה הזו נדרשת כי לאלגוריתמים של נקודות יש הטמעה מוגדרת וברירת המחדל של התנהגויות ברירת המחדל, לכן ציון ערך ברירת מחדל יהיה שגוי.

יצירת ערכים

  • operation_name(*xs: Value | Type) -> Value זמין לכל הפעולות. לדוגמה, add(lhs, rhs) לוקחת שני ערכי טנזור lhs ו-rhs מחזירה את הפלט של הערכה של הפעולה add עם ערכי הקלט האלה. עבור פעולות מסוימות, למשל: broadcast_in_dim, סוגי הפלט שלהם "load-bearing" (נושא עומס), כלומר נדרש כדי להעריך פעולה. במקרה הזה, הפונקציה לוקחת את הסוגים האלה כארגומנטים.

פונקציות על ערכים

  • כל הפונקציות והאופרטורים של Python זמינים. לדוגמה שניהם מינוי ופילוח אפשר להוסיף סימונים מ-Python ליצירת אינדקס לטינורים, צמדים.

  • תאריך ההגדרה של to_destination_type(x: Value, destination_type: Type) -> Value של tensors, ומחזירה את הערך המומר של x על סמך type(x) destination_type ככה:

def to_destination_type(x: Value, destination_type: Type) -> Value:
  if type(x) == destination_type:
    return x

  if is_quantized(destination_type):
    if is_quantized(type(x)):
      return quantize(x, destination_type)
    assert is_float(type(x))
    return quantize(x, destination_type)

  if is_quantized(type(x)):
    assert destination_type = expressed_type(type(x))
    return dequantize(type(x))

  return convert(x, destination_type)

קיים דיון מוקדם לגבי המיזוג של convert, uniform_quantize ועם פעולות uniform_dequantize (#1576). אחרי המיזוג אין צורך בפונקציה שלמעלה, ואנחנו יכולים להשתמש בשם הפעולה עבור convert במקום זאת.

  • הפונקציה is_nan(x: Value) -> Value מוגדרת בטנזטורים ומחזירה את הערך true אם כל הרכיבים של x הם NaN או false, אחרת. אם x הוא לא טנזור, מחזירה None.

  • הפונקציה is_sorted(x: Value) -> Value מוגדרת בטנזטורים ומחזירה את הערך true אם רכיבי x ממוינים בסדר עולה ביחס סדר לקסיקוגרפי של האינדקסים שלהם, או false, אחרת. אם x אינו tensor, הפונקציה מחזירה את 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 של tensors, ומחזירה num_results פרוסות של x לאורך הציר axis. אם x הוא לא t n s או 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 הוא לא סוג של t e n s o r f l o, t e n s o r f l o w או ערך או placeholder מאחד מהסוגים האלה, הפונקציה מחזירה None.

  • rank(x: Value | Placeholder | Type) -> Value הוא קיצור דרך עבור size(shape(x)).

  • הפונקציה shape(x: Value | Placeholder | Type) -> Value מוגדרת בקטע Functions ב'סוגים' דרך 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 מוגדר בסוגי t e n s o r f l o w, t e n s o l o w, הופכת אותם ל"קו בסיס", כלומר טיפוס עם אותה צורה אבל עם פרמטרי הקוונטיזציה של סוג הרכיב אופסו לערכי ברירת המחדל. הדבר משמש כטריק שימושי להשוואה בין סוגים של tenor ו-tensor בכמות גדולה. באופן אחיד, שנדרש לעיתים קרובות. במקרה של סוגים כמותיים, הפונקציה השוואת סוגים תוך התעלמות מפרמטרים של הקוונטיזציה, כלומר 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 מוגדרת בסוגים של img_tensor t, והופכת אותם סוגים של Tensor נקודה צפה (floating-point). הדבר קורה באמצעות המרת רכיבים כמותיים שמייצגים ערכים שלמים מסוג האחסון ערכי נקודה צפה (floating-point) מהסוג המבוטא באמצעות נקודת האפס וסקל שמשויך לסוג הרכיב הכמותי.
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 מוגדר בסוגי img_tensor נקודה צפה (floating-point) והופך אותם סוגי img_tensor. הפעולה הזו מתבצעת על ידי המרה של ערכים מסוג נקודה צפה (floating-point) מהסוג המבוטא לערכים של מספרים שלמים תואמים של סוג האחסון באמצעות נקודת האפס וקנה המידה שמשויכים לסוג הרכיב הכמותי.
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 משמש לציון כימות משקל בלבד עבור פעולה היברידית שמקבלת HMAC בנקודה צפה (floating-point) ו-rhs בטיפוסים כמותיים. הוא מפרק קלטים כמותיים לסוגים המפורשים שלהם, ומבצע חישוב לצוף. סוג הרכיב של חיישן float מסוג floating ומבוטא סוג של rhs כמותיים Tensor צריך להיות זהה.
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 לצפייה &quot;cross_replica_and_partition&quot; שלמעלה.

  • flattened_ids(replica_groups: Value) -> Value לצפייה ב-"flattened_id" שלמעלה.

דינמיות

ערכי 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>. אם אופרנד הצורה קבוע, אפשר לאמת באופן סטטי. אם צורת התוצאה היא דינמית לחלוטין, לא יכול להיות אי-התאמה.