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)
.
- (C12)
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
, כולל. - סוגים של נקודות צפות יכולים להיות אחד מהבאים:
- הסוגים של
f8E4M3FN
ו-f8E5M2
שתואמים לסוג הקידודיםE4M3
ו-E5M2
של פורמט FP8 שמתוארים ב: פורמטי FP8 ללמידה עמוקה (Deep Learning). - סוגי
f8E4M3FNUZ
וf8E5M2FNUZ
שתואמים לE4M3
ולE5M2
של פורמטי FP8 שמתוארים פורמטים מספריים של 8 ביט לרשתות נוירונים עמוקות - סוג
f8E4M3B11FNUZ
שתואם לקידודE4M3
של פורמטי FP8 מתואר ב: אימון של נקודה צפה (HFP8) היברידית והסקת מסקנות לגבי רשתות נוירונים עמוקות - סוג
bf16
שתואם לפורמטbfloat16
שמתואר ב: BFloat16: הסוד לביצועים גבוהים במעבדי Cloud TPU. - הסוגים של
f16
,f32
ו-f64
תואמים בהתאמהbinary16
('half precision'),binary32
('דיוק יחיד') וגם הפורמטיםbinary64
(דיוק כפול) שמתוארים בפורמט תקן IEEE 754. - סוג
tf32
תואם לפורמט TensorFloat32 והיא מספקת תמיכה מוגבלת ב-StableHLO.
- הסוגים של
- סוגים מורכבים מייצגים ערכים מורכבים שיש להם חלק ממשי
וחלק דמיוני מאותו סוג רכיב. מתחם נתמך
הסוגים הם
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)
.
- (C1)
- אם הפעולה משתמשת במערכים כמותיים:
- (C2)
is_quantized(lhs) and is_quantized(rhs) and is_quantized(result)
. - (C3)
storage_type(lhs) = storage_type(rhs) = storage_type(result)
. - (C4)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
. - (C5)
(is_per_axis_quantized(lhs) or is_per_axis_quantized(rhs)) = is_per_axis_quantized(result)
. - (C6) אם
is_per_axis_quantized(lhs)
, אזquantization_dimension(lhs) = quantization_dimension(result)
. - (C7) אם
is_per_axis_quantized(rhs)
, אזquantization_dimension(rhs) = quantization_dimension(result)
.
- (C2)
דוגמאות
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.add"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[6, 8], [10, 12]]
after_all
סמנטיקה
מבטיחה שהפעולות שמפיקות את inputs
יבוצעו לפני
פעולות שתלויות ב-result
. ביצוע הפעולה הזו לא עושה דבר,
קיים רק כדי ליצור יחסי תלות של נתונים מ-result
עד inputs
.
קלט
תווית | שם | סוג |
---|---|---|
(I1) | inputs |
מספר ושונות של token |
פלט
שם | סוג |
---|---|
result |
token |
דוגמאות
// %input0: !stablehlo.token
// %input1: !stablehlo.token
%result = "stablehlo.after_all"(%input0, %input1) : (!stablehlo.token, !stablehlo.token) -> !stablehlo.token
all_gather
סמנטיקה
בכל קבוצת תהליכים ברשת התהליכים StableHLO, משרשרת הערכים
של הטנזור operands
מכל תהליך לאורך all_gather_dim
ומפיק
results
רכיבי 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
סמנטיקה
בתוך כל קבוצת תהליכים ברשת התהליכים 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]
, כאשר:
id = d0 + ... + dk-1 + kd
.d
שווה ל-dimension
, ו-d0
, ... הם גדלי המימד הd
מתוךinputs
.
קלט
תווית | שם | סוג | מגבלות |
---|---|---|---|
(I1) | inputs |
מספר משתנה של טנסטורים או טנזורים כמותיים לכל טנזור | (C1-C6) |
(I2) | dimension |
קבוע מסוג si64 |
(C2), (C4), (C6) |
פלט
שם | סוג | מגבלות |
---|---|---|
result |
טנזור או לכל טנזור קוונטי | (C5-C6) |
מגבלות
- (C1)
same(element_type(inputs...))
. - (C2)
same(shape(inputs...))
מלבדdim(inputs..., dimension)
. - (C3)
0 < size(inputs)
. - (C4)
0 <= dimension < rank(inputs[0])
. - (C5)
element_type(result) = element_type(inputs[0])
. - (C6)
shape(result) = shape(inputs[0])
מלבד:dim(result, dimension) = dim(inputs[0], dimension) + ...
.
דוגמאות
// %input0: [[1, 2], [3, 4], [5, 6]]
// %input1: [[7, 8]]
%result = "stablehlo.concatenate"(%input0, %input1) {
dimension = 0 : i64
} : (tensor<3x2xi64>, tensor<1x2xi64>) -> tensor<4x2xi64>
// %result: [[1, 2], [3, 4], [5, 6], [7, 8]]
קבוע
סמנטיקה
מפיקה טנזור output
מקבוע value
.
קלט
תווית | שם | סוג | מגבלות |
---|---|---|---|
(I1) | value |
קבוע | (C1) |
פלט
שם | סוג | מגבלות |
---|---|---|
output |
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)
.
- (C27)
- אם הפעולה משתמשת במערכים כמותיים:
- (C28)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
. - (C29) אם
is_per_axis_quantized(rhs)
, ואזquantization_dimension(rhs) = kernel_output_feature_dimension
. - (C30) אם
is_per_axis_quantized(result)
, אזquantization_dimension(result) = output_feature_dimension
. - אם
is_quantized(lhs)
: - (C31)
storage_type(lhs) = storage_type(rhs)
. - (C32)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
. - (C33) אם
is_per_tensor_quantized(rhs)
, אזis_per_tensor_quantized(result)
. - אם
!is_quantized(lhs)
: - (C34)
element_type(lhs) = expressed_type(rhs) = element_type(result)
.
- (C28)
דוגמאות
// %lhs: [[
// [
// [1], [2], [5], [6]
// ],
// [
// [3], [4], [7], [8]
// ],
// [
// [10], [11], [14], [15]
// ],
// [
// [12], [13], [16], [17]
// ]
// ]]
//
// %rhs: [
// [[[1]], [[1]], [[1]]],
// [[[1]], [[1]], [[1]]],
// [[[1]], [[1]], [[1]]]
// ]
%result = "stablehlo.convolution"(%lhs, %rhs) {
window_strides = array<i64: 4, 4>,
padding = dense<0> : tensor<2x2xi64>,
lhs_dilation = array<i64: 2, 2>,
rhs_dilation = array<i64: 1, 1>,
window_reversal = array<i1: false, false>,
// In the StableHLO dialect, dimension numbers are encoded via:
// `[<input dimensions>]x[<kernel dimensions>]->[output dimensions]`.
// "b" is batch dimension, "f" is feature dimension,
// "i" is input feature dimension, "o" is output feature dimension,
// "0/1/etc" are spatial dimensions.
dimension_numbers = #stablehlo.conv<[b, 0, 1, f]x[0, 1, i, o]->[b, 0, 1, f]>,
batch_group_count = 1 : i64,
feature_group_count = 1 : i64,
precision_config = [#stablehlo<precision DEFAULT>, #stablehlo<precision DEFAULT>]
} : (tensor<1x4x4x1xi64>, tensor<3x3x1x1xi64>) -> tensor<1x2x2x1xi64>
// %result: [[
// [[10], [26]],
// [[46], [62]]
// ]]
קוסינוס
סמנטיקה
מבצעת פעולת קוסינוס מבוססת-רכיבים על טנזור operand
ומפיקה
result
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)
.
- (C13)
- אם הפעולה משתמשת במערכים כמותיים:
- (C14)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
. - (C15)
zero_points(rhs) = 0
. - (C16) אם
is_per_axis_quantized(rhs)
, אזquantization_dimension(rhs)
לא בrhs_contracting_dimensions
. - אם
is_quantized(lhs)
: - (C17)
storage_type(lhs) = storage_type(rhs)
. - (C18)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
. - (C19) אם
is_per_tensor_quantized(rhs)
, אזis_per_tensor_quantized(result)
. - אם
!is_quantized(lhs)
: - (C20)
element_type(lhs) = expressed_type(rhs) = element_type(result)
.
- (C14)
- אם
!is_empty_algorithm(lhs_precision_type, rhs_precision_type, accumulation_type, lhs_component_count, rhs_component_count, num_primitive_operations allow_imprecise_accumulation)
:- (C21)
precision_config... = DEFAULT
. - (C22)
0 < lhs_component_count
. - (C23)
0 < rhs_component_count
. - (C24)
0 < num_primitive_operations
.
- (C21)
דוגמאות
// %lhs: [
// [[1, 2],
// [3, 4]],
// [[5, 6],
// [7, 8]]
// ]
// %rhs: [
// [[1, 0],
// [0, 1]],
// [[1, 0],
// [0, 1]]
// ]
%result = "stablehlo.dot_general"(%lhs, %rhs) {
dot_dimension_numbers = #stablehlo.dot<
lhs_batching_dimensions = [0],
rhs_batching_dimensions = [0],
lhs_contracting_dimensions = [2],
rhs_contracting_dimensions = [1]
>,
precision_config = [#stablehlo<precision DEFAULT>, #stablehlo<precision DEFAULT>],
algorithm = #stablehlo.dot_algorithm<
lhs_precision_type = tf32,
rhs_precision_type = tf32,
accumulation_type = f32,
lhs_component_count = 1,
rhs_component_count = 1,
num_primitive_operations = 1,
allow_imprecise_accumulation = false
>
} : (tensor<2x2x2xi64>, tensor<2x2x2xi64>) -> tensor<2x2x2xi64>
// %result: [
// [[1, 2],
// [3, 4]],
// [[5, 6],
// [7, 8]]
// ]
dynamic_broadcast_in_dim
סמנטיקה
הפעולה הזו זהה מבחינה פונקציונלית ל-
broadcast_in_dim
אופס, אבל צורת התוצאה מצוינת באופן דינמי דרך output_dimensions
.
הפעולה הזו מקבלת גם את המאפיינים האופציונליים known_expanding_dimensions
, known_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)
.
- (C27)
- אם הפעולה משתמשת במערכים כמותיים:
- (C28)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
. - (C29) אם
is_per_axis_quantized(rhs)
, ואזquantization_dimension(rhs) = kernel_output_feature_dimension
. - (C30) אם
is_per_axis_quantized(result)
, אזquantization_dimension(result) = output_feature_dimension
. - אם
is_quantized(lhs)
: - (C31)
storage_type(lhs) = storage_type(rhs)
. - (C32)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
. - (C33) אם
is_per_tensor_quantized(rhs)
, אזis_per_tensor_quantized(result)
. - אם
!is_quantized(lhs)
: - (C34)
element_type(lhs) = expressed_type(rhs) = element_type(result)
.
- (C28)
דוגמאות
// %lhs: [[
// [[1], [2], [5], [6]],
// [[3], [4], [7], [8]],
// [[10], [11], [14], [15]],
// [[12], [13], [16], [17]]
// ]]
//
// %rhs: [
// [[[1]], [[1]], [[1]]],
// [[[1]], [[1]], [[1]]],
// [[[1]], [[1]], [[1]]]
// ]
// %padding: [[1, 1],
// [1, 1]]
%result = "stablehlo.dynamic_conv"(%lhs, %rhs, %padding) {
window_strides = array<i64: 4, 4>,
lhs_dilation = array<i64: 2, 2>,
rhs_dilation = array<i64: 1, 1>,
window_reversal = array<i1: false, false>,
dimension_numbers = #stablehlo.conv<raw
input_batch_dimension = 0,
input_feature_dimension = 3,
input_spatial_dimensions = [0, 1],
kernel_input_feature_dimension = 2,
kernel_output_feature_dimension = 3,
kernel_spatial_dimensions = [0, 1],
output_batch_dimension = 0,
output_feature_dimension = 3,
output_spatial_dimensions = [1, 2]
>,
feature_group_count = 1 : i64,
batch_group_count = 1 : i64,
precision_config = [#stablehlo<precision DEFAULT>, #stablehlo<precision DEFAULT>]
} : (tensor<1x4x4x1xi64>, tensor<3x3x1x1xi64>, tensor<2x2xi64>) -> tensor<1x2x2x1xi64>
// %result: [[
// [[1], [5]],
// [[10], [14]]
// ]]
dynamic_gather
סמנטיקה
הפעולה הזו זהה מבחינה פונקציונלית ל-
לאסוף
פעולה, כאשר 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
סמנטיקה
בכל קבוצת תהליכים ברשת התהליכים 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...
באמצעות דוגמה קונקרטית.
יותר רשמי,
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
באמצעות דוגמה קונקרטית.
יותר רשמי:
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
← %2
← return
ו
%1
← %0
← %2
← return
.
func.func @main() -> tensor<f64> {
%0 = stablehlo.constant dense<1.0> : tensor<f64>
%1 = stablehlo.constant dense<2.0> : tensor<f64>
%2 = stablehlo.add %0, %1 : tensor<f64>
return %2 : tensor<f64>
}
באופן רשמי יותר, תהליך StableHLO הוא שילוב של:
1) תוכנית StableHLO, 2) סטטוסים של פעולות (עדיין לא בוצעה,
כבר בוצע), ו-3) ערכי הביניים שהתהליך עובד עליהם.
התהליך מתחיל בערכי קלט לפונקציה main
וממשיך עד
תרשים של התפעול שמעדכנת את סטטוסי הפעולות וערכי הביניים
מסיים בערכי פלט. טרם נקבעה הגדרה רשמית נוספת
(#484).
ביצוע במקביל
ניתן להפעיל תוכניות StableHLO במקביל ולארגן אותן ברשת תהליכים דו-ממדית
של num_replicas
על num_partitions
, ששניהם מסוג ui32
.
ברשת התהליכים StableHLO, num_replicas * num_partitions
של StableHLO
מתבצעים בו-זמנית. לכל תהליך יש
process_id = (replica_id, partition_id)
, כאשר
replica_id
בreplica_ids = range(num_replicas)
ו
partition_id
ב-partition_ids = range(num_partitions)
שבהם לשניהם
מקלידים ui32
.
גודל רשת התהליכים ידוע באופן סטטי עבור כל תוכנית (
אנחנו מתכננים להפוך אותו לחלק מפורש מהתוכניות של StableHLO
#650), והמיקום
בתוך רשת התהליך ידועה באופן סטטי לכל תהליך. בכל תהליך יש
גישה למיקום שלו ברשת התהליכים דרך replica_id
וגם
partition_id
פעולות
בתוך רשת התהליכים, כל התוכנות יכולות להיות זהות (בתבנית תוכנית, נתונים מרובים" יכולים להיות שונים (ב'תוכנית מרובה', נתונים מרובים" ) או משהו ביניהם. בעתיד, אנחנו מתכננים כדי להוסיף תמיכה בניבים אחרים של הגדרת תוכניות StableHLO מקבילות, כולל GSPMD (#619).
בתוך רשת התהליכים, התהליכים כמעט בלתי תלויים זה בזה – יש להם סטטוסים נפרדים של פעולה, ערכי קלט/בינוני/פלט נפרדים ורוב הפעולות מבוצעות בנפרד בין תהליכים, למעט מספר קטן של פעולות קולקטיביות שמתוארות בהמשך.
בהינתן שהביצוע של רוב הפעולות משתמש רק בערכים
בדרך כלל אי אפשר להתייחס לערכים האלה לפי השמות שלהם.
עם זאת, כשמתארים סמנטיקה של פעולות קולקטיביות, הן לא מספיקות, וגם
שמובילה לסימון name@process_id
כדי להתייחס לערך name
בתהליך מסוים. (מנקודת מבט זו, name
לא כשיר יכול להיות
נחשב כקיצור של name@(replica_id(), partition_id())
).
סדר הביצוע בכל התהליכים מוגדר על-ידי ההטמעה, מלבד סנכרון באמצעות תקשורת מנקודה לנקודה ותפעול קולקטיבי כמתואר בהמשך.
תקשורת מנקודה לנקודה
תהליכי StableHLO יכולים לתקשר זה עם זה באמצעות
ערוצי StableHLO. ערוץ מיוצג על ידי מזהה חיובי מסוג מסוג
si64
באמצעות פעולות שונות ניתן לשלוח ערכים לערוצים
לקבל אותם מהערוצים.
ניסוח רשמי נוסף, למשל: מאיפה מגיעים מזהי הערוצים האלה, מתווספות למודעות עצמן ומהו סוג הסנכרון טרם נציג אותם, טרם נקבע (#484).
תקשורת בסטרימינג
לכל תהליך StableHLO יש גישה לשני ממשקי סטרימינג:
- מודעות בגוף הפיד שהמערכת יכולה לקרוא מהן.
- פיד חיצוני שאפשר לכתוב אליו.
בשונה מערוצים, שמשמשים לתקשורת בין תהליכים ולכן יש תהליכים בשני הקצוות, פידים ופידים בגוף הפיד מוגדר יישום סופי.
ניסוח רשמי נוסף, למשל: איך תקשורת בסטרימינג משפיעה על ביצוע עדיין טרם נקבע, ואיזה סוג של סנכרון הוא הניב. (#484).
פעולות קולקטיביות
יש שש פעולות קולקטיביות ב-StableHLO: all_gather
, all_reduce
,
all_to_all
, collective_broadcast
, collective_permute
וגם
reduce_scatter
. כל הפעולות האלה פיצלו את התהליכים בתהליך StableHLO
לבצע חלוקה לקבוצות של תהליכים ב-StableHLO, ולבצע חישוב משותף בתוך
כל קבוצת תהליך, בנפרד מקבוצות תהליך אחרות.
בתוך כל קבוצת תהליכים, פעולות קולקטיביות עשויות ליצור סנכרון מחסום כלשהו. ניסוח רשמי נוסף, למשל: על מתי בדיוק סנכרון מתרחש, איך בדיוק התהליכים מגיעים למחסום הזה, ומה יקרה אם לא, טרם נקבע (#484).
אם קבוצת התהליכים כוללת תקשורת בין מחיצות, כלומר
יש תהליכים בקבוצת התהליכים שמזהי המחיצות שלהם שונים, ואז ביצוע
של התפעול הקולקטיבי זקוק לערוץ, והתפעול הקולקטיבי חייב לספק
channel_id
חיובי מסוג si64
. אין צורך בתקשורת חוצת-עותקים
הערוצים שלך.
החישובים שבוצעו על ידי הפעולות הקולקטיביות הם ספציפיים לפעולות בודדות והם מתוארים בקטעי התפעול הנפרדים למעלה. אבל האסטרטגיות האלה שרשת התהליכים מחולקת לקבוצות תהליכים שמשותפות בין הפעולות האלה והם מתוארים בקטע הזה. יותר רשמי, StableHLO תומך ארבע אסטרטגיות אפשריות.
cross_replica
רק תכתובות בין כפילויות מתקיימות בתוך כל קבוצת תהליכים. הזה
האסטרטגיה הזו לוקחת את replica_groups
– רשימה של רשימות של מזהי רפליקות וחישובים
מכפלה קרטזית של replica_groups
של partition_ids
. replica_groups
חייבים להכיל רכיבים ייחודיים ויכסו את כל replica_ids
. באופן רשמי יותר, באמצעות
תחביר Python:
def cross_replica(replica_groups: List[List[ReplicaId]]) -> List[List[ProcessId]]:
for replica_group in replica_groups:
for partition_id in partition_ids:
process_group = []
for replica_id in replica_group:
process_group.append((replica_id, partition_id))
yield process_group
לדוגמה, בשביל replica_groups = [[0, 1], [2, 3]]
ו-num_partitions = 2
,
cross_replica
יפיק
[[(0, 0), (1, 0)], [(0, 1), (1, 1)], [(2, 0), (3, 0)], [(2, 1), (3, 1)]]
cross_partition
רק תקשורת בין מחיצות מתקיימת בתוך כל קבוצת תהליכים. הזה
האסטרטגיה הזו לוקחת את partition_groups
– רשימה של מזהי מחיצות
מחשבת מכפלה קרטזית של partition_groups
לפי replica_ids
.
המאפיין partition_groups
חייב לכלול אלמנטים ייחודיים והוא צריך לכסות את כל partition_ids
.
בצורה רשמית יותר, שימוש בתחביר Python:
def cross_partition(partition_groups: List[List[PartitionId]]) -> List[List[ProcessId]]:
for partition_group in partition_groups:
for replica_id in replica_ids:
process_group = []
for partition_id in partition_group:
process_group.append((replica_id, partition_id))
yield process_group
לדוגמה, בשביל partition_groups = [[0, 1]]
ו-num_replicas = 4
,
cross_partition
יפיק
[[(0, 0), (0, 1)], [(1, 0), (1, 1)], [(2, 0), (2, 1)], [(3, 0), (3, 1)]]
cross_replica_and_partition
עשויה להתרחש גם תקשורת בין-עותקים וגם תקשורת בין מחיצות
קבוצת תהליכים. השיטה הזו נמשכת replica_groups
– רשימת רשימות של
מזהי רפליקות – ומחשבות מוצרים קרטזיים של כל replica_group
על ידי
partition_ids
. replica_groups
חייב לכלול רכיבים ייחודיים ויכסה את כולם
replica_ids
. בצורה רשמית יותר, שימוש בתחביר Python:
def cross_replica_and_partition(replica_groups: List[List[ReplicaId]]) -> List[List[ProcessId]]:
for replica_group in replica_groups:
process_group = []
for partition_id in partition_ids:
for replica_id in replica_group:
process_group.append((replica_id, partition_id))
yield process_group
לדוגמה, בשביל replica_groups = [[0, 1], [2, 3]]
ו-num_partitions = 2
,
cross_replica_and_partition
יפיק
[[(0, 0), (1, 0), (0, 1), (1, 1)], [(2, 0), (3, 0), (2, 1), (3, 1)]]
flattened_ids
השיטה הזו לוקחת flattened_id_groups
– רשימת רשימות של "שטוח"
מזהי תהליכים בתור replica_id * num_partitions + partition_id
- וגם
הופכת אותם למזהי תהליכים. flattened_id_groups
חייבים לכלול רכיבים ייחודיים
ויכסו את כל process_ids
. בצורה רשמית יותר, שימוש בתחביר Python:
def flattened_ids(flattened_id_groups: List[List[ui32]]) -> List[List[ProcessId]]:
for flattened_id_group in flattened_id_groups:
process_group = []
for flattened_id in flattened_id_group:
replica_id = flattened_id // num_partitions
partition_id = flattened_id % num_partitions
process_group.append((replica_id, partition_id))
yield process_group
לדוגמה, בשביל flattened_id_groups = [[0, 1, 2, 3], [4, 5, 6, 7]]
,
num_replicas = 4
ו-num_partitions = 2
, flattened_ids
תפיק
[[(0, 0), (0, 1), (1, 0), (1, 1)], [(2, 0), (2, 1), (3, 0), (3, 1)]]
דיוק
בשלב זה, StableHLO לא מספקת כל אחריות לגבי דיוק מספרי, אבל זה עשוי להשתנות בעתיד. (#1156).
סמנטיקה של ביצוע של פעולה כמותית
הפרשנות של פעולות StableHLO כמותיות עשויה להשתנות בהתאם דרישות ויכולות חומרה. לדוגמה, חלק מהציוד עשוי לבחור פרשו פעולות כמותיות באמצעות פונקציית "מפענחים", מבצעים נקודה צפה (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
לצפייה "cross_replica_and_partition" שלמעלה.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>
. אם אופרנד הצורה קבוע,
אפשר לאמת באופן סטטי. אם צורת התוצאה היא דינמית לחלוטין,
לא יכול להיות אי-התאמה.