StableHLO คือชุดการดำเนินการสำหรับการดำเนินการระดับสูง (HLO) ในโมเดลแมชชีนเลิร์นนิง (ML) StableHLO ทำหน้าที่เป็นเลเยอร์ความสามารถในการพกพาระหว่างเฟรมเวิร์ก ML และคอมไพเลอร์ ML ต่างๆ โดยเฟรมเวิร์ก ML ที่สร้างโปรแกรม StableHLO จะเข้ากันได้กับคอมไพเลอร์ ML ที่ใช้โปรแกรม StableHLO
เป้าหมายของเราคือการลดความซับซ้อนและเร่งการพัฒนา ML โดยการสร้าง การทำงานร่วมกันระหว่างเฟรมเวิร์ก ML ต่างๆ (เช่น TensorFlow, JAX และ PyTorch) และคอมไพเลอร์ ML (เช่น XLA และ IREE) ให้มากขึ้น เอกสารนี้จึงมีข้อกำหนดสำหรับภาษาโปรแกรม StableHLO
ข้อกำหนดนี้มี 3 ส่วนหลัก ก่อนอื่น ส่วนโปรแกรมจะอธิบายโครงสร้างของโปรแกรม StableHLO ซึ่งประกอบด้วยฟังก์ชัน StableHLO ซึ่งประกอบด้วยการดำเนินการ StableHLO ภายในโครงสร้างดังกล่าว ส่วน Ops จะระบุความหมายของ แต่ละ Ops ส่วนการดำเนินการจะให้ความหมายสำหรับ การดำเนินการทั้งหมดเหล่านี้ร่วมกันภายในโปรแกรม สุดท้าย ส่วนสัญกรณ์จะอธิบายสัญกรณ์ที่ใช้ตลอดทั้ง ข้อกำหนด
หากต้องการดูข้อกำหนดจาก StableHLO รุ่นก่อนหน้า ให้เปิดที่เก็บที่รุ่นที่ติดแท็กที่ต้องการ เช่น ข้อกำหนด StableHLO v0.19.0 หากต้องการดูการเปลี่ยนแปลงที่เกิดขึ้นในการอัปเดตเวอร์ชันย่อยแต่ละครั้งของ StableHLO โปรดดู บันทึกเวอร์ชันใน VhloDialect.td
โปรแกรม
Program ::= {Func}
โปรแกรม StableHLO ประกอบด้วยฟังก์ชัน StableHLO จำนวนเท่าใดก็ได้
ด้านล่างนี้คือตัวอย่างโปรแกรมที่มีฟังก์ชัน @main ซึ่งมีอินพุต 3 รายการ
(%image, %weights และ %bias) และเอาต์พุต 1 รายการ เนื้อหาของฟังก์ชัน
มี 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 คล้ายกับตัวระบุในภาษาโปรแกรมหลายภาษา โดยมีลักษณะพิเศษ 2 อย่างคือ 1) ตัวระบุทั้งหมดมีเครื่องหมายที่ แยกความแตกต่างของตัวระบุประเภทต่างๆ 2) ตัวระบุค่าอาจเป็น ตัวเลขทั้งหมดเพื่อลดความซับซ้อนในการสร้างโปรแกรม StableHLO
ประเภท
Type ::= ValueType | NonValueType
ValueType ::= TensorType | QuantizedTensorType | TokenType | TupleType | BufferType
NonValueType ::= TensorElementType | QuantizedTensorElementType | FunctionType | StringType
ประเภท StableHLO แบ่งออกเป็นประเภทค่า (ซึ่งเรียกอีกอย่างว่าประเภทชั้นหนึ่ง) ซึ่งแสดงค่า StableHLO และประเภทที่ไม่ใช่ค่า ซึ่งอธิบายองค์ประกอบโปรแกรมอื่นๆ ประเภท StableHLO คล้ายกับประเภทในภาษาโปรแกรมหลายภาษา โดยความพิเศษหลักคือลักษณะเฉพาะของโดเมนของ StableHLO ซึ่งส่งผลให้เกิดผลลัพธ์ที่ผิดปกติบางอย่าง (เช่น ประเภทสเกลาร์ไม่ใช่ประเภทค่า)
TensorType ::= 'tensor' '<' Shape TensorElementType '>'
Shape ::= {DimensionSize 'x'}
DimensionSize ::= digit {digit} | '?'
ประเภทเทนเซอร์แสดงเทนเซอร์ ซึ่งก็คืออาร์เรย์หลายมิติ โดยมีรูปร่างและประเภทองค์ประกอบ ซึ่งรูปร่างแสดงถึงขนาดมิติข้อมูลที่ไม่เป็นลบหรือที่ไม่รู้จักในลำดับที่เพิ่มขึ้นของมิติข้อมูลที่เกี่ยวข้อง (เรียกอีกอย่างว่าแกน) ซึ่งมีหมายเลขตั้งแต่ 0 ถึง R-1 Rจำนวนมิติข้อมูลเรียกว่าอันดับ เช่น tensor<2x3xf32> คือ
ประเภทเทนเซอร์ที่มีรูปร่าง 2x3 และประเภทองค์ประกอบ f32 มี 2 มิติ
(หรืออีกนัยหนึ่งคือ 2 แกน) ได้แก่ มิติที่ 0 และมิติที่ 1 ซึ่งมีขนาด
เป็น 2 และ 3 โดยมีอันดับที่ 2
รูปร่างอาจไม่รู้จักบางส่วนหรือทั้งหมด (ไดนามิก) เช่น tensor<?x2xf64>
ไม่รู้จักบางส่วนและ tensor<?x?xf64> ไม่รู้จักทั้งหมด ขนาดมิติข้อมูลแบบไดนามิก
จะแสดงโดยใช้ ? ยกเลิกการจัดอันดับรูปร่างไม่ได้
ในอนาคต เราวางแผนที่จะขยายประเภทเทนเซอร์ให้ครอบคลุมมากกว่า ขนาดมิติและประเภทองค์ประกอบ เช่น การรวมเลย์เอาต์ (#629) และความกระจัดกระจาย (#1078)
QuantizedTensorType ::= 'tensor' '<' Shape QuantizedTensorElementType '>'
QuantizedTensorElementType ::= '!quant.uniform' '<'
QuantizationStorageType
['<' QuantizationStorageMin ':' QuantizationStorageMax '>']
':' QuantizationExpressedType
[':' QuantizationDimension]
',' QuantizationParameters '>'
QuantizationStorageType ::= IntegerType
QuantizationStorageMin ::= IntegerLiteral
QuantizationStorageMax ::= IntegerLiteral
QuantizationExpressedType ::= FloatType
QuantizationDimension ::= IntegerLiteral
QuantizationParameters ::= QuantizationParameter
| '{' QuantizationParameter {',' QuantizationParameter} '}'
QuantizationParameter ::= QuantizationScale [':' QuantizationZeroPoint]
QuantizationScale ::= FloatLiteral
QuantizationZeroPoint ::= IntegerLiteral
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
storage_type |
ประเภทจำนวนเต็ม | (C1-C3), (C8) |
storage_min |
ค่าคงที่จำนวนเต็ม | (C1), (C3), (C7) |
storage_max |
ค่าคงที่จำนวนเต็ม | (C2), (C3), (C7) |
expressed_type |
ประเภทจุดลอยตัว | (C4) |
quantization_dimension |
ค่าคงที่จำนวนเต็มที่ไม่บังคับ | (C10-C12) |
scales |
ค่าคงที่จุดลอยตัวจำนวนแปรผัน | (C4-C6), (C9), (C10), (C13) |
zero_points |
จำนวนค่าคงที่จำนวนเต็มที่เปลี่ยนแปลงได้ | (C7-C9) |
ประเภทองค์ประกอบที่กำหนดปริมาณแสดงค่าจำนวนเต็มของประเภทพื้นที่เก็บข้อมูลใน
ช่วงตั้งแต่ storage_min ถึง storage_max (รวม) ซึ่งสอดคล้องกับ
ค่าทศนิยมของประเภทที่แสดง สำหรับค่าจำนวนเต็มที่กำหนด i
ค่าทศนิยมที่เกี่ยวข้อง f สามารถคำนวณได้เป็น
f = (i - zero_point) * scale โดยที่ scale และ zero_point เรียกว่า
พารามิเตอร์การหาปริมาณ storage_min และ storage_max เป็นค่าที่ไม่บังคับ
ในไวยากรณ์ แต่มีค่าเริ่มต้นเป็น min_value(storage_type) และ
max_value(storage_type) ตามลำดับ ประเภทองค์ประกอบที่กำหนดปริมาณมีข้อจำกัดต่อไปนี้
- (C1)
type(storage_min) = storage_type - (C2)
type(storage_max) = storage_type - (C3)
min_value(storage_type) <= storage_min < storage_max <= max_value(storage_type) - (C4)
type(scales...) = expressed_type - (C5)
0 < scales - (C6)
is_finite(scales...) - (C7)
storage_min <= zero_points <= storage_max - (C8)
type(zero_points...) = storage_type - (C9)
size(scales) = size(zero_points) - (C10) ถ้า
is_empty(quantization_dimension)ให้size(scales) = 1 - (C11)
0 <= quantization_dimension
ปัจจุบัน QuantizationScale เป็นค่าคงที่แบบทศนิยม แต่ก็มี
ความสนใจอย่างมากในสเกลที่อิงตามจำนวนเต็ม ซึ่งแสดงด้วยตัวคูณและ
การเลื่อน เราวางแผนที่จะสำรวจฟีเจอร์นี้ในอนาคตอันใกล้นี้
(#1404)
เรากำลังพูดคุยกันอย่างต่อเนื่องเกี่ยวกับความหมายของ QuantizationZeroPoint
รวมถึงประเภท ค่า และความเป็นไปได้ที่จะมีจุดศูนย์เพียงจุดเดียวหรือ
อาจมีหลายจุดในประเภทเทนเซอร์ที่ควอนไทซ์ จาก
ผลการอภิปรายนี้ ข้อกำหนดเกี่ยวกับคะแนน 0 อาจมีการเปลี่ยนแปลง
ในอนาคต (#1405)
การอภิปรายอีกเรื่องที่กำลังดำเนินอยู่เกี่ยวข้องกับความหมายของ QuantizationStorageMin
และ QuantizationStorageMax เพื่อพิจารณาว่าควรมีการกำหนดข้อจำกัดใดๆ
กับค่าเหล่านี้และค่าของเทนเซอร์ที่ผ่านการหาปริมาณหรือไม่
(#1406)
สุดท้าย เราวางแผนที่จะสำรวจการแสดงมาตราส่วนที่ไม่รู้จักและจุดศูนย์ ในลักษณะเดียวกับที่เราวางแผนที่จะสำรวจการแสดงขนาดมิติข้อมูลที่ไม่รู้จัก (#1407)
ประเภทเทนเซอร์ที่เล็กลงแสดงเทนเซอร์ที่มีองค์ประกอบที่เล็กลง เทนเซอร์เหล่านี้เหมือนกับเทนเซอร์ปกติทุกประการ ยกเว้นว่าองค์ประกอบของเทนเซอร์มีประเภทองค์ประกอบที่ผ่านการวัดปริมาณแทนที่จะเป็นประเภทองค์ประกอบปกติ
ในเทนเซอร์ที่ผ่านการควอนไทซ์ การควอนไทซ์อาจเป็นต่อเทนเซอร์ ซึ่งหมายถึงการมี scale และ zero_point รายการเดียวสำหรับทั้งเทนเซอร์ หรืออาจเป็นต่อแกน ซึ่งหมายถึงการมี scales และ zero_points หลายรายการ โดยมี 1 คู่ต่อชิ้นของมิติข้อมูลหนึ่งๆ quantization_dimension กล่าวอย่างเป็นทางการมากขึ้นคือ ในเทนเซอร์ t
ที่มีการหาปริมาณต่อแกน จะมี dim(t, quantization_dimension) สไลซ์
ของ quantization_dimension: t[:, ..., 0, ..., :], t[:, ..., 1, ..., :],
ฯลฯ องค์ประกอบทั้งหมดในสไลซ์ที่ i จะใช้ scales[i] และ zero_points[i] เป็น
พารามิเตอร์การหาปริมาณ ประเภทเทนเซอร์ที่กำหนดปริมาณมีข้อจำกัดต่อไปนี้
- สำหรับการหาปริมาณต่อเทนเซอร์ ให้ทำดังนี้
- ไม่มีข้อจำกัดเพิ่มเติม
- สำหรับการหาปริมาณต่อแกน ให้ทำดังนี้
- (C12)
quantization_dimension < rank(self) - (C13)
dim(self, quantization_dimension) = size(scales)
- (C12)
TokenType ::= 'token'
ประเภทโทเค็นแสดงถึงโทเค็น ซึ่งก็คือค่าทึบแสงที่สร้างและใช้ โดยการดำเนินการบางอย่าง โทเค็นใช้เพื่อกำหนดลำดับการดำเนินการกับโอเปอเรชัน ตามที่อธิบายไว้ในส่วนการดำเนินการ
TupleType ::= 'tuple' '<' TupleElementTypes '>'
TupleElementTypes ::= [ValueType {',' ValueType}]
ประเภทบัฟเฟอร์แสดงถึงบัฟเฟอร์ ตัวอย่างเช่น ใน XLA บัฟเฟอร์คือ
อาร์เรย์หลายมิติที่มีพื้นที่เก็บข้อมูลที่สอดคล้องกัน เช่นเดียวกับประเภทเทนเซอร์
ประเภทบัฟเฟอร์มีรูปร่างและประเภทองค์ประกอบ โดยรูปร่างแสดงถึง
ขนาดมิติข้อมูลที่ไม่เป็นลบหรือไม่รู้จักตามลำดับจากน้อยไปมากของ
มิติข้อมูลที่เกี่ยวข้อง (เรียกอีกอย่างว่าแกน) ซึ่งมีหมายเลขตั้งแต่ 0
ถึง R-1 จำนวนมิติข้อมูล R เรียกว่าอันดับ เช่น
memref<2x3xf32> เป็นประเภทบัฟเฟอร์ที่มีรูปร่าง 2x3 และประเภทองค์ประกอบ f32 มี 2 มิติ (หรืออีกนัยหนึ่งคือ 2 แกน) ได้แก่ มิติที่ 0 และมิติที่ 1 ซึ่งมีขนาดเป็น 2 และ 3 โดยมีอันดับที่ 2
คุณจัดสรรบัฟเฟอร์ได้โดยใช้ custom_call ถึง CreateBuffer หรือ Pin และยกเลิกการจัดสรรผ่าน custom_call ถึง Unpin มีเพียงcustom_call ops เท่านั้นที่อ่านและ
เขียนเนื้อหาภายในบัฟเฟอร์ได้ ดูรายละเอียดเพิ่มเติมได้ที่ custom_call
ประเภท Tuple แสดง Tuple ซึ่งก็คือรายการแบบไม่เหมือนกัน ทูเพิลเป็นฟีเจอร์เดิม
ที่มีไว้เพื่อความเข้ากันได้กับ HLO เท่านั้น ใน HLO จะใช้ Tuple เพื่อแสดงอินพุตและเอาต์พุตแบบ Variadic ใน StableHLO ระบบรองรับอินพุตและเอาต์พุตแบบ Variadic โดยกำเนิด และการใช้ Tuple ใน StableHLO มีเพียงเพื่อแสดง ABI ของ HLO อย่างครอบคลุม เช่น T, tuple<T> และ tuple<tuple<T>> อาจแตกต่างกันอย่างมาก ทั้งนี้ขึ้นอยู่กับการใช้งานที่เฉพาะเจาะจง ในอนาคต เราวางแผนที่จะเปลี่ยนแปลง HLO ABI
ซึ่งอาจช่วยให้เรานำประเภท Tuple ออกจาก StableHLO ได้
(#598)
TensorElementType ::= BooleanType | IntegerType | FloatType | ComplexType
BooleanType ::= 'i1'
IntegerType ::= SignedIntegerType | UnsignedIntegerType
SignedIntegerType ::= 'si2' | 'si4' | 'si8' | 'si16' | 'si32' | 'si64'
UnsignedIntegerType ::= 'ui2' | 'ui4' | 'ui8' | 'ui16' | 'ui32' | 'ui64'
FloatType ::= 'f4E2M1FN' | 'f6E2M3FN' | 'f6E3M2FN' | 'f8E3M4' | 'f8E4M3'
| 'f8E4M3FN' | 'f8E4M3FNUZ' | 'f8E4M3B11FNUZ' | 'f8E5M2'
| 'f8E5M2FNUZ' | 'f8E8M0FNU' | 'bf16' | 'f16' | 'f32' | 'f64'
TensorFloat32 ::= 'tf32'
ComplexType ::= 'complex' '<' ComplexElementType '>'
ComplexElementType ::= 'f32' | 'f64'
ประเภทองค์ประกอบแสดงองค์ประกอบของประเภทเทนเซอร์ ประเภทเหล่านี้ไม่ใช่ประเภทระดับเฟิร์สคลาสใน StableHLO ซึ่งต่างจากภาษาโปรแกรมหลายภาษา
ซึ่งหมายความว่าโปรแกรม StableHLO ไม่สามารถแสดงค่าของประเภทเหล่านี้ได้โดยตรง (ดังนั้นจึงเป็นเรื่องปกติที่จะแสดงค่าสเกลาร์ของประเภท T ด้วยค่าเทนเซอร์ 0 มิติของประเภท tensor<T>)
- ประเภทบูลีนแสดงค่าบูลีน
trueและfalse - ประเภทจำนวนเต็มอาจเป็นแบบมีเครื่องหมาย (
si) หรือไม่มีเครื่องหมาย (ui) และมี ความกว้างของบิตที่รองรับอย่างใดอย่างหนึ่ง (2,4,8,16,32หรือ64) ประเภทsiNแบบมีเครื่องหมายแสดงค่าจำนวนเต็มตั้งแต่-2^(N-1)ถึง2^(N-1)-1รวม และประเภทuiNแบบไม่มีเครื่องหมายแสดงค่าจำนวนเต็มตั้งแต่0ถึง2^N-1รวม - ประเภททศนิยมอาจเป็นประเภทใดประเภทหนึ่งต่อไปนี้
f8E3M4,f8E4M3และf8E5M2เป็นเลขทศนิยมแบบลอยตัว 8 บิตตาม ข้อกำหนด IEEE-754f8E4M3FNและf8E5M2ที่สอดคล้องกับการเข้ารหัสE4M3และE5M2ของรูปแบบ FP8 ตามลำดับที่อธิบายไว้ในรูปแบบ FP8 สำหรับ Deep Learningf8E4M3FNUZและf8E5M2FNUZประเภทที่สอดคล้องกับการเข้ารหัสE4M3และE5M2ของรูปแบบ FP8 ที่อธิบายไว้ใน รูปแบบตัวเลข 8 บิตสำหรับ Deep Neural Networkf8E4M3B11FNUZประเภทที่สอดคล้องกับการเข้ารหัสE4M3ของรูปแบบ FP8 ที่อธิบายไว้ใน การฝึกและอนุมานแบบจุดลอยตัว 8 บิตแบบไฮบริด (HFP8) สำหรับเครือข่ายประสาทเทียมแบบลึกbf16ประเภทที่สอดคล้องกับbfloat16รูปแบบที่อธิบายไว้ใน BFloat16: เคล็ดลับสู่ประสิทธิภาพสูงบน Cloud TPU- ประเภท
f16,f32และf64ที่สอดคล้องกับรูปแบบbinary16("ความแม่นยำครึ่งหนึ่ง"),binary32("ความแม่นยำเดียว") และbinary64("ความแม่นยำสองเท่า") ตามที่อธิบายไว้ในมาตรฐาน IEEE 754 tf32ประเภทจะสอดคล้องกับรูปแบบ TensorFloat32 และรองรับใน StableHLO อย่างจำกัดf4E2M1FN,f6E2M3FN,f6E3M2FNและf8E8M0FNUประเภท MX (การปรับขนาดเล็ก) ที่อธิบายไว้ในข้อกำหนดรูปแบบการปรับขนาดเล็กของ OCP
- ประเภทเชิงซ้อนแสดงค่าเชิงซ้อนที่มีส่วนจริง
และส่วนจินตภาพของประเภทองค์ประกอบเดียวกัน ประเภทที่ซับซ้อนที่รองรับคือ
complex<f32>(ทั้ง 2 ส่วนเป็นประเภทf32) และcomplex<f64>(ทั้ง 2 ส่วนเป็นประเภทf64)
FunctionType ::= '(' InputTypes ')' '->' '(' OutputTypes ')'
InputTypes ::= [ValueType {',' ValueType}]
OutputTypes ::= [ValueType {',' ValueType}]
ประเภทฟังก์ชันแสดงทั้งฟังก์ชันที่มีชื่อและฟังก์ชันที่ไม่ระบุชื่อ โดยมี
ประเภทอินพุต (รายการประเภททางด้านซ้ายของ ->) และประเภทเอาต์พุต
(รายการประเภททางด้านขวาของ ->) ในภาษาโปรแกรม
หลายภาษา ประเภทฟังก์ชันเป็นคลาสแรก แต่ไม่ใช่ใน StableHLO
StringType ::= 'string'
ประเภทสตริงแสดงลำดับของไบต์ ไม่เหมือนกับในภาษาโปรแกรมหลายภาษา ประเภทสตริงไม่ใช่คลาสแรกใน StableHLO และใช้เพื่อระบุข้อมูลเมตาแบบคงที่สำหรับองค์ประกอบของโปรแกรมเท่านั้น
การดำเนินการ
การดำเนินการ StableHLO (หรือที่เรียกว่า ops) แสดงถึงชุดการดำเนินการระดับสูงที่ปิดแล้ว ในโมเดลแมชชีนเลิร์นนิง ดังที่ได้กล่าวไว้ข้างต้น ไวยากรณ์ StableHLO ได้รับแรงบันดาลใจอย่างมากจาก MLIR ซึ่งอาจไม่ใช่ทางเลือกที่ สะดวกที่สุด แต่ก็อาจเป็นตัวเลือกที่เหมาะสมที่สุดสำหรับเป้าหมายของ StableHLO ในการ สร้างการทำงานร่วมกันระหว่างเฟรมเวิร์ก ML และคอมไพเลอร์ ML ให้มากขึ้น
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 ฟังก์ชันไม่ใช่ค่าระดับเฟิร์สคลาส) และ
แอตทริบิวต์อินพุต (ระบุแบบคงที่เช่นกัน) ประเภทอินพุตและเอาต์พุต
ที่ใช้และสร้างโดย Op จะขึ้นอยู่กับตัวช่วยจำของ Op นั้น เช่น add
op ใช้ค่าอินพุต 2 ค่าและสร้างค่าเอาต์พุต 1 ค่า ในทางตรงกันข้าม
select_and_scatter op ใช้ค่าอินพุต 3 ค่า ฟังก์ชันอินพุต 2 รายการ และ
แอตทริบิวต์อินพุต 3 รายการ
OpInputFunc ::= '{' Unused FuncInputs ':' FuncBody '}'
Unused ::= '^' digit {digit}
| '^' letter {letter | digit}
ฟังก์ชันอินพุต (หรือที่เรียกว่าฟังก์ชันที่ไม่ระบุชื่อ) มีลักษณะคล้ายกับฟังก์ชันที่มีชื่อมาก ยกเว้นว่า 1) ฟังก์ชันอินพุตไม่มีตัวระบุ (จึงเป็นที่มาของชื่อ "ไม่ระบุชื่อ") 2) ฟังก์ชันอินพุตไม่ได้ประกาศประเภทเอาต์พุต (ระบบจะอนุมานประเภทเอาต์พุตจาก return op ภายในฟังก์ชัน)
ไวยากรณ์ของฟังก์ชันอินพุตมีส่วนที่ไม่ได้ใช้ในปัจจุบัน (ดูUnusedที่สร้างขึ้นด้านบน) ซึ่งมีไว้เพื่อความเข้ากันได้กับ MLIR ใน MLIR
มีแนวคิดที่ทั่วไปกว่าคือ "รีเจียน" ซึ่งมี "บล็อก" หลายบล็อก
ของ Op ที่เชื่อมต่อกันผ่าน Op การข้าม บล็อกเหล่านี้มีรหัสที่สอดคล้องกับUnusedการผลิต เพื่อให้แยกความแตกต่างจากกันได้
StableHLO ไม่มี Jump Op ดังนั้นส่วนที่เกี่ยวข้องของไวยากรณ์ MLIR จึงไม่ได้ใช้ (แต่ยังคงมีอยู่)
OpInputAttr ::= OpInputAttrName '=' OpInputAttrValue
OpInputAttrName ::= letter {letter | digit}
OpInputAttrValue ::= Constant
แอตทริบิวต์อินพุตมีชื่อและค่าซึ่งเป็นค่าคงที่ที่รองรับค่าใดค่าหนึ่ง
ซึ่งเป็นวิธีหลักในการระบุข้อมูลเมตาแบบคงที่สำหรับองค์ประกอบของโปรแกรม ตัวอย่างเช่น concatenate op ใช้แอตทริบิวต์ dimension เพื่อ
ระบุมิติที่ต่อค่าอินพุต ในทำนองเดียวกัน
slice op ใช้แอตทริบิวต์หลายรายการ เช่น start_indices และ limit_indices
เพื่อระบุขอบเขตที่ใช้ในการแบ่งค่าอินพุต
ปัจจุบันโปรแกรม StableHLO ในการใช้งานจริงบางครั้งมีแอตทริบิวต์ ซึ่งไม่ได้อธิบายไว้ในเอกสารนี้ ในอนาคต เราวางแผนที่จะ รวมแอตทริบิวต์เหล่านี้ไว้ในชุดการดำเนินการ StableHLO หรือห้ามไม่ให้ ปรากฏในโปรแกรม StableHLO ในระหว่างนี้ คุณสามารถดูรายการแอตทริบิวต์เหล่านี้ได้ที่นี่
layout(#629)mhlo.frontend_attributes(#628)mhlo.sharding(#619)output_operand_aliases(#740)- ข้อมูลเมตาตำแหน่ง (#594)
OpSignature ::= '(' [ValueType {',' ValueType}] ')' '->' '(' [ValueType {',' ValueType}] ')'
ลายเซ็น Op ประกอบด้วยประเภทของค่าอินพุตทั้งหมด (รายการประเภททางด้านซ้ายของ ->) และประเภทของค่าเอาต์พุตทั้งหมด (รายการประเภททางด้านขวาของ ->) กล่าวอย่างเคร่งครัดคือ ประเภทอินพุตซ้ำซ้อน และประเภทเอาต์พุตก็มักจะซ้ำซ้อนด้วยเช่นกัน (เนื่องจากสำหรับ Op StableHLO ส่วนใหญ่ ระบบจะอนุมานประเภทเอาต์พุตจากอินพุตได้) อย่างไรก็ตาม op
signature เป็นส่วนหนึ่งของไวยากรณ์ 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'
ค่าคงที่จำนวนเต็มแสดงค่าจำนวนเต็มผ่านสตริงที่ใช้สัญกรณ์ทศนิยมหรือ เลขฐานสิบหก ระบบไม่รองรับฐานอื่นๆ เช่น ฐาน 2 หรือฐาน 8 ค่าคงที่จำนวนเต็มมีข้อจำกัดต่อไปนี้
- (C1)
is_wellformed(integer_literal, integer_type)
FloatConstant ::= FloatLiteral ':' FloatType
FloatLiteral ::= SignPart IntegerPart FractionalPart ScientificPart
| '0x' [HexadecimalDigits]
SignPart ::= ['-' | '+']
IntegerPart ::= DecimalDigits
FractionalPart ::= ['.' [DecimalDigits]]
ScientificPart ::= [('e' | 'E') ['-' | '+'] DecimalDigits]
ค่าคงที่แบบทศนิยมแสดงค่าทศนิยมผ่านสตริงที่ ใช้สัญกรณ์ทศนิยมหรือสัญกรณ์วิทยาศาสตร์ นอกจากนี้ คุณยังใช้สัญกรณ์ฐานสิบหกเพื่อระบุบิตพื้นฐานในรูปแบบจุดลอยตัวของประเภทที่เกี่ยวข้องได้โดยตรง ค่าคงที่แบบทศนิยมมีข้อจำกัดต่อไปนี้
- (C1) หากใช้สัญกรณ์ที่ไม่ใช่เลขฐานสิบหก
is_wellformed(float_literal, float_type) - (C2) หากใช้สัญกรณ์เลขฐานสิบหก ให้ใช้
size(hexadecimal_digits) = num_bits(float_type) / 4
ComplexConstant ::= ComplexLiteral ':' ComplexType
ComplexLiteral ::= '(' RealPart ',' ImaginaryPart ')'
RealPart ::= FloatLiteral
ImaginaryPart ::= FloatLiteral
ค่าคงที่เชิงซ้อนแสดงค่าเชิงซ้อนโดยใช้รายการของส่วนจริง
(มาเป็นอันดับแรก) และส่วนจินตภาพ (มาเป็นอันดับที่สอง) ตัวอย่างเช่น (1.0, 0.0) : complex<f32> แทน 1.0 + 0.0i และ (0.0, 1.0) : complex<f32> แทน 0.0 + 1.0i ลำดับการจัดเก็บส่วนต่างๆ เหล่านี้ในหน่วยความจำจะขึ้นอยู่กับการใช้งาน ค่าคงที่ที่ซับซ้อน
มีข้อจำกัดดังนี้
- (C1)
is_wellformed(real_part, complex_element_type(complex_type)) - (C2)
is_wellformed(imaginary_part, complex_element_type(complex_type))
TensorConstant ::= TensorLiteral ':' TensorType
TensorLiteral ::= 'dense' '<' (DenseLiteral | ElementLiteral) '>'
DenseLiteral ::= DenseDimension | DenseElements
DenseDimension ::= '[' [DenseLiteral {',' DenseLiteral}] ']'
DenseElements ::= [ElementLiteral {',' ElementLiteral}]
ElementLiteral ::= BooleanLiteral | IntegerLiteral | FloatLiteral | ComplexLiteral
ค่าคงที่ของเทนเซอร์แสดงค่าเทนเซอร์โดยใช้ลิสต์ที่ซ้อนกันซึ่งระบุผ่าน
สัญกรณ์ NumPy เช่น dense<[[1, 2, 3], [4, 5, 6]]> : tensor<2x3xi32>
แสดงค่าเทนเซอร์ที่มีการแมปดัชนีกับองค์ประกอบดังนี้
{0, 0} => 1, {0, 1} => 2, {0, 2} => 3, {1, 0} => 4, {1, 1} => 5,
{1, 2} => 6 ลำดับที่จัดเก็บองค์ประกอบเหล่านี้ไว้ในหน่วยความจำนั้นขึ้นอยู่กับการใช้งาน
ค่าคงที่ของเทนเซอร์มีข้อจำกัดต่อไปนี้
- (C1)
has_syntax(tensor_literal, element_type(tensor_type))โดยที่has_syntax(element_literal: Syntax, element_type: Type) = is_wellformed(element_literal, type)has_syntax(tensor_literal: List, element_type: Type) = has_syntax(tensor_literal..., element_type)
- (C2)
has_shape(tensor_literal, shape(tensor_type))โดยที่has_shape(element_literal: Syntax, []) = truehas_shape(tensor_literal: List, shape: List) = size(tensor_literal) = shape[0] and has_shape(tensor_literal..., shape[1:])- หรือ
false
QuantizedTensorConstant ::= QuantizedTensorLiteral ':' QuantizedTensorType
QuantizedTensorLiteral ::= 'dense' '<' (DenseLiteral | ElementLiteral) '>'
ค่าคงที่ของเทนเซอร์ที่แปลงเป็นจำนวนเต็มแสดงค่าเทนเซอร์ที่แปลงเป็นจำนวนเต็มโดยใช้สัญกรณ์เดียวกับค่าคงที่ของเทนเซอร์ โดยมีองค์ประกอบที่ระบุเป็นค่าคงที่ของประเภทพื้นที่เก็บข้อมูล ค่าคงที่ของเทนเซอร์ที่แปลงเป็นจำนวนเต็มมีข้อจำกัดต่อไปนี้
- (C1)
has_syntax(quantized_tensor_literal, storage_type(quantized_tensor_type)) - (C2)
has_shape(quantized_tensor_literal, shape(quantized_tensor_type))
StringConstant ::= StringLiteral
StringLiteral ::= '"' {stringCharacter | escapeSequence} '"'
stringCharacter ::= all ASCII characters except '\00', '\01', ... '\1f' and '"'
escapeSequence ::= '\' ('"' | '\' | 'n' | 't' | (hexadecimalDigit hexadecimalDigit))
อักษรสตริงประกอบด้วยไบต์ที่ระบุโดยใช้อักขระ ASCII และ
ลำดับการหลีก โดยไม่ขึ้นอยู่กับการเข้ารหัส ดังนั้นการตีความไบต์เหล่านี้
จึงขึ้นอยู่กับการใช้งาน อักษรสตริงมีประเภท string
การดำเนินการ
แบบสัมบูรณ์
ความหมาย
ดำเนินการ abs แบบทีละองค์ประกอบในเทนเซอร์ operand และสร้างเทนเซอร์ result
โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับจำนวนเต็มที่มีเครื่องหมาย: โมดูลัสจำนวนเต็ม
- สำหรับ Float:
absจาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: โมดูลัสเชิงซ้อน
- สำหรับประเภทที่กำหนดปริมาณ:
dequantize_op_quantize(abs, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ของจำนวนเต็มที่มีเครื่องหมาย ทศนิยม หรือจำนวนเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1-C2) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ของจำนวนเต็มที่มีเครื่องหมายหรือประเภททศนิยม หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1-C2) |
ข้อจำกัด
- (C1)
shape(result) = shape(operand) - (C2)
baseline_element_type(result)มีคำจำกัดความดังนี้complex_element_type(element_type(operand))หากis_complex(operand)baseline_element_type(operand)หรือไม่เช่นนั้น
ตัวอย่าง
// %operand: [-2, 0, 2]
%result = "stablehlo.abs"(%operand) : (tensor<3xi32>) -> tensor<3xi32>
// %result: [2, 0, 2]
เพิ่ม
ความหมาย
ดำเนินการบวกแบบทีละองค์ประกอบของเทนเซอร์ 2 รายการ lhs และ rhs แล้วสร้างเทนเซอร์ result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับบูลีน: OR เชิงตรรกะ
- สำหรับจำนวนเต็ม: การบวกจำนวนเต็ม
- สำหรับ Float:
additionจาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: การบวกจำนวนเชิงซ้อน
- สำหรับประเภทที่กำหนดปริมาณ:
dequantize_op_quantize(add, lhs, rhs, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | lhs |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (C1-C6) |
| (I2) | rhs |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (C1-C5), (C7) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (C1-C7) |
ข้อจำกัด
- หากการดำเนินการใช้เทนเซอร์ที่ไม่ได้ทำ Quantize ให้ทำดังนี้
- (C1)
type(lhs) = type(rhs) = type(result)
- (C1)
- หากการดำเนินการใช้เทนเซอร์ที่กำหนดปริมาณ ให้ทำดังนี้
- (C2)
is_quantized(lhs) and is_quantized(rhs) and is_quantized(result) - (C3)
storage_type(lhs) = storage_type(rhs) = storage_type(result) - (C4)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result) - (C5)
(is_per_axis_quantized(lhs) or is_per_axis_quantized(rhs)) = is_per_axis_quantized(result) - (C6) ถ้า
is_per_axis_quantized(lhs)ให้quantization_dimension(lhs) = quantization_dimension(result) - (C7) ถ้า
is_per_axis_quantized(rhs)ให้quantization_dimension(rhs) = quantization_dimension(result)
- (C2)
ตัวอย่าง
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.add"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[6, 8], [10, 12]]
after_all
ความหมาย
ตรวจสอบว่าการดำเนินการที่สร้าง inputs จะดำเนินการก่อนการดำเนินการใดๆ ที่ขึ้นอยู่กับ result
การดำเนินการนี้ไม่มีผลใดๆ
มีไว้เพื่อสร้างการอ้างอิงข้อมูลจาก result ไปยัง inputs เท่านั้น
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท |
|---|---|---|
| (I1) | inputs |
จำนวนตัวแปรของ token |
เอาต์พุต
| ชื่อ | ประเภท |
|---|---|
result |
token |
ตัวอย่าง
// %input0: !stablehlo.token
// %input1: !stablehlo.token
%result = "stablehlo.after_all"(%input0, %input1) : (!stablehlo.token, !stablehlo.token) -> !stablehlo.token
all_gather
ความหมาย
ภายในกลุ่มกระบวนการแต่ละกลุ่มในตารางกระบวนการ StableHLO จะต่อค่า
ของเทนเซอร์ operands จากแต่ละกระบวนการตาม all_gather_dim และสร้างเทนเซอร์ results
การดำเนินการจะแยกตารางกระบวนการ StableHLO ออกเป็น process_groups ซึ่งกำหนดไว้ดังนี้
cross_replica(replica_groups)หากchannel_id <= 0 and use_global_device_ids = falsecross_replica_and_partition(replica_groups)หากchannel_id > 0 and use_global_device_ids = falseflattened_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_groupresults...@process = concatenate(operands...@process, all_gather_dim)สำหรับprocessทั้งหมดในprocess_group
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operands |
เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ | (C1), (C6) |
| (I2) | all_gather_dim |
ค่าคงที่ของประเภท si64 |
(C1), (C6) |
| (I3) | replica_groups |
ค่าคงที่เทนเซอร์ 2 มิติของประเภท si64 |
(C2-C4) |
| (I4) | channel_id |
ค่าคงที่ของประเภท si64 |
(C5) |
| (I5) | use_global_device_ids |
ค่าคงที่ของประเภท i1 |
(C5) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
results |
เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ | (C6) |
ข้อจำกัด
- (C1)
0 <= all_gather_dim < rank(operands...) - (C2)
is_unique(replica_groups) - (C3)
size(replica_groups)มีคำจำกัดความดังนี้num_replicasหากใช้cross_replicanum_replicasหากใช้cross_replica_and_partitionnum_processesหากใช้flattened_ids
- (C4)
0 <= replica_groups < size(replica_groups) - (C5) ถ้า
use_global_device_ids = trueให้channel_id > 0 - (C6)
type(results...) = type(operands...)ยกเว้นdim(results..., all_gather_dim) = dim(operands..., all_gather_dim) * dim(process_groups, 1)
ตัวอย่าง
// num_replicas: 2
// num_partitions: 1
// %operand0@(0, 0): [[1, 2], [3, 4]]
// %operand0@(1, 0): [[5, 6], [7, 8]]
// %operand1@(0, 0): [[11, 12], [13, 14]]
// %operand1@(1, 0): [[15, 16], [17, 18]]
%result:2 = "stablehlo.all_gather"(%operand0, %operand1) {
all_gather_dim = 1 : i64,
replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>,
// channel_id = 0
channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
// use_global_device_ids = false
} : (tensor<2x2xi64>, tensor<2x2xi64>) -> (tensor<2x4xi64>, tensor<2x4xi64>)
// %result0@(0, 0): [[1, 2, 5, 6], [3, 4, 7, 8]]
// %result0@(1, 0): [[1, 2, 5, 6], [3, 4, 7, 8]]
// %result1@(0, 0): [[11, 12, 15, 16], [13, 14, 17, 18]]
// %result1@(1, 0): [[11, 12, 15, 16], [13, 14, 17, 18]]
all_reduce
ความหมาย
ภายในกลุ่มกระบวนการแต่ละกลุ่มในตารางกระบวนการ StableHLO จะใช้ฟังก์ชันการลด computation กับค่าของเทนเซอร์ operands จากแต่ละกระบวนการ
และสร้างเทนเซอร์ results
การดำเนินการจะแยกตารางกระบวนการ StableHLO ออกเป็น process_groups ซึ่งกำหนดไว้ดังนี้
cross_replica(replica_groups)หากchannel_id <= 0 and use_global_device_ids = falsecross_replica_and_partition(replica_groups)หากchannel_id > 0 and use_global_device_ids = falseflattened_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 |
จำนวนตัวแปรของค่าคงที่เทนเซอร์ 1 มิติประเภท si64 |
(C1-C3) |
| (I3) | channel_id |
ค่าคงที่ของประเภท si64 |
(C4) |
| (I4) | use_global_device_ids |
ค่าคงที่ของประเภท i1 |
(C4) |
| (I5) | computation |
ฟังก์ชัน | (C5) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
results |
เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ | (C6-C7) |
ข้อจำกัด
- (C1)
is_unique(replica_groups) - (C2)
size(replica_groups)มีคำจำกัดความดังนี้num_replicasหากใช้cross_replicanum_replicasหากใช้cross_replica_and_partitionnum_processesหากใช้flattened_ids
- (C3)
0 <= replica_groups < size(replica_groups) - (C4) ถ้า
use_global_device_ids = trueให้channel_id > 0 - (C5)
computationมีประเภท(tensor<E>, tensor<E>) -> (tensor<E>)โดยที่is_promotable(element_type(operand), E) - (C6)
shape(results...) = shape(operands...) - (C7)
element_type(results...) = E
ตัวอย่าง
// num_replicas: 2
// num_partitions: 1
// %operand0@(0, 0): [1, 2, 3, 4]
// %operand0@(1, 0): [5, 6, 7, 8]
// %operand1@(0, 0): [9, 10, 11, 12]
// %operand1@(1, 0): [13, 14, 15, 16]
%result:2 = "stablehlo.all_reduce"(%operand0, %operand0) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
"stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>,
// channel_id = 0
channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
// use_global_device_ids = false
} : (tensor<4xi64>, tensor<4xi64>) -> (tensor<4xi64>, tensor<4xi64>)
// %result0@(0, 0): [6, 8, 10, 12]
// %result0@(1, 0): [6, 8, 10, 12]
// %result1@(0, 0): [22, 24, 26, 28]
// %result1@(1, 0): [22, 24, 26, 28]
all_to_all
ความหมาย
ในแต่ละกลุ่มกระบวนการในตารางกระบวนการ StableHLO จะแยกค่าของเทนเซอร์ operands ตาม split_dimension ออกเป็นส่วนๆ กระจายส่วนที่แยกแล้วระหว่างกระบวนการต่างๆ ต่อส่วนที่กระจายแล้วตาม concat_dimension และสร้างเทนเซอร์ results
การดำเนินการจะแยกตารางกระบวนการ StableHLO ออกเป็น process_groups ซึ่งกำหนดไว้ดังนี้
cross_replica(replica_groups)หากchannel_id <= 0cross_partition(replica_groups)หากchannel_id > 0
หลังจากนั้น ให้ทำดังนี้ภายในแต่ละ process_group
split_parts...@sender = split(operands...@sender, split_count, split_dimension)สำหรับsenderทั้งหมดในprocess_groupscattered_parts...@receiver = [split_parts...@sender[receiver_index] for sender in process_group]ที่ซึ่งreceiver_index = process_group.index(receiver)results...@process = concatenate(scattered_parts...@process, concat_dimension)
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operands |
เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ | (C1-C3), (C9) |
| (I2) | split_dimension |
ค่าคงที่ของประเภท si64 |
(C1), (C2), (C9) |
| (I3) | concat_dimension |
ค่าคงที่ของประเภท si64 |
(C3), (C9) |
| (I4) | split_count |
ค่าคงที่ของประเภท si64 |
(C2), (C4), (C8), (C9) |
| (I5) | replica_groups |
ค่าคงที่เทนเซอร์ 2 มิติของประเภท si64 |
(C5-C8) |
| (I6) | channel_id |
ค่าคงที่ของประเภท si64 |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
results |
เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ | (C9) |
ข้อจำกัด
- (C1)
0 <= split_dimension < rank(operands...) - (C2)
dim(operands..., split_dimension) % split_count = 0 - (C3)
0 <= concat_dimension < rank(operands...) - (C4)
0 < split_count - (C5)
is_unique(replica_groups) - (C6)
size(replica_groups)มีคำจำกัดความดังนี้num_replicasหากใช้cross_replicanum_partitionsหากใช้cross_partition
- (C7)
0 <= replica_groups < size(replica_groups) - (C8)
dim(replica_groups, 1) = split_count - (C9)
type(results...) = type(operands...)ยกเว้นในกรณีที่split_dimension != concat_dimensiondim(results..., split_dimension) = dim(operands..., split_dimension) / split_countdim(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 แบบทีละองค์ประกอบของเทนเซอร์ 2 รายการ lhs และ rhs แล้วสร้างเทนเซอร์ result
โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับบูลีน: AND เชิงตรรกะ
- สำหรับจำนวนเต็ม: AND แบบบิต
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | lhs |
เทนเซอร์ประเภทบูลีนหรือจำนวนเต็ม | (C1) |
| (I2) | rhs |
เทนเซอร์ประเภทบูลีนหรือจำนวนเต็ม | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทบูลีนหรือจำนวนเต็ม | (C1) |
ข้อจำกัด
- (C1)
type(lhs) = type(rhs) = type(result)
ตัวอย่าง
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.and"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[1, 2], [3, 0]]
atan2
ความหมาย
ดำเนินการ atan2 แบบทีละองค์ประกอบในเทนเซอร์ lhs และ rhs และสร้างเทนเซอร์ result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับ Float:
atan2จาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: complex atan2
- สำหรับประเภทที่กำหนดปริมาณ:
dequantize_op_quantize(atan2, lhs, rhs, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | lhs |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
| (I2) | rhs |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
ตัวอย่าง
// %lhs: [0.0, 1.0, -1.0]
// %rhs: [0.0, 0.0, 0.0]
%result = "stablehlo.atan2"(%lhs, %rhs) : (tensor<3xf64>, tensor<3xf64>) -> tensor<3xf64>
// %result: [0.0, 1.57079637, -1.57079637] // [0.0, pi/2, -pi/2]
batch_norm_grad
ความหมาย
คำนวณการไล่ระดับสีของอินพุตหลายรายการของ batch_norm_training โดยbatch_norm_trainingจะส่งต่อข้อผิดพลาดจาก grad_output และสร้างเทนเซอร์ grad_operand, grad_scale และ grad_offset
ในรูปแบบที่เป็นทางการมากขึ้น การดำเนินการนี้สามารถแสดงเป็นการแยกย่อยเป็นการดำเนินการ StableHLO ที่มีอยู่โดยใช้ไวยากรณ์ Python ดังนี้
def compute_sum(operand, feature_index):
(sum,) = reduce(
inputs=[operand],
init_values=[constant(0, element_type(operand))],
dimensions=[i for i in range(rank(operand)) if i != feature_index],
body=lambda x, y: add(x, y))
return sum
def compute_mean(operand, feature_index):
sum = compute_sum(operand, feature_index)
divisor = constant(size(operand) / dim(operand, feature_index),
element_type(operand))
divisor_bcast = broadcast_in_dim(divisor, [], type(sum))
return divide(sum, divisor_bcast)
def batch_norm_grad(operand, scale, mean, variance, grad_output, epsilon, feature_index):
# Broadcast inputs to type(operand)
scale_bcast = broadcast_in_dim(scale, [feature_index], type(operand))
mean_bcast = broadcast_in_dim(mean, [feature_index], type(operand))
variance_bcast = broadcast_in_dim(variance, [feature_index], type(operand))
epsilon_bcast = broadcast_in_dim(constant(epsilon, element_type(operand)), [],
type(operand))
# Perform normalization using the provided `mean` and `variance`
# Intermediate values will be useful for computing gradients
centered_operand = subtract(operand, mean_bcast)
stddev = sqrt(add(variance_bcast, epsilon_bcast))
normalized_operand = divide(centered_operand, stddev)
# Use the implementation from batchnorm_expander.cc in XLA
# Temporary variables have exactly the same names as in the C++ code
elements_per_feature = broadcast_in_dim(
constant(divide(size(operand), dim(operand, feature_index)),
element_type(grad_output)),
[], type(operand))
i1 = multiply(grad_output, elements_per_feature)
i2 = broadcast_in_dim(
compute_sum(grad_output, feature_index), [feature_index], type(operand))
i3 = broadcast_in_dim(
compute_sum(multiply(grad_output, centered_operand), feature_index),
[feature_index], type(operand))
i4 = multiply(i3, centered_operand)
i5 = divide(i4, add(variance_bcast, epsilon_bcast))
i6 = subtract(subtract(i1, i2), i5)
grad_operand =
multiply(divide(divide(scale_bcast, stddev), elements_per_feature), i6)
grad_scale =
compute_sum(multiply(grad_output, normalized_operand), feature_index)
grad_offset = compute_sum(grad_output, feature_index)
return grad_operand, grad_scale, grad_offset
สำหรับประเภทที่กำหนดปริมาณ ให้ดำเนินการ
dequantize_batch_norm_grad_or_training_quantize(lambda operand, scale, mean,
variance, grad_output: batch_norm_grad(operand, scale, mean, variance,
grad_output, epsilon, feature_index), operand, scale, mean, variance,
grad_output, type(grad_operand), type(grad_scale), type(feature_index))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C1-C3), (C5) |
| (I2) | scale |
เทนเซอร์ 1 มิติของประเภทจุดลอยตัวหรือประเภทที่วัดปริมาณต่อเทนเซอร์ | (C2), (C4), (C5) |
| (I3) | mean |
เทนเซอร์ 1 มิติของประเภทจุดลอยตัวหรือประเภทที่วัดปริมาณต่อเทนเซอร์ | (C2), (C4) |
| (I4) | variance |
เทนเซอร์ 1 มิติของประเภทจุดลอยตัวหรือประเภทที่วัดปริมาณต่อเทนเซอร์ | (C2), (C4) |
| (I5) | grad_output |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C2), (C3) |
| (I6) | epsilon |
ค่าคงที่ของประเภท f32 |
|
| (I7) | feature_index |
ค่าคงที่ของประเภท si64 |
(C1), (C5) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
grad_operand |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C2), (C3) |
grad_scale |
เทนเซอร์ 1 มิติของประเภทจุดลอยตัวหรือประเภทที่วัดปริมาณต่อเทนเซอร์ | (C2), (C4) |
grad_offset |
เทนเซอร์ 1 มิติของประเภทจุดลอยตัวหรือประเภทที่วัดปริมาณต่อเทนเซอร์ | (C2), (C4) |
ข้อจำกัด
- (C1)
0 <= feature_index < rank(operand) - (C2)
operand,scale,mean,variance,grad_output,grad_operand,grad_scaleและgrad_offsetมีbaseline_element_typeเดียวกัน - (ค3)
operand,grad_outputและgrad_operandมีรูปร่างเหมือนกัน - (C4)
scale,mean,variance,grad_scaleและgrad_offsetมี รูปร่างเดียวกัน - (C5)
size(scale) = dim(operand, feature_index)
ตัวอย่าง
// %operand: [
// [[1.0, 2.0], [3.0, 4.0]],
// [[3.0, 4.0], [1.0, 2.0]]
// ]
// %scale: [1.0, 1.0]
// %mean: [2.0, 3.0]
// %variance: [1.0, 1.0]
// %grad_output: [
// [[0.1, 0.1], [0.1, 0.1]],
// [[0.1, 0.1], [0.1, 0.1]]
// ]
%grad_operand, %grad_scale, %grad_offset =
"stablehlo.batch_norm_grad"(%operand, %scale, %mean, %variance, %grad_output) {
epsilon = 0.0 : f32,
feature_index = 2 : i64
} : (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>, tensor<2xf64>,
tensor<2x2x2xf64>) -> (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>)
// %grad_operand: [
// [[0.0, 0.0], [0.0, 0.0]],
// [[0.0, 0.0], [0.0, 0.0]]
// ]
// %grad_scale: [0.0, 0.0]
// %grad_offset: [0.4, 0.4]
batch_norm_inference
ความหมาย
ปรับoperandเทนเซอร์ในทุกมิติข้อมูล ยกเว้นมิติข้อมูลfeature_index และสร้างresultเทนเซอร์ ในรูปแบบที่เป็นทางการมากขึ้น การดำเนินการนี้
สามารถแสดงเป็นการแยกย่อยเป็นการดำเนินการ StableHLO ที่มีอยู่
โดยใช้ไวยากรณ์ Python ดังนี้
def batch_norm_inference(operand, scale, offset, mean, variance, epsilon, feature_index):
# Broadcast inputs to shape(operand)
scale_bcast = broadcast_in_dim(scale, [feature_index], type(operand))
offset_bcast = broadcast_in_dim(offset, [feature_index], type(operand))
mean_bcast = broadcast_in_dim(mean, [feature_index], type(operand))
variance_bcast = broadcast_in_dim(variance, [feature_index], type(operand))
epsilon_bcast = broadcast_in_dim(constant(epsilon, element_type(operand)), [],
type(operand))
# Perform normalization using the provided `mean` and `variance` instead of
# computing them like `batch_norm_training` does.
centered_operand = subtract(operand, mean_bcast)
stddev = sqrt(add(variance_bcast, epsilon_bcast))
normalized_operand = divide(centered_operand, stddev)
return add(multiply(scale_bcast, normalized_operand), offset_bcast)
สำหรับประเภทที่กำหนดปริมาณ ให้ดำเนินการ
dequantize_op_quantize(lambda operand, scale, offset, mean, variance:
batch_norm_inference(operand, scale, offset, mean, variance, epsilon,
feature_index), operand, scale, offset, mean, variance, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C1-C7) |
| (I2) | scale |
เทนเซอร์ 1 มิติของประเภทจุดลอยตัวหรือประเภทที่วัดปริมาณต่อเทนเซอร์ | (C2), (C3) |
| (I3) | offset |
เทนเซอร์ 1 มิติของประเภทจุดลอยตัวหรือประเภทที่วัดปริมาณต่อเทนเซอร์ | (C2), (C4) |
| (I4) | mean |
เทนเซอร์ 1 มิติของประเภทจุดลอยตัวหรือประเภทที่วัดปริมาณต่อเทนเซอร์ | (C5) |
| (I5) | variance |
เทนเซอร์ 1 มิติของประเภทจุดลอยตัวหรือประเภทที่วัดปริมาณต่อเทนเซอร์ | (C2), (C6) |
| (I6) | epsilon |
ค่าคงที่ของประเภท f32 |
|
| (I7) | feature_index |
ค่าคงที่ของประเภท si64 |
(C1), (C3-C6) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C2), (C7) |
ข้อจำกัด
- (C1)
0 <= feature_index < rank(operand) - (C2)
operand,scale,offset,mean,varianceและresultมีbaseline_element_typeเดียวกัน - (C3)
size(scale) = dim(operand, feature_index) - (C4)
size(offset) = dim(operand, feature_index) - (C5)
size(mean) = dim(operand, feature_index) - (C6)
size(variance) = dim(operand, feature_index) - (C7)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %operand: [
// [[1.0, 2.0], [3.0, 4.0]],
// [[3.0, 4.0], [1.0, 2.0]]
// ]
// %scale: [1.0, 1.0]
// %offset: [1.0, 1.0]
// %mean: [2.0, 3.0]
// %variance: [1.0, 1.0]
%result = "stablehlo.batch_norm_inference"(%operand, %scale, %offset, %mean, %variance) {
epsilon = 0.0 : f32,
feature_index = 2 : i64
} : (tensor<2x2x2xf64>, tensor<2xf64>, tensor<2xf64>, tensor<2xf64>, tensor<2xf64>) -> tensor<2x2x2xf64>
// %result: [
// [[0.0, 0.0], [2.0, 2.0]],
// [[2.0, 2.0], [0.0, 0.0]]
// ]
batch_norm_training
ความหมาย
คำนวณค่าเฉลี่ยและความแปรปรวนในทุกมิติ ยกเว้นมิติข้อมูล feature_index
และทำให้เทนเซอร์ operand เป็นมาตรฐาน ซึ่งจะสร้างเทนเซอร์ output, batch_mean
และ batch_var ในรูปแบบที่เป็นทางการมากขึ้น การดำเนินการนี้สามารถแสดงเป็นการ
แยกย่อยเป็นการดำเนินการ StableHLO ที่มีอยู่โดยใช้ไวยากรณ์ Python ดังนี้
def compute_mean(operand, feature_index):
(sum,) = reduce(
inputs=[operand],
init_values=[constant(0, element_type(operand))],
dimensions=[i for i in range(rank(operand)) if i != feature_index],
body=lambda x, y: add(x, y))
divisor = constant(size(operand) / dim(operand, feature_index),
element_type(operand))
divisor_bcast = broadcast_in_dim(divisor, [], type(sum))
return divide(sum, divisor_bcast)
def compute_variance(operand, feature_index):
mean = compute_mean(operand, feature_index)
mean_bcast = broadcast_in_dim(mean, [feature_index], type(operand))
centered_operand = subtract(operand, mean_bcast)
return compute_mean(mul(centered_operand, centered_operand), feature_index)
def batch_norm_training(operand, scale, offset, epsilon, feature_index):
mean = compute_mean(operand, feature_index)
variance = compute_variance(operand, feature_index)
return batch_norm_inference(operand, scale, offset, mean, variance, epsilon,
feature_index),
mean, variance
สำหรับประเภทที่กำหนดปริมาณ ให้ดำเนินการ
dequantize_batch_norm_grad_or_training_quantize(lambda operand, scale, offset:
batch_norm_training(operand, scale, offset, epsilon, feature_index), operand,
scale, offset, type(output), type(batch_mean), type(batch_var))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C1) |
| (I2) | scale |
เทนเซอร์ 1 มิติของจุดลอยตัวหรือควอนไทซ์ต่อเทนเซอร์ | (C2), (C3) |
| (I3) | offset |
เทนเซอร์ 1 มิติของจุดลอยตัวหรือควอนไทซ์ต่อเทนเซอร์ | (C2), (C4) |
| (I4) | epsilon |
ค่าคงที่ของประเภท f32 |
(C1), (C3-C6) |
| (I5) | feature_index |
ค่าคงที่ของประเภท si64 |
(C1), (C3-C6) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
output |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C7) |
batch_mean |
เทนเซอร์ 1 มิติของจุดลอยตัวหรือควอนไทซ์ต่อเทนเซอร์ | (C2), (C5) |
batch_var |
เทนเซอร์ 1 มิติของจุดลอยตัวหรือควอนไทซ์ต่อเทนเซอร์ | (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
ความหมาย
ดำเนินการบิตแคสต์กับเทนเซอร์ operand และสร้างเทนเซอร์ result
โดยตีความบิตของเทนเซอร์ operand ทั้งหมดใหม่โดยใช้
ประเภทของเทนเซอร์ result
กล่าวอย่างเป็นทางการมากขึ้น เมื่อกำหนด E = element_type(operand), E' = element_type(result)
และ R = rank(operand)
- หาก
num_bits(E') < num_bits(E)bits(result[i0, ..., iR-1, :]) = bits(operand[i0, ..., iR-1]) - หาก
num_bits(E') > num_bits(E)bits(result[i0, ..., iR-2]) = bits(operand[i0, ..., iR-2, :]) - หาก
num_bits(E') = num_bits(E)bits(result[i0, ..., iR-1]) = bits(operand[i0, ..., iR-1])
bits จะแสดงการแทนค่าที่กำหนดในหน่วยความจำ และลักษณะการทำงานของฟังก์ชันนี้
จะขึ้นอยู่กับการใช้งาน เนื่องจากรูปแบบที่แน่นอนของเทนเซอร์
ขึ้นอยู่กับการใช้งาน และรูปแบบที่แน่นอนของประเภทองค์ประกอบก็
ขึ้นอยู่กับการใช้งานด้วยเช่นกัน
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (C1-C2) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (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 + 1dim(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 - 1dim(result, i) = dim(operand, i)สำหรับ0 <= i < Rทุกคนdim(operand, R - 1) * num_bits(E) = num_bits(E')
- หาก
- (C2) หาก
is_complex(operand) or is_complex(result)ให้is_complex(operand) and is_complex(result)
ตัวอย่าง
// %operand: 0x0123456789ABCDEF
%result = "stablehlo.bitcast_convert"(%operand) : (tensor<f64>) -> tensor<4xf16>
// %result: [0xCDEF, 0x89AB, 0x4567, 0x0123] // little-endian representation
broadcast_in_dim
ความหมาย
ขยายมิติข้อมูลและ/หรืออันดับของเทนเซอร์อินพุตโดยการทำซ้ำข้อมูล
ในเทนเซอร์ operand และสร้างเทนเซอร์ result ในรูปแบบที่เป็นทางการมากขึ้น
result[result_index] = operand[operand_index] โดยที่สำหรับ d ทั้งหมดใน
axes(operand):
operand_index[d] = 0หากdim(operand, d) = 1operand_index[d] = result_index[broadcast_dimensions[d]]หรือไม่เช่นนั้น
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (C1-C2), (C5-C6) |
| (I2) | broadcast_dimensions |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C2-C6) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (C1), (C3), (C5-C6) |
ข้อจำกัด
- (C1)
element_type(result)มีค่าดังนี้element_type(operand)หาก!is_per_axis_quantized(operand)element_type(operand)ยกเว้นquantization_dimension(operand),scales(operand)และzero_points(operand)อาจแตกต่างจากquantization_dimension(result),scales(result)และzero_points(result)ตามลำดับ
- (C2)
size(broadcast_dimensions) = rank(operand) - (C3)
0 <= broadcast_dimensions < rank(result) - (C4)
is_unique(broadcast_dimensions) - (C5) สำหรับ
dทั้งหมดในaxes(operand)dim(operand, d) = 1หรือdim(operand, d) = dim(result, broadcast_dimensions[d])
- (C6) หาก
is_per_axis_quantized(result)quantization_dimension(result) = broadcast_dimensions[quantization_dimension(operand)]- หาก
dim(operand, quantization_dimension(operand)) = 1ให้scales(result)[i] = scales(operand)[0] and zero_points(result)[i] = zero_points(operand)[0] for i in range(dim(result, quantization_dimension(result)))
ตัวอย่าง
// %operand: [
// [1, 2, 3]
// ]
%result = "stablehlo.broadcast_in_dim"(%operand) {
broadcast_dimensions = array<i64: 2, 1>
} : (tensor<1x3xi32>) -> tensor<2x3x2xi32>
// %result: [
// [
// [1, 1],
// [2, 2],
// [3, 3]
// ],
// [
// [1, 1],
// [2, 2],
// [3, 3]
// ]
// ]
เคส
ความหมาย
สร้างเอาต์พุตจากการเรียกใช้ฟังก์ชันเพียง 1 ฟังก์ชันจาก branches
โดยขึ้นอยู่กับค่าของ index ในรูปแบบที่เป็นทางการมากขึ้น result = selected_branch()
โดยที่
selected_branch = branches[index]หาก0 <= index < size(branches)selected_branch = branches[-1]หรือไม่เช่นนั้น
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | index |
เทนเซอร์ 0 มิติของประเภท si32 |
|
| (I2) | branches |
ฟังก์ชันที่มีจำนวนอาร์กิวเมนต์ไม่แน่นอน | (C1-C4) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
results |
จำนวนเทนเซอร์ เทนเซอร์เชิงปริมาณ หรือโทเค็นที่เปลี่ยนแปลงได้ | (C4) |
ข้อจำกัด
- (C1)
0 < size(branches) - (C2)
input_types(branches...) = [] - (C3)
same(output_types(branches...)) - (C4)
type(results...) = output_types(branches[0])
ตัวอย่าง
// %index: -1
// %result_branch0: [0, 0]
// %result_branch1: [1, 1]
%result0, %result1 = "stablehlo.case"(%index) ({
"stablehlo.return"(%result_branch0, %result_branch0) : (tensor<2xi64>, tensor<2xi64>) -> ()
}, {
"stablehlo.return"(%result_branch1, %result_branch1) : (tensor<2xi64>, tensor<2xi64>) -> ()
}) : (tensor<i32>) -> (tensor<2xi64>, tensor<2xi64>)
// %result0: [1, 1]
// %result1: [1, 1]
cbrt
ความหมาย
ดำเนินการรูปลูกบาศก์แบบทีละองค์ประกอบในเทนเซอร์ operand และสร้างเทนเซอร์ result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับ Float:
rootn(x, 3)จาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: รากที่สามของจำนวนเชิงซ้อน
- สำหรับประเภทที่กำหนดปริมาณ:
dequantize_op_quantize(cbrt, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %operand: [0.0, 1.0, 8.0, 27.0]
%result = "stablehlo.cbrt"(%operand) : (tensor<4xf64>) -> tensor<4xf64>
// %result: [0.0, 1.0, 2.0, 3.0]
ceil
ความหมาย
ดำเนินการเพดานระดับองค์ประกอบของเทนเซอร์ operand และสร้างเทนเซอร์ result
ใช้การดำเนินการ roundToIntegralTowardPositive จากข้อกำหนด IEEE-754
สำหรับประเภทที่กำหนดปริมาณ ให้ดำเนินการ
dequantize_op_quantize(ceil, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %operand: [-0.8166, -0.2530, 0.2530, 0.8166, 2.0]
%result = "stablehlo.ceil"(%operand) : (tensor<5xf32>) -> tensor<5xf32>
// %result: [-0.0, -0.0, 1.0, 1.0, 2.0]
cholesky
ความหมาย
คำนวณการแยกย่อย Cholesky ของเมทริกซ์กลุ่ม
ในรูปแบบที่เป็นทางการมากขึ้น สำหรับ i ทั้งหมดใน index_space(result)
result[i0, ..., iR-3, :, :] คือการแยกตัวประกอบ Cholesky ของ
a[i0, ..., iR-3, :, :] ในรูปแบบของเมทริกซ์สามเหลี่ยมล่าง
(หาก lower คือ true) หรือเมทริกซ์สามเหลี่ยมบน (หาก lower คือ false)
ค่าเอาต์พุตในสามเหลี่ยมด้านตรงข้าม กล่าวคือ สามเหลี่ยมด้านบนแบบเข้มงวดหรือสามเหลี่ยมด้านล่างแบบเข้มงวดตามลำดับ จะขึ้นอยู่กับการใช้งาน
หากมี i ที่เมทริกซ์อินพุตไม่ใช่เมทริกซ์ Hermitian positive-definite
ระบบจะไม่กำหนดลักษณะการทำงาน
สำหรับประเภทที่กำหนดปริมาณ ให้ดำเนินการ
dequantize_op_quantize(lambda operand: cholesky(operand, lower), a, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | a |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1-C3) |
| (I2) | lower |
ค่าคงที่ของประเภท i1 |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(a) = baseline_type(result) - (C2)
2 <= rank(a) - (C3)
dim(a, -2) = dim(a, -1)
ตัวอย่าง
// %a: [
// [1.0, 2.0, 3.0],
// [2.0, 20.0, 26.0],
// [3.0, 26.0, 70.0]
// ]
%result = "stablehlo.cholesky"(%a) {
lower = true
} : (tensor<3x3xf32>) -> tensor<3x3xf64>
// %result: [
// [1.0, 0.0, 0.0],
// [2.0, 4.0, 0.0],
// [3.0, 5.0, 6.0]
// ]
แคลมป์
ความหมาย
ยึดทุกองค์ประกอบของเทนเซอร์ operand ระหว่างค่าต่ำสุดและสูงสุด
และสร้างเทนเซอร์ result ในรูปแบบที่เป็นทางการมากขึ้น result[result_index] =
minimum(maximum(operand[result_index], min_element), max_element)
โดยที่ min_element = rank(min) = 0 ? min[] : min[result_index]
max_element = rank(max) = 0 ? max[] : max[result_index] สำหรับประเภทที่กำหนดปริมาณ
จะดำเนินการ dequantize_op_quantize(clamp, min, operand, max, type(result))
การกำหนดลำดับสำหรับจำนวนเชิงซ้อนเกี่ยวข้องกับความหมายที่น่าประหลาดใจ ดังนั้นในอนาคตเราจึงวางแผนที่จะยกเลิกการรองรับจำนวนเชิงซ้อน สำหรับการดำเนินการนี้ (#560)
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | min |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1), (C3) |
| (I2) | operand |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1-C4) |
| (I3) | max |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C2), (C3) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C4) |
ข้อจำกัด
- (C1)
rank(min) = 0 or shape(min) = shape(operand) - (C2)
rank(max) = 0 or shape(max) = shape(operand) - (C3)
baseline_element_type(min) = baseline_element_type(operand) = baseline_element_type(max) - (C4)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %min: [5, 10, 15]
// %operand: [3, 13, 23]
// %max: [10, 15, 20]
%result = "stablehlo.clamp"(%min, %operand, %max) : (tensor<3xi32>, tensor<3xi32>, tensor<3xi32>) -> tensor<3xi32>
// %result: [5, 13, 20]
collective_broadcast
ความหมาย
ภายในกลุ่มกระบวนการแต่ละกลุ่มในตารางกระบวนการ StableHLO ให้ส่งค่าของเทนเซอร์ operand จากกระบวนการต้นทางไปยังกระบวนการเป้าหมาย และสร้างเทนเซอร์ result
การดำเนินการจะแยกตารางกระบวนการ StableHLO ออกเป็น process_groups ซึ่งกำหนดไว้ดังนี้
cross_replica(replica_groups)หากchannel_id <= 0cross_partition(replica_groups)หากchannel_id > 0
หลังจากนั้น result@process จะได้รับจาก
operand@process_groups[i, 0]หากมีiที่ทำให้กระบวนการอยู่ในprocess_groups[i]broadcast_in_dim(constant(is_quantized(result) ? quantize(0, element_type(result)) : 0, element_type(result)), [], type(result))มิฉะนั้น
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C3) |
| (I2) | replica_groups |
จำนวนตัวแปรของค่าคงที่เทนเซอร์ 1 มิติประเภท si64 |
(C1), (C2) |
| (I3) | channel_id |
ค่าคงที่ของประเภท si64 |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C3) |
ข้อจำกัด
- (C1)
is_unique(replica_groups) - (C2)
0 <= replica_groups < NโดยกำหนดNดังนี้num_replicasหากใช้cross_replicanum_partitionsหากใช้cross_partition
- (C3)
type(result) = type(operand)
ตัวอย่าง
// num_replicas: 4
// num_partitions: 1
// %operand@(0, 0): [[1, 2]]
// %operand@(1, 0): [[3, 4]]
// %operand@(2, 0): [[5, 6]]
// %operand@(3, 0): [[7, 8]]
%result = "stablehlo.collective_broadcast"(%operand) {
replica_groups = dense<[[2, 1]]> : tensor<1x2xi64>,
channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
} : (tensor1x2xi64>) -> tensor<1x2xi64>
// %result@(0, 0): [[0, 0]]
// %result@(1, 0): [[5, 6]]
// %result@(2, 0): [[5, 6]]
// %result@(3, 0): [[0, 0]]
collective_permute
ความหมาย
ภายในกลุ่มกระบวนการแต่ละกลุ่มในตารางกระบวนการ StableHLO จะส่งค่าของเทนเซอร์
operand จากกระบวนการต้นทางไปยังกระบวนการเป้าหมาย และสร้างเทนเซอร์
result
การดำเนินการจะแยกตารางกระบวนการ StableHLO ออกเป็น process_groups ซึ่งกำหนดไว้ดังนี้
cross_replica(source_target_pairs)หากchannel_id <= 0cross_partition(source_target_pairs)หากchannel_id > 0
หลังจากนั้น result@process จะได้รับจาก
operand@process_groups[i, 0]หากมีiที่process_groups[i, 1] = processbroadcast_in_dim(constant(is_quantized(result) ? quantize(0, element_type(result)) : 0, element_type(result)), [], type(result))มิฉะนั้น
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C5) |
| (I2) | source_target_pairs |
ค่าคงที่เทนเซอร์ 2 มิติของประเภท si64 |
(C1-C4) |
| (I3) | channel_id |
ค่าคงที่ของประเภท si64 |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1) |
ข้อจำกัด
- (C1)
dim(source_target_pairs, 1) = 2 - (C2)
is_unique(source_target_pairs[:, 0]) - (C3)
is_unique(source_target_pairs[:, 1]) - (C4)
0 <= source_target_pairs < Nโดยที่Nมีคำจำกัดความดังนี้num_replicasหากใช้cross_replicanum_partitionsหากใช้cross_partition
- (C5)
type(result) = type(operand)
ตัวอย่าง
// num_replicas: 3
// num_partitions: 1
// %operand@(0, 0): [[1, 2], [3, 4]]
// %operand@(1, 0): [[5, 6], [7, 8]]
// %operand@(2, 0): [[9, 10], [11, 12]]
%result = "stablehlo.collective_permute"(%operand) {
source_target_pairs = dense<[[0, 1], [1, 2]]> : tensor<2x2xi64>,
channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
} : (tensor<2x2xi64>) -> tensor<2x2xi64>
//
// %result@(0, 0): [[0, 0], [0, 0]]
// %result@(1, 0): [[1, 2], [3, 4]]
// %result@(2, 0): [[5, 6], [7, 8]]
เปรียบเทียบ
ความหมาย
ทําการเปรียบเทียบแบบทีละองค์ประกอบของเทนเซอร์ lhs และ rhs ตาม comparison_direction และ compare_type และสร้างเทนเซอร์ result
ค่าของ comparison_direction และ compare_type มีความหมายดังนี้
สำหรับประเภทองค์ประกอบบูลีนและจำนวนเต็ม ให้ทำดังนี้
EQ:lhs = rhsNE:lhs != rhsGE:lhs >= rhsGT:lhs > rhsLE:lhs <= rhsLT:lhs < rhs
สำหรับประเภทองค์ประกอบทศนิยมที่มี compare_type = FLOAT ตัวดำเนินการจะใช้การดำเนินการ IEEE-754 ต่อไปนี้
EQ:compareQuietEqualNE:compareQuietNotEqualGE:compareQuietGreaterEqualGT:compareQuietGreaterLE:compareQuietLessEqualLT:compareQuietLess
สำหรับประเภทองค์ประกอบทศนิยมที่มี compare_type = TOTALORDER ตัวดำเนินการจะใช้การผสมผสานระหว่างการดำเนินการ totalOrder และ compareQuietEqual จาก IEEE-754
สำหรับประเภทองค์ประกอบที่ซับซ้อน ระบบจะทำการเปรียบเทียบตามพจนานุกรมของคู่ (real, imag) โดยใช้ comparison_direction และ compare_type ที่ระบุ
การกำหนดลำดับให้กับจำนวนเชิงซ้อนเกี่ยวข้องกับความหมายที่น่าประหลาดใจ
ดังนั้นในอนาคตเราจึงวางแผนที่จะยกเลิกการรองรับจำนวนเชิงซ้อน
เมื่อ comparison_direction เป็น GE, GT, LE หรือ LT
(#560)
สำหรับประเภทที่เล็กลง ให้ดำเนินการ dequantize_compare(lhs, rhs,
comparison_direction)
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | lhs |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1-C3) |
| (I2) | rhs |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1-C2) |
| (I3) | comparison_direction |
enum ของ EQ, NE, GE, GT, LE และ LT |
|
| (I4) | compare_type |
enum ของ FLOAT, TOTALORDER, SIGNED และ UNSIGNED |
(C3) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor ประเภทบูลีน | (C2) |
ข้อจำกัด
- (C1)
baseline_element_type(lhs) = baseline_element_type(rhs) - (C2)
shape(lhs) = shape(rhs) = shape(result) - (C3)
compare_typeมีคำจำกัดความดังนี้SIGNEDหากis_signed_integer(element_type(lhs))UNSIGNEDหากis_unsigned_integer(element_type(lhs)) or is_boolean(element_type(lhs))FLOATหรือTOTALORDERหากis_float(element_type(lhs))FLOATหากis_complex(element_type(lhs))
ตัวอย่าง
// %lhs: [1.0, 3.0]
// %rhs: [1.1, 2.9]
%result = "stablehlo.compare"(%lhs, %rhs) {
comparison_direction = #stablehlo<comparison_direction LT>,
compare_type = #stablehlo<comparison_type FLOAT>
} : (tensor<2xf32>, tensor<2xf32>) -> tensor<2xi1>
// %result: [true, false]
ซับซ้อน
ความหมาย
ทำการแปลงค่าทีละองค์ประกอบเป็นค่าเชิงซ้อนจากคู่ของค่าจริงและค่าจินตภาพ lhs และ rhs แล้วสร้างเทนเซอร์ result
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | lhs |
เทนเซอร์ประเภท f32 หรือ f64 |
(C1-C3) |
| (I2) | rhs |
เทนเซอร์ประเภท f32 หรือ f64 |
(C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor ของประเภทที่ซับซ้อน | (C2), (C3) |
ข้อจำกัด
- (C1)
type(lhs) = type(rhs) - (C2)
shape(result) = shape(lhs) - (C3)
element_type(result)มีประเภทcomplex<E>โดยที่E = element_type(lhs)
ตัวอย่าง
// %lhs: [1.0, 3.0]
// %rhs: [2.0, 4.0]
%result = "stablehlo.complex"(%lhs, %rhs) : (tensor<2xf64>, tensor<2xf64>) -> tensor<2xcomplex<f64>>
// %result: [(1.0, 2.0), (3.0, 4.0)]
การผสม
ความหมาย
แคปซูลการดำเนินการที่ประกอบด้วยการดำเนินการ StableHLO อื่นๆ
โดยรับ inputs และ composite_attributes และสร้าง results
ระบบจะใช้ความหมายของตัวดำเนินการโดยแอตทริบิวต์ decomposition สามารถแทนที่composite op ด้วยการแยกย่อยโดยไม่เปลี่ยนความหมายของโปรแกรม
ในกรณีที่การแทรกการแยกย่อยไม่ได้ให้ความหมายของ Op เดียวกัน ให้ใช้ custom_call
ฟิลด์ version (ค่าเริ่มต้นคือ 0) ใช้เพื่อระบุเมื่อมีการเปลี่ยนแปลงความหมายของคอมโพสิต
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท |
|---|---|---|
| (I1) | inputs |
จำนวนค่าที่เปลี่ยนแปลงได้ |
| (I2) | name |
ค่าคงที่ของประเภท string |
| (I3) | composite_attributes |
พจนานุกรมแอตทริบิวต์ |
| (I4) | decomposition |
ค่าคงที่ของประเภท string |
| (I5) | version |
ค่าคงที่ของประเภท si32 |
เอาต์พุต
| ชื่อ | ประเภท |
|---|---|
results |
จำนวนค่าที่เปลี่ยนแปลงได้ |
ข้อจำกัด
- (C1)
is_namespaced_op_name(name) - (C2)
is_defined_in_parent_scope(decomposition) - (C3)
types(inputs...) == input_types(decomposition) - (C4)
types(results...) == output_types(decomposition)
ตัวอย่าง
%results = "stablehlo.composite"(%input0, %input1) {
name = "my_namespace.my_op",
composite_attributes = {
my_attribute = "my_value"
},
decomposition = @my_op,
version = 1 : i32
} : (tensor<f32>, tensor<f32>) -> tensor<f32>
เชื่อมโยง
ความหมาย
ต่อกัน inputs ตามมิติข้อมูล dimension ในลำดับเดียวกับอาร์กิวเมนต์ที่ระบุ
และสร้างเทนเซอร์ result ในรูปแบบที่เป็นทางการมากขึ้น
result[i0, ..., id, ..., iR-1] = inputs[k][i0, ..., kd, ..., iR-1] โดยที่
id = d0 + ... + dk-1 + kddมีค่าเท่ากับdimensionและd0, ... คือขนาดมิติที่dของinputs
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | inputs |
เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ | (C1-C6) |
| (I2) | dimension |
ค่าคงที่ของประเภท si64 |
(C2), (C4), (C6) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (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 |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (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]]
ทำให้เกิด Conversion
ความหมาย
ทำการแปลงทีละองค์ประกอบจากองค์ประกอบประเภทหนึ่งไปยังอีกประเภทหนึ่งในเทนเซอร์ operand และสร้างเทนเซอร์ result
สําหรับ Conversion boolean-to-any-supported-type ค่า false จะ
แปลงเป็น 0 และค่า true จะแปลงเป็น 1 สำหรับ Conversion any-supported-type-to-boolean ระบบจะแปลงค่า 0 เป็น false และแปลงค่าที่ไม่ใช่ 0 เป็น true ดูวิธีการทำงานนี้
สำหรับประเภทที่ซับซ้อนได้ที่ด้านล่าง
สำหรับ Conversion ที่เกี่ยวข้องกับจำนวนเต็มเป็นจำนวนเต็ม จำนวนเต็มเป็นทศนิยม หรือทศนิยมเป็นทศนิยม หากค่าแหล่งที่มาสามารถแสดงในประเภทปลายทางได้อย่างแม่นยำ ค่าผลลัพธ์จะเป็นการแสดงค่าที่แม่นยำนั้น มิฉะนั้น จะแจ้งลักษณะการทำงานในภายหลัง (#180)
สำหรับ Conversion ที่เกี่ยวข้องกับ floating-point-to-integer ระบบจะตัดส่วนเศษส่วนออก หากค่าที่ถูกตัดทอนแสดงในประเภทปลายทางไม่ได้ ลักษณะการทำงานจะยังไม่กำหนด (#180)
Conversion ที่เกี่ยวข้องกับจำนวนเชิงซ้อนเป็นจำนวนเชิงซ้อนจะทำงานเหมือนกับ Conversion จากจำนวนทศนิยมเป็นจำนวนทศนิยมสำหรับการแปลงส่วนที่เป็นจำนวนจริงและส่วนที่เป็นจำนวนจินตภาพ
สำหรับ Conversion complex-to-any-other-type และ any-other-type-to-complex ระบบจะไม่สนใจมูลค่าสมมติของแหล่งที่มาหรือตั้งค่ามูลค่าสมมติของปลายทางเป็น ศูนย์ตามลำดับ การแปลงส่วนจริงจะเป็นไปตาม การแปลงจุดลอย
ในทางทฤษฎี การดำเนินการนี้สามารถแสดงการยกเลิกการกำหนดปริมาณ (การแปลงจากเทนเซอร์ที่กำหนดปริมาณเป็นเทนเซอร์ปกติ) การกำหนดปริมาณ (การแปลงจากเทนเซอร์ปกติเป็นเทนเซอร์ที่กำหนดปริมาณ) และการกำหนดปริมาณใหม่ (การแปลงระหว่างเทนเซอร์ที่กำหนดปริมาณ) แต่ในขณะนี้เรามีการดำเนินการเฉพาะสำหรับการดำเนินการดังกล่าว - uniform_dequantize สำหรับกรณีการใช้งานแรก และ uniform_quantize สำหรับกรณีการใช้งานที่ 2 และ 3 ในอนาคต เราอาจรวม 2 การดำเนินการนี้
เข้ากับ convert (#1576)
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
Tensor | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor | (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_strideslhs_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 |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1), (C10-C11), (C14) (C25), (C27-C28), (C31-C32), (C34) |
| (I2) | rhs |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (C1), (C14-C16), (C25), (C27-C29), (C31-C34) |
| (I3) | window_strides |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C2-C3), (C25) |
| (I4) | padding |
ค่าคงที่เทนเซอร์ 2 มิติของประเภท si64 |
(C4), (C25) |
| (I5) | lhs_dilation |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C5-C6), (C25) |
| (I6) | rhs_dilation |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C7-C8), (C25) |
| (I7) | window_reversal |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท i1 |
(C9) |
| (I8) | input_batch_dimension |
ค่าคงที่ของประเภท si64 |
(C10), (C13), (C25) |
| (I9) | input_feature_dimension |
ค่าคงที่ของประเภท si64 |
(C11), (C13-C14) |
| (I10) | input_spatial_dimensions |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท 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 |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C17-C18), (C25) |
| (I14) | output_batch_dimension |
ค่าคงที่ของประเภท si64 |
(C20), (C25) |
| (I15) | output_feature_dimension |
ค่าคงที่ของประเภท si64 |
(C20), (C25), (C30) |
| (I16) | output_spatial_dimensions |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท 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 |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (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_dimensiondim(rhs, kernel_output_feature_dimension)หากresult_dim = output_feature_dimensionnum_windowsหรือในกรณีต่อไปนี้output_spatial_dimensions[spatial_dim] = result_dimlhs_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] + 1padded_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] + 1is_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 - หากการดำเนินการใช้เทนเซอร์ที่ไม่ได้ทำ Quantize ให้ทำดังนี้
- (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 โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับ Float:
cosจาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: โคไซน์เชิงซ้อน
- สำหรับประเภทที่กำหนดปริมาณ:
dequantize_op_quantize(cosine, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %operand: [
// [0.0, 1.57079632], // [0, pi/2]
// [3.14159265, 4.71238898] // [pi, 3pi/2]
// ]
%result = "stablehlo.cosine"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[1.0, 0.0], [-1.0, 0.0]]
count_leading_zeros
ความหมาย
ดำเนินการนับจำนวนบิตนำหน้าเป็น 0 แบบทีละองค์ประกอบในoperand
เทนเซอร์และสร้างเทนเซอร์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 อาจใช้เพื่อระบุข้อมูลเมตาเพิ่มเติมที่กำหนดการใช้งาน
ปัจจุบัน การดำเนินการนี้มีคอลเล็กชันข้อมูลเมตาที่ค่อนข้างไม่เป็นระเบียบ ซึ่งสะท้อนถึงวิวัฒนาการตามธรรมชาติของการดำเนินการที่คล้ายกันในคอมไพเลอร์ 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 |
จำนวนค่าคงที่แบบ Variadic ของประเภท string |
| (I7) | output_operand_aliases |
ระบุส่วนที่ใช้ชื่อแทนในเอาต์พุตและตัวถูกดำเนินการ |
เอาต์พุต
| ชื่อ | ประเภท |
|---|---|
results |
จำนวนค่าที่เปลี่ยนแปลงได้ |
(การรองรับ GPU ของ XLA) เป้าหมาย custom_call พิเศษ
มีฟังก์ชันพิเศษ 3 อย่างที่เกี่ยวข้องกับประเภท buffer ดังนี้
CreateBuffer สร้าง buffer ที่ยังไม่ได้เริ่มต้น Pin สร้าง buffer ที่เริ่มต้นแล้ว
และ Unpin ยกเลิกการจัดสรร buffer และส่งคืนเนื้อหาของ
buffercall_target_name
%uninitialized_buffer = "stablehlo.custom_call"() {
call_target_name = "CreateBuffer",
api_version = 4 : i32,
} : () -> memref<4xf64>
%initialized_buffer = "stablehlo.custom_call"(%init_value) {
call_target_name = "Pin",
api_version = 4 : i32,
} : (tensor<4xf64>) -> memref<4xf64>
%dealloc_buffer = "stablehlo.custom_call"(%initialized_buffer) {
call_target_name = "Unpin",
api_version = 4 : i32,
} : (memref<4xf64>) -> tensor<4xf64>
ชื่อแทน
การดำเนินการ custom_call บางอย่างอาจต้องใช้ส่วนหนึ่งในเอาต์พุตและอีกส่วนหนึ่งใน
ตัวถูกดำเนินการเพื่อใช้หน่วยความจำเดียวกัน ซึ่งแสดงได้ผ่าน
output_operand_aliases การแสดงคู่แทนประกอบด้วยรายการดัชนีเอาต์พุต
ของ Tuple ที่แสดงส่วนเอาต์พุต และ operand_index พร้อมกับรายการดัชนี Tuple ของตัวถูกดำเนินการ
ที่แสดงส่วนตัวถูกดำเนินการ รายการดัชนีเอาต์พุต
หรือดัชนีทูเพิลตัวถูกดำเนินการจะว่างเปล่าหากประเภทที่เกี่ยวข้องไม่ใช่ประเภท tuple
และอาจยาวเท่าใดก็ได้สำหรับประเภททูเพิลที่ซ้อนกันโดยพลการ ซึ่งคล้ายกับการแสดงแทนชื่อแทน XLA
ส่วนเอาต์พุตและส่วนอินพุตในคู่แทนต้องมีประเภทเดียวกัน สำหรับ
การดำเนินการ custom_call ที่ไม่ใช่การเรียกไปยัง CreateBuffer, Pin และ Unpin ตัว
bufferตัวถูกดำเนินการจะปรากฏในคู่ชื่อแทนได้สูงสุด 1 คู่ และเอาต์พุต buffer ต้องปรากฏในคู่ชื่อแทน 1 คู่
ตัวอย่าง
%results = "stablehlo.custom_call"(%input0) {
call_target_name = "foo",
has_side_effect = false,
backend_config = {bar = 42 : i32},
api_version = 4 : i32,
called_computations = [@foo]
} : (tensor<f64>) -> tensor<f64>
%updated_buffer = "stablehlo.custom_call"(%buffer) {
call_target_name = "Update",
api_version = 4 : i32,
output_operand_aliases = [
#stablehlo.output_operand_alias<output_tuple_indices = [],
operand_index = 0,
operand_tuple_indices = []>]
} : (memref<4xf64>) -> memref<4xf64>
หาร
ความหมาย
ทำการหารแบบทีละองค์ประกอบของเทนเซอร์ตัวตั้ง lhs และเทนเซอร์ตัวหาร rhs และ
สร้างเทนเซอร์ result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับจำนวนเต็ม: การหารจำนวนเต็มซึ่งให้ผลลัพธ์เป็นผลหารพีชคณิตโดยทิ้งส่วนที่เป็นเศษส่วน
- สำหรับ Float:
divisionจาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: การหารจำนวนเชิงซ้อน
- สำหรับประเภทที่กำหนดปริมาณ
dequantize_op_quantize(divide, lhs, rhs, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | lhs |
เทนเซอร์ของจำนวนเต็ม จุดลอย หรือประเภทเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
| (I2) | rhs |
เทนเซอร์ของจำนวนเต็ม จุดลอย หรือประเภทเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเต็ม จุดลอย หรือจำนวนเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
ตัวอย่าง
// %lhs: [17.1, -17.1, 17.1, -17.1]
// %rhs: [3.0, 3.0, -3.0, -3.0]
%result = "stablehlo.divide"(%lhs, %rhs) : (tensor<4xf32>, tensor<4xf32>) -> tensor<4xf32>
// %result: [5.66666651, -5.66666651, -5.66666651, 5.66666651]
dot_general
ความหมาย
คำนวณผลคูณสเกลาร์ระหว่างชิ้นส่วนของ lhs กับชิ้นส่วนของ rhs และสร้างเทนเซอร์ result
ในรูปแบบที่เป็นทางการมากขึ้น result[result_index] = dot_product โดยที่
lhs_result_dimensions = [d for d in axes(lhs) and d not in lhs_batching_dimensions and d not in lhs_contracting_dimensions]rhs_result_dimensions = [d for d in axes(rhs) and d not in rhs_batching_dimensions and d not in rhs_contracting_dimensions]result_batching_index + result_lhs_index + result_rhs_index = result_indexเมื่อsize(result_batching_index) = size(lhs_batching_dimensions),size(result_lhs_index) = size(lhs_result_dimensions)และsize(result_rhs_index) = size(rhs_result_dimensions)transposed_lhs = transpose(lhs, lhs_batching_dimensions + lhs_result_dimensions + lhs_contracting_dimensions)transposed_lhs_slice = slice(transposed_lhs, result_batching_index + result_lhs_index + [:, ..., :])reshaped_lhs_slice = reshape(transposed_lhs_slice, dims(lhs, lhs_contracting_dimensions))transposed_rhs = transpose(rhs, rhs_batching_dimensions + rhs_result_dimensions + rhs_contracting_dimensions)transposed_rhs_slice = slice(transposed_rhs, result_batching_index + result_rhs_index + [:, ..., :])reshaped_rhs_slice = reshape(transposed_rhs_slice, dims(rhs, rhs_contracting_dimensions))dot_product = reduce( inputs=[multiply(reshaped_lhs_slice, reshaped_rhs_slice)], init_values=[constant(0, element_type(result))], dimensions=range(size(lhs_contracting_dimensions)), body=lambda x, y: add(x, y))
สำหรับประเภทที่กำหนดปริมาณ ให้ดำเนินการ dequantize_op_quantize(
lambda lhs, rhs: dot_general(lhs, rhs, lhs_batching_dimensions,
rhs_batching_dimensions, lhs_contracting_dimensions,
rhs_contracting_dimensions, precision_config), lhs, rhs, type(result))
สำหรับประเภทที่เล็กลงแบบไฮบริด ให้ดำเนินการ hybrid_dequantize_then_op(
lambda lhs, rhs: dot_general(lhs, rhs, lhs_batching_dimensions,
rhs_batching_dimensions, lhs_contracting_dimensions,
rhs_contracting_dimensions, precision_config), lhs, rhs)
precision_config ควบคุมการแลกเปลี่ยนระหว่างความเร็วและความแม่นยำสำหรับการคำนวณในแบ็กเอนด์ของตัวเร่ง ซึ่งอาจเป็นค่าใดค่าหนึ่งต่อไปนี้ (ขณะนี้ความหมายของค่า 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 สำหรับการคำนวณที่มีความแม่นยำสูงขึ้น: bf16_6x tf32_3x ฯลฯ) สำหรับอัลกอริทึมที่ไม่มีการแยกส่วน ค่าเหล่านี้ ควรตั้งเป็น1allow_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 |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C5-C6), (C9-C10), (C12-C14), (C17-C18), (C20) |
| (I2) | rhs |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (C7-C10), (C12-C20) |
| (I3) | lhs_batching_dimensions |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C1), (C3), (C5), (C9), (C12) |
| (I4) | rhs_batching_dimensions |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C1), (C4), (C7), (C9) |
| (I5) | lhs_contracting_dimensions |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C2), (C3), (C6), (C10) |
| (I6) | rhs_contracting_dimensions |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท 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 |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (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) - หากการดำเนินการใช้เทนเซอร์ที่ไม่ได้ทำ Quantize ให้ทำดังนี้
- (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
op แต่จะระบุรูปร่างผลลัพธ์แบบไดนามิกผ่าน output_dimensions
การดำเนินการยังยอมรับแอตทริบิวต์ที่ไม่บังคับ known_expanding_dimensions, known_nonexpanding_dimensions
เพื่อแสดงความรู้แบบคงที่เกี่ยวกับลักษณะการขยายของมิติข้อมูล
หากไม่ได้ระบุไว้ ระบบจะถือว่ามิติข้อมูลทั้งหมดอาจขยายได้
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (C1-C2), (C5-C6), (C9) |
| (I2) | output_dimensions |
เทนเซอร์ 1 มิติของประเภทจำนวนเต็ม | (C7) |
| (I3) | broadcast_dimensions |
เทนเซอร์ค่าคงที่ 1 มิติประเภทจำนวนเต็ม | (C2-C6) |
| (I4) | known_expanding_dimensions |
เทนเซอร์ค่าคงที่ 1 มิติประเภทจำนวนเต็ม | (C8-C9) |
| (I5) | known_nonexpanding_dimensions |
เทนเซอร์ค่าคงที่ 1 มิติประเภทจำนวนเต็ม | (C8-C9) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (C1), (C3), (C5-C7) |
ข้อจำกัด
- (C1)
element_type(result)มีค่าดังนี้element_type(operand)หาก!is_per_axis_quantized(operand)element_type(operand)ยกเว้นquantization_dimension(operand),scales(operand)และzero_points(operand)อาจแตกต่างจากquantization_dimension(result),scales(result)และzero_points(result)ตามลำดับ
- (C2)
size(broadcast_dimensions) = rank(operand) - (C3)
0 <= broadcast_dimensions < rank(result) - (C4)
is_unique(broadcast_dimensions) - (C5) สำหรับ
dทั้งหมดในaxes(operand)dim(operand, d) = 1หรือdim(operand, d) = dim(result, broadcast_dimensions[d])
- (C6) หาก
is_per_axis_quantized(result)quantization_dimension(result) = broadcast_dimensions[quantization_dimension(operand)]- หาก
dim(operand, quantization_dimension(operand)) = 1ให้scales(result)[i] = scales(operand)[0] and zero_points(result)[i] = zero_points(operand)[0] for i in range(dim(result, quantization_dimension(result)))
- (C7)
size(output_dimensions) = rank(result) - (C8)
is_unique(known_expanding_dimensions + known_nonexpanding_dimensions) - (C9)
0 <= known_expanding_dimensions < rank(operand) - (C10)
0 <= known_nonexpanding_dimensions < rank(operand)
ตัวอย่าง
// %operand: [
// [1, 2, 3]
// ]
%operand = stablehlo.constant dense<[[1, 2, 3]]> : tensor<1x3xi64>
%output_dimensions = stablehlo.constant dense<[2, 3, 2]> : tensor<3xi64>
%result = "stablehlo.dynamic_broadcast_in_dim"(%operand, %output_dimensions) {
broadcast_dimensions = array<i64: 2, 1>,
known_expanding_dimensions = array<i64: 0>,
known_nonexpanding_dimensions = array<i64: 1>
} : (tensor<1x3xi64>, tensor<3xi64>) -> tensor<2x3x2xi64>
// %result: [
// [
// [1, 1],
// [2, 2],
// [3, 3]
// ],
// [
// [1, 1],
// [2, 2],
// [3, 3]
// ]
// ]
dynamic_conv
ความหมาย
การดำเนินการนี้จะเหมือนกับ
การแปลงคอนโวลูชัน
ในเชิงฟังก์ชัน แต่จะมีการระบุการเพิ่มพื้นที่แบบไดนามิกผ่าน padding
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | lhs |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1), (C10-C11), (C14) (C25), (C26-C27), (C30-C31), (C33) |
| (I2) | rhs |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (C1), (C14-C16), (C26-C28), (C30-C33) |
| (I3) | padding |
เทนเซอร์ 2 มิติของประเภทจำนวนเต็ม | (C4) |
| (I4) | window_strides |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C2-C3) |
| (I5) | lhs_dilation |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C5-C6) |
| (I6) | rhs_dilation |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C7-C8) |
| (I7) | window_reversal |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท i1 |
(C9) |
| (I8) | input_batch_dimension |
ค่าคงที่ของประเภท si64 |
(C10), (C13) |
| (I9) | input_feature_dimension |
ค่าคงที่ของประเภท si64 |
(C11), (C13-C14) |
| (I10) | input_spatial_dimensions |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท 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 |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C17-C18) |
| (I14) | output_batch_dimension |
ค่าคงที่ของประเภท si64 |
(C20) |
| (I15) | output_feature_dimension |
ค่าคงที่ของประเภท si64 |
(C20), (C29) |
| (I16) | output_spatial_dimensions |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท 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 |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (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_dimensiondim(rhs, kernel_output_feature_dimension)หากresult_dim = output_feature_dimensionnum_windowsหรือในกรณีต่อไปนี้output_spatial_dimensions[spatial_dim] = result_dimlhs_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] + 1padded_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] + 1is_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 - หากการดำเนินการใช้เทนเซอร์ที่ไม่ได้ทำ Quantize ให้ทำดังนี้
- (C27)
element_type(lhs) = element_type(rhs) = element_type(result)
- (C27)
- หากการดำเนินการใช้เทนเซอร์ที่กำหนดปริมาณ ให้ทำดังนี้
- (C28)
is_quantized(lhs) = is_quantized(result) and is_quantized(rhs) - (C29) หาก
is_per_axis_quantized(rhs)ให้quantization_dimension(rhs) = kernel_output_feature_dimension - (C30) หาก
is_per_axis_quantized(result)ให้quantization_dimension(result) = output_feature_dimension - หาก
is_quantized(lhs) - (C31)
storage_type(lhs) = storage_type(rhs) - (C32)
expressed_type(lhs) = expressed_type(rhs) = expressed_type(result) - (C33) หาก
is_per_tensor_quantized(rhs)ให้is_per_tensor_quantized(result) - หาก
!is_quantized(lhs) - (C34)
element_type(lhs) = expressed_type(rhs) = element_type(result)
- (C28)
ตัวอย่าง
// %lhs: [[
// [[1], [2], [5], [6]],
// [[3], [4], [7], [8]],
// [[10], [11], [14], [15]],
// [[12], [13], [16], [17]]
// ]]
//
// %rhs: [
// [[[1]], [[1]], [[1]]],
// [[[1]], [[1]], [[1]]],
// [[[1]], [[1]], [[1]]]
// ]
// %padding: [[1, 1],
// [1, 1]]
%result = "stablehlo.dynamic_conv"(%lhs, %rhs, %padding) {
window_strides = array<i64: 4, 4>,
lhs_dilation = array<i64: 2, 2>,
rhs_dilation = array<i64: 1, 1>,
window_reversal = array<i1: false, false>,
dimension_numbers = #stablehlo.conv<raw
input_batch_dimension = 0,
input_feature_dimension = 3,
input_spatial_dimensions = [0, 1],
kernel_input_feature_dimension = 2,
kernel_output_feature_dimension = 3,
kernel_spatial_dimensions = [0, 1],
output_batch_dimension = 0,
output_feature_dimension = 3,
output_spatial_dimensions = [1, 2]
>,
feature_group_count = 1 : i64,
batch_group_count = 1 : i64,
precision_config = [#stablehlo<precision DEFAULT>, #stablehlo<precision DEFAULT>]
} : (tensor<1x4x4x1xi64>, tensor<3x3x1x1xi64>, tensor<2x2xi64>) -> tensor<1x2x2x1xi64>
// %result: [[
// [[1], [5]],
// [[10], [14]]
// ]]
dynamic_gather
ความหมาย
การดำเนินการนี้มีฟังก์ชันเหมือนกับ
gather
op โดยมี slice_sizes ที่ระบุแบบไดนามิกเป็นค่า
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1), (C7), (C10-C12), (C14) |
| (I2) | start_indices |
Tensor ประเภทจำนวนเต็ม | (C2), (C3), (C13) |
| (I3) | slice_sizes |
เทนเซอร์ 1 มิติของประเภทจำนวนเต็ม | (C8), (C11-C13) |
| (I4) | offset_dims |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C1), (C4-C5), (C13) |
| (I5) | collapsed_slice_dims |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C1), (C6-C8), (C13) |
| (I6) | start_index_map |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C3), (C9), (C10) |
| (I7) | index_vector_dim |
ค่าคงที่ของประเภท si64 |
(C2), (C3), (C13) |
| (I8) | indices_are_sorted |
ค่าคงที่ของประเภท i1 |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (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
op แต่จะระบุรูปร่างผลลัพธ์แบบไดนามิกผ่าน output_shape
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | output_shape |
เทนเซอร์ 1 มิติของประเภทจำนวนเต็ม | (C1), (C2) |
| (I2) | iota_dimension |
si64 |
(C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเต็ม จุดลอย หรือจำนวนเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C2) |
ข้อจำกัด
- (C1)
0 <= iota_dimension < size(output_shape) - (C2)
rank(result) = size(output_shape)
ตัวอย่าง
%output_shape = stablehlo.constant dense<[4, 5]> : tensor<2xi64>
%result = "stablehlo.dynamic_iota"(%output_shape) {
iota_dimension = 0 : i64
} : (tensor<2xi64>) -> tensor<4x5xi64>
// %result: [
// [0, 0, 0, 0, 0],
// [1, 1, 1, 1, 1],
// [2, 2, 2, 2, 2],
// [3, 3, 3, 3, 3]
// ]
dynamic_pad
ความหมาย
การดำเนินการนี้ทำงานเหมือนกับ
pad
op แต่มี edge_padding_low, edge_padding_high และ interior_padding
ที่ระบุแบบไดนามิกเป็นค่า
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1), (C2), (C4) |
| (I2) | padding_value |
เทนเซอร์ 0 มิติหรือเทนเซอร์ที่ผ่านการวัดปริมาณต่อเทนเซอร์ | (C1) |
| (I3) | edge_padding_low |
เทนเซอร์ 1 มิติของประเภทจำนวนเต็ม | (C1), (C4) |
| (I4) | edge_padding_high |
เทนเซอร์ 1 มิติของประเภทจำนวนเต็ม | (C1), (C4) |
| (I5) | interior_padding |
เทนเซอร์ 1 มิติของประเภทจำนวนเต็ม | (C2-C4) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C3-C6) |
ข้อจำกัด
- (C1)
element_type(operand) = element_type(padding_value) = element_type(result) - (C2)
size(edge_padding_low) = size(edge_padding_high) = size(interior_padding) = rank(operand) - (C3)
0 <= interior_padding - (C4)
shape(result) = shape(operand) + edge_padding_low + max(shape(operand) - 1, 0) * interior_padding + edge_padding_high
ตัวอย่าง
// %operand: [
// [1, 2, 3],
// [4, 5, 6]
// ]
// %padding_value: 0
// %edge_padding_low: [0, 1]
// %edge_padding_high: [2, 1]
// %interior_padding: [1, 2]
%result = "stablehlo.dynamic_pad"(%operand, %padding_value,
%edge_padding_low, %edge_padding_high, %interior_padding
) : (tensor<2x3xi64>, tensor<i64>, tensor<2xi64>, tensor<2xi64>, tensor<2xi64>) -> tensor<5x9xi64>
// %result: [
// [0, 1, 0, 0, 2, 0, 0, 3, 0],
// [0, 0, 0, 0, 0, 0, 0, 0, 0],
// [0, 4, 0, 0, 5, 0, 0, 6, 0],
// [0, 0, 0, 0, 0, 0, 0, 0, 0],
// [0, 0, 0, 0, 0, 0, 0, 0, 0]
// ]
dynamic_reshape
ความหมาย
การดำเนินการนี้จะเหมือนกับ
reshape
op โดยที่รูปร่างผลลัพธ์จะระบุแบบไดนามิกผ่าน output_shape
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (C1-C3) |
| (I2) | output_shape |
เทนเซอร์ 1 มิติของประเภทจำนวนเต็ม | (C4) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (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
ความหมาย
ดึง 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 |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1), (C2), (C4) |
| (I2) | start_indices |
จำนวนตัวแปรของเทนเซอร์ 0 มิติประเภทจำนวนเต็ม | (C2), (C3) |
| (I3) | slice_sizes |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C2), (C4), (C5) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (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 |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1-C4), (C6) |
| (I2) | update |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C2), (C3), (C6) |
| (I3) | start_indices |
จำนวนตัวแปรของเทนเซอร์ 0 มิติประเภทจำนวนเต็ม | (C4), (C5) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (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 โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับ Float:
expจาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: ฟังก์ชันเลขชี้กำลังเชิงซ้อน
- สำหรับประเภทที่กำหนดปริมาณ
dequantize_op_quantize(exponential, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %operand: [[0.0, 1.0], [2.0, 3.0]]
%result = "stablehlo.exponential"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [[1.0, 2.7182818284590451], [7.3890560989306504, 20.085536923187668]]
exponential_minus_one
ความหมาย
ดำเนินการยกกำลังลบ 1 แบบทีละองค์ประกอบในเทนเซอร์ operand และสร้างเทนเซอร์ result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับ Float:
expm1จาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: ฟังก์ชันเลขชี้กำลังเชิงซ้อนลบด้วย 1
- สำหรับประเภทที่กำหนดปริมาณ
dequantize_op_quantize(exponential_minus_one, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %operand: [0.0, 1.0]
%result = "stablehlo.exponential_minus_one"(%operand) : (tensor<2xf64>) -> tensor<2xf64>
// %result: [0.0, 1.71828187]
fft
ความหมาย
ดำเนินการแปลงฟูริเยร์ไปข้างหน้าและแปลงฟูริเยร์ผกผันสำหรับอินพุต/เอาต์พุตที่เป็นจำนวนจริงและจำนวนเชิงซ้อน
fft_type เป็นค่าใดค่าหนึ่งต่อไปนี้
FFT: ส่งต่อ FFT จากจำนวนเชิงซ้อนไปยังจำนวนเชิงซ้อนIFFT: FFT แบบผกผันจากจำนวนเชิงซ้อนเป็นจำนวนเชิงซ้อนRFFT: FFT จากจำนวนจริงเป็นจำนวนเชิงซ้อนIRFFT: FFT แบบผกผันจากจำนวนจริงเป็นจำนวนเชิงซ้อน (เช่น รับจำนวนเชิงซ้อน ส่งคืนจำนวนจริง)
กล่าวอย่างเป็นทางการมากขึ้น เมื่อพิจารณาฟังก์ชัน fft ซึ่งรับเทนเซอร์ 1 มิติของ
ประเภทที่ซับซ้อนเป็นอินพุต สร้างเทนเซอร์ 1 มิติของประเภทเดียวกันเป็น
เอาต์พุต และคำนวณการแปลงฟูริเยร์แบบไม่ต่อเนื่อง
สำหรับ 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 ซึ่งรับเทนเซอร์ 1 มิติของ
ประเภททศนิยม จะสร้างเทนเซอร์ 1 มิติของประเภทเชิงซ้อนที่มี
ความหมายของทศนิยมเดียวกัน และทำงานดังนี้
rfft(real_operand) = truncated_resultที่complex_operand... = (real_operand..., 0.0)complex_result = fft(complex_operand)truncated_result = complex_result[:(rank(complex_result) / 2 + 1)]
(เมื่อคำนวณการแปลงฟูริเยร์แบบไม่ต่อเนื่องสำหรับตัวถูกดำเนินการจริง N/2 + 1 องค์ประกอบแรกของผลลัพธ์จะกำหนดส่วนที่เหลือของผลลัพธ์อย่างชัดเจน
ดังนั้นระบบจะตัดผลลัพธ์ของ rfft เพื่อหลีกเลี่ยงการคำนวณองค์ประกอบที่ซ้ำกัน)
สำหรับ fft_type = RFFT result จะกำหนดเป็นผลลัพธ์สุดท้ายของชุดการคำนวณ L
โดยที่ L = size(fft_length) เช่น สำหรับ L = 3
result1[i0, ..., :] = rfft(operand[i0, ..., :])result2[i0, ..., :, iR-1] = fft(result1[i0, ..., :, iR-1])result[i0, ..., :, iR-2, iR-1] = fft(result2[i0, ..., :, iR-2, iR-1])
สุดท้ายนี้ เมื่อพิจารณาฟังก์ชัน irfft ซึ่งมีลายเซ็นประเภทเดียวกันและ
คำนวณค่าผกผันของ rfft
สำหรับ fft_type = IRFFT result จะกำหนดเป็นค่าผกผันของการคำนวณ
สำหรับ fft_type = RFFT เช่น สำหรับ L = 3
result1[i0, ..., :, iR-2, iR-1] = ifft(operand[i0, ..., :, iR-2, iR-1])result2[i0, ..., :, iR-1] = ifft(result1[i0, ..., :, iR-1])result[i0, ..., :] = irfft(result2[i0, ..., :])
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ของประเภททศนิยมหรือจำนวนเชิงซ้อน | (C1), (C2), (C4), (C5) |
| (I2) | fft_type |
enum ของ FFT, IFFT, RFFT และ IRFFT |
(C2), (C5) |
| (I3) | fft_length |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C1), (C3), (C4) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ของประเภททศนิยมหรือจำนวนเชิงซ้อน | (C2), (C4), (C5) |
ข้อจำกัด
- (C1)
size(fft_length) <= rank(operand) - (C2) ความสัมพันธ์ระหว่างประเภทองค์ประกอบ
operandกับresultจะแตกต่างกันดังนี้- หาก
fft_type = FFT,element_type(operand)และelement_type(result)มีประเภทที่ซับซ้อนเหมือนกัน - หาก
fft_type = IFFT,element_type(operand)และelement_type(result)มีประเภทที่ซับซ้อนเหมือนกัน - หาก
fft_type = RFFT,element_type(operand)เป็นประเภทจุดลอยตัวและelement_type(result)เป็นประเภทจำนวนเชิงซ้อนที่มีความหมายของจุดลอยตัวเดียวกัน - หาก
fft_type = IRFFT,element_type(operand)เป็นประเภทที่ซับซ้อนและelement_type(result)เป็นประเภทจุดลอยตัวที่มีความหมายของจุดลอยตัวเดียวกัน
- หาก
- (C3)
1 <= size(fft_length) <= 3 - (C4) หากใน
operandและresultมีเทนเซอร์realที่เป็นประเภท จุดลอยตัว แล้วshape(real)[-size(fft_length):] = fft_length - (C5)
shape(result) = shape(operand)ยกเว้น:- หาก
fft_type = RFFTdim(result, -1) = dim(operand, -1) = 0 ? 0 : dim(operand, -1) / 2 + 1 - หาก
fft_type = IRFFTdim(operand, -1) = dim(result, -1) = 0 ? 0 : dim(result, -1) / 2 + 1
- หาก
ตัวอย่าง
// %operand: [(1.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0)]
%result = "stablehlo.fft"(%operand) {
fft_type = #stablehlo<fft_type FFT>,
fft_length = array<i64: 4>
} : (tensor<4xcomplex<f32>>) -> tensor<4xcomplex<f32>>
// %result: [(1.0, 0.0), (1.0, 0.0), (1.0, 0.0), (1.0, 0.0)]
ชั้น
ความหมาย
ดำเนินการฟังก์ชันพื้นของเทนเซอร์ operand แบบทีละองค์ประกอบและสร้างเทนเซอร์ result
ใช้การดำเนินการ roundToIntegralTowardNegative จากข้อกำหนด IEEE-754
สำหรับประเภทที่กำหนดปริมาณ ให้ดำเนินการ
dequantize_op_quantize(floor, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %operand: [-0.8166, -0.2530, 0.2530, 0.8166, 2.0]
%result = "stablehlo.floor"(%operand) : (tensor<5xf32>) -> tensor<5xf32>
// %result: [-1.0, -1.0, 0.0, 0.0, 2.0]
รวบรวม
ความหมาย
รวบรวมชิ้นจากเทนเซอร์ operand จากออฟเซ็ตที่ระบุใน start_indices
และสร้างเทนเซอร์ result
แผนภาพต่อไปนี้แสดงวิธีที่องค์ประกอบใน result แมปกับองค์ประกอบใน
operand โดยใช้ตัวอย่างที่ชัดเจน แผนภาพจะเลือกตัวอย่างresult
ดัชนี 2-3 รายการและอธิบายโดยละเอียดว่าดัชนีเหล่านั้นสอดคล้องกับดัชนี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_dimsoperand_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 |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1), (C8), (C11), (C17), (C19-C21), (C23) |
| (I2) | start_indices |
Tensor ประเภทจำนวนเต็ม | (C2-C3), (C14), (C17), (C22) |
| (I3) | offset_dims |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C1), (C4-C5), (C22) |
| (I4) | collapsed_slice_dims |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C1), (C6-C9), (C22) |
| (I5) | operand_batching_dims |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C1), (C6), (C10-C12), (C16-C18), (C22) |
| (I6) | start_indices_batching_dims |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C13-C17) |
| (I7) | start_index_map |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C3), (C18-C19) |
| (I8) | index_vector_dim |
ค่าคงที่ของประเภท si64 |
(C2-C3), (C15), (C22) |
| (I9) | slice_sizes |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C9), (C12), (C20-C22) |
| (I10) | indices_are_sorted |
ค่าคงที่ของประเภท i1 |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (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) โดย Semantics จะเกี่ยวข้องกับเฉพาะรูปร่าง
ของประเภทเท่านั้น โดยองค์ประกอบอาจเป็นอะไรก็ได้
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (C1) |
| (I2) | dimension |
ค่าคงที่ของประเภท si64 |
(C1) |
เอาต์พุต
| ชื่อ | ประเภท |
|---|---|
result |
เทนเซอร์ 0 มิติของประเภท 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))
%result = "stablehlo.get_tuple_element"(%operand) <{index = 0 : i32}> : (tuple<tensor<2xf64>, tuple<tensor<i64>>>) -> tensor<2xf64>
// %result: [1.0, 2.0]
ถ้า
ความหมาย
สร้างเอาต์พุตจากการเรียกใช้ฟังก์ชันเพียง 1 ฟังก์ชันจาก true_branch หรือ
false_branch โดยขึ้นอยู่กับค่าของ pred กล่าวอย่างเป็นทางการมากขึ้น result =
pred ? true_branch() : false_branch()
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | pred |
เทนเซอร์ 0 มิติของประเภท i1 |
|
| (I2) | true_branch |
ฟังก์ชัน | (C1-C3) |
| (I3) | false_branch |
ฟังก์ชัน | (C1), (C2) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
results |
จำนวนเทนเซอร์ เทนเซอร์เชิงปริมาณ หรือโทเค็นที่เปลี่ยนแปลงได้ | (C3) |
ข้อจำกัด
- (C1)
input_types(true_branch) = input_types(false_branch) = [] - (C2)
output_types(true_branch) = output_types(false_branch) - (C3)
type(results...) = output_types(true_branch)
ตัวอย่าง
// %result_true_branch: 10
// %result_false_branch: 11
// %pred: true
%result = "stablehlo.if"(%pred) ({
"stablehlo.return"(%result_true_branch) : (tensor<i32>) -> ()
}, {
"stablehlo.return"(%result_false_branch) : (tensor<i32>) -> ()
}) : (tensor<i1>) -> tensor<i32>
// %result: 10
imag
ความหมาย
แยกส่วนจินตภาพแบบทีละองค์ประกอบจาก operand และสร้างเทนเซอร์ result กล่าวอย่างเป็นทางการมากขึ้นคือ สำหรับแต่ละองค์ประกอบ x
imag(x) = is_complex(x) ? imaginary_part(x) :
constant(0, element_type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ของประเภททศนิยมหรือจำนวนเชิงซ้อน | (C1), (C2) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจุดลอยตัว | (C1), (C2) |
ข้อจำกัด
- (C1)
shape(result) = shape(operand) - (C2)
element_type(result)มีคำจำกัดความดังนี้complex_element_type(element_type(operand))หากis_complex(operand)element_type(operand)หรือไม่เช่นนั้น
ตัวอย่าง
// %operand: [(1.0, 2.0), (3.0, 4.0)]
%result = "stablehlo.imag"(%operand) : (tensor<2xcomplex<f32>>) -> tensor<2xf32>
// %result: [2.0, 4.0]
ในฟีด
ความหมาย
อ่านข้อมูลจากฟีดในหน้าและสร้าง results
ความหมายของ infeed_config จะขึ้นอยู่กับการติดตั้งใช้งาน
results ประกอบด้วยค่าเพย์โหลดซึ่งมาเป็นอันดับแรกและโทเค็นซึ่งมาเป็นอันดับสุดท้าย
ในอนาคต เราวางแผนที่จะแยกเพย์โหลดและโทเค็นออกเป็น 2 เอาต์พุต
แยกกันเพื่อปรับปรุงความชัดเจน
(#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 โดยเรียงค่าจากน้อยไปมากเริ่มจาก 0
ตามมิติข้อมูล iota_dimension กล่าวอย่างเป็นทางการมากขึ้น
output[output_index] = constant(is_quantized(output) ?
quantize(output_index[iota_dimension], element_type(output)) :
output_index[iota_dimension], element_type(output))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | iota_dimension |
si64 |
(C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
output |
เทนเซอร์ประเภทจำนวนเต็ม จุดลอย หรือจำนวนเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
0 <= iota_dimension < rank(output)
ตัวอย่าง
%output = "stablehlo.iota"() {
iota_dimension = 0 : i64
} : () -> tensor<4x5xi32>
// %output: [
// [0, 0, 0, 0, 0],
// [1, 1, 1, 1, 1],
// [2, 2, 2, 2, 2],
// [3, 3, 3, 3, 3]
// ]
%output = "stablehlo.iota"() {
iota_dimension = 1 : i64
} : () -> tensor<4x5xi32>
// %output: [
// [0, 1, 2, 3, 4],
// [0, 1, 2, 3, 4],
// [0, 1, 2, 3, 4],
// [0, 1, 2, 3, 4]
// ]
is_finite
ความหมาย
ตรวจสอบทีละองค์ประกอบว่าค่าใน x เป็นค่าจำกัดหรือไม่ (เช่น ไม่ใช่ +Inf, -Inf หรือ NaN) และสร้างเทนเซอร์ y ใช้isFinite
การดำเนินการจากข้อกำหนด IEEE-754 สำหรับประเภทที่กำหนดปริมาณ ผลลัพธ์จะเป็น true เสมอ
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | x |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
y |
Tensor ประเภทบูลีน | (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]
บันทึก
ความหมาย
ดำเนินการลอการิทึมแบบทีละองค์ประกอบในเทนเซอร์ operand และสร้างเทนเซอร์ result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับ Float:
logจาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: ลอการิทึมเชิงซ้อน
- สำหรับประเภทที่กำหนดปริมาณ:
dequantize_op_quantize(log, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %operand: [[1.0, 2.0], [3.0, 4.0]]
%result = "stablehlo.log"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [[0.0, 0.69314718055994529], [1.0986122886681098, 1.3862943611198906]]
log_plus_one
ความหมาย
ดำเนินการลอการิทึมแบบทีละองค์ประกอบบวก 1 ในเทนเซอร์ operand และสร้างเทนเซอร์ result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับ Float:
logp1จาก IEEE-754 - สำหรับจำนวนเชิงซ้อน ให้ทำดังนี้
complex(log(hypot(real(x) + 1, imag(x))), atan2(imag(x), real(x) + 1)) - สำหรับประเภทที่กำหนดปริมาณ
dequantize_op_quantize(log_plus_one, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %operand: [0.0, -0.999, 7.0, 6.38905621, 15.0]
%result = "stablehlo.log_plus_one"(%operand) : (tensor<5xf64>) -> tensor<5xf64>
// %result: [0.0, -6.90776825, 2.07944155, 2.0, 2.77258873]
โลจิสติกส์
ความหมาย
ดำเนินการลอจิสติกส์แบบทีละองค์ประกอบในเทนเซอร์ operand และสร้างเทนเซอร์ result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับ Float:
division(1, addition(1, exp(-x)))จาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: ลอจิสติกเชิงซ้อน
- สำหรับประเภทที่กำหนดปริมาณ
dequantize_op_quantize(logistic, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %operand: [[0.0, 1.0], [2.0, 3.0]]
%result = "stablehlo.logistic"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [[0.5, 0.73105858], [0.88079708, 0.95257413]]
แผนที่
ความหมาย
ใช้ฟังก์ชันแมป computation กับ inputs ตาม dimensions และ
สร้างเทนเซอร์ result
กล่าวอย่างเป็นทางการมากขึ้น result[result_index] = computation(inputs...[result_index])
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | inputs |
เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ | (C1-C4) |
| (I2) | dimensions |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C3) |
| (I3) | computation |
ฟังก์ชัน | (C4) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (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 เชิงตรรกะ
- สำหรับจำนวนเต็ม: จำนวนเต็มสูงสุด
- สำหรับ Float:
maximumจาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: ค่าสูงสุดตามพจนานุกรมสำหรับคู่
(real, imaginary)การกำหนดลำดับสำหรับจำนวนเชิงซ้อนเกี่ยวข้องกับความหมายที่น่าประหลาดใจ ดังนั้นในอนาคตเราจึงวางแผนที่จะยกเลิกการรองรับจำนวนเชิงซ้อน สำหรับการดำเนินการนี้ (#560) - สำหรับประเภทที่กำหนดปริมาณ
dequantize_op_quantize(maximum, lhs, rhs, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | lhs |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1) |
| (I2) | rhs |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (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]]
ขั้นต่ำ
ความหมาย
ดำเนินการ min แบบทีละองค์ประกอบในเทนเซอร์ lhs และ rhs และสร้างเทนเซอร์ result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับบูลีน: AND เชิงตรรกะ
- สำหรับจำนวนเต็ม: จำนวนเต็มขั้นต่ำ
- สำหรับ Float:
minimumจาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: ค่าต่ำสุดตามพจนานุกรมสำหรับคู่
(real, imaginary)การกำหนดลำดับสำหรับจำนวนเชิงซ้อนเกี่ยวข้องกับความหมายที่น่าประหลาดใจ ดังนั้นในอนาคตเราจึงวางแผนที่จะยกเลิกการรองรับจำนวนเชิงซ้อน สำหรับการดำเนินการนี้ (#560) - สำหรับประเภทที่กำหนดปริมาณ
dequantize_op_quantize(minimum, lhs, rhs, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | lhs |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1) |
| (I2) | rhs |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (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]]
คูณ
ความหมาย
ดำเนินการคูณแบบทีละองค์ประกอบของเทนเซอร์ 2 รายการ lhs และ rhs แล้วสร้างเทนเซอร์ result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับบูลีน: AND เชิงตรรกะ
- สำหรับจำนวนเต็ม: การคูณจำนวนเต็ม
- สำหรับ Float:
multiplicationจาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: การคูณเชิงซ้อน
- สำหรับประเภทที่กำหนดปริมาณ
dequantize_op_quantize(multiply, lhs, rhs, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | lhs |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1) |
| (I2) | rhs |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.multiply"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 12], [21, 32]]
ปฏิเสธ
ความหมาย
ดำเนินการปฏิเสธแบบทีละองค์ประกอบของเทนเซอร์ operand และสร้างเทนเซอร์ result
โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับจำนวนเต็มที่มีเครื่องหมาย: การนิเสธจำนวนเต็ม
- สำหรับจำนวนเต็มแบบไม่มีเครื่องหมาย: bitcast เป็นจำนวนเต็มแบบมีเครื่องหมาย, การปฏิเสธจำนวนเต็ม, bitcast กลับเป็นจำนวนเต็มแบบไม่มีเครื่องหมาย
- สำหรับ Float:
negateจาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: นิเสธเชิงซ้อน
- สำหรับประเภทที่กำหนดปริมาณ
dequantize_op_quantize(negate, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจำนวนเต็ม จุดลอย หรือจำนวนเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเต็ม จุดลอย หรือจำนวนเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// Negation operation with integer Tensors
// %operand: [0, -2]
%result = "stablehlo.negate"(%operand) : (tensor<2xi32>) -> tensor<2xi32>
// %result: [0, 2]
// Negation operation with with complex tensors
// %operand: (2.5, 0.0)
%result = "stablehlo.negate"(%operand) : (tensor<1xcomplex<f32>>) -> tensor<1xcomplex<f32>>
// %result: [-2.5, -0.0]
ไม่ใช่
ความหมาย
ดำเนินการ NOT แบบทีละองค์ประกอบของเทนเซอร์ operand และสร้างเทนเซอร์ result
โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับบูลีน: NOT เชิงตรรกะ
- สำหรับจำนวนเต็ม: NOT แบบบิต
อาร์กิวเมนต์
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
operand |
เทนเซอร์ประเภทบูลีนหรือจำนวนเต็ม | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทบูลีนหรือจำนวนเต็ม | (C1) |
ข้อจำกัด
- (C1)
type(operand) = type(result)
ตัวอย่าง
// Bitwise operation with with integer tensors
// %operand: [[1, 2], [3, 4]]
%result = "stablehlo.not"(%operand) : (tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[-2, -3], [-4, -5]]
// Bitwise operation with with boolean tensors
// %operand: [true, false]
%result = "stablehlo.not"(%operand) : (tensor<2xi1>) -> tensor<2xi1>
// %result: [false, true]
optimization_barrier
ความหมาย
ตรวจสอบว่าการดำเนินการที่สร้าง operand จะดำเนินการก่อนการดำเนินการใดๆ ที่ขึ้นอยู่กับ result และป้องกันการแปลงคอมไพเลอร์
จากการย้ายการดำเนินการข้ามขอบเขต นอกเหนือจากนั้น การดำเนินการคือ
ข้อมูลประจำตัว นั่นคือ result = operand
อาร์กิวเมนต์
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
operand |
จำนวนเทนเซอร์แบบ Variadic, เทนเซอร์หรือโทเค็นที่ควอนไทซ์ต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
จำนวนเทนเซอร์แบบ Variadic, เทนเซอร์หรือโทเค็นที่ควอนไทซ์ต่อเทนเซอร์ | (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 แบบทีละองค์ประกอบของเทนเซอร์ 2 รายการ lhs และ rhs แล้วสร้างเทนเซอร์ result
โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับบูลีน: OR เชิงตรรกะ
- สำหรับจำนวนเต็ม: บิตไวส์ OR
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | lhs |
เทนเซอร์ประเภทจำนวนเต็มหรือบูลีน | (C1) |
| (I2) | rhs |
เทนเซอร์ประเภทจำนวนเต็มหรือบูลีน | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเต็มหรือบูลีน | (C1) |
ข้อจำกัด
- (C1)
type(lhs) = type(rhs) = type(result)
ตัวอย่าง
// Bitwise operation with with integer tensors
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.or"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 6], [7, 12]]
// Logical operation with with boolean tensors
// %lhs: [[false, false], [true, true]]
// %rhs: [[false, true], [false, true]]
%result = "stablehlo.or"(%lhs, %rhs) : (tensor<2x2xi1>, tensor<2x2xi1>) -> tensor<2x2xi1>
// %result: [[false, true], [true, true]]
ฟีดภายนอก
ความหมาย
เขียน 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 ระบุจำนวนการเพิ่ม Padding
ที่ด้านล่าง (ถัดจากดัชนี 0) และด้านบน (ถัดจากดัชนีสูงสุด) ของ
แต่ละมิติ ตามลำดับ จำนวนการเว้นวรรคอาจเป็นค่าลบได้ โดยค่าสัมบูรณ์ของการเว้นวรรคที่เป็นลบจะระบุจำนวนองค์ประกอบที่จะนำออกจากมิติข้อมูลที่ระบุ
interior_padding ระบุจำนวนระยะห่างจากเส้นขอบที่เพิ่มระหว่างองค์ประกอบ 2 รายการในแต่ละมิติข้อมูล ซึ่งต้องไม่เป็นค่าลบ การเพิ่มระยะขอบภายในจะเกิดขึ้นก่อนการเพิ่มระยะขอบที่ขอบ เพื่อให้การเพิ่มระยะขอบเชิงลบจะนำองค์ประกอบออกจากตัวถูกดำเนินการที่มีการเพิ่มระยะขอบภายใน
กล่าวอย่างเป็นทางการมากขึ้น result[result_index] มีคำจำกัดความดังนี้
operand[operand_index]ifresult_index = edge_padding_low + operand_index * (interior_padding + 1)padding_valueหรือไม่เช่นนั้น
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1), (C2), (C4) |
| (I2) | padding_value |
เทนเซอร์ 0 มิติหรือเทนเซอร์ที่ผ่านการวัดปริมาณต่อเทนเซอร์ | (C1) |
| (I3) | edge_padding_low |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C1), (C4) |
| (I4) | edge_padding_high |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C1), (C4) |
| (I5) | interior_padding |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C2-C4) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (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 |
เทนเซอร์ 0 มิติของประเภท 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 โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับจำนวนเต็ม: การยกกำลังจำนวนเต็ม
- สำหรับ Float:
powจาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: การยกกำลังเชิงซ้อน
- สำหรับประเภทที่กำหนดปริมาณ:
dequantize_op_quantize(power, lhs, rhs, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | lhs |
เทนเซอร์ประเภทจำนวนเต็ม จุดลอย หรือจำนวนเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
| (I2) | rhs |
เทนเซอร์ประเภทจำนวนเต็ม จุดลอย หรือจำนวนเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเต็ม จุดลอย หรือจำนวนเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %lhs: [-2.0, -0.0, -36.0, 5.0, 3.0, 10000.0]
// %rhs: [2.0, 2.0, 1.1, 2.0, -1.0, 10.0]
%result = "stablehlo.power"(%lhs, %rhs) : (tensor<6xf64>, tensor<6xf64>) -> tensor<6xf64>
// %result: [4.0, 0.0, -nan, 25.0, 0.333333343, inf]
จริง
ความหมาย
ดึงส่วนจริงทีละองค์ประกอบจาก operand และสร้างเทนเซอร์ result
กล่าวอย่างเป็นทางการมากขึ้นคือ สำหรับแต่ละองค์ประกอบ x
real(x) = is_complex(x) ? real_part(x) : x
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ของประเภททศนิยมหรือจำนวนเชิงซ้อน | (C1), (C2) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจุดลอยตัว | (C1), (C2) |
ข้อจำกัด
- (C1)
shape(result) = shape(operand) - (C2)
element_type(result)มีคำจำกัดความดังนี้complex_element_type(element_type(operand))หากis_complex(operand)element_type(operand)หรือไม่เช่นนั้น
ตัวอย่าง
// %operand: [(1.0, 2.0), (3.0, 4.0)]
%result = "stablehlo.real"(%operand) : (tensor<2xcomplex<f32>>) -> tensor<2xf32>
// %result: [1.0, 3.0]
recv
ความหมาย
รับข้อมูลจากแชแนลที่มี channel_id และสร้าง results
หาก is_host_transfer เป็น true การดำเนินการจะโอนข้อมูลจากโฮสต์ ไม่เช่นนั้น ระบบจะโอนข้อมูลจากอุปกรณ์อื่นตามค่าของ
source_target_pairs โดยแฟล็กนี้จะทำซ้ำข้อมูลที่ระบุไว้ใน
channel_type ดังนั้นในอนาคตเราจึงวางแผนที่จะเก็บไว้เพียงรายการเดียว
(#666) หาก is_host_transfer
= false และ source_target_pairs เป็น None หรือว่างเปล่า ระบบจะถือว่าเป็นการทำงานที่ไม่ได้กำหนดไว้
results ประกอบด้วยค่าเพย์โหลดซึ่งมาเป็นอันดับแรกและโทเค็นซึ่งมาเป็นอันดับสุดท้าย
ในอนาคต เราวางแผนที่จะแยกเพย์โหลดและโทเค็นออกเป็น 2 เอาต์พุต
แยกกันเพื่อปรับปรุงความชัดเจน
(#670)
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | token |
token |
|
| (I2) | channel_id |
ค่าคงที่ของประเภท si64 |
|
| (I3) | channel_type |
enum ของ DEVICE_TO_DEVICE และ DEVICE_TO_HOST |
(C5) |
| (I4) | is_host_transfer |
ค่าคงที่ของประเภท i1 |
(C5-C6) |
| (I5) | source_target_pairs |
ค่าคงที่เทนเซอร์ 2 มิติของประเภท si64 |
(C1-C4), (C6) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
results |
จำนวนเทนเซอร์ เทนเซอร์เชิงปริมาณ หรือโทเค็นที่เปลี่ยนแปลงได้ | (C2-C4) |
ข้อจำกัด
- (C1)
dim(source_target_pairs, 1) = 2 - (C2)
is_unique(source_target_pairs[:, 0]) - (C3)
is_unique(source_target_pairs[:, 1]) - (C4)
0 <= source_target_pairs < Nโดยที่Nมีคำจำกัดความดังนี้num_replicasหากใช้cross_replicanum_partitionsหากใช้cross_partition
- (C5)
channel_typeมีคำจำกัดความดังนี้DEVICE_TO_HOSTหากis_host_transfer = trueDEVICE_TO_DEVICEหรือไม่เช่นนั้น
ตัวอย่าง
%results0, %results1 = "stablehlo.recv"(%token) {
channel_handle = #stablehlo.channel_handle<handle = 0, type = 1>,
is_host_transfer = false,
source_target_pairs = dense<[[0, 1], [1, 2]]> : tensor<2x2xi64>
} : (!stablehlo.token) -> (tensor<2x2xi64>, !stablehlo.token)
ลด
ความหมาย
ใช้ฟังก์ชันการลด body กับ inputs และ init_values ตาม
dimensions และสร้างเทนเซอร์ results
ลำดับของการลดค่าจะขึ้นอยู่กับการใช้งาน ซึ่งหมายความว่า body และ
init_values ต้องสร้างโมโนอิดเพื่อให้มั่นใจว่าการดำเนินการจะให้ผลลัพธ์
เดียวกันสำหรับอินพุตทั้งหมดในการใช้งานทั้งหมด อย่างไรก็ตาม เงื่อนไขนี้
ใช้ไม่ได้กับการลดขนาดที่ได้รับความนิยมหลายรายการ เช่น การบวกทศนิยมสำหรับ body และ 0 สำหรับ init_values ไม่ได้สร้าง Monoid จริงๆ เนื่องจาก
การบวกทศนิยมไม่ใช่การเชื่อมโยง
กล่าวอย่างเป็นทางการมากขึ้น results...[j0, ..., jR-1] = reduce(input_slices_converted) โดยที่
input_slices = inputs...[j0, ..., :, ..., jR-1]โดยแทรก:ที่dimensionsinput_slices_converted = to_destination_type(input_slices..., type(func_inputs(body)[:len(func_inputs(body))//2])...)init_values_converted = to_destination_type(init_values..., type(func_inputs(body)[len(func_inputs(body))//2:])...)reduce(input_slices_converted) = exec(schedule)สำหรับต้นไม้แบบไบนารีบางต้นscheduleโดยที่exec(node) = body(exec(node.left), exec(node.right))exec(leaf) = leaf.value
scheduleเป็นไบนารีทรีแบบสมบูรณ์ที่กำหนดการใช้งานซึ่งการทราเวอร์แซลแบบอินออร์เดอร์ ประกอบด้วยinput_slices_converted...[index]สำหรับindexทั้งหมดในindex_space(input_slices_converted)ตามลำดับแบบพจนานุกรมจากน้อยไปมาก ของindex- แทรกด้วยจำนวน
init_values_convertedที่กำหนดไว้ในการใช้งานในตำแหน่งที่กำหนดไว้ในการใช้งาน
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | inputs |
เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ | (C1-C4), (C6), (C7) |
| (I2) | init_values |
จำนวนเทนเซอร์ 0 มิติหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์แบบแปรผัน | (C2), (C3) |
| (I3) | dimensions |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C4), (C5), (C7) |
| (I4) | body |
ฟังก์ชัน | (C6) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
results |
เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ | (C3), (C7), (C8) |
ข้อจำกัด
- (C1)
same(shape(inputs...)) - (C2)
element_type(inputs...) = element_type(init_values...) - (C3)
0 < size(inputs) = size(init_values) = size(results) = N - (C4)
0 <= dimensions < rank(inputs[0]) - (C5)
is_unique(dimensions) - (C6)
bodyมีประเภท(tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ...,tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>)โดยที่is_promotable(element_type(inputs[i]), Ei) - (C7)
shape(results...) = shape(inputs...)ยกเว้นว่าขนาดมิติข้อมูลของinputs...ที่สอดคล้องกับdimensionsจะไม่รวมอยู่ด้วย - (C8)
element_type(results[i]) = Eiสำหรับiทั้งหมดใน[0,N)
ตัวอย่าง
// %input = [[0, 1, 2, 3, 4, 5]]
// %init_value = 0
%result = "stablehlo.reduce"(%input, %init_value) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
"stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
dimensions = array<i64: 1>
} : (tensor<1x6xi64>, tensor<i64>) -> tensor<1xi64>
// %result = [15]
reduce_precision
ความหมาย
ทำการแปลงองค์ประกอบของ operand เป็นประเภทจุดลอยตัวอื่น
ที่ใช้ exponent_bits และ mantissa_bits แล้วแปลงกลับเป็นประเภทจุดลอยตัวเดิม
และสร้างเทนเซอร์ output
ในรูปแบบที่เป็นทางการมากขึ้น
- ระบบจะอัปเดตบิตแมนทิสซาของค่าเดิมเพื่อปัดเศษค่าเดิม
ให้เป็นค่าที่ใกล้เคียงที่สุดซึ่งแสดงได้ด้วย
mantissa_bitsโดยใช้ ความหมายของroundToIntegralTiesToEven - จากนั้นหาก
mantissa_bitsน้อยกว่าจำนวนบิตแมนทิสซาของ ค่าเดิม ระบบจะตัดบิตแมนทิสซาเป็นmantissa_bits - จากนั้นหากบิตเลขชี้กำลังของผลลัพธ์กลางไม่พอดีกับช่วงที่
exponent_bitsระบุ ผลลัพธ์กลางจะล้นไปยังอนันต์โดยใช้เครื่องหมายเดิม หรือล้นไปยัง 0 โดยใช้เครื่องหมายเดิม - สำหรับประเภทที่กำหนดปริมาณ ให้ดำเนินการ
dequantize_op_quantize( lambda operand: reduce_precision(operand, exponent_bits, mantissa_bits), operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C1) |
| (I2) | exponent_bits |
ค่าคงที่ของประเภท si32 |
(C2) |
| (I3) | mantissa_bits |
ค่าคงที่ของประเภท si32 |
(C3) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
output |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(output) - (C2)
1 <= exponent_bits - (C3)
0 <= mantissa_bits
ตัวอย่าง
// Logical values: +Inf, NaN, +Denormal, 0.0, 65519.0, 65520.0
// %operand: [0x7FF0000000000000, 0x7FFFFFFFFFFFFFFF, 0x0000000000000001, 0.0, 65519.0, 65520.0]
%output = "stablehlo.reduce_precision"(%operand) {
exponent_bits = 5 : i32,
mantissa_bits = 10 : i32
} : (tensor<6xf64>) -> tensor<6xf64>
// Logical values: +Inf, NaN, 0.0, 0.0, 65504.0, +Inf
// %output: [0x7FF0000000000000, 0x7FFFFFFFFFFFFFFF, 0.0, 0.0, 65504.0, 0x7FF0000000000000]
reduce_scatter
ความหมาย
ภายในกลุ่มกระบวนการแต่ละกลุ่มในตารางกระบวนการ StableHLO จะทำการลด
โดยใช้ computations กับค่าของเทนเซอร์ operand จากแต่ละกระบวนการ
แยกผลการลดตาม scatter_dimension ออกเป็นส่วนๆ แล้วกระจาย
ส่วนที่แยกแล้วระหว่างกระบวนการเพื่อสร้าง result
การดำเนินการจะแยกตารางกระบวนการ StableHLO ออกเป็น process_groups ซึ่งกำหนดไว้ดังนี้
cross_replica(replica_groups)หากchannel_id <= 0 and use_global_device_ids = falsecross_replica_and_partition(replica_groups)หากchannel_id > 0 and use_global_device_ids = falseflattened_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 |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1), (C2), (C7), (C8) |
| (I2) | scatter_dimension |
ค่าคงที่ของประเภท si64 |
(C1), (C2), (C8) |
| (I3) | replica_groups |
ค่าคงที่เทนเซอร์ 2 มิติของประเภท si64 |
(C3-C5) |
| (I4) | channel_id |
ค่าคงที่ของประเภท si64 |
(C6) |
| (I5) | use_global_device_ids |
ค่าคงที่ของประเภท i1 |
(C6) |
| (I6) | computation |
ฟังก์ชัน | (C7) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C8-C9) |
ข้อจำกัด
- (C1)
dim(operand, scatter_dimension) % dim(process_groups, 1) = 0 - (C2)
0 <= scatter_dimension < rank(operand) - (C3)
is_unique(replica_groups) - (C4)
size(replica_groups)มีคำจำกัดความดังนี้num_replicasหากใช้cross_replicanum_replicasหากใช้cross_replica_and_partitionnum_processesหากใช้flattened_ids
- (C5)
0 <= replica_groups < size(replica_groups) - (C6) ถ้า
use_global_device_ids = trueให้channel_id > 0 - (C7)
computationมีประเภท(tensor<E>, tensor<E>) -> (tensor<E>)โดยที่is_promotable(element_type(operand), E) - (C8)
shape(result) = shape(operand)ยกเว้นdim(result, scatter_dimension) = dim(operand, scatter_dimension) / dim(process_groups, 1)
- (C9)
element_type(result) = E
ตัวอย่าง
// num_replicas: 2
// num_partitions: 1
// %operand@(0, 0): [[1, 2, 3, 4],
// [5, 6, 7, 8]]
// %operand@(1, 0): [[9, 10, 11, 12],
// [13, 14, 15, 16]]
%result = "stablehlo.reduce_scatter"(%operand) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
"stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
scatter_dimension = 1 : i64,
replica_groups = dense<[[0, 1]]> : tensor<1x2xi64>,
channel_handle = #stablehlo.channel_handle<handle = 0, type = 0>
} : (tensor<2x4xi64>) -> tensor<2x2xi64>
//
// %result@(0, 0): [[10, 12],
// [18, 20]]
// %result@(1, 0): [[14, 16],
// [22, 24]]
reduce_window
ความหมาย
ใช้ฟังก์ชันการลด body กับหน้าต่างของ inputs และ init_values
และสร้าง results
แผนภาพต่อไปนี้แสดงวิธีคำนวณองค์ประกอบใน results... จาก inputs... โดยใช้ตัวอย่างที่เป็นรูปธรรม
ในรูปแบบที่เป็นทางการมากขึ้น
results...[result_index] = reduce(windows, init_values, axes(inputs...), body)
(ดู reduce) โดยที่
padded_inputs = pad(inputs..., init_values..., padding[:, 0], padding[:, 1], base_dilations - 1)window_start = result_index * window_strideswindow_end = window_start + (window_dimensions - 1) * window_dilations + 1windows = slice(padded_inputs..., window_start, window_end, window_dilations)
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | inputs |
เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ | (C1-C4), (C6), (C8), (C10), (C12), (C13), (C15) |
| (I2) | init_values |
จำนวนเทนเซอร์ 0 มิติหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์แบบแปรผัน | (C1), (C13) |
| (I3) | window_dimensions |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C4), (C5), (C15) |
| (I4) | window_strides |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C6), (C7), (C15) |
| (I5) | base_dilations |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C8), (C9), (C15) |
| (I6) | window_dilations |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C10), (C11), (C15) |
| (I7) | padding |
ค่าคงที่เทนเซอร์ 2 มิติของประเภท 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 + 1padded_input_shape = padding[:, 0] + dilated_input_shape + padding[:, 1]dilated_window_shape = (window_dimensions - 1) * window_dilations + 1is_empty_window = padded_input_shape = 0 || dilated_window_shape > padded_input_shapenum_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))
สำหรับประเภทองค์ประกอบทศนิยม การดำเนินการนี้จะตรงกันข้ามกับการดำเนินการ remainder จากข้อกำหนด IEEE-754 ซึ่ง d เป็นค่าจำนวนเต็มที่ใกล้เคียงกับค่าที่แน่นอนของ lhs/rhs มากที่สุด โดยมีค่าเท่ากัน
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | lhs |
เทนเซอร์ของจำนวนเต็ม จุดลอย หรือประเภทเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
| (I2) | rhs |
เทนเซอร์ของจำนวนเต็ม จุดลอย หรือประเภทเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ของจำนวนเต็ม จุดลอย หรือประเภทเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %lhs: [17, -17, 17, -17]
// %rhs: [3, 3, -3, -3]
%result = "stablehlo.remainder"(%lhs, %rhs) : (tensor<4xi64>, tensor<4xi64>) -> tensor<4xi64>
// %result: [2, -2, 2, -2]
replica_id
ความหมาย
สร้าง replica_id ของกระบวนการปัจจุบัน
เอาต์พุต
| ชื่อ | ประเภท |
|---|---|
result |
เทนเซอร์ 0 มิติของประเภท ui32 |
ตัวอย่าง
%result = "stablehlo.replica_id"() : () -> tensor<ui32>
ปรับรูปร่าง
ความหมาย
ดำเนินการเปลี่ยนรูปร่างของเทนเซอร์ operand เป็นเทนเซอร์ result ในเชิงแนวคิด การดำเนินการนี้
เทียบเท่ากับการคงการแสดงผล Canonical เดิมไว้ แต่มีโอกาสที่จะเปลี่ยน
รูปร่าง เช่น จาก tensor<2x3xf32> เป็น tensor<3x2xf32> หรือ tensor<6xf32>
กล่าวอย่างเป็นทางการมากขึ้นคือ result[result_index] = operand[operand_index] where
result_index และ operand_index มีตำแหน่งเดียวกันในการเรียงตามพจนานุกรม
ของ index_space(result) และ index_space(operand)
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (C1-C3) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (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ในdimensionsoperand_index[d] = result_index[d]หรือไม่เช่นนั้น
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1), (C3) |
| (I2) | dimensions |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C2), (C3) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1), (C3) |
ข้อจำกัด
- (C1)
type(operand) = type(result) - (C2)
is_unique(dimensions) - (C3)
0 <= dimensions < rank(result)
ตัวอย่าง
// %operand = [[1, 2], [3, 4], [5, 6]]
%result = "stablehlo.reverse"(%operand) {
dimensions = array<i64: 1>
} : (tensor<3x2xi32>) -> tensor<3x2xi32>
// %result: [[2, 1], [4, 3], [6, 5]]
rng
ความหมาย
สร้างตัวเลขสุ่มโดยใช้อัลกอริทึม rng_distribution และสร้างเทนเซอร์ result ที่มีรูปร่างที่กำหนด shape
หากเป็น rng_distribution = UNIFORM ระบบจะสร้างตัวเลขสุ่ม
ตามการแจกแจงแบบสม่ำเสมอในช่วง [a, b) หาก a >= b
ลักษณะการทำงานจะไม่ได้กำหนดไว้
หาก rng_distribution = NORMAL ระบบจะสร้างตัวเลขสุ่ม
ตามการแจกแจงปกติที่มีค่าเฉลี่ย = a และค่าเบี่ยงเบนมาตรฐาน = b
หากเป็น b < 0 ระบบจะไม่กำหนดลักษณะการทำงาน
วิธีที่แน่นอนในการสร้างตัวเลขสุ่มนั้นขึ้นอยู่กับการใช้งาน เช่น อาจเป็นหรือไม่เป็นแบบดีเทอร์มินิสติก และอาจใช้หรือไม่ใช้สถานะที่ซ่อนอยู่
ในการพูดคุยกับผู้มีส่วนเกี่ยวข้องหลายราย เราพบว่าการดำเนินการนี้ถือว่าเลิกใช้งานแล้วอย่างมีประสิทธิภาพ ดังนั้นในอนาคตเราจึงวางแผนที่จะสำรวจการนำออก (#597)
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | a |
เทนเซอร์ 0 มิติของจำนวนเต็ม บูลีน หรือประเภทจุดลอยตัว | (C1), (C2) |
| (I2) | b |
เทนเซอร์ 0 มิติของจำนวนเต็ม บูลีน หรือประเภทจุดลอยตัว | (C1), (C2) |
| (I3) | shape |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C3) |
| (I4) | rng_distribution |
enum ของ UNIFORM และ NORMAL |
(C2) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ของจำนวนเต็ม บูลีน หรือประเภทจุดลอยตัว | (C1-C3) |
ข้อจำกัด
- (C1)
element_type(a) = element_type(b) = element_type(result) - (C2) ถ้า
rng_distribution = NORMALให้is_float(a) - (C3)
shape(result) = shape
ตัวอย่าง
// %a = 0
// %b = 2
// %shape = [3, 3]
%result = "stablehlo.rng"(%a, %b, %shape) {
rng_distribution = #stablehlo<rng_distribution UNIFORM>
} : (tensor<i32>, tensor<i32>, tensor<2xi64>) -> tensor<3x3xi32>
// %result: [
// [1, 0, 1],
// [1, 1, 1],
// [0, 0, 0]
// ]
rng_bit_generator
ความหมาย
แสดงผล output ที่มีบิตแบบสุ่มที่สม่ำเสมอและสถานะเอาต์พุตที่อัปเดตแล้ว
output_state โดยใช้อัลกอริทึมตัวสร้างตัวเลขหลอกแบบสุ่ม rng_algorithm
เมื่อกำหนดสถานะเริ่มต้น initial_state เอาต์พุตรับประกันได้ว่าจะเป็นฟังก์ชันที่กำหนดได้ของ initial_state แต่ไม่รับประกันว่าจะกำหนดได้ระหว่างการใช้งาน
rng_algorithm เป็นค่าใดค่าหนึ่งต่อไปนี้
DEFAULT: อัลกอริทึมที่กำหนดการใช้งานTHREE_FRY: รูปแบบที่กำหนดการใช้งานของอัลกอริทึม Threefry*PHILOX: รูปแบบที่กำหนดการใช้งานของอัลกอริทึม Philox*
* ดู: Salmon et al. SC 2011 เลขสุ่มแบบขนาน: ง่ายเหมือนนับ 1, 2, 3
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | rng_algorithm |
enum ของ DEFAULT, THREE_FRY และ PHILOX |
(C2) |
| (I2) | initial_state |
เทนเซอร์ 1 มิติของประเภท ui64 |
(C1), (C2) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
output_state |
เทนเซอร์ 1 มิติของประเภท ui64 |
(C1) |
output |
เทนเซอร์ประเภทจำนวนเต็มหรือจุดลอยตัว |
ข้อจำกัด
- (C1)
type(initial_state) = type(output_state) - (C2)
size(initial_state)มีคำจำกัดความดังนี้- การใช้งานที่กำหนดไว้หาก
rng_algorithm = DEFAULT 2หากrng_algorithm = THREE_FRY2หรือ3หากrng_algorithm = PHILOX
- การใช้งานที่กำหนดไว้หาก
ตัวอย่าง
// %initial_state: [1, 2]
%output_state, %output = "stablehlo.rng_bit_generator"(%initial_state) {
rng_algorithm = #stablehlo<rng_algorithm THREE_FRY>
} : (tensor<2xui64>) -> (tensor<2xui64>, tensor<2x2xui64>)
// %output_state: [1, 6]
// %output: [
// [9236835810183407956, 16087790271692313299],
// [18212823393184779219, 2658481902456610144]
// ]
round_nearest_afz
ความหมาย
ดำเนินการปัดเศษแบบทีละองค์ประกอบเข้าหาจำนวนเต็มที่ใกล้ที่สุด โดยปัดเศษค่าที่อยู่กึ่งกลางออก
จากศูนย์ในเทนเซอร์ operand และสร้างเทนเซอร์ result ใช้การดำเนินการ roundToIntegralTiesToAway จากข้อกำหนด IEEE-754 สำหรับ
ประเภทที่กำหนดปริมาณ จะดำเนินการ
dequantize_op_quantize(round_nearest_afz, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %operand = [-2.5, 0.4, 0.5, 0.6, 2.5]
%result = "stablehlo.round_nearest_afz"(%operand) : (tensor<5xf64>) -> tensor<5xf64>
// %result: [-3.0, 0.0, 1.0, 1.0, 3.0]
round_nearest_even
ความหมาย
ดำเนินการปัดเศษตามองค์ประกอบไปยังจำนวนเต็มที่ใกล้ที่สุด โดยจะปัดเศษ
ไปยังจำนวนเต็มคู่ในเทนเซอร์ operand และสร้างเทนเซอร์ result
ใช้การดำเนินการ roundToIntegralTiesToEven จากข้อกำหนด IEEE-754
สำหรับประเภทที่กำหนดปริมาณ ให้ดำเนินการ
dequantize_op_quantize(round_nearest_even, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %operand = [-2.5, 0.4, 0.5, 0.6, 2.5]
%result = "stablehlo.round_nearest_even"(%operand) : (tensor<5xf64>) -> tensor<5xf64>
// %result: [-2.0, 0.0, 0.0, 1.0, 2.0]
rsqrt
ความหมาย
ดำเนินการรากที่สองส่วนกลับแบบทีละองค์ประกอบในเทนเซอร์ operand และสร้างเทนเซอร์ result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับ Float:
rSqrtจาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: รากที่สองของส่วนกลับของจำนวนเชิงซ้อน
- สำหรับประเภทที่กำหนดปริมาณ:
dequantize_op_quantize(rsqrt, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %operand: [[1.0, 4.0], [9.0, 25.0]]
%result = "stablehlo.rsqrt"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[1.0, 0.5], [0.33333343, 0.2]]
กระจาย
ความหมาย
สร้างเทนเซอร์ results ซึ่งเท่ากับเทนเซอร์ inputs ยกเว้นว่า
มีการอัปเดตหลายๆ สไลซ์ที่ระบุโดย scatter_indices ด้วยค่า
updates โดยใช้ update_computation
แผนภาพต่อไปนี้แสดงวิธีที่องค์ประกอบใน updates... แมปกับองค์ประกอบใน
results... โดยใช้ตัวอย่างที่ชัดเจน แผนภาพจะเลือกตัวอย่างดัชนี 2-3 รายการ
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]ifd_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_dimsresult_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 |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C2), (C4), (C7-C8) |
| (I5) | inserted_window_dims |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C2), (C4), (C9-C11) |
| (I6) | input_batching_dims |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C2), (C4), (C9), (C12-13), (C17-18), (C20) |
| (I7) | scatter_indices_batching_dims |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C14-C18) |
| (I8) | scatter_dims_to_operand_dims |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท 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_dimupdate_window_dim_sizes <= shape(inputs[0])ยกเว้นว่า จะไม่รวมขนาดมิติข้อมูลในinputs[0]ที่สอดคล้องกับinserted_window_dimsและinput_batching_dimscombineวางupdate_scatter_dim_sizesที่แกนซึ่งสอดคล้องกับupdate_scatter_dimsและupdate_window_dim_sizesที่แกนซึ่งสอดคล้อง กับupdate_window_dims
- (C5)
0 < size(inputs) = size(updates) = N - (C6)
element_type(updates...) = element_type(inputs...) - (C7)
is_unique(update_window_dims) and is_sorted(update_window_dims) - (C8)
0 <= update_window_dims < rank(updates[0]) - (C9)
is_unique(concatenate(inserted_window_dims, input_batching_dims)) - (C10)
is_sorted(inserted_window_dims) - (C11)
0 <= inserted_window_dims < rank(inputs[0]) - (C12)
is_sorted(input_batching_dims) - (C13)
0 <= input_batching_dims < rank(inputs[0])) - (C14)
is_unique(scatter_indices_batching_dims) - (C15)
0 <= scatter_indices_batching_dims < rank(scatter_indices) - (C16)
index_vector_dim not in scatter_indices_batching_dims - (C17)
size(input_batching_dims) == size(scatter_indices_batching_dims) - (C18)
dim(inputs[0], input_batching_dims...) = dim(scatter_indices, scatter_indices_batching_dims...) - (C19)
size(scatter_dims_to_operand_dims) = index_vector_dim < rank(scatter_indices) ? dim(scatter_indices, index_vector_dim) : 1 - (C20)
is_unique(concatenate(scatter_dims_to_operand_dims, input_batching_dims)) - (C21)
0 <= scatter_dims_to_operand_dims < rank(inputs[0]) - (C22)
0 <= index_vector_dim <= rank(scatter_indices) - (C23)
update_computationมีประเภท(tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ..., tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>)โดยที่is_promotable(element_type(inputs[i]), Ei) - (C24)
shape(inputs...) = shape(results...) - (C25)
element_type(results[i]) = Eiสำหรับiทั้งหมดใน[0,N)
ตัวอย่าง
// %input: [
// [
// [[1, 2], [3, 4], [5, 6], [7, 8]],
// [[9, 10],[11, 12], [13, 14], [15, 16]],
// [[17, 18], [19, 20], [21, 22], [23, 24]]
// ],
// [
// [[25, 26], [27, 28], [29, 30], [31, 32]],
// [[33, 34], [35, 36], [37, 38], [39, 40]],
// [[41, 42], [43, 44], [45, 46], [47, 48]]
// ]
// ]
// %scatter_indices: [
// [
// [[0, 0], [1, 0], [2, 1]],
// [[0, 1], [1, 1], [0, 9]]
// ],
// [
// [[0, 0], [2, 1], [2, 2]],
// [[1, 2], [0, 1], [1, 0]]
// ]
// ]
// %update: [
// [
// [[[1, 1], [1, 1]], [[1, 1], [1, 1]], [[1, 1], [1, 1]]],
// [[[1, 1], [1, 1]], [[1, 1], [1, 1]], [[1, 1], [1, 1]]]
// ],
// [
// [[[1, 1], [1, 1]], [[1, 1], [1, 1]], [[1, 1], [1, 1]]],
// [[[1, 1], [1, 1]], [[1, 1], [1, 1]], [[1, 1], [1, 1]]]
// ]
// ]
%result = "stablehlo.scatter"(%input, %scatter_indices, %update) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
"stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
scatter_dimension_numbers = #stablehlo.scatter<
update_window_dims = [3, 4],
inserted_window_dims = [1],
input_batching_dims = [0],
scatter_indices_batching_dims = [1],
scatter_dims_to_operand_dims = [2, 1],
index_vector_dim = 3>,
indices_are_sorted = false,
unique_indices = false
} : (tensor<2x3x4x2xi64>, tensor<2x2x3x2xi64>, tensor<2x2x3x2x2xi64>) -> tensor<2x3x4x2xi64>
// %result: [
// [
// [[3, 4], [6, 7], [6, 7], [7, 8]],
// [[9, 10],[11, 12], [15, 16], [17, 18]],
// [[17, 18], [19, 20], [22, 23], [24, 25]]
// ],
// [
// [[25, 26], [28, 29], [30, 31], [31, 32]],
// [[35, 36], [38, 39], [38, 39], [39, 40]],
// [[41, 42], [44, 45], [46, 47], [47, 48]]
// ]
// ]
เลือก
ความหมาย
สร้างเทนเซอร์ result โดยเลือกแต่ละองค์ประกอบจากเทนเซอร์ on_true หรือ on_false ตามค่าขององค์ประกอบที่สอดคล้องกันของ pred
ในรูปแบบที่เป็นทางการมากขึ้น result[result_index] = pred_element ? on_true[result_index] :
on_false[result_index] โดยที่ pred_element = rank(pred) = 0 ? pred[] :
pred[result_index] สำหรับประเภทที่กำหนดปริมาณ ให้ดำเนินการ
dequantize_select_quantize(pred, on_true, on_false, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | pred |
Tensor ประเภท i1 |
(C1) |
| (I2) | on_true |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1-C2) |
| (I3) | on_false |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C2) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C2) |
ข้อจำกัด
- (C1)
rank(pred) = 0 or shape(pred) = shape(on_true) - (C2)
baseline_type(on_true) = baseline_type(on_false) = baseline_type(result)
ตัวอย่าง
// %pred: [[false, true], [true, false]]
// %on_true: [[1, 2], [3, 4]]
// %on_false: [[5, 6], [7, 8]]
%result = "stablehlo.select"(%pred, %on_true, %on_false) : (tensor<2x2xi1>, tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[5, 2], [3, 8]]
select_and_scatter
ความหมาย
กระจายค่าจากเทนเซอร์ source โดยใช้ scatter ตาม
ผลลัพธ์ของ reduce_window ของเทนเซอร์ input โดยใช้ select และสร้าง
เทนเซอร์ result
แผนภาพต่อไปนี้แสดงวิธีคำนวณองค์ประกอบใน result จาก
operand และ source โดยใช้ตัวอย่างที่เป็นรูปธรรม
ในรูปแบบที่เป็นทางการมากขึ้น
selected_values = reduce_window_without_init(...)โดยมีข้อมูลต่อไปนี้inputs = [operand].window_dimensions,window_stridesและpaddingซึ่งใช้ตามที่เป็นอยู่base_dilations = windows_dilations = 1bodyมีคำจำกัดความดังนี้
def body(arg0: tensor<E>, arg1: tensor<E>) -> tensor<E>: return select(arg0, arg1) ? arg0 : arg1;โดย
E = element_type(operand)และreduce_window_without_initทำงาน เหมือนกับreduce_windowทุกประการ ยกเว้นว่าscheduleของreduceที่อยู่เบื้องหลังreduce(ดู 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_indexsource_indices = [source_index for source_index in indices(source) if selected_index(source_index) = result_index]
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1-C4), (C6), (C8-C11) |
| (I2) | source |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1), (C2) |
| (I3) | init_value |
เทนเซอร์ 0 มิติหรือเทนเซอร์ที่ผ่านการวัดปริมาณต่อเทนเซอร์ | (C3) |
| (I4) | window_dimensions |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C2), (C4), (C5) |
| (I5) | window_strides |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C2), (C6), (C7) |
| (I6) | padding |
ค่าคงที่เทนเซอร์ 2 มิติของประเภท si64 |
(C2), (C8) |
| (I7) | select |
ฟังก์ชัน | (C9) |
| (I8) | scatter |
ฟังก์ชัน | (C10) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (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_shapenum_windows = is_empty_window ? 0 : floor((padded_operand_shape - window_dimensions) / window_strides) + 1
- (C3)
element_type(init_value) = element_type(operand) - (C4)
size(window_dimensions) = rank(operand) - (C5)
0 < window_dimensions - (C6)
size(window_strides) = rank(operand) - (C7)
0 < window_strides - (C8)
shape(padding) = [rank(operand), 2] - (C9)
selectมีประเภท(tensor<E>, tensor<E>) -> tensor<i1>โดยที่E = element_type(operand) - (C10)
scatterมีประเภท(tensor<E>, tensor<E>) -> tensor<E>โดยที่is_promotable(element_type(operand), E) - (C11)
shape(operand) = shape(result) - (C12)
element_type(result) = E
ตัวอย่าง
// %operand: [[1, 5], [2, 5], [3, 6], [4, 4]]
// %source: [[5, 6], [7, 8]]
// %init_value: 0
%result = "stablehlo.select_and_scatter"(%operand, %source, %init_value) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.compare"(%arg0, %arg1) {
comparison_direction = #stablehlo<comparison_direction GE>
} : (tensor<i64>, tensor<i64>) -> tensor<i1>
"stablehlo.return"(%0) : (tensor<i1>) -> ()
}, {
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>):
%0 = "stablehlo.add"(%arg0, %arg1) : (tensor<i64>, tensor<i64>) -> tensor<i64>
"stablehlo.return"(%0) : (tensor<i64>) -> ()
}) {
window_dimensions = array<i64: 3, 1>,
window_strides = array<i64: 2, 1>,
padding = dense<[[0, 1], [0, 0]]> : tensor<2x2xi64>
} : (tensor<4x2xi64>, tensor<2x2xi64>, tensor<i64>) -> tensor<4x2xi64>
// %result: [[0, 0], [0, 0], [5, 14], [7, 0]]
ส่ง
ความหมาย
ส่ง inputs ไปยังช่อง channel_id จากนั้นระบบจะส่งอินพุตไปยังอุปกรณ์อื่นๆ
ตามลำดับที่ระบุโดย source_target_pairs การดำเนินการจะสร้างresultโทเค็น
หาก is_host_transfer เป็น true การดำเนินการจะโอนข้อมูลไปยัง
โฮสต์ ไม่เช่นนั้น ระบบจะโอนข้อมูลไปยังอุปกรณ์อื่นตามค่าของ
source_target_pairs โดยแฟล็กนี้จะทำซ้ำข้อมูลที่ระบุไว้ใน
channel_type ดังนั้นในอนาคตเราจึงวางแผนที่จะเก็บไว้เพียงรายการเดียว
(#666) หาก is_host_transfer
= false และ source_target_pairs เป็น None หรือว่างเปล่า ระบบจะถือว่าเป็นการทำงานที่ไม่ได้กำหนดไว้
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | inputs |
จำนวนเทนเซอร์หรือเทนเซอร์ที่ควอนไทซ์ได้ | |
| (I2) | token |
token |
|
| (I3) | channel_id |
ค่าคงที่ของประเภท si64 |
|
| (I4) | channel_type |
enum ของ DEVICE_TO_DEVICE และ DEVICE_TO_HOST |
(C5) |
| (I5) | is_host_transfer |
ค่าคงที่ของประเภท i1 |
(C5-C6) |
| (I6) | source_target_pairs |
ค่าคงที่เทนเซอร์ 2 มิติของประเภท si64 |
(C1-C4), (C6) |
เอาต์พุต
| ชื่อ | ประเภท |
|---|---|
result |
token |
ข้อจำกัด
- (C1)
dim(source_target_pairs, 1) = 2 - (C2)
is_unique(source_target_pairs[:, 0]) - (C3)
is_unique(source_target_pairs[:, 1]) - (C4)
0 <= source_target_pairs < Nโดยที่Nมีคำจำกัดความดังนี้num_replicasหากใช้cross_replicanum_partitionsหากใช้cross_partition
- (C5)
channel_typeมีคำจำกัดความดังนี้DEVICE_TO_HOSTหากis_host_transfer = trueDEVICE_TO_DEVICEหรือไม่เช่นนั้น
ตัวอย่าง
%result = "stablehlo.send"(%operand, %token) {
channel_handle = #stablehlo.channel_handle<handle = 0, type = 1>,
is_host_transfer = false,
source_target_pairs = dense<[[0, 1], [1, 2]]> : tensor<2x2xi64>
} : (tensor<2x2xi64>, !stablehlo.token) -> !stablehlo.token
shift_left
ความหมาย
ดำเนินการเลื่อนบิตไปทางซ้ายแบบทีละองค์ประกอบในเทนเซอร์ lhs ตามจำนวนบิต rhs และสร้างเทนเซอร์ result
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | lhs |
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
ความหมาย
ดำเนินการเลื่อนบิตไปทางขวาแบบเลขคณิตทีละองค์ประกอบในเทนเซอร์ 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
ความหมาย
ดำเนินการเลื่อนบิตตรรกะไปทางขวาแบบทีละองค์ประกอบในเทนเซอร์ 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 |
เทนเซอร์ของจำนวนเต็มที่มีเครื่องหมาย ทศนิยม หรือจำนวนเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ของจำนวนเต็มที่มีเครื่องหมาย ทศนิยม หรือจำนวนเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// Logical values: +NaN, -1.0, -0.0, +0.0, 1.0
// operand: [0x7FFFFFFFFFFFFFFF, -1.0, -0.0, 0.0, 1.0]
%result = "stablehlo.sign"(%operand) : (tensor<5xf64>) -> tensor<5xf64>
// Logical values: +NaN, -1.0, -0.0, +0.0, 1.0
// %result: [0x7FFFFFFFFFFFFFFF, -1.0, -0.0, 0.0, 1.0]
ไซน์
ความหมาย
ดำเนินการฟังก์ชันไซน์แบบทีละองค์ประกอบกับเทนเซอร์ operand และสร้างเทนเซอร์ result
โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับ Float:
sinจาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: ไซน์เชิงซ้อน
- สำหรับประเภทที่กำหนดปริมาณ:
dequantize_op_quantize(sine, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %operand: [
// [0.0, 1.57079632], // [0, pi/2]
// [3.14159265, 4.71238898] // [pi, 3pi/2]
// ]
%result = "stablehlo.sine"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[0.0, 1.0], [0.0, -1.0]]
ส่วนแบ่ง
ความหมาย
ดึงข้อมูล Slice จาก operand โดยใช้ดัชนีเริ่มต้นที่คำนวณแบบคงที่
และสร้างเทนเซอร์ result start_indices มีดัชนีเริ่มต้นของ
สไลซ์สำหรับแต่ละมิติข้อมูล limit_indices มีดัชนีสิ้นสุด
(ไม่รวม) ของสไลซ์สำหรับแต่ละมิติข้อมูล และ strides มีระยะก้าวกระโดด
สำหรับแต่ละมิติข้อมูล
ในรูปแบบที่เป็นทางการมากขึ้น result[result_index] = operand[operand_index] where
operand_index = start_indices + result_index * strides
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (C1-C3), (C5) |
| (I2) | start_indices |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C2), (C3), (C5) |
| (I3) | limit_indices |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C2), (C3), (C5) |
| (I4) | strides |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C2), (C4) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณต่อ Tensor | (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]
// ]
จัดเรียง
ความหมาย
จัดเรียงสไลซ์ 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]) + dimensionresult_slice = [ri0, ..., :, ..., riR-1]โดยที่riNคือองค์ประกอบแต่ละรายการในresult_indexและแทรก:ที่adjusted_dimensioninputs_together = (inputs[0]..., ..., inputs[N-1]...)results_together[result_slice] = sort(inputs_together[result_slice], comparator_together)- โดย
sortจะจัดเรียงสไลซ์ 1 มิติในลำดับที่ไม่ใช่จากมากไปน้อย โดยคาดหวังว่าcomparator_togetherจะแสดงผลtrueหากอาร์กิวเมนต์ทางด้านซ้ายน้อยกว่าอาร์กิวเมนต์ที่ 2 ทางด้านขวา def comparator_together(lhs_together, rhs_together): args = [] for (lhs_el, rhs_el) in zip(lhs_together, rhs_together): args.append(lhs_el) args.append(rhs_el) return comparator(*args)(results[0]..., ..., results[N-1]...) = results_together
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | inputs |
เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ | (C1-C5) |
| (I2) | dimension |
ค่าคงที่ของประเภท si64 |
(C4) |
| (I3) | is_stable |
ค่าคงที่ของประเภท i1 |
|
| (I4) | comparator |
ฟังก์ชัน | (C5) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
results |
เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ | (C2), (C3) |
ข้อจำกัด
- (C1)
0 < size(inputs) - (C2)
type(inputs...) = type(results...) - (C3)
same(shape(inputs...) + shape(results...)) - (C4)
-R <= dimension < Rโดยที่R = rank(inputs[0]) - (C5)
comparatorมีประเภท(tensor<E1>, tensor<E1>, ..., tensor<EN-1>, tensor<EN-1>) -> tensor<i1>โดยที่Ei = element_type(inputs[i])
ตัวอย่าง
// %input0 = [[1, 2, 3], [3, 2, 1]]
// %input1 = [[3, 2, 1], [1, 2, 3]]
%result0, %result1 = "stablehlo.sort"(%input0, %input1) ({
^bb0(%arg0: tensor<i64>, %arg1: tensor<i64>, %arg2: tensor<i64>, %arg3: tensor<i64>):
%predicate = "stablehlo.compare"(%arg0, %arg1) {
comparison_direction = #stablehlo<comparison_direction GT>
} : (tensor<i64>, tensor<i64>) -> tensor<i1>
"stablehlo.return"(%predicate) : (tensor<i1>) -> ()
}) {
dimension = 0 : i64,
is_stable = true
} : (tensor<2x3xi64>, tensor<2x3xi64>) -> (tensor<2x3xi64>, tensor<2x3xi64>)
// %result0 = [[3, 2, 3], [1, 2, 1]]
// %result1 = [[1, 2, 1], [3, 2, 3]]
sqrt
ความหมาย
ดำเนินการรากที่สองแบบทีละองค์ประกอบในเทนเซอร์ operand และสร้างเทนเซอร์
result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับ Float:
squareRootจาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: รากที่สองของจำนวนเชิงซ้อน
- สำหรับประเภทที่กำหนดปริมาณ:
dequantize_op_quantize(sqrt, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %operand: [[0.0, 1.0], [4.0, 9.0]]
%result = "stablehlo.sqrt"(%operand) : (tensor<2x2xf32>) -> tensor<2x2xf32>
// %result: [[0.0, 1.0], [2.0, 3.0]]
ลบ
ความหมาย
ดำเนินการลบแบบทีละองค์ประกอบของเทนเซอร์ 2 ตัว lhs และ rhs และสร้างเทนเซอร์ result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับจำนวนเต็ม: การลบจำนวนเต็ม
- สำหรับ Float:
subtractionจาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: การลบจำนวนเชิงซ้อน
- สำหรับประเภทที่กำหนดปริมาณ
dequantize_op_quantize(subtract, lhs, rhs, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | lhs |
เทนเซอร์ประเภทจำนวนเต็ม จุดลอย หรือจำนวนเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
| (I2) | rhs |
เทนเซอร์ประเภทจำนวนเต็ม จุดลอย หรือจำนวนเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเต็ม จุดลอย หรือจำนวนเชิงซ้อน หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(lhs) = baseline_type(rhs) = baseline_type(result)
ตัวอย่าง
// %lhs: [[6, 8], [10, 12]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.subtract"(%lhs, %rhs) : (tensor<2x2xf32>, tensor<2x2xf32>) -> (tensor<2x2xf32>)
// %result: [[1, 2], [3, 4]]
tan
ความหมาย
ดำเนินการแทนเจนต์แบบทีละองค์ประกอบในเทนเซอร์ operand และสร้างเทนเซอร์ result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับ Float:
tanจาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: แทนเจนต์เชิงซ้อน
- สำหรับประเภทที่กำหนดปริมาณ:
dequantize_op_quantize(tan, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %operand: [
// [0.0, 1.57079632], // [0, pi/2]
// [3.14159265, 4.71238898] // [pi, 3pi/2]
// ]
%result = "stablehlo.tan"(%operand) : (tensor<2x2xf64>) -> tensor<2x2xf64>
// %result: [
// [0.0, 1.63312e+16],
// [0.0, 5.44375e+15]
// ]
tanh
ความหมาย
ดำเนินการไฮเพอร์โบลิกแทนเจนต์แบบทีละองค์ประกอบในเทนเซอร์ operand และ
สร้างเทนเซอร์ result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับ Float:
tanhจาก IEEE-754 - สำหรับจำนวนเชิงซ้อน: ไฮเปอร์โบลิกแทนเจนต์ของจำนวนเชิงซ้อน
- สำหรับประเภทที่กำหนดปริมาณ
dequantize_op_quantize(tanh, operand, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_type(operand) = baseline_type(result)
ตัวอย่าง
// %operand: [-1.0, 0.0, 1.0]
%result = "stablehlo.tanh"(%operand) : (tensor<3xf32>) -> tensor<3xf32>
// %result: [-0.76159416, 0.0, 0.76159416]
สลับ
ความหมาย
สลับมิติข้อมูลของเทนเซอร์ operand โดยใช้ permutation และสร้างเทนเซอร์ result ในรูปแบบที่เป็นทางการมากขึ้น result[result_index] = operand[operand_index]
โดยที่ result_index[d] = operand_index[permutation[d]]
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (C1-C4) |
| (I2) | permutation |
ค่าคงที่เทนเซอร์ 1 มิติของประเภท si64 |
(C2-C4) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor หรือ Tensor ที่มีการวัดปริมาณ | (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: ดำเนินการกับทรานสโพสของaADJOINT: ดำเนินการกับทรานสโพสผันสังยุคของa
ระบบจะอ่านข้อมูลอินพุตจากสามเหลี่ยมด้านล่างของ a เท่านั้น หาก lower เป็น true หรือสามเหลี่ยมด้านบนของ a ระบบจะแสดงข้อมูลเอาต์พุตในสามเหลี่ยมเดียวกัน
ค่าในสามเหลี่ยมอีกอันจะขึ้นอยู่กับการใช้งาน
หาก unit_diagonal เป็นจริง การติดตั้งใช้งานจะถือว่าองค์ประกอบแนวทแยง
ของ a เท่ากับ 1 มิเช่นนั้น ลักษณะการทำงานจะไม่ได้กำหนดไว้
สำหรับประเภทที่กำหนดปริมาณ ให้ดำเนินการ
dequantize_op_quantize(lambda x, y: triangular_solve(x, y, left_side, lower,
unit_diagonal, transpose_a), a, b, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | a |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1-C3) |
| (I2) | b |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1-C4) |
| (I3) | left_side |
ค่าคงที่ของประเภท i1 |
(C3) |
| (I4) | lower |
ค่าคงที่ของประเภท i1 |
|
| (I5) | unit_diagonal |
ค่าคงที่ของประเภท i1 |
|
| (I6) | transpose_a |
enum ของ NO_TRANSPOSE, TRANSPOSE และ ADJOINT |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ | (C1) |
ข้อจำกัด
- (C1)
baseline_element_type(a) = baseline_element_type(b) - (C2)
2 <= rank(a) = rank(b) = R - (C3) ความสัมพันธ์ระหว่าง
shape(a)กับshape(b)มีคำจำกัดความดังนี้shape(a)[:-3] = shape(b)[:-3]dim(a, -2) = dim(a, -1) = dim(b, left_side ? -2 : -1)
- (C4)
baseline_type(b) = baseline_type(result)
ตัวอย่าง
// %a = [
// [1.0, 0.0, 0.0],
// [2.0, 4.0, 0.0],
// [3.0, 5.0, 6.0]
// ]
// %b = [
// [2.0, 0.0, 0.0],
// [4.0, 8.0, 0.0],
// [6.0, 10.0, 12.0]
// ]
%result = "stablehlo.triangular_solve"(%a, %b) {
left_side = true,
lower = true,
unit_diagonal = false,
transpose_a = #stablehlo<transpose NO_TRANSPOSE>
} : (tensor<3x3xf32>, tensor<3x3xf32>) -> tensor<3x3xf32>
// %result: [
// [2.0, 0.0, 0.0],
// [0.0, 2.0, 0.0],
// [0.0, 0.0, 2.0]
// ]
ทูเพิล
ความหมาย
สร้างทูเพิล result จากค่า val
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | val |
จำนวนค่าที่เปลี่ยนแปลงได้ | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
ทูเพิล | (C1) |
ข้อจำกัด
- (C1)
resultมีประเภทtuple<E0, ..., EN-1>โดยที่Ei = type(val[i])
ตัวอย่าง
// %val0: memref[1.0, 2.0]
// %val1: (3)
%result = "stablehlo.tuple"(%val0, %val1) : (memref<2xf32>, tuple<tensor<i32>>) -> tuple<memref<2xf32>, tuple<tensor<i32>>>
// %result: (memref[1.0, 2.0], (3))
uniform_dequantize
ความหมาย
ทำการแปลงเทนเซอร์ที่เล็กลง operand เป็นเทนเซอร์แบบจุดลอยตัว result ทีละองค์ประกอบตามพารามิเตอร์การลดขนาดที่กำหนดโดยประเภท operand
กล่าวอย่างเป็นทางการมากขึ้น result = dequantize(operand)
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
Tensor ที่ทำให้เล็กลง | (C1), (C2) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทจุดลอยตัว | (C1), (C2) |
ข้อจำกัด
- (C1)
shape(operand) = shape(result) - (C2)
element_type(result) = expressed_type(operand)
ตัวอย่าง
// %operand: [10, 10]
%result = "stablehlo.uniform_dequantize"(%operand) : (tensor<2x!quant.uniform<i8:f32:0, {0.1:-30,0.5:-20}>>) -> tensor<2xf32>
// %result: [4.0, 15.0]
uniform_quantize
ความหมาย
ทำการแปลงเทนเซอร์แบบจุดลอยตัวหรือเทนเซอร์ที่เล็กลง
operandเป็นเทนเซอร์ที่เล็กลง result ตามพารามิเตอร์การลดขนาด
ที่กำหนดโดยประเภท result
กล่าวอย่างเป็นทางการมากขึ้น
- หาก
is_float(operand)result = quantize(operand, type(result))
- หาก
is_quantized(operand)float_result = dequantize(operand)result = quantize(float_result, type(result))
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | operand |
เทนเซอร์ประเภทจุดลอยตัวหรือประเภทที่ผ่านการวัดปริมาณ | (C1), (C2) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
Tensor ที่ทำให้เล็กลง | (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 แบบทีละองค์ประกอบของเทนเซอร์ 2 รายการ lhs และ rhs แล้วสร้างเทนเซอร์ result
โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ
- สำหรับบูลีน: XOR เชิงตรรกะ
- สำหรับจำนวนเต็ม: บิตไวส์ XOR
อินพุต
| ป้ายกำกับ | ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|---|
| (I1) | lhs |
เทนเซอร์ประเภทบูลีนหรือจำนวนเต็ม | (C1) |
| (I2) | rhs |
เทนเซอร์ประเภทบูลีนหรือจำนวนเต็ม | (C1) |
เอาต์พุต
| ชื่อ | ประเภท | ข้อจำกัด |
|---|---|---|
result |
เทนเซอร์ประเภทบูลีนหรือจำนวนเต็ม | (C1) |
ข้อจำกัด
- (C1)
type(lhs) = type(rhs) = type(result)
ตัวอย่าง
// Bitwise operation with with integer tensors
// %lhs: [[1, 2], [3, 4]]
// %rhs: [[5, 6], [7, 8]]
%result = "stablehlo.xor"(%lhs, %rhs) : (tensor<2x2xi32>, tensor<2x2xi32>) -> tensor<2x2xi32>
// %result: [[4, 4], [4, 12]]
// Logical operation with with boolean tensors
// %lhs: [[false, false], [true, true]]
// %rhs: [[false, true], [false, true]]
%result = "stablehlo.xor"(%lhs, %rhs) : (tensor<2x2xi1>, tensor<2x2xi1>) -> tensor<2x2xi1>
// %result: [[false, true], [true, false]]
การทำงานร่วมกันของภาษาถิ่น
ปัจจุบัน โปรแกรม StableHLO ในการใช้งานจริงบางครั้งมีการดำเนินการที่ StableHLO ไม่ได้กำหนดไว้
โมดูล ฟังก์ชัน การเรียกใช้ และการคืนค่า
StableHLO ใช้การดำเนินการ MLIR ต้นทางสำหรับ ModuleOp, FuncOp, CallOp และ ReturnOp การดำเนินการนี้มีขึ้นเพื่อให้ทำงานร่วมกับกลไก MLIR ที่มีอยู่ได้ดียิ่งขึ้น เนื่องจากมีการเขียนการส่งผ่านที่มีประโยชน์หลายรายการโดยกำหนดเป้าหมายเป็น FuncOp และ ModuleOp และไปป์ไลน์การคอมไพล์หลายรายการคาดหวังให้มี Op เหล่านี้อยู่ การรับประกันความเข้ากันได้อย่างเต็มรูปแบบ จะใช้กับ Op เหล่านี้ หากมีการเปลี่ยนแปลงใดๆ เกี่ยวกับ Op เหล่านี้ในลักษณะที่ไม่เข้ากัน (เช่น การนำออก) เราจะเพิ่ม Op ที่เทียบเท่าใน StableHLO เพื่อรักษาความเข้ากันได้
CHLO
ชุดตัวดำเนินการ CHLO มีการดำเนินการระดับสูงกว่าซึ่งแยกย่อยเป็น StableHLO ปัจจุบันเราไม่รับประกันความเข้ากันได้ของ CHLO หากต้องการรับประกันความเข้ากันได้ ต้องใช้ chlo-legalize-to-stablehlo pass ก่อนการทำให้เป็นอนุกรม
การดำเนินการกับรูปร่าง
Use Case ทั่วไปในชุมชนคือการใช้การดำเนินการบางอย่างจาก Dialect MLIR หลักในโปรแกรม StableHLO แบบไดนามิกเพื่อทำการคำนวณรูปร่าง
โดยทั่วไปแล้ว จะมีshape
โอเปอเรเตอร์shape_ofหรือnum_elements tensorโอเปอเรเตอร์
เช่น dim หรือ from_elements และประเภท index ในตัว
RFC ความเคลื่อนไหว > O2
ระบุว่าอยู่นอกขอบเขต แต่มีการรองรับประเภท index บางส่วน
เพื่อวัตถุประสงค์ในการทำงานร่วมกัน เราไม่รับประกันความเข้ากันได้สำหรับ
การดำเนินการหรือประเภทเหล่านี้ คุณใช้การส่งผ่าน shape-legalize-to-stablehlo
เพื่อแปลงการดำเนินการเหล่านี้เป็นการดำเนินการ StableHLO ที่รองรับอย่างเต็มรูปแบบได้
การดำเนินการที่เลิกใช้งานแล้ว
การดำเนินการ StableHLO หลายรายการที่สืบทอดมาจาก MHLO ถูกเลิกใช้งานแล้วและกำลังจะออกจาก StableHLO ดูรายละเอียดทั้งหมดเกี่ยวกับการนำออกเหล่านี้ได้ใน StableHLO v1.0 Cleanup #2283 ปัญหาในเครื่องมือติดตามสำหรับการเลิกใช้งานเหล่านี้คือ #2340
การดำเนินการเหล่านี้แบ่งออกเป็น 2 หมวดหมู่ ได้แก่
- การดำเนินการ StableHLO ในหมวดหมู่ "ไม่ได้อยู่ใน HLO" - เดิมการดำเนินการเหล่านี้เป็นส่วนหนึ่งของชุดการดำเนินการ StableHLO แต่ต่อมาถือว่าไม่เหมาะสมกับชุดการดำเนินการดังกล่าว
broadcast,create_token,cross-replica-sum,dot,einsum,torch_index_select,unary_einsum(#3) - การดำเนินการที่ไม่ได้ใช้ - การดำเนินการเหล่านี้อาจมีประโยชน์ในบางครั้ง แต่การดำเนินการ
ยังไม่ได้รับการพัฒนา หรือไปป์ไลน์ที่ใช้การดำเนินการเหล่านี้ได้รับการ
ปรับโครงสร้างใหม่เพื่อไม่ให้ต้องใช้การดำเนินการเหล่านี้อีกต่อไป ซึ่งรวมถึง
map,tuple(#598),get_tuple_element,rng,complexการเปรียบเทียบ #560, และ Convolutionwindow_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ฟังก์ชัน
และคำนวณค่าเอาต์พุต ค่าเอาต์พุตของฟังก์ชันจะคำนวณโดย
การเรียกใช้กราฟของ Op ที่มีรากฐานมาจาก Op return ที่เกี่ยวข้อง
ลำดับการดำเนินการจะขึ้นอยู่กับการใช้งานตราบใดที่สอดคล้องกับ
โฟลว์ของข้อมูล กล่าวคือ หากมีการดำเนินการกับ Op ก่อนที่จะมีการใช้งาน ใน StableHLO การดำเนินการที่มีผลข้างเคียงทั้งหมดจะใช้โทเค็น 1 รายการและสร้างโทเค็น 1 รายการ (สามารถมัลติเพล็กซ์โทเค็นหลายรายการเป็นโทเค็นเดียวผ่าน after_all) ดังนั้นลำดับการดำเนินการของผลข้างเคียงจึงสอดคล้องกับโฟลว์ของข้อมูลด้วย ตัวอย่างเช่น ในโปรแกรมด้านล่าง
ลำดับการดำเนินการที่เป็นไปได้มี 2 แบบ ได้แก่ %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 สามารถดำเนินการแบบคู่ขนาน จัดระเบียบเป็นตารางกระบวนการ 2 มิติขนาด num_replicas x num_partitions ซึ่งทั้ง 2 มีประเภทเป็น 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) ซึ่งทั้ง 2 รายการมี
ประเภท ui32
ขนาดของตารางกริดกระบวนการจะทราบแบบคงที่สำหรับทุกโปรแกรม (ในอนาคต เราวางแผนที่จะทำให้เป็นส่วนที่ชัดเจนของโปรแกรม StableHLO #650) และตำแหน่งภายในตารางกริดกระบวนการจะทราบแบบคงที่สำหรับทุกกระบวนการ แต่ละกระบวนการมีสิทธิ์เข้าถึงตำแหน่งของตัวเองภายในกริดกระบวนการผ่านการดำเนินการ replica_id และ partition_id
ในตารางกระบวนการ โปรแกรมทั้งหมดอาจเหมือนกัน (ในรูปแบบ "โปรแกรมเดียว ข้อมูลหลายรายการ") อาจแตกต่างกันทั้งหมด (ในรูปแบบ "หลายโปรแกรม ข้อมูลหลายรายการ") หรืออาจเป็นแบบผสมก็ได้ ในอนาคต เราวางแผนที่จะรองรับสำนวนอื่นๆ ในการกำหนดโปรแกรม StableHLO แบบขนาน ซึ่งรวมถึง GSPMD (#619)
ภายในตารางกริดของกระบวนการ กระบวนการส่วนใหญ่จะทำงานแยกกัน โดยมีสถานะการทำงาน ค่าอินพุต/ค่ากลาง/ค่าเอาต์พุตแยกกัน และส่วนใหญ่จะดำเนินการแยกกันระหว่างกระบวนการ ยกเว้นการดำเนินการแบบกลุ่มจำนวนเล็กน้อยที่อธิบายไว้ด้านล่าง
เนื่องจากการดำเนินการส่วนใหญ่ใช้ค่าจากกระบวนการเดียวกันเท่านั้น จึงมักจะไม่มีความกำกวมในการอ้างอิงค่าเหล่านี้ตามชื่อ
อย่างไรก็ตาม เมื่ออธิบายความหมายของ Collective Ops นั้นไม่เพียงพอ และ
ทำให้เกิดสัญกรณ์ name@process_id เพื่ออ้างอิงถึงค่า name
ภายในกระบวนการที่เฉพาะเจาะจง (ในมุมมองนั้น name ที่ไม่มีคุณสมบัติอาจถือเป็นรูปแบบย่อของ name@(replica_id(), partition_id()))
ลำดับการดำเนินการในกระบวนการต่างๆ จะขึ้นอยู่กับการติดตั้งใช้งาน ยกเว้นการ ซิงค์ที่เกิดจากการสื่อสารแบบจุดต่อจุดและการดำเนินการแบบกลุ่ม ตามที่อธิบายไว้ด้านล่าง
การสื่อสารแบบจุดต่อจุด
กระบวนการ StableHLO สามารถสื่อสารกันผ่านแชแนล StableHLO ได้ ช่องจะแสดงด้วยรหัสบวกของประเภท
si64 คุณสามารถส่งค่าไปยังช่องทางต่างๆ และรับค่าจากช่องทางต่างๆ ได้ผ่านการดำเนินการต่างๆ
การกำหนดรูปแบบเพิ่มเติม เช่น แหล่งที่มาของรหัสช่องเหล่านี้ วิธีที่โปรแกรมประมวลผลรับรู้รหัสช่อง และการซิงค์ประเภทใดที่รหัสช่องเหล่านี้นำมาใช้ ยังอยู่ระหว่างการพิจารณา (#484)
การสื่อสารผ่านการสตรีม
กระบวนการ StableHLO ทุกกระบวนการมีสิทธิ์เข้าถึงอินเทอร์เฟซการสตรีม 2 รายการ ได้แก่
- ในฟีดที่อ่านได้
- Outfeed ที่เขียนได้
ฟีดขาเข้าและฟีดขาออกมีการใช้งานปลายทางอื่นๆ ที่กำหนดไว้ ซึ่งแตกต่างจากช่องที่ใช้ในการสื่อสารระหว่างกระบวนการและมีกระบวนการที่ปลายทั้ง 2 ด้าน
การกำหนดรูปแบบเพิ่มเติม เช่น วิธีที่การสื่อสารผ่านการสตรีมมีอิทธิพลต่อลำดับการดำเนินการและประเภทของการซิงค์ที่เกิดขึ้นจากลำดับการดำเนินการนั้น จะมีการกำหนดในภายหลัง (#484)
การดำเนินการแบบกลุ่ม
StableHLO มีการดำเนินการแบบกลุ่ม 6 รายการ ได้แก่ all_gather, all_reduce,
all_to_all, collective_broadcast, collective_permute และ
reduce_scatter การดำเนินการทั้งหมดนี้จะแยกกระบวนการในกริดกระบวนการ StableHLO
ออกเป็นกลุ่มกระบวนการ StableHLO และดำเนินการคำนวณร่วมกันภายใน
แต่ละกลุ่มกระบวนการ โดยไม่ขึ้นอยู่กับกลุ่มกระบวนการอื่นๆ
ภายในกลุ่มกระบวนการแต่ละกลุ่ม การดำเนินการแบบกลุ่มอาจทำให้เกิด อุปสรรคในการซิงโครไนซ์ การกำหนดรูปแบบเพิ่มเติม เช่น การอธิบายเวลาที่การซิงค์นี้เกิดขึ้นอย่างแน่นอน วิธีที่กระบวนการต่างๆ มาถึงอุปสรรคนี้ และสิ่งที่เกิดขึ้นหากกระบวนการไม่เป็นไปตามนี้ จะมีการกำหนดในภายหลัง (#484)
หากกลุ่มกระบวนการเกี่ยวข้องกับการสื่อสารข้ามพาร์ติชัน กล่าวคือ มีกระบวนการในกลุ่มกระบวนการที่มีรหัสพาร์ติชันแตกต่างกัน การดำเนินการของ Op แบบกลุ่มจะต้องมีแชแนล และ Op แบบกลุ่มต้องระบุ channel_id ที่เป็นบวกของประเภท si64 การสื่อสารข้ามรีเพล็กซ์ไม่จำเป็นต้องมี
แชแนล
การคำนวณที่ดำเนินการโดยการดำเนินการแบบกลุ่มจะเฉพาะเจาะจงกับการดำเนินการแต่ละรายการ และอธิบายไว้ในส่วนการดำเนินการแต่ละรายการด้านบน อย่างไรก็ตาม กลยุทธ์ที่ใช้ในการแบ่งตารางกระบวนการออกเป็นกลุ่มกระบวนการจะแชร์ระหว่างการดำเนินการเหล่านี้ และอธิบายไว้ในส่วนนี้ กล่าวอย่างเป็นทางการมากขึ้น StableHLO รองรับ กลยุทธ์ทั้ง 4 ต่อไปนี้
cross_replica
การสื่อสารข้ามรีพลิกาจะเกิดขึ้นภายในกลุ่มกระบวนการแต่ละกลุ่มเท่านั้น กลยุทธ์นี้ใช้ replica_groups ซึ่งเป็นรายการของรายการรหัสรีพลิกา และคำนวณ
ผลคูณคาร์ทีเซียนของ replica_groups กับ partition_ids replica_groups
ต้องมีองค์ประกอบที่ไม่ซ้ำกันและครอบคลุม replica_ids ทั้งหมด ในรูปแบบที่เป็นทางการมากขึ้น การใช้ไวยากรณ์ Python จะเป็นดังนี้
def cross_replica(replica_groups: List[List[ReplicaId]]) -> List[List[ProcessId]]:
for replica_group in replica_groups:
for partition_id in partition_ids:
process_group = []
for replica_id in replica_group:
process_group.append((replica_id, partition_id))
yield process_group
เช่น สำหรับ replica_groups = [[0, 1], [2, 3]] และ num_partitions = 2
cross_replica จะสร้าง
[[(0, 0), (1, 0)], [(0, 1), (1, 1)], [(2, 0), (3, 0)], [(2, 1), (3, 1)]]
cross_partition
การสื่อสารข้ามพาร์ติชันจะเกิดขึ้นภายในกลุ่มกระบวนการแต่ละกลุ่มเท่านั้น กลยุทธ์นี้ใช้ partition_groups ซึ่งเป็นรายการของรายการรหัสพาร์ติชัน และคำนวณผลคูณคาร์ทีเซียนของ partition_groups โดย replica_ids
partition_groups ต้องมีองค์ประกอบที่ไม่ซ้ำกันและครอบคลุม partition_ids ทั้งหมด
หรือจะใช้ไวยากรณ์ Python อย่างเป็นทางการมากขึ้นก็ได้ ดังนี้
def cross_partition(partition_groups: List[List[PartitionId]]) -> List[List[ProcessId]]:
for partition_group in partition_groups:
for replica_id in replica_ids:
process_group = []
for partition_id in partition_group:
process_group.append((replica_id, partition_id))
yield process_group
เช่น สำหรับ partition_groups = [[0, 1]] และ num_replicas = 4
cross_partition จะสร้าง
[[(0, 0), (0, 1)], [(1, 0), (1, 1)], [(2, 0), (2, 1)], [(3, 0), (3, 1)]]
cross_replica_and_partition
การสื่อสารทั้งแบบข้ามรีพลิกาและข้ามพาร์ติชันอาจเกิดขึ้นภายในกลุ่มกระบวนการแต่ละกลุ่ม กลยุทธ์นี้ใช้ replica_groups ซึ่งเป็นรายการของรายการรหัสรีพลิกา และคำนวณผลคูณคาร์ทีเซียนของแต่ละ replica_group โดย partition_ids replica_groups ต้องมีองค์ประกอบที่ไม่ซ้ำกันและครอบคลุม replica_ids ทั้งหมด หรือจะใช้ไวยากรณ์ Python อย่างเป็นทางการมากขึ้นก็ได้ ดังนี้
def cross_replica_and_partition(replica_groups: List[List[ReplicaId]]) -> List[List[ProcessId]]:
for replica_group in replica_groups:
process_group = []
for partition_id in partition_ids:
for replica_id in replica_group:
process_group.append((replica_id, partition_id))
yield process_group
เช่น สำหรับ replica_groups = [[0, 1], [2, 3]] และ num_partitions = 2
cross_replica_and_partition จะสร้าง
[[(0, 0), (1, 0), (0, 1), (1, 1)], [(2, 0), (3, 0), (2, 1), (3, 1)]]
flattened_ids
กลยุทธ์นี้ใช้ flattened_id_groups ซึ่งเป็นรายการของรายการรหัสกระบวนการที่ "ยุบ" แล้ว
ในรูปแบบ replica_id * num_partitions + partition_id และ
เปลี่ยนให้เป็นรหัสกระบวนการ flattened_id_groups ต้องมีองค์ประกอบที่ไม่ซ้ำกัน
และครอบคลุม process_ids ทั้งหมด หรือจะใช้ไวยากรณ์ Python อย่างเป็นทางการมากขึ้นก็ได้ ดังนี้
def flattened_ids(flattened_id_groups: List[List[ui32]]) -> List[List[ProcessId]]:
for flattened_id_group in flattened_id_groups:
process_group = []
for flattened_id in flattened_id_group:
replica_id = flattened_id // num_partitions
partition_id = flattened_id % num_partitions
process_group.append((replica_id, partition_id))
yield process_group
เช่น สำหรับ flattened_id_groups = [[0, 1, 2, 3], [4, 5, 6, 7]],
num_replicas = 4 และ num_partitions = 2, flattened_ids จะสร้าง [[(0, 0), (0, 1), (1, 0), (1, 1)], [(2, 0), (2, 1), (3, 0), (3, 1)]]
ความแม่นยำ
ปัจจุบัน StableHLO ไม่รับประกันความถูกต้องของตัวเลข แต่ในอนาคตอาจมีการเปลี่ยนแปลง (#1156)
ความหมายของการดำเนินการที่กำหนดปริมาณ
การตีความการทำงานของ StableHLO ที่มีการวัดปริมาณอาจแตกต่างกันไปตาม ข้อกำหนดและความสามารถของฮาร์ดแวร์ ตัวอย่างเช่น ฮาร์ดแวร์บางอย่างอาจเลือก ตีความการดำเนินการที่ผ่านการวัดปริมาณโดยใช้กลยุทธ์ "ยกเลิกการวัดปริมาณ ดำเนินการจุดลอยตัว และวัดปริมาณในที่สุด" ส่วนโปรแกรมอื่นๆ อาจทำการคำนวณทั้งหมด ด้วยการคำนวณจำนวนเต็ม ดังนั้น การตีความการดำเนินการ StableHLO ที่มีการวัดปริมาณจึงขึ้นอยู่กับการ ติดตั้งใช้งานที่เฉพาะเจาะจงเท่านั้น การตีความการหาปริมาณแบบไฮบริด (#1575) ควรเป็นไปตาม ความหมายที่กำหนดไว้ในข้อกำหนด (ผ่าน 1792)
ข้อผิดพลาด
โปรแกรม StableHLO ได้รับการตรวจสอบผ่านชุดข้อจํากัดที่ครอบคลุมสําหรับ การดําเนินการแต่ละอย่าง ซึ่งจะตัดข้อผิดพลาดหลายประเภทออกก่อนรันไทม์ อย่างไรก็ตาม ยังคงมีโอกาสเกิดข้อผิดพลาดได้ เช่น ผ่านการล้นของจำนวนเต็ม การเข้าถึงที่อยู่นอกขอบเขต ฯลฯ ข้อผิดพลาดทั้งหมดเหล่านี้จะส่งผลให้เกิดลักษณะการทำงานที่กำหนดโดยการใช้งาน เว้นแต่จะมีการระบุไว้อย่างชัดเจน แต่ลักษณะการทำงานนี้อาจเปลี่ยนแปลงในอนาคต (#1157)
ข้อยกเว้นของจุดลอยตัว
ข้อยกเว้นสำหรับกฎข้อนี้คือข้อยกเว้นเกี่ยวกับจำนวนทศนิยมในโปรแกรม StableHLO
ซึ่งมีลักษณะการทำงานที่กำหนดไว้อย่างชัดเจน การดำเนินการที่ทำให้เกิดข้อยกเว้นที่กำหนดโดยมาตรฐาน IEEE-754 (การดำเนินการที่ไม่ถูกต้อง การหารด้วย 0 การล้น การอันเดอร์โฟลว์ หรือข้อยกเว้นที่ไม่แน่นอน) จะสร้างผลลัพธ์เริ่มต้น (ตามที่กำหนดไว้ในมาตรฐาน) และดำเนินการต่อโดยไม่ต้องเพิ่มสถานะที่เกี่ยวข้อง ซึ่งคล้ายกับการจัดการข้อยกเว้น raiseNoFlag จากมาตรฐาน ข้อยกเว้นสำหรับการดำเนินการที่ไม่เป็นไปตามมาตรฐาน (เช่น เลขคณิตที่ซับซ้อนและฟังก์ชันอดิศัยบางอย่าง) จะขึ้นอยู่กับการ
ใช้งาน
รูปร่างไม่ตรงกัน
StableHLO รองรับเทนเซอร์ที่มีรูปร่างแบบไดนามิก อย่างไรก็ตาม รูปร่างต้องตรงกันที่รันไทม์ มิเช่นนั้นลักษณะการทำงานจะไม่ได้กำหนดไว้ StableHLO ไม่ได้ระบุ การดำเนินการที่สามารถยืนยันว่าเทนเซอร์มีรูปร่างที่กำหนดในรันไทม์ การสร้างโค้ดที่ถูกต้องเป็นความรับผิดชอบของโปรดิวเซอร์
ตัวอย่างที่เฉพาะเจาะจงคือโปรแกรมด้านล่างนี้ใช้ได้ อย่างไรก็ตาม ในเวลาเรียกใช้ รูปร่างที่แน่นอนของ %arg0 และ %arg1 จะต้องเหมือนกัน ไม่เช่นนั้นลักษณะการทำงานของโปรแกรมจะไม่ได้กำหนดไว้
func.func @foo(%arg0: tensor<?xi32>, %arg1: tensor<?xi32>) -> tensor<?xi32> {
%0 = stablehlo.add %arg0, %arg1 : tensor<?xi32>
return %0 : tensor<?xi32>
}
สัญกรณ์
เอกสารนี้ใช้ไวยากรณ์ EBNF
รูปแบบ ISO ที่แก้ไขแล้ว (ISO/IEC 14977:1996,
Wikipedia)
ในการอธิบายไวยากรณ์ โดยมีการแก้ไข 2 อย่างคือ 1) กำหนดกฎโดยใช้ ::= แทน =
2) การต่อกันจะแสดงโดยใช้การวางติดกันแทน ,
สำหรับการอธิบายความหมาย (เช่น ภายในส่วน "ประเภท" "ค่าคงที่" และ "การดำเนินการ") เราใช้สูตรที่อิงตามไวยากรณ์ของ Python ซึ่งขยายการรองรับ เพื่อแสดงการดำเนินการอาร์เรย์อย่างกระชับตามที่อธิบายไว้ด้านล่าง วิธีนี้เหมาะสำหรับข้อมูลโค้ดขนาดเล็ก แต่ในกรณีที่จำเป็นต้องใช้ข้อมูลโค้ดขนาดใหญ่ เราจะใช้ไวยากรณ์ Python แบบดั้งเดิมซึ่งจะมีการแนะนำอย่างชัดเจนเสมอ
สูตร
มาดูวิธีการทำงานของสูตรตามตัวอย่างจากdot_general
ข้อกำหนดกัน ข้อจำกัดอย่างหนึ่งสำหรับการดำเนินการนี้มีลักษณะดังนี้
dim(lhs, lhs_batching_dimensions...) = dim(rhs, rhs_batching_dimensions...)
ชื่อที่ใช้ในสูตรนี้มาจาก 2 แหล่งที่มา ได้แก่ 1) ฟังก์ชันส่วนกลาง เช่น
dim และ 2) คำจำกัดความของสมาชิกขององค์ประกอบโปรแกรมที่เกี่ยวข้อง เช่น
อินพุต lhs, lhs_batching_dimensions, rhs และ rhs_batching_dimensions
ที่กำหนดไว้ในส่วน "อินพุต" ของ dot_general
ดังที่กล่าวไว้ข้างต้น ไวยากรณ์ของสูตรนี้อิงตาม Python โดยมีส่วนขยายบางอย่างที่เน้นความกระชับ หากต้องการทำความเข้าใจสูตร ให้แปลงสูตร เป็นไวยากรณ์ Python แบบเดิม
ก) ในสูตรเหล่านี้ เราใช้ = เพื่อแสดงถึงความเท่ากัน ดังนั้นขั้นตอนแรกในการทำความเข้าใจไวยากรณ์ของ Python คือการแทนที่ = ด้วย == ดังนี้
dim(lhs, lhs_batching_dimensions...) == dim(rhs, rhs_batching_dimensions...)
ข) นอกจากนี้ สูตรเหล่านี้ยังรองรับเครื่องหมายจุดไข่ปลา (...) ซึ่งจะเปลี่ยนนิพจน์สเกลาร์
เป็นนิพจน์เทนเซอร์ กล่าวโดยย่อ f(xs...) หมายความโดยประมาณว่า "สำหรับแต่ละ
สเกลาร์ x ในเทนเซอร์ xs ให้คำนวณสเกลาร์ f(x) แล้วส่งคืนทั้งหมด
ผลลัพธ์สเกลาร์เหล่านี้ร่วมกันเป็นผลลัพธ์เทนเซอร์" ในไวยากรณ์ Python แบบดั้งเดิม
สูตรตัวอย่างของเราจะกลายเป็น
[dim(lhs, dim1) for dim1 in lhs_batching_dimensions] ==
[dim(rhs, dim2) for dim2 in rhs_batching_dimensions]
เครื่องหมายจุดไข่ปลาช่วยให้เราหลีกเลี่ยงการทำงานในระดับสเกลาร์แต่ละรายการได้บ่อยครั้ง
อย่างไรก็ตาม ในบางกรณีที่ซับซ้อน อาจใช้ไวยากรณ์แบบกึ่งทางการระดับล่าง
เช่นเดียวกับในสูตร start_indices[bi0, ..., :, ..., biN]
จากข้อกำหนด gather เพื่อความกระชับ เราจึงไม่ได้
ให้รูปแบบที่แน่นอนสำหรับการแปลไวยากรณ์ดังกล่าวเป็น Python แบบดั้งเดิม
โดยหวังว่าคุณจะยังคงเข้าใจได้โดยสัญชาตญาณเป็นกรณีไป
โปรดแจ้งให้เราทราบหากสูตรบางสูตรดูไม่ชัดเจน และเราจะพยายาม
ปรับปรุงสูตรเหล่านั้น
นอกจากนี้ คุณจะเห็นว่าสูตรใช้เครื่องหมายจุดไข่ปลาเพื่อขยายรายการทุกประเภท รวมถึงเทนเซอร์ รายการเทนเซอร์ (ซึ่งอาจเกิดจากเทนเซอร์จำนวน ตัวแปร) ฯลฯ นี่เป็นอีกส่วนที่เราไม่ได้ให้รูปแบบที่แน่นอน (เช่น รายการไม่ได้เป็นส่วนหนึ่งของระบบประเภท StableHLO ด้วยซ้ำ) และ เราอาศัยความเข้าใจที่ใช้งานง่ายแทน
ค) เครื่องมือการเขียนที่น่าสังเกตสุดท้ายที่เราใช้คือการออกอากาศโดยนัย แม้ว่าชุดคำสั่ง StableHLO จะไม่รองรับการออกอากาศโดยนัย แต่สูตรจะรองรับเพื่อความกระชับ กล่าวโดยย่อคือ หากใช้สเกลาร์ในบริบทที่คาดหวังเทนเซอร์ ระบบจะออกอากาศสเกลาร์ไปยังรูปร่างที่คาดหวัง
หากจะใช้ตัวอย่าง dot_general ต่อไป นี่คือข้อจำกัดอีกอย่าง
0 <= lhs_batching_dimensions < rank(lhs) ตามที่กำหนดไว้ในdot_general
ข้อกำหนด lhs_batching_dimensions คือเทนเซอร์ แต่ทั้ง 0 และ rank(lhs) เป็นสเกลาร์ หลังจากที่เราใช้การออกอากาศโดยนัยแล้ว สูตรจะกลายเป็น [0, ..., 0] <= lhs_batching_dimensions < [rank(lhs), ..., rank(lhs)]
เมื่อใช้กับdot_generalการดำเนินการหนึ่งๆ สูตรนี้จะ
ประเมินเป็นเทนเซอร์ของบูลีน เมื่อใช้สูตรเป็นข้อจํากัด ข้อจํากัดจะยังคงมีผลหากสูตรให้ค่าเป็น true หรือเป็นเทนเซอร์ที่มีเฉพาะองค์ประกอบ true
ชื่อ
ในสูตร ขอบเขตคำศัพท์ประกอบด้วย 1) ฟังก์ชันส่วนกลาง 2) คำจำกัดความของสมาชิก
3) คำจำกัดความในเครื่อง รายการฟังก์ชันส่วนกลางแสดงอยู่ด้านล่าง รายการ คำจำกัดความขององค์ประกอบจะขึ้นอยู่กับองค์ประกอบของโปรแกรมที่ใช้สัญกรณ์ ดังนี้
- สำหรับการดำเนินการ คำจำกัดความของสมาชิกจะมีชื่อที่ระบุไว้ในส่วน "อินพุต" และ "เอาต์พุต"
- สำหรับส่วนอื่นๆ ทั้งหมด คำจำกัดความของสมาชิกจะรวมส่วนโครงสร้างขององค์ประกอบโปรแกรม ซึ่งตั้งชื่อตามเทอร์มินัลที่ไม่ใช่ EBNF ที่เกี่ยวข้อง ส่วนใหญ่แล้ว ชื่อของส่วนโครงสร้างเหล่านี้ได้มาจากการแปลงชื่อของเทอร์มินัลที่ไม่ใช่เทอร์มินัลเป็นรูปแบบ Snake Case (เช่น
IntegerLiteral=>integer_literal) แต่บางครั้งชื่ออาจย่อในกระบวนการนี้ (เช่นQuantizationStorageType=>storage_type) ในกรณีนี้ ชื่อจะมีการระบุอย่างชัดเจนในลักษณะเดียวกับส่วน "อินพุต" / "เอาต์พุต" ในข้อกำหนดการดำเนินการ - นอกจากนี้ คำจำกัดความของสมาชิกจะมี
selfเสมอเพื่ออ้างอิงถึง องค์ประกอบโปรแกรมที่เกี่ยวข้อง
ค่า
เมื่อมีการประเมินสูตร สูตรจะทำงานกับค่าประเภทต่อไปนี้
1) Value (ค่าจริง เช่น dense<[[1, 2], [3, 4]]> : tensor<2x2xi32>;
ค่าเหล่านี้จะทราบประเภทของตัวเองเสมอ)
2) Placeholder (ค่าในอนาคต เช่น lhs, rhs หรือ result; ค่าจริงของค่าเหล่านี้
ยังไม่ทราบ ทราบเพียงประเภทของค่า)
3) Type (ประเภทตามที่กำหนดไว้ในส่วน "ประเภท")
4) Function (ฟังก์ชันส่วนกลางตามที่กำหนดไว้ในส่วน "ฟังก์ชัน")
ชื่ออาจอ้างอิงถึงค่าที่แตกต่างกันไปตามบริบท กล่าวอย่างเจาะจงคือ ส่วน "ความหมาย" สำหรับการดำเนินการ (และส่วนที่เทียบเท่าสำหรับองค์ประกอบโปรแกรมอื่นๆ) จะกำหนดตรรกะรันไทม์ ดังนั้นอินพุตทั้งหมดจึงพร้อมใช้งานเป็น Value
ในทางตรงกันข้าม ส่วน "ข้อจำกัด" สำหรับการดำเนินการ (และสิ่งที่เทียบเท่า) จะกำหนดตรรกะ "เวลาคอมไพล์" กล่าวคือ สิ่งที่มักจะดำเนินการก่อนรันไทม์
ดังนั้นจึงมีเฉพาะอินพุตค่าคงที่เท่านั้นที่ใช้ได้เป็น Value และอินพุตอื่นๆ
จะใช้ได้เป็น Placeholder เท่านั้น
| ชื่อ | ใน "Semantics" | ใน "ข้อจำกัด" |
|---|---|---|
| ฟังก์ชันส่วนกลาง | Function |
Function |
| อินพุตคงที่ | Value |
Value |
| อินพุตที่ไม่คงที่ | Value |
Placeholder |
| เอาต์พุต | Value |
Placeholder |
| คำจำกัดความในพื้นที่ | ขึ้นอยู่กับคำจำกัดความ | ขึ้นอยู่กับคำจำกัดความ |
มาดูตัวอย่างการดำเนินการ transpose กัน
%result = "stablehlo.transpose"(%operand) {
permutation = dense<[2, 1, 0]> : tensor<3xi64>
} : (tensor<2x3x2xi32>) -> tensor<2x3x2xi32>
สำหรับการดำเนินการนี้ permutation เป็นค่าคงที่ จึงพร้อมใช้งานเป็น Value
ทั้งในเชิงความหมายและข้อจำกัด ในทางตรงกันข้าม operand และ result จะ
พร้อมใช้งานเป็น Value ในความหมาย แต่เป็น Placeholder ในข้อจำกัดเท่านั้น
ฟังก์ชัน
การสร้างประเภท
ไม่มีฟังก์ชันที่ใช้สร้างประเภทได้ แต่เราจะใช้ไวยากรณ์ประเภทโดยตรงแทนเนื่องจากมักจะกระชับกว่า เช่น
(tensor<E>, tensor<E>) -> (tensor<E>) แทนที่จะเป็น function_type(
[tensor_type([], E), tensor_type([], E)], [tensor_type([], E)])
ฟังก์ชันในประเภท
element_typeได้รับการกำหนดในประเภทเทนเซอร์และประเภทเทนเซอร์ที่ผ่านการวัดปริมาณ และจะแสดงผลTensorElementTypeหรือQuantizedTensorElementTypeของTensorTypeหรือQuantizedTensorTypeที่เกี่ยวข้องตามลำดับ
def element_type(x: Value | Placeholder | Type):
if type(x) == TensorType:
return tensor_element_type(x)
if type(x) == QuantizedTensorType:
return quantized_tensor_element_type(x)
if type(x) is not Type:
return element_type(type(x))
is_per_axis_quantized(x: Value | Placeholder | Type) -> Valueเป็นทางลัด สำหรับis_quantized(x) and quantization_dimension(x) is not Noneis_per_tensor_quantized(x: Value | Placeholder | Type) -> Valueเป็น ทางลัดสำหรับis_quantized(x) and quantization_dimension(x) is Noneis_promotable(x: Type, y: Type) -> boolจะตรวจสอบว่าสามารถเลื่อนระดับประเภทxเป็นประเภทyได้หรือไม่ เมื่อxและyเป็นQuantizedTensorElementTypeโปรโมชัน จะมีผลกับstorage_typeเท่านั้น โปรโมชันเวอร์ชันนี้ ปัจจุบันใช้ในบริบทของการคำนวณการลด (ดูรายละเอียดเพิ่มเติมใน RFC)
def is_promotable(x: Type, y: Type) -> Value:
is_same_type = (is_bool(x) and is_bool(y)) or
(is_integer(x) and is_integer(y)) or (is_float(x) and is_float(y)) or
(is_complex(x) and is_complex(y)) or
(is_quantized(x) and is_quantized(y) and expressed_type(x) = expressed_type(y))
if is_same_type == False:
return False
if is_integer(x) or is_float(x):
return bitwidth(x) <= bitwidth(y)
if is_complex(x):
return bitwidth(element_type(x)) <= bitwidth(element_type(y))
if is_quantized(x):
return bitwidth(storage_type(x)) <= bitwidth(storage_type(y))
return false
is_quantized(x: Value | Placeholder | Type) -> Valueเป็นทางลัดสำหรับis_quantized_tensor_element_type(x)is_type_name(x: Value | Placeholder | Type) -> Valueใช้ได้กับทุกประเภท เช่นis_float(x)จะแสดงผลtrueหากxเป็นFloatTypeหากxเป็นค่าหรือตัวยึดตำแหน่ง ฟังก์ชันนี้จะเป็นทางลัดสำหรับis_type_name(type(x))max_value(x: Type) -> Valueแสดงผลค่าสูงสุดของTensorElementTypeหากxไม่ใช่TensorElementTypeจะแสดงผลNonemin_value(x: Type) -> Valueแสดงผลค่าต่ำสุดที่เป็นไปได้ของTensorElementTypeหากxไม่ใช่TensorElementTypeจะแสดงผลNonemember_name(x: Value | Placeholder | Type) -> Anyใช้ได้กับคำจำกัดความของสมาชิก ทุกประเภทmember_nameเช่นtensor_element_type(x)จะแสดงผลส่วนTensorElementTypeของTensorTypeที่เกี่ยวข้อง หากxเป็นค่าหรือตัวยึดตำแหน่ง ฟังก์ชันนี้จะเป็นทางลัดสำหรับmember_name(type(x))หากxไม่ใช่ประเภทที่มีสมาชิกที่เหมาะสม หรือ ค่าหรือตัวยึดตำแหน่งของประเภทดังกล่าว จะแสดงผลNoneis_empty_algorithm(*args: Type)ตรวจสอบว่าได้ตั้งค่าฟิลด์อัลกอริทึมทั้งหมดของ DOT เป็นNoneหรือไม่ ซึ่งจำเป็นเนื่องจากอัลกอริทึมแบบจุดมีการกำหนดลักษณะการทำงานเริ่มต้น ไว้ในการติดตั้งใช้งาน ดังนั้นการระบุค่าเริ่มต้นจึงไม่ถูกต้อง
การสร้างค่า
operation_name(*xs: Value | Type) -> Valueใช้ได้กับการดำเนินการทั้งหมด เช่นadd(lhs, rhs)จะรับค่าเทนเซอร์ 2 ค่า ได้แก่lhsและrhsและ แสดงผลลัพธ์ของการประเมินการดำเนินการaddด้วยอินพุตเหล่านี้ สำหรับการดำเนินการบางอย่าง เช่นbroadcast_in_dimประเภทของเอาต์พุตคือ "รับน้ำหนัก" ซึ่งหมายถึงจำเป็นต่อการประเมินการดำเนินการ ในกรณีนี้ ฟังก์ชัน จะใช้ประเภทเหล่านี้เป็นอาร์กิวเมนต์
ฟังก์ชันในค่า
ตัวดำเนินการและฟังก์ชันทั้งหมดของ Python พร้อมใช้งาน เช่น ทั้งการ สมัครใช้บริการ และสัญกรณ์การแบ่งส่วน จาก Python พร้อมให้ใช้งานเพื่อจัดทำดัชนีในเทนเซอร์ เทนเซอร์ที่ผ่านการควอนไทซ์ และทูเพิล
to_destination_type(x: Value, destination_type: Type) -> Valueมีการกําหนดใน เทนเซอร์และแสดงผลค่าที่แปลงแล้วของxโดยอิงตามtype(x)และdestination_typeดังนี้
def to_destination_type(x: Value, destination_type: Type) -> Value:
if type(x) == destination_type:
return x
if is_quantized(destination_type):
if is_quantized(type(x)):
return quantize(x, destination_type)
assert is_float(type(x))
return quantize(x, destination_type)
if is_quantized(type(x)):
assert destination_type = expressed_type(type(x))
return dequantize(type(x))
return convert(x, destination_type)
มีการพูดคุยเบื้องต้นเกี่ยวกับการผสานรวมการดำเนินการของ convert, uniform_quantize และ
uniform_dequantize (#1576)
หลังจากผสานแล้ว เราไม่จำเป็นต้องใช้ฟังก์ชันข้างต้นและสามารถใช้ชื่อการดำเนินการ
สำหรับ convert แทนได้
is_nan(x: Value) -> Valueจะกำหนดในเทนเซอร์และแสดงผลtrueหากองค์ประกอบทั้งหมดของxเป็นNaNหรือfalseหากxไม่ใช่เทนเซอร์ จะแสดงผลNoneis_sorted(x: Value) -> Valueจะกำหนดในเทนเซอร์และแสดงผลtrueหาก องค์ประกอบของxจัดเรียงตามลำดับจากน้อยไปมากโดยอิงตามลำดับ พจนานุกรมจากน้อยไปมากของดัชนี หรือfalseในกรณีอื่นๆ หากxไม่ใช่เทนเซอร์ จะแสดงผลNoneis_unique(x: Value) -> Valueจะกำหนดในเทนเซอร์และแสดงผลtrueหากxไม่มีองค์ประกอบที่ซ้ำกัน หรือfalseในกรณีอื่นๆ หากxไม่ใช่เทนเซอร์ จะแสดงผลNonemember_name(x: Value) -> Anyจะกำหนดสำหรับคำจำกัดความของสมาชิกทั้งหมดmember_nameของค่าทั้งหมด เช่นreal_part(x)จะแสดงผลRealPartส่วนของComplexConstantที่เกี่ยวข้อง หากxไม่ใช่ค่าที่มีสมาชิกที่เหมาะสม จะแสดงผลNonesame(x: Value) -> Valueจะกำหนดไว้ในเทนเซอร์และแสดงผลtrueหากองค์ประกอบของxทั้งหมดเท่ากัน หรือแสดงผลfalseหากไม่เป็นเช่นนั้น หากเทนเซอร์ไม่มีองค์ประกอบ จะถือว่า "เท่ากันทั้งหมด" นั่นคือฟังก์ชันจะแสดงผลtrueหากxไม่ใช่เทนเซอร์ ฟังก์ชันจะแสดงผลNonesplit(x: Value, num_results: Value, axis: Value) -> Valueมีการกำหนดใน เทนเซอร์และส่งคืนชิ้นส่วนnum_resultsของxตามแกนaxisหากxไม่ใช่เทนเซอร์หรือdim(x, axis) % num_results != 0จะแสดงผลNoneis_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ไม่ใช่ประเภทเทนเซอร์ ประเภทเทนเซอร์ที่ผ่านการวัดปริมาณ หรือค่า หรือตัวยึดตำแหน่งของประเภทใดประเภทหนึ่งเหล่านี้ จะแสดงผลNonerank(x: Value | Placeholder | Type) -> Valueเป็นทางลัดสำหรับsize(shape(x))shape(x: Value | Placeholder | Type) -> Valueมีคำจำกัดความในส่วน "ฟังก์ชัน ในประเภท" ผ่านmember_namesize(x: Value | Placeholder | Type) -> Valueเป็นทางลัดสำหรับreduce(lambda x, y: x * y, shape(x))
การคำนวณการหาปริมาณ
def baseline_element_type(x: Value | Placeholder | Type) -> Typeเป็น ทางลัดสำหรับelement_type(baseline_type(x))baseline_typeจะกำหนดไว้ในประเภทเทนเซอร์และประเภทเทนเซอร์ที่แปลงเป็นจำนวนเต็ม และ แปลงเป็น "พื้นฐาน" กล่าวคือ ประเภทที่มีรูปร่างเดียวกันแต่มี พารามิเตอร์การแปลงเป็นจำนวนเต็มของประเภทองค์ประกอบที่รีเซ็ตเป็นค่าเริ่มต้น ซึ่งใช้เป็นเคล็ดลับที่มีประโยชน์ในการเปรียบเทียบทั้งประเภทเทนเซอร์และเทนเซอร์ที่แปลงเป็นจำนวนเต็ม อย่างสม่ำเสมอ ซึ่งจำเป็นต้องใช้บ่อยครั้ง สำหรับประเภทที่วัดปริมาณได้ การดำเนินการนี้จะช่วยให้เปรียบเทียบประเภทต่างๆ ได้โดยไม่สนใจพารามิเตอร์การวัดปริมาณ กล่าวคือshape,storage_type,expressed_type,storage_min,storage_maxและquantization_dimension(สำหรับประเภทที่วัดปริมาณต่อแกน) ต้องตรงกันทั้งหมด แต่scalesและzero pointsอาจแตกต่างกัน
def baseline_type(x: Value | Placeholder | Type) -> Type:
if type(x) == TensorType:
return x
if type(x) == QuantizedTensorType:
element_type = quantized_tensor_element_type(x)
baseline_element_type = QuantizedTensorElementType(
storage_type = storage_type(element_type),
storage_min = storage_min(element_type),
storage_max = storage_max(element_type),
expressed_type = expressed_type(element_type),
quantization_dimension = quantization_dimension(element_type),
scales = [constant(1.0, expressed_type(element_type))] * dim(x, quantization_dimension(element_type)),
zero_points = [constant(0, storage_type(element_type))] * dim(x, quantization_dimension(element_type)))
return QuantizedTensorType(shape(x), baseline_element_type)
if type(x) is not Type:
return baseline_element_type(type(x))
dequantizeมีการกำหนดในประเภทเทนเซอร์ที่กำหนดปริมาณและเปลี่ยนให้เป็น ประเภทเทนเซอร์แบบจุดลอย ซึ่งจะเกิดขึ้นผ่านการแปลงองค์ประกอบที่กำหนดปริมาณ ซึ่งแสดงค่าจำนวนเต็มของประเภทพื้นที่เก็บข้อมูลเป็นค่าทศนิยมที่สอดคล้องกันของประเภทที่แสดงโดยใช้จุดศูนย์และสเกล ที่เชื่อมโยงกับประเภทองค์ประกอบที่กำหนดปริมาณ
def compute_zero_points(quantized_type, result_type):
if is_per_tensor_quantized(quantized_type):
return broadcast_in_dim(constant(zero_point(quantized_type), storage_type(quantized_type)), [], result_type)
if is_per_axis_quantized(quantized_type):
for i in index_space(result_type):
d = quantization_dimension(quantized_type)
zero_points[i] = zero_points(quantized_type)[i[d]]
return zero_points
def compute_scales(quantized_type, result_type):
if is_per_tensor_quantized(quantized_type):
return broadcast_in_dim(constant(scale(quantized_type), expressed_type(quantized_type)), [],
type(result_type))
if is_per_axis_quantized(quantized_type):
for i in index_space(result_type):
d = quantization_dimension(quantized_type)
scales[i] = scales(quantized_type)[i[d]]
return scales
def dequantize(x: Value) -> Value:
assert is_quantized(x)
x_storage = bitcast_convert(x, storage_type(x))
x_storage_sub = x_storage - compute_zero_points(type(x), type(x_storage))
x_expressed_sub = convert(x_storage_sub, expressed_type(x))
return x_expressed_sub * compute_scales(type(x), type(x_expressed_sub))
quantizeมีการกำหนดในประเภทเทนเซอร์แบบจุดลอยและเปลี่ยนให้เป็นประเภทเทนเซอร์ที่ผ่านการวัดปริมาณ ซึ่งจะเกิดขึ้นผ่านการแปลงค่าทศนิยม ของประเภทที่แสดงเป็นค่าจำนวนเต็มที่สอดคล้องกับประเภทพื้นที่เก็บข้อมูล โดยใช้จุดศูนย์และสเกลที่เชื่อมโยงกับประเภทองค์ประกอบที่วัดปริมาณ
def quantize(x: Value, result_type: Type) -> Value:
assert is_float(x) and is_quantized(result_type)
zero_points = compute_zero_points(result_type, TensorType(shape(x), storage_type(result_type)))
converted_zero_points = convert(zero_points, expressed_type(result_type))
converted_min = convert(storage_min(result_type), expressed_type(result_type))
converted_max = convert(storage_max(result_type), expressed_type(result_type))
x_scaled = x / compute_scales(result_type, type(x))
x_scaled_add_zp = x_scaled + converted_zero_points
x_clamped = clamp(converted_min, x_scaled_add_zp, converted_max)
x_rounded = round_nearest_even(x_clamped)
return convert(x_rounded, result_type)
dequantize_op_quantizeใช้เพื่อระบุการคำนวณแบบทีละองค์ประกอบใน เทนเซอร์ที่กำหนดปริมาณ ซึ่งจะยกเลิกการลดขนาด กล่าวคือ เปลี่ยนองค์ประกอบที่ลดขนาดแล้วให้เป็นประเภทที่แสดง จากนั้นจึงดำเนินการ แล้วจึงลดขนาด กล่าวคือ เปลี่ยนผลลัพธ์กลับไปเป็นประเภทการจัดเก็บ ปัจจุบันฟังก์ชันนี้ใช้ได้กับการวัดปริมาณต่อเทนเซอร์เท่านั้น การกำหนดปริมาณต่อแกนอยู่ระหว่างการพัฒนา (#1574)
def dequantize_op_quantize(op, *inputs_and_output_type):
inputs = inputs_and_output_type[:-1]
output_type = inputs_and_output_type[-1]
float_inputs = map(dequantize, inputs)
float_result = op(*float_inputs)
return quantize(float_result, output_type)
def dequantize_batch_norm_grad_or_training_quantize(op, *inputs_and_output_types):
inputs = inputs_and_output_type[:-3]
float_inputs = map(dequantize, inputs)
float_results = op(*float_inputs)
return map(quantize, float_results, inputs_and_output_type[-3:])
def dequantize_compare(lhs, rhs, comparison_direction):
float_lhs = dequantize(lhs)
float_rhs = dequantize(rhs)
return compare(float_lhs, float_rhs, comparison_direction, FLOAT)
def dequantize_select_quantize(pred, on_true, on_false, output_type):
float_on_true = dequantize(on_true)
float_on_false = dequantize(on_false)
float_result = select(pred, float_on_true, float_on_false)
return quantize(float_result, output_type)
hybrid_dequantize_then_opใช้เพื่อระบุการหาปริมาณเฉพาะน้ำหนักสำหรับ การดำเนินการแบบไฮบริดที่ยอมรับ lhs ในประเภททศนิยมและ rhs ในประเภทที่หาปริมาณแล้ว โดยจะ ยกเลิกการหาปริมาณอินพุตที่หาปริมาณแล้วให้เป็นประเภทที่แสดง และทำการคำนวณ ในรูปแบบทศนิยม ประเภทองค์ประกอบของเทนเซอร์ lhs แบบลอยและประเภทที่แสดงของเทนเซอร์ rhs ที่ควอนไทซ์ ควรเหมือนกัน
def hybrid_dequantize_then_op(op, lhs, rhs):
assert(is_float(lhs) and is_quantized(rhs) and element_type(lhs) == expressed_type(rhs))
return op(lhs, dequantize(rhs))
การคำนวณกริด
cross_partition(replica_groups: Value) -> Valueดูส่วน "cross_replica" ด้านบนcross_replica(replica_groups: Value) -> Valueดูส่วน "cross_replica" ด้านบนcross_replica_and_partition(replica_groups: Value) -> Valueดูส่วน "cross_replica_and_partition" ด้านบนflattened_ids(replica_groups: Value) -> Valueดูส่วน "flattened_ids" ด้านบน
พลวัต
ค่า StableHLO อาจมีขนาดมิติข้อมูลแบบไดนามิก เช่น tensor<?xi64>
อย่างไรก็ตาม ค่า StableHLO จะมีจำนวนมิติข้อมูลแบบไดนามิกไม่ได้ (ไดนามิกแบบไม่มีอันดับ เช่น tensor<*xi64>) ตัวถูกดำเนินการและผลลัพธ์สามารถใช้ขนาดมิติข้อมูลแบบไดนามิกได้ แม้ว่าจะมีข้อจำกัดเกี่ยวกับขนาดก็ตาม ข้อจำกัดจะได้รับการ
ยืนยันแบบคงที่หากเป็นไปได้ ไม่เช่นนั้นจะเลื่อนไปรันไทม์ และ
การไม่ตรงกันจะส่งผลให้เกิดลักษณะการทำงานที่ไม่ได้กำหนดไว้ โปรดดูตัวอย่างด้านล่าง
รูปร่างไม่ตรงกันสำหรับการดำเนินการแบบอิงตามองค์ประกอบแบบเอกภาค
ลองพิจารณาโปรแกรมตัวอย่างต่อไปนี้
func.func @foo(%arg0: tensor<?xf64>) {
%0 = stablehlo.abs %arg0 : (tensor<?xf64>) -> tensor<2xf64>
return
}
โปรแกรมดังกล่าวเป็นโปรแกรมที่ผิดปกติ เนื่องจากโดยทั่วไปแล้วเรามักจะทราบรูปร่างของ
ผลลัพธ์ แต่ไม่ทราบรูปร่างของอินพุต อย่างไรก็ตาม นี่เป็นโปรแกรม StableHLO ที่ถูกต้อง ตรวจสอบการทำงานของ abs แบบคงที่ในโปรแกรมนี้ไม่ได้ เนื่องจากไม่ทราบรูปร่างที่แน่นอนของตัวถูกดำเนินการ อย่างไรก็ตาม รูปร่าง
เข้ากันได้อย่างแน่นอน และตรวจสอบได้แบบคงที่ ? อาจกลายเป็น 2
ขณะรันไทม์ และจะไม่มีปัญหาใดๆ อย่างไรก็ตาม ? อาจ
กลายเป็นจำนวนเต็มอื่นๆ ด้วย ซึ่งในกรณีนี้จะไม่มีการกำหนดลักษณะการทำงาน
โปรดทราบว่าหากขนาดมิติข้อมูลเป็นแบบไดนามิกในผลลัพธ์ จะต้องไม่มีลักษณะการทำงานที่ไม่ได้กำหนดไว้ แน่นอนว่าไม่มีขนาด "ที่คาดไว้" จึงไม่มีการ ไม่ตรงกัน
รูปร่างไม่ตรงกันสำหรับการดำเนินการแบบไบนารีแบบทีละองค์ประกอบ
ลองพิจารณาโปรแกรมตัวอย่างต่อไปนี้
func.func @foo(%arg0: tensor<?xf64>, %arg1: tensor<?xf64>) {
%0 = stablehlo.add %arg0, %arg0 : (tensor<?xf64>, tensor<?xf64>) -> tensor<?xf64>
return
}
สำหรับการดำเนินการแบบไบนารีแบบทีละองค์ประกอบ รูปร่างของอินพุตและ ผลลัพธ์ต้องตรงกันที่รันไทม์ ในเวลาคอมไพล์ มิติข้อมูลแบบคงที่ต้องเท่ากัน มิฉะนั้นก็เพียงแค่ต้องเข้ากันได้ หากมิติข้อมูลใดๆเป็นแบบไดนามิกในอินพุต อาจเกิดลักษณะการทำงานที่ไม่ได้กำหนดไว้ ในเวลาเรียกใช้ เนื่องจากขนาดแบบไดนามิกอาจไม่ตรงกับขนาดที่เกี่ยวข้อง ในตัวถูกดำเนินการอื่น (ไม่ว่าจะเป็นแบบคงที่หรือแบบไดนามิก) หากอินพุตทั้งหมดเป็นแบบ คงที่ ไม่ว่าผลลัพธ์จะเป็นแบบไดนามิกหรือไม่ก็ไม่สำคัญ ระบบจะตรวจสอบมิติข้อมูลที่ทราบแบบคงที่แบบคงที่ และมิติข้อมูลแบบไดนามิกจะไม่ กำหนดข้อจำกัดใดๆ
รูปร่างไม่ตรงกันสำหรับ Op ที่ใช้รูปร่างเอาต์พุตเป็นตัวถูกดำเนินการ
ลองพิจารณาโปรแกรมตัวอย่างต่อไปนี้
func.func @foo(%arg0: tensor<2xi32>) {
%0 = stablehlo.dynamic_iota %arg0, dim = 0 : (tensor<2xi32>) -> tensor<3x4xi64>
return
}
ค่าในตัวถูกดำเนินการของรูปร่างที่รันไทม์ต้องตรงกับรูปร่างของผลลัพธ์
มิฉะนั้นลักษณะการทำงานจะไม่ได้รับการกำหนด กล่าวคือ ที่รันไทม์ %arg0 ต้องมีค่าเป็น dense<[3, 4]> : tensor<2xi32> หากตัวถูกดำเนินการของรูปร่างเป็นค่าคงที่ จะตรวจสอบได้แบบคงที่ หากรูปร่างผลลัพธ์เป็นแบบไดนามิกทั้งหมด จะไม่มีการไม่ตรงกัน