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

StableHLO คือชุดการดำเนินการสำหรับการดำเนินการระดับสูง (HLO) ในโมเดลแมชชีนเลิร์นนิง 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
NonValueType ::= TensorElementType | QuantizedTensorElementType | FunctionType | StringType

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

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

ประเภท Tensor แสดงถึง Tensor หรืออาร์เรย์หลายมิติ โดยจะมีรูปร่างและประเภทองค์ประกอบ โดยรูปร่างจะแสดงขนาดมิติข้อมูลที่ไม่ใช่ค่าลบหรือไม่ทราบค่าตามลําดับจากน้อยไปมากของมิติข้อมูลที่เกี่ยวข้อง (หรือที่เรียกว่าแกน) ซึ่งระบุหมายเลขตั้งแต่ 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
  • (ค2) 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 จุด 1 จุดหรืออาจเป็น 0 จุดในประเภท Tensor ที่เล็กลงหรือไม่ จากผลการพูดคุยครั้งนี้ ข้อกําหนดเกี่ยวกับจุดศูนย์อาจมีการเปลี่ยนแปลงในอนาคต (#1405)

การพูดคุยอีกเรื่องหนึ่งที่กำลังดำเนินอยู่เกี่ยวข้องกับความหมายของ QuantizationStorageMin และ QuantizationStorageMax เพื่อพิจารณาว่าควรมีข้อจำกัดสำหรับค่าเหล่านี้และค่าของ Tensor ที่แปลงค่าเป็นจำนวนเต็มหรือไม่ (#1406)

สุดท้าย เราวางแผนที่จะสำรวจการนำเสนอรูปแบบที่ไม่รู้จักและจุดเริ่มต้น เช่นเดียวกับที่เราวางแผนที่จะสำรวจการนำเสนอขนาดมิติข้อมูลที่ไม่ทราบ (#1407)

ประเภท tensor ที่กำหนดแสดงถึง Tensor ที่มีองค์ประกอบที่เล็กลง tensor เหล่านี้เหมือนกับ tensor ปกติทุกประการ เว้นแต่องค์ประกอบของ Tensor จะมีประเภทองค์ประกอบที่เล็กลงแทนที่จะเป็นประเภทองค์ประกอบปกติ

ในเทนเซอร์ที่ผ่านการแปลงค่า การแปลงค่าอาจเป็นต่อเทนเซอร์ ซึ่งหมายความว่ามี scale และ zero_point 1 คู่สำหรับเทนเซอร์ทั้งรายการ หรืออาจเป็นต่อแกน ซึ่งหมายความว่ามี scales และ zero_points หลายคู่ โดย 1 คู่ต่อส่วนย่อยของมิติข้อมูล quantization_dimension ที่เฉพาะเจาะจง กล่าวอย่างเป็นทางการคือ ในเทนเซอร์ t ที่มีการปรับขนาดตามแกน จะมี dim(t, quantization_dimension) ส่วนของ quantization_dimension ดังนี้ t[:, ..., 0, ..., :], t[:, ..., 1, ..., :] เป็นต้น องค์ประกอบทั้งหมดในส่วนที่ i ใช้ scales[i] และ zero_points[i] เป็นพารามิเตอร์การปรับขนาด ประเภท Tensor ที่แปลงค่าเป็นจำนวนเต็มมีข้อจำกัดต่อไปนี้

  • สำหรับการปันส่วนต่อ Tensor ให้ทำดังนี้
    • ไม่มีข้อจำกัดเพิ่มเติม
  • สําหรับการแจกแจงต่อแกน ให้ทำดังนี้
    • (C12) quantization_dimension < rank(self)
    • (C13) dim(self, quantization_dimension) = size(scales)
TokenType ::= 'token'

ประเภทโทเค็นแสดงถึงโทเค็น ซึ่งเป็นค่าที่ไม่โปร่งใสที่การดำเนินการบางอย่างสร้างขึ้นและใช้ โทเค็นใช้เพื่อกำหนดลําดับการดําเนินการของการดำเนินการตามที่อธิบายไว้ในส่วนการดําเนินการ

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

ประเภททูเปิลแสดงถึงทูเปิล เช่น รายการที่หลากหลาย Tuples เป็นฟีเจอร์เดิมที่มีไว้เพื่อใช้งานร่วมกับ HLO เท่านั้น ใน HLO ระบบจะใช้ทูเพลเพื่อแสดงอินพุตและเอาต์พุตแบบผันแปร ใน StableHLO ระบบจะรองรับอินพุตและเอาต์พุตแบบผันแปรโดยค่าเริ่มต้น และการใช้ทูเพลตใน StableHLO มีไว้เพื่อแสดง HLO ABI อย่างครอบคลุมเท่านั้น เช่น T, tuple<T> และ tuple<tuple<T>> อาจแตกต่างกันอย่างมากโดยขึ้นอยู่กับการใช้งาน ในอนาคต เราวางแผนที่จะเปลี่ยนแปลง HLO ABI ซึ่งอาจทำให้เรานำประเภททูเปิลออกจาก StableHLO ได้ (#598)

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

ประเภทองค์ประกอบแสดงองค์ประกอบของประเภท Tensor ภาษาประเภทนี้ต่างจากภาษาโปรแกรมหลายภาษา ตรงไม่ใช่คลาสแรกใน 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 (หรือที่เรียกว่าการดำเนินการ) หมายถึงชุดการดำเนินการระดับสูงแบบปิดในโมเดลแมชชีนเลิร์นนิง ตามที่เราได้กล่าวไปข้างต้น ไวยากรณ์ของ 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 ฟังก์ชันไม่ใช่ค่าระดับบนสุด) และคุณลักษณะอินพุต (ระบุแบบคงที่เช่นกัน) ประเภทของอินพุตและเอาต์พุตที่ใช้และสร้างขึ้นโดยการดำเนินการจะขึ้นอยู่กับความสามารถในการจำ เช่น 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 จะมีแนวคิด "ภูมิภาค" ทั่วไปมากขึ้น ซึ่งอาจมี "บล็อก" ของการดำเนินการหลายรายการที่เชื่อมต่อกันผ่านการดำเนินการ Jump บล็อกเหล่านี้มีรหัสที่สอดคล้องกับUnusedเวอร์ชันที่ใช้งานจริงเพื่อให้แยกความแตกต่างกันได้ StableHLO ไม่มีการข้าม ดังนั้นระบบจึงไม่ได้ใช้ส่วนที่เกี่ยวข้องของไวยากรณ์ MLIR (แต่ยังคงอยู่)

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

แอตทริบิวต์อินพุตมีชื่อและค่าซึ่งเป็นหนึ่งในค่าคงที่ที่รองรับ ซึ่งเป็นวิธีหลักในการระบุข้อมูลเมตาแบบคงที่สำหรับองค์ประกอบโปรแกรม เช่น อ็อป concatenate ใช้แอตทริบิวต์ 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}] ')'

ลายเซ็นการดำเนินการประกอบด้วยประเภทของค่าอินพุตทั้งหมด (รายการประเภททางด้านซ้ายของ ->) และประเภทของค่าเอาต์พุตทั้งหมด (รายการประเภททางด้านขวาของ ->) ในทางเทคนิคแล้ว ประเภทอินพุตจะซ้ำซ้อนและประเภทเอาต์พุตก็ซ้ำซ้อนเกือบทุกครั้งด้วย (เนื่องจากสำหรับการดำเนินการ StableHLO ส่วนใหญ่ ประเภทเอาต์พุตจะอนุมานได้จากอินพุต) อย่างไรก็ตาม ลายเซ็น op ก็ตั้งใจเป็นส่วนหนึ่งของไวยากรณ์ 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'

ค่าคงที่จำนวนเต็มแสดงค่าจำนวนเต็มผ่านสตริงที่ใช้การเขียนเลขทศนิยมหรือเลขฐาน 16 ระบบไม่รองรับฐานอื่นๆ เช่น ฐาน 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))
  • (ค2) 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

ค่าคงที่ของ Tensor แสดงค่า Tensor โดยใช้ลิสต์ที่ซ้อนกันซึ่งระบุผ่านรูปแบบนิพจน์ NumPy เช่น dense<[[1, 2, 3], [4, 5, 6]]> : tensor<2x3xi32> represent a tensor value with the following mapping from indices to elements: {0, 0} => 1, {0, 1} => 2, {0, 2} => 3, {1, 0} => 4, {1, 1} => 5, {1, 2} => 6 ลำดับการจัดเก็บองค์ประกอบเหล่านี้ในหน่วยความจำจะกำหนดการใช้งาน ค่าคงที่ Tensor มีข้อจำกัดต่อไปนี้

  • (C1) has_syntax(tensor_literal, element_type(tensor_type)) โดยที่
    • has_syntax(element_literal: Syntax, element_type: Type) = is_wellformed(element_literal, type)
    • has_syntax(tensor_literal: List, element_type: Type) = has_syntax(tensor_literal..., element_type)
  • (C2) has_shape(tensor_literal, shape(tensor_type)) ซึ่งมีดังนี้
    • has_shape(element_literal: Syntax, []) = true
    • has_shape(tensor_literal: List, shape: List) = size(tensor_literal) = shape[0] and has_shape(tensor_literal..., shape[1:])
    • หรือไม่เช่นนั้น false
QuantizedTensorConstant ::= QuantizedTensorLiteral ':' QuantizedTensorType
QuantizedTensorLiteral  ::= 'dense' '<' (DenseLiteral | ElementLiteral) '>'

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

  • (C1) has_syntax(quantized_tensor_literal, storage_type(quantized_tensor_type))
  • (ค2) 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

ความหมาย

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

  • สำหรับจำนวนเต็มที่มีเครื่องหมาย: โมดูลัสจำนวนเต็ม
  • สำหรับตัวเลขทศนิยม: abs จาก IEEE-754
  • สำหรับจำนวนเชิงซ้อน: โมดูลัสเชิงซ้อน
  • สำหรับประเภทที่มีการแปลงเป็นจำนวนเต็ม: dequantize_op_quantize(abs, operand, type(result))

อินพุต

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

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result เทนเซอร์ของจำนวนเต็มที่มีเครื่องหมายหรือประเภททศนิยม หรือเทนเซอร์ที่ผ่านการแปลงเชิงปริมาณต่อเทนเซอร์ (C1-C2)

ข้อจำกัด

  • (C1) shape(result) = shape(operand)
  • (C2) baseline_element_type(result) มีคำจำกัดความดังนี้
    • complex_element_type(element_type(operand)) หาก is_complex(operand)
    • baseline_element_type(operand) ในกรณีอื่น

ตัวอย่าง

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

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

เพิ่ม

ความหมาย

ทำการบวก Tensor 2 ตัว lhs และ rhs และสร้าง Tensor result ทําสิ่งต่อไปนี้โดยขึ้นอยู่กับประเภทองค์ประกอบ

  • สําหรับบูลีน: OR เชิงตรรกะ
  • สําหรับจํานวนเต็ม: การเพิ่มจํานวนเต็ม
  • สำหรับตัวเลขทศนิยม: addition จาก IEEE-754
  • สำหรับจำนวนเชิงซ้อน: การบวกเชิงซ้อน
  • สำหรับประเภทที่มีการแปลงเป็นจำนวนเต็ม: dequantize_op_quantize(add, lhs, rhs, type(result))

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) lhs เทนเซอร์หรือเทนเซอร์ที่ผ่านการแปลงค่าเป็นจำนวนเต็ม (C1-C6)
(I2) rhs tensor หรือ tensor แบบเชิงปริมาณ (C1-C5), (C7)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result เทนเซอร์หรือเทนเซอร์ที่ผ่านการแปลงค่าเป็นจำนวนเต็ม (C1-C7)

ข้อจำกัด

  • หากการดำเนินการใช้เทนเซอร์ที่ไม่ผ่านการแปลงค่า ให้ทำดังนี้
    • (C1) type(lhs) = type(rhs) = type(result)
  • หากการดำเนินการใช้ Tensor ที่เล็กลง
    • (ค2) is_quantized(lhs) and is_quantized(rhs) and is_quantized(result)
    • (C3) storage_type(lhs) = storage_type(rhs) = storage_type(result)
    • (C4) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
    • (C5) (is_per_axis_quantized(lhs) or is_per_axis_quantized(rhs)) = is_per_axis_quantized(result)
    • (C6) ถ้าis_per_axis_quantized(lhs) ให้quantization_dimension(lhs) = quantization_dimension(result)
    • (C7) ถ้าis_per_axis_quantized(rhs) ให้quantization_dimension(rhs) = quantization_dimension(result)

ตัวอย่าง

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

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

after_all

ความหมาย

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

อินพุต

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

เอาต์พุต

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

ตัวอย่าง

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

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

all_gather

ความหมาย

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

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

  • cross_replica(replica_groups) if channel_id <= 0 and use_global_device_ids = false
  • cross_replica_and_partition(replica_groups) หากเป็น channel_id > 0 and use_global_device_ids = false
  • flattened_ids(replica_groups) if channel_id > 0 and use_global_device_ids = true

หลังจากนั้น ภายในแต่ละ process_group:

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

อินพุต

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

ตัวอย่าง

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

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

all_reduce

ความหมาย

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

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

  • cross_replica(replica_groups) if channel_id <= 0 and use_global_device_ids = false
  • cross_replica_and_partition(replica_groups) หากเป็น channel_id > 0 and use_global_device_ids = false
  • flattened_ids(replica_groups) if channel_id > 0 and use_global_device_ids = true

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

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

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operands จำนวนเทนเซอร์แบบผันแปรหรือเทนเซอร์ที่แปลงค่าต่อเทนเซอร์ (C5), (C6)
(I2) replica_groups จำนวนตัวแปรของค่าคงที่เทนเซอร์ 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...)
  • (ค7) 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 Tensor คือ lhs และ rhs และสร้าง Tensor result ดำเนินการต่อไปนี้โดยขึ้นอยู่กับประเภทองค์ประกอบ

  • สําหรับบูลีน: AND เชิงตรรกะ
  • สำหรับจำนวนเต็ม: Bitwise AND

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) lhs เทนเซอร์ประเภทบูลีนหรือจำนวนเต็ม (C1)
(I2) rhs tensor ของประเภทบูลีนหรือจำนวนเต็ม (C1)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result tensor ของประเภทบูลีนหรือจำนวนเต็ม (C1)

ข้อจำกัด

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

ตัวอย่าง

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

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

atan2

ความหมาย

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

  • สำหรับแบบลอย: atan2 จาก IEEE-754
  • สำหรับจำนวนเชิงซ้อน: 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 Backpropagating จาก grad_output และสร้าง grad_operand, grad_scale และ grad_offset tensor การดำเนินการนี้สามารถแสดงเป็นการจัดเรียงการดำเนินการ 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 มิติของจุดลอยตัวหรือประเภทต่อ 1 เซนเซอร์ (C2), (C4)
(I4) variance ความเครียด 1 มิติของจุดลอยตัวหรือประเภทต่อ 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 มิติของจุดลอยตัวหรือประเภทต่อ 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 เดียวกัน
  • (C3) operand, grad_output และ grad_operand มีรูปร่างเหมือนกัน
  • (C4) scale, mean, variance, grad_scale และ grad_offset มีรูปร่างเหมือนกัน
  • (C5) size(scale) = dim(operand, feature_index)

ตัวอย่าง

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

batch_norm_inference

ความหมาย

ปรับค่า Tensor operand ให้เป็นมาตรฐานในทุกมิติข้อมูลยกเว้นมิติข้อมูล feature_index และสร้าง Tensor 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 มิติของจุดลอยตัวหรือประเภทต่อ 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)
  • (ค7) 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 tensor ของประเภทจุดลอยตัวหรือ tensor ที่แปลงค่าเป็น 1 เซนเซอร์ (C1)
(I2) scale เทนเซอร์ 1 มิติของจำนวนจุดลอยตัวหรือจำนวนจุดลอยตัวต่อเทนเซอร์ (C2), (C3)
(I3) offset ความเครียด 1 มิติของจุดลอยตัวหรือต่อ 1 เซนเซอร์ที่แปลงค่าเป็นจำนวน 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 มิติของจุดลอยตัวหรือต่อ 1 เซนเซอร์ที่แปลงค่าเป็นจำนวน 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)
  • (ค7) 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

อรรถศาสตร์

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

เขียนอย่างเป็นทางการมากขึ้นคือ เมื่อมี E = element_type(operand), E' = element_type(result) และ R = rank(operand)

  • หาก num_bits(E') < num_bits(E) bits(result[i0, ..., iR-1, :]) = bits(operand[i0, ..., iR-1])
  • หาก num_bits(E') > num_bits(E) bits(result[i0, ..., iR-2]) = bits(operand[i0, ..., iR-2, :])
  • หาก num_bits(E') = num_bits(E) bits(result[i0, ..., iR-1]) = bits(operand[i0, ..., iR-1])

bits จะแสดงผลการแสดงค่าหนึ่งๆ ในหน่วยความจำ และระบบจะกำหนดลักษณะการทำงานของค่าดังกล่าวเนื่องจากมีการกำหนดการแสดง Tensor ที่แน่นอนและมีการกำหนดการใช้งานการแสดงประเภทองค์ประกอบที่แน่นอนด้วย

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand เทนเซอร์หรือเทนเซอร์ที่ผ่านการแปลงค่าเป็นจำนวนเต็ม (C1-C2)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result เทนเซอร์หรือเทนเซอร์ที่ผ่านการแปลงค่าเป็นจำนวนเต็ม (C1-C2)

ข้อจำกัด

  • (C1) ให้ E = is_quantized(operand) ? storage_type(operand) : element_type(operand), E' = is_quantized(result) ? storage_type(result) : element_type(result) และ R = rank(operand):
    • หาก num_bits(E') = num_bits(E) shape(result) = shape(operand)
    • ในกรณี num_bits(E') < num_bits(E)
    • rank(result) = R + 1
    • dim(result, i) = dim(operand, i) สำหรับ 0 <= i < R ทั้งหมด
    • dim(result, R) * num_bits(E') = num_bits(E)
    • หาก num_bits(E') > num_bits(E):
    • rank(result) = R - 1
    • dim(result, i) = dim(operand, i) สำหรับ 0 <= i < R ทั้งหมด
    • dim(operand, R - 1) * num_bits(E) = num_bits(E')
  • (C2) หากเป็น is_complex(operand) or is_complex(result) แล้ว is_complex(operand) and is_complex(result)

ตัวอย่าง

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

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

broadcast_in_dim

ความหมาย

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

  • operand_index[d] = 0 หาก dim(operand, d) = 1
  • จ่าย operand_index[d] = result_index[broadcast_dimensions[d]]

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand เทนเซอร์หรือเทนเซอร์ที่ผ่านการแปลงค่าเป็นจำนวนเต็ม (C1-C2), (C5-C6)
(I2) broadcast_dimensions ค่าคงที่เทนเซอร์ 1 มิติประเภท si64 (C2-C6)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result เทนเซอร์หรือเทนเซอร์ที่ผ่านการแปลงค่าเป็นจำนวนเต็ม (C1), (C3), (C5-C6)

ข้อจำกัด

  • (C1) element_type(result) คำนวณจากข้อมูลต่อไปนี้
    • element_type(operand) หาก !is_per_axis_quantized(operand)
    • element_type(operand) ยกเว้น quantization_dimension(operand), scales(operand) และ zero_points(operand) อาจแตกต่างจาก quantization_dimension(result), scales(result) และ zero_points(result) ตามลำดับ
  • (ค2) size(broadcast_dimensions) = rank(operand)
  • (C3) 0 <= broadcast_dimensions < rank(result)
  • (C4) is_unique(broadcast_dimensions)
  • (C5) สําหรับ d ทั้งหมดใน axes(operand) ให้ทําดังนี้
    • dim(operand, d) = 1หรือ
    • dim(operand, d) = dim(result, broadcast_dimensions[d])
  • (C6) หาก is_per_axis_quantized(result)
    • quantization_dimension(result) = broadcast_dimensions[quantization_dimension(operand)]
    • ถ้าdim(operand, quantization_dimension(operand)) = 1 ให้ scales(result)[i] = scales(operand)[0] and zero_points(result)[i] = zero_points(operand)[0] for i in range(dim(result, quantization_dimension(result)))

ตัวอย่าง

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

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

เคส

ความหมาย

สร้างเอาต์พุตจากการดำเนินการฟังก์ชันจาก branches เพียง 1 รายการเท่านั้น โดยขึ้นอยู่กับค่าของ 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]

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

CBT

ความหมาย

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

  • สำหรับตัวเลขทศนิยม: rootn(x, 3) จาก IEEE-754
  • สำหรับจำนวนเชิงซ้อน: รากที่สามเชิงซ้อน
  • สำหรับประเภทที่เล็กลง: dequantize_op_quantize(cbrt, operand, type(result))

อินพุต

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

เอาต์พุต

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

ข้อจำกัด

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

ตัวอย่าง

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

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

ceil

ความหมาย

ดำเนินการปัดเศษทศนิยมขึ้นทีละองค์ประกอบของเทนเซอร์ operand และสร้างเทนเซอร์ result ใช้การดำเนินการ roundToIntegralTowardPositive จากข้อกำหนด IEEE-754 สําหรับประเภทที่กําหนดค่าได้ ให้ทําการ dequantize_op_quantize(ceil, operand, type(result))

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand tensor ของประเภทจุดลอยตัวหรือ tensor ที่แปลงค่าเป็น 1 เซนเซอร์ (C1)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result tensor ของประเภทจุดลอยตัวหรือ tensor ที่แปลงค่าเป็น 1 เซนเซอร์ (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 ที่แน่นอนเชิงบวก ระบบจะไม่ระบุลักษณะการทำงาน

สําหรับประเภทที่กําหนดค่าได้ ให้ทําการ dequantize_op_quantize(lambda operand: cholesky(operand, lower), a, type(result))

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) a เทนเซอร์ประเภททศนิยมหรือเชิงซ้อน หรือเทนเซอร์ที่ผ่านการแปลงเชิงปริมาณต่อเทนเซอร์ (C1-C3)
(I2) lower ค่าคงที่ tensor ของประเภท i1 ค่าคงที่ 0 มิติ

เอาต์พุต

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

ข้อจำกัด

  • (C1) baseline_type(a) = baseline_type(result)
  • (ค2) 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]
//          ]

หนีบ

อรรถศาสตร์

ยึดทุกองค์ประกอบของ tensor ของ operand ระหว่างค่าต่ำสุดและสูงสุด และสร้าง Tensor ขนาด 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)
  • (ค2) 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 ให้ส่งค่าของ Tensor operand จากกระบวนการต้นทางไปยังกระบวนการเป้าหมายและสร้าง Tensor 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 ในการปรับปริมาณ (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
  • (ค2) 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]]

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

เปรียบเทียบ

ความหมาย

ทำการเปรียบเทียบ tensor ของ lhs และ rhs ตามองค์ประกอบ comparison_direction และ compare_type และสร้าง Tensor เป็น 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 ในการปรับปริมาณ (C1-C2)
(I3) comparison_direction enum ของ EQ, NE, GE, GT, LE และ LT
(I4) compare_type enum ของ FLOAT, TOTALORDER, SIGNED และ UNSIGNED (C3)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result เทนเซอร์ประเภทบูลีน (C2)

ข้อจำกัด

  • (C1) baseline_element_type(lhs) = baseline_element_type(rhs)
  • (ค2) 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)
  • (ค2) shape(result) = shape(lhs)
  • (C3) element_type(result) เป็นประเภท complex<E> โดยที่ E = element_type(lhs)

ตัวอย่าง

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

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

การผสม

ความหมาย

บรรจุการดำเนินการที่สร้างขึ้น (ประกอบด้วย) การดำเนินการ StableHLO อื่นๆ โดยนำ inputs และ composite_attributes มาสร้าง results แอตทริบิวต์ decomposition จะนําความหมายของการดำเนินการไปใช้ คุณสามารถแทนที่การดําเนินการ composite ด้วยการเปลี่ยนรูปแบบได้โดยไม่ต้องเปลี่ยนความหมายของโปรแกรม ในกรณีที่ในบรรทัดการแยกย่อยไม่ได้ให้ความหมายด้านตรงข้ามเหมือนกัน ให้เลือกใช้ custom_call

ช่อง version (ค่าเริ่มต้นคือ 0) ใช้ระบุเมื่อมีการเปลี่ยนแปลงความหมายของการผสม

อินพุต

ป้ายกำกับ ชื่อ ประเภท
(I1) inputs จำนวนค่าแบบผันแปร
(I2) name ค่าคงที่ประเภท string
(I3) composite_attributes พจนานุกรมแอตทริบิวต์
(I4) decomposition ค่าคงที่ประเภท string
(I5) version ค่าคงที่ประเภท si32

เอาต์พุต

ชื่อ ประเภท
results จำนวนค่าแบบผันแปร

ข้อจำกัด

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

ตัวอย่าง

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

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

concatenate

ความหมาย

ต่อ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

สําหรับการเปลี่ยนค่า boolean เป็นประเภทที่รองรับ ระบบจะแปลงค่า false เป็น 0 และแปลงค่า true เป็น 1 สําหรับการแปลง any-supported-type-to-boolean ระบบจะแปลงค่า 0 เป็น false และแปลงค่าที่ไม่ใช่ 0 เป็น true ดูวิธีการทํางานของการดำเนินการนี้กับประเภทที่ซับซ้อนได้ที่ด้านล่าง

สำหรับ Conversion ที่เกี่ยวข้องกับ integer-to-integer, integer-to-Floing-point หรือ Floating-point-to-Floing-point หากค่าต้นทางสามารถแสดงในประเภทปลายทางได้อย่างถูกต้อง ค่าผลลัพธ์จะเป็นค่าการเป็นตัวแทนที่ถูกต้อง มิฉะนั้น ระบบจะกำหนดลักษณะการทำงานในภายหลัง (#180)

สําหรับการแปลงที่เกี่ยวข้องกับทศนิยมเป็นจํานวนเต็ม ระบบจะตัดเศษส่วน หากค่าที่ถูกตัดให้สั้นลงแสดงในประเภทปลายทางไม่ได้ ลักษณะการทํางานจะยังไม่ทราบ (#180)

Conversion ที่เกี่ยวข้องกับ Conversion แบบซับซ้อนไปซับซ้อนมีลักษณะการทำงานเดียวกันกับ Conversion จุดลอยตัวไปยังจุดลอยตัว สำหรับการแปลงส่วนจริงและส่วนจินตภาพ

สําหรับการเปลี่ยนประเภทจากเชิงซ้อนเป็นประเภทอื่นๆ และประเภทอื่นๆ เป็นเชิงซ้อน ระบบจะไม่สนใจค่าจินตภาพของต้นทางหรือตั้งค่าจินตภาพของปลายทางเป็น 0 ตามลําดับ การแปลงของส่วนที่เป็นจำนวนจริงจะเป็นไปตามการแปลงทศนิยม

โดยหลักการแล้ว การดำเนินการนี้อาจแสดงการลดจำนวน (Conversion จาก Tensor ที่เล็กลงเป็น Tensor ปกติ) การแปลงปริมาณ (การแปลงจาก Tensor ปกติเป็น Tensor ที่ควอนไซส์) และการแปลงจำนวนใหม่ (การแปลงระหว่าง Tensor ที่เล็กลง) แต่ในตอนนี้เรามีการดำเนินการเฉพาะสำหรับ -uniform_dequantize สำหรับ Use Case แรกและ 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] where 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 ค่าคงที่ tensor ใน 1 มิติของประเภท si64 (C5-C6), (C25)
(I6) rhs_dilation ค่าคงที่เทนเซอร์ 1 มิติประเภท si64 (C7-C8), (C25)
(I7) window_reversal ค่าคงที่ tensor ใน 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 จำนวนรายการแบบผันแปรของ DEFAULT, HIGH และ HIGHEST (C24)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result tensor หรือ tensor แบบเชิงปริมาณ (C25-C28), (C30), (C32-34)

ข้อจำกัด

  • (C1) N = rank(lhs) = rank(rhs)
  • (ค2) 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
  • (ค7) size(rhs_dilation) = N - 2
  • (C8) 0 < rhs_dilation
  • (C9) size(window_reversal) = N - 2
  • (C10) dim(lhs, input_batch_dimension) % batch_group_count = 0
  • (C11) dim(lhs, input_feature_dimension) % feature_group_count = 0
  • (C12) size(input_spatial_dimensions) = N - 2
  • (C13) กำหนดให้ input_dimensions = [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]
    • is_unique(input_dimensions)
    • 0 <= input_dimensions < N
  • (C14) dim(rhs, kernel_input_feature_dimension) = dim(lhs, input_feature_dimension) / feature_group_count
  • (C15) dim(rhs, kernel_output_feature_dimension) % batch_group_count = 0
  • (C16) dim(rhs, kernel_output_feature_dimension) % feature_group_count = 0
  • (C17) size(kernel_spatial_dimensions) = N - 2
  • (C18) kernel_dimensions = kernel_spatial_dimensions + [kernel_input_feature_dimension] + [kernel_output_feature_dimension] ที่ได้รับ
    • is_unique(kernel_dimensions)
    • 0 <= kernel_dimensions < N
  • (C19) size(output_spatial_dimensions) = N - 2
  • (C20) เงื่อนไข output_dimensions = [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]
    • is_unique(output_dimensions)
    • 0 <= output_dimensions < N
  • (C21) 0 < feature_group_count
  • (C22) 0 < batch_group_count
  • (C23) feature_group_count = 1 or batch_group_count = 1
  • (C24) size(precision_config) = 2
  • (C25) dim(result, result_dim) มีคำจำกัดความดังนี้
    • dim(lhs, input_batch_dimension) / batch_group_count หากเป็น result_dim = output_batch_dimension
    • dim(rhs, kernel_output_feature_dimension) หาก result_dim = output_feature_dimension
    • num_windows ในกรณีอื่นๆ โดยที่
    • output_spatial_dimensions[spatial_dim] = result_dim
    • lhs_dim = input_spatial_dimensions[spatial_dim]
    • rhs_dim = kernel_spatial_dimensions[spatial_dim]
    • dilated_input_shape[lhs_dim] = dim(lhs, lhs_dim) = 0 ? 0 : (dim(lhs, lhs_dim) - 1) * lhs_dilation[spatial_dim] + 1
    • padded_input_shape[lhs_dim] = padding[spatial_dim, 0] + dilated_input_shape[lhs_dim] + padding[spatial_dim, 1]
    • dilated_window_shape[lhs_dim] = dim(rhs, rhs_dim) = 0 ? 0 : (dim(rhs, rhs_dim) - 1) * rhs_dilation[spatial_dim] + 1
    • is_empty_window[lhs_dim] = padded_input_shape[lhs_dim] = 0 || dilated_window_shape[lhs_dim] > padded_input_shape[lhs_dim]
    • num_windows = is_empty_window[lhs_dim] ? 0 : floor((padded_input_shape[lhs_dim] - dilated_window_shape[lhs_dim]) / window_strides[spatial_dim]) + 1
  • (C26) rank(result) = N
  • หากการดำเนินการใช้เทนเซอร์ที่ไม่ผ่านการแปลงค่า ให้ทำดังนี้
    • (C27) element_type(lhs) = element_type(rhs) = element_type(result)
  • หากการดำเนินการใช้เทนเซอร์ที่แปลงค่าเป็นจำนวนเต็ม ให้ทำดังนี้
    • (C28) is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
    • (C29) ถ้าis_per_axis_quantized(rhs) ให้quantization_dimension(rhs) = kernel_output_feature_dimension
    • (C30) หาก is_per_axis_quantized(result) ให้ quantization_dimension(result) = output_feature_dimension
    • ในกรณี is_quantized(lhs)
    • (C31) storage_type(lhs) = storage_type(rhs)
    • (C32) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
    • (C33) ถ้าis_per_tensor_quantized(rhs) ให้ is_per_tensor_quantized(result)
    • ในกรณี !is_quantized(lhs)
    • (C34) element_type(lhs) = expressed_type(rhs) = element_type(result)

ตัวอย่าง

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

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

โคไซน์

ความหมาย

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

  • สำหรับตัวเลขทศนิยม: cos จาก IEEE-754
  • สำหรับจำนวนเชิงซ้อน: โคไซน์เชิงซ้อน
  • สำหรับประเภทที่มีการแปลงเป็นจำนวนเต็ม: dequantize_op_quantize(cosine, operand, type(result))

อินพุต

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

เอาต์พุต

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

ข้อจำกัด

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

ตัวอย่าง

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

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

count_leading_zeros

ความหมาย

นับจํานวนบิต 0 นําหน้าขององค์ประกอบในเทนเซอร์ operand และสร้างเทนเซอร์ result

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand เทนเซอร์ประเภทจำนวนเต็ม (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 จำนวนคงที่แบบผันแปรของประเภท string

เอาต์พุต

ชื่อ ประเภท
results จำนวนค่าแบบผันแปร

ตัวอย่าง

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

หาร

อรรถศาสตร์

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

  • สําหรับจํานวนเต็ม: การหารจํานวนเต็มซึ่งให้ผลหารพีชคณิตโดยทิ้งเศษส่วน
  • สำหรับแบบลอย: division จาก IEEE-754
  • สำหรับจำนวนเชิงซ้อน: การหารจำนวนเชิงซ้อน
  • สำหรับประเภทที่มีการแปลงค่าเป็นจำนวนเต็ม ให้ทำดังนี้
    • dequantize_op_quantize(divide, lhs, rhs, type(result))

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) lhs เทนเซอร์ประเภทจำนวนเต็ม ทศนิยม หรือเชิงซ้อน หรือเทนเซอร์ที่ผ่านการแปลงเชิงปริมาณต่อเทนเซอร์ (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 และสร้าง Tensor 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 จะควบคุมการแลกเปลี่ยนระหว่างความเร็วและความแม่นยำสำหรับการคำนวณในแบ็กเอนด์ Accelerator ค่านี้อาจเป็นค่าใดค่าหนึ่งต่อไปนี้ (ขณะนี้ ความหมายของค่า 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 ออกเป็นคอมโพเนนต์หลายรายการ และดำเนินการ DOT "พื้นฐาน" หลายรายการกับค่าเหล่านั้น ซึ่งมักจะเป็นการจําลองความแม่นยําที่สูงขึ้น (เช่น การใช้ประโยชน์จากประเภทข้อมูลปัญญาประดิษฐ์ 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 เทนเซอร์หรือเทนเซอร์ที่ผ่านการแปลงค่าเป็นจำนวนเต็ม (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 จำนวนรายการแบบผันแปรของ DEFAULT, HIGH และ HIGHEST (C11), (C21)
(I8) lhs_precision_type FloatType หรือ TensorFloat32 (C21)
(I9) rhs_precision_type FloatType หรือ TensorFloat32 (C21)
(I10) accumulation_type FloatType หรือ TensorFloat32 (C21)
(I11) lhs_component_count ค่าคงที่ประเภท si32 (C21), (C22)
(I12) rhs_component_count ค่าคงที่ของประเภท si32 (C21), (C23)
(I13) num_primitive_operations ค่าคงที่ประเภท si32 (C21), (C24)
(I14) allow_imprecise_accumulation ค่าคงที่ประเภท bool (C21)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result เทนเซอร์หรือเทนเซอร์ที่ผ่านการแปลงค่าเป็นจำนวนเต็ม (C12), (C14), (C18-C20)

ข้อจำกัด

  • (C1) size(lhs_batching_dimensions) = size(rhs_batching_dimensions)
  • (ค2) 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)
  • (ค7) 0 <= rhs_batching_dimensions < rank(rhs)
  • (C8) 0 <= rhs_contracting_dimensions < rank(rhs)
  • (C9) dim(lhs, lhs_batching_dimensions...) = dim(rhs, rhs_batching_dimensions...)
  • (C10) dim(lhs, lhs_contracting_dimensions...) = dim(rhs, rhs_contracting_dimensions...)
  • (C11) size(precision_config) = 2
  • (C12) shape(result) = dim(lhs, lhs_batching_dimensions) + dim(lhs, lhs_result_dimensions) + dim(rhs, rhs_result_dimensions)
  • หากการดำเนินการใช้เทนเซอร์ที่ไม่ผ่านการแปลงค่า ให้ทำดังนี้
    • (C13) element_type(lhs) = element_type(rhs)
  • หากการดำเนินการใช้เทนเซอร์ที่แปลงค่าเป็นจำนวนเต็ม ให้ทำดังนี้
    • (C14) is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
    • (C15) zero_points(rhs) = 0
    • (C16) หากเป็น is_per_axis_quantized(rhs) แสดงว่า quantization_dimension(rhs) ไม่ใช่ใน rhs_contracting_dimensions
    • ในกรณี is_quantized(lhs)
    • (C17) storage_type(lhs) = storage_type(rhs)
    • (C18) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
    • (C19) ถ้า is_per_tensor_quantized(rhs) ให้ is_per_tensor_quantized(result)
    • ในกรณี !is_quantized(lhs)
    • (C20) element_type(lhs) = expressed_type(rhs) = element_type(result)
  • หาก !is_empty_algorithm(lhs_precision_type, rhs_precision_type, accumulation_type, lhs_component_count, rhs_component_count, num_primitive_operations allow_imprecise_accumulation)
    • (C21) precision_config... = DEFAULT
    • (C22) 0 < lhs_component_count
    • (C23) 0 < rhs_component_count
    • (C24) 0 < num_primitive_operations

ตัวอย่าง

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

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

dynamic_broadcast_in_dim

ความหมาย

การดำเนินการนี้มีฟังก์ชันการทำงานเหมือนกับ broadcast_in_dimop แต่จะมีการระบุรูปแบบผลลัพธ์แบบไดนามิกผ่าน output_dimensions

นอกจากนี้ การดำเนินการยังยอมรับแอตทริบิวต์ที่ไม่บังคับ known_expanding_dimensions, known_nonexpanding_dimensions เพื่อแสดงความรู้แบบคงที่เกี่ยวกับลักษณะการขยายตัวของมิติข้อมูล หากไม่ได้ระบุ ระบบจะถือว่ามิติข้อมูลทั้งหมดอาจขยาย

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand เทนเซอร์หรือเทนเซอร์ที่ผ่านการแปลงค่าเป็นจำนวนเต็ม (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 เทนเซอร์หรือเทนเซอร์ที่ผ่านการแปลงค่าเป็นจำนวนเต็ม (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) ตามลำดับ
  • (ค2) 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

ความหมาย

การดำเนินการนี้มีฟังก์ชันการทำงานเหมือนกับ convolution op แต่จะมีการระบุการเติมข้อมูลแบบไดนามิกผ่าน padding

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) lhs tensor หรือต่อ tensor ในการปรับปริมาณ (C1), (C10-C11), (C14) (C25), (C26-C27), (C30-C31), (C33)
(I2) rhs เทนเซอร์หรือเทนเซอร์ที่ผ่านการแปลงค่าเป็นจำนวนเต็ม (C1), (C14-C16), (C26-C28), (C30-C33)
(I3) padding เทนเซอร์ 2 มิติประเภทจำนวนเต็ม (C4)
(I4) window_strides ค่าคงที่เทนเซอร์ 1 มิติประเภท si64 (C2-C3)
(I5) lhs_dilation ค่าคงที่ tensor ใน 1 มิติของประเภท si64 (C5-C6)
(I6) rhs_dilation ค่าคงที่เทนเซอร์ 1 มิติประเภท si64 (C7-C8)
(I7) window_reversal ค่าคงที่ tensor ใน 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 จำนวนรายการแบบผันแปรของ DEFAULT, HIGH และ HIGHEST (C24)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result เทนเซอร์หรือเทนเซอร์ที่ผ่านการแปลงค่าเป็นจำนวนเต็ม (C25-C27), (C29), (C31-C33)

ข้อจำกัด

  • (C1) N = rank(lhs) = rank(rhs)
  • (ค2) 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
  • (ค7) size(rhs_dilation) = N - 2
  • (C8) 0 < rhs_dilation
  • (C9) size(window_reversal) = N - 2
  • (C10) dim(lhs, input_batch_dimension) % batch_group_count = 0
  • (C11) dim(lhs, input_feature_dimension) % feature_group_count = 0
  • (C12) size(input_spatial_dimensions) = N - 2
  • (C13) กำหนดให้ input_dimensions = [input_batch_dimension] + input_spatial_dimensions + [input_feature_dimension]
    • is_unique(input_dimensions)
    • 0 <= input_dimensions < N
  • (C14) dim(rhs, kernel_input_feature_dimension) = dim(lhs, input_feature_dimension) / feature_group_count
  • (C15) dim(rhs, kernel_output_feature_dimension) % batch_group_count = 0
  • (C16) dim(rhs, kernel_output_feature_dimension) % feature_group_count = 0
  • (C17) size(kernel_spatial_dimensions) = N - 2
  • (C18) kernel_dimensions = kernel_spatial_dimensions + [kernel_input_feature_dimension] + [kernel_output_feature_dimension] ที่ได้รับ
    • is_unique(kernel_dimensions)
    • 0 <= kernel_dimensions < N
  • (C19) size(output_spatial_dimensions) = N - 2
  • (C20) เงื่อนไข output_dimensions = [output_batch_dimension] + output_spatial_dimensions + [output_feature_dimension]
    • is_unique(output_dimensions)
    • 0 <= output_dimensions < N
  • (C21) 0 < feature_group_count
  • (C22) 0 < batch_group_count
  • (C23) feature_group_count = 1 or batch_group_count = 1
  • (C24) size(precision_config) = 2
  • (C25) dim(result, result_dim) มีคำจำกัดความดังนี้
    • dim(lhs, input_batch_dimension) / batch_group_count หากเป็น result_dim = output_batch_dimension
    • dim(rhs, kernel_output_feature_dimension) หาก result_dim = output_feature_dimension
    • num_windows ในกรณีอื่นๆ โดยที่
    • output_spatial_dimensions[spatial_dim] = result_dim
    • lhs_dim = input_spatial_dimensions[spatial_dim]
    • rhs_dim = kernel_spatial_dimensions[spatial_dim]
    • dilated_input_shape[lhs_dim] = dim(lhs, lhs_dim) = 0 ? 0 : (dim(lhs, lhs_dim) - 1) * lhs_dilation[spatial_dim] + 1
    • padded_input_shape[lhs_dim] = padding[spatial_dim, 0] + dilated_input_shape[lhs_dim] + padding[spatial_dim, 1]
    • dilated_window_shape[lhs_dim] = dim(rhs, rhs_dim) = 0 ? 0 : (dim(rhs, rhs_dim) - 1) * rhs_dilation[spatial_dim] + 1
    • is_empty_window[lhs_dim] = padded_input_shape[lhs_dim] = 0 || dilated_window_shape[lhs_dim] > padded_input_shape[lhs_dim]
    • num_windows = is_empty_window[lhs_dim] ? 0 : floor((padded_input_shape[lhs_dim] - dilated_window_shape[lhs_dim]) / window_strides[spatial_dim]) + 1
  • (C26) rank(result) = N
  • หากการดำเนินการใช้เทนเซอร์ที่ไม่ผ่านการแปลงค่า ให้ทำดังนี้
    • (C27) element_type(lhs) = element_type(rhs) = element_type(result)
  • หากการดำเนินการใช้เทนเซอร์ที่แปลงค่าเป็นจำนวนเต็ม ให้ทำดังนี้
    • (C28) is_quantized(lhs) = is_quantized(result) and is_quantized(rhs)
    • (C29) ถ้าis_per_axis_quantized(rhs) ให้quantization_dimension(rhs) = kernel_output_feature_dimension
    • (C30) หาก is_per_axis_quantized(result) ให้ quantization_dimension(result) = output_feature_dimension
    • ในกรณี is_quantized(lhs)
    • (C31) storage_type(lhs) = storage_type(rhs)
    • (C32) expressed_type(lhs) = expressed_type(rhs) = expressed_type(result)
    • (C33) ถ้าis_per_tensor_quantized(rhs) ให้ is_per_tensor_quantized(result)
    • ในกรณี !is_quantized(lhs)
    • (C34) element_type(lhs) = expressed_type(rhs) = element_type(result)

ตัวอย่าง

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

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

dynamic_gather

ความหมาย

การดำเนินการนี้มีฟังก์ชันการทำงานเหมือนกับ gather op โดยที่ slice_sizes จะระบุเป็นค่าแบบไดนามิก

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand Tensor หรือ Tensor ที่แปลงค่าเป็นจำนวนเต็มต่อ Tensor (C1), (C7), (C10-C12), (C14)
(I2) start_indices เทนเซอร์ประเภทจำนวนเต็ม (C2), (C3), (C13)
(I3) slice_sizes เทนเซอร์ 1 มิติประเภทจำนวนเต็ม (C8), (C11-C13)
(I4) offset_dims ค่าคงที่ tensor ใน 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)
  • (ค2) 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)
  • (ค7) 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)
  • (ค2) rank(result) = size(output_shape)

ตัวอย่าง

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

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

dynamic_pad

ความหมาย

การดำเนินการนี้มีฟังก์ชันการทำงานเหมือนกับการดำเนินการ pad แต่มีการระบุ edge_padding_low, edge_padding_high และ interior_padding เป็นค่าแบบไดนามิก

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand 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 แต่จะมีการระบุรูปแบบผลลัพธ์แบบไดนามิกผ่าน output_shape

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand tensor หรือ tensor แบบเชิงปริมาณ (C1-C3)
(I2) output_shape เทนเซอร์ 1 มิติประเภทจำนวนเต็ม (C4)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result เทนเซอร์หรือเทนเซอร์ที่ผ่านการแปลงค่าเป็นจำนวนเต็ม (C1-C4)

ข้อจำกัด

  • (C1) element_type(result) คำนวณจากข้อมูลต่อไปนี้
    • element_type(operand) หาก !is_per_axis_quantized(operand)
    • element_type(operand) ยกเว้นว่า quantization_dimension(operand) และ quantization_dimension(result) อาจแตกต่างกัน
  • (ค2) size(operand) = size(result)
  • (C3) หาก is_per_axis_quantized(operand)
    • reduce(dims(operand, [0, 1, ..., quantization_dimension(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [0, 1, ..., quantization_dimension(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y)
    • dim(operand, quantization_dimension(operand)) = dim(result, quantization_dimension(result))
    • reduce(dims(operand, [quantization_dimension(operand) + 1, ..., rank(operand) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y) = reduce(dims(result, [quantization_dimension(result) + 1, ..., rank(result) - 1]), init_values=1, dimensions=[0], body=lambda x, y: x * y)
  • (C4) size(output_shape) = rank(result)

ตัวอย่าง

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

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

dynamic_slice

อรรถศาสตร์

ดึงข้อมูลส่วนจาก operand โดยใช้ดัชนีเริ่มต้นที่คำนวณแบบไดนามิก และสร้างเทนเซอร์ result start_indices มีดัชนีเริ่มต้นของส่วนสำหรับมิติข้อมูลแต่ละรายการซึ่งอาจมีการปรับ และ slice_sizes มีขนาดของส่วนสำหรับมิติข้อมูลแต่ละรายการ ในรูปแบบที่เป็นทางการ result[result_index] = operand[operand_index] โดยที่

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

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand tensor หรือต่อ tensor ในการปรับปริมาณ (C1), (C2), (C4)
(I2) start_indices จำนวนแปรผันของ tenor ที่เป็น 0 มิติของประเภทจำนวนเต็ม (C2), (C3)
(I3) slice_sizes ค่าคงที่เทนเซอร์ 1 มิติประเภท si64 (C2), (C4), (C5)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result Tensor หรือ Tensor ที่แปลงค่าเป็นจำนวนเต็มต่อ Tensor (C1), (C5)

ข้อจำกัด

  • (C1) element_type(operand) = element_type(result)
  • (ค2) 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] if 0 <= update_index < shape(update) where:
    • 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 จำนวนแปรผันของ tenor ที่เป็น 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 ทําสิ่งต่อไปนี้โดยขึ้นอยู่กับประเภทองค์ประกอบ

  • สำหรับตัวเลขทศนิยม: 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 ทําสิ่งต่อไปนี้โดยขึ้นอยู่กับประเภทองค์ประกอบ

  • สำหรับตัวเลขทศนิยม: 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 ซึ่งนำ Tensor 1 มิติของประเภทเชิงซ้อนเป็นอินพุตจะสร้าง Tensor 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 where
  • complex_operand... = (real_operand..., 0.0)
  • complex_result = fft(complex_operand)
  • truncated_result = complex_result[:(rank(complex_result) / 2 + 1)]

(เมื่อคำนวณการแปลง Fourier แบบไม่ต่อเนื่องสำหรับตัวดำเนินการจริง องค์ประกอบ 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 Tensor ของจุดลอยตัวหรือประเภทเชิงซ้อน (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 มี tensor real ของประเภทจุดลอยตัว 1 ค่าแล้ว 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 tensor ของประเภทจุดลอยตัวหรือ tensor ที่แปลงค่าเป็น 1 เซนเซอร์ (C1)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result tensor ของประเภทจุดลอยตัวหรือ tensor ที่แปลงค่าเป็น 1 เซนเซอร์ (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]) if d_operand = start_index_map[d_start]
    • จ่าย full_start_index[d_operand] = 0
  • สำหรับ d_operand ใน axes(operand)
    • full_batching_index[d_operand] = batch_index[d_start - (d_start < index_vector_dim ? 0 : 1)] if d_operand = operand_batching_dims[i_batching] and 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 ค่าคงที่ tensor ใน 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 ในการปรับปริมาณ (C5), (C22-C23)

ข้อจำกัด

  • (C1) rank(operand) = size(offset_dims) + size(collapsed_slice_dims) + size(operand_batching_dims)
  • (ค2) 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))
  • (ค7) 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) where:
    • 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) ความหมายเกี่ยวข้องกับองค์ประกอบ รูปร่างของประเภทเท่านั้น element-type อาจเป็นอะไรก็ได้

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(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 Tuple (C1), (C2)
(I2) index ค่าคงที่ประเภท si32 (C1), (C2)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result ประเภทใดก็ได้ที่รองรับ (C2)

ข้อจำกัด

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

ตัวอย่าง

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

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

ถ้า

ความหมาย

สร้างเอาต์พุตจากการดำเนินการฟังก์ชันจาก true_branch หรือ false_branch เพียง 1 รายการเท่านั้น โดยขึ้นอยู่กับค่าของ pred หรือจะเรียกอย่างเป็นทางการว่า result = pred ? true_branch() : false_branch()

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) pred เทนเซอร์ 0 มิติประเภท i1
(I2) true_branch ฟังก์ชัน (C1-C3)
(I3) false_branch ฟังก์ชัน (C1), (C2)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
results จำนวนแปรผันของ Tensor, Tensor ที่เล็กลง หรือโทเค็น (C3)

ข้อจำกัด

  • (C1) input_types(true_branch) = input_types(false_branch) = []
  • (ค2) 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 tensor ของประเภทจุดลอยตัวหรือ tensor ที่แปลงค่าเป็น 1 เซนเซอร์ (C1)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
y เทนเซอร์ประเภทบูลีน (C1)

ข้อจำกัด

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

ตัวอย่าง

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

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

บันทึก

ความหมาย

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

  • สำหรับแบบลอย: log จาก IEEE-754
  • สำหรับจำนวนเชิงซ้อน: ลอการิทึมเชิงซ้อน
  • สำหรับประเภทที่มีการแปลงเป็นจำนวนเต็ม: dequantize_op_quantize(log, operand, type(result))

อินพุต

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

เอาต์พุต

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

ข้อจำกัด

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

ตัวอย่าง

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

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

log_plus_one

ความหมาย

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

  • สำหรับตัวเลขทศนิยม: logp1 จาก IEEE-754
  • สำหรับจำนวนเชิงซ้อน: ลอการิทึมเชิงซ้อนบวก 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]

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

โลจิสติกส์

อรรถศาสตร์

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

  • สำหรับแบบลอย: division(1, addition(1, exp(-x))) จาก IEEE-754
  • สําหรับจํานวนเชิงซ้อน: ลอจิสติกเชิงซ้อน
  • สําหรับประเภทที่มีการแปลงค่าเป็นจำนวนเต็ม ให้ใช้รูปแบบต่อไปนี้ dequantize_op_quantize(logistic, operand, type(result))

อินพุต

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

เอาต์พุต

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

ข้อจำกัด

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

ตัวอย่าง

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

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

แผนที่

อรรถศาสตร์

ใช้ฟังก์ชันการแมป computation กับ inputs ตาม dimensions และสร้างเทนเซอร์ result

หรือจะเรียกอย่างเป็นทางการว่า result[result_index] = computation(inputs...[result_index])

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) inputs จำนวนเทนเซอร์แบบผันแปรหรือเทนเซอร์ที่แปลงค่าต่อเทนเซอร์ (C1-C4)
(I2) dimensions ค่าคงที่ tensor ใน 1 มิติของประเภท si64 (C3)
(I3) computation ฟังก์ชัน (C4)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result Tensor หรือ Tensor ที่แปลงค่าเป็นจำนวนเต็มต่อ Tensor (C1), (C4)

ข้อจำกัด

  • (C1) shape(inputs...) = shape(result)
  • (ค2) 0 < size(inputs) = N
  • (C3) dimensions = range(rank(inputs[0]))
  • (C4) computation มีประเภท (tensor<E0>, ..., tensor<EN-1>) -> tensor<E'> โดยที่ Ei = element_type(inputs[i]) และ E' = element_type(result)

ตัวอย่าง

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

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

สูงสุด

ความหมาย

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

  • สําหรับบูลีน: OR เชิงตรรกะ
  • สําหรับจํานวนเต็ม: จํานวนเต็มสูงสุด
  • สำหรับแบบลอย: maximum จาก IEEE-754
  • สำหรับจํานวนเชิงซ้อน: ลําดับตัวอักษรสูงสุดสําหรับคู่ (real, imaginary) การกำหนดลําดับสำหรับจํานวนเชิงซ้อนเกี่ยวข้องกับความหมายที่แปลกประหลาด ดังนั้นในอนาคตเราจึงวางแผนที่จะยกเลิกการรองรับจํานวนเชิงซ้อนสําหรับการดำเนินการนี้ (#560)
  • สำหรับประเภทที่มีการแปลงค่าเป็นจำนวนเต็ม ให้ทำดังนี้
    • dequantize_op_quantize(maximum, lhs, rhs, type(result))

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) lhs tensor หรือต่อ tensor ในการปรับปริมาณ (C1)
(I2) rhs 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]]

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

ขั้นต่ำ

อรรถศาสตร์

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

  • สําหรับบูลีน: AND เชิงตรรกะ
  • สําหรับจํานวนเต็ม: จํานวนเต็มขั้นต่ำ
  • สำหรับตัวเลขทศนิยม: minimum จาก IEEE-754
  • สำหรับจํานวนเชิงซ้อน: น้อยที่สุดตามลําดับตัวอักษรสําหรับคู่ (real, imaginary) การกำหนดลําดับสำหรับจํานวนเชิงซ้อนเกี่ยวข้องกับความหมายที่แปลกประหลาด ดังนั้นในอนาคตเราจึงวางแผนที่จะยกเลิกการรองรับจํานวนเชิงซ้อนสําหรับการดำเนินการนี้ (#560)
  • สำหรับประเภทที่เล็กลง
    • dequantize_op_quantize(minimum, lhs, rhs, type(result))

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) lhs tensor หรือต่อ tensor ในการปรับปริมาณ (C1)
(I2) rhs 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 เชิงตรรกะ
  • สําหรับจํานวนเต็ม: การคูณจํานวนเต็ม
  • สำหรับแบบลอย: multiplication จาก IEEE-754
  • สำหรับจำนวนเชิงซ้อน: การคูณเชิงซ้อน
  • สำหรับประเภทที่มีการแปลงค่าเป็นจำนวนเต็ม ให้ทำดังนี้
    • dequantize_op_quantize(multiply, lhs, rhs, type(result))

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) lhs tensor หรือต่อ tensor ในการปรับปริมาณ (C1)
(I2) rhs 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 ทําสิ่งต่อไปนี้โดยขึ้นอยู่กับประเภทองค์ประกอบ

  • สําหรับจํานวนเต็มที่มีเครื่องหมาย: การปฏิเสธจํานวนเต็ม
  • สําหรับจํานวนเต็มแบบไม่มีเครื่องหมาย: บิตแคสต์เป็นจํานวนเต็มแบบมีเครื่องหมาย การปฏิเสธจํานวนเต็ม บิตแคสต์กลับเป็นจํานวนเต็มแบบไม่มีเครื่องหมาย
  • สำหรับตัวเลขทศนิยม: 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]

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

ไม่ใช่

ความหมาย

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

  • สำหรับบูลีน: ตรรกะ NOT
  • สำหรับจำนวนเต็ม: Bitwise NOT

อาร์กิวเมนต์

ชื่อ ประเภท ข้อจำกัด
operand tensor ของประเภทบูลีนหรือจำนวนเต็ม (C1)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result tensor ของประเภทบูลีนหรือจำนวนเต็ม (C1)

ข้อจำกัด

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

ตัวอย่าง

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

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

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

optimization_barrier

อรรถศาสตร์

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

อาร์กิวเมนต์

ชื่อ ประเภท ข้อจำกัด
operand จำนวนตัวแปรของ Tensor ต่อ tensor ที่แปลงค่าเป็นจำนวนหรือโทเค็น (C1)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result จำนวนตัวแปรของ Tensor ต่อ tensor ที่แปลงค่าเป็นจำนวนหรือโทเค็น (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]]

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

Outfeed

ความหมาย

เขียน inputs ไปยังเอาต์ฟีดและสร้างโทเค็น result

ความหมายของ outfeed_config ขึ้นอยู่กับการใช้งาน

อินพุต

ป้ายกำกับ ชื่อ ประเภท
(I1) inputs จำนวนเทนเซอร์แบบผันแปรหรือเทนเซอร์ที่ผ่านการแปลงค่าเป็นจำนวนจริง
(I2) token token
(I3) outfeed_config ค่าคงที่ประเภท string

เอาต์พุต

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

ตัวอย่าง

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

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

แผ่น

ความหมาย

ขยาย operand ด้วยระยะห่างจากขอบรอบๆ เทรนเนอร์ รวมถึงระหว่างองค์ประกอบของ Tensor ด้วย padding_value ที่ระบุ

edge_padding_low และ edge_padding_high ระบุจำนวนการถ่วงที่เพิ่มไว้ที่ปลายด้านต่ำ (ถัดจากดัชนี 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 ในการปรับปริมาณ (C1), (C2), (C4)
(I2) padding_value เทนเซอร์ 0 มิติหรือเทนเซอร์ที่แปลงค่าเป็นจำนวนเต็มต่อเทนเซอร์ (C1)
(I3) edge_padding_low ค่าคงที่เทนเซอร์ 1 มิติประเภท si64 (C1), (C4)
(I4) edge_padding_high ค่าคงที่ tensor ใน 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

ความหมาย

ดำเนินการนับตามองค์ประกอบตามจำนวนบิตที่ตั้งค่าไว้ใน tensor ของ operand และสร้าง tensor ขนาด result

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand เทนเซอร์ประเภทจำนวนเต็ม (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]

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

พาวเวอร์

ความหมาย

ทำการยกกำลังตามองค์ประกอบของ tensor ของ lhs ด้วย rhs tensor และสร้าง Tensor ขนาด result ทําสิ่งต่อไปนี้โดยขึ้นอยู่กับประเภทองค์ประกอบ

  • สําหรับจํานวนเต็ม: เลขยกกำลังจำนวนเต็ม
  • สำหรับตัวเลขทศนิยม: pow จาก IEEE-754
  • สําหรับจํานวนเชิงซ้อน: การยกกำลังเชิงซ้อน
  • สำหรับประเภทที่เล็กลง: dequantize_op_quantize(power, lhs, rhs, type(result))

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) lhs เทนเซอร์ประเภทจำนวนเต็ม ทศนิยม หรือเชิงซ้อน หรือเทนเซอร์ที่ผ่านการแปลงเชิงปริมาณต่อเทนเซอร์ (C1)
(I2) rhs เทนเซอร์ประเภทจำนวนเต็ม ทศนิยม หรือเชิงซ้อน หรือเทนเซอร์ที่ผ่านการแปลงเชิงปริมาณต่อเทนเซอร์ (C1)

เอาต์พุต

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

ข้อจำกัด

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

ตัวอย่าง

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

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

จริง

อรรถศาสตร์

ดึงข้อมูลส่วนที่เป็นจริงทีละองค์ประกอบจาก operand และสร้างเทนเซอร์ result เขียนอย่างเป็นทางการมากขึ้นสำหรับองค์ประกอบ x แต่ละรายการ ดังนี้ real(x) = is_complex(x) ? real_part(x) : x

อินพุต

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

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result เทนเซอร์ประเภทจุดลอยตัว (C1), (C2)

ข้อจำกัด

  • (C1) shape(result) = shape(operand)
  • (C2) element_type(result) มีความหมายดังต่อไปนี้
    • complex_element_type(element_type(operand)) หาก is_complex(operand)
    • จ่าย element_type(operand)

ตัวอย่าง

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

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

recv

ความหมาย

รับข้อมูลจากแชแนลที่มี channel_id และสร้าง results

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

results ประกอบด้วยค่าเพย์โหลดที่มาก่อนและโทเค็นที่ตามหลัง ในอนาคตเราวางแผนที่จะแยกเพย์โหลดและโทเค็นออกเป็น 2 ออกพุตแยกกันเพื่อปรับปรุงความชัดเจน (#670)

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) token token (C4)
(I2) channel_id ค่าคงที่ประเภท si64
(I3) channel_type enum ของ DEVICE_TO_DEVICE และ HOST_TO_DEVICE (C1)
(I4) is_host_transfer ค่าคงที่ประเภท i1 (C1)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
results จำนวนเทนเซอร์ เทนเซอร์ที่แปลงเป็นจำนวนจริง หรือโทเค็นแบบผันแปร (C2-C4)

ข้อจำกัด

  • (C1) channel_type หมายถึง
    • HOST_TO_DEVICE หาก is_host_transfer = true
    • DEVICE_TO_DEVICE ในกรณีอื่น
  • (ค2) 0 < size(results)
  • (C3) is_empty(result[:-1]) หรือ is_tensor(type(results[:-1]))
  • (C4) is_token(type(results[-1]))

ตัวอย่าง

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

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

ลด

ความหมาย

ใช้ฟังก์ชันการลด body กับ inputs และ init_values ตาม dimensions และสร้างเทนเซอร์ results

ลําดับของการลดคือการกําหนดการใช้งาน ซึ่งหมายความว่า body และ init_values ต้องอยู่ในรูปแบบโมนอยด์เพื่อรับประกันว่าการดําเนินการจะสร้างผลลัพธ์เดียวกันสําหรับอินพุตทั้งหมดสําหรับการติดตั้งใช้งานทั้งหมด อย่างไรก็ตาม เงื่อนไขนี้ใช้ไม่ได้กับการลดราคายอดนิยมหลายรายการ เช่น การเพิ่มจุดลอยตัวสำหรับ body และ 0 สำหรับ init_values ไม่ได้ก่อให้เกิดโมนอยด์เนื่องจากการเพิ่มจุดลอยตัวนั้นไม่ได้เชื่อมโยงกัน

อย่างเป็นทางการมากขึ้น results...[j0, ..., jR-1] = reduce(input_slices_converted) ซึ่ง

  • input_slices = inputs...[j0, ..., :, ..., jR-1] โดยแทรก : ที่ dimensions
  • input_slices_converted = to_destination_type(input_slices..., type(func_inputs(body)[:len(func_inputs(body))//2])...)
  • init_values_converted = to_destination_type(init_values..., type(func_inputs(body)[len(func_inputs(body))//2:])...)
  • reduce(input_slices_converted) = exec(schedule) สำหรับแผนผังไบนารีบางทรี schedule โดยที่
    • exec(node) = body(exec(node.left), exec(node.right))
    • exec(leaf) = leaf.value
  • schedule คือต้นไม้ไบนารีแบบสมบูรณ์ที่กําหนดโดยการใช้งาน ซึ่งการเรียกใช้ตามลําดับประกอบด้วยการดำเนินการต่อไปนี้
    • input_slices_converted...[index] สำหรับ index ทั้งหมดใน index_space(input_slices_converted) ในลำดับจากน้อยไปมากของ index
    • แทรกด้วย init_values_converted จำนวนที่กําหนดโดยการใช้งานในตําแหน่งที่กําหนดโดยการใช้งาน

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) inputs จำนวนเทนเซอร์แบบผันแปรหรือเทนเซอร์ที่แปลงค่าต่อเทนเซอร์ (C1-C4), (C6), (C7)
(I2) init_values เทนเซอร์ 0 มิติจํานวนไม่จํากัดหรือเทนเซอร์ที่แปลงค่าเป็นจำนวนจริงต่อเทนเซอร์ (C2), (C3)
(I3) dimensions ค่าคงที่ tensor ใน 1 มิติของประเภท si64 (C4), (C5), (C7)
(I4) body ฟังก์ชัน (C6)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
results จำนวนแปรผันของ Tensor หรือ Tensor ต่อ Tensor ที่เล็กลง (C3), (C7), (C8)

ข้อจำกัด

  • (C1) same(shape(inputs...))
  • (ค2) 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 และเปลี่ยนกลับไปเป็นประเภทจุดลอยตัวดั้งเดิมและสร้าง Tensor เป็น output

ในรูปแบบทางการ

  • ระบบจะอัปเดตบิตเศษทศนิยมของค่าเดิมเพื่อปัดเศษค่าเดิมเป็นค่าที่ใกล้เคียงที่สุดซึ่งแสดงได้ด้วย mantissa_bits โดยใช้นิพจน์เชิงความหมายของ roundToIntegralTiesToEven
  • จากนั้น หาก mantissa_bits น้อยกว่าจำนวนบิตแมนทิสซาของค่าเดิม ระบบจะตัดบิตแมนทิสซาเป็น mantissa_bits
  • จากนั้น หากบิตตัวคูณของผลลัพธ์กลางไม่พอดีกับช่วงที่กำหนดโดย exponent_bits ผลลัพธ์กลางจะล้นเป็นอนันต์โดยใช้เครื่องหมายเดิม หรือจะต่ำกว่าศูนย์โดยใช้เครื่องหมายเดิม
  • สำหรับประเภทที่เล็กลง จะดำเนินการ dequantize_op_quantize( lambda operand: reduce_precision(operand, exponent_bits, mantissa_bits), operand, type(result))

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand tensor ของประเภทจุดลอยตัวหรือ tensor ที่แปลงค่าเป็น 1 เซนเซอร์ (C1)
(I2) exponent_bits ค่าคงที่ประเภท si32 (C2)
(I3) mantissa_bits ค่าคงที่ประเภท si32 (C3)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
output tensor ของประเภทจุดลอยตัวหรือ tensor ที่แปลงค่าเป็น 1 เซนเซอร์ (C1)

ข้อจำกัด

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

ตัวอย่าง

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

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

reduce_scatter

อรรถศาสตร์

reduce_scatter

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

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

  • cross_replica(replica_groups) if channel_id <= 0 and use_global_device_ids = false
  • cross_replica_and_partition(replica_groups) หากเป็น channel_id > 0 and use_global_device_ids = false
  • flattened_ids(replica_groups) if channel_id > 0 and use_global_device_ids = true

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

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

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand 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
  • (ค2) 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 จำนวนแปรผันของ Tensor หรือ Tensor ต่อ Tensor ที่เล็กลง (C1-C4), (C6), (C8), (C10), (C12), (C13), (C15)
(I2) init_values เทนเซอร์ 0 มิติจํานวนไม่จํากัดหรือเทนเซอร์ที่แปลงค่าเป็นจำนวนจริงต่อเทนเซอร์ (C1), (C13)
(I3) window_dimensions ค่าคงที่ tensor ใน 1 มิติของประเภท si64 (C4), (C5), (C15)
(I4) window_strides ค่าคงที่เทนเซอร์ 1 มิติประเภท si64 (C6), (C7), (C15)
(I5) base_dilations ค่าคงที่เทนเซอร์ 1 มิติประเภท si64 (C8), (C9), (C15)
(I6) window_dilations ค่าคงที่ tensor ใน 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
  • (ค2) 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])
  • (ค7) 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 และพจน์ divisor 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] โดยที่ 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) อาจแตกต่างกัน
  • (ค2) 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)
  • (ค2) 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 Tensor จำนวนเต็ม 0 มิติของจำนวนเต็ม บูลีน หรือประเภทจุดลอยตัว (C1), (C2)
(I3) shape ค่าคงที่ tensor ใน 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

ความหมาย

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

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand tensor ของประเภทจุดลอยตัวหรือ tensor ที่แปลงค่าเป็น 1 เซนเซอร์ (C1)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result tensor ของประเภทจุดลอยตัวหรือ tensor ที่แปลงค่าเป็น 1 เซนเซอร์ (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

ความหมาย

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

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand tensor ของประเภทจุดลอยตัวหรือ tensor ที่แปลงค่าเป็น 1 เซนเซอร์ (C1)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result tensor ของประเภทจุดลอยตัวหรือ tensor ที่แปลงค่าเป็น 1 เซนเซอร์ (C1)

ข้อจำกัด

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

ตัวอย่าง

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

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

rsqrt

ความหมาย

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

  • สำหรับตัวเลขทศนิยม: rSqrt จาก IEEE-754
  • สำหรับจำนวนเชิงซ้อน: รากที่สองของส่วนกลับเชิงซ้อน
  • สำหรับประเภทที่เล็กลง: dequantize_op_quantize(rsqrt, operand, type(result))

อินพุต

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

เอาต์พุต

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

ข้อจำกัด

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

ตัวอย่าง

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

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

กระจาย

ความหมาย

สร้าง tensor ของ results ซึ่งเท่ากับ inputs tensor เว้นแต่ว่าสไลซ์หลายส่วนที่ระบุโดย scatter_indices จะได้รับการอัปเดตด้วยค่า updates โดยใช้ update_computation

แผนภาพต่อไปนี้แสดงวิธีที่องค์ประกอบใน updates... แมปกับองค์ประกอบใน results... โดยใช้ตัวอย่างที่ชัดเจน แผนภาพจะเลือกupdates...ดัชนีตัวอย่าง 2-3 รายการและอธิบายรายละเอียดว่า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)] if d_input = input_batching_dims[i_batching] and 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) where:
    • หาก result_index อยู่ในขอบเขตสำหรับ shape(results...)
    • updates_converted = to_destination_type( updates...[update_index], type(func_inputs(update_computation) [len(func_inputs(update_computation))//2:])... )
    • updated_values = update_computation(results...[result_index], updates_converted)
    • updated_results เป็นสำเนาของ results ซึ่งตั้งค่า results...[result_index] เป็น updated_values...
    • ไม่เช่นนั้น
    • updated_results = results
  • exec([], results) = results

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

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

อินพุต

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

ตัวอย่าง

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

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

เลือก

ความหมาย

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

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) pred เทนเซอร์ประเภท i1 (C1)
(I2) on_true 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)
  • (ค2) baseline_type(on_true) = baseline_type(on_false) = baseline_type(result)

ตัวอย่าง

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

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

select_and_scatter

ความหมาย

กระจายค่าจากเทนเซอร์ source โดยใช้ scatter โดยอิงตามผลลัพธ์ของ reduce_window ของเทนเซอร์ input โดยใช้ select และสร้างเทนเซอร์ result

แผนภาพต่อไปนี้แสดงวิธีคํานวณองค์ประกอบใน result จาก operand และ source โดยใช้ตัวอย่างที่ชัดเจน

select_and_scatter

ในรูปแบบทางการ

  • selected_values = reduce_window_without_init(...) ที่มีอินพุตต่อไปนี้

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

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

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

    • source_values = [source[source_index] for source_index in source_indices]
    • selected_index(source_index) = operand_index if 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)
  • (ค7) 0 < window_strides
  • (C8) shape(padding) = [rank(operand), 2]
  • (C9) select มีประเภท (tensor<E>, tensor<E>) -> tensor<i1> โดยที่ E = element_type(operand)
  • (C10) scatter มีประเภท (tensor<E>, tensor<E>) -> tensor<E> โดยที่ is_promotable(element_type(operand), E)
  • (C11) shape(operand) = shape(result)
  • (C12) element_type(result) = E

ตัวอย่าง

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

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

ส่ง

ความหมาย

ส่ง inputs ไปยังช่อง channel_id และสร้างโทเค็น result

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

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) inputs จำนวนเทนเซอร์แบบผันแปรหรือเทนเซอร์ที่ผ่านการแปลงค่าเป็นจำนวนจริง
(I2) token token
(I3) channel_id ค่าคงที่ของประเภท si64
(I4) channel_type enum ของ DEVICE_TO_DEVICE และ DEVICE_TO_HOST (C1)
(I5) is_host_transfer ค่าคงที่ของประเภท i1 (C1)

เอาต์พุต

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

ข้อจำกัด

  • (C1) channel_type หมายถึง
    • DEVICE_TO_HOST หาก is_host_transfer = true
    • จ่าย DEVICE_TO_DEVICE

ตัวอย่าง

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

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

shift_left

ความหมาย

ทำการดำเนินการลูกศรซ้ายจากองค์ประกอบบน tensor ของ lhs ด้วยจํานวนบิต rhs และสร้าง tensor ขนาด result

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) lhs เทนเซอร์ประเภทจำนวนเต็ม (C1)
(I2) rhs เทนเซอร์ประเภทจำนวนเต็ม (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

ความหมาย

ทำการดำเนินการ Shift ขวาของเอลิเมนต์ใน lhs Tensor ด้วย rhs จำนวนบิตและสร้าง result tensor

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) lhs เทนเซอร์ประเภทจำนวนเต็ม (C1)
(I2) rhs เทนเซอร์ประเภทจำนวนเต็ม (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 เทนเซอร์ประเภทจำนวนเต็ม (C1)
(I2) rhs เทนเซอร์ประเภทจำนวนเต็ม (C1)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result Tensor ของประเภทจำนวนเต็ม (C1)

ข้อจำกัด

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

ตัวอย่าง

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

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

เครื่องหมาย

ความหมาย

แสดงผลเครื่องหมายของ operand ตามองค์ประกอบและสร้างเทนเซอร์ result ในทางที่เป็นทางการมากขึ้น ความหมายขององค์ประกอบ x แต่ละรายการจะแสดงโดยใช้ไวยากรณ์ Python ดังนี้

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

สำหรับประเภทที่เล็กลง จะดำเนินการ dequantize_op_quantize(sign, operand, type(result))

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand tensor ของจำนวนเต็มที่มีการลงชื่อ จุดลอยตัว หรือประเภทเชิงซ้อน หรือ Tensor ควอนไทล์ (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]

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

ไซน์

ความหมาย

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

  • สำหรับแบบลอย: sin จาก IEEE-754
  • สำหรับจำนวนเชิงซ้อน: ไซน์เชิงซ้อน
  • สำหรับประเภทที่มีการแปลงเป็นจำนวนเต็ม: dequantize_op_quantize(sine, operand, type(result))

อินพุต

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

เอาต์พุต

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

ข้อจำกัด

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

ตัวอย่าง

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

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

ส่วนแบ่ง

ความหมาย

แยกชิ้นส่วนจาก operand โดยใช้ดัชนีเริ่มต้นที่คำนวณแบบคงที่และสร้าง Tensor result start_indices มีดัชนีเริ่มต้นของส่วนสำหรับมิติข้อมูลแต่ละรายการ limit_indices มีดัชนีสิ้นสุด (ไม่รวม) ของส่วนสำหรับมิติข้อมูลแต่ละรายการ และ strides มีระยะห่างสำหรับมิติข้อมูลแต่ละรายการ

เขียนเป็นทางการคือ result[result_index] = operand[operand_index] โดยที่ 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)
  • (ค2) size(start_indices) = size(limit_indices) = size(strides) = rank(operand)
  • (C3) 0 <= start_indices <= limit_indices <= shape(operand)
  • (C4) 0 < strides
  • (C5) shape(result) = ceil((limit_indices - start_indices) / strides)

ตัวอย่าง

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

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

จัดเรียง

ความหมาย

จัดเรียงชิ้นส่วน inputs แบบ 1 มิติตามมิติข้อมูล dimension เข้าด้วยกันตาม comparator แล้วสร้าง results

dimension ต่างจากอินพุตที่คล้ายกันในการดำเนินการอื่นๆ เนื่องจากอนุญาตให้ใช้ค่าลบ โดยมีความหมายตามที่อธิบายไว้ด้านล่าง ในอนาคต การกระทำนี้อาจไม่ได้รับอนุญาต ด้วยเหตุผลด้านความสอดคล้อง (#1377)

หาก is_stable เป็นจริง การจัดเรียงจะมีค่าคงที่ กล่าวคือ ลำดับสัมพัทธ์ขององค์ประกอบที่ถือว่าเท่ากับตัวเปรียบเทียบจะยังคงอยู่ ในกรณีที่มีอินพุตเดียว 2 องค์ประกอบ 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)
  • (ค2) type(inputs...) = type(results...)
  • (C3) same(shape(inputs...) + shape(results...))
  • (C4) -R <= dimension < R โดยที่ R = rank(inputs[0])
  • (C5) comparator เป็นประเภท (tensor<E1>, tensor<E1>, ..., tensor<EN-1>, tensor<EN-1>) -> tensor<i1>, โดยที่ Ei = element_type(inputs[i])

ตัวอย่าง

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

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

sqrt

ความหมาย

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

  • สำหรับแบบลอย: squareRoot จาก IEEE-754
  • สำหรับจำนวนเชิงซ้อน: รากที่สองของจำนวนเชิงซ้อน
  • สำหรับประเภทที่เล็กลง: dequantize_op_quantize(sqrt, operand, type(result))

อินพุต

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

เอาต์พุต

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

ข้อจำกัด

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

ตัวอย่าง

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

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

ลบ

ความหมาย

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

  • สําหรับจํานวนเต็ม: การลบจํานวนเต็ม
  • สำหรับตัวเลขทศนิยม: subtraction จาก IEEE-754
  • สำหรับจำนวนเชิงซ้อน: การลบเชิงซ้อน
  • สำหรับประเภทที่มีการแปลงค่าเป็นจำนวนเต็ม ให้ทำดังนี้
    • dequantize_op_quantize(subtract, lhs, rhs, type(result))

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) lhs เทนเซอร์ประเภทจำนวนเต็ม ทศนิยม หรือเชิงซ้อน หรือเทนเซอร์ที่ผ่านการแปลงเชิงปริมาณต่อเทนเซอร์ (C1)
(I2) rhs เทนเซอร์ประเภทจำนวนเต็ม ทศนิยม หรือเชิงซ้อน หรือเทนเซอร์ที่ผ่านการแปลงเชิงปริมาณต่อเทนเซอร์ (C1)

เอาต์พุต

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

ข้อจำกัด

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

ตัวอย่าง

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

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

tan

ความหมาย

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

  • สำหรับตัวเลขทศนิยม: tan จาก IEEE-754
  • สำหรับจำนวนเชิงซ้อน: แทนเจนต์เชิงซ้อน
  • สำหรับประเภทที่มีการแปลงเป็นจำนวนเต็ม: dequantize_op_quantize(tan, operand, type(result))

อินพุต

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

เอาต์พุต

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

ข้อจำกัด

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

ตัวอย่าง

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

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

tanh

อรรถศาสตร์

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

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

อินพุต

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

เอาต์พุต

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

ข้อจำกัด

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

ตัวอย่าง

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

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

เปลี่ยนลําดับ

ความหมาย

เปลี่ยนลําดับมิติข้อมูลของเทนเซอร์ operand โดยใช้ permutation และสร้างเทนเซอร์ result เขียนเป็นทางการคือ result[result_index] = operand[operand_index] โดยที่ result_index[d] = operand_index[permutation[d]]

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand เทนเซอร์หรือเทนเซอร์ที่ผ่านการแปลงค่าเป็นจำนวนเต็ม (C1-C4)
(I2) permutation ค่าคงที่เทนเซอร์ 1 มิติประเภท si64 (C2-C4)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result เทนเซอร์หรือเทนเซอร์ที่ผ่านการแปลงค่าเป็นจำนวนเต็ม (C1), (C3-C4)

ข้อจำกัด

  • (C1) element_type(result) คำนวณจากข้อมูลต่อไปนี้
    • element_type(operand) หาก !is_per_axis_quantized(operand)
    • element_type(operand) เว้นแต่ว่า quantization_dimension(operand) และ quantization_dimension(result) อาจต่างกัน
  • (C2) permutation เป็นการเรียงสับเปลี่ยนของ range(rank(operand))
  • (C3) shape(result) = dim(operand, permutation...)
  • (C4) ถ้าis_per_axis_quantized(result) ให้ quantization_dimension(operand) = permutation(quantization_dimension(result))

ตัวอย่าง

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

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

triangular_solve

ความหมาย

แก้ระบบสมการเชิงเส้นหลายชุดด้วยเมทริกซ์สัมประสิทธิ์สามเหลี่ยมล่างหรือบน

พูดให้เข้าใจง่ายขึ้นคือ เมื่อทราบ a และ b แล้ว result[i0, ..., iR-3, :, :] คือค่าของ op(a[i0, ..., iR-3, :, :]) * x = b[i0, ..., iR-3, :, :] เมื่อ left_side เป็น true หรือ x * op(a[i0, ..., iR-3, :, :]) = b[i0, ..., iR-3, :, :] เมื่อ left_side เป็น false โดยหาค่าตัวแปร x โดยที่ op(a) กำหนดโดย transpose_a ซึ่งอาจเป็นค่าใดค่าหนึ่งต่อไปนี้

  • NO_TRANSPOSE: ดำเนินการโดยใช้ a ตามที่เป็น
  • TRANSPOSE: ดำเนินการกับการเปลี่ยนรูปแบบของ a
  • ADJOINT: ดำเนินการกับ Conjugate Transpose ของ 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)
  • (ค2) 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 Tuple (C1)

ข้อจำกัด

  • (C1) result เป็นประเภท tuple<E0, ..., EN-1> ที่ Ei = type(val[i])

ตัวอย่าง

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

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

uniform_dequantize

ความหมาย

ทำการแปลงทีละองค์ประกอบของเทนเซอร์ที่ผ่านการแปลงค่า operand เป็นเทนเซอร์แบบจุดลอยตัว result ตามพารามิเตอร์การแปลงค่าที่กําหนดโดยประเภท operand

หรือจะเรียกอย่างเป็นทางการว่า result = dequantize(operand)

อินพุต

ป้ายกำกับ ชื่อ ประเภท ข้อจำกัด
(I1) operand เทนเซอร์ที่แปลงค่าเป็นจำนวนเต็ม (C1), (C2)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result เทนเซอร์ประเภทจุดลอยตัว (C1), (C2)

ข้อจำกัด

  • (C1) shape(operand) = shape(result)
  • (ค2) 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

อรรถศาสตร์

ทำการแปลงที่วัดเชิงองค์ประกอบของ Tensor จุดลอยตัวหรือ tensor ที่เล็กลง operand เป็น Tensor ที่ควอนไซส์ result ตามพารามิเตอร์การแปลงปริมาณที่กำหนดโดยประเภท result

อย่างเป็นทางการ

  • หาก is_float(operand)
    • result = quantize(operand, type(result))
  • หากเป็น is_quantized(operand)
    • float_result = dequantize(operand)
    • result = quantize(float_result, type(result))

อินพุต

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

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result เทนเซอร์ที่แปลงค่าเป็นจำนวนเต็ม (C1), (C2)

ข้อจำกัด

  • (C1) shape(operand) = shape(result)
  • (ค2) 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 จำนวนแปรผันของ Tensor, Tensor ที่เล็กลง หรือโทเค็น (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 tensor ของประเภทบูลีนหรือจำนวนเต็ม (C1)

เอาต์พุต

ชื่อ ประเภท ข้อจำกัด
result tensor ของประเภทบูลีนหรือจำนวนเต็ม (C1)

ข้อจำกัด

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

ตัวอย่าง

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

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

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

การทำงานร่วมกันของภาษาถิ่น

ขณะนี้ โปรแกรม StableHLO ที่ใช้จริงบางครั้งอาจมีการดำเนินการที่ StableHLO ไม่ได้กำหนด

โมดูล ฟังก์ชัน การเรียก และผลลัพธ์

StableHLO ใช้การดำเนินการอัปสตรีม MLIR สำหรับ ModuleOp, FuncOp, CallOp และ ReturnOp การดำเนินการนี้ทำขึ้นเพื่อให้ทำงานร่วมกันกับกลไก MLIR ที่มีอยู่ได้ดียิ่งขึ้น เนื่องจากมีการเขียนการผ่านข้อมูลจำนวนมากที่มีประโยชน์เพื่อกำหนดเป้าหมาย FuncOp และ ModuleOp และไปป์ไลน์การคอมไพล์จำนวนมากคาดหวังว่าจะมีการดำเนินการเหล่านี้ การดำเนินการเหล่านี้ใช้การรับประกันความเข้ากันได้อย่างเต็มรูปแบบ หากมีการเปลี่ยนแปลงเกี่ยวกับการดำเนินการเหล่านี้ในลักษณะที่เข้ากันไม่ได้ (เช่น การนำออก) ระบบจะเพิ่มรายการที่เทียบเท่าของ StableHLO เพื่อรักษาความเข้ากันได้

ชโล

ชุดการดำเนินการ CHLO มีการดำเนินการในระดับที่สูงขึ้นซึ่งจะแยกออกเป็น StableHLO ปัจจุบันยังไม่มีการรับประกันความเข้ากันได้สำหรับ CHLO คุณต้องใช้ chlo-legalize-to-stablehlo pass ก่อนการแปลงเป็นอนุกรมเพื่อรับประกันความเข้ากันได้

การดำเนินการรูปร่าง

การใช้การดำเนินการบางอย่างจากภาษา MLIR หลักในโปรแกรม StableHLO แบบไดนามิกเพื่อดำเนินการคำนวณรูปร่างเป็น Use Case ทั่วไปในชุมชน โดยปกติแล้ว คำสั่งเหล่านี้จะประกอบด้วยรูปแบบ shape อย่างเช่น shape_of หรือ num_elements, รูปแบบ tensor อย่างเช่น dim หรือ from_elements และประเภท index ในตัว

Dynamism RFC > O2 ระบุว่ารายการเหล่านี้อยู่นอกขอบเขต แต่มีการรองรับindexบางประเภทเพื่อวัตถุประสงค์ในการทํางานร่วมกัน เราไม่รับประกันความเข้ากันได้สำหรับอุปกรณ์หรือประเภทเหล่านี้ คุณสามารถใช้พาส shape-legalize-to-stablehlo เพื่อแปลงการดำเนินการเหล่านี้เป็นการดำเนินการ StableHLO ที่รองรับอย่างเต็มรูปแบบ

การดำเนินการที่เลิกใช้งาน

การดำเนินการ StableHLO หลายรายการที่รับค่ามาจาก MHLO นั้นเลิกใช้งานแล้วและกำลังจะออกจาก StableHLO ดูรายละเอียดทั้งหมดเกี่ยวกับการนําออกเหล่านี้ได้ใน StableHLO v1.0 Cleanup #2283 ปัญหาเครื่องมือติดตามสำหรับการเลิกใช้งานเหล่านี้คือ #2340

การดำเนินการเหล่านี้แบ่งออกเป็น 2-3 หมวดหมู่ ดังนี้

  • หมวดหมู่ "ไม่อยู่ใน HLO" ของการดำเนินการ StableHLO - ตอนแรกเป็นส่วนหนึ่งของกลุ่มเป้าหมาย StableHLO แต่ต่อมาถือว่าไม่เหมาะสม: broadcast, create_token, cross-replica-sum, dot, einsum, torch_index_select, unary_einsum (#3)
  • การดำเนินการที่ไม่ได้ใช้ - การดำเนินการเหล่านี้อาจมีประโยชน์ในบางจุด แต่การดำเนินการนั้นยังไม่ได้รับการพัฒนา หรือไปป์ไลน์ที่ใช้การดำเนินการเหล่านี้ได้รับการแยกส่วนใหม่เพื่อไม่ให้ต้องใช้การดำเนินการดังกล่าวอีกต่อไป ซึ่งรวมถึง map, tuple (#598), get_tuple_element, rng, complex การเปรียบเทียบ #560 และ 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 และคำนวณค่าเอาต์พุต ระบบจะคำนวณค่าเอาต์พุตของฟังก์ชันโดยการเรียกใช้กราฟของการดำเนินการที่รูทในการดำเนินการ return ที่เกี่ยวข้อง

ลําดับการดําเนินการจะกําหนดโดยการใช้งาน ตราบใดที่สอดคล้องกับการไหลของข้อมูล เช่น หากดําเนินการก่อนใช้งาน ใน 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 ตาม 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) ต่างก็มีประเภท ui32

เราจะทราบขนาดของตารางกระบวนการแบบคงที่สำหรับทุกโปรแกรม (ในอนาคต เรากำลังวางแผนที่จะทำให้ตารางนี้เป็นส่วนที่ชัดเจนของโปรแกรม StableHLO #650) และอันดับภายในตารางกระบวนการจะรู้กันอยู่แล้วสำหรับทุกกระบวนการ แต่ละกระบวนการจะเข้าถึงตำแหน่งของกระบวนการภายในตารางกริดกระบวนการได้ผ่านการดำเนินการ replica_id และ partition_id

ในตารางกริดกระบวนการ โปรแกรมทั้งหมดอาจเหมือนกัน (ในรูปแบบ "โปรแกรมเดียว ข้อมูลหลายรายการ") ทั้งหมดอาจแตกต่างกัน (ในรูปแบบ "โปรแกรมหลายรายการ ข้อมูลหลายรายการ") หรือเป็นแบบผสมก็ได้ ในอนาคต เราวางแผนที่จะเปิดตัวการรองรับนิพจน์อื่นๆ ในการกําหนดโปรแกรม StableHLO แบบขนาน ซึ่งรวมถึง GSPMD (#619)

ในตารางกริดกระบวนการ กระบวนการส่วนใหญ่จะแยกจากกัน โดยมีสถานะการดำเนินการแยกกัน ค่าอินพุต/กลาง/เอาต์พุตแยกกัน และการดำเนินการส่วนใหญ่จะดำเนินการแยกกันระหว่างกระบวนการต่างๆ ยกเว้นการดำเนินการแบบรวมจำนวนเล็กน้อยที่อธิบายไว้ด้านล่าง

เนื่องจากการดำเนินการของ Ops ส่วนใหญ่ใช้เฉพาะค่าจากกระบวนการเดียวกัน จึงสามารถอ้างถึงค่าเหล่านี้ด้วยชื่อได้ไม่ชัดเจน อย่างไรก็ตาม เมื่ออธิบายความหมายของการดำเนินการแบบรวม รูปแบบดังกล่าวไม่เพียงพอ และนั่นทำให้เกิดเครื่องหมาย name@process_id เพื่ออ้างอิงค่า name ภายในกระบวนการหนึ่งๆ (จากมุมมองนี้ name ที่ไม่เข้าเกณฑ์จะมองว่าเป็นชื่อย่อของ name@(replica_id(), partition_id()) ได้)

ลําดับการดําเนินการในกระบวนการต่างๆ จะกําหนดโดยการใช้งาน ยกเว้นการซิงค์ที่เกิดจากการสื่อสารแบบจุดต่อจุดและการดำเนินการแบบรวมตามที่อธิบายไว้ด้านล่าง

การสื่อสารแบบจุดต่อจุด

กระบวนการ StableHLO สามารถสื่อสารกันได้ผ่านแชแนล StableHLO ช่องจะแสดงด้วยรหัสบวกประเภท si64 คุณสามารถส่งค่าไปยังแชแนลและรับค่าจากแชแนลผ่านการดำเนินการต่างๆ

รายละเอียดเพิ่มเติม เช่น แหล่งที่มาของรหัสช่องเหล่านี้ วิธีที่โปรแกรมประมวลผลจะรับรู้รหัสเหล่านี้ และประเภทการซิงค์ที่รหัสเหล่านี้นำมาใช้ อยู่ระหว่างพิจารณา (#484)

การสื่อสารแบบสตรีม

ทุกกระบวนการของ StableHLO จะสามารถเข้าถึงอินเทอร์เฟซสตรีมมิง 2 แบบ ได้แก่

  • ฟีดที่อ่านได้
  • โฆษณานอกฟีดที่เขียนถึงได้

ซึ่งแตกต่างจากแชแนลที่ใช้เพื่อสื่อสารระหว่างกระบวนการต่างๆ จึงมีกระบวนการที่ปลายทั้ง 2 ด้าน แต่อินฟีดและเอาต์ฟีดจะกำหนดการติดตั้งใช้งานที่ปลายอีกด้านหนึ่ง

การกำหนดรูปแบบเพิ่มเติม เช่น วิธีที่การสื่อสารแบบสตรีมส่งผลต่อลําดับการดําเนินการและการซิงค์ประเภทใดที่นำมาใช้ในการสื่อสารนั้น อยู่ระหว่างพิจารณา (#484)

การดำเนินการแบบรวม

การดำเนินการแบบรวมใน StableHLO มี 6 รายการ ได้แก่ all_gather, all_reduce, all_to_all, collective_broadcast, collective_permute และ reduce_scatter การดำเนินการทั้งหมดนี้แยกกระบวนการในตารางกระบวนการ StableHLO เป็นกลุ่มกระบวนการ StableHLO และดำเนินการคำนวณร่วมภายในกลุ่มกระบวนการแต่ละกลุ่ม โดยแยกออกจากกลุ่มกระบวนการอื่นๆ

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

หากกลุ่มกระบวนการเกี่ยวข้องกับการสื่อสารข้ามพาร์ติชัน เช่น มีกระบวนการในกลุ่มกระบวนการที่มีรหัสพาร์ติชันต่างกัน การดำเนินการของการดำเนินการแบบรวมจึงต้องมีช่องทาง และการดำเนินการแบบรวมต้องระบุ 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 รองรับ Tensor ที่มีรูปร่างแบบไดนามิก อย่างไรก็ตาม รูปร่างจะต้องตรงกันบนรันไทม์ มิเช่นนั้นจะไม่สามารถกำหนดลักษณะการทำงานได้ StableHLO ไม่ได้ระบุการดำเนินการอย่างชัดเจนที่สามารถยืนยันได้ว่า Tensor มีรูปร่างที่กำหนดในระหว่างรันไทม์ โปรดิวเซอร์มีหน้าที่รับผิดชอบในการสร้างโค้ดที่ถูกต้อง

ตัวอย่างที่เฉพาะเจาะจงคือ โปรแกรมด้านล่างนี้ถูกต้อง อย่างไรก็ตาม ขณะรันไทม์ รูปร่างของ %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 หรือเป็น tensor ที่มีเฉพาะองค์ประกอบ 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) ในส่วน "ฟังก์ชันส่วนกลาง"), 4) Function (ฟังก์ชันตามที่กำหนดไว้")

ชื่ออาจหมายถึงค่าที่แตกต่างกัน ทั้งนี้ขึ้นอยู่กับบริบท กล่าวอย่างเจาะจงก็คือ ส่วน "Semantics" สำหรับการดำเนินการ (และเทียบเท่ากับองค์ประกอบอื่นๆ ของโปรแกรม) จะกำหนดตรรกะรันไทม์ ดังนั้นอินพุตทั้งหมดจึงพร้อมใช้งานเป็น 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 ได้รับการกําหนดในประเภท Tensor และประเภท Tensor ที่แปลงค่าเป็นจำนวนเต็ม และแสดงผล TensorElementType หรือ QuantizedTensorElementType ของ TensorType หรือ QuantizedTensorType ที่เกี่ยวข้องตามลําดับ
def element_type(x: Value | Placeholder | Type):
 if type(x) == TensorType:
    return tensor_element_type(x)
  if type(x) == QuantizedTensorType:
    return quantized_tensor_element_type(x)
  if type(x) is not Type:
    return element_type(type(x))
  • is_per_axis_quantized(x: Value | Placeholder | Type) -> Value เป็นทางลัดสำหรับ is_quantized(x) and quantization_dimension(x) is not None

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

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

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

  if is_same_type == False:
    return False

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

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

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

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

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

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

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

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

  • is_empty_algorithm(*args: Type) ตรวจสอบว่าช่องอัลกอริทึมจุดทั้งหมดตั้งค่าเป็น None หรือไม่ ซึ่งจําเป็นเนื่องจากอัลกอริทึมของจุดมีลักษณะการทํางานเริ่มต้นที่กําหนดโดยการใช้งาน ดังนั้นการระบุค่าเริ่มต้นจึงไม่ถูกต้อง

การสร้างค่า

  • operation_name(*xs: Value | Type) -> Value. ใช้ได้กับการดำเนินการทั้งหมด เช่น add(lhs, rhs) จะรับค่าเทนเซอร์ 2 ค่า ได้แก่ lhs และ rhs แล้วแสดงผลลัพธ์ของการประเมินการดำเนินการ add ด้วยอินพุตเหล่านี้ สําหรับการดำเนินการบางอย่าง เช่น broadcast_in_dim ประเภทเอาต์พุตจะ "รับน้ำหนัก" กล่าวคือ ต้องใช้ในการประเมินการดำเนินการ ในกรณีนี้ ฟังก์ชันจะใช้ประเภทเหล่านี้เป็นอาร์กิวเมนต์

ฟังก์ชันสำหรับค่า

  • ออปเรเตอร์และฟังก์ชันของ Python ทั้งหมดพร้อมใช้งาน เช่น คุณสามารถระบุทั้งนิพจน์การสมัครใช้บริการและการแบ่งส่วนจาก Python เพื่อจัดทำดัชนีใน Tensor, Tensor ที่ผ่านการแปลงเชิงปริมาณ และทูเพลต

  • 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 ได้รับการกําหนดใน Tensor และจะแสดงผลเป็น true หากองค์ประกอบทั้งหมดของ x เป็น NaN หรือ false มิเช่นนั้น หาก x ไม่ใช่ Tensor จะแสดงผล None

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

  • split(x: Value, num_results: Value, axis: Value) -> Value ได้รับการกําหนดในเทนเซอร์และแสดงผลส่วน num_results ของ x ตามแกน axis หาก x ไม่ใช่ tensor หรือ 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 ได้รับการกำหนดไว้ใน Tensor และแสดงดัชนี 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 กำหนดสำหรับประเภท tensor และประเภท tensor ที่เล็กลงและเปลี่ยนรูปแบบเป็น "เส้นฐาน" กล่าวคือ ประเภทที่มีรูปร่างเหมือนกันแต่มีพารามิเตอร์การแปลงปริมาณของประเภทองค์ประกอบจะรีเซ็ตเป็นค่าเริ่มต้น ซึ่งใช้เพื่อเปรียบเทียบทั้งประเภท Tensor และ Tensor ที่ผ่านการแปลงค่าอย่างสม่ำเสมอ ซึ่งจำเป็นต้องใช้บ่อยครั้ง สําหรับประเภทที่มีการแปลงค่าเป็นจำนวนเต็ม ตัวเลือกนี้จะเปิดใช้การเปรียบเทียบประเภทโดยไม่สนใจพารามิเตอร์การแปลงค่าเป็นจำนวนเต็ม ซึ่งก็คือ shape, storage_type, expressed_type, storage_min, storage_max และ quantization_dimension (สําหรับประเภทที่มีการแปลงค่าเป็นจำนวนเต็มตามแกน) ทั้งหมดต้องตรงกัน แต่ scales และ zero points อาจแตกต่างกัน

def baseline_type(x: Value | Placeholder | Type) -> Type:
  if type(x) == TensorType:
    return x
  if type(x) == QuantizedTensorType:
    element_type = quantized_tensor_element_type(x)
    baseline_element_type = QuantizedTensorElementType(
      storage_type = storage_type(element_type),
      storage_min = storage_min(element_type),
      storage_max = storage_max(element_type),
      expressed_type = expressed_type(element_type),
      quantization_dimension = quantization_dimension(element_type),
      scales = [constant(1.0, expressed_type(element_type))] * dim(x, quantization_dimension(element_type)),
      zero_points = [constant(0, storage_type(element_type))] * dim(x, quantization_dimension(element_type)))
    return QuantizedTensorType(shape(x), baseline_element_type)
  if type(x) is not Type:
    return baseline_element_type(type(x))
  • dequantize กำหนดในประเภท Tensor ที่แปลงค่าเป็นจำนวนเต็มและเปลี่ยนให้เป็นประเภท Tensor แบบทศนิยม ซึ่งเกิดขึ้นผ่านการแปลงองค์ประกอบที่ปรับเป็นจำนวนเต็มซึ่งแสดงค่าจำนวนเต็มของประเภทพื้นที่เก็บข้อมูลเป็นค่าทศนิยมที่สอดคล้องกันของประเภทที่แสดงโดยใช้จุดศูนย์และมาตราส่วนซึ่งเชื่อมโยงกับประเภทองค์ประกอบที่ปรับเป็นจำนวนเต็ม
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 กำหนดในประเภท Tensor แบบทศนิยมและเปลี่ยนเป็นประเภท Tensor แบบปัดเศษ ซึ่งเกิดขึ้นผ่านการแปลงค่าจุดลอยตัวของประเภทที่แสดงเป็นค่าจำนวนเต็มที่สอดคล้องของประเภทพื้นที่เก็บข้อมูล โดยใช้จุดเป็น 0 และสเกลที่เชื่อมโยงกับประเภทองค์ประกอบที่ควอนซ์
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 ใช้เพื่อระบุการคำนวณระดับองค์ประกอบบน Tensor แบบควอนซ์ โดยจะแปลงค่าจากรูปแบบเชิงปริมาณเป็นรูปแบบที่แสดง จากนั้นจะดำเนินการ แล้วแปลงค่ากลับเป็นรูปแบบการจัดเก็บ ปัจจุบันฟังก์ชันนี้ใช้ได้กับการแปลงค่าเป็นจำนวนเต็มต่อเทนเซอร์เท่านั้น การวัดขนาดต่อแกนอยู่ระหว่างดำเนินการ (#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 ในประเภทที่แปลงค่าแล้ว โดยจะแปลงอินพุตที่ผ่านการแปลงเชิงปริมาณเป็นประเภทที่แสดงและดำเนินการคํานวณในรูปแบบ float ประเภทองค์ประกอบของเทมพอริง lhs แบบลอยตัวและประเภทที่แสดงของเทมพอริง rhs แบบเชิงปริมาณควรเหมือนกัน
def hybrid_dequantize_then_op(op, lhs, rhs):
  assert(is_float(lhs) and is_quantized(rhs) and element_type(lhs) == expressed_type(rhs))
  return op(lhs, dequantize(rhs))

การคํานวณตารางกริด

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

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

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

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

ความคิดวิพากษ์วิจารณ์

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

รูปร่างไม่ตรงกันสําหรับการดำเนินการแบบอนุภาคเดียว

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

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

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

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

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

ลองดูโปรแกรมจำลองต่อไปนี้

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

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

รูปร่างไม่ตรงกันสําหรับการดำเนินการที่ใช้รูปร่างเอาต์พุตเป็นตัวถูกดำเนินการ

ลองดูโปรแกรมจำลองต่อไปนี้

func.func @foo(%arg0: tensor<2xi32>) {
  %0 = stablehlo.dynamic_iota %arg0, dim = 0 : (tensor<2xi32>) -> tensor<3x4xi64>
  return
}

ค่าในโอเปอเรเตอร์รูปร่างที่รันไทม์ต้องตรงกับรูปร่างของผลลัพธ์ ไม่เช่นนั้นระบบจะไม่ระบุลักษณะการทำงาน กล่าวคือ %arg0 ต้องมีค่าเป็น dense<[3, 4]> : tensor<2xi32> ขณะรันไทม์ หากตัวดำเนินการรูปร่างเป็นค่าคงที่ การดำเนินการนี้จะตรวจสอบแบบคงที่ได้ หากรูปร่างของผลลัพธ์เป็นแบบไดนามิกทั้งหมด ก็จะไม่มีการจับคู่ที่ไม่ตรงกัน