ข้อกำหนดของ StableHLO

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)
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 รวม
  • ประเภททศนิยมอาจเป็นประเภทใดประเภทหนึ่งต่อไปนี้
  • ประเภทเชิงซ้อนแสดงค่าเชิงซ้อนที่มีส่วนจริง และส่วนจินตภาพของประเภทองค์ประกอบเดียวกัน ประเภทที่ซับซ้อนที่รองรับคือ 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, []) = true
    • has_shape(tensor_literal: List, shape: List) = size(tensor_literal) = shape[0] and has_shape(tensor_literal..., shape[1:])
    • หรือ false
QuantizedTensorConstant ::= QuantizedTensorLiteral ':' QuantizedTensorType
QuantizedTensorLiteral  ::= 'dense' '<' (DenseLiteral | ElementLiteral) '>'

ค่าคงที่ของเทนเซอร์ที่แปลงเป็นจำนวนเต็มแสดงค่าเทนเซอร์ที่แปลงเป็นจำนวนเต็มโดยใช้สัญกรณ์เดียวกับค่าคงที่ของเทนเซอร์ โดยมีองค์ประกอบที่ระบุเป็นค่าคงที่ของประเภทพื้นที่เก็บข้อมูล ค่าคงที่ของเทนเซอร์ที่แปลงเป็นจำนวนเต็มมีข้อจำกัดต่อไปนี้

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

อักษรสตริงประกอบด้วยไบต์ที่ระบุโดยใช้อักขระ ASCII และ ลำดับการหลีก โดยไม่ขึ้นอยู่กับการเข้ารหัส ดังนั้นการตีความไบต์เหล่านี้ จึงขึ้นอยู่กับการใช้งาน อักษรสตริงมีประเภท string

การดำเนินการ

แบบสัมบูรณ์

ความหมาย

ดำเนินการ abs แบบทีละองค์ประกอบในเทนเซอร์ operand และสร้างเทนเซอร์ result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ

  • สำหรับจำนวนเต็มที่มีเครื่องหมาย: โมดูลัสจำนวนเต็ม
  • สำหรับ 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)
  • หากการดำเนินการใช้เทนเซอร์ที่กำหนดปริมาณ ให้ทำดังนี้
    • (C2) is_quantized(lhs) and is_quantized(rhs) and is_quantized(result)
    • (C3) storage_type(lhs) = storage_type(rhs) = storage_type(result)
    • (C4) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
    • (C5) (is_per_axis_quantized(lhs) or is_per_axis_quantized(rhs)) = is_per_axis_quantized(result)
    • (C6) ถ้า is_per_axis_quantized(lhs) ให้ quantization_dimension(lhs) = quantization_dimension(result)
    • (C7) ถ้า is_per_axis_quantized(rhs) ให้ quantization_dimension(rhs) = quantization_dimension(result)

ตัวอย่าง

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

 ตัวอย่างเพิ่มเติม

after_all

ความหมาย

ตรวจสอบว่าการดำเนินการที่สร้าง inputs จะดำเนินการก่อนการดำเนินการใดๆ ที่ขึ้นอยู่กับ result การดำเนินการนี้ไม่มีผลใดๆ มีไว้เพื่อสร้างการอ้างอิงข้อมูลจาก result ไปยัง inputs เท่านั้น

อินพุต

ป้ายกำกับ ชื่อ ประเภท
(I1) inputs จำนวนตัวแปรของ token

เอาต์พุต

ชื่อ ประเภท
result token

ตัวอย่าง

// %input0: !stablehlo.token
// %input1: !stablehlo.token
%result = "stablehlo.after_all"(%input0, %input1) : (!stablehlo.token, !stablehlo.token) -> !stablehlo.token

 ตัวอย่างเพิ่มเติม

all_gather

ความหมาย

ภายในกลุ่มกระบวนการแต่ละกลุ่มในตารางกระบวนการ StableHLO จะต่อค่า ของเทนเซอร์ operands จากแต่ละกระบวนการตาม all_gather_dim และสร้างเทนเซอร์ results

การดำเนินการจะแยกตารางกระบวนการ StableHLO ออกเป็น process_groups ซึ่งกำหนดไว้ดังนี้

  • cross_replica(replica_groups) หาก channel_id <= 0 and use_global_device_ids = false
  • cross_replica_and_partition(replica_groups) หาก channel_id > 0 and use_global_device_ids = false
  • flattened_ids(replica_groups) หาก channel_id > 0 and use_global_device_ids = true

หลังจากนั้น ให้ทำดังนี้ภายในแต่ละ process_group

  • operands...@receiver = [operand@sender for sender in process_group] สำหรับreceiverทั้งหมดใน process_group
  • results...@process = concatenate(operands...@process, all_gather_dim) สำหรับprocessทั้งหมดใน process_group

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operands เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ (C1), (C6)
(I2) all_gather_dim ค่าคงที่ของประเภท si64 (C1), (C6)
(I3) replica_groups ค่าคงที่เทนเซอร์ 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_replica
    • num_replicas หากใช้ cross_replica_and_partition
    • num_processes หากใช้ flattened_ids
  • (C4) 0 <= replica_groups < size(replica_groups)
  • (C5) ถ้า use_global_device_ids = true ให้ channel_id > 0
  • (C6) type(results...) = type(operands...) ยกเว้น
    • dim(results..., all_gather_dim) = dim(operands..., all_gather_dim) * dim(process_groups, 1)

ตัวอย่าง

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

 ตัวอย่างเพิ่มเติม

all_reduce

ความหมาย

ภายในกลุ่มกระบวนการแต่ละกลุ่มในตารางกระบวนการ StableHLO จะใช้ฟังก์ชันการลด computation กับค่าของเทนเซอร์ operands จากแต่ละกระบวนการ และสร้างเทนเซอร์ results

การดำเนินการจะแยกตารางกระบวนการ StableHLO ออกเป็น process_groups ซึ่งกำหนดไว้ดังนี้

  • cross_replica(replica_groups) หาก channel_id <= 0 and use_global_device_ids = false
  • cross_replica_and_partition(replica_groups) หาก channel_id > 0 and use_global_device_ids = false
  • flattened_ids(replica_groups) หาก channel_id > 0 and use_global_device_ids = true

หลังจากนั้น ให้ทำดังนี้ภายในแต่ละ process_group

  • results...@process[result_index] = exec(schedule) สำหรับต้นไม้แบบไบนารีบางต้น schedule โดยที่
    • exec(node) = computation(exec(node.left), exec(node.right))
    • exec(leaf) = leaf.value
  • schedule เป็นไบนารีทรีที่กำหนดการใช้งานซึ่งการข้ามแบบอินออร์เดอร์ คือ to_destination_type(operands...@process_group...[result_index], type(func_inputs(computation)[0]))

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operands เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ (C5), (C6)
(I2) replica_groups จำนวนตัวแปรของค่าคงที่เทนเซอร์ 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_replica
    • num_replicas หากใช้ cross_replica_and_partition
    • num_processes หากใช้ flattened_ids
  • (C3) 0 <= replica_groups < size(replica_groups)
  • (C4) ถ้า use_global_device_ids = true ให้ channel_id > 0
  • (C5) computation มีประเภท (tensor<E>, tensor<E>) -> (tensor<E>) โดยที่ is_promotable(element_type(operand), E)
  • (C6) shape(results...) = shape(operands...)
  • (C7) element_type(results...) = E

ตัวอย่าง

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

 ตัวอย่างเพิ่มเติม

all_to_all

ความหมาย

all_to_all

ในแต่ละกลุ่มกระบวนการในตารางกระบวนการ StableHLO จะแยกค่าของเทนเซอร์ operands ตาม split_dimension ออกเป็นส่วนๆ กระจายส่วนที่แยกแล้วระหว่างกระบวนการต่างๆ ต่อส่วนที่กระจายแล้วตาม concat_dimension และสร้างเทนเซอร์ results การดำเนินการจะแยกตารางกระบวนการ StableHLO ออกเป็น process_groups ซึ่งกำหนดไว้ดังนี้

  • cross_replica(replica_groups) หาก channel_id <= 0
  • cross_partition(replica_groups) หาก channel_id > 0

หลังจากนั้น ให้ทำดังนี้ภายในแต่ละ process_group

  • split_parts...@sender = split(operands...@sender, split_count, split_dimension) สำหรับsenderทั้งหมดในprocess_group
  • scattered_parts...@receiver = [split_parts...@sender[receiver_index] for sender in process_group] ที่ซึ่ง receiver_index = process_group.index(receiver)
  • results...@process = concatenate(scattered_parts...@process, concat_dimension)

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operands เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ (C1-C3), (C9)
(I2) split_dimension ค่าคงที่ของประเภท si64 (C1), (C2), (C9)
(I3) concat_dimension ค่าคงที่ของประเภท si64 (C3), (C9)
(I4) split_count ค่าคงที่ของประเภท si64 (C2), (C4), (C8), (C9)
(I5) replica_groups ค่าคงที่เทนเซอร์ 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_replica
    • num_partitions หากใช้ cross_partition
  • (C7) 0 <= replica_groups < size(replica_groups)
  • (C8) dim(replica_groups, 1) = split_count
  • (C9) type(results...) = type(operands...) ยกเว้นในกรณีที่ split_dimension != concat_dimension
    • dim(results..., split_dimension) = dim(operands..., split_dimension) / split_count
    • dim(results..., concat_dimension) = dim(operands..., concat_dimension) * split_count

ตัวอย่าง

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

 ตัวอย่างเพิ่มเติม

และ

ความหมาย

ดำเนินการ AND แบบทีละองค์ประกอบของเทนเซอร์ 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 + 1
    • dim(result, i) = dim(operand, i) สำหรับ 0 <= i < R ทุกคน
    • dim(result, R) * num_bits(E') = num_bits(E)
    • หาก num_bits(E') > num_bits(E)
    • rank(result) = R - 1
    • dim(result, i) = dim(operand, i) สำหรับ 0 <= i < R ทุกคน
    • dim(operand, R - 1) * num_bits(E) = num_bits(E')
  • (C2) หาก is_complex(operand) or is_complex(result) ให้ is_complex(operand) and is_complex(result)

ตัวอย่าง

// %operand: 0x0123456789ABCDEF
%result = "stablehlo.bitcast_convert"(%operand) : (tensor<f64>) -> tensor<4xf16>
// %result: [0xCDEF, 0x89AB, 0x4567, 0x0123] // little-endian representation

 ตัวอย่างเพิ่มเติม

broadcast_in_dim

ความหมาย

broadcast_in_dim

ขยายมิติข้อมูลและ/หรืออันดับของเทนเซอร์อินพุตโดยการทำซ้ำข้อมูล ในเทนเซอร์ operand และสร้างเทนเซอร์ result ในรูปแบบที่เป็นทางการมากขึ้น result[result_index] = operand[operand_index] โดยที่สำหรับ d ทั้งหมดใน axes(operand):

  • operand_index[d] = 0 หาก dim(operand, d) = 1
  • operand_index[d] = result_index[broadcast_dimensions[d]] หรือไม่เช่นนั้น

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand 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 <= 0
  • cross_partition(replica_groups) หาก channel_id > 0

หลังจากนั้น result@process จะได้รับจาก

  • operand@process_groups[i, 0] หากมี i ที่ทำให้กระบวนการอยู่ใน process_groups[i]
  • broadcast_in_dim(constant(is_quantized(result) ? quantize(0, element_type(result)) : 0, element_type(result)), [], type(result)) มิฉะนั้น

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand 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_replica
    • num_partitions หากใช้ cross_partition
  • (C3) type(result) = type(operand)

ตัวอย่าง

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

collective_permute

ความหมาย

ภายในกลุ่มกระบวนการแต่ละกลุ่มในตารางกระบวนการ StableHLO จะส่งค่าของเทนเซอร์ operand จากกระบวนการต้นทางไปยังกระบวนการเป้าหมาย และสร้างเทนเซอร์ result

การดำเนินการจะแยกตารางกระบวนการ StableHLO ออกเป็น process_groups ซึ่งกำหนดไว้ดังนี้

  • cross_replica(source_target_pairs) หาก channel_id <= 0
  • cross_partition(source_target_pairs) หาก channel_id > 0

หลังจากนั้น result@process จะได้รับจาก

  • operand@process_groups[i, 0] หากมี i ที่ process_groups[i, 1] = process
  • broadcast_in_dim(constant(is_quantized(result) ? quantize(0, element_type(result)) : 0, element_type(result)), [], type(result)) มิฉะนั้น

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand 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_replica
    • num_partitions หากใช้ cross_partition
  • (C5) type(result) = type(operand)

ตัวอย่าง

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

 ตัวอย่างเพิ่มเติม

เปรียบเทียบ

ความหมาย

ทําการเปรียบเทียบแบบทีละองค์ประกอบของเทนเซอร์ lhs และ rhs ตาม comparison_direction และ compare_type และสร้างเทนเซอร์ result

ค่าของ comparison_direction และ compare_type มีความหมายดังนี้

สำหรับประเภทองค์ประกอบบูลีนและจำนวนเต็ม ให้ทำดังนี้

  • EQ: lhs = rhs
  • NE: lhs != rhs
  • GE: lhs >= rhs
  • GT: lhs > rhs
  • LE: lhs <= rhs
  • LT: lhs < rhs

สำหรับประเภทองค์ประกอบทศนิยมที่มี compare_type = FLOAT ตัวดำเนินการจะใช้การดำเนินการ IEEE-754 ต่อไปนี้

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

สำหรับประเภทองค์ประกอบทศนิยมที่มี compare_type = TOTALORDER ตัวดำเนินการจะใช้การผสมผสานระหว่างการดำเนินการ totalOrder และ compareQuietEqual จาก IEEE-754

สำหรับประเภทองค์ประกอบที่ซับซ้อน ระบบจะทำการเปรียบเทียบตามพจนานุกรมของคู่ (real, imag) โดยใช้ comparison_direction และ compare_type ที่ระบุ การกำหนดลำดับให้กับจำนวนเชิงซ้อนเกี่ยวข้องกับความหมายที่น่าประหลาดใจ ดังนั้นในอนาคตเราจึงวางแผนที่จะยกเลิกการรองรับจำนวนเชิงซ้อน เมื่อ comparison_direction เป็น GE, GT, LE หรือ LT (#560)

สำหรับประเภทที่เล็กลง ให้ดำเนินการ dequantize_compare(lhs, rhs, comparison_direction)

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) lhs 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] โดยที่

  1. id = d0 + ... + dk-1 + kd
  2. d มีค่าเท่ากับ 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_strides
  • lhs_window = slice(padded_lhs, lhs_window_start, lhs_window_start + lhs_window_dimensions, lhs_window_dilations)
  • reversed_lhs_window = reverse(lhs_window, [input_spatial_dimensions[dim] for dim in range(size(window_reversal)) if window_reversal[dim] = true]) ดูเหมือนว่าไม่มีการใช้ฟีเจอร์นี้ ดังนั้นในอนาคตเราจึงวางแผนที่จะนำฟีเจอร์นี้ออก (#1181)
  • dot_product = dot_general(reversed_lhs_window, rhs, lhs_batching_dimensions=[], lhs_contracting_dimensions=input_spatial_dimensions + [input_feature_dimension], rhs_batching_dimensions=[], rhs_contracting_dimensions=kernel_spatial_dimensions + [kernel_input_feature_dimension])

หาก feature_group_count > 1

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

หาก batch_group_count > 1

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

สำหรับประเภทที่กำหนดปริมาณ ให้ดำเนินการ dequantize_op_quantize( lambda lhs, rhs: convolution(lhs, rhs, window_strides, padding, lhs_dilation, rhs_dilation, window_reversal, input_batch_dimension, input_feature_dimension, input_spatial_dimensions, kernel_input_feature_dimension, kernel_output_feature_dimension, kernel_spatial_dimensions, output_batch_dimension, output_feature_dimension, output_spatial_dimensions, feature_group_count, batch_group_count, precision_config), lhs, rhs, type(result))

สำหรับประเภทที่เล็กลงแบบไฮบริด ให้ดำเนินการ hybrid_dequantize_then_op( lambda lhs, rhs: convolution(lhs, rhs, window_strides, padding, lhs_dilation, rhs_dilation, window_reversal, input_batch_dimension, input_feature_dimension, input_spatial_dimensions, kernel_input_feature_dimension, kernel_output_feature_dimension, kernel_spatial_dimensions, output_batch_dimension, output_feature_dimension, output_spatial_dimensions, feature_group_count, batch_group_count, precision_config), lhs, rhs)

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) lhs 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_dimension
    • dim(rhs, kernel_output_feature_dimension) หาก result_dim = output_feature_dimension
    • num_windows หรือในกรณีต่อไปนี้
    • output_spatial_dimensions[spatial_dim] = result_dim
    • lhs_dim = input_spatial_dimensions[spatial_dim]
    • rhs_dim = kernel_spatial_dimensions[spatial_dim]
    • dilated_input_shape[lhs_dim] = dim(lhs, lhs_dim) = 0 ? 0 : (dim(lhs, lhs_dim) - 1) * lhs_dilation[spatial_dim] + 1
    • padded_input_shape[lhs_dim] = padding[spatial_dim, 0] + dilated_input_shape[lhs_dim] + padding[spatial_dim, 1]
    • dilated_window_shape[lhs_dim] = dim(rhs, rhs_dim) = 0 ? 0 : (dim(rhs, rhs_dim) - 1) * rhs_dilation[spatial_dim] + 1
    • is_empty_window[lhs_dim] = padded_input_shape[lhs_dim] = 0 || dilated_window_shape[lhs_dim] > padded_input_shape[lhs_dim]
    • num_windows = is_empty_window[lhs_dim] ? 0 : floor((padded_input_shape[lhs_dim] - dilated_window_shape[lhs_dim]) / window_strides[spatial_dim]) + 1
  • (C26) rank(result) = N
  • หากการดำเนินการใช้เทนเซอร์ที่ไม่ได้ทำ Quantize ให้ทำดังนี้
    • (C27) element_type(lhs) = element_type(rhs) = element_type(result)
  • หากการดำเนินการใช้เทนเซอร์ที่กำหนดปริมาณ ให้ทำดังนี้
    • (C28) is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
    • (C29) หาก is_per_axis_quantized(rhs) ให้ quantization_dimension(rhs) = kernel_output_feature_dimension
    • (C30) หาก is_per_axis_quantized(result) ให้ quantization_dimension(result) = output_feature_dimension
    • หาก is_quantized(lhs)
    • (C31) storage_type(lhs) = storage_type(rhs)
    • (C32) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
    • (C33) หาก is_per_tensor_quantized(rhs) ให้ is_per_tensor_quantized(result)
    • หาก !is_quantized(lhs)
    • (C34) element_type(lhs) = expressed_type(rhs) = element_type(result)

ตัวอย่าง

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

 ตัวอย่างเพิ่มเติม

โคไซน์

ความหมาย

ดำเนินการโคไซน์แบบทีละองค์ประกอบในเทนเซอร์ operand และสร้างเทนเซอร์ result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ

  • สำหรับ 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 ฯลฯ) สำหรับอัลกอริทึมที่ไม่มีการแยกส่วน ค่าเหล่านี้ ควรตั้งเป็น 1
  • allow_imprecise_accumulation เพื่อระบุว่าอนุญาตให้สะสมในความแม่นยำต่ำกว่า สำหรับบางขั้นตอนหรือไม่ (เช่น CUBLASLT_MATMUL_DESC_FAST_ACCUM)

ตัวอย่างแอตทริบิวต์ DotAlgorithm

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


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


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

การติดตั้งใช้งานจะเป็นตัวกำหนดว่ารองรับการผสมผสานใดบ้าง โดยทั่วไปแล้ว เราไม่รับประกันว่า StableHLO จะรองรับอัลกอริทึมแต่ละรายการในตัวเร่งแต่ละประเภท หากอัลกอริทึมที่ระบุไม่ได้รับการรองรับ ควรแสดงข้อผิดพลาดแทนที่จะเปลี่ยนไปใช้อัลกอริทึมอื่น การยืนยัน StableHLO จะเป็นการยืนยันอย่างเต็มความสามารถ เพื่อป้องกันอัลกอริทึมที่ไม่ทราบว่ารองรับในฮาร์ดแวร์ใดๆ

ดูค่าอัลกอริทึมที่รองรับบางค่าได้ที่ xla_data.proto > Algorithm ตั๋ว #2483 บันทึกแผนการสร้างเอกสารส่วนกลางเกี่ยวกับอัลกอริทึมที่แบ็กเอนด์รองรับ

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) lhs 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)
  • หากการดำเนินการใช้เทนเซอร์ที่ผ่านการวัดปริมาณ ให้ทำดังนี้
    • (C14) is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
    • (C15) zero_points(rhs) = 0
    • (C16) หาก is_per_axis_quantized(rhs) แสดงว่า quantization_dimension(rhs) ไม่อยู่ใน rhs_contracting_dimensions
    • หาก is_quantized(lhs)
    • (C17) storage_type(lhs) = storage_type(rhs)
    • (C18) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
    • (C19) ถ้า is_per_tensor_quantized(rhs) แล้ว is_per_tensor_quantized(result)
    • หาก !is_quantized(lhs)
    • (C20) element_type(lhs) = expressed_type(rhs) = element_type(result)
  • หาก !is_empty_algorithm(lhs_precision_type, rhs_precision_type, accumulation_type, lhs_component_count, rhs_component_count, num_primitive_operations allow_imprecise_accumulation)
    • (C21) precision_config... = DEFAULT
    • (C22) 0 < lhs_component_count
    • (C23) 0 < rhs_component_count
    • (C24) 0 < num_primitive_operations

ตัวอย่าง

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

 ตัวอย่างเพิ่มเติม

dynamic_broadcast_in_dim

ความหมาย

การดำเนินการนี้มีฟังก์ชันการทำงานเหมือนกับ broadcast_in_dim 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_dimension
    • dim(rhs, kernel_output_feature_dimension) หาก result_dim = output_feature_dimension
    • num_windows หรือในกรณีต่อไปนี้
    • output_spatial_dimensions[spatial_dim] = result_dim
    • lhs_dim = input_spatial_dimensions[spatial_dim]
    • rhs_dim = kernel_spatial_dimensions[spatial_dim]
    • dilated_input_shape[lhs_dim] = dim(lhs, lhs_dim) = 0 ? 0 : (dim(lhs, lhs_dim) - 1) * lhs_dilation[spatial_dim] + 1
    • padded_input_shape[lhs_dim] = padding[spatial_dim, 0] + dilated_input_shape[lhs_dim] + padding[spatial_dim, 1]
    • dilated_window_shape[lhs_dim] = dim(rhs, rhs_dim) = 0 ? 0 : (dim(rhs, rhs_dim) - 1) * rhs_dilation[spatial_dim] + 1
    • is_empty_window[lhs_dim] = padded_input_shape[lhs_dim] = 0 || dilated_window_shape[lhs_dim] > padded_input_shape[lhs_dim]
    • num_windows = is_empty_window[lhs_dim] ? 0 : floor((padded_input_shape[lhs_dim] - dilated_window_shape[lhs_dim]) / window_strides[spatial_dim]) + 1
  • (C26) rank(result) = N
  • หากการดำเนินการใช้เทนเซอร์ที่ไม่ได้ทำ Quantize ให้ทำดังนี้
    • (C27) element_type(lhs) = element_type(rhs) = element_type(result)
  • หากการดำเนินการใช้เทนเซอร์ที่กำหนดปริมาณ ให้ทำดังนี้
    • (C28) is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
    • (C29) หาก is_per_axis_quantized(rhs) ให้ quantization_dimension(rhs) = kernel_output_feature_dimension
    • (C30) หาก is_per_axis_quantized(result) ให้ quantization_dimension(result) = output_feature_dimension
    • หาก is_quantized(lhs)
    • (C31) storage_type(lhs) = storage_type(rhs)
    • (C32) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
    • (C33) หาก is_per_tensor_quantized(rhs) ให้ is_per_tensor_quantized(result)
    • หาก !is_quantized(lhs)
    • (C34) element_type(lhs) = expressed_type(rhs) = element_type(result)

ตัวอย่าง

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

 ตัวอย่างเพิ่มเติม

dynamic_gather

ความหมาย

การดำเนินการนี้มีฟังก์ชันเหมือนกับ 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 = RFFT dim(result, -1) = dim(operand, -1) = 0 ? 0 : dim(operand, -1) / 2 + 1
    • หาก fft_type = IRFFT dim(operand, -1) = dim(result, -1) = 0 ? 0 : dim(result, -1) / 2 + 1

ตัวอย่าง

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

ชั้น

ความหมาย

ดำเนินการฟังก์ชันพื้นของเทนเซอร์ operand แบบทีละองค์ประกอบและสร้างเทนเซอร์ result ใช้การดำเนินการ roundToIntegralTowardNegative จากข้อกำหนด IEEE-754 สำหรับประเภทที่กำหนดปริมาณ ให้ดำเนินการ dequantize_op_quantize(floor, operand, type(result))

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ (C1)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ (C1)

ข้อจำกัด

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

ตัวอย่าง

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

 ตัวอย่างเพิ่มเติม

รวบรวม

ความหมาย

รวบรวมชิ้นจากเทนเซอร์ operand จากออฟเซ็ตที่ระบุใน start_indices และสร้างเทนเซอร์ result

แผนภาพต่อไปนี้แสดงวิธีที่องค์ประกอบใน result แมปกับองค์ประกอบใน operand โดยใช้ตัวอย่างที่ชัดเจน แผนภาพจะเลือกตัวอย่างresult ดัชนี 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_dims
  • operand_index = full_start_index + full_batching_index + full_offset_index

หาก indices_are_sorted เป็น true การติดตั้งใช้งานจะถือว่า start_indices เรียงตาม start_index_map มิฉะนั้นลักษณะการทำงานจะไม่ได้กำหนดไว้ กล่าวอย่างเป็นทางการมากขึ้นคือ สำหรับ i1 < i2 ทั้งหมดจาก indices(result) full_start_index(i1) <= full_start_index(i2)

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand 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] if result_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_replica
    • num_partitions หากใช้ cross_partition
  • (C5) channel_type มีคำจำกัดความดังนี้
    • DEVICE_TO_HOST หาก is_host_transfer = true
    • DEVICE_TO_DEVICE หรือไม่เช่นนั้น

ตัวอย่าง

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

 ตัวอย่างเพิ่มเติม

ลด

ความหมาย

ใช้ฟังก์ชันการลด 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] โดยแทรก : ที่ dimensions
  • input_slices_converted = to_destination_type(input_slices..., type(func_inputs(body)[:len(func_inputs(body))//2])...)
  • init_values_converted = to_destination_type(init_values..., type(func_inputs(body)[len(func_inputs(body))//2:])...)
  • reduce(input_slices_converted) = exec(schedule) สำหรับต้นไม้แบบไบนารีบางต้น schedule โดยที่
    • exec(node) = body(exec(node.left), exec(node.right))
    • exec(leaf) = leaf.value
  • schedule เป็นไบนารีทรีแบบสมบูรณ์ที่กำหนดการใช้งานซึ่งการทราเวอร์แซลแบบอินออร์เดอร์ ประกอบด้วย
    • input_slices_converted...[index] สำหรับ index ทั้งหมดใน index_space(input_slices_converted) ตามลำดับแบบพจนานุกรมจากน้อยไปมาก ของ index
    • แทรกด้วยจำนวน init_values_converted ที่กำหนดไว้ในการใช้งานในตำแหน่งที่กำหนดไว้ในการใช้งาน

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) inputs เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ (C1-C4), (C6), (C7)
(I2) init_values จำนวนเทนเซอร์ 0 มิติหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์แบบแปรผัน (C2), (C3)
(I3) dimensions ค่าคงที่เทนเซอร์ 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

ความหมาย

reduce_scatter

ภายในกลุ่มกระบวนการแต่ละกลุ่มในตารางกระบวนการ StableHLO จะทำการลด โดยใช้ computations กับค่าของเทนเซอร์ operand จากแต่ละกระบวนการ แยกผลการลดตาม scatter_dimension ออกเป็นส่วนๆ แล้วกระจาย ส่วนที่แยกแล้วระหว่างกระบวนการเพื่อสร้าง result

การดำเนินการจะแยกตารางกระบวนการ StableHLO ออกเป็น process_groups ซึ่งกำหนดไว้ดังนี้

  • cross_replica(replica_groups) หาก channel_id <= 0 and use_global_device_ids = false
  • cross_replica_and_partition(replica_groups) หาก channel_id > 0 and use_global_device_ids = false
  • flattened_ids(replica_groups) หาก channel_id > 0 and use_global_device_ids = true

หลังจากนั้น ให้ทำดังนี้ภายในแต่ละ process_group

  • reduced_value = all_reduce(operand, replica_groups, channel_id, use_global_device_ids, computation)
  • parts@sender = split(reduced_value@sender, dim(process_groups, 1), scatter_dimension)
  • result@receiver = parts@sender[receiver_index] สำหรับ sender ทั้งหมดใน process_group โดยที่ receiver_index = process_group.index(receiver)

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand 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_replica
    • num_replicas หากใช้ cross_replica_and_partition
    • num_processes หากใช้ flattened_ids
  • (C5) 0 <= replica_groups < size(replica_groups)
  • (C6) ถ้า use_global_device_ids = true ให้ channel_id > 0
  • (C7) computation มีประเภท (tensor<E>, tensor<E>) -> (tensor<E>) โดยที่ is_promotable(element_type(operand), E)
  • (C8) shape(result) = shape(operand) ยกเว้น
    • dim(result, scatter_dimension) = dim(operand, scatter_dimension) / dim(process_groups, 1)
  • (C9) element_type(result) = E

ตัวอย่าง

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

 ตัวอย่างเพิ่มเติม

reduce_window

ความหมาย

ใช้ฟังก์ชันการลด body กับหน้าต่างของ inputs และ init_values และสร้าง results

แผนภาพต่อไปนี้แสดงวิธีคำนวณองค์ประกอบใน results... จาก inputs... โดยใช้ตัวอย่างที่เป็นรูปธรรม

reduce_window

ในรูปแบบที่เป็นทางการมากขึ้น results...[result_index] = reduce(windows, init_values, axes(inputs...), body) (ดู reduce) โดยที่

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

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) inputs เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ (C1-C4), (C6), (C8), (C10), (C12), (C13), (C15)
(I2) init_values จำนวนเทนเซอร์ 0 มิติหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์แบบแปรผัน (C1), (C13)
(I3) window_dimensions ค่าคงที่เทนเซอร์ 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 + 1
    • padded_input_shape = padding[:, 0] + dilated_input_shape + padding[:, 1]
    • dilated_window_shape = (window_dimensions - 1) * window_dilations + 1
    • is_empty_window = padded_input_shape = 0 || dilated_window_shape > padded_input_shape
    • num_windows = is_empty_window ? 0 : floor((padded_input_shape - dilated_window_shape) / window_strides) + 1
  • (C16) element_type(results[i]) = Ei สำหรับ i ทั้งหมดใน [0,N)

ตัวอย่าง

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

 ตัวอย่างเพิ่มเติม

เศษที่เหลือ

ความหมาย

ดำเนินการหาเศษแบบทีละองค์ประกอบของเทนเซอร์ตัวตั้ง lhs และเทนเซอร์ตัวหาร rhs และ สร้างเทนเซอร์ result

กล่าวอย่างเป็นทางการมากขึ้นคือ เครื่องหมายของผลลัพธ์จะมาจากตัวตั้ง และ ค่าสัมบูรณ์ของผลลัพธ์จะน้อยกว่าค่าสัมบูรณ์ของตัวหารเสมอ ส่วนที่เหลือคำนวณได้เป็น lhs - d * rhs โดย d มีค่าดังนี้

  • สำหรับจำนวนเต็ม: stablehlo.divide(lhs, rhs)
  • สำหรับโฟลต: division(lhs, rhs) จาก IEEE-754 ที่มีแอตทริบิวต์การปัดเศษ roundTowardZero
  • สำหรับจำนวนเชิงซ้อน: TBD (#997)
  • สำหรับประเภทที่กำหนดปริมาณ
    • dequantize_op_quantize(remainder, lhs, rhs, type(result))

สำหรับประเภทองค์ประกอบทศนิยม การดำเนินการนี้จะตรงกันข้ามกับการดำเนินการ 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 ใน dimensions
  • operand_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_FRY
    • 2 หรือ 3 หาก rng_algorithm = PHILOX

ตัวอย่าง

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

round_nearest_afz

ความหมาย

ดำเนินการปัดเศษแบบทีละองค์ประกอบเข้าหาจำนวนเต็มที่ใกล้ที่สุด โดยปัดเศษค่าที่อยู่กึ่งกลางออก จากศูนย์ในเทนเซอร์ operand และสร้างเทนเซอร์ result ใช้การดำเนินการ roundToIntegralTiesToAway จากข้อกำหนด IEEE-754 สำหรับ ประเภทที่กำหนดปริมาณ จะดำเนินการ dequantize_op_quantize(round_nearest_afz, operand, type(result))

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ (C1)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ (C1)

ข้อจำกัด

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

ตัวอย่าง

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

 ตัวอย่างเพิ่มเติม

round_nearest_even

ความหมาย

ดำเนินการปัดเศษตามองค์ประกอบไปยังจำนวนเต็มที่ใกล้ที่สุด โดยจะปัดเศษ ไปยังจำนวนเต็มคู่ในเทนเซอร์ operand และสร้างเทนเซอร์ result ใช้การดำเนินการ roundToIntegralTiesToEven จากข้อกำหนด IEEE-754 สำหรับประเภทที่กำหนดปริมาณ ให้ดำเนินการ dequantize_op_quantize(round_nearest_even, operand, type(result))

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ (C1)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result เทนเซอร์ประเภทจุดลอยตัวหรือเทนเซอร์ที่กำหนดปริมาณต่อเทนเซอร์ (C1)

ข้อจำกัด

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

ตัวอย่าง

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

 ตัวอย่างเพิ่มเติม

rsqrt

ความหมาย

ดำเนินการรากที่สองส่วนกลับแบบทีละองค์ประกอบในเทนเซอร์ operand และสร้างเทนเซอร์ result โดยจะทำสิ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับประเภทองค์ประกอบ

  • สำหรับ 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] if d_input = scatter_dims_to_operand_dims[d_start]
    • full_start_index[d_input] = 0 หรือไม่เช่นนั้น
  • สำหรับ d_input ใน axes(inputs[0])
    • full_batching_index[d_input] = update_scatter_index[d_start - (d_start < index_vector_dim ? 0 : 1)] หาก d_input = input_batching_dims[i_batching] และ d_start = scatter_indices_batching_dims[i_batching]
    • full_batching_index[d_input] = 0 หรือไม่เช่นนั้น
  • update_window_index = update_index[update_window_dims...]
  • full_window_index = [wi0, ..., 0, ..., wiN] โดยที่ wi คือองค์ประกอบแต่ละรายการใน update_window_index และ 0 จะแทรกที่ดัชนีตั้งแต่ inserted_window_dims ถึง input_batching_dims
  • result_index = full_start_index + full_batching_index + full_window_index

โดย results = exec(schedule, inputs) มีรายละเอียดดังนี้

  • schedule คือการเรียงสับเปลี่ยนที่กำหนดการใช้งานของ index_space(updates[0])
  • exec([update_index, ...], results) = exec([...], updated_results) โดยมีเงื่อนไขดังนี้
    • หาก result_index อยู่ในขอบเขตสำหรับ shape(results...)
    • updates_converted = to_destination_type( updates...[update_index], type(func_inputs(update_computation) [len(func_inputs(update_computation))//2:])... )
    • updated_values = update_computation(results...[result_index], updates_converted)
    • updated_results คือสำเนาของ results ที่มี results...[result_index] ตั้งค่าเป็น updated_values...
    • ไม่เช่นนั้น
    • updated_results = results
  • exec([], results) = results

หาก indices_are_sorted เป็น true การติดตั้งใช้งานจะถือว่า scatter_indices ได้รับการจัดเรียงตาม scatter_dims_to_operand_dims มิฉะนั้นลักษณะการทำงานจะไม่ได้กำหนดไว้ กล่าวอย่างเป็นทางการมากขึ้นคือ สำหรับทุก i1 < i2 จาก indices(result), full_start_index(i1) <= full_start_index(i2)

หาก unique_indices เป็น true การติดตั้งใช้งานจะถือว่าดัชนี result_index ทั้งหมดที่กระจายอยู่มีค่าที่ไม่ซ้ำกัน หาก unique_indices เป็น true แต่ดัชนีที่กระจายอยู่ไม่ซ้ำกัน ลักษณะการทำงานจะเป็น ไม่กำหนด

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) inputs เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ (C1), (C2), (C4-C6), (C11), (C13), (C18), (C21), (C23-C24)
(I2) scatter_indices Tensor ประเภทจำนวนเต็ม (C4), (C15), (C19), (C22)
(I3) updates เทนเซอร์จำนวนตัวแปรหรือเทนเซอร์ที่หาปริมาณต่อเทนเซอร์ (C3-C6), (C8)
(I4) update_window_dims ค่าคงที่เทนเซอร์ 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_dim
    • update_window_dim_sizes <= shape(inputs[0]) ยกเว้นว่า จะไม่รวมขนาดมิติข้อมูลใน inputs[0] ที่สอดคล้องกับ inserted_window_dims และ input_batching_dims
    • combine วาง update_scatter_dim_sizes ที่แกนซึ่งสอดคล้องกับ update_scatter_dims และ update_window_dim_sizes ที่แกนซึ่งสอดคล้อง กับ update_window_dims
  • (C5) 0 < size(inputs) = size(updates) = N
  • (C6) element_type(updates...) = element_type(inputs...)
  • (C7) is_unique(update_window_dims) and is_sorted(update_window_dims)
  • (C8) 0 <= update_window_dims < rank(updates[0])
  • (C9) is_unique(concatenate(inserted_window_dims, input_batching_dims))
  • (C10) is_sorted(inserted_window_dims)
  • (C11) 0 <= inserted_window_dims < rank(inputs[0])
  • (C12) is_sorted(input_batching_dims)
  • (C13) 0 <= input_batching_dims < rank(inputs[0]))
  • (C14) is_unique(scatter_indices_batching_dims)
  • (C15) 0 <= scatter_indices_batching_dims < rank(scatter_indices)
  • (C16) index_vector_dim not in scatter_indices_batching_dims
  • (C17) size(input_batching_dims) == size(scatter_indices_batching_dims)
  • (C18) dim(inputs[0], input_batching_dims...) = dim(scatter_indices, scatter_indices_batching_dims...)
  • (C19) size(scatter_dims_to_operand_dims) = index_vector_dim < rank(scatter_indices) ? dim(scatter_indices, index_vector_dim) : 1
  • (C20) is_unique(concatenate(scatter_dims_to_operand_dims, input_batching_dims))
  • (C21) 0 <= scatter_dims_to_operand_dims < rank(inputs[0])
  • (C22) 0 <= index_vector_dim <= rank(scatter_indices)
  • (C23) update_computation มีประเภท (tensor<E0>, ..., tensor<EN-1>, tensor<E0>, ..., tensor<EN-1>) -> (tensor<E0>, ..., tensor<EN-1>) โดยที่ is_promotable(element_type(inputs[i]), Ei)
  • (C24) shape(inputs...) = shape(results...)
  • (C25) element_type(results[i]) = Ei สำหรับiทั้งหมดใน[0,N)

ตัวอย่าง

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

 ตัวอย่างเพิ่มเติม

เลือก

ความหมาย

สร้างเทนเซอร์ result โดยเลือกแต่ละองค์ประกอบจากเทนเซอร์ on_true หรือ on_false ตามค่าขององค์ประกอบที่สอดคล้องกันของ pred ในรูปแบบที่เป็นทางการมากขึ้น result[result_index] = pred_element ? on_true[result_index] : on_false[result_index] โดยที่ pred_element = rank(pred) = 0 ? pred[] : pred[result_index] สำหรับประเภทที่กำหนดปริมาณ ให้ดำเนินการ dequantize_select_quantize(pred, on_true, on_false, type(result))

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) pred 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 โดยใช้ตัวอย่างที่เป็นรูปธรรม

select_and_scatter

ในรูปแบบที่เป็นทางการมากขึ้น

  • selected_values = reduce_window_without_init(...) โดยมีข้อมูลต่อไปนี้

    • inputs = [operand].
    • window_dimensions, window_strides และ padding ซึ่งใช้ตามที่เป็นอยู่
    • base_dilations = windows_dilations = 1
    • body มีคำจำกัดความดังนี้
    def body(arg0: tensor<E>, arg1: tensor<E>) -> tensor<E>:
      return select(arg0, arg1) ? arg0 : arg1;
    

    โดย E = element_type(operand) และ reduce_window_without_init ทำงาน เหมือนกับ reduce_window ทุกประการ ยกเว้นว่า schedule ของ reduce ที่อยู่เบื้องหลัง reduce (ดู reduce) จะไม่รวมค่าเริ่มต้น ปัจจุบันยังไม่ได้ระบุว่าจะเกิดอะไรขึ้นหากหน้าต่างที่เกี่ยวข้องไม่มีค่า (#731)

  • result[result_index] = reduce([source_values], [init_value], [0], scatter) โดยที่

    • source_values = [source[source_index] for source_index in source_indices]
    • selected_index(source_index) = operand_index หาก selected_values[source_index] มีองค์ประกอบ operand จาก operand_index
    • source_indices = [source_index for source_index in indices(source) if selected_index(source_index) = result_index]

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand 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_shape
    • num_windows = is_empty_window ? 0 : floor((padded_operand_shape - window_dimensions) / window_strides) + 1
  • (C3) element_type(init_value) = element_type(operand)
  • (C4) size(window_dimensions) = rank(operand)
  • (C5) 0 < window_dimensions
  • (C6) size(window_strides) = rank(operand)
  • (C7) 0 < window_strides
  • (C8) shape(padding) = [rank(operand), 2]
  • (C9) select มีประเภท (tensor<E>, tensor<E>) -> tensor<i1> โดยที่ E = element_type(operand)
  • (C10) scatter มีประเภท (tensor<E>, tensor<E>) -> tensor<E> โดยที่ is_promotable(element_type(operand), E)
  • (C11) shape(operand) = shape(result)
  • (C12) element_type(result) = E

ตัวอย่าง

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

 ตัวอย่างเพิ่มเติม

ส่ง

ความหมาย

ส่ง inputs ไปยังช่อง channel_id จากนั้นระบบจะส่งอินพุตไปยังอุปกรณ์อื่นๆ ตามลำดับที่ระบุโดย source_target_pairs การดำเนินการจะสร้างresultโทเค็น

หาก is_host_transfer เป็น true การดำเนินการจะโอนข้อมูลไปยัง โฮสต์ ไม่เช่นนั้น ระบบจะโอนข้อมูลไปยังอุปกรณ์อื่นตามค่าของ source_target_pairs โดยแฟล็กนี้จะทำซ้ำข้อมูลที่ระบุไว้ใน channel_type ดังนั้นในอนาคตเราจึงวางแผนที่จะเก็บไว้เพียงรายการเดียว (#666) หาก is_host_transfer = false และ source_target_pairs เป็น None หรือว่างเปล่า ระบบจะถือว่าเป็นการทำงานที่ไม่ได้กำหนดไว้

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) inputs จำนวนเทนเซอร์หรือเทนเซอร์ที่ควอนไทซ์ได้
(I2) token token
(I3) channel_id ค่าคงที่ของประเภท si64
(I4) channel_type 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_replica
    • num_partitions หากใช้ cross_partition
  • (C5) channel_type มีคำจำกัดความดังนี้
    • DEVICE_TO_HOST หาก is_host_transfer = true
    • DEVICE_TO_DEVICE หรือไม่เช่นนั้น

ตัวอย่าง

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

 ตัวอย่างเพิ่มเติม

shift_left

ความหมาย

ดำเนินการเลื่อนบิตไปทางซ้ายแบบทีละองค์ประกอบในเทนเซอร์ lhs ตามจำนวนบิต rhs และสร้างเทนเซอร์ result

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) lhs 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]) + dimension
  • result_slice = [ri0, ..., :, ..., riR-1] โดยที่ riN คือองค์ประกอบแต่ละรายการใน result_index และแทรก : ที่ adjusted_dimension
  • inputs_together = (inputs[0]..., ..., inputs[N-1]...)
  • results_together[result_slice] = sort(inputs_together[result_slice], comparator_together)
  • โดย sort จะจัดเรียงสไลซ์ 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: ดำเนินการกับทรานสโพสของ a
  • ADJOINT: ดำเนินการกับทรานสโพสผันสังยุคของ a

ระบบจะอ่านข้อมูลอินพุตจากสามเหลี่ยมด้านล่างของ a เท่านั้น หาก lower เป็น true หรือสามเหลี่ยมด้านบนของ a ระบบจะแสดงข้อมูลเอาต์พุตในสามเหลี่ยมเดียวกัน ค่าในสามเหลี่ยมอีกอันจะขึ้นอยู่กับการใช้งาน

หาก unit_diagonal เป็นจริง การติดตั้งใช้งานจะถือว่าองค์ประกอบแนวทแยง ของ a เท่ากับ 1 มิเช่นนั้น ลักษณะการทำงานจะไม่ได้กำหนดไว้

สำหรับประเภทที่กำหนดปริมาณ ให้ดำเนินการ dequantize_op_quantize(lambda x, y: triangular_solve(x, y, left_side, lower, unit_diagonal, transpose_a), a, b, type(result))

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) a เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ (C1-C3)
(I2) b เทนเซอร์ประเภทจำนวนเชิงซ้อนหรือจุดลอยตัว หรือเทนเซอร์ที่วัดปริมาณต่อเทนเซอร์ (C1-C4)
(I3) left_side ค่าคงที่ของประเภท i1 (C3)
(I4) lower ค่าคงที่ของประเภท i1
(I5) unit_diagonal ค่าคงที่ของประเภท i1
(I6) transpose_a 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, และ Convolution window_reversal (#1181)

เราสามารถนำการดำเนินการบางอย่างเหล่านี้ออกได้ง่ายๆ เนื่องจากสามารถแสดงได้โดยใช้การดำเนินการที่มีอยู่ (broadcast, create_token, cross-replica-sum, dot, unary_einsum) และจะนำออกหลังจากช่วงเวลาที่เข้ากันได้ที่มีอยู่ผ่านไป (6 เดือน) ส่วนอื่นๆ ยังอยู่ระหว่างการพิจารณาเพื่อนำออก (einsum, get_tuple_element, map, rng torch_index_select, tuple, complex การเปรียบเทียบ window_reversal) โดยเราจะนำการดำเนินการเหล่านี้ออกหรือเพิ่มลงในข้อกำหนดพร้อมการรองรับอย่างเต็มรูปแบบ ทั้งนี้ขึ้นอยู่กับความคิดเห็นจากชุมชน จนกว่าจะทราบอนาคตของฟีเจอร์เหล่านี้ เราจึงรับประกันความเข้ากันได้เพียง 6 เดือน

การลงมือปฏิบัติ

การดำเนินการตามลำดับ

โปรแกรม StableHLO จะดำเนินการโดยการระบุค่าอินพุตให้กับmainฟังก์ชัน และคำนวณค่าเอาต์พุต ค่าเอาต์พุตของฟังก์ชันจะคำนวณโดย การเรียกใช้กราฟของ Op ที่มีรากฐานมาจาก Op return ที่เกี่ยวข้อง

ลำดับการดำเนินการจะขึ้นอยู่กับการใช้งานตราบใดที่สอดคล้องกับ โฟลว์ของข้อมูล กล่าวคือ หากมีการดำเนินการกับ Op ก่อนที่จะมีการใช้งาน ใน StableHLO การดำเนินการที่มีผลข้างเคียงทั้งหมดจะใช้โทเค็น 1 รายการและสร้างโทเค็น 1 รายการ (สามารถมัลติเพล็กซ์โทเค็นหลายรายการเป็นโทเค็นเดียวผ่าน after_all) ดังนั้นลำดับการดำเนินการของผลข้างเคียงจึงสอดคล้องกับโฟลว์ของข้อมูลด้วย ตัวอย่างเช่น ในโปรแกรมด้านล่าง ลำดับการดำเนินการที่เป็นไปได้มี 2 แบบ ได้แก่ %0%1%2return และ %1%0%2return

func.func @main() -> tensor<f64> {
  %0 = stablehlo.constant dense<1.0> : tensor<f64>
  %1 = stablehlo.constant dense<2.0> : tensor<f64>
  %2 = stablehlo.add %0, %1 : tensor<f64>
  return %2 : tensor<f64>
}

กล่าวอย่างเป็นทางการมากขึ้น กระบวนการ StableHLO คือการรวมกันของ 1) โปรแกรม StableHLO, 2) สถานะการดำเนินการ (ยังไม่ได้ดำเนินการ ดำเนินการแล้ว) และ 3) ค่ากลางที่กระบวนการกำลังดำเนินการ กระบวนการนี้เริ่มต้นด้วยค่าอินพุตไปยังฟังก์ชัน main ดำเนินการผ่านกราฟของสถานะการดำเนินการอัปเดตการดำเนินการและค่ากลาง และสิ้นสุดด้วยค่าเอาต์พุต จะมีการกำหนดรูปแบบเพิ่มเติมในภายหลัง (#484)

การดำเนินการแบบคู่ขนาน

โปรแกรม StableHLO สามารถดำเนินการแบบคู่ขนาน จัดระเบียบเป็นตารางกระบวนการ 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 None

  • is_per_tensor_quantized(x: Value | Placeholder | Type) -> Value เป็น ทางลัดสำหรับ is_quantized(x) and quantization_dimension(x) is None

  • is_promotable(x: Type, y: Type) -> bool จะตรวจสอบว่าสามารถเลื่อนระดับประเภท x เป็นประเภท y ได้หรือไม่ เมื่อ x และ y เป็น QuantizedTensorElementType โปรโมชัน จะมีผลกับ storage_type เท่านั้น โปรโมชันเวอร์ชันนี้ ปัจจุบันใช้ในบริบทของการคำนวณการลด (ดูรายละเอียดเพิ่มเติมใน RFC)

def is_promotable(x: Type, y: Type) -> Value:
  is_same_type = (is_bool(x) and is_bool(y)) or
    (is_integer(x) and is_integer(y)) or (is_float(x) and is_float(y)) or
    (is_complex(x) and is_complex(y)) or
    (is_quantized(x) and is_quantized(y) and expressed_type(x) = expressed_type(y))

  if is_same_type == False:
    return False

  if is_integer(x) or is_float(x):
    return bitwidth(x) <= bitwidth(y)

  if is_complex(x):
    return bitwidth(element_type(x)) <= bitwidth(element_type(y))

  if is_quantized(x):
    return bitwidth(storage_type(x)) <= bitwidth(storage_type(y))

  return false
  • is_quantized(x: Value | Placeholder | Type) -> Value เป็นทางลัดสำหรับ is_quantized_tensor_element_type(x)

  • is_type_name(x: Value | Placeholder | Type) -> Value ใช้ได้กับทุกประเภท เช่น is_float(x) จะแสดงผล true หาก x เป็น FloatType หาก x เป็นค่าหรือตัวยึดตำแหน่ง ฟังก์ชันนี้จะเป็นทางลัดสำหรับ is_type_name(type(x))

  • max_value(x: Type) -> Value แสดงผลค่าสูงสุดของ TensorElementType หาก x ไม่ใช่ TensorElementType จะแสดงผล None

  • min_value(x: Type) -> Value แสดงผลค่าต่ำสุดที่เป็นไปได้ของ TensorElementType หาก x ไม่ใช่ TensorElementType จะแสดงผล None

  • member_name(x: Value | Placeholder | Type) -> Any ใช้ได้กับคำจำกัดความของสมาชิก ทุกประเภทmember_name เช่น tensor_element_type(x) จะแสดงผลส่วน TensorElementType ของ TensorType ที่เกี่ยวข้อง หาก x เป็นค่าหรือตัวยึดตำแหน่ง ฟังก์ชันนี้จะเป็นทางลัดสำหรับ member_name(type(x)) หาก x ไม่ใช่ประเภทที่มีสมาชิกที่เหมาะสม หรือ ค่าหรือตัวยึดตำแหน่งของประเภทดังกล่าว จะแสดงผล None

  • is_empty_algorithm(*args: Type) ตรวจสอบว่าได้ตั้งค่าฟิลด์อัลกอริทึมทั้งหมดของ 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 ไม่ใช่เทนเซอร์ จะแสดงผล None

  • is_sorted(x: Value) -> Value จะกำหนดในเทนเซอร์และแสดงผล true หาก องค์ประกอบของ x จัดเรียงตามลำดับจากน้อยไปมากโดยอิงตามลำดับ พจนานุกรมจากน้อยไปมากของดัชนี หรือ false ในกรณีอื่นๆ หาก x ไม่ใช่เทนเซอร์ จะแสดงผล None

  • is_unique(x: Value) -> Value จะกำหนดในเทนเซอร์และแสดงผล true หาก x ไม่มีองค์ประกอบที่ซ้ำกัน หรือ false ในกรณีอื่นๆ หาก x ไม่ใช่เทนเซอร์ จะแสดงผล None

  • member_name(x: Value) -> Any จะกำหนดสำหรับคำจำกัดความของสมาชิกทั้งหมด member_nameของค่าทั้งหมด เช่น real_part(x) จะแสดงผล RealPart ส่วนของ ComplexConstant ที่เกี่ยวข้อง หาก x ไม่ใช่ค่าที่มีสมาชิกที่เหมาะสม จะแสดงผล None

  • same(x: Value) -> Value จะกำหนดไว้ในเทนเซอร์และแสดงผล true หากองค์ประกอบของ x ทั้งหมดเท่ากัน หรือแสดงผล false หากไม่เป็นเช่นนั้น หากเทนเซอร์ไม่มีองค์ประกอบ จะถือว่า "เท่ากันทั้งหมด" นั่นคือฟังก์ชันจะแสดงผล true หาก x ไม่ใช่เทนเซอร์ ฟังก์ชันจะแสดงผล None

  • split(x: Value, num_results: Value, axis: Value) -> Value มีการกำหนดใน เทนเซอร์และส่งคืนชิ้นส่วน num_results ของ x ตามแกน axis หาก x ไม่ใช่เทนเซอร์หรือ dim(x, axis) % num_results != 0 จะแสดงผล None

  • is_defined_in_parent_scope(x: Value) -> Value จะกำหนดไว้ในสตริง และแสดงผล true หาก x เป็นชื่อของฟังก์ชันที่กำหนดไว้ในขอบเขตเดียวกัน กับฟังก์ชันหลักของตัวดำเนินการที่เกี่ยวข้อง

  • is_namespaced_op_name(x: Value) -> Value มีการกำหนดไว้ในสตริงและจะแสดงผล true หาก x เป็นชื่อการดำเนินการที่ถูกต้อง ซึ่งหมายความว่าชื่อการดำเนินการดังกล่าวเป็นไปตามนิพจน์ทั่วไปต่อไปนี้ [a-zA-Z][a-zA-Z0-9_]*([.][a-zA-Z0-9_$]+)+

การคำนวณรูปร่าง

  • axes(x: Value | Placeholder | Type) -> Value เป็นทางลัดสำหรับ range(rank(x))

  • dim(x: Value | Placeholder | Type, axis: Value) -> Value เป็นทางลัดสำหรับ shape(x)[axis]

  • dims(x: Value | Placeholder | Type, axes: List) -> List เป็นทางลัดสำหรับ list(map(lambda axis: dim(x, axis), axes))

  • index_space(x: Value | Placeholder | Type) -> Value จะกำหนดไว้ในเทนเซอร์ และแสดงดัชนี size(x) สำหรับ TensorType ที่เกี่ยวข้องซึ่งจัดเรียงตาม ลำดับพจนานุกรมจากน้อยไปมาก นั่นคือ [0, ..., 0], [0, ..., 1], ..., shape(x) - 1 หาก x ไม่ใช่ประเภทเทนเซอร์ ประเภทเทนเซอร์ที่ผ่านการวัดปริมาณ หรือค่า หรือตัวยึดตำแหน่งของประเภทใดประเภทหนึ่งเหล่านี้ จะแสดงผล None

  • rank(x: Value | Placeholder | Type) -> Value เป็นทางลัดสำหรับ size(shape(x))

  • shape(x: Value | Placeholder | Type) -> Value มีคำจำกัดความในส่วน "ฟังก์ชัน ในประเภท" ผ่าน member_name

  • size(x: Value | Placeholder | Type) -> Value เป็นทางลัดสำหรับ reduce(lambda x, y: x * y, shape(x))

การคำนวณการหาปริมาณ

  • def baseline_element_type(x: Value | Placeholder | Type) -> Type เป็น ทางลัดสำหรับ element_type(baseline_type(x))

  • baseline_type จะกำหนดไว้ในประเภทเทนเซอร์และประเภทเทนเซอร์ที่แปลงเป็นจำนวนเต็ม และ แปลงเป็น "พื้นฐาน" กล่าวคือ ประเภทที่มีรูปร่างเดียวกันแต่มี พารามิเตอร์การแปลงเป็นจำนวนเต็มของประเภทองค์ประกอบที่รีเซ็ตเป็นค่าเริ่มต้น ซึ่งใช้เป็นเคล็ดลับที่มีประโยชน์ในการเปรียบเทียบทั้งประเภทเทนเซอร์และเทนเซอร์ที่แปลงเป็นจำนวนเต็ม อย่างสม่ำเสมอ ซึ่งจำเป็นต้องใช้บ่อยครั้ง สำหรับประเภทที่วัดปริมาณได้ การดำเนินการนี้จะช่วยให้เปรียบเทียบประเภทต่างๆ ได้โดยไม่สนใจพารามิเตอร์การวัดปริมาณ กล่าวคือ shape, storage_type, expressed_type, storage_min, storage_max และ quantization_dimension (สำหรับประเภทที่วัดปริมาณต่อแกน) ต้องตรงกันทั้งหมด แต่ scales และ zero points อาจแตกต่างกัน

def baseline_type(x: Value | Placeholder | Type) -> Type:
  if type(x) == TensorType:
    return x
  if type(x) == QuantizedTensorType:
    element_type = quantized_tensor_element_type(x)
    baseline_element_type = QuantizedTensorElementType(
      storage_type = storage_type(element_type),
      storage_min = storage_min(element_type),
      storage_max = storage_max(element_type),
      expressed_type = expressed_type(element_type),
      quantization_dimension = quantization_dimension(element_type),
      scales = [constant(1.0, expressed_type(element_type))] * dim(x, quantization_dimension(element_type)),
      zero_points = [constant(0, storage_type(element_type))] * dim(x, quantization_dimension(element_type)))
    return QuantizedTensorType(shape(x), baseline_element_type)
  if type(x) is not Type:
    return baseline_element_type(type(x))
  • dequantize มีการกำหนดในประเภทเทนเซอร์ที่กำหนดปริมาณและเปลี่ยนให้เป็น ประเภทเทนเซอร์แบบจุดลอย ซึ่งจะเกิดขึ้นผ่านการแปลงองค์ประกอบที่กำหนดปริมาณ ซึ่งแสดงค่าจำนวนเต็มของประเภทพื้นที่เก็บข้อมูลเป็นค่าทศนิยมที่สอดคล้องกันของประเภทที่แสดงโดยใช้จุดศูนย์และสเกล ที่เชื่อมโยงกับประเภทองค์ประกอบที่กำหนดปริมาณ
def compute_zero_points(quantized_type, result_type):
  if is_per_tensor_quantized(quantized_type):
    return broadcast_in_dim(constant(zero_point(quantized_type), storage_type(quantized_type)), [], result_type)
  if is_per_axis_quantized(quantized_type):
    for i in index_space(result_type):
      d = quantization_dimension(quantized_type)
      zero_points[i] = zero_points(quantized_type)[i[d]]
    return zero_points

def compute_scales(quantized_type, result_type):
  if is_per_tensor_quantized(quantized_type):
    return broadcast_in_dim(constant(scale(quantized_type), expressed_type(quantized_type)), [],
            type(result_type))
  if is_per_axis_quantized(quantized_type):
    for i in index_space(result_type):
      d = quantization_dimension(quantized_type)
      scales[i] = scales(quantized_type)[i[d]]
    return scales

def dequantize(x: Value) -> Value:
  assert is_quantized(x)
  x_storage = bitcast_convert(x, storage_type(x))
  x_storage_sub = x_storage - compute_zero_points(type(x), type(x_storage))
  x_expressed_sub = convert(x_storage_sub, expressed_type(x))
  return x_expressed_sub * compute_scales(type(x), type(x_expressed_sub))
  • quantize มีการกำหนดในประเภทเทนเซอร์แบบจุดลอยและเปลี่ยนให้เป็นประเภทเทนเซอร์ที่ผ่านการวัดปริมาณ ซึ่งจะเกิดขึ้นผ่านการแปลงค่าทศนิยม ของประเภทที่แสดงเป็นค่าจำนวนเต็มที่สอดคล้องกับประเภทพื้นที่เก็บข้อมูล โดยใช้จุดศูนย์และสเกลที่เชื่อมโยงกับประเภทองค์ประกอบที่วัดปริมาณ
def quantize(x: Value, result_type: Type) -> Value:
  assert is_float(x) and is_quantized(result_type)
  zero_points = compute_zero_points(result_type, TensorType(shape(x), storage_type(result_type)))
  converted_zero_points = convert(zero_points, expressed_type(result_type))
  converted_min = convert(storage_min(result_type), expressed_type(result_type))
  converted_max = convert(storage_max(result_type), expressed_type(result_type))

  x_scaled = x / compute_scales(result_type, type(x))
  x_scaled_add_zp = x_scaled + converted_zero_points
  x_clamped = clamp(converted_min, x_scaled_add_zp, converted_max)
  x_rounded = round_nearest_even(x_clamped)
  return convert(x_rounded, result_type)
  • dequantize_op_quantize ใช้เพื่อระบุการคำนวณแบบทีละองค์ประกอบใน เทนเซอร์ที่กำหนดปริมาณ ซึ่งจะยกเลิกการลดขนาด กล่าวคือ เปลี่ยนองค์ประกอบที่ลดขนาดแล้วให้เป็นประเภทที่แสดง จากนั้นจึงดำเนินการ แล้วจึงลดขนาด กล่าวคือ เปลี่ยนผลลัพธ์กลับไปเป็นประเภทการจัดเก็บ ปัจจุบันฟังก์ชันนี้ใช้ได้กับการวัดปริมาณต่อเทนเซอร์เท่านั้น การกำหนดปริมาณต่อแกนอยู่ระหว่างการพัฒนา (#1574)
def dequantize_op_quantize(op, *inputs_and_output_type):
  inputs = inputs_and_output_type[:-1]
  output_type = inputs_and_output_type[-1]

  float_inputs = map(dequantize, inputs)
  float_result = op(*float_inputs)
  return quantize(float_result, output_type)

def dequantize_batch_norm_grad_or_training_quantize(op, *inputs_and_output_types):
  inputs = inputs_and_output_type[:-3]
  float_inputs = map(dequantize, inputs)
  float_results = op(*float_inputs)
  return map(quantize, float_results, inputs_and_output_type[-3:])

def dequantize_compare(lhs, rhs, comparison_direction):
  float_lhs = dequantize(lhs)
  float_rhs = dequantize(rhs)
  return compare(float_lhs, float_rhs, comparison_direction, FLOAT)

def dequantize_select_quantize(pred, on_true, on_false, output_type):
  float_on_true = dequantize(on_true)
  float_on_false = dequantize(on_false)
  float_result = select(pred, float_on_true, float_on_false)
  return quantize(float_result, output_type)
  • hybrid_dequantize_then_op ใช้เพื่อระบุการหาปริมาณเฉพาะน้ำหนักสำหรับ การดำเนินการแบบไฮบริดที่ยอมรับ lhs ในประเภททศนิยมและ rhs ในประเภทที่หาปริมาณแล้ว โดยจะ ยกเลิกการหาปริมาณอินพุตที่หาปริมาณแล้วให้เป็นประเภทที่แสดง และทำการคำนวณ ในรูปแบบทศนิยม ประเภทองค์ประกอบของเทนเซอร์ lhs แบบลอยและประเภทที่แสดงของเทนเซอร์ rhs ที่ควอนไทซ์ ควรเหมือนกัน
def hybrid_dequantize_then_op(op, lhs, rhs):
  assert(is_float(lhs) and is_quantized(rhs) and element_type(lhs) == expressed_type(rhs))
  return op(lhs, dequantize(rhs))

การคำนวณกริด

  • cross_partition(replica_groups: Value) -> Value ดูส่วน "cross_replica" ด้านบน

  • cross_replica(replica_groups: Value) -> Value ดูส่วน "cross_replica" ด้านบน

  • cross_replica_and_partition(replica_groups: Value) -> Value ดูส่วน "cross_replica_and_partition" ด้านบน

  • flattened_ids(replica_groups: Value) -> Value ดูส่วน "flattened_ids" ด้านบน

พลวัต

ค่า StableHLO อาจมีขนาดมิติข้อมูลแบบไดนามิก เช่น tensor<?xi64> อย่างไรก็ตาม ค่า StableHLO จะมีจำนวนมิติข้อมูลแบบไดนามิกไม่ได้ (ไดนามิกแบบไม่มีอันดับ เช่น tensor<*xi64>) ตัวถูกดำเนินการและผลลัพธ์สามารถใช้ขนาดมิติข้อมูลแบบไดนามิกได้ แม้ว่าจะมีข้อจำกัดเกี่ยวกับขนาดก็ตาม ข้อจำกัดจะได้รับการ ยืนยันแบบคงที่หากเป็นไปได้ ไม่เช่นนั้นจะเลื่อนไปรันไทม์ และ การไม่ตรงกันจะส่งผลให้เกิดลักษณะการทำงานที่ไม่ได้กำหนดไว้ โปรดดูตัวอย่างด้านล่าง

รูปร่างไม่ตรงกันสำหรับการดำเนินการแบบอิงตามองค์ประกอบแบบเอกภาค

ลองพิจารณาโปรแกรมตัวอย่างต่อไปนี้

func.func @foo(%arg0: tensor<?xf64>) {
  %0 = stablehlo.abs %arg0 : (tensor<?xf64>) -> tensor<2xf64>
  return
}

โปรแกรมดังกล่าวเป็นโปรแกรมที่ผิดปกติ เนื่องจากโดยทั่วไปแล้วเรามักจะทราบรูปร่างของ ผลลัพธ์ แต่ไม่ทราบรูปร่างของอินพุต อย่างไรก็ตาม นี่เป็นโปรแกรม StableHLO ที่ถูกต้อง ตรวจสอบการทำงานของ abs แบบคงที่ในโปรแกรมนี้ไม่ได้ เนื่องจากไม่ทราบรูปร่างที่แน่นอนของตัวถูกดำเนินการ อย่างไรก็ตาม รูปร่าง เข้ากันได้อย่างแน่นอน และตรวจสอบได้แบบคงที่ ? อาจกลายเป็น 2 ขณะรันไทม์ และจะไม่มีปัญหาใดๆ อย่างไรก็ตาม ? อาจ กลายเป็นจำนวนเต็มอื่นๆ ด้วย ซึ่งในกรณีนี้จะไม่มีการกำหนดลักษณะการทำงาน

โปรดทราบว่าหากขนาดมิติข้อมูลเป็นแบบไดนามิกในผลลัพธ์ จะต้องไม่มีลักษณะการทำงานที่ไม่ได้กำหนดไว้ แน่นอนว่าไม่มีขนาด "ที่คาดไว้" จึงไม่มีการ ไม่ตรงกัน

รูปร่างไม่ตรงกันสำหรับการดำเนินการแบบไบนารีแบบทีละองค์ประกอบ

ลองพิจารณาโปรแกรมตัวอย่างต่อไปนี้

func.func @foo(%arg0: tensor<?xf64>, %arg1: tensor<?xf64>) {
  %0 = stablehlo.add %arg0, %arg0 : (tensor<?xf64>, tensor<?xf64>) -> tensor<?xf64>
  return
}

สำหรับการดำเนินการแบบไบนารีแบบทีละองค์ประกอบ รูปร่างของอินพุตและ ผลลัพธ์ต้องตรงกันที่รันไทม์ ในเวลาคอมไพล์ มิติข้อมูลแบบคงที่ต้องเท่ากัน มิฉะนั้นก็เพียงแค่ต้องเข้ากันได้ หากมิติข้อมูลใดๆเป็นแบบไดนามิกในอินพุต อาจเกิดลักษณะการทำงานที่ไม่ได้กำหนดไว้ ในเวลาเรียกใช้ เนื่องจากขนาดแบบไดนามิกอาจไม่ตรงกับขนาดที่เกี่ยวข้อง ในตัวถูกดำเนินการอื่น (ไม่ว่าจะเป็นแบบคงที่หรือแบบไดนามิก) หากอินพุตทั้งหมดเป็นแบบ คงที่ ไม่ว่าผลลัพธ์จะเป็นแบบไดนามิกหรือไม่ก็ไม่สำคัญ ระบบจะตรวจสอบมิติข้อมูลที่ทราบแบบคงที่แบบคงที่ และมิติข้อมูลแบบไดนามิกจะไม่ กำหนดข้อจำกัดใดๆ

รูปร่างไม่ตรงกันสำหรับ 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> หากตัวถูกดำเนินการของรูปร่างเป็นค่าคงที่ จะตรวจสอบได้แบบคงที่ หากรูปร่างผลลัพธ์เป็นแบบไดนามิกทั้งหมด จะไม่มีการไม่ตรงกัน